From e54fb7da1a7dea1602bdb7da8e9fbbca9edc4060 Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Wed, 27 Mar 2024 15:15:14 +0200 Subject: [PATCH] pss: support DecodePrivateKey and DecodePublicKey traits (#424) * pkcs1v15: ensure that these keys are used only with the old RSA OID RSA PSS keys can be used either with the old rsaEncryption OID or with the id-RSASSA-PSS, while PKCS1v15 are limited to rsaEncryption. Enforce this limitation before adding support for is-RSASSA-PSS handling. Signed-off-by: Dmitry Baryshkov * feat: allow id-RSASSA-PSS in verify_algorithm_id() Allow both rsaEncoding and id-RSASSA-PSS OIDs in verify_algorithm_id(). Signed-off-by: Dmitry Baryshkov * pss: support DecodePrivateKey and DecodePublicKey traits Implement necessary conversion traits to enable DecodePrivateKey and DecodePublicKey traits implementation. --------- Signed-off-by: Dmitry Baryshkov --- src/encoding.rs | 25 +++++++++++---- src/pkcs1v15/signing_key.rs | 3 ++ src/pkcs1v15/verifying_key.rs | 2 ++ src/pss.rs | 5 ++- src/pss/signing_key.rs | 15 +++++++++ src/pss/verifying_key.rs | 23 +++++++++++++- tests/examples/pkcs8/rsa2048-rfc9421-priv.der | Bin 0 -> 1218 bytes tests/examples/pkcs8/rsa2048-rfc9421-pub.der | Bin 0 -> 294 bytes tests/pkcs8.rs | 30 ++++++++++++++++++ 9 files changed, 93 insertions(+), 10 deletions(-) create mode 100644 tests/examples/pkcs8/rsa2048-rfc9421-priv.der create mode 100644 tests/examples/pkcs8/rsa2048-rfc9421-pub.der diff --git a/src/encoding.rs b/src/encoding.rs index bc0fac2c..b6ce17be 100644 --- a/src/encoding.rs +++ b/src/encoding.rs @@ -8,16 +8,29 @@ use crate::{ BigUint, RsaPrivateKey, RsaPublicKey, }; use core::convert::{TryFrom, TryInto}; -use pkcs8::{der::Encode, Document, EncodePrivateKey, EncodePublicKey, SecretDocument}; +use pkcs8::{ + der::Encode, Document, EncodePrivateKey, EncodePublicKey, ObjectIdentifier, SecretDocument, +}; use zeroize::Zeroizing; +/// ObjectID for the RSA PSS keys +pub const ID_RSASSA_PSS: ObjectIdentifier = ObjectIdentifier::new_unwrap("1.2.840.113549.1.1.10"); + /// Verify that the `AlgorithmIdentifier` for a key is correct. fn verify_algorithm_id(algorithm: &pkcs8::AlgorithmIdentifierRef) -> pkcs8::spki::Result<()> { - algorithm.assert_algorithm_oid(pkcs1::ALGORITHM_OID)?; - - if algorithm.parameters_any()? != pkcs8::der::asn1::Null.into() { - return Err(pkcs8::spki::Error::KeyMalformed); - } + match algorithm.oid { + pkcs1::ALGORITHM_OID => { + if algorithm.parameters_any()? != pkcs8::der::asn1::Null.into() { + return Err(pkcs8::spki::Error::KeyMalformed); + } + } + ID_RSASSA_PSS => { + if !algorithm.parameters.is_none() { + return Err(pkcs8::spki::Error::KeyMalformed); + } + } + _ => return Err(pkcs8::spki::Error::OidUnknown { oid: algorithm.oid }), + }; Ok(()) } diff --git a/src/pkcs1v15/signing_key.rs b/src/pkcs1v15/signing_key.rs index 24754589..eb60a5bd 100644 --- a/src/pkcs1v15/signing_key.rs +++ b/src/pkcs1v15/signing_key.rs @@ -248,6 +248,9 @@ where type Error = pkcs8::Error; fn try_from(private_key_info: pkcs8::PrivateKeyInfo<'_>) -> pkcs8::Result { + private_key_info + .algorithm + .assert_algorithm_oid(pkcs1::ALGORITHM_OID)?; RsaPrivateKey::try_from(private_key_info).map(Self::new) } } diff --git a/src/pkcs1v15/verifying_key.rs b/src/pkcs1v15/verifying_key.rs index fafb4a4d..9e11c544 100644 --- a/src/pkcs1v15/verifying_key.rs +++ b/src/pkcs1v15/verifying_key.rs @@ -198,6 +198,8 @@ where type Error = pkcs8::spki::Error; fn try_from(spki: pkcs8::SubjectPublicKeyInfoRef<'_>) -> pkcs8::spki::Result { + spki.algorithm.assert_algorithm_oid(pkcs1::ALGORITHM_OID)?; + RsaPublicKey::try_from(spki).map(Self::new) } } diff --git a/src/pss.rs b/src/pss.rs index e0b94137..6d4fae8c 100644 --- a/src/pss.rs +++ b/src/pss.rs @@ -22,7 +22,7 @@ pub use self::{ use alloc::{boxed::Box, vec::Vec}; use core::fmt::{self, Debug}; -use const_oid::{AssociatedOid, ObjectIdentifier}; +use const_oid::AssociatedOid; use digest::{Digest, DynDigest, FixedOutputReset}; use num_bigint::BigUint; use pkcs1::RsaPssParams; @@ -32,6 +32,7 @@ use rand_core::CryptoRngCore; use crate::algorithms::pad::{uint_to_be_pad, uint_to_zeroizing_be_pad}; use crate::algorithms::pss::*; use crate::algorithms::rsa::{rsa_decrypt_and_check, rsa_encrypt}; +use crate::encoding::ID_RSASSA_PSS; use crate::errors::{Error, Result}; use crate::traits::PublicKeyParts; use crate::traits::SignatureScheme; @@ -240,8 +241,6 @@ fn get_pss_signature_algo_id(salt_len: u8) -> pkcs8::spki::Result(salt_len); Ok(AlgorithmIdentifierOwned { diff --git a/src/pss/signing_key.rs b/src/pss/signing_key.rs index 39b41472..be2d203d 100644 --- a/src/pss/signing_key.rs +++ b/src/pss/signing_key.rs @@ -1,4 +1,5 @@ use super::{get_pss_signature_algo_id, sign_digest, Signature, VerifyingKey}; +use crate::encoding::ID_RSASSA_PSS; use crate::{Result, RsaPrivateKey}; use const_oid::AssociatedOid; use core::marker::PhantomData; @@ -219,4 +220,18 @@ where } } +impl TryFrom> for SigningKey +where + D: Digest + AssociatedOid, +{ + type Error = pkcs8::Error; + + fn try_from(private_key_info: pkcs8::PrivateKeyInfo<'_>) -> pkcs8::Result { + private_key_info + .algorithm + .assert_algorithm_oid(ID_RSASSA_PSS)?; + RsaPrivateKey::try_from(private_key_info).map(Self::new) + } +} + impl ZeroizeOnDrop for SigningKey where D: Digest {} diff --git a/src/pss/verifying_key.rs b/src/pss/verifying_key.rs index ed065492..e98fc7c5 100644 --- a/src/pss/verifying_key.rs +++ b/src/pss/verifying_key.rs @@ -1,10 +1,11 @@ use super::{verify_digest, Signature}; +use crate::encoding::ID_RSASSA_PSS; use crate::RsaPublicKey; use core::marker::PhantomData; use digest::{Digest, FixedOutputReset}; use pkcs8::{ spki::{der::AnyRef, AlgorithmIdentifierRef, AssociatedAlgorithmIdentifier}, - Document, EncodePublicKey, + AssociatedOid, Document, EncodePublicKey, }; use signature::{hazmat::PrehashVerifier, DigestVerifier, Verifier}; @@ -156,3 +157,23 @@ where key.inner } } + +impl TryFrom> for VerifyingKey +where + D: Digest + AssociatedOid, +{ + type Error = pkcs8::spki::Error; + + fn try_from(spki: pkcs8::SubjectPublicKeyInfoRef<'_>) -> pkcs8::spki::Result { + match spki.algorithm.oid { + ID_RSASSA_PSS | pkcs1::ALGORITHM_OID => (), + _ => { + return Err(spki::Error::OidUnknown { + oid: spki.algorithm.oid, + }); + } + } + + RsaPublicKey::try_from(spki).map(Self::new) + } +} diff --git a/tests/examples/pkcs8/rsa2048-rfc9421-priv.der b/tests/examples/pkcs8/rsa2048-rfc9421-priv.der new file mode 100644 index 0000000000000000000000000000000000000000..4585234df3b88c33591dc19ec04a394f650f0e23 GIT binary patch literal 1218 zcmV;z1U>sOf&{(-0RS)y1_>&LNQUOtExwzPX+oI6340o8E!TDWNB{lBMvoxU5K z*$`GqOG9_n0`g}27ehLqO9;@7=K4%$xl zRVy31TAs|SJVaYIv`Lism=hz@9zc`UaQ>HQthy|QEYMAyS_*s^0N|h-JeE*u(MVU& zr{9879(qmZ?Vg6Wh6RD`lL5uHS+jUu^1GJmMaqtcA0?5Afy^)jP+YQJIVV$}mW%}b z4?Bj?%1%y46x2!MoQU#N7E4ebOU7?0yDGede*yeXOEfRFq0b}w(DL`iFGV!sV)k#d z&fFLwaf3qQjM!l8Avc5CVm6?94>KSG(+5cG91>M+e5Y7`jtAV~*mub!z9EKxwyl;# zP5`rp{}gcyww-_x9ycM&=3M%U6dDwg)k90SQ-p!*vUFnAPTd}*UBV*-G45l;qhOxG zi>7+M;M}31E{=I?wMbes8`6En2?BwE0K@DEb?tjwmNW{OY2B7|xX%t!tw55sb|9v% zse#%L5EI{F4B^POFu@VpaugDdApeb}TTIxZ;YFz9aKBH7$m<|Jmr|&tLgixMpG8+` zxY{+$x+)V-UE*{C3u<;C@IB&o{J-8S16D(g(S6hbQmcT$CwoXqrbEjWilBPi!vcYU z0HV<7mP~CYw50M0ktU`>8ye?moDl%ZAo>wTTTmEw-thB9!2*GS0Ev+`OQ5N$U}!)*PO)*2nWuqv8Um_7 z(k2lDxi8%=5Nh+mP-{gba7|FW;0s!tG$7B7~52g<=)Kxcz-${hGX}3#9MthFQh@4`5Ur*lyK{mEVNP%~nvylZ; zLjr+;0F4kTeTcubpreHUUNb{>U8H3->KF+;bm(bPEI%F7H#lXRgn0Hf(6a_uj+p$A zv0ondAf&n5h4F(A+hDe6@4FLfG1potr0S^E$f&mHwf&l>luZw1zdiK$0e?7Xd zKU+5W0WH3pk!fETtBoLyM10F+W5uqWq-};=cF%tEEb+^JE*(Z6j&a+>aNQia-f6y| z1>?yNIy!LVXOn!DhTwoc4&M%p>X0ukNM!erCv`gFeX<2S)jT}&4(#l!6anghJ2Rd$ z#hEj$2uBUsokgW+9ES^-Eb{(Zi7J-Yb=G>S&M%uM*Dpw*17Xzw0Nz$o+})-ux-GQe zOB4H{$dutZ^n;Q*Xf{dg4-3wX3QHzfpbk9dYMeVkl>yao_FA}ToY@do sNlQa_)dKQn`xiqxpGyePjpN03rf|-XReJePXIGjsbEU}J0s{d60l^xBrvLx| literal 0 HcmV?d00001 diff --git a/tests/pkcs8.rs b/tests/pkcs8.rs index ee597e18..17a0f00c 100644 --- a/tests/pkcs8.rs +++ b/tests/pkcs8.rs @@ -14,10 +14,17 @@ const RSA_2048_PRIV_PEM: &str = include_str!("examples/pkcs8/rsa2048-priv.pem"); #[cfg(feature = "pem")] const RSA_2048_PUB_PEM: &str = include_str!("examples/pkcs8/rsa2048-pub.pem"); +/// RSA-2048 PSS PKCS#8 private key encoded as DER +const RSA_2048_PSS_PRIV_DER: &[u8] = include_bytes!("examples/pkcs8/rsa2048-rfc9421-priv.der"); + +/// RSA-2048 PSS PKCS#8 public key encoded as DER +const RSA_2048_PSS_PUB_DER: &[u8] = include_bytes!("examples/pkcs8/rsa2048-rfc9421-pub.der"); + use hex_literal::hex; use rsa::{ pkcs1v15, pkcs8::{DecodePrivateKey, DecodePublicKey, EncodePrivateKey, EncodePublicKey}, + pss, traits::{PrivateKeyParts, PublicKeyParts}, RsaPrivateKey, RsaPublicKey, }; @@ -51,6 +58,29 @@ fn decode_rsa2048_pub_der() { let _ = pkcs1v15::VerifyingKey::::from_public_key_der(RSA_2048_PUB_DER).unwrap(); } +#[test] +fn decode_rsa2048_pss_priv_der() { + let key = RsaPrivateKey::from_pkcs8_der(RSA_2048_PSS_PRIV_DER).unwrap(); + + assert_eq!(&key.n().to_bytes_be(), &hex!("AF8B669B7AF6D1677F3DBAAF3F5B36F9012DBE9B91695F18AB8D208D447CCB6463C5AE9DA46D865C76CF7EF32CF1CB7E2E1D461F8E71DBC470DD1CB9DE69BEA005E3C90F3A3A70E467937C9586E0803E0EDF0E8CEA902F2E4864F79027753AE27DB2053CD53C3CF30EECECAB1401EA803B339E33C59933AD08470DD99D45A5681C870B982CF2FE5A892A96D775D67AAACE2F9B27D72F48A00361D50000DE5652DCDDA62CBA2DB4E04B13FBA1C894E139F483923A683649EC0F0BCE8D0A4B2658A00E3CE66A9C3B419501D570F65AB868E4FDBFA77E9DBE1B9CD91056494B4377D502F266FB17433A9F4B08D08DE3C576A670CE90557AF94F67579A3273A5C8DB")); + assert_eq!(&key.e().to_bytes_be(), &hex!("010001")); + assert_eq!(&key.d().to_bytes_be(), &hex!("9407C8A9FA426289954A17C02A7C1FDA50FD234C0A8E41EC0AD64289FE24025C10AAA5BA37EB482F76DD391F9559FD10D590480EDA4EF7552B1BBA5A9ECCAB3C445B36B44994F8981323D31E4093D670FE9768ACBA2C862CD04D9C5A0A7C1800E0A01B3C96506AD14857D0A7DF82521E7A4DE7ED9E86B7860581ED9301C5B659B3785DF2BB96EA45CA8E871F25918981CC3004505CB25E3927539F968C04FD0F3B86D0CA4E4E4714D449E39C88F254164B501E4BC66F29BB2ABC847F01FC4E4B342FB5A1CF23FAD0F2F7C52F4534E262F66FB3CEDC1821718342E28CD860EC213783DA6236A07A0F332003D30748EC1C12556D7CA7587E8E07DCE1D95EC4A611")); + assert_eq!(&key.primes()[0].to_bytes_be(), &hex!("E55FBA212239C846821579BE7E4D44336C700167A478F542032BEBF506D3945382670B7D5B08D48E1B4A46EB22E54ABE21867FB6AD96444E00B386FF14710CB69D80111E3721CBE65CFA8A141A1492D5434BB7538481EBB27462D54EDD1EA55DC2230431EE63C4A3609EC28BA67ABEE0DCA1A12E8E796BB5485A331BD27DC509")); + assert_eq!(&key.primes()[1].to_bytes_be(), &hex!("C3EC0875ED7B5B96340A9869DD9674B8CF0E52AD4092B57620A6AEA981DA0F1013DF610CE1C8B630C111DA7214128E20FF8DA55B4CD8A2E145A8E370BF4F87C8EB203E9752A8A442E562E09F455769B8DA35CCBA2A134F5DE274020B6A7620F03DE276FCBFDE2B0356438DD17DD40152AB80C1277B4849A643CB158AA07ADBC3")); + + let _ = pss::SigningKey::::from_pkcs8_der(RSA_2048_PSS_PRIV_DER).unwrap(); +} + +#[test] +fn decode_rsa2048_pss_pub_der() { + let key = RsaPublicKey::from_public_key_der(RSA_2048_PSS_PUB_DER).unwrap(); + + assert_eq!(&key.n().to_bytes_be(), &hex!("AF8B669B7AF6D1677F3DBAAF3F5B36F9012DBE9B91695F18AB8D208D447CCB6463C5AE9DA46D865C76CF7EF32CF1CB7E2E1D461F8E71DBC470DD1CB9DE69BEA005E3C90F3A3A70E467937C9586E0803E0EDF0E8CEA902F2E4864F79027753AE27DB2053CD53C3CF30EECECAB1401EA803B339E33C59933AD08470DD99D45A5681C870B982CF2FE5A892A96D775D67AAACE2F9B27D72F48A00361D50000DE5652DCDDA62CBA2DB4E04B13FBA1C894E139F483923A683649EC0F0BCE8D0A4B2658A00E3CE66A9C3B419501D570F65AB868E4FDBFA77E9DBE1B9CD91056494B4377D502F266FB17433A9F4B08D08DE3C576A670CE90557AF94F67579A3273A5C8DB")); + assert_eq!(&key.e().to_bytes_be(), &hex!("010001")); + + let _ = pss::VerifyingKey::::from_public_key_der(RSA_2048_PSS_PUB_DER).unwrap(); +} + #[test] fn encode_rsa2048_priv_der() { let key = RsaPrivateKey::from_pkcs8_der(RSA_2048_PRIV_DER).unwrap();