diff --git a/.github/workflows/bedrock.yml b/.github/workflows/bedrock.yml index d9bea7b..c0a9e6a 100644 --- a/.github/workflows/bedrock.yml +++ b/.github/workflows/bedrock.yml @@ -48,6 +48,24 @@ jobs: components: clippy - run: cargo clippy --all-features -- -D warnings + conventional-signature-features: + runs-on: ubuntu-latest + strategy: + matrix: + feature: + - ecdsa-signatures + - ed25519-signatures + - rsa-signatures + steps: + - uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 # v5 + with: + persist-credentials: false + - uses: dtolnay/rust-toolchain@d1031067263f94b142dd6c0ce24c5eb9d02d52a0 # master + with: + toolchain: stable + components: clippy + - run: cargo clippy --lib --no-default-features --features "${{ matrix.feature }}" -- -D warnings + doc: runs-on: ubuntu-latest steps: diff --git a/CHANGELOG.md b/CHANGELOG.md index cf2a6cf..a540845 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,19 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased +### Added + +- Added independent `ecdsa-signatures`, `ed25519-signatures`, and + `rsa-signatures` feature flags for conventional signature operations. + +### Changed + +- Replaced crate-local byte serialization wrappers with direct `serdect` + adapters and limited `hex` to test-only formatting and vector decoding. +- Retained `classical-signatures` as a compatibility umbrella while allowing + downstream consumers to omit the RSA implementation affected by + RUSTSEC-2023-0071. + ## v0.5.2 - 2026-09-01 ### Added diff --git a/Cargo.lock b/Cargo.lock index b6d8ce4..1cbac04 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -862,9 +862,6 @@ name = "hex" version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" -dependencies = [ - "serde", -] [[package]] name = "hkdf" @@ -1943,7 +1940,7 @@ dependencies = [ [[package]] name = "tectonic-bedrock" -version = "0.5.2" +version = "0.5.3" dependencies = [ "aes", "aes-gcm", diff --git a/Cargo.toml b/Cargo.toml index 6a3ab9d..17cc4a5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,13 +12,15 @@ name = "tectonic-bedrock" readme = "README.md" repository = "https://github.com/tectonic-labs/bedrock" rust-version = "1.95" -version = "0.5.2" +version = "0.5.3" [features] bird-of-prey = ["dep:curve25519-dalek", "falcon", "hmac", "ml-dsa", "sha2", "zeroize", "kgen", "sign", "vrfy"] -classical-signatures = ["dep:ed25519-dalek", "dep:p256", "dep:p384", "dep:rsa", "dep:signature", "p256/ecdsa", "p256/pkcs8", "p384/ecdsa", "p384/pkcs8", "random", "rand_core_010", "sha2", "zeroize"] +classical-signatures = ["ecdsa-signatures", "ed25519-signatures", "rsa-signatures"] decp = [] default = ["eth_falcon", "falcon", "ml-dsa", "slh-dsa", "mayo", "decp", "encp", "kgen", "sign", "vrfy", "hhd"] +ecdsa-signatures = ["dep:p256", "dep:p384", "dep:signature", "p256/ecdsa", "p256/pkcs8", "p384/ecdsa", "p384/pkcs8", "sha2", "zeroize"] +ed25519-signatures = ["dep:ed25519-dalek", "dep:signature", "zeroize"] encp = [] eth_falcon = ["falcon", "rand_chacha", "rand_core", "fn-dsa-comm/eth_falcon", "fn-dsa-sign?/eth_falcon", "fn-dsa-vrfy?/eth_falcon", "zeroize"] falcon = ["fn-dsa-comm"] @@ -32,6 +34,7 @@ mceliece = ["dep:pq-mceliece", "rand_core_010", "getrandom_v04"] ml-dsa = ["dep:ml-dsa", "rand_core_010", "getrandom_v04"] ml-kem = ["dep:ml-kem"] random = ["getrandom_v04"] +rsa-signatures = ["dep:rsa", "dep:signature", "random", "rand_core_010", "sha2", "zeroize"] sign = ["fn-dsa-comm", "fn-dsa-sign", "rand_core", "rand_core/getrandom"] slh-dsa = ["dep:slh-dsa", "rand_core_010", "getrandom_v04"] sntrup = ["dep:sntrup", "rand_core_010", "getrandom_v04"] @@ -51,7 +54,6 @@ curve25519-dalek = { version = "5", default-features = false, features = ["alloc ed25519-dalek = { version = "3.0.0", default-features = false, features = ["alloc", "fast", "pkcs8", "signature", "zeroize"], optional = true } frodo-kem-rs = { version = "0.9", default-features = false, features = ["frodo"], optional = true } getrandom_v04 = { package = "getrandom", version = "0.4", features = ["sys_rng"], optional = true } -hex = "0.4" hkdf = { version = "0.13.0", optional = true } hmac = { version = "0.13", features = ["zeroize"], optional = true } hqc-kem = { version = "=0.1.0-rc.0", default-features = false, features = ["alloc", "dcap", "ecap", "kgen"], optional = true } @@ -118,7 +120,7 @@ version = "0.3.0" ciborium = "0.2.2" criterion = "0.8" falcon-rust = "0.1.3" -hex = { version = "0.4", features = ["serde"] } +hex = "0.4" postcard = { version = "1.0", default-features = false, features = ["use-std"] } rand = "0.10" rand_chacha = "0.10" diff --git a/README.md b/README.md index b6cde0a..3d0c7b0 100644 --- a/README.md +++ b/README.md @@ -528,8 +528,16 @@ Control which algorithms and operations are enabled: - `mceliece` - Enable Classic McEliece key encapsulation - `frodo` - Enable FrodoKEM key encapsulation - `hqc` - Enable HQC key encapsulation and, with `hhd`, HQC HD derivation -- `classical-signatures` - Enable transport-neutral RSA, P-256 ECDSA, P-384 - ECDSA, and Ed25519 signing, verification, and private-key loading +- `ecdsa-signatures` - Enable transport-neutral P-256 and P-384 ECDSA signing, + verification, and private-key loading +- `ed25519-signatures` - Enable transport-neutral Ed25519 signing, + verification, and private-key loading +- `rsa-signatures` - Enable transport-neutral RSA signing, verification, and + private-key loading. This feature is independently opt-in because the current + RustCrypto RSA implementation is affected by + [RUSTSEC-2023-0071](https://rustsec.org/advisories/RUSTSEC-2023-0071.html). +- `classical-signatures` - Compatibility umbrella enabling `ecdsa-signatures`, + `ed25519-signatures`, and `rsa-signatures` - `key-agreement` - Enable ephemeral X25519, P-256, and P-384 key agreement (also enables `random`) - `sntrup` - Enable Streamlined NTRU Prime key encapsulation @@ -594,7 +602,13 @@ tectonic-bedrock = { version = "0.5.2", default-features = false, features = ["k Conventional signature operations and private-key loading only: ```toml -tectonic-bedrock = { version = "0.5.2", default-features = false, features = ["classical-signatures"] } +tectonic-bedrock = { version = "0.5.3", default-features = false, features = ["classical-signatures"] } +``` + +RSA-free conventional signature operations: + +```toml +tectonic-bedrock = { version = "0.5.3", default-features = false, features = ["ecdsa-signatures", "ed25519-signatures"] } ``` ## Error Handling diff --git a/src/bird_of_prey.rs b/src/bird_of_prey.rs index d879054..8a988b0 100644 --- a/src/bird_of_prey.rs +++ b/src/bird_of_prey.rs @@ -24,7 +24,7 @@ use crate::det_rng::DetRng; use crate::error::{Error, Result}; use crate::ml_dsa::{MlDsaScheme, MlDsaSignature, MlDsaSigningKey, MlDsaVerificationKey}; -use crate::{deserialize_hex_or_bin, os_rng, serialize_hex_or_bin}; +use crate::os_rng; use curve25519_dalek::EdwardsPoint; use curve25519_dalek::edwards::CompressedEdwardsY; use curve25519_dalek::scalar::Scalar; @@ -128,8 +128,8 @@ serde_impl!(BirdOfPreyScheme); pub(crate) struct InnerBirdOfPrey { scheme: BirdOfPreyScheme, #[serde( - serialize_with = "serialize_hex_or_bin", - deserialize_with = "deserialize_hex_or_bin" + serialize_with = "serdect::slice::serialize_hex_lower_or_bin", + deserialize_with = "serdect::slice::deserialize_hex_or_bin_vec" )] value: Vec, } diff --git a/src/classical_signature.rs b/src/classical_signature.rs index c8a0d8c..f76a839 100644 --- a/src/classical_signature.rs +++ b/src/classical_signature.rs @@ -2,26 +2,49 @@ use core::fmt; +#[cfg(feature = "ed25519-signatures")] use ed25519_dalek::{ Signature as Ed25519Signature, SigningKey as Ed25519SigningKey, VerifyingKey as Ed25519VerifyingKey, }; +#[cfg(feature = "ecdsa-signatures")] use p256::ecdsa::{ Signature as P256Signature, SigningKey as P256SigningKey, VerifyingKey as P256VerifyingKey, }; +#[cfg(feature = "ecdsa-signatures")] use p384::ecdsa::{ Signature as P384Signature, SigningKey as P384SigningKey, VerifyingKey as P384VerifyingKey, }; +#[cfg(feature = "rsa-signatures")] use rsa::pkcs1::{DecodeRsaPrivateKey, DecodeRsaPublicKey, EncodeRsaPublicKey}; -use rsa::pkcs8::{DecodePrivateKey, EncodePublicKey}; +#[cfg(feature = "rsa-signatures")] use rsa::pss::{Signature as RsaPssSignature, VerifyingKey as RsaPssVerifyingKey}; +#[cfg(feature = "rsa-signatures")] use rsa::traits::{PublicKeyParts, SignatureScheme as RsaSignatureScheme}; +#[cfg(feature = "rsa-signatures")] use rsa::{Pkcs1v15Sign, Pss, RsaPrivateKey, RsaPublicKey}; -use sha2::{Digest as _, Sha256, Sha384, Sha512}; +#[cfg(feature = "rsa-signatures")] +use sha2::Digest as _; +#[cfg(any(feature = "ecdsa-signatures", feature = "rsa-signatures"))] +use sha2::{Sha256, Sha384, Sha512}; +#[cfg(any(feature = "ecdsa-signatures", feature = "ed25519-signatures"))] use signature::Signer as _; +#[cfg(any(feature = "ecdsa-signatures", feature = "rsa-signatures"))] use signature::hazmat::PrehashVerifier; use thiserror::Error as ThisError; +#[cfg(all(not(feature = "ecdsa-signatures"), feature = "ed25519-signatures"))] +use ed25519_dalek::pkcs8::{DecodePrivateKey as _, EncodePublicKey as _}; +#[cfg(feature = "ecdsa-signatures")] +use p256::pkcs8::{DecodePrivateKey as _, EncodePublicKey as _}; +#[cfg(all( + not(feature = "ecdsa-signatures"), + not(feature = "ed25519-signatures"), + feature = "rsa-signatures" +))] +use rsa::pkcs8::{DecodePrivateKey as _, EncodePublicKey as _}; + +#[cfg(feature = "rsa-signatures")] const RSA_MINIMUM_BITS: u32 = 2048; /// Errors returned by conventional signature operations. @@ -119,9 +142,13 @@ pub enum ClassicalVerificationAlgorithm { } enum SigningKeyInner { + #[cfg(feature = "rsa-signatures")] Rsa(RsaPrivateKey), + #[cfg(feature = "ecdsa-signatures")] EcdsaP256(P256SigningKey), + #[cfg(feature = "ecdsa-signatures")] EcdsaP384(P384SigningKey), + #[cfg(feature = "ed25519-signatures")] Ed25519(Ed25519SigningKey), } @@ -133,17 +160,21 @@ pub struct ClassicalSigningKey { } impl ClassicalSigningKey { - /// Loads RSA, P-256, P-384, or Ed25519 key material from PKCS#8 DER. + /// Loads enabled conventional key material from PKCS#8 DER. pub fn from_pkcs8_der(der: &[u8]) -> Result { + #[cfg(feature = "rsa-signatures")] if let Ok(key) = RsaPrivateKey::from_pkcs8_der(der) { return Self::from_rsa(key); } + #[cfg(feature = "ecdsa-signatures")] if let Ok(key) = p256::SecretKey::from_pkcs8_der(der) { return Self::from_p256(P256SigningKey::from(key)); } + #[cfg(feature = "ecdsa-signatures")] if let Ok(key) = p384::SecretKey::from_pkcs8_der(der) { return Self::from_p384(P384SigningKey::from(key)); } + #[cfg(feature = "ed25519-signatures")] if let Ok(key) = Ed25519SigningKey::from_pkcs8_der(der) { return Self::from_ed25519(key); } @@ -151,6 +182,7 @@ impl ClassicalSigningKey { } /// Loads an RSA private key from PKCS#1 DER. + #[cfg(feature = "rsa-signatures")] pub fn from_pkcs1_der(der: &[u8]) -> Result { RsaPrivateKey::from_pkcs1_der(der) .map_err(|_| ClassicalSignatureError::InvalidPrivateKey) @@ -158,6 +190,7 @@ impl ClassicalSigningKey { } /// Loads a P-256 or P-384 private key from SEC1 DER. + #[cfg(feature = "ecdsa-signatures")] pub fn from_sec1_der(der: &[u8]) -> Result { if let Ok(key) = p256::SecretKey::from_sec1_der(der) { return Self::from_p256(P256SigningKey::from(key)); @@ -171,9 +204,13 @@ impl ClassicalSigningKey { /// Returns the key's public-key family. pub const fn algorithm(&self) -> ClassicalSignatureAlgorithm { match self.inner { + #[cfg(feature = "rsa-signatures")] SigningKeyInner::Rsa(_) => ClassicalSignatureAlgorithm::Rsa, + #[cfg(feature = "ecdsa-signatures")] SigningKeyInner::EcdsaP256(_) => ClassicalSignatureAlgorithm::EcdsaP256, + #[cfg(feature = "ecdsa-signatures")] SigningKeyInner::EcdsaP384(_) => ClassicalSignatureAlgorithm::EcdsaP384, + #[cfg(feature = "ed25519-signatures")] SigningKeyInner::Ed25519(_) => ClassicalSignatureAlgorithm::Ed25519, } } @@ -226,29 +263,40 @@ impl ClassicalSigningKey { return Err(ClassicalSignatureError::UnsupportedScheme); } - match (&self.inner, scheme) { - (SigningKeyInner::EcdsaP256(key), ClassicalSignatureScheme::EcdsaP256Sha256) => { - let signature: P256Signature = key - .try_sign(message) - .map_err(|_| ClassicalSignatureError::SigningFailed)?; - let signature = signature.normalize_s(); - Ok(signature.to_der().as_bytes().to_vec()) - } - (SigningKeyInner::EcdsaP384(key), ClassicalSignatureScheme::EcdsaP384Sha384) => { - let signature: P384Signature = key - .try_sign(message) - .map_err(|_| ClassicalSignatureError::SigningFailed)?; - let signature = signature.normalize_s(); - Ok(signature.to_der().as_bytes().to_vec()) - } - (SigningKeyInner::Ed25519(key), ClassicalSignatureScheme::Ed25519) => { - Ok(key.sign(message).to_bytes().to_vec()) - } - (SigningKeyInner::Rsa(key), scheme) => sign_rsa(key, scheme, message), - _ => Err(ClassicalSignatureError::UnsupportedScheme), + match &self.inner { + #[cfg(feature = "ecdsa-signatures")] + SigningKeyInner::EcdsaP256(key) => match scheme { + ClassicalSignatureScheme::EcdsaP256Sha256 => { + let signature: P256Signature = key + .try_sign(message) + .map_err(|_| ClassicalSignatureError::SigningFailed)?; + let signature = signature.normalize_s(); + Ok(signature.to_der().as_bytes().to_vec()) + } + _ => Err(ClassicalSignatureError::UnsupportedScheme), + }, + #[cfg(feature = "ecdsa-signatures")] + SigningKeyInner::EcdsaP384(key) => match scheme { + ClassicalSignatureScheme::EcdsaP384Sha384 => { + let signature: P384Signature = key + .try_sign(message) + .map_err(|_| ClassicalSignatureError::SigningFailed)?; + let signature = signature.normalize_s(); + Ok(signature.to_der().as_bytes().to_vec()) + } + _ => Err(ClassicalSignatureError::UnsupportedScheme), + }, + #[cfg(feature = "ed25519-signatures")] + SigningKeyInner::Ed25519(key) => match scheme { + ClassicalSignatureScheme::Ed25519 => Ok(key.sign(message).to_bytes().to_vec()), + _ => Err(ClassicalSignatureError::UnsupportedScheme), + }, + #[cfg(feature = "rsa-signatures")] + SigningKeyInner::Rsa(key) => sign_rsa(key, scheme, message), } } + #[cfg(feature = "rsa-signatures")] fn from_rsa(key: RsaPrivateKey) -> Result { if key.n().bits() < RSA_MINIMUM_BITS { return Err(ClassicalSignatureError::InvalidPrivateKey); @@ -272,6 +320,7 @@ impl ClassicalSigningKey { }) } + #[cfg(feature = "ecdsa-signatures")] fn from_p256(key: P256SigningKey) -> Result { let verifying_key = key.verifying_key(); let public_key = verifying_key.to_sec1_point(false).as_bytes().to_vec(); @@ -287,6 +336,7 @@ impl ClassicalSigningKey { }) } + #[cfg(feature = "ecdsa-signatures")] fn from_p384(key: P384SigningKey) -> Result { let verifying_key = key.verifying_key(); let public_key = verifying_key.to_sec1_point(false).as_bytes().to_vec(); @@ -302,6 +352,7 @@ impl ClassicalSigningKey { }) } + #[cfg(feature = "ed25519-signatures")] fn from_ed25519(key: Ed25519SigningKey) -> Result { let verifying_key = key.verifying_key(); let public_key = verifying_key.to_bytes().to_vec(); @@ -336,24 +387,40 @@ pub fn verify( signature: &[u8], ) -> Result<(), ClassicalSignatureError> { match algorithm { + #[cfg(feature = "ecdsa-signatures")] ClassicalVerificationAlgorithm::EcdsaP256Sha256 => { verify_p256::(public_key, message, signature) } + #[cfg(feature = "ecdsa-signatures")] ClassicalVerificationAlgorithm::EcdsaP256Sha384 => { verify_p256::(public_key, message, signature) } + #[cfg(feature = "ecdsa-signatures")] ClassicalVerificationAlgorithm::EcdsaP256Sha512 => { verify_p256::(public_key, message, signature) } + #[cfg(feature = "ecdsa-signatures")] ClassicalVerificationAlgorithm::EcdsaP384Sha256 => { verify_p384::(public_key, message, signature) } + #[cfg(feature = "ecdsa-signatures")] ClassicalVerificationAlgorithm::EcdsaP384Sha384 => { verify_p384::(public_key, message, signature) } + #[cfg(feature = "ecdsa-signatures")] ClassicalVerificationAlgorithm::EcdsaP384Sha512 => { verify_p384::(public_key, message, signature) } + #[cfg(not(feature = "ecdsa-signatures"))] + ClassicalVerificationAlgorithm::EcdsaP256Sha256 + | ClassicalVerificationAlgorithm::EcdsaP256Sha384 + | ClassicalVerificationAlgorithm::EcdsaP256Sha512 + | ClassicalVerificationAlgorithm::EcdsaP384Sha256 + | ClassicalVerificationAlgorithm::EcdsaP384Sha384 + | ClassicalVerificationAlgorithm::EcdsaP384Sha512 => { + Err(ClassicalSignatureError::UnsupportedScheme) + } + #[cfg(feature = "ed25519-signatures")] ClassicalVerificationAlgorithm::Ed25519 => { let key = Ed25519VerifyingKey::try_from(public_key) .map_err(|_| ClassicalSignatureError::InvalidPublicKey)?; @@ -362,37 +429,55 @@ pub fn verify( key.verify_strict(message, &signature) .map_err(|_| ClassicalSignatureError::InvalidSignature) } + #[cfg(not(feature = "ed25519-signatures"))] + ClassicalVerificationAlgorithm::Ed25519 => Err(ClassicalSignatureError::UnsupportedScheme), + #[cfg(feature = "rsa-signatures")] ClassicalVerificationAlgorithm::RsaPssSha256 => { verify_rsa_pss::(public_key, &Sha256::digest(message), signature) } + #[cfg(feature = "rsa-signatures")] ClassicalVerificationAlgorithm::RsaPssSha384 => { verify_rsa_pss::(public_key, &Sha384::digest(message), signature) } + #[cfg(feature = "rsa-signatures")] ClassicalVerificationAlgorithm::RsaPssSha512 => { verify_rsa_pss::(public_key, &Sha512::digest(message), signature) } + #[cfg(feature = "rsa-signatures")] ClassicalVerificationAlgorithm::RsaPkcs1Sha256 => verify_rsa( public_key, Pkcs1v15Sign::new::(), &Sha256::digest(message), signature, ), + #[cfg(feature = "rsa-signatures")] ClassicalVerificationAlgorithm::RsaPkcs1Sha384 => verify_rsa( public_key, Pkcs1v15Sign::new::(), &Sha384::digest(message), signature, ), + #[cfg(feature = "rsa-signatures")] ClassicalVerificationAlgorithm::RsaPkcs1Sha512 => verify_rsa( public_key, Pkcs1v15Sign::new::(), &Sha512::digest(message), signature, ), + #[cfg(not(feature = "rsa-signatures"))] + ClassicalVerificationAlgorithm::RsaPssSha256 + | ClassicalVerificationAlgorithm::RsaPssSha384 + | ClassicalVerificationAlgorithm::RsaPssSha512 + | ClassicalVerificationAlgorithm::RsaPkcs1Sha256 + | ClassicalVerificationAlgorithm::RsaPkcs1Sha384 + | ClassicalVerificationAlgorithm::RsaPkcs1Sha512 => { + Err(ClassicalSignatureError::UnsupportedScheme) + } } } /// Signs a message with the requested RSA scheme. +#[cfg(feature = "rsa-signatures")] fn sign_rsa( key: &RsaPrivateKey, scheme: ClassicalSignatureScheme, @@ -424,6 +509,7 @@ fn sign_rsa( } /// Verifies an ECDSA signature with a P-256 public key and caller-selected digest. +#[cfg(feature = "ecdsa-signatures")] fn verify_p256( public_key: &[u8], message: &[u8], @@ -441,6 +527,7 @@ where } /// Verifies an ECDSA signature with a P-384 public key and caller-selected digest. +#[cfg(feature = "ecdsa-signatures")] fn verify_p384( public_key: &[u8], message: &[u8], @@ -458,6 +545,7 @@ where } /// Verifies an RSA-PSS signature while accepting its encoded salt length. +#[cfg(feature = "rsa-signatures")] fn verify_rsa_pss( public_key: &[u8], digest: &[u8], @@ -475,6 +563,7 @@ where } /// Verifies an RSA signature using a caller-selected padding scheme. +#[cfg(feature = "rsa-signatures")] fn verify_rsa( public_key: &[u8], scheme: S, @@ -490,6 +579,7 @@ where } /// Decodes a PKCS#1 RSA public key and enforces the minimum key size. +#[cfg(feature = "rsa-signatures")] fn parse_rsa_public_key(public_key: &[u8]) -> Result { let key = RsaPublicKey::from_pkcs1_der(public_key) .map_err(|_| ClassicalSignatureError::InvalidPublicKey)?; @@ -502,12 +592,22 @@ fn parse_rsa_public_key(public_key: &[u8]) -> Result, } diff --git a/src/kem.rs b/src/kem.rs index 712e0d2..ace0f75 100644 --- a/src/kem.rs +++ b/src/kem.rs @@ -2,6 +2,7 @@ //! //! Supports the KEM families enabled through Cargo features. +use crate::error::*; #[cfg(any( feature = "frodo", feature = "hqc", @@ -9,7 +10,6 @@ feature = "sntrup" ))] use crate::os_rng; -use crate::{deserialize_hex_or_bin, error::*, serialize_hex_or_bin}; use serde::{Deserialize, Serialize}; #[cfg(feature = "mceliece")] @@ -939,8 +939,8 @@ impl zeroize::ZeroizeOnDrop for KemSharedSecret {} pub(crate) struct InnerKem { scheme: KemScheme, #[serde( - serialize_with = "serialize_hex_or_bin", - deserialize_with = "deserialize_hex_or_bin" + serialize_with = "serdect::slice::serialize_hex_lower_or_bin", + deserialize_with = "serdect::slice::deserialize_hex_or_bin_vec" )] value: Vec, } diff --git a/src/lib.rs b/src/lib.rs index 3e6120a..010f251 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -21,7 +21,11 @@ mod macros; #[cfg(feature = "bird-of-prey")] pub mod bird_of_prey; -#[cfg(feature = "classical-signatures")] +#[cfg(any( + feature = "ecdsa-signatures", + feature = "ed25519-signatures", + feature = "rsa-signatures" +))] pub mod classical_signature; #[cfg(feature = "bird-of-prey")] pub mod det_rng; @@ -79,20 +83,6 @@ pub(crate) fn os_rng() -> rand_core_010::UnwrapErr { rand_core_010::UnwrapErr(getrandom_v04::SysRng) } -pub(crate) fn serialize_hex_or_bin(bytes: &Vec, s: S) -> Result -where - S: serde::Serializer, -{ - serdect::slice::serialize_hex_lower_or_bin(&bytes, s) -} - -pub(crate) fn deserialize_hex_or_bin<'de, D>(d: D) -> Result, D::Error> -where - D: serde::Deserializer<'de>, -{ - serdect::slice::deserialize_hex_or_bin_vec(d) -} - #[cfg(all( test, any( diff --git a/src/mayo.rs b/src/mayo.rs index 30d1b97..8e94ba6 100644 --- a/src/mayo.rs +++ b/src/mayo.rs @@ -1,8 +1,8 @@ //! MAYO key and signature methods. +use crate::error::*; #[cfg(feature = "kgen")] use crate::os_rng; -use crate::{deserialize_hex_or_bin, error::*, serialize_hex_or_bin}; use serde::{Deserialize, Serialize}; macro_rules! impl_mayo_struct { @@ -241,8 +241,8 @@ impl_mayo_struct!(MayoSignature, validate_signature, "a valid signature"); pub(crate) struct InnerMayo { scheme: MayoScheme, #[serde( - serialize_with = "serialize_hex_or_bin", - deserialize_with = "deserialize_hex_or_bin" + serialize_with = "serdect::slice::serialize_hex_lower_or_bin", + deserialize_with = "serdect::slice::deserialize_hex_or_bin_vec" )] value: Vec, } diff --git a/src/ml_dsa.rs b/src/ml_dsa.rs index 0cc42d5..cdb5dfe 100644 --- a/src/ml_dsa.rs +++ b/src/ml_dsa.rs @@ -1,8 +1,8 @@ //! ML-DSA key and signature methods. +use crate::error::*; #[cfg(any(feature = "kgen", feature = "sign"))] use crate::os_rng; -use crate::{deserialize_hex_or_bin, error::*, serialize_hex_or_bin}; use serde::{Deserialize, Serialize}; macro_rules! impl_ml_dsa_struct { @@ -271,8 +271,8 @@ impl_ml_dsa_struct!(MlDsaSignature, validate_signature, "a valid signature"); pub(crate) struct InnerMlDsa { scheme: MlDsaScheme, #[serde( - serialize_with = "serialize_hex_or_bin", - deserialize_with = "deserialize_hex_or_bin" + serialize_with = "serdect::slice::serialize_hex_lower_or_bin", + deserialize_with = "serdect::slice::deserialize_hex_or_bin_vec" )] value: Vec, } diff --git a/src/slh_dsa.rs b/src/slh_dsa.rs index 3ab2b85..b05fe8b 100644 --- a/src/slh_dsa.rs +++ b/src/slh_dsa.rs @@ -1,8 +1,8 @@ //! SLH-DSA key and signature methods. +use crate::error::*; #[cfg(feature = "kgen")] use crate::os_rng; -use crate::{deserialize_hex_or_bin, error::*, serialize_hex_or_bin}; use serde::{Deserialize, Serialize}; macro_rules! impl_slh_dsa_struct { @@ -279,8 +279,8 @@ impl_slh_dsa_struct!(SlhDsaSignature, validate_signature, "a valid signature"); pub(crate) struct InnerSlhDsa { scheme: SlhDsaScheme, #[serde( - serialize_with = "serialize_hex_or_bin", - deserialize_with = "deserialize_hex_or_bin" + serialize_with = "serdect::slice::serialize_hex_lower_or_bin", + deserialize_with = "serdect::slice::deserialize_hex_or_bin_vec" )] value: Vec, } diff --git a/src/xmss.rs b/src/xmss.rs index a00d068..ffd1eca 100644 --- a/src/xmss.rs +++ b/src/xmss.rs @@ -20,7 +20,7 @@ use core::fmt; use serde::{Deserialize, Serialize}; -use crate::{deserialize_hex_or_bin, error::*, os_rng, serialize_hex_or_bin}; +use crate::{error::*, os_rng}; const OID_LEN: usize = 4; const INDEX_LEN: usize = 4; @@ -816,8 +816,8 @@ macro_rules! with_xmss_params { pub(crate) struct InnerXmss { scheme: XmssScheme, #[serde( - serialize_with = "serialize_hex_or_bin", - deserialize_with = "deserialize_hex_or_bin" + serialize_with = "serdect::slice::serialize_hex_lower_or_bin", + deserialize_with = "serdect::slice::deserialize_hex_or_bin_vec" )] value: Vec, } diff --git a/src/xwing.rs b/src/xwing.rs index 47094ec..3aa11e2 100644 --- a/src/xwing.rs +++ b/src/xwing.rs @@ -5,7 +5,7 @@ //! key. It is a general-purpose hybrid post-quantum KEM that combines X25519 with ML-KEM //! or Classic McEliece. -use crate::{deserialize_hex_or_bin, error::*, kem::*, os_rng, serialize_hex_or_bin}; +use crate::{error::*, kem::*, os_rng}; use rand_core_010::Rng; use serde::{Deserialize, Serialize}; use sha3::{ @@ -288,8 +288,8 @@ impl Ciphertext { pub struct DecapsulationKey { scheme: XwingScheme, #[serde( - serialize_with = "serialize_hex_or_bin", - deserialize_with = "deserialize_hex_or_bin" + serialize_with = "serdect::slice::serialize_hex_lower_or_bin", + deserialize_with = "serdect::slice::deserialize_hex_or_bin_vec" )] seed: Vec, } @@ -547,40 +547,36 @@ mod tests { #[allow(unused)] #[derive(serde::Deserialize)] struct TestVector { - #[serde(deserialize_with = "hex::serde::deserialize")] - seed: Vec, - #[serde(deserialize_with = "hex::serde::deserialize")] - eseed: Vec, - #[serde(deserialize_with = "hex::serde::deserialize")] - ss: [u8; 32], - #[serde(deserialize_with = "hex::serde::deserialize")] - sk: [u8; 32], - #[serde(deserialize_with = "hex::serde::deserialize")] - pk: Vec, - #[serde(deserialize_with = "hex::serde::deserialize")] - ct: Vec, + seed: serdect::slice::HexLowerOrBin, + eseed: serdect::slice::HexLowerOrBin, + ss: serdect::array::HexLowerOrBin<32>, + sk: serdect::array::HexLowerOrBin<32>, + pk: serdect::slice::HexLowerOrBin, + ct: serdect::slice::HexLowerOrBin, } let test_vectors = serde_json::from_str::>(vectors).unwrap(); for test in &test_vectors { let (pk, sk) = XwingScheme::X25519MlKem768 - .keypair_from_seed(&test.seed) + .keypair_from_seed(&test.seed.0) .unwrap(); let mut out_pk_bytes = pk.pk_m.to_raw_bytes(); out_pk_bytes.extend_from_slice(pk.pk_x.as_bytes()); - assert_eq!(test.pk, out_pk_bytes); - assert_eq!(test.sk, sk.seed.as_slice()); - - let ct_m = - KemCiphertext::from_raw_bytes(KemScheme::MlKem768, &test.ct[..test.ct.len() - 32]) - .unwrap(); - let ct_x_bytes: [u8; 32] = (&test.ct[test.ct.len() - 32..]).try_into().unwrap(); + assert_eq!(test.pk.0, out_pk_bytes); + assert_eq!(test.sk.0, sk.seed.as_slice()); + + let ct_m = KemCiphertext::from_raw_bytes( + KemScheme::MlKem768, + &test.ct.0[..test.ct.0.len() - 32], + ) + .unwrap(); + let ct_x_bytes: [u8; 32] = (&test.ct.0[test.ct.0.len() - 32..]).try_into().unwrap(); let ct_x = PublicKey::from(ct_x_bytes); let ct = Ciphertext { ct_m, ct_x }; let ss = sk.decapsulate(&ct).unwrap(); - assert_eq!(ss, test.ss); + assert_eq!(ss, test.ss.0); } }