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