| 1 | use crate::error; |
| 2 | use crate::polyfill::{unwrap_const, ArrayFlatMap, LeadingZerosStripped}; |
| 3 | use core::num::NonZeroU64; |
| 4 | |
| 5 | /// The exponent `e` of an RSA public key. |
| 6 | #[derive (Clone, Copy)] |
| 7 | pub struct PublicExponent(NonZeroU64); |
| 8 | |
| 9 | impl PublicExponent { |
| 10 | #[cfg (test)] |
| 11 | const ALL_CONSTANTS: [Self; 3] = [Self::_3, Self::_65537, Self::MAX]; |
| 12 | |
| 13 | pub(super) const _3: Self = Self(unwrap_const(NonZeroU64::new(3))); |
| 14 | pub(super) const _65537: Self = Self(unwrap_const(NonZeroU64::new(65537))); |
| 15 | |
| 16 | // This limit was chosen to bound the performance of the simple |
| 17 | // exponentiation-by-squaring implementation in `elem_exp_vartime`. In |
| 18 | // particular, it helps mitigate theoretical resource exhaustion attacks. 33 |
| 19 | // bits was chosen as the limit based on the recommendations in [1] and |
| 20 | // [2]. Windows CryptoAPI (at least older versions) doesn't support values |
| 21 | // larger than 32 bits [3], so it is unlikely that exponents larger than 32 |
| 22 | // bits are being used for anything Windows commonly does. |
| 23 | // |
| 24 | // [1] https://www.imperialviolet.org/2012/03/16/rsae.html |
| 25 | // [2] https://www.imperialviolet.org/2012/03/17/rsados.html |
| 26 | // [3] https://msdn.microsoft.com/en-us/library/aa387685(VS.85).aspx |
| 27 | const MAX: Self = Self(unwrap_const(NonZeroU64::new((1u64 << 33) - 1))); |
| 28 | |
| 29 | pub(super) fn from_be_bytes( |
| 30 | input: untrusted::Input, |
| 31 | min_value: Self, |
| 32 | ) -> Result<Self, error::KeyRejected> { |
| 33 | // See `PublicKey::from_modulus_and_exponent` for background on the step |
| 34 | // numbering. |
| 35 | |
| 36 | if input.len() > 5 { |
| 37 | return Err(error::KeyRejected::too_large()); |
| 38 | } |
| 39 | let value = input.read_all(error::KeyRejected::invalid_encoding(), |input| { |
| 40 | // The exponent can't be zero and it can't be prefixed with |
| 41 | // zero-valued bytes. |
| 42 | if input.peek(0) { |
| 43 | return Err(error::KeyRejected::invalid_encoding()); |
| 44 | } |
| 45 | let mut value = 0u64; |
| 46 | loop { |
| 47 | let byte = input |
| 48 | .read_byte() |
| 49 | .map_err(|untrusted::EndOfInput| error::KeyRejected::invalid_encoding())?; |
| 50 | value = (value << 8) | u64::from(byte); |
| 51 | if input.at_end() { |
| 52 | return Ok(value); |
| 53 | } |
| 54 | } |
| 55 | })?; |
| 56 | |
| 57 | // Step 2 / Step b. NIST SP800-89 defers to FIPS 186-3, which requires |
| 58 | // `e >= 65537`. We enforce this when signing, but are more flexible in |
| 59 | // verification, for compatibility. Only small public exponents are |
| 60 | // supported. |
| 61 | let value = NonZeroU64::new(value).ok_or_else(error::KeyRejected::too_small)?; |
| 62 | if value < min_value.0 { |
| 63 | return Err(error::KeyRejected::too_small()); |
| 64 | } |
| 65 | if value > Self::MAX.0 { |
| 66 | return Err(error::KeyRejected::too_large()); |
| 67 | } |
| 68 | |
| 69 | // Step 3 / Step c. |
| 70 | if value.get() & 1 != 1 { |
| 71 | return Err(error::KeyRejected::invalid_component()); |
| 72 | } |
| 73 | |
| 74 | Ok(Self(value)) |
| 75 | } |
| 76 | |
| 77 | /// The big-endian encoding of the exponent. |
| 78 | /// |
| 79 | /// There are no leading zeros. |
| 80 | pub fn be_bytes(&self) -> impl ExactSizeIterator<Item = u8> + Clone + '_ { |
| 81 | // The `unwrap()` won't fail as `self.0` is only a few bytes long. |
| 82 | let bytes = ArrayFlatMap::new(core::iter::once(self.0.get()), u64::to_be_bytes).unwrap(); |
| 83 | LeadingZerosStripped::new(bytes) |
| 84 | } |
| 85 | |
| 86 | pub(super) fn value(self) -> NonZeroU64 { |
| 87 | self.0 |
| 88 | } |
| 89 | } |
| 90 | |
| 91 | #[cfg (test)] |
| 92 | mod tests { |
| 93 | use super::*; |
| 94 | |
| 95 | #[test ] |
| 96 | fn test_public_exponent_constants() { |
| 97 | for value in PublicExponent::ALL_CONSTANTS.iter() { |
| 98 | let value: u64 = value.0.into(); |
| 99 | assert_eq!(value & 1, 1); |
| 100 | assert!(value >= PublicExponent::_3.0.into()); // The absolute minimum. |
| 101 | assert!(value <= PublicExponent::MAX.0.into()); |
| 102 | } |
| 103 | } |
| 104 | } |
| 105 | |