1use core::borrow::Borrow;
7use core::{ptr, str};
8
9use secp256k1_sys::types::{c_int, c_uchar, c_void};
10
11use crate::ffi::{self, CPtr};
12use crate::key::{PublicKey, SecretKey};
13use crate::{constants, Error};
14
15const SHARED_SECRET_SIZE: usize = constants::SECRET_KEY_SIZE;
17
18#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
35pub struct SharedSecret([u8; SHARED_SECRET_SIZE]);
36impl_display_secret!(SharedSecret);
37impl_non_secure_erase!(SharedSecret, 0, [0u8; SHARED_SECRET_SIZE]);
38
39impl SharedSecret {
40 #[inline]
42 pub fn new(point: &PublicKey, scalar: &SecretKey) -> SharedSecret {
43 let mut buf = [0u8; SHARED_SECRET_SIZE];
44 let res = unsafe {
45 ffi::secp256k1_ecdh(
46 ffi::secp256k1_context_no_precomp,
47 buf.as_mut_ptr(),
48 point.as_c_ptr(),
49 scalar.as_c_ptr(),
50 ffi::secp256k1_ecdh_hash_function_default,
51 ptr::null_mut(),
52 )
53 };
54 debug_assert_eq!(res, 1);
55 SharedSecret(buf)
56 }
57
58 #[inline]
60 pub fn secret_bytes(&self) -> [u8; SHARED_SECRET_SIZE] { self.0 }
61
62 #[inline]
64 pub fn from_bytes(bytes: [u8; SHARED_SECRET_SIZE]) -> SharedSecret { SharedSecret(bytes) }
65
66 #[deprecated(since = "TBD", note = "Use `from_bytes` instead.")]
68 #[inline]
69 pub fn from_slice(bytes: &[u8]) -> Result<SharedSecret, Error> {
70 match bytes.len() {
71 SHARED_SECRET_SIZE => {
72 let mut ret = [0u8; SHARED_SECRET_SIZE];
73 ret[..].copy_from_slice(bytes);
74 Ok(SharedSecret(ret))
75 }
76 _ => Err(Error::InvalidSharedSecret),
77 }
78 }
79}
80
81impl str::FromStr for SharedSecret {
82 type Err = Error;
83 fn from_str(s: &str) -> Result<SharedSecret, Error> {
84 let mut res = [0u8; SHARED_SECRET_SIZE];
85 match crate::from_hex(s, &mut res) {
86 Ok(SHARED_SECRET_SIZE) => Ok(SharedSecret::from_bytes(res)),
87 _ => Err(Error::InvalidSharedSecret),
88 }
89 }
90}
91
92impl Borrow<[u8]> for SharedSecret {
93 fn borrow(&self) -> &[u8] { &self.0 }
94}
95
96impl AsRef<[u8]> for SharedSecret {
97 fn as_ref(&self) -> &[u8] { &self.0 }
98}
99
100pub fn shared_secret_point(point: &PublicKey, scalar: &SecretKey) -> [u8; 64] {
130 let mut xy = [0u8; 64];
131
132 let res = unsafe {
133 ffi::secp256k1_ecdh(
134 ffi::secp256k1_context_no_precomp,
135 xy.as_mut_ptr(),
136 point.as_c_ptr(),
137 scalar.as_c_ptr(),
138 Some(c_callback),
139 ptr::null_mut(),
140 )
141 };
142 debug_assert_eq!(res, 1);
145 xy
146}
147
148unsafe extern "C" fn c_callback(
149 output: *mut c_uchar,
150 x: *const c_uchar,
151 y: *const c_uchar,
152 _data: *mut c_void,
153) -> c_int {
154 ptr::copy_nonoverlapping(x, output, 32);
155 ptr::copy_nonoverlapping(y, output.offset(32), 32);
156 1
157}
158
159#[cfg(feature = "serde")]
160impl ::serde::Serialize for SharedSecret {
161 fn serialize<S: ::serde::Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
162 if s.is_human_readable() {
163 let mut buf = [0u8; SHARED_SECRET_SIZE * 2];
164 s.serialize_str(crate::to_hex(&self.0, &mut buf).expect("fixed-size hex serialization"))
165 } else {
166 s.serialize_bytes(self.as_ref())
167 }
168 }
169}
170
171#[cfg(feature = "serde")]
172impl<'de> ::serde::Deserialize<'de> for SharedSecret {
173 fn deserialize<D: ::serde::Deserializer<'de>>(d: D) -> Result<Self, D::Error> {
174 if d.is_human_readable() {
175 d.deserialize_str(super::serde_util::FromStrVisitor::new(
176 "a hex string representing 32 byte SharedSecret",
177 ))
178 } else {
179 d.deserialize_bytes(super::serde_util::BytesVisitor::new(
180 "raw 32 bytes SharedSecret",
181 SharedSecret::from_slice,
182 ))
183 }
184 }
185}
186
187#[cfg(test)]
188#[allow(unused_imports)]
189mod tests {
190 #[cfg(target_arch = "wasm32")]
191 use wasm_bindgen_test::wasm_bindgen_test as test;
192
193 use super::SharedSecret;
194 use crate::Secp256k1;
195
196 #[test]
197 #[cfg(all(feature = "rand", feature = "std"))]
198 fn ecdh() {
199 let s = Secp256k1::signing_only();
200 let (sk1, pk1) = s.generate_keypair(&mut rand::thread_rng());
201 let (sk2, pk2) = s.generate_keypair(&mut rand::thread_rng());
202
203 let sec1 = SharedSecret::new(&pk2, &sk1);
204 let sec2 = SharedSecret::new(&pk1, &sk2);
205 let sec_odd = SharedSecret::new(&pk1, &sk1);
206 assert_eq!(sec1, sec2);
207 assert!(sec_odd != sec2);
208 }
209
210 #[test]
211 fn test_c_callback() {
212 let x = [5u8; 32];
213 let y = [7u8; 32];
214 let mut output = [0u8; 64];
215 let res = unsafe {
216 super::c_callback(output.as_mut_ptr(), x.as_ptr(), y.as_ptr(), core::ptr::null_mut())
217 };
218 assert_eq!(res, 1);
219 let mut new_x = [0u8; 32];
220 let mut new_y = [0u8; 32];
221 new_x.copy_from_slice(&output[..32]);
222 new_y.copy_from_slice(&output[32..]);
223 assert_eq!(x, new_x);
224 assert_eq!(y, new_y);
225 }
226
227 #[test]
228 #[cfg(not(secp256k1_fuzz))]
229 #[cfg(all(feature = "hashes", feature = "rand", feature = "std"))]
230 fn hashes_and_sys_generate_same_secret() {
231 use hashes::{sha256, Hash, HashEngine};
232
233 use crate::ecdh::shared_secret_point;
234
235 let s = Secp256k1::signing_only();
236 let (sk1, _) = s.generate_keypair(&mut rand::thread_rng());
237 let (_, pk2) = s.generate_keypair(&mut rand::thread_rng());
238
239 let secret_sys = SharedSecret::new(&pk2, &sk1);
240
241 let xy = shared_secret_point(&pk2, &sk1);
242
243 let version = (xy[63] & 0x01) | 0x02;
245 let mut engine = sha256::HashEngine::default();
246 engine.input(&[version]);
247 engine.input(&xy.as_ref()[..32]);
248 let secret_bh = sha256::Hash::from_engine(engine);
249
250 assert_eq!(secret_bh.as_byte_array(), secret_sys.as_ref());
251 }
252
253 #[test]
254 #[cfg(all(feature = "serde", feature = "alloc"))]
255 fn serde() {
256 use serde_test::{assert_tokens, Configure, Token};
257 #[rustfmt::skip]
258 static BYTES: [u8; 32] = [
259 1, 1, 1, 1, 1, 1, 1, 1,
260 0, 1, 2, 3, 4, 5, 6, 7,
261 0xff, 0xff, 0, 0, 0xff, 0xff, 0, 0,
262 99, 99, 99, 99, 99, 99, 99, 99
263 ];
264 static STR: &str = "01010101010101010001020304050607ffff0000ffff00006363636363636363";
265
266 let secret = SharedSecret::from_slice(&BYTES).unwrap();
267
268 assert_tokens(&secret.compact(), &[Token::BorrowedBytes(&BYTES[..])]);
269 assert_tokens(&secret.compact(), &[Token::Bytes(&BYTES)]);
270 assert_tokens(&secret.compact(), &[Token::ByteBuf(&BYTES)]);
271
272 assert_tokens(&secret.readable(), &[Token::BorrowedStr(STR)]);
273 assert_tokens(&secret.readable(), &[Token::Str(STR)]);
274 assert_tokens(&secret.readable(), &[Token::String(STR)]);
275 }
276}
277
278#[cfg(bench)]
279#[cfg(all(feature = "rand", feature = "std"))] mod benches {
281 use test::{black_box, Bencher};
282
283 use super::SharedSecret;
284 use crate::Secp256k1;
285
286 #[bench]
287 pub fn bench_ecdh(bh: &mut Bencher) {
288 let s = Secp256k1::signing_only();
289 let (sk, pk) = s.generate_keypair(&mut rand::thread_rng());
290
291 bh.iter(|| {
292 let res = SharedSecret::new(&pk, &sk);
293 black_box(res);
294 });
295 }
296}