16
16
//!
17
17
18
18
use core:: ptr;
19
- use core:: ops :: { FnMut , Deref } ;
19
+ use core:: borrow :: Borrow ;
20
20
21
21
use key:: { SecretKey , PublicKey } ;
22
22
use ffi:: { self , CPtr } ;
@@ -34,153 +34,99 @@ use secp256k1_sys::types::{c_int, c_uchar, c_void};
34
34
/// let s = Secp256k1::new();
35
35
/// let (sk1, pk1) = s.generate_keypair(&mut thread_rng());
36
36
/// 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 );
39
39
/// assert_eq!(sec1, sec2);
40
40
/// # }
41
41
// ```
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 ] ) ;
116
44
117
45
impl SharedSecret {
118
46
/// Creates a new shared secret from a pubkey and secret key.
119
47
#[ inline]
120
48
pub fn new ( point : & PublicKey , scalar : & SecretKey ) -> SharedSecret {
121
- let mut ss = SharedSecret :: empty ( ) ;
49
+ let mut buf = [ 0u8 ; 32 ] ;
122
50
let res = unsafe {
123
51
ffi:: secp256k1_ecdh (
124
52
ffi:: secp256k1_context_no_precomp,
125
- ss . get_data_mut_ptr ( ) ,
53
+ buf . as_mut_ptr ( ) ,
126
54
point. as_c_ptr ( ) ,
127
55
scalar. as_c_ptr ( ) ,
128
56
ffi:: secp256k1_ecdh_hash_function_default,
129
57
ptr:: null_mut ( ) ,
130
58
)
131
59
} ;
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
134
60
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)
137
62
}
63
+ }
138
64
65
+ impl Borrow < [ u8 ] > for SharedSecret {
66
+ fn borrow ( & self ) -> & [ u8 ] {
67
+ & self . 0
68
+ }
69
+ }
139
70
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
+ }
163
76
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
+ }
177
125
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
184
130
}
185
131
186
132
#[ cfg( test) ]
@@ -200,45 +146,13 @@ mod tests {
200
146
let ( sk1, pk1) = s. generate_keypair ( & mut thread_rng ( ) ) ;
201
147
let ( sk2, pk2) = s. generate_keypair ( & mut thread_rng ( ) ) ;
202
148
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 ) ;
205
151
let sec_odd = SharedSecret :: new ( & pk1, & sk1) ;
206
152
assert_eq ! ( sec1, sec2) ;
207
153
assert ! ( sec_odd != sec2) ;
208
154
}
209
155
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
-
242
156
#[ test]
243
157
fn test_c_callback ( ) {
244
158
let x = [ 5u8 ; 32 ] ;
@@ -253,6 +167,30 @@ mod tests {
253
167
assert_eq ! ( x, new_x) ;
254
168
assert_eq ! ( y, new_y) ;
255
169
}
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
+ }
256
194
}
257
195
258
196
#[ cfg( all( test, feature = "unstable" ) ) ]
0 commit comments