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
|
//! Generate prime components for the RSA Private Key
|
||||||
|
|
||||||
use alloc::vec::Vec;
|
use alloc::vec::Vec;
|
||||||
use num_bigint::traits::ModInverse;
|
|
||||||
use num_bigint::{BigUint, RandPrime};
|
use num_bigint::{BigUint, RandPrime};
|
||||||
#[allow(unused_imports)]
|
#[allow(unused_imports)]
|
||||||
use num_traits::Float;
|
use num_traits::Float;
|
||||||
use num_traits::{One, Zero};
|
use num_traits::Zero;
|
||||||
use rand_core::CryptoRngCore;
|
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 struct RsaPrivateKeyComponents {
|
||||||
pub n: BigUint,
|
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 n = compute_modulus(&primes);
|
||||||
let mut totient = BigUint::one();
|
|
||||||
|
|
||||||
for prime in &primes {
|
|
||||||
n *= prime;
|
|
||||||
totient *= prime - BigUint::one();
|
|
||||||
}
|
|
||||||
|
|
||||||
if n.bits() != bit_size {
|
if n.bits() != bit_size {
|
||||||
// This should never happen for nprimes == 2 because
|
// 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;
|
continue 'next;
|
||||||
}
|
}
|
||||||
|
|
||||||
// NOTE: `mod_inverse` checks if `exp` evenly divides `totient` and returns `None` if so.
|
if let Ok(d) = compute_private_exponent_euler_totient(&primes, exp) {
|
||||||
// This ensures that `exp` is not a factor of any `(prime - 1)`.
|
|
||||||
if let Some(d) = exp.mod_inverse(totient) {
|
|
||||||
n_final = n;
|
n_final = n;
|
||||||
d_final = d.to_biguint().unwrap();
|
d_final = d;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -245,6 +245,64 @@ pub fn recover_primes(n: &BigUint, e: &BigUint, d: &BigUint) -> Result<(BigUint,
|
|||||||
Ok((p, q))
|
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)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use num_traits::FromPrimitive;
|
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 zeroize::{Zeroize, ZeroizeOnDrop};
|
||||||
|
|
||||||
use crate::algorithms::generate::generate_multi_prime_key_with_exp;
|
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::dummy_rng::DummyRng;
|
||||||
use crate::errors::{Error, Result};
|
use crate::errors::{Error, Result};
|
||||||
@ -279,6 +282,46 @@ impl RsaPrivateKey {
|
|||||||
Ok(k)
|
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`.
|
/// Get the public key from the private key, cloning `n` and `e`.
|
||||||
///
|
///
|
||||||
/// Generally this is not needed since `RsaPrivateKey` implements the `PublicKey` trait,
|
/// Generally this is not needed since `RsaPrivateKey` implements the `PublicKey` trait,
|
||||||
@ -495,6 +538,7 @@ mod tests {
|
|||||||
|
|
||||||
use hex_literal::hex;
|
use hex_literal::hex;
|
||||||
use num_traits::{FromPrimitive, ToPrimitive};
|
use num_traits::{FromPrimitive, ToPrimitive};
|
||||||
|
use pkcs8::DecodePrivateKey;
|
||||||
use rand_chacha::{rand_core::SeedableRng, ChaCha8Rng};
|
use rand_chacha::{rand_core::SeedableRng, ChaCha8Rng};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -753,4 +797,47 @@ mod tests {
|
|||||||
Error::ModulusTooLarge
|
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