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..bc017393 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -28,6 +28,7 @@ zeroize = { version = "1.5", features = ["alloc"] } # optional dependencies sha1 = { version = "=0.11.0-pre.3", optional = true, default-features = false, features = ["oid"] } +serdect = { version = "0.2.0", optional = true } sha2 = { version = "=0.11.0-pre.3", optional = true, default-features = false, features = ["oid"] } serde = { version = "1.0.184", optional = true, default-features = false, features = ["derive"] } @@ -52,7 +53,7 @@ default = ["std", "pem", "u64_digit"] hazmat = [] getrandom = ["rand_core/getrandom"] nightly = ["num-bigint/nightly"] -serde = ["dep:serde", "num-bigint/serde"] +serde = ["dep:serde", "dep:serdect", "num-bigint/serde"] pem = ["pkcs1/pem", "pkcs8/pem"] pkcs5 = ["pkcs8/encryption"] u64_digit = ["num-bigint/u64_digit"] diff --git a/src/encoding.rs b/src/encoding.rs index b6ce17be..3151791d 100644 --- a/src/encoding.rs +++ b/src/encoding.rs @@ -17,7 +17,7 @@ use zeroize::Zeroizing; 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<()> { +pub(crate) fn verify_algorithm_id(algorithm: &pkcs8::AlgorithmIdentifierRef) -> pkcs8::spki::Result<()> { match algorithm.oid { pkcs1::ALGORITHM_OID => { if algorithm.parameters_any()? != pkcs8::der::asn1::Null.into() { diff --git a/src/key.rs b/src/key.rs index 5e6de22f..03931dab 100644 --- a/src/key.rs +++ b/src/key.rs @@ -6,9 +6,13 @@ use num_bigint::{BigInt, BigUint}; use num_integer::Integer; use num_traits::{FromPrimitive, One, ToPrimitive}; use rand_core::CryptoRngCore; -#[cfg(feature = "serde")] -use serde::{Deserialize, Serialize}; use zeroize::{Zeroize, ZeroizeOnDrop}; +#[cfg(feature = "serde")] +use { + serdect::serde::{de, ser, Deserialize, Serialize}, + spki::{EncodePublicKey, DecodePublicKey}, + pkcs8::{EncodePrivateKey, DecodePrivateKey} +}; use crate::algorithms::generate::generate_multi_prime_key_with_exp; use crate::algorithms::rsa::{ @@ -23,7 +27,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 +39,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 +47,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 +532,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::*; @@ -640,66 +685,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, - ]; - 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, + Token::Str("3054020100300d06092a864886f70d01010105000440303e020100020900cc6c6130e35b46bf0203010001020863de1ac858580019020500f65cff5d020500d46b68cb02046d9a09f102047b4e3a4f020500f45065cc") ]; - 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/oaep/decrypting_key.rs b/src/oaep/decrypting_key.rs index eacff38e..83ab2824 100644 --- a/src/oaep/decrypting_key.rs +++ b/src/oaep/decrypting_key.rs @@ -11,12 +11,15 @@ use alloc::{ use core::marker::PhantomData; use digest::{Digest, FixedOutputReset}; use rand_core::CryptoRngCore; +#[cfg(feature = "serde")] +use serde::{Deserialize, Serialize}; use zeroize::ZeroizeOnDrop; /// Decryption key for PKCS#1 v1.5 decryption as described in [RFC8017 § 7.1]. /// /// [RFC8017 § 7.1]: https://datatracker.ietf.org/doc/html/rfc8017#section-7.1 #[derive(Debug, Clone)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct DecryptingKey where D: Digest, @@ -94,3 +97,44 @@ where MGD: Digest + FixedOutputReset, { } + +impl PartialEq for DecryptingKey +where + D: Digest, + MGD: Digest + FixedOutputReset, +{ + fn eq(&self, other: &Self) -> bool { + self.inner == other.inner && self.label == other.label + } +} + +#[cfg(test)] +mod tests { + #[test] + #[cfg(feature = "serde")] + fn test_serde() { + use super::*; + use rand_chacha::{rand_core::SeedableRng, ChaCha8Rng}; + use serde_test::{assert_tokens, Configure, Token}; + use sha2::Sha256; + + let mut rng = ChaCha8Rng::from_seed([42; 32]); + let decrypting_key = DecryptingKey::::new( + RsaPrivateKey::new(&mut rng, 64).expect("failed to generate key"), + ); + + let tokens = [ + Token::Struct { name: "DecryptingKey", len: 4 }, + Token::Str("inner"), + Token::Str("3054020100300d06092a864886f70d01010105000440303e020100020900cc6c6130e35b46bf0203010001020863de1ac858580019020500f65cff5d020500d46b68cb02046d9a09f102047b4e3a4f020500f45065cc"), + Token::Str("label"), + Token::None, + Token::Str("phantom"), + Token::UnitStruct { name: "PhantomData", }, + Token::Str("mg_phantom"), + Token::UnitStruct { name: "PhantomData", }, + Token::StructEnd, + ]; + assert_tokens(&decrypting_key.readable(), &tokens); + } +} diff --git a/src/oaep/encrypting_key.rs b/src/oaep/encrypting_key.rs index 0951e652..1565e467 100644 --- a/src/oaep/encrypting_key.rs +++ b/src/oaep/encrypting_key.rs @@ -7,11 +7,14 @@ use alloc::{ use core::marker::PhantomData; use digest::{Digest, FixedOutputReset}; use rand_core::CryptoRngCore; +#[cfg(feature = "serde")] +use serde::{Deserialize, Serialize}; /// Encryption key for PKCS#1 v1.5 encryption as described in [RFC8017 § 7.1]. /// /// [RFC8017 § 7.1]: https://datatracker.ietf.org/doc/html/rfc8017#section-7.1 #[derive(Debug, Clone)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct EncryptingKey where D: Digest, @@ -62,3 +65,48 @@ where encrypt_digest::<_, D, MGD>(rng, &self.inner, msg, self.label.as_ref().cloned()) } } + +impl PartialEq for EncryptingKey +where + D: Digest, + MGD: Digest + FixedOutputReset, +{ + fn eq(&self, other: &Self) -> bool { + self.inner == other.inner && self.label == other.label + } +} + +#[cfg(test)] +mod tests { + + #[test] + #[cfg(feature = "serde")] + fn test_serde() { + use super::*; + use rand_chacha::{rand_core::SeedableRng, ChaCha8Rng}; + use serde_test::{assert_tokens, Configure, Token}; + + let mut rng = ChaCha8Rng::from_seed([42; 32]); + let priv_key = crate::RsaPrivateKey::new(&mut rng, 64).expect("failed to generate key"); + let encrypting_key = EncryptingKey::::new(priv_key.to_public_key()); + + let tokens = [ + Token::Struct { + name: "EncryptingKey", + len: 4, + }, + Token::Str("inner"), + Token::Str( + "3024300d06092a864886f70d01010105000313003010020900cc6c6130e35b46bf0203010001", + ), + Token::Str("label"), + Token::None, + Token::Str("phantom"), + Token::UnitStruct { name: "PhantomData", }, + Token::Str("mg_phantom"), + Token::UnitStruct { name: "PhantomData", }, + Token::StructEnd, + ]; + assert_tokens(&encrypting_key.readable(), &tokens); + } +} diff --git a/src/pkcs1v15/decrypting_key.rs b/src/pkcs1v15/decrypting_key.rs index 0bd6dc89..78aee178 100644 --- a/src/pkcs1v15/decrypting_key.rs +++ b/src/pkcs1v15/decrypting_key.rs @@ -6,12 +6,15 @@ use crate::{ }; use alloc::vec::Vec; use rand_core::CryptoRngCore; +#[cfg(feature = "serde")] +use serde::{Deserialize, Serialize}; use zeroize::ZeroizeOnDrop; /// Decryption key for PKCS#1 v1.5 decryption as described in [RFC8017 § 7.2]. /// /// [RFC8017 § 7.2]: https://datatracker.ietf.org/doc/html/rfc8017#section-7.2 -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct DecryptingKey { inner: RsaPrivateKey, } @@ -49,3 +52,26 @@ impl EncryptingKeypair for DecryptingKey { } impl ZeroizeOnDrop for DecryptingKey {} + +#[cfg(test)] +mod tests { + #[test] + #[cfg(feature = "serde")] + fn test_serde() { + use super::*; + use rand_chacha::{rand_core::SeedableRng, ChaCha8Rng}; + use serde_test::{assert_tokens, Configure, Token}; + + let mut rng = ChaCha8Rng::from_seed([42; 32]); + let decrypting_key = + DecryptingKey::new(RsaPrivateKey::new(&mut rng, 64).expect("failed to generate key")); + + let tokens = [ + Token::Struct { name: "DecryptingKey", len: 1 }, + Token::Str("inner"), + Token::Str("3054020100300d06092a864886f70d01010105000440303e020100020900cc6c6130e35b46bf0203010001020863de1ac858580019020500f65cff5d020500d46b68cb02046d9a09f102047b4e3a4f020500f45065cc"), + Token::StructEnd, + ]; + assert_tokens(&decrypting_key.readable(), &tokens); + } +} diff --git a/src/pkcs1v15/encrypting_key.rs b/src/pkcs1v15/encrypting_key.rs index 80db0f60..f1ff3fd3 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)] +#[derive(Debug, Clone, PartialEq)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct EncryptingKey { pub(super) inner: RsaPublicKey, } @@ -27,3 +30,26 @@ impl RandomizedEncryptor for EncryptingKey { encrypt(rng, &self.inner, msg) } } + +#[cfg(test)] +mod tests { + #[test] + #[cfg(feature = "serde")] + fn test_serde() { + use super::*; + use rand_chacha::{rand_core::SeedableRng, ChaCha8Rng}; + use serde_test::{assert_tokens, Configure, Token}; + + let mut rng = ChaCha8Rng::from_seed([42; 32]); + let priv_key = crate::RsaPrivateKey::new(&mut rng, 64).expect("failed to generate key"); + let encrypting_key = EncryptingKey::new(priv_key.to_public_key()); + + let tokens = [ + Token::Struct { name: "EncryptingKey", len: 1 }, + Token::Str("inner"), + Token::Str("3024300d06092a864886f70d01010105000313003010020900cc6c6130e35b46bf0203010001"), + Token::StructEnd, + ]; + assert_tokens(&encrypting_key.clone().readable(), &tokens); + } +} \ No newline at end of file diff --git a/src/pkcs1v15/signature.rs b/src/pkcs1v15/signature.rs index a07b6468..d8fd2708 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::{de, Deserialize, Serialize}; use core::fmt::{Debug, Display, Formatter, LowerHex, UpperHex}; use num_bigint::BigUint; use spki::{ @@ -79,3 +81,42 @@ 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.to_bytes(), serializer) + } +} + +#[cfg(feature = "serde")] +impl<'de> Deserialize<'de> for Signature { + fn deserialize(deserializer: D) -> core::result::Result + where + D: serdect::serde::Deserializer<'de>, + { + serdect::slice::deserialize_hex_or_bin_vec(deserializer)?.as_slice().try_into().map_err(de::Error::custom) + } +} + +#[cfg(test)] +mod tests { + #[test] + #[cfg(feature = "serde")] + fn test_serde() { + use super::*; + use serde_test::{assert_tokens, Configure, Token}; + let signature = Signature { + inner: BigUint::new(Vec::from([42])), + len: 1, + }; + + let tokens = [ + Token::Str("2a"), + ]; + assert_tokens(&signature.readable(), &tokens); + } +} \ No newline at end of file diff --git a/src/pkcs1v15/signing_key.rs b/src/pkcs1v15/signing_key.rs index eb60a5bd..ac6fa3e7 100644 --- a/src/pkcs1v15/signing_key.rs +++ b/src/pkcs1v15/signing_key.rs @@ -7,10 +7,15 @@ use pkcs8::{ spki::{ der::AnyRef, AlgorithmIdentifierRef, AssociatedAlgorithmIdentifier, SignatureAlgorithmIdentifier, - }, - AssociatedOid, EncodePrivateKey, SecretDocument, + }, AssociatedOid, EncodePrivateKey, SecretDocument }; use rand_core::CryptoRngCore; +#[cfg(feature = "serde")] +use { + pkcs8::DecodePrivateKey, + serdect::serde::{de, ser, Deserialize, Serialize}, +}; + use signature::{ hazmat::PrehashSigner, DigestSigner, Keypair, RandomizedDigestSigner, RandomizedSigner, Signer, }; @@ -256,3 +261,59 @@ where } impl ZeroizeOnDrop for SigningKey where D: Digest {} + +impl PartialEq for SigningKey where D: Digest { + fn eq(&self, other: &Self) -> bool { + self.inner == other.inner && self.prefix == other.prefix + } +} + +#[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.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)?; + Self::from_pkcs8_der(&der_bytes).map_err(de::Error::custom) + } +} + +#[cfg(test)] +mod tests { + #[test] + #[cfg(feature = "serde")] + fn test_serde() { + use super::*; + use rand_chacha::{rand_core::SeedableRng, ChaCha8Rng}; + use serde_test::{assert_tokens, Configure, Token}; + use sha2::Sha256; + + let mut rng = ChaCha8Rng::from_seed([42; 32]); + let priv_key = crate::RsaPrivateKey::new(&mut rng, 64).expect("failed to generate key"); + let signing_key = SigningKey::::new(priv_key); + + let tokens = [ + Token::Str("3054020100300d06092a864886f70d01010105000440303e020100020900cc6c6130e35b46bf0203010001020863de1ac858580019020500f65cff5d020500d46b68cb02046d9a09f102047b4e3a4f020500f45065cc") + ]; + + assert_tokens(&signing_key.readable(), &tokens); + } +} \ No newline at end of file diff --git a/src/pkcs1v15/verifying_key.rs b/src/pkcs1v15/verifying_key.rs index 9e11c544..baa77c98 100644 --- a/src/pkcs1v15/verifying_key.rs +++ b/src/pkcs1v15/verifying_key.rs @@ -8,9 +8,17 @@ use pkcs8::{ der::AnyRef, AlgorithmIdentifierRef, AssociatedAlgorithmIdentifier, SignatureAlgorithmIdentifier, }, - AssociatedOid, Document, EncodePublicKey, + AssociatedOid, }; + +#[cfg(feature = "serde")] +use { + serdect::serde::{de, ser, Deserialize, Serialize}, + spki::DecodePublicKey, +}; + use signature::{hazmat::PrehashVerifier, DigestVerifier, Verifier}; +use spki::{Document, EncodePublicKey}; /// Verifying key for `RSASSA-PKCS1-v1_5` signatures as described in [RFC8017 § 8.2]. /// @@ -203,3 +211,63 @@ where RsaPublicKey::try_from(spki).map(Self::new) } } + +impl PartialEq for VerifyingKey +where + D: Digest, +{ + fn eq(&self, other: &Self) -> bool { + self.inner == other.inner && self.prefix == other.prefix + } +} + +#[cfg(feature = "serde")] +impl Serialize for VerifyingKey +where + D: Digest, +{ + fn serialize(&self, serializer: S) -> Result + where + S: 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, 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)?; + Self::from_public_key_der(&der_bytes).map_err(de::Error::custom) + } +} + +#[cfg(test)] +mod tests { + #[test] + #[cfg(feature = "serde")] + fn test_serde() { + use super::*; + use rand_chacha::{rand_core::SeedableRng, ChaCha8Rng}; + use serde_test::{assert_tokens, Configure, Token}; + use sha2::Sha256; + + let mut rng = ChaCha8Rng::from_seed([42; 32]); + let priv_key = crate::RsaPrivateKey::new(&mut rng, 64).expect("failed to generate key"); + let pub_key = priv_key.to_public_key(); + let verifying_key = VerifyingKey::::new(pub_key); + + let tokens = [ + Token::Str("3024300d06092a864886f70d01010105000313003010020900cc6c6130e35b46bf0203010001") + ]; + + assert_tokens(&verifying_key.readable(), &tokens); + } +} diff --git a/src/pss/blinded_signing_key.rs b/src/pss/blinded_signing_key.rs index adc0ff5f..5d46d5a8 100644 --- a/src/pss/blinded_signing_key.rs +++ b/src/pss/blinded_signing_key.rs @@ -15,7 +15,11 @@ use signature::{ hazmat::RandomizedPrehashSigner, Keypair, RandomizedDigestSigner, RandomizedSigner, }; use zeroize::ZeroizeOnDrop; - +#[cfg(feature = "serde")] +use { + serdect::serde::{de, ser, Deserialize, Serialize}, + pkcs8::DecodePrivateKey, +}; /// Signing key for producing "blinded" RSASSA-PSS signatures as described in /// [draft-irtf-cfrg-rsa-blind-signatures](https://datatracker.ietf.org/doc/draft-irtf-cfrg-rsa-blind-signatures/). #[derive(Debug, Clone)] @@ -197,4 +201,74 @@ where } } +impl TryFrom> for BlindedSigningKey +where + D: Digest + AssociatedOid, +{ + type Error = pkcs8::Error; + + fn try_from(private_key_info: pkcs8::PrivateKeyInfo<'_>) -> pkcs8::Result { + RsaPrivateKey::try_from(private_key_info).map(Self::new) + } +} + impl ZeroizeOnDrop for BlindedSigningKey where D: Digest {} + +impl PartialEq for BlindedSigningKey +where + D: Digest, +{ + fn eq(&self, other: &Self) -> bool { + self.inner == other.inner && self.salt_len == other.salt_len + } +} + +#[cfg(feature = "serde")] +impl Serialize for BlindedSigningKey +where + D: Digest, +{ + fn serialize(&self, serializer: S) -> core::result::Result + where + S: serde::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, D> Deserialize<'de> for BlindedSigningKey +where + D: Digest + AssociatedOid, +{ + fn deserialize(deserializer: De) -> core::result::Result + where + De: serde::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 { + #[test] + #[cfg(feature = "serde")] + fn test_serde() { + use super::*; + use rand_chacha::{rand_core::SeedableRng, ChaCha8Rng}; + use serde_test::{assert_tokens, Configure, Token}; + use sha2::Sha256; + + let mut rng = ChaCha8Rng::from_seed([42; 32]); + let signing_key = BlindedSigningKey::::new( + RsaPrivateKey::new(&mut rng, 64).expect("failed to generate key"), + ); + + let tokens = [ + Token::Str("3054020100300d06092a864886f70d01010105000440303e020100020900cc6c6130e35b46bf0203010001020863de1ac858580019020500f65cff5d020500d46b68cb02046d9a09f102047b4e3a4f020500f45065cc") + ]; + assert_tokens(&signing_key.readable(), &tokens); + } +} diff --git a/src/pss/signature.rs b/src/pss/signature.rs index 031e2201..d7d32ae8 100644 --- a/src/pss/signature.rs +++ b/src/pss/signature.rs @@ -5,6 +5,8 @@ use ::signature::SignatureEncoding; use alloc::{boxed::Box, string::ToString}; use core::fmt::{Debug, Display, Formatter, LowerHex, UpperHex}; use num_bigint::BigUint; +#[cfg(feature = "serde")] +use serdect::serde::{de, Deserialize, Serialize}; use spki::{ der::{asn1::BitString, Result as DerResult}, SignatureBitStringEncoding, @@ -73,3 +75,45 @@ 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.to_bytes(), serializer) + } +} + +#[cfg(feature = "serde")] +impl<'de> Deserialize<'de> for Signature { + fn deserialize(deserializer: D) -> core::result::Result + where + D: serdect::serde::Deserializer<'de>, + { + serdect::slice::deserialize_hex_or_bin_vec(deserializer)? + .as_slice() + .try_into() + .map_err(de::Error::custom) + } +} + +#[cfg(test)] +mod tests { + #[test] + #[cfg(feature = "serde")] + fn test_serde() { + use super::*; + use serde_test::{assert_tokens, Configure, Token}; + let signature = Signature { + inner: BigUint::new(Vec::from([42])), + len: 1, + }; + + let tokens = [ + Token::Str("2a"), + ]; + assert_tokens(&signature.readable(), &tokens); + } +} \ No newline at end of file diff --git a/src/pss/signing_key.rs b/src/pss/signing_key.rs index be2d203d..0ed526bd 100644 --- a/src/pss/signing_key.rs +++ b/src/pss/signing_key.rs @@ -1,5 +1,5 @@ use super::{get_pss_signature_algo_id, sign_digest, Signature, VerifyingKey}; -use crate::encoding::ID_RSASSA_PSS; +use crate::encoding::verify_algorithm_id; use crate::{Result, RsaPrivateKey}; use const_oid::AssociatedOid; use core::marker::PhantomData; @@ -16,6 +16,11 @@ use signature::{ hazmat::RandomizedPrehashSigner, Keypair, RandomizedDigestSigner, RandomizedSigner, }; use zeroize::ZeroizeOnDrop; +#[cfg(feature = "serde")] +use { + pkcs8::DecodePrivateKey, + serdect::serde::{de, ser, Deserialize, Serialize}, +}; #[cfg(feature = "getrandom")] use { @@ -227,11 +232,68 @@ where type Error = pkcs8::Error; fn try_from(private_key_info: pkcs8::PrivateKeyInfo<'_>) -> pkcs8::Result { - private_key_info - .algorithm - .assert_algorithm_oid(ID_RSASSA_PSS)?; + verify_algorithm_id(&private_key_info.algorithm)?; RsaPrivateKey::try_from(private_key_info).map(Self::new) } } impl ZeroizeOnDrop for SigningKey where D: Digest {} + +impl PartialEq for SigningKey +where + D: Digest, +{ + fn eq(&self, other: &Self) -> bool { + self.inner == other.inner && self.salt_len == other.salt_len + } +} + +#[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.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)?; + Self::from_pkcs8_der(&der_bytes).map_err(de::Error::custom) + } +} + +#[cfg(test)] +mod tests { + #[test] + #[cfg(feature = "serde")] + fn test_serde() { + use super::*; + use rand_chacha::{rand_core::SeedableRng, ChaCha8Rng}; + use serde_test::{assert_tokens, Configure, Token}; + use sha2::Sha256; + + let mut rng = ChaCha8Rng::from_seed([42; 32]); + let priv_key = crate::RsaPrivateKey::new(&mut rng, 64).expect("failed to generate key"); + let signing_key = SigningKey::::new(priv_key); + + let tokens = [ + Token::Str("3054020100300d06092a864886f70d01010105000440303e020100020900cc6c6130e35b46bf0203010001020863de1ac858580019020500f65cff5d020500d46b68cb02046d9a09f102047b4e3a4f020500f45065cc") + ]; + + assert_tokens(&signing_key.readable(), &tokens); + } +} diff --git a/src/pss/verifying_key.rs b/src/pss/verifying_key.rs index e98fc7c5..b1ea02e6 100644 --- a/src/pss/verifying_key.rs +++ b/src/pss/verifying_key.rs @@ -8,6 +8,11 @@ use pkcs8::{ AssociatedOid, Document, EncodePublicKey, }; use signature::{hazmat::PrehashVerifier, DigestVerifier, Verifier}; +#[cfg(feature = "serde")] +use { + serdect::serde::{de, ser, Deserialize, Serialize}, + spki::DecodePublicKey, +}; /// Verifying key for checking the validity of RSASSA-PSS signatures as /// described in [RFC8017 § 8.1]. @@ -165,7 +170,7 @@ where type Error = pkcs8::spki::Error; fn try_from(spki: pkcs8::SubjectPublicKeyInfoRef<'_>) -> pkcs8::spki::Result { - match spki.algorithm.oid { + match spki.algorithm.oid { ID_RSASSA_PSS | pkcs1::ALGORITHM_OID => (), _ => { return Err(spki::Error::OidUnknown { @@ -177,3 +182,63 @@ where RsaPublicKey::try_from(spki).map(Self::new) } } + +impl PartialEq for VerifyingKey +where + D: Digest, +{ + fn eq(&self, other: &Self) -> bool { + self.inner == other.inner && self.salt_len == other.salt_len + } +} + +#[cfg(feature = "serde")] +impl Serialize for VerifyingKey +where + D: Digest, +{ + fn serialize(&self, serializer: S) -> Result + where + S: 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, 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)?; + Self::from_public_key_der(&der_bytes).map_err(de::Error::custom) + } +} + +#[cfg(test)] +mod tests { + #[test] + #[cfg(feature = "serde")] + fn test_serde() { + use super::*; + use rand_chacha::{rand_core::SeedableRng, ChaCha8Rng}; + use serde_test::{assert_tokens, Configure, Token}; + use sha2::Sha256; + + let mut rng = ChaCha8Rng::from_seed([42; 32]); + let priv_key = crate::RsaPrivateKey::new(&mut rng, 64).expect("failed to generate key"); + let pub_key = priv_key.to_public_key(); + let verifying_key = VerifyingKey::::new(pub_key); + + let tokens = [ + Token::Str("3024300d06092a864886f70d01010105000313003010020900cc6c6130e35b46bf0203010001") + ]; + + assert_tokens(&verifying_key.readable(), &tokens); + } +}