From 0f907ebd5c0419624f3209f272b09166553e9d4d Mon Sep 17 00:00:00 2001 From: BlackHoleFox Date: Mon, 8 Jun 2020 21:48:25 -0500 Subject: [PATCH] Implement RSA key precomputation --- Cargo.lock | 3 +++ Cargo.toml | 5 +++- src/key.rs | 72 ++++++++++++++++++++++++++++++++++++++++++++---------- 3 files changed, 66 insertions(+), 14 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 372451f..a1ad037 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1064,6 +1064,9 @@ dependencies = [ "log", "nom", "num-bigint", + "num-bigint-dig", + "num-integer", + "num-traits", "p256", "p384", "pbkdf2", diff --git a/Cargo.toml b/Cargo.toml index c61244d..ae2d756 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -31,7 +31,10 @@ getrandom = "0.1" hmac = "0.7" log = "0.4" nom = "5" -num-bigint = { version = "0.2", features = ["rand"] } +num-bigint_rsa = { version = "0.6", features = ["rand"], package = "num-bigint-dig" } +num-bigint = { version = "0.2", features = ["rand"] } +num-traits = "0.2" +num-integer = "0.1" pbkdf2 = "0.3" p256 = "0.2" p384 = "0.1" diff --git a/src/key.rs b/src/key.rs index f1ac7b3..510db55 100644 --- a/src/key.rs +++ b/src/key.rs @@ -58,6 +58,12 @@ use crate::{ }; use elliptic_curve::weierstrass::PublicKey as EcPublicKey; use log::{error, warn}; +#[cfg(feature = "untested")] +use num_bigint_rsa::traits::ModInverse; +#[cfg(feature = "untested")] +use num_integer::Integer; +#[cfg(feature = "untested")] +use num_traits::{FromPrimitive, One}; use rsa::{BigUint, RSAPublicKey}; #[cfg(feature = "untested")] use zeroize::Zeroizing; @@ -72,6 +78,9 @@ const TAG_ECC_POINT: u8 = 0x86; #[cfg(feature = "untested")] const KEYDATA_LEN: usize = 1024; +#[cfg(feature = "untested")] +const KEYDATA_RSA_EXP: u64 = 65537; + /// Slot identifiers. /// #[derive(Clone, Copy, Debug, PartialEq)] @@ -703,21 +712,58 @@ fn write_key( /// The key data that makes up an RSA key. #[cfg(feature = "untested")] -pub struct RsaKeyData<'a> { +pub struct RsaKeyData { /// The secret prime `p`. - pub p: &'a [u8], + p: Zeroizing>, /// The secret prime, `q`. - pub q: &'a [u8], + q: Zeroizing>, /// D mod (P-1) - pub dp: &'a [u8], + dp: Zeroizing>, /// D mod (Q-1) - pub dq: &'a [u8], + dq: Zeroizing>, /// Q^-1 mod P - pub qinv: &'a [u8], + qinv: Zeroizing>, } #[cfg(feature = "untested")] -impl RsaKeyData<'_> { +impl RsaKeyData { + /// Generates a new RSA key data set from two randomly generated, secret, primes. + /// + /// Panics if `secret_p` or `secret_q` are invalid primes. + pub fn new(secret_p: &[u8], secret_q: &[u8]) -> Self { + let p = BigUint::from_bytes_be(secret_p); + let q = BigUint::from_bytes_be(secret_q); + + let totient = { + let p_t = &p - BigUint::one(); + let q_t = &p - BigUint::one(); + + p_t.lcm(&q_t) + }; + + let exp = BigUint::from_u64(KEYDATA_RSA_EXP).unwrap(); + + let d = exp.mod_inverse(&totient).unwrap(); + let d = d.to_biguint().unwrap(); + + // We calculate the optimization values ahead of time, instead of making the user + // do so. + + let dp = &d % (&p - BigUint::one()); + let dq = &d % (&q - BigUint::one()); + + let qinv = q.clone().mod_inverse(&p).unwrap(); + let (_, qinv) = qinv.to_bytes_be(); + + RsaKeyData { + p: Zeroizing::new(p.to_bytes_be()), + q: Zeroizing::new(q.to_bytes_be()), + dp: Zeroizing::new(dp.to_bytes_be()), + dq: Zeroizing::new(dq.to_bytes_be()), + qinv: Zeroizing::new(qinv), + } + } + fn total_len(&self) -> usize { self.p.len() + self.q.len() + self.dp.len() + self.qinv.len() } @@ -731,7 +777,7 @@ pub fn import_rsa_key( yubikey: &mut YubiKey, slot: SlotId, algorithm: AlgorithmId, - key_data: RsaKeyData<'_>, + key_data: RsaKeyData, touch_policy: TouchPolicy, pin_policy: PinPolicy, ) -> Result<(), Error> { @@ -745,11 +791,11 @@ pub fn import_rsa_key( } let params = vec![ - key_data.p, - key_data.q, - key_data.dp, - key_data.dq, - key_data.qinv, + key_data.p.as_slice(), + key_data.q.as_slice(), + key_data.dp.as_slice(), + key_data.dq.as_slice(), + key_data.qinv.as_slice(), ]; write_key(yubikey, slot, params, pin_policy, touch_policy, algorithm)?;