Adds RsaPrivateKey::from_primes
and RsaPrivateKey::from_p_q
methods (#386)
This is used on Yubico HSM for import/export under wrap as well as when importing a key unsealed.
This commit is contained in:
parent
00eaa91db5
commit
63409e526c
@ -1,14 +1,16 @@
|
||||
//! Generate prime components for the RSA Private Key
|
||||
|
||||
use alloc::vec::Vec;
|
||||
use num_bigint::traits::ModInverse;
|
||||
use num_bigint::{BigUint, RandPrime};
|
||||
#[allow(unused_imports)]
|
||||
use num_traits::Float;
|
||||
use num_traits::{One, Zero};
|
||||
use num_traits::Zero;
|
||||
use rand_core::CryptoRngCore;
|
||||
|
||||
use crate::errors::{Error, Result};
|
||||
use crate::{
|
||||
algorithms::rsa::{compute_modulus, compute_private_exponent_euler_totient},
|
||||
errors::{Error, Result},
|
||||
};
|
||||
|
||||
pub struct RsaPrivateKeyComponents {
|
||||
pub n: BigUint,
|
||||
@ -89,13 +91,7 @@ pub(crate) fn generate_multi_prime_key_with_exp<R: CryptoRngCore + ?Sized>(
|
||||
}
|
||||
}
|
||||
|
||||
let mut n = BigUint::one();
|
||||
let mut totient = BigUint::one();
|
||||
|
||||
for prime in &primes {
|
||||
n *= prime;
|
||||
totient *= prime - BigUint::one();
|
||||
}
|
||||
let n = compute_modulus(&primes);
|
||||
|
||||
if n.bits() != bit_size {
|
||||
// This should never happen for nprimes == 2 because
|
||||
@ -104,11 +100,9 @@ pub(crate) fn generate_multi_prime_key_with_exp<R: CryptoRngCore + ?Sized>(
|
||||
continue 'next;
|
||||
}
|
||||
|
||||
// NOTE: `mod_inverse` checks if `exp` evenly divides `totient` and returns `None` if so.
|
||||
// This ensures that `exp` is not a factor of any `(prime - 1)`.
|
||||
if let Some(d) = exp.mod_inverse(totient) {
|
||||
if let Ok(d) = compute_private_exponent_euler_totient(&primes, exp) {
|
||||
n_final = n;
|
||||
d_final = d.to_biguint().unwrap();
|
||||
d_final = d;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -245,6 +245,64 @@ pub fn recover_primes(n: &BigUint, e: &BigUint, d: &BigUint) -> Result<(BigUint,
|
||||
Ok((p, q))
|
||||
}
|
||||
|
||||
/// Compute the modulus of a key from its primes.
|
||||
pub(crate) fn compute_modulus(primes: &[BigUint]) -> BigUint {
|
||||
primes.iter().product()
|
||||
}
|
||||
|
||||
/// Compute the private exponent from its primes (p and q) and public exponent
|
||||
/// This uses Euler's totient function
|
||||
#[inline]
|
||||
pub(crate) fn compute_private_exponent_euler_totient(
|
||||
primes: &[BigUint],
|
||||
exp: &BigUint,
|
||||
) -> Result<BigUint> {
|
||||
if primes.len() < 2 {
|
||||
return Err(Error::InvalidPrime);
|
||||
}
|
||||
|
||||
let mut totient = BigUint::one();
|
||||
|
||||
for prime in primes {
|
||||
totient *= prime - BigUint::one();
|
||||
}
|
||||
|
||||
// NOTE: `mod_inverse` checks if `exp` evenly divides `totient` and returns `None` if so.
|
||||
// This ensures that `exp` is not a factor of any `(prime - 1)`.
|
||||
if let Some(d) = exp.mod_inverse(totient) {
|
||||
Ok(d.to_biguint().unwrap())
|
||||
} else {
|
||||
// `exp` evenly divides `totient`
|
||||
Err(Error::InvalidPrime)
|
||||
}
|
||||
}
|
||||
|
||||
/// Compute the private exponent from its primes (p and q) and public exponent
|
||||
///
|
||||
/// This is using the method defined by
|
||||
/// [NIST 800-56B Section 6.2.1](https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-56Br2.pdf#page=47).
|
||||
/// (Carmichael function)
|
||||
///
|
||||
/// FIPS 186-4 **requires** the private exponent to be less than λ(n), which would
|
||||
/// make Euler's totiem unreliable.
|
||||
#[inline]
|
||||
pub(crate) fn compute_private_exponent_carmicheal(
|
||||
p: &BigUint,
|
||||
q: &BigUint,
|
||||
exp: &BigUint,
|
||||
) -> Result<BigUint> {
|
||||
let p1 = p - BigUint::one();
|
||||
let q1 = q - BigUint::one();
|
||||
|
||||
let lcm = p1.lcm(&q1);
|
||||
if let Some(d) = exp.mod_inverse(lcm) {
|
||||
Ok(d.to_biguint().unwrap())
|
||||
} else {
|
||||
// `exp` evenly divides `lcm`
|
||||
Err(Error::InvalidPrime)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use num_traits::FromPrimitive;
|
||||
|
89
src/key.rs
89
src/key.rs
@ -11,7 +11,10 @@ use serde::{Deserialize, Serialize};
|
||||
use zeroize::{Zeroize, ZeroizeOnDrop};
|
||||
|
||||
use crate::algorithms::generate::generate_multi_prime_key_with_exp;
|
||||
use crate::algorithms::rsa::recover_primes;
|
||||
use crate::algorithms::rsa::{
|
||||
compute_modulus, compute_private_exponent_carmicheal, compute_private_exponent_euler_totient,
|
||||
recover_primes,
|
||||
};
|
||||
|
||||
use crate::dummy_rng::DummyRng;
|
||||
use crate::errors::{Error, Result};
|
||||
@ -279,6 +282,46 @@ impl RsaPrivateKey {
|
||||
Ok(k)
|
||||
}
|
||||
|
||||
/// Constructs an RSA key pair from its two primes p and q.
|
||||
///
|
||||
/// This will rebuild the private exponent and the modulus.
|
||||
///
|
||||
/// Private exponent will be rebuilt using the method defined in
|
||||
/// [NIST 800-56B Section 6.2.1](https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-56Br2.pdf#page=47).
|
||||
pub fn from_p_q(p: BigUint, q: BigUint, public_exponent: BigUint) -> Result<RsaPrivateKey> {
|
||||
if p == q {
|
||||
return Err(Error::InvalidPrime);
|
||||
}
|
||||
|
||||
let n = compute_modulus(&[p.clone(), q.clone()]);
|
||||
let d = compute_private_exponent_carmicheal(&p, &q, &public_exponent)?;
|
||||
|
||||
Self::from_components(n, public_exponent, d, vec![p, q])
|
||||
}
|
||||
|
||||
/// Constructs an RSA key pair from its primes.
|
||||
///
|
||||
/// This will rebuild the private exponent and the modulus.
|
||||
pub fn from_primes(primes: Vec<BigUint>, public_exponent: BigUint) -> Result<RsaPrivateKey> {
|
||||
if primes.len() < 2 {
|
||||
return Err(Error::NprimesTooSmall);
|
||||
}
|
||||
|
||||
// Makes sure that primes is pairwise unequal.
|
||||
for (i, prime1) in primes.iter().enumerate() {
|
||||
for prime2 in primes.iter().take(i) {
|
||||
if prime1 == prime2 {
|
||||
return Err(Error::InvalidPrime);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let n = compute_modulus(&primes);
|
||||
let d = compute_private_exponent_euler_totient(&primes, &public_exponent)?;
|
||||
|
||||
Self::from_components(n, public_exponent, d, primes)
|
||||
}
|
||||
|
||||
/// Get the public key from the private key, cloning `n` and `e`.
|
||||
///
|
||||
/// Generally this is not needed since `RsaPrivateKey` implements the `PublicKey` trait,
|
||||
@ -495,6 +538,7 @@ mod tests {
|
||||
|
||||
use hex_literal::hex;
|
||||
use num_traits::{FromPrimitive, ToPrimitive};
|
||||
use pkcs8::DecodePrivateKey;
|
||||
use rand_chacha::{rand_core::SeedableRng, ChaCha8Rng};
|
||||
|
||||
#[test]
|
||||
@ -753,4 +797,47 @@ mod tests {
|
||||
Error::ModulusTooLarge
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn build_key_from_primes() {
|
||||
const RSA_2048_PRIV_DER: &[u8] = include_bytes!("../tests/examples/pkcs8/rsa2048-priv.der");
|
||||
let ref_key = RsaPrivateKey::from_pkcs8_der(RSA_2048_PRIV_DER).unwrap();
|
||||
assert_eq!(ref_key.validate(), Ok(()));
|
||||
|
||||
let primes = ref_key.primes().to_vec();
|
||||
|
||||
let exp = ref_key.e().clone();
|
||||
let key =
|
||||
RsaPrivateKey::from_primes(primes, exp).expect("failed to import key from primes");
|
||||
assert_eq!(key.validate(), Ok(()));
|
||||
|
||||
assert_eq!(key.n(), ref_key.n());
|
||||
|
||||
assert_eq!(key.dp(), ref_key.dp());
|
||||
assert_eq!(key.dq(), ref_key.dq());
|
||||
|
||||
assert_eq!(key.d(), ref_key.d());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn build_key_from_p_q() {
|
||||
const RSA_2048_SP800_PRIV_DER: &[u8] =
|
||||
include_bytes!("../tests/examples/pkcs8/rsa2048-sp800-56b-priv.der");
|
||||
let ref_key = RsaPrivateKey::from_pkcs8_der(RSA_2048_SP800_PRIV_DER).unwrap();
|
||||
assert_eq!(ref_key.validate(), Ok(()));
|
||||
|
||||
let primes = ref_key.primes().to_vec();
|
||||
let exp = ref_key.e().clone();
|
||||
|
||||
let key = RsaPrivateKey::from_p_q(primes[0].clone(), primes[1].clone(), exp)
|
||||
.expect("failed to import key from primes");
|
||||
assert_eq!(key.validate(), Ok(()));
|
||||
|
||||
assert_eq!(key.n(), ref_key.n());
|
||||
|
||||
assert_eq!(key.dp(), ref_key.dp());
|
||||
assert_eq!(key.dq(), ref_key.dq());
|
||||
|
||||
assert_eq!(key.d(), ref_key.d());
|
||||
}
|
||||
}
|
||||
|
BIN
tests/examples/pkcs8/rsa2048-sp800-56b-priv.der
Normal file
BIN
tests/examples/pkcs8/rsa2048-sp800-56b-priv.der
Normal file
Binary file not shown.
Loading…
x
Reference in New Issue
Block a user