burble_crypto/
p256.rs

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/// P-256 elliptic curve secret key.
11#[derive(Zeroize, ZeroizeOnDrop)]
12#[must_use]
13#[repr(transparent)]
14pub struct SecretKey(p256::NonZeroScalar);
15
16debug_secret!(SecretKey);
17
18impl SecretKey {
19    /// Generates a new random secret key.
20    #[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    /// Computes the associated public key.
27    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    /// Computes a shared secret from the local secret key and remote public
40    /// key. Returns [`None`] if the public key is either invalid or derived
41    /// from the same secret key ([Vol 3] Part H, Section 2.3.5.6.1).
42    #[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; // TODO: Compile-time option for debug-only mode
47        }
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        // Constant-time ops not required:
52        // https://github.com/RustCrypto/traits/issues/1227
53        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/// P-256 elliptic curve public key ([Vol 3] Part H, Section 3.5.6).
59#[derive(Clone, Copy, Debug, Eq, PartialEq)]
60#[must_use]
61pub struct PublicKey {
62    x: PublicKeyX,
63    y: Coord,
64}
65
66impl PublicKey {
67    /// Returns the public key X coordinate.
68    #[inline(always)]
69    pub const fn x(&self) -> &PublicKeyX {
70        &self.x
71    }
72
73    /// Returns whether `self` is the debug public key
74    /// ([Vol 3] Part H, Section 2.3.5.6.1).
75    #[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/// 256-bit elliptic curve coordinate in big-endian byte order.
104#[derive(Clone, Copy, Debug, Eq, PartialEq)]
105#[repr(transparent)]
106struct Coord([u8; 256 / u8::BITS as usize]);
107
108/// P-256 elliptic curve public key affine X coordinate.
109#[derive(Clone, Copy, Debug, Eq, PartialEq)]
110#[must_use]
111#[repr(transparent)]
112pub struct PublicKeyX(Coord);
113
114impl PublicKeyX {
115    /// Creates the coordinate from a big-endian encoded byte array.
116    #[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    /// Returns the coordinate in big-endian byte order.
123    #[inline(always)]
124    pub(super) const fn as_be_bytes(&self) -> &[u8; mem::size_of::<Self>()] {
125        &self.0 .0
126    }
127}
128
129/// P-256 elliptic curve shared secret ([Vol 3] Part H, Section 2.3.5.6.1).
130#[derive(ZeroizeOnDrop)]
131#[must_use]
132#[repr(transparent)]
133pub struct DHKey(ecdh::SharedSecret);
134
135debug_secret!(DHKey);
136
137impl DHKey {
138    /// Generates LE Secure Connections `MacKey` and `LTK`
139    /// ([Vol 3] Part H, Section 2.2.7).
140    #[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/// Combines `hi` and `lo` values into a big-endian byte array.
162#[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    /// Debug mode key ([Vol 3] Part H, Section 2.3.5.6.1).
187    #[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    /// P-256 data set 1 ([Vol 2] Part G, Section 7.1.2.1).
213    #[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    /// P-256 data set 2 ([Vol 2] Part G, Section 7.1.2.2).
263    #[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    /// Key generation function ([Vol 3] Part H, Section D.3).
310    #[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}