Skip to content

Commit

Permalink
Merge #627: Add bindings to the ElligatorSwift implementation
Browse files Browse the repository at this point in the history
39febcb Create rust-bidings (Davidson Souza)

Pull request description:

  ~**Marking as draft as this is an unreleased feature from libsecp**~

  From upstream:
  This implements encoding of curve points using the ElligatorSwift algorithm, using 4 new API calls:

      secp256k1_ellswift_encode, which converts a public key to a 64-byte pseudorandom encoding.
      secp256k1_ellswift_decode, the reverse operation to convert back to normal public keys.
      secp256k1_ellswift_create, which can be seen as a combination of secp256k1_ec_pubkey_create + secp256k1_ellswift_encode, but is somewhat safer.
      secp256k1_ellswift_xdh, which implements x-only Diffie-Hellman directly on top of 64-byte encoded public keys, and more efficiently than decoding + invoking normal ECDH.

  This algorithm allows mapping any pair of field elements (u, t) to a (valid) x coordinate in the curve. This allows representing a field element as a 64-bytes bit string that is indistinguishable from random. You can build a pair of (u, t) from any group element as well.
  We also have an integrated x-only ECDH that can be used to establish a shared secret between two parties. All algorithms are compatible with BIP324 and are tested against the BIP's test cases.

  I have a few questions about the rust side of the implementation:
  Should it be always on, or leave it behind a cargo feature? In `libsecp` this module is default on, but you can disable it.
  I'm not exposing the low-level functions, instead you can use high-level types to interact with ellswift. Is it reasonable to also expose a safe version of the functions above?

ACKs for top commit:
  tcharding:
    ACK 39febcb
  apoelstra:
    ACK 39febcb

Tree-SHA512: a3c06304a03af9509ff3ef16fd39ee56ec22fc12d1b36be4c20aaa2ad01e98dd34ea64c66db782d3a2c10c3a7b44c701762d45f8d82f45b62db3379710c89c42
  • Loading branch information
apoelstra committed Oct 9, 2023
2 parents da4f67b + 39febcb commit 3aada83
Show file tree
Hide file tree
Showing 6 changed files with 822 additions and 1 deletion.
3 changes: 2 additions & 1 deletion secp256k1-sys/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@ fn main() {
.define("SECP256K1_API", Some(""))
.define("ENABLE_MODULE_ECDH", Some("1"))
.define("ENABLE_MODULE_SCHNORRSIG", Some("1"))
.define("ENABLE_MODULE_EXTRAKEYS", Some("1"));
.define("ENABLE_MODULE_EXTRAKEYS", Some("1"))
.define("ENABLE_MODULE_ELLSWIFT", Some("1"));

if cfg!(feature = "lowmemory") {
base_config.define("ECMULT_WINDOW_SIZE", Some("4")); // A low-enough value to consume negligible memory
Expand Down
105 changes: 105 additions & 0 deletions secp256k1-sys/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,15 @@ pub type SchnorrNonceFn = Option<unsafe extern "C" fn(
data: *mut c_void,
) -> c_int>;

/// A hash function used by `ellswift_ecdh` to hash the final ECDH shared secret.
pub type EllswiftEcdhHashFn = Option<unsafe extern "C" fn(
output: *mut c_uchar,
x32: *const c_uchar,
ell_a64: *const c_uchar,
ell_b64: *const c_uchar,
data: *mut c_void,
) -> c_int>;

/// Data structure that contains additional arguments for schnorrsig_sign_custom.
#[repr(C)]
pub struct SchnorrSigExtraParams {
Expand Down Expand Up @@ -517,11 +526,32 @@ impl core::hash::Hash for Keypair {
}
}

/// Library-internal representation of a ElligatorSwift encoded group element.
#[repr(C)]
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct ElligatorSwift([u8; 64]);

impl ElligatorSwift {
pub fn from_array(arr: [u8; 64]) -> Self {
ElligatorSwift(arr)
}
pub fn to_array(self) -> [u8; 64] {
self.0
}
}

impl_array_newtype!(ElligatorSwift, u8, 64);
impl_raw_debug!(ElligatorSwift);

extern "C" {
/// Default ECDH hash function
#[cfg_attr(not(rust_secp_no_symbol_renaming), link_name = "rustsecp256k1_v0_9_0_ecdh_hash_function_default")]
pub static secp256k1_ecdh_hash_function_default: EcdhHashFn;

/// Default ECDH hash function for BIP324 key establishment
#[cfg_attr(not(rust_secp_no_symbol_renaming), link_name = "rustsecp256k1_v0_9_0_ellswift_xdh_hash_function_bip324")]
pub static secp256k1_ellswift_xdh_hash_function_bip324: EllswiftEcdhHashFn;

#[cfg_attr(not(rust_secp_no_symbol_renaming), link_name = "rustsecp256k1_v0_9_0_nonce_function_rfc6979")]
pub static secp256k1_nonce_function_rfc6979: NonceFn;

Expand Down Expand Up @@ -600,6 +630,34 @@ extern "C" {
output_pubkey: *mut PublicKey,
keypair: *const Keypair)
-> c_int;
// Elligator Swift
#[cfg_attr(not(rust_secp_no_symbol_renaming), link_name = "rustsecp256k1_v0_9_0_ellswift_encode")]
pub fn secp256k1_ellswift_encode(ctx: *const Context,
ell64: *mut c_uchar,
pubkey: *const PublicKey,
rnd32: *const c_uchar)
-> c_int;
#[cfg_attr(not(rust_secp_no_symbol_renaming), link_name = "rustsecp256k1_v0_9_0_ellswift_decode")]
pub fn secp256k1_ellswift_decode(ctx: *const Context,
pubkey: *mut u8,
ell64: *const c_uchar)
-> c_int;
#[cfg_attr(not(rust_secp_no_symbol_renaming), link_name = "rustsecp256k1_v0_9_0_ellswift_create")]
pub fn secp256k1_ellswift_create(ctx: *const Context,
ell64: *mut c_uchar,
seckey32: *const c_uchar,
aux_rand32: *const c_uchar)
-> c_int;
#[cfg_attr(not(rust_secp_no_symbol_renaming), link_name = "rustsecp256k1_v0_9_0_ellswift_xdh")]
pub fn secp256k1_ellswift_xdh(ctx: *const Context,
output: *mut c_uchar,
ell_a64: *const c_uchar,
ell_b64: *const c_uchar,
seckey32: *const c_uchar,
party: c_int,
hashfp: EllswiftEcdhHashFn,
data: *mut c_void)
-> c_int;
}

#[cfg(not(secp256k1_fuzz))]
Expand Down Expand Up @@ -979,6 +1037,53 @@ impl<T> CPtr for [T] {
}
}

impl<T> CPtr for &[T] {
type Target = T;
fn as_c_ptr(&self) -> *const Self::Target {
if self.is_empty() {
ptr::null()
} else {
self.as_ptr()
}
}

fn as_mut_c_ptr(&mut self) -> *mut Self::Target {
if self.is_empty() {
ptr::null_mut()
} else {
self.as_ptr() as *mut Self::Target
}
}

}

impl CPtr for [u8; 32] {
type Target = u8;
fn as_c_ptr(&self) -> *const Self::Target {
self.as_ptr()
}

fn as_mut_c_ptr(&mut self) -> *mut Self::Target {
self.as_mut_ptr()
}
}

impl <T: CPtr> CPtr for Option<T> {
type Target = T::Target;
fn as_mut_c_ptr(&mut self) -> *mut Self::Target {
match self {
Some(contents) => contents.as_mut_c_ptr(),
None => ptr::null_mut(),
}
}
fn as_c_ptr(&self) -> *const Self::Target {
match self {
Some(content) => content.as_c_ptr(),
None => ptr::null(),
}
}
}

#[cfg(secp256k1_fuzz)]
mod fuzz_dummy {
use super::*;
Expand Down
3 changes: 3 additions & 0 deletions src/constants.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,9 @@ pub const SCHNORR_PUBLIC_KEY_SIZE: usize = 32;
/// The size of a key pair.
pub const KEY_PAIR_SIZE: usize = 96;

/// The size of a full ElligatorSwift encoding.
pub const ELLSWIFT_ENCODING_SIZE: usize = 64;

/// The Prime for the secp256k1 field element.
#[rustfmt::skip]
pub const FIELD_SIZE: [u8; 32] = [
Expand Down
Loading

0 comments on commit 3aada83

Please sign in to comment.