lakers_crypto_rustcrypto/
lib.rs

1#![no_std]
2
3use lakers_shared::{
4    BufferCiphertext3, BufferPlaintext3, BytesCcmIvLen, BytesCcmKeyLen, BytesHashLen,
5    BytesMaxBuffer, BytesMaxInfoBuffer, BytesP256ElemLen, Crypto as CryptoTrait, EDHOCError,
6    AES_CCM_TAG_LEN, MAX_BUFFER_LEN,
7};
8
9use ccm::AeadInPlace;
10use ccm::KeyInit;
11use p256::elliptic_curve::point::AffineCoordinates;
12use p256::elliptic_curve::point::DecompressPoint;
13use sha2::Digest;
14
15type AesCcm16_64_128 = ccm::Ccm<aes::Aes128, ccm::consts::U8, ccm::consts::U13>;
16
17/// A type representing cryptographic operations through various RustCrypto crates (eg. [aes],
18/// [ccm], [p256]).
19///
20/// Its size depends on the implementation of Rng passed in at creation.
21pub struct Crypto<Rng: rand_core::RngCore + rand_core::CryptoRng> {
22    rng: Rng,
23}
24
25impl<Rng: rand_core::RngCore + rand_core::CryptoRng> Crypto<Rng> {
26    pub const fn new(rng: Rng) -> Self {
27        Self { rng }
28    }
29}
30
31impl<Rng: rand_core::RngCore + rand_core::CryptoRng> core::fmt::Debug for Crypto<Rng> {
32    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> Result<(), core::fmt::Error> {
33        f.debug_struct("lakers_crypto_rustcrypto::Crypto")
34            .field("rng", &core::any::type_name::<Rng>())
35            .finish()
36    }
37}
38
39impl<Rng: rand_core::RngCore + rand_core::CryptoRng> CryptoTrait for Crypto<Rng> {
40    fn sha256_digest(&mut self, message: &BytesMaxBuffer, message_len: usize) -> BytesHashLen {
41        let mut hasher = sha2::Sha256::new();
42        hasher.update(&message[..message_len]);
43        hasher.finalize().into()
44    }
45
46    fn hkdf_expand(
47        &mut self,
48        prk: &BytesHashLen,
49        info: &BytesMaxInfoBuffer,
50        info_len: usize,
51        length: usize,
52    ) -> BytesMaxBuffer {
53        let hkdf =
54            hkdf::Hkdf::<sha2::Sha256>::from_prk(prk).expect("Static size was checked at extract");
55        let mut output: BytesMaxBuffer = [0; MAX_BUFFER_LEN];
56        hkdf.expand(&info[..info_len], &mut output[..length])
57            .expect("Static lengths match the algorithm");
58        output
59    }
60
61    fn hkdf_extract(&mut self, salt: &BytesHashLen, ikm: &BytesP256ElemLen) -> BytesHashLen {
62        // While it'd be nice to just pass around an Hkdf, the extract output is not a type generic
63        // of this trait (yet?).
64        let mut extracted = hkdf::HkdfExtract::<sha2::Sha256>::new(Some(salt));
65        extracted.input_ikm(ikm);
66        extracted.finalize().0.into()
67    }
68
69    fn aes_ccm_encrypt_tag_8(
70        &mut self,
71        key: &BytesCcmKeyLen,
72        iv: &BytesCcmIvLen,
73        ad: &[u8],
74        plaintext: &BufferPlaintext3,
75    ) -> BufferCiphertext3 {
76        let key = AesCcm16_64_128::new(key.into());
77        let mut outbuffer = BufferCiphertext3::new();
78        outbuffer.content[..plaintext.len].copy_from_slice(plaintext.as_slice());
79        if let Ok(tag) =
80            key.encrypt_in_place_detached(iv.into(), ad, &mut outbuffer.content[..plaintext.len])
81        {
82            outbuffer.content[plaintext.len..][..AES_CCM_TAG_LEN].copy_from_slice(&tag);
83        } else {
84            panic!("Preconfigured sizes should not allow encryption to fail")
85        }
86        outbuffer.len = plaintext.len + AES_CCM_TAG_LEN;
87        outbuffer
88    }
89
90    fn aes_ccm_decrypt_tag_8(
91        &mut self,
92        key: &BytesCcmKeyLen,
93        iv: &BytesCcmIvLen,
94        ad: &[u8],
95        ciphertext: &BufferCiphertext3,
96    ) -> Result<BufferPlaintext3, EDHOCError> {
97        let key = AesCcm16_64_128::new(key.into());
98        let mut buffer = BufferPlaintext3::new();
99        buffer.len = ciphertext.len - AES_CCM_TAG_LEN;
100        buffer.content[..buffer.len].copy_from_slice(&ciphertext.content[..buffer.len]);
101        let tag = &ciphertext.content[buffer.len..][..AES_CCM_TAG_LEN];
102        key.decrypt_in_place_detached(iv.into(), ad, &mut buffer.content[..buffer.len], tag.into())
103            .map_err(|_| EDHOCError::MacVerificationFailed)?;
104        Ok(buffer)
105    }
106
107    fn p256_ecdh(
108        &mut self,
109        private_key: &BytesP256ElemLen,
110        public_key: &BytesP256ElemLen,
111    ) -> BytesP256ElemLen {
112        let secret = p256::SecretKey::from_bytes(private_key.as_slice().into())
113            .expect("Invalid secret key generated");
114        let public = p256::AffinePoint::decompress(
115            public_key.into(),
116            1.into(), /* Y coordinate choice does not matter for ECDH operation */
117        )
118        // While this can actually panic so far, the proper fix is in
119        // https://github.com/openwsn-berkeley/lakers/issues/93 which will justify this to be a
120        // panic (because after that, public key validity will be an invariant of the public key
121        // type)
122        .expect("Public key is not a good point");
123
124        (*p256::ecdh::diffie_hellman(secret.to_nonzero_scalar(), public).raw_secret_bytes()).into()
125    }
126
127    fn get_random_byte(&mut self) -> u8 {
128        self.rng.next_u32() as _
129    }
130
131    fn p256_generate_key_pair(&mut self) -> (BytesP256ElemLen, BytesP256ElemLen) {
132        let secret = p256::SecretKey::random(&mut self.rng);
133
134        let public_key = secret.public_key().as_affine().x();
135        let private_key = secret.to_bytes();
136
137        (private_key.into(), public_key.into())
138    }
139}