diff --git a/Cargo.lock b/Cargo.lock index be2f48c4..ddfc86f2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -19,6 +19,12 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +[[package]] +name = "base16ct" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" + [[package]] name = "base64ct" version = "1.6.0" @@ -406,9 +412,9 @@ checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" [[package]] name = "proc-macro2" -version = "1.0.69" +version = "1.0.79" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "134c189feb4956b20f6f547d2cf727d4c0fe06722b20a0eec87ed445a97f92da" +checksum = "e835ff2298f5721608eb1a980ecaee1aef2c132bf95ecc026a11b7bf3c01c02e" dependencies = [ "unicode-ident", ] @@ -441,9 +447,9 @@ checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" [[package]] name = "quote" -version = "1.0.33" +version = "1.0.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" +checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" dependencies = [ "proc-macro2", ] @@ -522,6 +528,7 @@ dependencies = [ "rand_xorshift", "serde", "serde_test", + "serdect", "sha1", "sha2 0.11.0-pre.3", "sha3", @@ -578,18 +585,18 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.192" +version = "1.0.197" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bca2a08484b285dcb282d0f67b26cadc0df8b19f8c12502c13d966bf9482f001" +checksum = "3fb1c873e1b9b056a4dc4c0c198b24c3ffa059243875552b2bd0933b1aee4ce2" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.192" +version = "1.0.197" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6c7207fbec9faa48073f3e3074cbe553af6ea512d7c21ba46e434e70ea9fbc1" +checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b" dependencies = [ "proc-macro2", "quote", @@ -605,6 +612,16 @@ dependencies = [ "serde", ] +[[package]] +name = "serdect" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a84f14a19e9a014bb9f4512488d9829a68e04ecabffb0f9904cd1ace94598177" +dependencies = [ + "base16ct", + "serde", +] + [[package]] name = "sha1" version = "0.11.0-pre.3" @@ -688,9 +705,9 @@ checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" [[package]] name = "syn" -version = "2.0.39" +version = "2.0.53" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23e78b90f2fcf45d3e842032ce32e3f2d1545ba6636271dcbf24fa306d87be7a" +checksum = "7383cd0e49fff4b6b90ca5670bfd3e9d6a733b3f90c686605aa7eec8c4996032" dependencies = [ "proc-macro2", "quote", diff --git a/Cargo.toml b/Cargo.toml index f8977424..a3dc38fe 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,6 +25,7 @@ pkcs8 = { version = "=0.11.0-pre.0", default-features = false, features = ["allo signature = { version = "=2.3.0-pre.3", default-features = false , features = ["alloc", "digest", "rand_core"] } spki = { version = "=0.8.0-pre.0", default-features = false, features = ["alloc"] } zeroize = { version = "1.5", features = ["alloc"] } +serdect = "0.2.0" # optional dependencies sha1 = { version = "=0.11.0-pre.3", optional = true, default-features = false, features = ["oid"] } diff --git a/src/key.rs b/src/key.rs index 5e6de22f..59f8b7c2 100644 --- a/src/key.rs +++ b/src/key.rs @@ -5,9 +5,11 @@ use num_bigint::Sign::Plus; use num_bigint::{BigInt, BigUint}; use num_integer::Integer; use num_traits::{FromPrimitive, One, ToPrimitive}; +use pkcs8::{DecodePrivateKey, EncodePrivateKey}; use rand_core::CryptoRngCore; #[cfg(feature = "serde")] -use serde::{Deserialize, Serialize}; +use serdect::serde::{de, ser, Deserialize, Serialize}; +use spki::{DecodePublicKey, EncodePublicKey}; use zeroize::{Zeroize, ZeroizeOnDrop}; use crate::algorithms::generate::generate_multi_prime_key_with_exp; @@ -23,7 +25,6 @@ use crate::CrtValue; /// Represents the public part of an RSA key. #[derive(Debug, Clone, Hash, PartialEq, Eq)] -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct RsaPublicKey { /// Modulus: product of prime numbers `p` and `q` n: BigUint, @@ -36,7 +37,6 @@ pub struct RsaPublicKey { /// Represents a whole RSA key, public and private parts. #[derive(Debug, Clone)] -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct RsaPrivateKey { /// Public components of the private key. pubkey_components: RsaPublicKey, @@ -45,7 +45,6 @@ pub struct RsaPrivateKey { /// Prime factors of N, contains >= 2 elements. pub(crate) primes: Vec, /// precomputed values to speed up private operations - #[cfg_attr(feature = "serde", serde(skip))] pub(crate) precomputed: Option, } @@ -531,6 +530,50 @@ fn check_public_with_max_size(public_key: &impl PublicKeyParts, max_size: usize) Ok(()) } +#[cfg(feature = "serde")] +impl Serialize for RsaPublicKey { + fn serialize(&self, serializer: S) -> core::prelude::v1::Result + where + S: serdect::serde::Serializer, + { + let der = self.to_public_key_der().map_err(ser::Error::custom)?; + serdect::slice::serialize_hex_lower_or_bin(&der, serializer) + } +} + +#[cfg(feature = "serde")] +impl<'de> Deserialize<'de> for RsaPublicKey { + fn deserialize(deserializer: D) -> core::prelude::v1::Result + where + D: serdect::serde::Deserializer<'de>, + { + let der_bytes = serdect::slice::deserialize_hex_or_bin_vec(deserializer)?; + Self::from_public_key_der(&der_bytes).map_err(de::Error::custom) + } +} + +#[cfg(feature = "serde")] +impl Serialize for RsaPrivateKey { + fn serialize(&self, serializer: S) -> core::prelude::v1::Result + where + S: ser::Serializer, + { + let der = self.to_pkcs8_der().map_err(ser::Error::custom)?; + serdect::slice::serialize_hex_lower_or_bin(&der.as_bytes(), serializer) + } +} + +#[cfg(feature = "serde")] +impl<'de> Deserialize<'de> for RsaPrivateKey { + fn deserialize(deserializer: D) -> core::prelude::v1::Result + where + D: de::Deserializer<'de>, + { + let der_bytes = serdect::slice::deserialize_hex_or_bin_vec(deserializer)?; + Self::from_pkcs8_der(&der_bytes).map_err(de::Error::custom) + } +} + #[cfg(test)] mod tests { use super::*; @@ -538,7 +581,6 @@ mod tests { use hex_literal::hex; use num_traits::{FromPrimitive, ToPrimitive}; - use pkcs8::DecodePrivateKey; use rand_chacha::{rand_core::SeedableRng, ChaCha8Rng}; #[test] @@ -640,66 +682,23 @@ mod tests { #[cfg(feature = "serde")] fn test_serde() { use rand_chacha::{rand_core::SeedableRng, ChaCha8Rng}; - use serde_test::{assert_tokens, Token}; + use serde_test::{assert_tokens, Configure, Token}; let mut rng = ChaCha8Rng::from_seed([42; 32]); let priv_key = RsaPrivateKey::new(&mut rng, 64).expect("failed to generate key"); let priv_tokens = [ - Token::Struct { - name: "RsaPrivateKey", - len: 3, - }, - Token::Str("pubkey_components"), - Token::Struct { - name: "RsaPublicKey", - len: 2, - }, - Token::Str("n"), - Token::Seq { len: Some(2) }, - Token::U32(3814409919), - Token::U32(3429654832), - Token::SeqEnd, - Token::Str("e"), - Token::Seq { len: Some(1) }, - Token::U32(65537), - Token::SeqEnd, - Token::StructEnd, - Token::Str("d"), - Token::Seq { len: Some(2) }, - Token::U32(1482162201), - Token::U32(1675500232), - Token::SeqEnd, - Token::Str("primes"), - Token::Seq { len: Some(2) }, - Token::Seq { len: Some(1) }, - Token::U32(4133289821), - Token::SeqEnd, - Token::Seq { len: Some(1) }, - Token::U32(3563808971), - Token::SeqEnd, - Token::SeqEnd, - Token::StructEnd, + Token::Str("3054020100300d06092a864886f70d01010105000440303e020100020900cc6c6130e35b46bf0203010001020863de1ac858580019020500f65cff5d020500d46b68cb02046d9a09f102047b4e3a4f020500f45065cc") ]; - assert_tokens(&priv_key, &priv_tokens); - - let priv_tokens = [ - Token::Struct { - name: "RsaPublicKey", - len: 2, - }, - Token::Str("n"), - Token::Seq { len: Some(2) }, - Token::U32(3814409919), - Token::U32(3429654832), - Token::SeqEnd, - Token::Str("e"), - Token::Seq { len: Some(1) }, - Token::U32(65537), - Token::SeqEnd, - Token::StructEnd, - ]; - assert_tokens(&RsaPublicKey::from(priv_key), &priv_tokens); + assert_tokens(&priv_key.clone().readable(), &priv_tokens); + + let priv_tokens = [Token::Str( + "3024300d06092a864886f70d01010105000313003010020900cc6c6130e35b46bf0203010001", + )]; + assert_tokens( + &RsaPublicKey::from(priv_key.clone()).readable(), + &priv_tokens, + ); } #[test] diff --git a/src/pkcs1v15/decrypting_key.rs b/src/pkcs1v15/decrypting_key.rs index 0bd6dc89..32982f6c 100644 --- a/src/pkcs1v15/decrypting_key.rs +++ b/src/pkcs1v15/decrypting_key.rs @@ -4,6 +4,8 @@ use crate::{ traits::{Decryptor, EncryptingKeypair, RandomizedDecryptor}, Result, RsaPrivateKey, }; +#[cfg(feature = "serde")] +use serde::{Deserialize, Serialize}; use alloc::vec::Vec; use rand_core::CryptoRngCore; use zeroize::ZeroizeOnDrop; @@ -12,6 +14,7 @@ use zeroize::ZeroizeOnDrop; /// /// [RFC8017 § 7.2]: https://datatracker.ietf.org/doc/html/rfc8017#section-7.2 #[derive(Debug, Clone)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct DecryptingKey { inner: RsaPrivateKey, } diff --git a/src/pkcs1v15/encrypting_key.rs b/src/pkcs1v15/encrypting_key.rs index 80db0f60..ccf5eec2 100644 --- a/src/pkcs1v15/encrypting_key.rs +++ b/src/pkcs1v15/encrypting_key.rs @@ -1,12 +1,15 @@ use super::encrypt; use crate::{traits::RandomizedEncryptor, Result, RsaPublicKey}; use alloc::vec::Vec; +#[cfg(feature = "serde")] +use serde::{Deserialize, Serialize}; use rand_core::CryptoRngCore; /// Encryption key for PKCS#1 v1.5 encryption as described in [RFC8017 § 7.2]. /// /// [RFC8017 § 7.2]: https://datatracker.ietf.org/doc/html/rfc8017#section-7.2 #[derive(Debug, Clone)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct EncryptingKey { pub(super) inner: RsaPublicKey, } diff --git a/src/pkcs1v15/signature.rs b/src/pkcs1v15/signature.rs index a07b6468..8151566b 100644 --- a/src/pkcs1v15/signature.rs +++ b/src/pkcs1v15/signature.rs @@ -3,6 +3,8 @@ use crate::algorithms::pad::uint_to_be_pad; use ::signature::SignatureEncoding; use alloc::{boxed::Box, string::ToString}; +#[cfg(feature = "serde")] +use serdect::serde::{Deserialize, Serialize}; use core::fmt::{Debug, Display, Formatter, LowerHex, UpperHex}; use num_bigint::BigUint; use spki::{ @@ -79,3 +81,29 @@ impl Display for Signature { write!(f, "{:X}", self) } } + +#[cfg(feature = "serde")] +impl Serialize for Signature { + fn serialize(&self, serializer: S) -> core::result::Result + where + S: serdect::serde::Serializer, + { + serdect::slice::serialize_hex_lower_or_bin(&self.inner.to_bytes_be(), serializer) + } +} + +#[cfg(feature = "serde")] +impl<'de> Deserialize<'de> for Signature { + fn deserialize(deserializer: D) -> core::result::Result + where + D: serdect::serde::Deserializer<'de>, + { + let bytes = serdect::slice::deserialize_hex_or_bin_vec(deserializer)?; + let inner = BigUint::from_bytes_be(&bytes); + + Ok(Self { + inner, + len: bytes.len(), + }) + } +} \ No newline at end of file diff --git a/src/pkcs1v15/signing_key.rs b/src/pkcs1v15/signing_key.rs index 24754589..35043ecd 100644 --- a/src/pkcs1v15/signing_key.rs +++ b/src/pkcs1v15/signing_key.rs @@ -1,16 +1,19 @@ use super::{oid, pkcs1v15_generate_prefix, sign, Signature, VerifyingKey}; use crate::{dummy_rng::DummyRng, Result, RsaPrivateKey}; use alloc::vec::Vec; +use spki::der::Decode; use core::marker::PhantomData; use digest::Digest; +use pkcs1::EncodeRsaPrivateKey; use pkcs8::{ spki::{ der::AnyRef, AlgorithmIdentifierRef, AssociatedAlgorithmIdentifier, SignatureAlgorithmIdentifier, - }, - AssociatedOid, EncodePrivateKey, SecretDocument, + }, AssociatedOid, EncodePrivateKey, PrivateKeyInfo, SecretDocument }; use rand_core::CryptoRngCore; +#[cfg(feature = "serde")] +use serdect::serde::{de, ser, Deserialize, Serialize}; use signature::{ hazmat::PrehashSigner, DigestSigner, Keypair, RandomizedDigestSigner, RandomizedSigner, Signer, }; @@ -253,3 +256,32 @@ where } impl ZeroizeOnDrop for SigningKey where D: Digest {} + +#[cfg(feature = "serde")] +impl Serialize for SigningKey +where + D: Digest, +{ + fn serialize(&self, serializer: S) -> core::result::Result + where + S: serdect::serde::Serializer, + { + let der = self.inner.to_pkcs8_der().map_err(ser::Error::custom)?; + serdect::slice::serialize_hex_lower_or_bin(&der.as_bytes(), serializer) + } +} + +#[cfg(feature = "serde")] +impl<'de, D> Deserialize<'de> for SigningKey +where + D: Digest + AssociatedOid, +{ + fn deserialize(deserializer: De) -> core::result::Result + where + De: serdect::serde::Deserializer<'de>, + { + let der_bytes = serdect::slice::deserialize_hex_or_bin_vec(deserializer)?; + let pki = PrivateKeyInfo::from_der(&der_bytes).map_err(de::Error::custom)?; + Self::try_from(pki).map_err(de::Error::custom) + } +} diff --git a/src/pkcs1v15/verifying_key.rs b/src/pkcs1v15/verifying_key.rs index fafb4a4d..023c6b81 100644 --- a/src/pkcs1v15/verifying_key.rs +++ b/src/pkcs1v15/verifying_key.rs @@ -7,10 +7,13 @@ use pkcs8::{ spki::{ der::AnyRef, AlgorithmIdentifierRef, AssociatedAlgorithmIdentifier, SignatureAlgorithmIdentifier, - }, - AssociatedOid, Document, EncodePublicKey, + }, AssociatedOid }; + +#[cfg(feature = "serde")] +use serdect::serde::{ser, de, Deserialize, Serialize}; use signature::{hazmat::PrehashVerifier, DigestVerifier, Verifier}; +use spki::{der::Decode, Document, EncodePublicKey, SubjectPublicKeyInfo}; /// Verifying key for `RSASSA-PKCS1-v1_5` signatures as described in [RFC8017 § 8.2]. /// @@ -201,3 +204,32 @@ where RsaPublicKey::try_from(spki).map(Self::new) } } + +#[cfg(feature = "serde")] +impl Serialize for VerifyingKey +where + D: Digest, +{ + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + let der = self.inner.to_public_key_der().map_err(ser::Error::custom)?; + serdect::slice::serialize_hex_lower_or_bin(&der, serializer) + } +} + +#[cfg(feature = "serde")] +impl<'de, D> Deserialize<'de> for VerifyingKey +where + D: Digest + AssociatedOid, +{ + fn deserialize(deserializer: De) -> Result + where + De: serde::Deserializer<'de>, + { + let der_bytes = serdect::slice::deserialize_hex_or_bin_vec(deserializer)?; + let spki = SubjectPublicKeyInfo::from_der(&der_bytes).map_err(de::Error::custom)?; + Self::try_from(spki).map_err(de::Error::custom) + } +}