1616//!
1717
1818use core:: ptr;
19- use core:: ops :: { FnMut , Deref } ;
19+ use core:: borrow :: Borrow ;
2020
2121use key:: { SecretKey , PublicKey } ;
2222use ffi:: { self , CPtr } ;
@@ -34,153 +34,99 @@ use secp256k1_sys::types::{c_int, c_uchar, c_void};
3434/// let s = Secp256k1::new();
3535/// let (sk1, pk1) = s.generate_keypair(&mut thread_rng());
3636/// let (sk2, pk2) = s.generate_keypair(&mut thread_rng());
37- /// let sec1 = SharedSecret::new(&pk1 , &sk2 );
38- /// let sec2 = SharedSecret::new(&pk2 , &sk1 );
37+ /// let sec1 = SharedSecret::new(&pk2 , &sk1 );
38+ /// let sec2 = SharedSecret::new(&pk1 , &sk2 );
3939/// assert_eq!(sec1, sec2);
4040/// # }
4141// ```
42- #[ derive( Copy , Clone ) ]
43- pub struct SharedSecret {
44- data : [ u8 ; 256 ] ,
45- len : usize ,
46- }
47- impl_raw_debug ! ( SharedSecret ) ;
48-
49-
50- // This implementes `From<N>` for all `[u8; N]` arrays from 128bits(16 byte) to 2048bits allowing known hash lengths.
51- // Lower than 128 bits isn't resistant to collisions any more.
52- impl_from_array_len ! ( SharedSecret , 256 , ( 16 20 28 32 48 64 96 128 256 ) ) ;
53-
54- impl SharedSecret {
55-
56- /// Creates an empty `SharedSecret`.
57- pub ( crate ) fn empty ( ) -> SharedSecret {
58- SharedSecret {
59- data : [ 0u8 ; 256 ] ,
60- len : 0 ,
61- }
62- }
63-
64- /// Gets a pointer to the underlying data with the specified capacity.
65- pub ( crate ) fn get_data_mut_ptr ( & mut self ) -> * mut u8 {
66- self . data . as_mut_ptr ( )
67- }
68-
69- /// Gets the capacity of the underlying data buffer.
70- pub fn capacity ( & self ) -> usize {
71- self . data . len ( )
72- }
73-
74- /// Gets the len of the used data.
75- pub fn len ( & self ) -> usize {
76- self . len
77- }
78-
79- /// Returns true if the underlying data buffer is empty.
80- pub fn is_empty ( & self ) -> bool {
81- self . data . is_empty ( )
82- }
83-
84- /// Sets the length of the object.
85- pub ( crate ) fn set_len ( & mut self , len : usize ) {
86- debug_assert ! ( len <= self . data. len( ) ) ;
87- self . len = len;
88- }
89- }
90-
91- impl PartialEq for SharedSecret {
92- fn eq ( & self , other : & SharedSecret ) -> bool {
93- self . as_ref ( ) == other. as_ref ( )
94- }
95- }
96-
97- impl AsRef < [ u8 ] > for SharedSecret {
98- fn as_ref ( & self ) -> & [ u8 ] {
99- & self . data [ ..self . len ]
100- }
101- }
102-
103- impl Deref for SharedSecret {
104- type Target = [ u8 ] ;
105- fn deref ( & self ) -> & [ u8 ] {
106- & self . data [ ..self . len ]
107- }
108- }
109-
110-
111- unsafe extern "C" fn c_callback ( output : * mut c_uchar , x : * const c_uchar , y : * const c_uchar , _data : * mut c_void ) -> c_int {
112- ptr:: copy_nonoverlapping ( x, output, 32 ) ;
113- ptr:: copy_nonoverlapping ( y, output. offset ( 32 ) , 32 ) ;
114- 1
115- }
42+ #[ derive( Copy , Clone , Debug , PartialEq , Eq , PartialOrd , Ord , Hash ) ]
43+ pub struct SharedSecret ( [ u8 ; 32 ] ) ;
11644
11745impl SharedSecret {
11846 /// Creates a new shared secret from a pubkey and secret key.
11947 #[ inline]
12048 pub fn new ( point : & PublicKey , scalar : & SecretKey ) -> SharedSecret {
121- let mut ss = SharedSecret :: empty ( ) ;
49+ let mut buf = [ 0u8 ; 32 ] ;
12250 let res = unsafe {
12351 ffi:: secp256k1_ecdh (
12452 ffi:: secp256k1_context_no_precomp,
125- ss . get_data_mut_ptr ( ) ,
53+ buf . as_mut_ptr ( ) ,
12654 point. as_c_ptr ( ) ,
12755 scalar. as_c_ptr ( ) ,
12856 ffi:: secp256k1_ecdh_hash_function_default,
12957 ptr:: null_mut ( ) ,
13058 )
13159 } ;
132- // The default `secp256k1_ecdh_hash_function_default` should always return 1.
133- // and the scalar was verified to be valid(0 > scalar > group_order) via the type system
13460 debug_assert_eq ! ( res, 1 ) ;
135- ss. set_len ( 32 ) ; // The default hash function is SHA256, which is 32 bytes long.
136- ss
61+ SharedSecret ( buf)
13762 }
63+ }
13864
65+ impl Borrow < [ u8 ] > for SharedSecret {
66+ fn borrow ( & self ) -> & [ u8 ] {
67+ & self . 0
68+ }
69+ }
13970
140- /// Creates a new shared secret from a pubkey and secret key with applied custom hash function.
141- /// The custom hash function must be in the form of `fn(x: [u8;32], y: [u8;32]) -> SharedSecret`
142- /// `SharedSecret` can be easily created via the `From` impl from arrays.
143- /// # Examples
144- /// ```
145- /// # #[cfg(any(feature = "alloc", features = "std"))] {
146- /// # use secp256k1::ecdh::SharedSecret;
147- /// # use secp256k1::{Secp256k1, PublicKey, SecretKey};
148- /// # fn sha2(_a: &[u8], _b: &[u8]) -> [u8; 32] {[0u8; 32]}
149- /// # let secp = Secp256k1::signing_only();
150- /// # let secret_key = SecretKey::from_slice(&[3u8; 32]).unwrap();
151- /// # let secret_key2 = SecretKey::from_slice(&[7u8; 32]).unwrap();
152- /// # let public_key = PublicKey::from_secret_key(&secp, &secret_key2);
153- ///
154- /// let secret = SharedSecret::new_with_hash(&public_key, &secret_key, |x,y| {
155- /// let hash: [u8; 32] = sha2(&x,&y);
156- /// hash.into()
157- /// });
158- /// # }
159- /// ```
160- pub fn new_with_hash < F > ( point : & PublicKey , scalar : & SecretKey , mut hash_function : F ) -> SharedSecret
161- where F : FnMut ( [ u8 ; 32 ] , [ u8 ; 32 ] ) -> SharedSecret {
162- let mut xy = [ 0u8 ; 64 ] ;
71+ impl AsRef < [ u8 ] > for SharedSecret {
72+ fn as_ref ( & self ) -> & [ u8 ] {
73+ & self . 0
74+ }
75+ }
16376
164- let res = unsafe {
165- ffi:: secp256k1_ecdh (
166- ffi:: secp256k1_context_no_precomp,
167- xy. as_mut_ptr ( ) ,
168- point. as_ptr ( ) ,
169- scalar. as_ptr ( ) ,
170- Some ( c_callback) ,
171- ptr:: null_mut ( ) ,
172- )
173- } ;
174- // Our callback *always* returns 1.
175- // and the scalar was verified to be valid(0 > scalar > group_order) via the type system
176- debug_assert_eq ! ( res, 1 ) ;
77+ /// Creates a shared point from public key and secret key.
78+ ///
79+ /// **Important: use of a strong cryptographic hash function may be critical to security! Do NOT use
80+ /// unless you understand cryptographical implications.** If not, use SharedSecret instead.
81+ ///
82+ /// Can be used like `SharedSecret` but caller is responsible for then hashing the returned buffer.
83+ /// This allows for the use of a custom hash function since `SharedSecret` uses SHA256.
84+ ///
85+ /// # Returns
86+ ///
87+ /// 64 bytes representing the (x,y) co-ordinates of a point on the curve (32 bytes each).
88+ ///
89+ /// # Examples
90+ /// ```
91+ /// # #[cfg(all(feature = "bitcoin_hashes", feature = "rand-std", feature = "std"))] {
92+ /// # use secp256k1::{ecdh, Secp256k1, PublicKey, SecretKey};
93+ /// # use secp256k1::hashes::{Hash, sha512};
94+ /// # use secp256k1::rand::thread_rng;
95+ ///
96+ /// let s = Secp256k1::new();
97+ /// let (sk1, pk1) = s.generate_keypair(&mut thread_rng());
98+ /// let (sk2, pk2) = s.generate_keypair(&mut thread_rng());
99+ ///
100+ /// let point1 = ecdh::shared_secret_point(&pk2, &sk1);
101+ /// let secret1 = sha512::Hash::hash(&point1);
102+ /// let point2 = ecdh::shared_secret_point(&pk1, &sk2);
103+ /// let secret2 = sha512::Hash::hash(&point2);
104+ /// assert_eq!(secret1, secret2)
105+ /// # }
106+ /// ```
107+ pub fn shared_secret_point ( point : & PublicKey , scalar : & SecretKey ) -> [ u8 ; 64 ] {
108+ let mut xy = [ 0u8 ; 64 ] ;
109+
110+ let res = unsafe {
111+ ffi:: secp256k1_ecdh (
112+ ffi:: secp256k1_context_no_precomp,
113+ xy. as_mut_ptr ( ) ,
114+ point. as_ptr ( ) ,
115+ scalar. as_ptr ( ) ,
116+ Some ( c_callback) ,
117+ ptr:: null_mut ( ) ,
118+ )
119+ } ;
120+ // Our callback *always* returns 1.
121+ // The scalar was verified to be valid (0 > scalar > group_order) via the type system.
122+ debug_assert_eq ! ( res, 1 ) ;
123+ xy
124+ }
177125
178- let mut x = [ 0u8 ; 32 ] ;
179- let mut y = [ 0u8 ; 32 ] ;
180- x. copy_from_slice ( & xy[ ..32 ] ) ;
181- y. copy_from_slice ( & xy[ 32 ..] ) ;
182- hash_function ( x, y)
183- }
126+ unsafe extern "C" fn c_callback ( output : * mut c_uchar , x : * const c_uchar , y : * const c_uchar , _data : * mut c_void ) -> c_int {
127+ ptr:: copy_nonoverlapping ( x, output, 32 ) ;
128+ ptr:: copy_nonoverlapping ( y, output. offset ( 32 ) , 32 ) ;
129+ 1
184130}
185131
186132#[ cfg( test) ]
@@ -200,45 +146,13 @@ mod tests {
200146 let ( sk1, pk1) = s. generate_keypair ( & mut thread_rng ( ) ) ;
201147 let ( sk2, pk2) = s. generate_keypair ( & mut thread_rng ( ) ) ;
202148
203- let sec1 = SharedSecret :: new ( & pk1 , & sk2 ) ;
204- let sec2 = SharedSecret :: new ( & pk2 , & sk1 ) ;
149+ let sec1 = SharedSecret :: new ( & pk2 , & sk1 ) ;
150+ let sec2 = SharedSecret :: new ( & pk1 , & sk2 ) ;
205151 let sec_odd = SharedSecret :: new ( & pk1, & sk1) ;
206152 assert_eq ! ( sec1, sec2) ;
207153 assert ! ( sec_odd != sec2) ;
208154 }
209155
210- #[ test]
211- #[ cfg( all( feature="std" , feature = "rand-std" ) ) ]
212- fn ecdh_with_hash ( ) {
213- let s = Secp256k1 :: signing_only ( ) ;
214- let ( sk1, pk1) = s. generate_keypair ( & mut thread_rng ( ) ) ;
215- let ( sk2, pk2) = s. generate_keypair ( & mut thread_rng ( ) ) ;
216-
217- let sec1 = SharedSecret :: new_with_hash ( & pk1, & sk2, |x, _| x. into ( ) ) ;
218- let sec2 = SharedSecret :: new_with_hash ( & pk2, & sk1, |x, _| x. into ( ) ) ;
219- let sec_odd = SharedSecret :: new_with_hash ( & pk1, & sk1, |x, _| x. into ( ) ) ;
220- assert_eq ! ( sec1, sec2) ;
221- assert_ne ! ( sec_odd, sec2) ;
222- }
223-
224- #[ test]
225- #[ cfg( all( feature="std" , feature = "rand-std" ) ) ]
226- fn ecdh_with_hash_callback ( ) {
227- let s = Secp256k1 :: signing_only ( ) ;
228- let ( sk1, pk1) = s. generate_keypair ( & mut thread_rng ( ) ) ;
229- let expect_result: [ u8 ; 64 ] = [ 123 ; 64 ] ;
230- let mut x_out = [ 0u8 ; 32 ] ;
231- let mut y_out = [ 0u8 ; 32 ] ;
232- let result = SharedSecret :: new_with_hash ( & pk1, & sk1, |x, y| {
233- x_out = x;
234- y_out = y;
235- expect_result. into ( )
236- } ) ;
237- assert_eq ! ( & expect_result[ ..] , & result[ ..] ) ;
238- assert_ne ! ( x_out, [ 0u8 ; 32 ] ) ;
239- assert_ne ! ( y_out, [ 0u8 ; 32 ] ) ;
240- }
241-
242156 #[ test]
243157 fn test_c_callback ( ) {
244158 let x = [ 5u8 ; 32 ] ;
@@ -253,6 +167,30 @@ mod tests {
253167 assert_eq ! ( x, new_x) ;
254168 assert_eq ! ( y, new_y) ;
255169 }
170+
171+ #[ test]
172+ #[ cfg( not( fuzzing) ) ]
173+ #[ cfg( all( feature="rand-std" , feature = "std" , feature = "bitcoin_hashes" ) ) ]
174+ fn bitcoin_hashes_and_sys_generate_same_secret ( ) {
175+ use hashes:: { sha256, Hash , HashEngine } ;
176+
177+ let s = Secp256k1 :: signing_only ( ) ;
178+ let ( sk1, _) = s. generate_keypair ( & mut thread_rng ( ) ) ;
179+ let ( _, pk2) = s. generate_keypair ( & mut thread_rng ( ) ) ;
180+
181+ let secret_sys = SharedSecret :: new ( & pk2, & sk1) ;
182+
183+ let xy = shared_secret_point ( & pk2, & sk1) ;
184+
185+ // Mimics logic in `bitcoin-core/secp256k1/src/module/main_impl.h`
186+ let version = ( xy[ 63 ] & 0x01 ) | 0x02 ;
187+ let mut engine = sha256:: HashEngine :: default ( ) ;
188+ engine. input ( & [ version] ) ;
189+ engine. input ( & xy. as_ref ( ) [ ..32 ] ) ;
190+ let secret_bh = sha256:: Hash :: from_engine ( engine) ;
191+
192+ assert_eq ! ( secret_bh. as_inner( ) , secret_sys. as_ref( ) ) ;
193+ }
256194}
257195
258196#[ cfg( all( test, feature = "unstable" ) ) ]
0 commit comments