Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 58 additions & 0 deletions src/ecdh.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,12 @@
//!

use core::{ops, ptr};
use core::ops::{FnMut};
use core::slice::{from_raw_parts, from_raw_parts_mut};

use key::{SecretKey, PublicKey};
use ffi::{self, CPtr};
use types::{c_int, c_uchar, c_void};

/// A tag used for recovering the public key from a compact signature
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
Expand All @@ -44,6 +47,31 @@ impl SharedSecret {
}
}

/// Creates a new shared secret from a pubkey and secret key with applied custom hash function
pub fn new_with_hash<F>(point: &PublicKey, scalar: &SecretKey, hash: &mut F) -> SharedSecret
where F: FnMut(&mut [u8], &[u8], &[u8]) -> i32
{
extern "C" fn hash_callback<F>(output: *mut c_uchar, x: *const c_uchar, y: *const c_uchar, data: *const c_void) -> c_int
where F: FnMut(&mut [u8], &[u8], &[u8]) -> i32
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would consider actually using i16 here. and casting it to c_int in the function. that way it should be compatible with any rust supporting architecture while being lossless.

{
let callback: &mut F = unsafe { &mut *(data as *mut F) };
unsafe { (*callback)(from_raw_parts_mut(output, 32), from_raw_parts(x, 32), from_raw_parts(y, 32)) }
}
unsafe {
let mut ss = ffi::SharedSecret::new();
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please minimize the code inside the unsafe block to the minimum possible needed (i.e. the ffi call only)

let res = ffi::secp256k1_ecdh(
ffi::secp256k1_context_no_precomp,
&mut ss,
point.as_ptr(),
scalar.as_ptr(),
hash_callback::<F>,
hash as *mut F as *mut c_void,
);
debug_assert_eq!(res, 1);
SharedSecret::from(ss)
}
}

/// Obtains a raw pointer suitable for use with FFI functions
#[inline]
pub fn as_ptr(&self) -> *const ffi::SharedSecret {
Expand Down Expand Up @@ -114,6 +142,36 @@ mod tests {
assert_eq!(sec1, sec2);
assert!(sec_odd != sec2);
}

#[test]
fn ecdh_with_hash() {
let s = Secp256k1::signing_only();
let (sk1, pk1) = s.generate_keypair(&mut thread_rng());
let (sk2, pk2) = s.generate_keypair(&mut thread_rng());

let sec1 = SharedSecret::new_with_hash(&pk1, &sk2, &mut hash);
let sec2 = SharedSecret::new_with_hash(&pk2, &sk1, &mut hash);
let sec_odd = SharedSecret::new_with_hash(&pk1, &sk1, &mut hash);
assert_eq!(sec1, sec2);
assert!(sec_odd != sec2);
}

fn hash(output: &mut [u8], x: &[u8], _y: &[u8]) -> i32 {
output.copy_from_slice(x);
1
}

#[test]
fn ecdh_with_hash_callback() {
let s = Secp256k1::signing_only();
let (sk1, pk1) = s.generate_keypair(&mut thread_rng());
let expect_result: &[u8] = &[123u8;32];
let result = SharedSecret::new_with_hash(&pk1, &sk1, &mut |output, _, _ | {
output.copy_from_slice(expect_result);
1
});
assert_eq!(expect_result, &result[..]);
}
}

#[cfg(all(test, feature = "unstable"))]
Expand Down
2 changes: 1 addition & 1 deletion src/ffi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ pub type EcdhHashFn = unsafe extern "C" fn(
x: *const c_uchar,
y: *const c_uchar,
data: *const c_void,
);
) -> c_int;

/// A Secp256k1 context, containing various precomputed values and such
/// needed to do elliptic curve computations. If you create one of these
Expand Down