1use std::fmt::Debug;
2use std::mem;
3
4use p256::ecdh;
5use structbuf::{Packer, Unpacker};
6use zeroize::{Zeroize, ZeroizeOnDrop};
7
8use crate::{debug_secret, Addr, AesCmac, Codec, Key, MacKey, Nonce, LTK};
9
10#[derive(Zeroize, ZeroizeOnDrop)]
12#[must_use]
13#[repr(transparent)]
14pub struct SecretKey(p256::NonZeroScalar);
15
16debug_secret!(SecretKey);
17
18impl SecretKey {
19 #[allow(clippy::new_without_default)]
21 #[inline(always)]
22 pub fn new() -> Self {
23 Self(p256::NonZeroScalar::random(&mut rand_core::OsRng))
24 }
25
26 pub fn public_key(&self) -> PublicKey {
28 use p256::elliptic_curve::sec1::{Coordinates::Uncompressed, ToEncodedPoint};
29 let p = p256::PublicKey::from_secret_scalar(&self.0).to_encoded_point(false);
30 match p.coordinates() {
31 Uncompressed { x, y } => PublicKey {
32 x: PublicKeyX(Coord(*x.as_ref())),
33 y: Coord(*y.as_ref()),
34 },
35 _ => unreachable!("invalid secret key"),
36 }
37 }
38
39 #[must_use]
43 pub fn dh_key(&self, pk: PublicKey) -> Option<DHKey> {
44 use p256::elliptic_curve::sec1::FromEncodedPoint;
45 if pk.is_debug() {
46 return None; }
48 let (x, y) = (&pk.x.0 .0.into(), &pk.y.0.into());
49 let rep = p256::EncodedPoint::from_affine_coordinates(x, y, false);
50 let lpk = p256::PublicKey::from_secret_scalar(&self.0);
51 let rpk = Option::from(p256::PublicKey::from_encoded_point(&rep)).unwrap_or(lpk);
54 (rpk != lpk).then(|| DHKey(ecdh::diffie_hellman(&self.0, rpk.as_affine())))
55 }
56}
57
58#[derive(Clone, Copy, Debug, Eq, PartialEq)]
60#[must_use]
61pub struct PublicKey {
62 x: PublicKeyX,
63 y: Coord,
64}
65
66impl PublicKey {
67 #[inline(always)]
69 pub const fn x(&self) -> &PublicKeyX {
70 &self.x
71 }
72
73 #[allow(clippy::unusual_byte_groupings)]
76 fn is_debug(&self) -> bool {
77 let (x, y) = (&self.x.0 .0, &self.y.0);
78 x[..16] == u128::to_be_bytes(0x20b003d2_f297be2c_5e2c83a7_e9f9a5b9)
79 && x[16..] == u128::to_be_bytes(0xeff49111_acf4fddb_cc030148_0e359de6)
80 && y[..16] == u128::to_be_bytes(0xdc809c49_652aeb6d_63329abf_5a52155c)
81 && y[16..] == u128::to_be_bytes(0x766345c2_8fed3024_741c8ed0_1589d28b)
82 }
83}
84
85impl Codec for PublicKey {
86 #[inline]
87 fn pack(&self, p: &mut Packer) {
88 let (mut x, mut y) = (self.x.0 .0, self.y.0);
89 x.reverse();
90 y.reverse();
91 p.put(x).put(y);
92 }
93
94 #[inline]
95 fn unpack(p: &mut Unpacker) -> Option<Self> {
96 let (mut x, mut y) = (PublicKeyX(Coord(p.bytes())), Coord(p.bytes()));
97 x.0 .0.reverse();
98 y.0.reverse();
99 Some(Self { x, y })
100 }
101}
102
103#[derive(Clone, Copy, Debug, Eq, PartialEq)]
105#[repr(transparent)]
106struct Coord([u8; 256 / u8::BITS as usize]);
107
108#[derive(Clone, Copy, Debug, Eq, PartialEq)]
110#[must_use]
111#[repr(transparent)]
112pub struct PublicKeyX(Coord);
113
114impl PublicKeyX {
115 #[cfg(test)]
117 #[inline]
118 pub(super) const fn from_be_bytes(x: [u8; mem::size_of::<Self>()]) -> Self {
119 Self(Coord(x))
120 }
121
122 #[inline(always)]
124 pub(super) const fn as_be_bytes(&self) -> &[u8; mem::size_of::<Self>()] {
125 &self.0 .0
126 }
127}
128
129#[derive(ZeroizeOnDrop)]
131#[must_use]
132#[repr(transparent)]
133pub struct DHKey(ecdh::SharedSecret);
134
135debug_secret!(DHKey);
136
137impl DHKey {
138 #[inline]
141 pub fn f5(&self, n1: Nonce, n2: Nonce, a1: Addr, a2: Addr) -> (MacKey, LTK) {
142 let n1 = n1.0.to_be_bytes();
143 let n2 = n2.0.to_be_bytes();
144 let half = |m: &mut AesCmac, counter: u8| {
145 m.update([counter])
146 .update(b"btle")
147 .update(n1)
148 .update(n2)
149 .update(a1.0)
150 .update(a2.0)
151 .update(256_u16.to_be_bytes())
152 .finalize_key()
153 };
154 let mut m = AesCmac::new(&Key::new(0x6C88_8391_AAF5_A538_6037_0BDB_5A60_83BE));
155 m.update(self.0.raw_secret_bytes());
156 let mut m = AesCmac::new(&m.finalize_key());
157 (MacKey(half(&mut m, 0)), LTK(u128::from(&half(&mut m, 1))))
158 }
159}
160
161#[allow(clippy::redundant_pub_crate)]
163#[cfg(test)]
164pub(super) fn u256<T: From<[u8; 32]>>(hi: u128, lo: u128) -> T {
165 let mut b = [0; 32];
166 b[..16].copy_from_slice(&hi.to_be_bytes());
167 b[16..].copy_from_slice(&lo.to_be_bytes());
168 T::from(b)
169}
170
171#[allow(clippy::unusual_byte_groupings)]
172#[cfg(test)]
173mod tests {
174 use structbuf::{Pack, StructBuf, Unpack};
175
176 use super::*;
177
178 #[test]
179 fn sizes() {
180 assert_eq!(mem::size_of::<Coord>(), 32);
181 assert_eq!(mem::size_of::<PublicKey>(), 64);
182 assert_eq!(mem::size_of::<SecretKey>(), 32);
183 assert_eq!(mem::size_of::<DHKey>(), 32);
184 }
185
186 #[test]
188 fn debug_key() {
189 let sk = secret_key(
190 0x3f49f6d4_a3c55f38_74c9b3e3_d2103f50,
191 0x4aff607b_eb40b799_5899b8a6_cd3c1abd,
192 );
193 let pk = PublicKey {
194 x: PublicKeyX(Coord(u256(
195 0x20b003d2_f297be2c_5e2c83a7_e9f9a5b9,
196 0xeff49111_acf4fddb_cc030148_0e359de6,
197 ))),
198 y: Coord(u256(
199 0xdc809c49_652aeb6d_63329abf_5a52155c,
200 0x766345c2_8fed3024_741c8ed0_1589d28b,
201 )),
202 };
203 assert_eq!(sk.public_key(), pk);
204 assert!(pk.is_debug());
205
206 let mut b = StructBuf::with_capacity(mem::size_of_val(&pk));
207 pk.pack(&mut b.append());
208 assert_eq!(b.unpack().u8(), 0xe6);
209 assert_eq!(PublicKey::unpack(&mut b.unpack()).unwrap(), pk);
210 }
211
212 #[test]
214 fn p256_1() {
215 let (ska, skb) = (
216 secret_key(
217 0x3f49f6d4_a3c55f38_74c9b3e3_d2103f50,
218 0x4aff607b_eb40b799_5899b8a6_cd3c1abd,
219 ),
220 secret_key(
221 0x55188b3d_32f6bb9a_900afcfb_eed4e72a,
222 0x59cb9ac2_f19d7cfb_6b4fdd49_f47fc5fd,
223 ),
224 );
225 let (pka, pkb) = (
226 PublicKey {
227 x: PublicKeyX(Coord(u256(
228 0x20b003d2_f297be2c_5e2c83a7_e9f9a5b9,
229 0xeff49111_acf4fddb_cc030148_0e359de6,
230 ))),
231 y: Coord(u256(
232 0xdc809c49_652aeb6d_63329abf_5a52155c,
233 0x766345c2_8fed3024_741c8ed0_1589d28b,
234 )),
235 },
236 PublicKey {
237 x: PublicKeyX(Coord(u256(
238 0x1ea1f0f0_1faf1d96_09592284_f19e4c00,
239 0x47b58afd_8615a69f_559077b2_2faaa190,
240 ))),
241 y: Coord(u256(
242 0x4c55f33e_429dad37_7356703a_9ab85160,
243 0x472d1130_e28e3676_5f89aff9_15b1214a,
244 )),
245 },
246 );
247 let dh_key = shared_secret(
248 0xec0234a3_57c8ad05_341010a6_0a397d9b,
249 0x99796b13_b4f866f1_868d34f3_73bfa698,
250 );
251 assert_eq!(ska.public_key(), pka);
252 assert_eq!(skb.public_key(), pkb);
253 assert_eq!(
254 ska.dh_key(pkb).unwrap().0.raw_secret_bytes(),
255 dh_key.0.raw_secret_bytes()
256 );
257
258 assert!(!pkb.is_debug());
259 assert!(skb.dh_key(pkb).is_none());
260 }
261
262 #[test]
264 fn p256_2() {
265 let (ska, skb) = (
266 secret_key(
267 0x06a51669_3c9aa31a_6084545d_0c5db641,
268 0xb48572b9_7203ddff_b7ac73f7_d0457663,
269 ),
270 secret_key(
271 0x529aa067_0d72cd64_97502ed4_73502b03,
272 0x7e8803b5_c60829a5_a3caa219_505530ba,
273 ),
274 );
275 let (pka, pkb) = (
276 PublicKey {
277 x: PublicKeyX(Coord(u256(
278 0x2c31a47b_5779809e_f44cb5ea_af5c3e43,
279 0xd5f8faad_4a8794cb_987e9b03_745c78dd,
280 ))),
281 y: Coord(u256(
282 0x91951218_3898dfbe_cd52e240_8e43871f,
283 0xd0211091_17bd3ed4_eaf84377_43715d4f,
284 )),
285 },
286 PublicKey {
287 x: PublicKeyX(Coord(u256(
288 0xf465e43f_f23d3f1b_9dc7dfc0_4da87581,
289 0x84dbc966_204796ec_cf0d6cf5_e16500cc,
290 ))),
291 y: Coord(u256(
292 0x0201d048_bcbbd899_eeefc424_164e33c2,
293 0x01c2b010_ca6b4d43_a8a155ca_d8ecb279,
294 )),
295 },
296 );
297 let dh_key = shared_secret(
298 0xab85843a_2f6d883f_62e5684b_38e30733,
299 0x5fe6e194_5ecd1960_4105c6f2_3221eb69,
300 );
301 assert_eq!(ska.public_key(), pka);
302 assert_eq!(skb.public_key(), pkb);
303 assert_eq!(
304 ska.dh_key(pkb).unwrap().0.raw_secret_bytes(),
305 dh_key.0.raw_secret_bytes()
306 );
307 }
308
309 #[test]
311 fn dh_key_f5() {
312 let w = shared_secret(
313 0xec0234a3_57c8ad05_341010a6_0a397d9b,
314 0x99796b13_b4f866f1_868d34f3_73bfa698,
315 );
316 let n1 = Nonce(0xd5cb8454_d177733e_ffffb2ec_712baeab);
317 let n2 = Nonce(0xa6e8e7cc_25a75f6e_216583f7_ff3dc4cf);
318 let a1 = Addr([0x00, 0x56, 0x12, 0x37, 0x37, 0xbf, 0xce]);
319 let a2 = Addr([0x00, 0xa7, 0x13, 0x70, 0x2d, 0xcf, 0xc1]);
320 let (mk, ltk) = w.f5(n1, n2, a1, a2);
321 assert_eq!(ltk.0, 0x69867911_69d7cd23_980522b5_94750a38);
322 assert_eq!(u128::from(&mk.0), 0x2965f176_a1084a02_fd3f6a20_ce636e20);
323 }
324
325 #[inline]
326 fn secret_key(hi: u128, lo: u128) -> SecretKey {
327 SecretKey(p256::NonZeroScalar::from_repr(u256(hi, lo)).unwrap())
328 }
329
330 #[inline]
331 fn shared_secret(hi: u128, lo: u128) -> DHKey {
332 DHKey(ecdh::SharedSecret::from(u256::<p256::FieldBytes>(hi, lo)))
333 }
334}