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)]
33pub struct SharedSecret([u8; SHARED_SECRET_SIZE]);
34impl_display_secret!(SharedSecret);
35impl_non_secure_erase!(SharedSecret, 0, [0u8; SHARED_SECRET_SIZE]);
36
37impl SharedSecret {
38 #[inline]
40 pub fn new(point: &PublicKey, scalar: &SecretKey) -> SharedSecret {
41 let mut buf = [0u8; SHARED_SECRET_SIZE];
42 let res = unsafe {
43 ffi::secp256k1_ecdh(
44 ffi::secp256k1_context_no_precomp,
45 buf.as_mut_ptr(),
46 point.as_c_ptr(),
47 scalar.as_c_ptr(),
48 ffi::secp256k1_ecdh_hash_function_default,
49 ptr::null_mut(),
50 )
51 };
52 debug_assert_eq!(res, 1);
53 SharedSecret(buf)
54 }
55
56 #[inline]
58 pub fn to_secret_bytes(&self) -> [u8; SHARED_SECRET_SIZE] { self.0 }
59
60 #[deprecated(since = "TBD", note = "Use `to_secret_bytes` instead.")]
62 #[inline]
63 pub fn secret_bytes(&self) -> [u8; SHARED_SECRET_SIZE] { self.to_secret_bytes() }
64
65 #[inline]
67 pub fn from_bytes(bytes: [u8; SHARED_SECRET_SIZE]) -> SharedSecret { SharedSecret(bytes) }
68
69 #[deprecated(since = "0.31.0", note = "Use `from_bytes` instead.")]
71 #[inline]
72 pub fn from_slice(bytes: &[u8]) -> Result<SharedSecret, Error> {
73 match bytes.len() {
74 SHARED_SECRET_SIZE => {
75 let mut ret = [0u8; SHARED_SECRET_SIZE];
76 ret[..].copy_from_slice(bytes);
77 Ok(SharedSecret(ret))
78 }
79 _ => Err(Error::InvalidSharedSecret),
80 }
81 }
82}
83
84impl str::FromStr for SharedSecret {
85 type Err = Error;
86 fn from_str(s: &str) -> Result<SharedSecret, Error> {
87 let mut res = [0u8; SHARED_SECRET_SIZE];
88 match crate::from_hex(s, &mut res) {
89 Ok(SHARED_SECRET_SIZE) => Ok(SharedSecret::from_bytes(res)),
90 _ => Err(Error::InvalidSharedSecret),
91 }
92 }
93}
94
95impl Borrow<[u8]> for SharedSecret {
96 fn borrow(&self) -> &[u8] { &self.0 }
97}
98
99impl AsRef<[u8]> for SharedSecret {
100 fn as_ref(&self) -> &[u8] { &self.0 }
101}
102
103pub 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 |x| x.try_into().map(SharedSecret::from_bytes),
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
195 #[test]
196 fn ecdh() {
197 let (sk1, pk1) = crate::test_random_keypair();
198 let (sk2, pk2) = crate::test_random_keypair();
199
200 let sec1 = SharedSecret::new(&pk2, &sk1);
201 let sec2 = SharedSecret::new(&pk1, &sk2);
202 let sec_odd = SharedSecret::new(&pk1, &sk1);
203 assert_eq!(sec1, sec2);
204 assert!(sec_odd != sec2);
205 }
206
207 #[test]
208 fn test_c_callback() {
209 let x = [5u8; 32];
210 let y = [7u8; 32];
211 let mut output = [0u8; 64];
212 let res = unsafe {
213 super::c_callback(output.as_mut_ptr(), x.as_ptr(), y.as_ptr(), core::ptr::null_mut())
214 };
215 assert_eq!(res, 1);
216 let mut new_x = [0u8; 32];
217 let mut new_y = [0u8; 32];
218 new_x.copy_from_slice(&output[..32]);
219 new_y.copy_from_slice(&output[32..]);
220 assert_eq!(x, new_x);
221 assert_eq!(y, new_y);
222 }
223
224 #[test]
225 #[cfg(all(feature = "serde", feature = "alloc"))]
226 fn serde() {
227 use serde_test::{assert_tokens, Configure, Token};
228 #[rustfmt::skip]
229 static BYTES: [u8; 32] = [
230 1, 1, 1, 1, 1, 1, 1, 1,
231 0, 1, 2, 3, 4, 5, 6, 7,
232 0xff, 0xff, 0, 0, 0xff, 0xff, 0, 0,
233 99, 99, 99, 99, 99, 99, 99, 99
234 ];
235 static STR: &str = "01010101010101010001020304050607ffff0000ffff00006363636363636363";
236
237 let secret = SharedSecret::from_bytes(BYTES);
238
239 assert_tokens(&secret.compact(), &[Token::BorrowedBytes(&BYTES[..])]);
240 assert_tokens(&secret.compact(), &[Token::Bytes(&BYTES)]);
241 assert_tokens(&secret.compact(), &[Token::ByteBuf(&BYTES)]);
242
243 assert_tokens(&secret.readable(), &[Token::BorrowedStr(STR)]);
244 assert_tokens(&secret.readable(), &[Token::Str(STR)]);
245 assert_tokens(&secret.readable(), &[Token::String(STR)]);
246 }
247}
248
249#[cfg(bench)]
250#[cfg(all(feature = "rand", feature = "std"))] mod benches {
252 use test::{black_box, Bencher};
253
254 use super::SharedSecret;
255
256 #[bench]
257 pub fn bench_ecdh(bh: &mut Bencher) {
258 let (sk, pk) = s.generate_keypair(&mut rand::rng());
259
260 bh.iter(|| {
261 let res = SharedSecret::new(&pk, &sk);
262 black_box(res);
263 });
264 }
265}