aranya_crypto_core/
rust.rs

1//! [RustCrypto] cryptography.
2//!
3//! [RustCrypto]: https://github.com/RustCrypto
4
5use core::{borrow::Borrow, fmt, result::Result};
6
7use aes_gcm::{
8    aead::{AeadInPlace, KeyInit, KeySizeUser},
9    A_MAX, C_MAX, P_MAX,
10};
11use crypto_common::BlockSizeUser;
12use ecdsa::{
13    der,
14    signature::{Signer as Signer_, Verifier},
15};
16use elliptic_curve::{
17    ecdh,
18    scalar::NonZeroScalar,
19    sec1::{EncodedPoint, FromEncodedPoint, ToEncodedPoint},
20    CurveArithmetic, FieldBytesSize,
21};
22use rand_core::{impls, CryptoRng, RngCore};
23use sha2::digest::OutputSizeUser;
24use subtle::{Choice, ConstantTimeEq};
25use typenum::{Unsigned, U12, U16};
26
27use crate::{
28    aead::{
29        check_open_in_place_params, check_seal_in_place_params, Aead, AeadId, AeadKey, IndCca2,
30        Lifetime, OpenError, SealError,
31    },
32    csprng::Csprng,
33    ec::{Curve, Secp256r1, Secp384r1},
34    hash::{Block, Digest, Hash, HashId},
35    hex,
36    hkdf::hkdf_impl,
37    hmac::hmac_impl,
38    import::{try_from_slice, ExportError, Import, ImportError},
39    kem::{dhkem_impl, DecapKey, Ecdh, EcdhError, EncapKey},
40    keys::{PublicKey, SecretKey, SecretKeyBytes},
41    signer::{Signature, Signer, SignerError, SignerId, SigningKey, VerifyingKey},
42    zeroize::ZeroizeOnDrop,
43};
44
45/// AES-256-GCM.
46#[cfg_attr(feature = "clone-aead", derive(Clone))]
47pub struct Aes256Gcm(aes_gcm::Aes256Gcm);
48
49impl Aead for Aes256Gcm {
50    const ID: AeadId = AeadId::Aes256Gcm;
51
52    // Assumes a random nonce.
53    const LIFETIME: Lifetime = Lifetime::Messages(u32::MAX as u64);
54
55    type KeySize = <aes_gcm::Aes256Gcm as KeySizeUser>::KeySize;
56    type NonceSize = U12;
57    type Overhead = U16; // tag only
58
59    const MAX_PLAINTEXT_SIZE: u64 = P_MAX;
60    const MAX_ADDITIONAL_DATA_SIZE: u64 = A_MAX;
61    const MAX_CIPHERTEXT_SIZE: u64 = C_MAX;
62
63    type Key = AeadKey<Self::KeySize>;
64
65    #[inline]
66    fn new(key: &Self::Key) -> Self {
67        let key: &[u8; 32] = key.as_array();
68        Self(aes_gcm::Aes256Gcm::new(key.into()))
69    }
70
71    fn seal_in_place(
72        &self,
73        nonce: &[u8],
74        data: &mut [u8],
75        tag: &mut [u8],
76        additional_data: &[u8],
77    ) -> Result<(), SealError> {
78        check_seal_in_place_params::<Self>(nonce, data, tag, additional_data)?;
79
80        let got_tag = self
81            .0
82            .encrypt_in_place_detached(
83                // From<&[T]> for GenericArray<T, _> panics on incorrect length
84                #[allow(clippy::unnecessary_fallible_conversions)]
85                nonce
86                    .try_into()
87                    .map_err(|_| SealError::InvalidNonceSize(InvalidNonceSize))?,
88                additional_data,
89                data,
90            )
91            .map_err(|_| SealError::Encryption)?;
92        tag.copy_from_slice(&got_tag[..]);
93
94        Ok(())
95    }
96
97    fn open_in_place(
98        &self,
99        nonce: &[u8],
100        data: &mut [u8],
101        tag: &[u8],
102        additional_data: &[u8],
103    ) -> Result<(), OpenError> {
104        check_open_in_place_params::<Self>(nonce, data, tag, additional_data)?;
105
106        self.0
107            .decrypt_in_place_detached(
108                // From<&[T]> for GenericArray<T, _> panics on incorrect length
109                #[allow(clippy::unnecessary_fallible_conversions)]
110                nonce
111                    .try_into()
112                    .map_err(|_| OpenError::InvalidNonceSize(InvalidNonceSize))?,
113                additional_data,
114                data,
115                // From<&[T]> for GenericArray<T, _> panics on incorrect length
116                #[allow(clippy::unnecessary_fallible_conversions)]
117                tag.try_into().map_err(|_| OpenError::InvalidOverheadSize)?,
118            )
119            .map_err(|_| OpenError::Authentication)
120    }
121}
122
123impl IndCca2 for Aes256Gcm {}
124
125#[cfg(feature = "committing-aead")]
126mod committing {
127    use aes::cipher::{BlockEncrypt, BlockSizeUser, KeyInit};
128    use generic_array::GenericArray;
129    use typenum::{Unsigned, U32};
130
131    use super::{Aes256Gcm, Sha256};
132    use crate::aead::{AeadKey, BlockCipher};
133
134    /// AES-256.
135    #[doc(hidden)]
136    pub struct Aes256(aes::Aes256);
137
138    impl BlockCipher for Aes256 {
139        type BlockSize = <aes::Aes256 as BlockSizeUser>::BlockSize;
140        const BLOCK_SIZE: usize = Self::BlockSize::USIZE;
141        type Key = AeadKey<U32>;
142
143        fn new(key: &Self::Key) -> Self {
144            let key: &[u8; 32] = key.as_array();
145            let cipher = <aes::Aes256 as KeyInit>::new(key.into());
146            Self(cipher)
147        }
148
149        fn encrypt_block(&self, block: &mut GenericArray<u8, Self::BlockSize>) {
150            // Mismatched GenericArray versions, yay.
151            let block: &mut [u8; 16] = block.as_mut();
152            self.0.encrypt_block(block.into())
153        }
154    }
155
156    crate::aead::utc_aead!(Cmt1Aes256Gcm, Aes256Gcm, Aes256, "CMT-1 AES-256-GCM.");
157    crate::aead::hte_aead!(Cmt4Aes256Gcm, Cmt1Aes256Gcm, Sha256, "CMT-4 AES-256-GCM.");
158}
159#[cfg(feature = "committing-aead")]
160#[cfg_attr(docsrs, doc(cfg(feature = "committing-aead")))]
161pub use committing::*;
162
163use crate::aead::InvalidNonceSize;
164
165macro_rules! curve_impl {
166    ($name:ident, $doc:expr, $inner:ty, $point:ident, $curve:ident) => {
167        #[doc = concat!($doc, ".")]
168        pub use $inner as $name;
169
170        impl Curve for $inner {
171            type ScalarSize = <$curve as Curve>::ScalarSize;
172            type CompressedSize = <$curve as Curve>::CompressedSize;
173            type UncompressedSize = <$curve as Curve>::UncompressedSize;
174        }
175
176        #[doc = concat!("An encoded ", $doc, "point.")]
177        #[derive(Copy, Clone)]
178        pub struct $point(EncodedPoint<$inner>);
179
180        impl Borrow<[u8]> for $point {
181            fn borrow(&self) -> &[u8] {
182                self.0.as_bytes()
183            }
184        }
185
186        impl<'a> Import<&'a [u8]> for $point {
187            fn import(data: &'a [u8]) -> Result<Self, ImportError> {
188                let point = EncodedPoint::<$inner>::from_bytes(data)
189                    .map_err(|_| ImportError::InvalidSyntax)?;
190                Ok(Self(point))
191            }
192        }
193    };
194}
195curve_impl!(P256, "NIST-P256", p256::NistP256, P256Point, Secp256r1);
196curve_impl!(P384, "NIST-P256", p384::NistP384, P384Point, Secp384r1);
197
198/// An ECDH shared secret.
199#[derive(ZeroizeOnDrop)]
200pub struct SharedSecret<C>(ecdh::SharedSecret<C>)
201where
202    C: CurveArithmetic;
203
204impl<C> Borrow<[u8]> for SharedSecret<C>
205where
206    C: CurveArithmetic,
207{
208    fn borrow(&self) -> &[u8] {
209        self.0.raw_secret_bytes().as_slice()
210    }
211}
212
213macro_rules! ecdh_impl {
214    (
215        $curve:ident,
216        $doc:expr,
217        $sk:ident,
218        $pk:ident,
219        $point:ident $(,)?
220    ) => {
221        #[doc = concat!($doc, " ECDH private key.")]
222        #[derive(Clone, ZeroizeOnDrop)]
223        pub struct $sk(NonZeroScalar<$curve>);
224
225        impl DecapKey for $sk {
226            type EncapKey = $pk;
227
228            #[inline]
229            fn public(&self) -> Result<$pk, $crate::signer::PkError> {
230                Ok($pk(elliptic_curve::PublicKey::from_secret_scalar(&self.0)))
231            }
232        }
233
234        impl SecretKey for $sk {
235            type Size = FieldBytesSize<$curve>;
236
237            #[inline]
238            fn new<R: Csprng>(rng: &mut R) -> Self {
239                let sk = NonZeroScalar::random(&mut RngWrapper(rng));
240                Self(sk)
241            }
242
243            #[inline]
244            fn try_export_secret(&self) -> Result<SecretKeyBytes<Self::Size>, ExportError> {
245                // Mismatched GenericArray versions, yay.
246                let secret: [u8; FieldBytesSize::<$curve>::USIZE] = self.0.to_bytes().into();
247                Ok(SecretKeyBytes::new(secret.into()))
248            }
249        }
250
251        #[cfg(test)]
252        impl fmt::Debug for $sk {
253            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
254                hex::ct_write(f, self.0.to_bytes().as_slice())
255            }
256        }
257
258        impl ConstantTimeEq for $sk {
259            fn ct_eq(&self, other: &Self) -> Choice {
260                self.0.ct_eq(&other.0)
261            }
262        }
263
264        impl<'a> Import<&'a [u8]> for $sk {
265            fn import(data: &'a [u8]) -> Result<Self, ImportError> {
266                let bytes = *try_from_slice(data)?;
267                let sk = Option::from(NonZeroScalar::from_repr(bytes.into()))
268                    .ok_or(ImportError::InvalidSyntax)?;
269                Ok(Self(sk))
270            }
271        }
272
273        #[doc = concat!($doc, " ECDH public key.")]
274        #[derive(Clone, Eq, PartialEq)]
275        pub struct $pk(elliptic_curve::PublicKey<$curve>);
276
277        impl EncapKey for $pk {}
278
279        impl PublicKey for $pk {
280            type Data = $point;
281
282            fn export(&self) -> Self::Data {
283                $point(self.0.to_encoded_point(false))
284            }
285        }
286
287        impl fmt::Debug for $pk {
288            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
289                hex::ct_write(f, self.export().borrow())
290            }
291        }
292
293        impl<'a> Import<&'a [u8]> for $pk {
294            fn import(data: &'a [u8]) -> Result<Self, ImportError> {
295                let point = EncodedPoint::<$curve>::from_bytes(data)
296                    .map_err(|_| ImportError::InvalidSyntax)?;
297                let pk = Option::from(elliptic_curve::PublicKey::from_encoded_point(&point))
298                    .ok_or(ImportError::InvalidSyntax)?;
299                Ok(Self(pk))
300            }
301        }
302
303        impl Ecdh for $curve {
304            const SCALAR_SIZE: usize = <$curve as Curve>::ScalarSize::USIZE;
305
306            type PrivateKey = $sk;
307            type PublicKey = $pk;
308            type SharedSecret = SharedSecret<$curve>;
309
310            fn ecdh(
311                local: &Self::PrivateKey,
312                remote: &Self::PublicKey,
313            ) -> Result<Self::SharedSecret, EcdhError> {
314                let secret = ecdh::diffie_hellman(&local.0, remote.0.as_affine());
315                Ok(SharedSecret(secret))
316            }
317        }
318    };
319}
320ecdh_impl!(P256, "P-256", P256PrivateKey, P256PublicKey, P256Point);
321ecdh_impl!(P384, "P-384", P384PrivateKey, P384PublicKey, P384Point);
322// RustCrypto hasn't really implemented P-521.
323
324dhkem_impl!(
325    DhKemP256HkdfSha256,
326    "DHKEM(P256, HKDF-SHA256)",
327    P256,
328    HkdfSha256,
329    P256PrivateKey,
330    P256PublicKey,
331);
332
333/// An ASN.1 DER encoded ECDSA signature.
334#[derive(Clone)]
335pub struct SigBytes<T>(T);
336
337impl<T> Borrow<[u8]> for SigBytes<T>
338where
339    T: AsRef<[u8]>,
340{
341    fn borrow(&self) -> &[u8] {
342        self.0.as_ref()
343    }
344}
345
346macro_rules! ecdsa_impl {
347    (
348        $curve:ident,
349        $doc:expr,
350        $sk:ident,
351        $pk:ident,
352        $sig:ident,
353        $point:ident $(,)?
354    ) => {
355        #[doc = concat!($doc, " ECDSA private key.")]
356        #[derive(Clone, ZeroizeOnDrop)]
357        pub struct $sk(ecdsa::SigningKey<$curve>);
358
359        impl SigningKey<$curve> for $sk {
360            fn sign(&self, msg: &[u8]) -> Result<$sig, SignerError> {
361                let sig = self.0.sign(msg);
362                Ok($sig(sig))
363            }
364
365            #[inline]
366            fn public(&self) -> Result<$pk, $crate::signer::PkError> {
367                Ok($pk(ecdsa::VerifyingKey::from(&self.0)))
368            }
369        }
370
371        impl SecretKey for $sk {
372            type Size = FieldBytesSize<$curve>;
373
374            #[inline]
375            fn new<R: Csprng>(rng: &mut R) -> Self {
376                let sk = ecdsa::SigningKey::random(&mut RngWrapper(rng));
377                Self(sk)
378            }
379
380            #[inline]
381            fn try_export_secret(&self) -> Result<SecretKeyBytes<Self::Size>, ExportError> {
382                // Mismatched GenericArray versions, yay.
383                let secret: [u8; FieldBytesSize::<$curve>::USIZE] = self.0.to_bytes().into();
384                Ok(SecretKeyBytes::new(secret.into()))
385            }
386        }
387
388        #[cfg(test)]
389        impl fmt::Debug for $sk {
390            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
391                hex::ct_write(f, self.0.to_bytes().as_slice())
392            }
393        }
394
395        impl ConstantTimeEq for $sk {
396            fn ct_eq(&self, other: &Self) -> Choice {
397                self.0.ct_eq(&other.0)
398            }
399        }
400
401        impl<'a> Import<&'a [u8]> for $sk {
402            fn import(data: &'a [u8]) -> Result<Self, ImportError> {
403                let sk =
404                    ecdsa::SigningKey::from_slice(data).map_err(|_| ImportError::InvalidSyntax)?;
405                Ok(Self(sk))
406            }
407        }
408
409        #[doc = concat!($doc, " ECDSA public key.")]
410        #[derive(Clone, Eq, PartialEq)]
411        pub struct $pk(ecdsa::VerifyingKey<$curve>);
412
413        impl VerifyingKey<$curve> for $pk {
414            fn verify(&self, msg: &[u8], sig: &$sig) -> Result<(), SignerError> {
415                self.0
416                    .verify(msg, &sig.0)
417                    .map_err(|_| SignerError::Verification)
418            }
419        }
420
421        impl PublicKey for $pk {
422            type Data = $point;
423
424            fn export(&self) -> Self::Data {
425                $point(self.0.to_encoded_point(false))
426            }
427        }
428
429        impl fmt::Debug for $pk {
430            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
431                hex::ct_write(f, self.export().borrow())
432            }
433        }
434
435        impl<'a> Import<&'a [u8]> for $pk {
436            fn import(data: &'a [u8]) -> Result<Self, ImportError> {
437                let point = EncodedPoint::<$curve>::from_bytes(data)
438                    .map_err(|_| ImportError::InvalidSyntax)?;
439                let pk = ecdsa::VerifyingKey::from_encoded_point(&point)
440                    .map_err(|_| ImportError::InvalidSyntax)?;
441                Ok(Self(pk))
442            }
443        }
444
445        #[doc = concat!($doc, " ECDSA signature.")]
446        #[derive(Clone, Debug)]
447        pub struct $sig(ecdsa::Signature<$curve>);
448
449        impl Signature<$curve> for $sig {
450            type Data = SigBytes<der::Signature<$curve>>;
451
452            fn export(&self) -> Self::Data {
453                SigBytes(self.0.to_der())
454            }
455        }
456
457        impl<'a> Import<&'a [u8]> for $sig {
458            fn import(data: &'a [u8]) -> Result<Self, ImportError> {
459                let sig =
460                    ecdsa::Signature::from_der(data).map_err(|_| ImportError::InvalidSyntax)?;
461                Ok(Self(sig))
462            }
463        }
464
465        impl Signer for $curve {
466            const ID: SignerId = SignerId::$curve;
467
468            type SigningKey = $sk;
469            type VerifyingKey = $pk;
470            type Signature = $sig;
471        }
472    };
473}
474ecdsa_impl!(
475    P256,
476    "P-256",
477    P256SigningKey,
478    P256VerifyingKey,
479    P256Signature,
480    P256Point,
481);
482ecdsa_impl!(
483    P384,
484    "P-384",
485    P384SigningKey,
486    P384VerifyingKey,
487    P384Signature,
488    P384Point,
489);
490
491macro_rules! hash_impl {
492    (
493        $name:ident,
494        $doc:expr
495    ) => {
496        #[doc = concat!($doc, ".")]
497        #[derive(Clone, Debug, Default)]
498        pub struct $name(sha2::$name);
499
500        impl Hash for $name {
501            const ID: HashId = HashId::$name;
502
503            type DigestSize = <sha2::$name as OutputSizeUser>::OutputSize;
504            const DIGEST_SIZE: usize =
505                <<sha2::$name as OutputSizeUser>::OutputSize as Unsigned>::USIZE;
506
507            const BLOCK_SIZE: usize = <sha2::$name as BlockSizeUser>::BlockSize::USIZE;
508            type Block = Block<{ Self::BLOCK_SIZE }>;
509
510            #[inline]
511            fn new() -> Self {
512                Self(<sha2::$name as sha2::Digest>::new())
513            }
514
515            #[inline]
516            fn update(&mut self, data: &[u8]) {
517                sha2::Digest::update(&mut self.0, data)
518            }
519
520            #[inline]
521            fn digest(self) -> Digest<Self::DigestSize> {
522                Digest::from_array(sha2::Digest::finalize(self.0).into())
523            }
524
525            #[inline]
526            fn hash(data: &[u8]) -> Digest<Self::DigestSize> {
527                Digest::from_array(<sha2::$name as sha2::Digest>::digest(data).into())
528            }
529        }
530    };
531}
532hash_impl!(Sha256, "SHA-256");
533hash_impl!(Sha384, "SHA-384");
534hash_impl!(Sha512, "SHA-512");
535
536hkdf_impl!(HkdfSha256, "HKDF-SHA256", Sha256);
537hkdf_impl!(HkdfSha384, "HKDF-SHA384", Sha384);
538hkdf_impl!(HkdfSha512, "HKDF-SHA512", Sha512);
539
540hmac_impl!(HmacSha256, "HMAC-SHA256", Sha256);
541hmac_impl!(HmacSha384, "HMAC-SHA384", Sha384);
542hmac_impl!(HmacSha512, "HMAC-SHA512", Sha512);
543
544/// Translates [`Csprng`] to [`RngCore`].
545struct RngWrapper<'a, R>(&'a mut R);
546
547impl<R> CryptoRng for RngWrapper<'_, R> {}
548
549impl<R: Csprng> RngCore for RngWrapper<'_, R> {
550    fn next_u32(&mut self) -> u32 {
551        impls::next_u32_via_fill(self)
552    }
553
554    fn next_u64(&mut self) -> u64 {
555        impls::next_u64_via_fill(self)
556    }
557
558    fn fill_bytes(&mut self, dst: &mut [u8]) {
559        self.0.fill_bytes(dst)
560    }
561
562    fn try_fill_bytes(&mut self, dst: &mut [u8]) -> Result<(), rand_core::Error> {
563        self.fill_bytes(dst);
564        Ok(())
565    }
566}
567
568#[cfg(test)]
569mod tests {
570    use super::*;
571
572    mod aead_tests {
573        use super::*;
574        use crate::test_util::test_aead;
575
576        test_aead!(aes256gcm, Aes256Gcm, AeadTest::AesGcm);
577
578        #[cfg(feature = "committing-aead")]
579        mod committing {
580            use super::*;
581
582            test_aead!(cmd1_aead_aes256_gcm, Cmt1Aes256Gcm);
583            test_aead!(cmd4_aead_aes256_gcm, Cmt4Aes256Gcm);
584        }
585    }
586
587    mod ecdh_tests {
588        use super::*;
589        use crate::test_util::vectors::{test_ecdh, EcdhTest};
590
591        #[test]
592        fn test_ecdh_p256() {
593            test_ecdh::<P256>(EcdhTest::EcdhSecp256r1Ecpoint);
594        }
595
596        #[test]
597        fn test_ecdh_p384() {
598            test_ecdh::<P384>(EcdhTest::EcdhSecp384r1Ecpoint);
599        }
600    }
601
602    mod ecdsa_tests {
603        use super::*;
604        use crate::test_util::test_signer;
605
606        test_signer!(p256, P256, EcdsaTest::EcdsaSecp256r1Sha256);
607        test_signer!(p384, P384, EcdsaTest::EcdsaSecp384r1Sha384);
608    }
609
610    mod hkdf_tests {
611        use super::*;
612        use crate::test_util::test_kdf;
613
614        test_kdf!(test_hkdf_sha256, HkdfSha256, HkdfTest::HkdfSha256);
615        test_kdf!(test_hkdf_sha384, HkdfSha384, HkdfTest::HkdfSha384);
616        test_kdf!(test_hkdf_sha512, HkdfSha512, HkdfTest::HkdfSha512);
617    }
618
619    mod hmac_tests {
620        use super::*;
621        use crate::test_util::test_mac;
622
623        test_mac!(test_hmac_sha256, HmacSha256, MacTest::HmacSha256);
624        test_mac!(test_hmac_sha384, HmacSha384, MacTest::HmacSha384);
625        test_mac!(test_hmac_sha512, HmacSha512, MacTest::HmacSha512);
626    }
627
628    mod hpke_tests {
629        use super::*;
630        use crate::test_util::test_hpke;
631
632        test_hpke!(
633            sha256,
634            DhKemP256HkdfSha256,
635            HkdfSha256,
636            Aes256Gcm,
637            HpkeTest::HpkeDhKemP256HkdfSha256HkdfSha256Aes256Gcm,
638        );
639        test_hpke!(
640            sha512,
641            DhKemP256HkdfSha256,
642            HkdfSha512,
643            Aes256Gcm,
644            HpkeTest::HpkeDhKemP256HkdfSha256HkdfSha512Aes256Gcm,
645        );
646    }
647}