diff --git a/src/rsa/keypair.rs b/src/rsa/keypair.rs index ef05960838..887ab4b6d5 100644 --- a/src/rsa/keypair.rs +++ b/src/rsa/keypair.rs @@ -302,7 +302,7 @@ impl KeyPair { cpu_features, )?; - let n = public_key.n().value(); + let n = public_key.inner().n().value(); // 6.4.1.4.3 says to skip 6.4.1.2.1 Step 2. @@ -323,7 +323,7 @@ impl KeyPair { // TODO: First, stop if `p < (√2) * 2**((nBits/2) - 1)`. // // Second, stop if `p > 2**(nBits/2) - 1`. - let half_n_bits = public_key.n().len_bits().half_rounded_up(); + let half_n_bits = public_key.inner().n().len_bits().half_rounded_up(); if p_bits != half_n_bits { return Err(KeyRejected::inconsistent_components()); } @@ -580,7 +580,7 @@ impl KeyPair { // Use the output buffer as the scratch space for the signature to // reduce the required stack space. - padding_alg.encode(m_hash, signature, self.public().n().len_bits(), rng)?; + padding_alg.encode(m_hash, signature, self.public().inner().n().len_bits(), rng)?; // RFC 8017 Section 5.1.2: RSADP, using the Chinese Remainder Theorem // with Garner's algorithm. @@ -607,7 +607,7 @@ impl KeyPair { // RFC 8017 Section 5.1.2: RSADP, using the Chinese Remainder Theorem // with Garner's algorithm. - let n = self.public.n().value(); + let n = self.public.inner().n().value(); // Step 1. The value zero is also rejected. let base = bigint::Elem::from_be_bytes_padded(untrusted::Input::from(base), n)?; @@ -648,7 +648,7 @@ impl KeyPair { // minimum value, since the relationship of `e` to `d`, `p`, and `q` is // not verified during `KeyPair` construction. { - let verify = self.public.exponentiate_elem(m.clone()); + let verify = self.public.inner().exponentiate_elem(m.clone()); bigint::elem_verify_equal_consttime(&verify, &c)?; } diff --git a/src/rsa/public_key.rs b/src/rsa/public_key.rs index 9673c94a1c..2fa1e0d43c 100644 --- a/src/rsa/public_key.rs +++ b/src/rsa/public_key.rs @@ -25,8 +25,7 @@ use core::num::NonZeroU64; /// An RSA Public Key. #[derive(Clone)] pub struct PublicKey { - n: PublicModulus, - e: PublicExponent, + inner: Inner, serialized: Box<[u8]>, } @@ -41,9 +40,64 @@ impl PublicKey { e_min_value: PublicExponent, cpu_features: cpu::Features, ) -> Result { + let inner = Inner::from_modulus_and_exponent( + n, + e, + n_min_bits, + n_max_bits, + e_min_value, + cpu_features, + )?; + let n_bytes = n; let e_bytes = e; + // TODO: Remove this re-parsing, and stop allocating this here. + // Instead we should serialize on demand without allocation, from + // `Modulus::be_bytes()` and `Exponent::be_bytes()`. Once this is + // fixed, merge `Inner` back into `PublicKey`. + let n_bytes = io::Positive::from_be_bytes(n_bytes) + .map_err(|_: error::Unspecified| error::KeyRejected::unexpected_error())?; + let e_bytes = io::Positive::from_be_bytes(e_bytes) + .map_err(|_: error::Unspecified| error::KeyRejected::unexpected_error())?; + let serialized = der_writer::write_all(der::Tag::Sequence, &|output| { + der_writer::write_positive_integer(output, &n_bytes); + der_writer::write_positive_integer(output, &e_bytes); + }); + + Ok(Self { inner, serialized }) + } + + /// The length, in bytes, of the public modulus. + /// + /// The modulus length is rounded up to a whole number of bytes if its + /// bit length isn't a multiple of 8. + pub fn modulus_len(&self) -> usize { + self.inner.n().len_bits().as_usize_bytes_rounded_up() + } + + pub(super) fn inner(&self) -> &Inner { + &self.inner + } +} + +/// `PublicKey` but without any superfluous allocations, optimized for one-shot +/// RSA signature verification. +#[derive(Clone)] +pub(crate) struct Inner { + n: PublicModulus, + e: PublicExponent, +} + +impl Inner { + pub(super) fn from_modulus_and_exponent( + n: untrusted::Input, + e: untrusted::Input, + n_min_bits: bits::BitLength, + n_max_bits: bits::BitLength, + e_min_value: PublicExponent, + cpu_features: cpu::Features, + ) -> Result { // This is an incomplete implementation of NIST SP800-56Br1 Section // 6.4.2.2, "Partial Public-Key Validation for RSA." That spec defers // to NIST SP800-89 Section 5.3.3, "(Explicit) Partial Public Key @@ -64,27 +118,7 @@ impl PublicKey { // XXX: Steps 4 & 5 / Steps d, e, & f are not implemented. This is also the // case in most other commonly-used crypto libraries. - // TODO: Remove this re-parsing, and stop allocating this here. - // Instead we should serialize on demand without allocation, from - // `Modulus::be_bytes()` and `Exponent::be_bytes()`. - let n_bytes = io::Positive::from_be_bytes(n_bytes) - .map_err(|_: error::Unspecified| error::KeyRejected::unexpected_error())?; - let e_bytes = io::Positive::from_be_bytes(e_bytes) - .map_err(|_: error::Unspecified| error::KeyRejected::unexpected_error())?; - let serialized = der_writer::write_all(der::Tag::Sequence, &|output| { - der_writer::write_positive_integer(output, &n_bytes); - der_writer::write_positive_integer(output, &e_bytes); - }); - - Ok(Self { n, e, serialized }) - } - - /// The length, in bytes, of the public modulus. - /// - /// The modulus length is rounded up to a whole number of bytes if its - /// bit length isn't a multiple of 8. - pub fn modulus_len(&self) -> usize { - self.n().len_bits().as_usize_bytes_rounded_up() + Ok(Self { n, e }) } /// The public modulus. diff --git a/src/rsa/public_key_components.rs b/src/rsa/public_key_components.rs index c30fb44104..5c02e03613 100644 --- a/src/rsa/public_key_components.rs +++ b/src/rsa/public_key_components.rs @@ -45,8 +45,8 @@ where { fn from(public_key: &PublicKey) -> Self { Self { - n: public_key.n().be_bytes().collect(), - e: public_key.e().be_bytes().collect(), + n: public_key.inner().n().be_bytes().collect(), + e: public_key.inner().e().be_bytes().collect(), } } } diff --git a/src/rsa/verification.rs b/src/rsa/verification.rs index 9d765f84ca..80627816db 100644 --- a/src/rsa/verification.rs +++ b/src/rsa/verification.rs @@ -15,7 +15,7 @@ //! Verification of RSA signatures. use super::{ - parse_public_key, PublicExponent, PublicKey, RsaParameters, PUBLIC_KEY_PUBLIC_MODULUS_MAX_LEN, + parse_public_key, public_key, PublicExponent, RsaParameters, PUBLIC_KEY_PUBLIC_MODULUS_MAX_LEN, }; use crate::{bits, cpu, digest, error, sealed, signature}; @@ -201,7 +201,7 @@ pub(crate) fn verify_rsa_( // exponent value is 2**16 + 1, but it isn't clear if this is just for // signing or also for verification. We support exponents of 3 and larger // for compatibility with other commonly-used crypto libraries. - let key = PublicKey::from_modulus_and_exponent( + let key = public_key::Inner::from_modulus_and_exponent( n, e, params.min_bits,