diff --git a/Cargo.toml b/Cargo.toml index 0c16adbe4..736bd9f06 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -131,6 +131,7 @@ include = [ "tests/rsa_test_private_key_2048.p8", "tests/rsa_test_public_key_2048.der", "tests/rsa_test_public_key_2048_debug.txt", + "tests/rsa_test_public_modulus.bin", "third_party/fiat/curve25519_32.h", "third_party/fiat/curve25519_64.h", "third_party/fiat/p256_32.h", diff --git a/src/arithmetic/bigint.rs b/src/arithmetic/bigint.rs index a25996eec..aa58e6863 100644 --- a/src/arithmetic/bigint.rs +++ b/src/arithmetic/bigint.rs @@ -40,7 +40,7 @@ use crate::{ arithmetic::montgomery::*, bits, bssl, c, error, limb::{self, Limb, LimbMask, LIMB_BITS, LIMB_BYTES}, - polyfill::u64_from_usize, + polyfill::{u64_from_usize, LeadingZerosStripped}, }; use alloc::{borrow::ToOwned as _, boxed::Box, vec, vec::Vec}; use core::{ @@ -352,6 +352,12 @@ impl Modulus { } } +impl Modulus { + pub fn be_bytes(&self) -> LeadingZerosStripped + Clone + '_> { + LeadingZerosStripped::new(limb::unstripped_be_bytes(&self.limbs)) + } +} + pub(crate) struct PartialModulus<'a, M> { limbs: &'a [Limb], n0: N0, diff --git a/src/limb.rs b/src/limb.rs index 4d189e39d..0825913c2 100644 --- a/src/limb.rs +++ b/src/limb.rs @@ -237,7 +237,7 @@ pub fn parse_big_endian_and_pad_consttime( } pub fn big_endian_from_limbs(limbs: &[Limb], out: &mut [u8]) { - let be_bytes = be_bytes(limbs); + let be_bytes = unstripped_be_bytes(limbs); assert_eq!(out.len(), be_bytes.len()); out.iter_mut().zip(be_bytes).for_each(|(o, i)| { *o = i; @@ -246,8 +246,9 @@ pub fn big_endian_from_limbs(limbs: &[Limb], out: &mut [u8]) { /// Returns an iterator of the big-endian encoding of `limbs`. /// -/// The number of bytes returned will be a multiple of `LIMB_BYTES`. -fn be_bytes(limbs: &[Limb]) -> impl ExactSizeIterator + Clone + '_ { +/// The number of bytes returned will be a multiple of `LIMB_BYTES` +/// and thus may be padded with leading zeros. +pub fn unstripped_be_bytes(limbs: &[Limb]) -> impl ExactSizeIterator + Clone + '_ { // The unwrap is safe because a slice can never be larger than `usize` bytes. ArrayFlatMap::new(limbs.iter().rev().copied(), |limb| { core::array::IntoIter::new(Limb::to_be_bytes(limb)) diff --git a/src/rsa/public/exponent.rs b/src/rsa/public/exponent.rs index 68d06c79d..38fc755c0 100644 --- a/src/rsa/public/exponent.rs +++ b/src/rsa/public/exponent.rs @@ -1,4 +1,5 @@ use crate::error; +use crate::polyfill::{ArrayFlatMap, LeadingZerosStripped}; use core::num::NonZeroU64; /// The exponent `e` of an RSA public key. @@ -77,6 +78,18 @@ impl Exponent { Ok(Self(value)) } + + /// The big-endian encoding of the exponent. + /// + /// There are no leading zeros. + pub fn be_bytes(&self) -> impl ExactSizeIterator + Clone + '_ { + // The `unwrap()` won't fail as `self.0` is only a few bytes long. + let bytes = ArrayFlatMap::new(core::iter::once(self.0.get()), |value| { + core::array::IntoIter::new(u64::to_be_bytes(value)) + }) + .unwrap(); + LeadingZerosStripped::new(bytes) + } } impl From for NonZeroU64 { diff --git a/src/rsa/public/modulus.rs b/src/rsa/public/modulus.rs index 50cfcb26c..3849c262d 100644 --- a/src/rsa/public/modulus.rs +++ b/src/rsa/public/modulus.rs @@ -48,6 +48,14 @@ impl Modulus { Ok(Self { value, bits }) } + /// The big-endian encoding of the modulus. + /// + /// There are no leading zeros. + #[inline] + pub fn be_bytes(&self) -> impl ExactSizeIterator + Clone + '_ { + self.value.be_bytes() + } + /// The length of the modulus in bits. #[inline] pub fn len_bits(&self) -> bits::BitLength { diff --git a/src/rsa/signing.rs b/src/rsa/signing.rs index 76ebde88a..726b479d3 100644 --- a/src/rsa/signing.rs +++ b/src/rsa/signing.rs @@ -484,22 +484,6 @@ impl RsaSubjectPublicKey { }); Ok(Self(bytes)) } - - /// The public modulus (n). - pub fn modulus(&self) -> io::Positive { - // Parsing won't fail because we serialized it ourselves. - let (public_key, _exponent) = - super::parse_public_key(untrusted::Input::from(self.as_ref())).unwrap(); - public_key - } - - /// The public exponent (e). - pub fn exponent(&self) -> io::Positive { - // Parsing won't fail because we serialized it ourselves. - let (_public_key, exponent) = - super::parse_public_key(untrusted::Input::from(self.as_ref())).unwrap(); - exponent - } } struct PrivatePrime { diff --git a/tests/rsa_test_public_modulus.bin b/tests/rsa_test_public_modulus.bin new file mode 100644 index 000000000..1f473ee60 Binary files /dev/null and b/tests/rsa_test_public_modulus.bin differ diff --git a/tests/rsa_tests.rs b/tests/rsa_tests.rs index 19c9e89fb..c7d1afd33 100644 --- a/tests/rsa_tests.rs +++ b/tests/rsa_tests.rs @@ -329,13 +329,18 @@ fn rsa_test_public_key_coverage() { // Test `Clone`. let _ = key_pair.public_key().clone(); - // Test `exponent()`. + // Test modulus encoding. + const PUBLIC_KEY_MODULUS_BE_BYTES: &[u8] = include_bytes!("rsa_test_public_modulus.bin"); assert_eq!( - &[0x01, 0x00, 0x01], - key_pair - .public_key() - .exponent() - .big_endian_without_leading_zero() + PUBLIC_KEY_MODULUS_BE_BYTES, + key_pair.public().n().be_bytes().collect::>() + ); + + // Test exponent encoding. + const _65537: &[u8] = &[0x01, 0x00, 0x01]; + assert_eq!( + _65537, + &key_pair.public().e().be_bytes().collect::>() ); // Test `Debug`