Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Deterministic implementation of prime factors recovery #380

74 changes: 72 additions & 2 deletions src/algorithms/rsa.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,10 @@
use alloc::borrow::Cow;
use alloc::vec::Vec;
use num_bigint::{BigInt, BigUint, IntoBigInt, IntoBigUint, ModInverse, RandBigInt, ToBigInt};
use num_traits::{One, Signed, Zero};
use num_integer::{sqrt, Integer};
use num_traits::{FromPrimitive, One, Pow, Signed, Zero};
use rand_core::CryptoRngCore;
use zeroize::Zeroize;
use zeroize::{Zeroize, Zeroizing};

use crate::errors::{Error, Result};
use crate::traits::{PrivateKeyParts, PublicKeyParts};
Expand Down Expand Up @@ -194,3 +195,72 @@ fn blind<R: CryptoRngCore, K: PublicKeyParts>(
fn unblind(key: &impl PublicKeyParts, m: &BigUint, unblinder: &BigUint) -> BigUint {
(m * unblinder) % key.n()
}

/// The following (deterministic) algorithm also recovers the prime factors `p` and `q` of a modulus `n`, given the
/// public exponent `e` and private exponent `d` using the method described in
/// [NIST 800-56B Appendix C.2](https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-56Br2.pdf).
tarcieri marked this conversation as resolved.
Show resolved Hide resolved
pub fn recover_primes(n: &BigUint, e: &BigUint, d: &BigUint) -> Result<(BigUint, BigUint)> {
let one = BigUint::one();
// 1. Let a = (de – 1) × GCD(n – 1, de – 1).
let a = Zeroizing::new((d * e - &one) * (n - &one).gcd(&(d * e - &one)));

// 2. Let m = floor(a /n) and r = a – m n, so that a = m n + r and 0 ≤ r < n.
let m = Zeroizing::new(&*a / n);
let r = Zeroizing::new(&*a - &*m * n);

// 3. Let b = ( (n – r)/(m + 1) ) + 1; if b is not an integer or b^2 ≤ 4n, then output an error indicator,
// and exit without further processing.

zheylmun marked this conversation as resolved.
Show resolved Hide resolved
let modulus_check = Zeroizing::new((n - &*r) % (&*m + &one));
if !modulus_check.is_zero() {
return Err(Error::InvalidArguments);
}
let b = Zeroizing::new((n - &*r) / (&*m + &one) + one);

let four = BigUint::from_u8(4).unwrap();
let four_n = Zeroizing::new(n * four);
let two = BigUint::from_u8(2).unwrap();
let b_squared = Zeroizing::new(b.pow(2u32));
if *b_squared <= *four_n {
return Err(Error::InvalidArguments);
}
let b_squared_minus_four_n = Zeroizing::new(&*b_squared - &*four_n);

// 4. Let ϒ be the positive square root of b^2 – 4n; if ϒ is not an integer,
// then output an error indicator, and exit without further processing.
let y = Zeroizing::new(sqrt((*b_squared_minus_four_n).clone()));

let y_squared = Zeroizing::new(y.pow(2u32));
let sqrt_is_whole_number = y_squared == b_squared_minus_four_n;
if !sqrt_is_whole_number {
return Err(Error::InvalidArguments);
}
let p = (&*b + &*y) / &two;
let q = (&*b - &*y) / two;

Ok((p, q))
}

#[cfg(test)]
mod tests {
use num_traits::FromPrimitive;

use super::*;

#[test]
fn recover_primes_works() {
let n = BigUint::parse_bytes(b"00d397b84d98a4c26138ed1b695a8106ead91d553bf06041b62d3fdc50a041e222b8f4529689c1b82c5e71554f5dd69fa2f4b6158cf0dbeb57811a0fc327e1f28e74fe74d3bc166c1eabdc1b8b57b934ca8be5b00b4f29975bcc99acaf415b59bb28a6782bb41a2c3c2976b3c18dbadef62f00c6bb226640095096c0cc60d22fe7ef987d75c6a81b10d96bf292028af110dc7cc1bbc43d22adab379a0cd5d8078cc780ff5cd6209dea34c922cf784f7717e428d75b5aec8ff30e5f0141510766e2e0ab8d473c84e8710b2b98227c3db095337ad3452f19e2b9bfbccdd8148abf6776fa552775e6e75956e45229ae5a9c46949bab1e622f0e48f56524a84ed3483b", 16).unwrap();
let e = BigUint::from_u64(65537).unwrap();
let d = BigUint::parse_bytes(b"00c4e70c689162c94c660828191b52b4d8392115df486a9adbe831e458d73958320dc1b755456e93701e9702d76fb0b92f90e01d1fe248153281fe79aa9763a92fae69d8d7ecd144de29fa135bd14f9573e349e45031e3b76982f583003826c552e89a397c1a06bd2163488630d92e8c2bb643d7abef700da95d685c941489a46f54b5316f62b5d2c3a7f1bbd134cb37353a44683fdc9d95d36458de22f6c44057fe74a0a436c4308f73f4da42f35c47ac16a7138d483afc91e41dc3a1127382e0c0f5119b0221b4fc639d6b9c38177a6de9b526ebd88c38d7982c07f98a0efd877d508aae275b946915c02e2e1106d175d74ec6777f5e80d12c053d9c7be1e341", 16).unwrap();
let p = BigUint::parse_bytes(b"00f827bbf3a41877c7cc59aebf42ed4b29c32defcb8ed96863d5b090a05a8930dd624a21c9dcf9838568fdfa0df65b8462a5f2ac913d6c56f975532bd8e78fb07bd405ca99a484bcf59f019bbddcb3933f2bce706300b4f7b110120c5df9018159067c35da3061a56c8635a52b54273b31271b4311f0795df6021e6355e1a42e61",16).unwrap();
let q = BigUint::parse_bytes(b"00da4817ce0089dd36f2ade6a3ff410c73ec34bf1b4f6bda38431bfede11cef1f7f6efa70e5f8063a3b1f6e17296ffb15feefa0912a0325b8d1fd65a559e717b5b961ec345072e0ec5203d03441d29af4d64054a04507410cf1da78e7b6119d909ec66e6ad625bf995b279a4b3c5be7d895cd7c5b9c4c497fde730916fcdb4e41b", 16).unwrap();

let (mut p1, mut q1) = recover_primes(&n, &e, &d).unwrap();

if p1 < q1 {
std::mem::swap(&mut p1, &mut q1);
}
assert_eq!(p, p1);
assert_eq!(q, q1);
}
}
4 changes: 4 additions & 0 deletions src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,9 @@ pub enum Error {

/// Invalid padding length.
InvalidPadLen,

/// Invalid arguments.
InvalidArguments,
}

#[cfg(feature = "std")]
Expand Down Expand Up @@ -91,6 +94,7 @@ impl core::fmt::Display for Error {
Error::Internal => write!(f, "internal error"),
Error::LabelTooLong => write!(f, "label too long"),
Error::InvalidPadLen => write!(f, "invalid padding length"),
Error::InvalidArguments => write!(f, "invalid arguments"),
}
}
}
Expand Down
15 changes: 11 additions & 4 deletions src/key.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ 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::dummy_rng::DummyRng;
use crate::errors::{Error, Result};
use crate::traits::{PaddingScheme, PrivateKeyParts, PublicKeyParts, SignatureScheme};
Expand Down Expand Up @@ -232,12 +234,17 @@ impl RsaPrivateKey {
n: BigUint,
e: BigUint,
d: BigUint,
primes: Vec<BigUint>,
mut primes: Vec<BigUint>,
) -> Result<RsaPrivateKey> {
// TODO(tarcieri): support recovering `p` and `q` from `d` if `primes` is empty
// See method in Appendix C: https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-56Br1.pdf
if primes.len() < 2 {
return Err(Error::NprimesTooSmall);
if !primes.is_empty() {
return Err(Error::NprimesTooSmall);
}
// Recover `p` and `q` from `d`.
// See method in Appendix C.2: https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-56Br2.pdf
let (p, q) = recover_primes(&n, &e, &d)?;
primes.push(p);
primes.push(q);
}

let mut k = RsaPrivateKey {
Expand Down