Skip to content

Commit 461010b

Browse files
teythoonsimo5
authored andcommitted
ossl: Add some helpers to support RSA as defined in RFC9580.
1 parent ad2f4fd commit 461010b

File tree

2 files changed

+225
-0
lines changed

2 files changed

+225
-0
lines changed

ossl/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,3 +33,4 @@ dynamic = [] # Builds against system libcrypto.so
3333
fips = ["ossl350"] # Builds against sources and libfips.a instead of libcrypto
3434
log = ["dep:log", "dep:vsprintf"] # Error tracing using log crate
3535
dummy-integrity = [] # USE ONLY for testing as a dev-depenency
36+
rfc9580 = [] # Enables features required for OpenPGP implementations

ossl/src/pkey.rs

Lines changed: 224 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -385,6 +385,201 @@ impl Drop for RsaData {
385385
}
386386
}
387387

388+
#[cfg(feature = "rfc9580")]
389+
impl RsaData {
390+
/// Creates a parameter set from `d`, `p`, and `q`.
391+
pub fn from_dpq(
392+
d: &[u8],
393+
p: &[u8],
394+
q: &[u8],
395+
) -> Result<(Vec<u8>, RsaData), Error> {
396+
use crate::BigNum;
397+
398+
/// Subtracts `b` from `a` storing the result in `res`.
399+
fn checked_sub(
400+
res: &mut BigNum,
401+
a: &BigNum,
402+
b: &BigNum,
403+
) -> Result<(), Error> {
404+
let ret =
405+
unsafe { BN_sub(res.as_mut_ptr(), a.as_ptr(), b.as_ptr()) };
406+
407+
if ret == 1 {
408+
Ok(())
409+
} else {
410+
trace_ossl!("BN_sub()");
411+
Err(Error::new(ErrorKind::OsslError))
412+
}
413+
}
414+
415+
/// Computes the product of the given numbers storing the result
416+
/// in `res`.
417+
fn checked_mul(
418+
res: &mut BigNum,
419+
a: &BigNum,
420+
b: &BigNum,
421+
) -> Result<(), Error> {
422+
unsafe {
423+
let ctx = BN_CTX_secure_new();
424+
if ctx.is_null() {
425+
return Err(Error::new(ErrorKind::NullPtr));
426+
}
427+
428+
let ret = BN_mul(res.as_mut_ptr(), a.as_ptr(), b.as_ptr(), ctx);
429+
430+
BN_CTX_free(ctx);
431+
432+
if ret == 1 {
433+
Ok(())
434+
} else {
435+
trace_ossl!("BN_mul()");
436+
Err(Error::new(ErrorKind::OsslError))
437+
}
438+
}
439+
}
440+
441+
/// Computes the inverse of `a` modulo `n` storing the result in
442+
/// `res`.
443+
fn checked_mod_inverse(
444+
res: &mut BigNum,
445+
a: &BigNum,
446+
n: &BigNum,
447+
) -> Result<(), Error> {
448+
unsafe {
449+
let ctx = BN_CTX_secure_new();
450+
if ctx.is_null() {
451+
return Err(Error::new(ErrorKind::NullPtr));
452+
}
453+
454+
let ret = BN_mod_inverse(
455+
res.as_mut_ptr(),
456+
a.as_ptr(),
457+
n.as_ptr(),
458+
ctx,
459+
);
460+
461+
BN_CTX_free(ctx);
462+
463+
if !ret.is_null() {
464+
Ok(())
465+
} else {
466+
trace_ossl!("BN_mod_inverse()");
467+
Err(Error::new(ErrorKind::OsslError))
468+
}
469+
}
470+
}
471+
472+
/// Configures the given `BigNum` to use constant-time
473+
/// operations.
474+
fn use_constant_time_ops(bn: &mut BigNum) {
475+
unsafe {
476+
BN_set_flags(bn.as_mut_ptr(), BN_FLG_CONSTTIME as i32);
477+
}
478+
}
479+
480+
let (e, n) = {
481+
use crate::BigNum;
482+
483+
// Compute n = p * q.
484+
let p = BigNum::from_bigendian_slice(p)?;
485+
let q = BigNum::from_bigendian_slice(q)?;
486+
487+
let mut n = BigNum::new()?;
488+
use_constant_time_ops(&mut n);
489+
checked_mul(&mut n, &p, &q)?;
490+
491+
// Compute 𝜙 = (p - 1) * (q - 1).
492+
let one = BigNum::from_bigendian_slice(&[1])?;
493+
let mut p_dec = BigNum::new()?;
494+
use_constant_time_ops(&mut p_dec);
495+
checked_sub(&mut p_dec, &p, &one)?;
496+
let mut q_dec = BigNum::new()?;
497+
use_constant_time_ops(&mut q_dec);
498+
checked_sub(&mut q_dec, &q, &one)?;
499+
500+
let mut phi = BigNum::new()?;
501+
use_constant_time_ops(&mut phi);
502+
checked_mul(&mut phi, &p_dec, &q_dec)?;
503+
504+
// Compute e ≡ d⁻¹ (mod 𝜙).
505+
let d = BigNum::from_bigendian_slice(d)?;
506+
let mut e = BigNum::new()?;
507+
checked_mod_inverse(&mut e, &d, &phi)?;
508+
509+
(e, n)
510+
};
511+
512+
Ok((
513+
e.to_bigendian_vec()?,
514+
RsaData {
515+
n: n.to_bigendian_vec()?,
516+
d: Some(OsslSecret::from_vec(d.to_vec())),
517+
p: Some(OsslSecret::from_vec(p.to_vec())),
518+
q: Some(OsslSecret::from_vec(q.to_vec())),
519+
a: None,
520+
b: None,
521+
c: None,
522+
},
523+
))
524+
}
525+
526+
/// Computes the inverse of p modulo q, i.e. u ≡ p⁻¹ (mod q).
527+
pub fn inverse_p_mod_q(&self) -> Result<Vec<u8>, Error> {
528+
use crate::BigNum;
529+
530+
/// Computes the inverse of `a` modulo `n` storing the result in
531+
/// `res`.
532+
fn checked_mod_inverse(
533+
res: &mut BigNum,
534+
a: &BigNum,
535+
n: &BigNum,
536+
) -> Result<(), Error> {
537+
unsafe {
538+
let ctx = BN_CTX_secure_new();
539+
if ctx.is_null() {
540+
return Err(Error::new(ErrorKind::NullPtr));
541+
}
542+
543+
let ret = BN_mod_inverse(
544+
res.as_mut_ptr(),
545+
a.as_ptr(),
546+
n.as_ptr(),
547+
ctx,
548+
);
549+
550+
BN_CTX_free(ctx);
551+
552+
if !ret.is_null() {
553+
Ok(())
554+
} else {
555+
trace_ossl!("BN_mod_inverse()");
556+
Err(Error::new(ErrorKind::OsslError))
557+
}
558+
}
559+
}
560+
561+
/// Configures the given `BigNum` to use constant-time
562+
/// operations.
563+
fn use_constant_time_ops(bn: &mut BigNum) {
564+
unsafe {
565+
BN_set_flags(bn.as_mut_ptr(), BN_FLG_CONSTTIME as i32);
566+
}
567+
}
568+
569+
let p = BigNum::from_bigendian_slice(
570+
self.p.as_ref().ok_or(Error::new(ErrorKind::NullPtr))?,
571+
)?;
572+
let q = BigNum::from_bigendian_slice(
573+
self.q.as_ref().ok_or(Error::new(ErrorKind::NullPtr))?,
574+
)?;
575+
let mut u = BigNum::new()?;
576+
use_constant_time_ops(&mut u);
577+
checked_mod_inverse(&mut u, &p, &q)?;
578+
579+
Ok(u.to_bigendian_vec()?)
580+
}
581+
}
582+
388583
/// Wrapper to handle import/export data based on the type
389584
#[derive(Debug)]
390585
pub enum PkeyData {
@@ -1080,3 +1275,32 @@ impl Drop for EvpPkey {
10801275

10811276
unsafe impl Send for EvpPkey {}
10821277
unsafe impl Sync for EvpPkey {}
1278+
1279+
#[cfg(test)]
1280+
mod tests {
1281+
use super::*;
1282+
1283+
/// Tests importing a key from d, p, and q, and the computation of
1284+
/// the inverse of p mod q.
1285+
///
1286+
/// This test vector has been extracted from Sequoia's test suite.
1287+
#[cfg(feature = "rfc9580")]
1288+
#[test]
1289+
fn import_rsa_dpq() -> Result<(), Error> {
1290+
// The inputs.
1291+
let d = b"\x14\xC4\x3A\x0C\x3A\x79\xA4\xF7\x63\x0D\x89\x93\x63\x8B\x56\x9C\x29\x2E\xCD\xCF\xBF\xB0\xEC\x66\x52\xC3\x70\x1B\x19\x21\x73\xDE\x8B\xAC\x0E\xF2\xE1\x28\x42\x66\x56\x55\x00\x3B\xFD\x50\xC4\x7C\xBC\x9D\xEB\x7D\xF4\x81\xFC\xC3\xBF\xF7\xFF\xD0\x41\x3E\x50\x3B\x5F\x5D\x5F\x56\x67\x5E\x00\xCE\xA4\x53\xB8\x59\xA0\x40\xC8\x96\x6D\x12\x09\x27\xBE\x1D\xF1\xC2\x68\xFC\xF0\x14\xD6\x52\x77\x07\xC8\x12\x36\x9C\x9A\x5C\xAF\x43\xCC\x95\x20\xBB\x0A\x44\x94\xDD\xB4\x4F\x45\x4E\x3A\x1A\x30\x0D\x66\x40\xAC\x68\xE8\xB0\xFD\xCD\x6C\x6B\x6C\xB5\xF7\xE4\x36\x95\xC2\x96\x98\xFD\xCA\x39\x6C\x1A\x2E\x55\xAD\xB6\xE0\xF8\x2C\xFF\xBC\xD3\x32\x15\x52\x39\xB3\x92\x35\xDB\x8B\x68\xAF\x2D\x4A\x6E\x64\xB8\x28\x63\xC4\x24\x94\x2D\xA9\xDB\x93\x56\xE3\xBC\xD0\xB6\x38\x84\x04\xA4\xC6\x18\x48\xFE\xB2\xF8\xE1\x60\x37\x52\x96\x41\xA5\x79\xF6\x3D\xB7\x2A\x71\x5B\x7A\x75\xBF\x7F\xA2\x5A\xC8\xA1\x38\xF2\x5A\xBD\x14\xFC\xAF\xB4\x54\x83\xA4\xBD\x49\xA2\x8B\x91\xB0\xE0\x4A\x1B\x21\x54\x07\x19\x70\x64\x7C\x3E\x9F\x8D\x8B\xE4\x70\xD1\xE7\xBE\x4E\x5C\xCE\xF1";
1292+
let p = b"\xC8\x32\xD1\x17\x41\x4D\x8F\x37\x09\x18\x32\x4C\x4C\xF4\xA2\x15\x27\x43\x3D\xBB\xB5\xF6\x1F\xCF\xD2\xE4\x43\x61\x07\x0E\x9E\x35\x1F\x0A\x5D\xFB\x3A\x45\x74\x61\x73\x73\x7B\x5F\x1F\x87\xFB\x54\x8D\xA8\x85\x3E\xB0\xB7\xC7\xF5\xC9\x13\x99\x8D\x40\xE6\xA6\xD0\x71\x3A\xE3\x2D\x4A\xC3\xA3\xFF\xF7\x72\x82\x14\x52\xA4\xBA\x63\x0E\x17\xCA\xCA\x18\xC4\x3A\x40\x79\xF1\x86\xB3\x10\x4B\x9F\xB2\xAE\x2E\x13\x38\x8D\x2C\xF9\x88\x4C\x25\x53\xEF\xF9\xD1\x8B\x1A\x7C\xE7\xF6\x4B\x73\x51\x31\xFA\x44\x1D\x36\x65\x71\xDA\xFC\x6F";
1293+
let q = b"\xCC\x30\xE9\xCC\xCB\x31\x28\xB5\x90\xFF\x06\x62\x42\x5B\x24\x0E\x00\xFE\xE2\x37\xC4\xAC\xBB\x3B\x8F\xF2\x0E\x3F\x78\xCF\x6B\x7C\xE8\x75\x57\x7C\x15\x9D\x1A\x66\xF2\x0A\xE5\xD3\x0B\xE7\x40\xF7\xE7\x00\xB6\x86\xB5\xD9\x20\x67\xE0\x4A\xC0\x90\xA4\x13\x4D\xC9\xB0\x12\xC5\xCD\x4C\xEB\xA1\x91\x2D\x43\x58\x6E\xB6\x75\xA0\x93\xF0\x5B\xC5\x31\xCA\xB7\xC6\x22\x0C\xD3\xEC\x84\xC5\x91\xA1\x5F\x2C\x8E\x07\x5D\xA1\x98\x67\xC5\x7A\x58\x16\x71\x3D\xED\x91\x03\x0D\xD4\x25\x07\x89\x9B\x33\x98\xA3\x70\xD9\xE7\xC8\x17\xA3\xD9";
1294+
1295+
// The expected outputs.
1296+
let expect_e = b"\x01\x00\x01";
1297+
let expect_n = b"\x9f\xae\xbe\xfc\x24\x19\x92\xff\xba\xf1\xb1\x08\x3b\xcb\x52\x22\x6a\x5b\x94\xaa\xa6\xd7\x9a\x93\x17\xcf\xc9\xa6\x77\xfb\x58\x28\x1d\x64\xca\x69\xca\x91\xc8\x82\xbd\x82\x77\x08\xaa\xbf\xdd\xcd\xc0\x95\x39\x55\xef\x1e\x2a\x29\xc5\xc8\x2f\x95\xd2\xb8\xe3\x5d\xab\xdc\x47\x1e\x91\x72\xc6\x33\x09\x2c\x06\x0c\x36\x7f\x8f\x47\xa0\x60\xc8\xb2\x46\x27\xd3\x13\x84\x1c\x44\x2d\x01\xb0\xec\xc1\x0b\xfb\xfe\xe2\x15\x3e\x8d\xf7\x67\xae\xf0\xf4\xf2\x52\x74\x30\x74\x35\xc0\xe8\x95\x79\x33\x8f\x5f\x6d\x80\xa2\x1b\xfd\xac\x09\x74\xb2\x56\xd2\x49\x0d\xc4\x16\x91\x64\x12\x65\xab\x02\xf3\x63\xe6\x15\x7e\x02\xff\x94\x2a\xba\x76\x7a\x9d\x74\x4b\x93\x1e\xfd\x12\xb1\xf0\x0b\x3a\x8e\xf4\x6a\x98\xee\xb8\x0f\x12\xb9\x95\xd0\x77\x76\x2d\x75\x2d\x01\xeb\x02\x99\x20\x45\x89\x1d\xce\x95\xed\x4c\xc0\xdc\x29\xeb\xb8\x73\x42\x61\x48\x2e\xaa\x01\x44\xa9\x89\xa0\x43\x9f\x86\x33\xa2\x4c\x23\x04\x4f\x84\x8f\xec\x81\x36\xa5\xca\x46\x28\x9c\x8f\xc8\x91\xf0\x95\xfb\x06\xf1\x22\x93\x5c\x13\xac\xbb\xd3\x54\x8b\x35\xf8\x1e\xf4\x99\xe2\x88\x57\x53\xa7\x17";
1298+
let expect_u = b"\x32\xd4\xd9\x5a\x71\xa5\x4f\xf7\x04\xaa\xd1\x3b\x90\x32\xbd\xf3\x14\x29\x69\x4a\x5e\x57\x93\xa6\x88\x1d\xc1\xcb\xb4\x84\x76\x27\xb4\xaa\xf8\x99\xc7\xbb\xb4\x19\x51\x41\x18\x6d\x52\xfe\x1d\xcd\x14\x3c\x38\x9e\xf9\xa3\x2b\xed\x97\xd9\x8d\x7d\x66\x88\x38\x1f\xc9\xbf\x95\x0e\xc5\xe5\xe1\x8e\x1d\xb4\x3f\x6d\xcd\x0a\x5a\xed\xa0\xcf\xb9\x95\x88\x9a\x2c\x4c\x74\x65\xf0\xfc\xc6\xaf\x39\x00\xaa\xab\x84\xcc\x1c\xc3\x88\xc7\xd2\x58\x49\x79\xb4\xd8\x38\x47\x8a\xad\xf0\xfa\x48\xf6\xcb\x6f\x30\x6f\xf3\x95\xf9\x58\x2c\x3b";
1299+
1300+
let (e, data) = RsaData::from_dpq(d, p, q)?;
1301+
assert_eq!(e, expect_e);
1302+
assert_eq!(&data.n, expect_n);
1303+
assert_eq!(&data.inverse_p_mod_q()?, expect_u);
1304+
Ok(())
1305+
}
1306+
}

0 commit comments

Comments
 (0)