From 9759cb07f5dc0742ea85912981251a14444c85ca Mon Sep 17 00:00:00 2001 From: Elichai Turkel Date: Sun, 10 Nov 2019 00:02:44 +0200 Subject: [PATCH] Replace SharedSecret with a more generic alternative --- secp256k1-sys/src/lib.rs | 23 +----- secp256k1-sys/src/macros.rs | 1 + src/ecdh.rs | 135 +++++++++++++++++++----------------- src/macros.rs | 17 +++++ 4 files changed, 93 insertions(+), 83 deletions(-) diff --git a/secp256k1-sys/src/lib.rs b/secp256k1-sys/src/lib.rs index 3d189f5..c7b2196 100644 --- a/secp256k1-sys/src/lib.rs +++ b/secp256k1-sys/src/lib.rs @@ -72,7 +72,7 @@ pub type EcdhHashFn = unsafe extern "C" fn( x: *const c_uchar, y: *const c_uchar, data: *mut 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 @@ -134,25 +134,6 @@ impl Default for Signature { } } -/// Library-internal representation of an ECDH shared secret -#[repr(C)] -pub struct SharedSecret([c_uchar; 32]); -impl_array_newtype!(SharedSecret, c_uchar, 32); -impl_raw_debug!(SharedSecret); - -impl SharedSecret { - /// Create a new (zeroed) signature usable for the FFI interface - pub fn new() -> SharedSecret { SharedSecret([0; 32]) } - /// Create a new (uninitialized) signature usable for the FFI interface - #[deprecated(since = "0.15.3", note = "Please use the new function instead")] - pub unsafe fn blank() -> SharedSecret { SharedSecret::new() } -} - -impl Default for SharedSecret { - fn default() -> Self { - SharedSecret::new() - } -} #[cfg(not(feature = "fuzztarget"))] extern "C" { @@ -296,7 +277,7 @@ extern "C" { #[cfg_attr(not(feature = "external-symbols"), link_name = "rustsecp256k1_v0_1_0_ecdh")] pub fn secp256k1_ecdh( cx: *const Context, - output: *mut SharedSecret, + output: *mut c_uchar, pubkey: *const PublicKey, privkey: *const c_uchar, hashfp: EcdhHashFn, diff --git a/secp256k1-sys/src/macros.rs b/secp256k1-sys/src/macros.rs index 76c45bd..3263ff1 100644 --- a/secp256k1-sys/src/macros.rs +++ b/secp256k1-sys/src/macros.rs @@ -144,6 +144,7 @@ macro_rules! impl_array_newtype { } } +#[macro_export] macro_rules! impl_raw_debug { ($thing:ident) => { impl ::core::fmt::Debug for $thing { diff --git a/src/ecdh.rs b/src/ecdh.rs index ebe38b9..4b08dbb 100644 --- a/src/ecdh.rs +++ b/src/ecdh.rs @@ -16,83 +16,94 @@ //! Support for shared secret computations //! -use core::{ops, ptr}; +use core::ptr; +use core::ops::Deref; use key::{SecretKey, PublicKey}; use ffi::{self, CPtr}; /// A tag used for recovering the public key from a compact signature -#[derive(Copy, Clone, PartialEq, Eq, Debug)] -pub struct SharedSecret(ffi::SharedSecret); +#[derive(Copy, Clone)] +pub struct SharedSecret { + data: [u8; 256], + len: usize, +} +impl_raw_debug!(SharedSecret); + + +// This implementes `From` for all `[u8; N]` arrays from 128bits(16 byte) to 2048bits allowing known hash lengths. +// Lower than 128 bits isn't resistant to collisions any more. +impl_from_array_len!(SharedSecret, 256, (16 20 28 32 48 64 96 128 256)); + +impl SharedSecret { + + /// Create an empty SharedSecret + pub(crate) fn empty() -> SharedSecret { + SharedSecret { + data: [0u8; 256], + len: 0, + } + } + + /// Get a pointer to the underlying data with the specified capacity. + pub(crate) fn get_data_mut_ptr(&mut self) -> *mut u8 { + self.data.as_mut_ptr() + } + + /// Get the capacity of the underlying data buffer. + pub fn capacity(&self) -> usize { + self.data.len() + } + + /// Get the len of the used data. + pub fn len(&self) -> usize { + self.len + } + + /// Set the length of the object. + pub(crate) fn set_len(&mut self, len: usize) { + self.len = len; + } +} + +impl PartialEq for SharedSecret { + fn eq(&self, other: &SharedSecret) -> bool { + &self.data[..self.len] == &other.data[..other.len] + } +} + +impl AsRef<[u8]> for SharedSecret { + fn as_ref(&self) -> &[u8] { + &self.data[..self.len] + } +} + +impl Deref for SharedSecret { + type Target = [u8]; + fn deref(&self) -> &[u8] { + &self.data[..self.len] + } +} + impl SharedSecret { /// Creates a new shared secret from a pubkey and secret key #[inline] pub fn new(point: &PublicKey, scalar: &SecretKey) -> SharedSecret { - unsafe { - let mut ss = ffi::SharedSecret::new(); - let res = ffi::secp256k1_ecdh( + let mut ss = SharedSecret::empty(); + let res = unsafe { + ffi::secp256k1_ecdh( ffi::secp256k1_context_no_precomp, - &mut ss, + ss.get_data_mut_ptr(), point.as_c_ptr(), scalar.as_c_ptr(), ffi::secp256k1_ecdh_hash_function_default, ptr::null_mut(), - ); - debug_assert_eq!(res, 1); - SharedSecret(ss) - } - } - - /// Obtains a raw pointer suitable for use with FFI functions - #[inline] - pub fn as_ptr(&self) -> *const ffi::SharedSecret { - &self.0 as *const _ - } -} - -/// Creates a new shared secret from a FFI shared secret -impl From for SharedSecret { - #[inline] - fn from(ss: ffi::SharedSecret) -> SharedSecret { - SharedSecret(ss) - } -} - - -impl ops::Index for SharedSecret { - type Output = u8; - - #[inline] - fn index(&self, index: usize) -> &u8 { - &self.0[index] - } -} - -impl ops::Index> for SharedSecret { - type Output = [u8]; - - #[inline] - fn index(&self, index: ops::Range) -> &[u8] { - &self.0[index] - } -} - -impl ops::Index> for SharedSecret { - type Output = [u8]; - - #[inline] - fn index(&self, index: ops::RangeFrom) -> &[u8] { - &self.0[index.start..] - } -} - -impl ops::Index for SharedSecret { - type Output = [u8]; - - #[inline] - fn index(&self, _: ops::RangeFull) -> &[u8] { - &self.0[..] + ) + }; + debug_assert_eq!(res, 1); // The default `secp256k1_ecdh_hash_function_default` should always return 1. + ss.set_len(32); // The default hash function is SHA256, which is 32 bytes long. + ss } } diff --git a/src/macros.rs b/src/macros.rs index bf8fb0c..9cf9ba6 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -27,6 +27,23 @@ macro_rules! impl_pretty_debug { } } +macro_rules! impl_from_array_len { + ($thing:ident, $capacity:tt, ($($N:tt)+)) => { + $( + impl From<[u8; $N]> for $thing { + fn from(arr: [u8; $N]) -> Self { + let mut data = [0u8; $capacity]; + data[..$N].copy_from_slice(&arr); + $thing { + data, + len: $N, + } + } + } + )+ + } +} + #[cfg(feature="serde")] /// Implements `Serialize` and `Deserialize` for a type `$t` which represents /// a newtype over a byte-slice over length `$len`. Type `$t` must implement