diff --git a/Cargo.lock b/Cargo.lock index 3199dc0..5213dc5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -14,9 +14,9 @@ dependencies = [ [[package]] name = "aes" -version = "0.9.1" +version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1fc76eaeac4c9164506c466d4ffdd8ec9d0c5bf57ee97177c4d8eceb3a0e138" +checksum = "f8eb277bec05f56a0e0591f155a484cbd0f4f07ff2905051a48c72f004f7ed58" dependencies = [ "cipher", "cpubits", @@ -66,9 +66,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.2.67" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e17dd265a7d0f31ef544e1b20e03add05d3b45b491b633b10d67145d2acc1a38" +checksum = "5add81bb678e6cb321aff7fa0dc7689ad82b112dbc032cea19f91d6b8e3582b9" dependencies = [ "find-msvc-tools", "shlex", @@ -220,7 +220,7 @@ checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.119", ] [[package]] @@ -286,6 +286,32 @@ dependencies = [ "zeroize", ] +[[package]] +name = "ed448" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae112a25f86ae3598d4e8533ed1e65149cec6eb21918e7a6f4c06dddec370263" +dependencies = [ + "pkcs8", + "signature", +] + +[[package]] +name = "ed448-goldilocks" +version = "0.14.0-pre.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b805154de2e68f59874ec217ca36790dcffe500cd872c60fe509d28d0814a74d" +dependencies = [ + "ed448", + "elliptic-curve", + "hash2curve", + "rand_core", + "serdect", + "shake", + "signature", + "subtle", +] + [[package]] name = "elliptic-curve" version = "0.14.1" @@ -373,6 +399,16 @@ dependencies = [ "subtle", ] +[[package]] +name = "hash2curve" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1eaf40612d7d854743e7189228a6d528f0f6e8502cf6a0cb831d28a218b7f3f6" +dependencies = [ + "digest", + "elliptic-curve", +] + [[package]] name = "hkdf" version = "0.13.0" @@ -393,9 +429,9 @@ dependencies = [ [[package]] name = "hybrid-array" -version = "0.4.13" +version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "818356c5132c1fede50f837ca96afbe78ff42413047f4abb886217845e1b6c8c" +checksum = "707114b52a152fa7bdb290cd7cd5912d9467273b6d74e21b8d81aca1f8533f6b" dependencies = [ "subtle", "typenum", @@ -411,11 +447,21 @@ dependencies = [ "hybrid-array", ] +[[package]] +name = "keccak" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffd9697dc4a9a62e2da93389f34400b77a28f0287711263cabb203b3ccb9c0e4" +dependencies = [ + "cfg-if", + "cpufeatures", +] + [[package]] name = "libc" -version = "0.2.186" +version = "0.2.189" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" +checksum = "3eaf3ede3fee6db1a4c2ee091bf8a8b4dccdc6d17f656fb07896ee72867612f2" [[package]] name = "log" @@ -548,18 +594,18 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.106" +version = "1.0.107" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" +checksum = "985e7ec9bb745e6ce6535b544d84d6cd6f7ad8bd711c398938ae983b91a766d9" dependencies = [ "unicode-ident", ] [[package]] name = "quote" -version = "1.0.46" +version = "1.0.47" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfbc457d0c7a0759a614551b11a6409e5951f6c7537be1f1b7682b9ae9230368" +checksum = "1fbf4db142a473a8d80c26bbf18454ed458bf8d26c8219c331daecfdbd079001" dependencies = [ "proc-macro2", ] @@ -630,9 +676,9 @@ dependencies = [ [[package]] name = "rustls" -version = "0.23.42" +version = "0.23.43" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c54fcab019b409d04215d3a17cb438fd7fbf192ee61461f20f4fe18704bc138" +checksum = "0283386ce02abc0151e1761d08802dfe86c173b0b494af5cbc086574e453da06" dependencies = [ "log", "once_cell", @@ -644,9 +690,9 @@ dependencies = [ [[package]] name = "rustls-pki-types" -version = "1.15.0" +version = "1.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "764899a24af3980067ee14bc143654f297b22eaebfe3c7b6b211920a5a59b046" +checksum = "2f4925028c7eb5d1fcdaf196971378ed9d2c1c4efc7dc5d011256f76c99c0a96" dependencies = [ "zeroize", ] @@ -663,6 +709,7 @@ dependencies = [ "digest", "ecdsa", "ed25519-dalek", + "ed448-goldilocks", "getrandom 0.4.3", "hmac", "p256", @@ -676,6 +723,7 @@ dependencies = [ "sha2", "signature", "x25519-dalek", + "x448", ] [[package]] @@ -711,31 +759,31 @@ checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd" [[package]] name = "serde" -version = "1.0.228" +version = "1.0.229" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +checksum = "4148590afebada386688f18773da617792bf2ef03ffc1e4cbd2b1d45b023e0ba" dependencies = [ "serde_core", ] [[package]] name = "serde_core" -version = "1.0.228" +version = "1.0.229" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" +checksum = "67dca2c9c51e58a4791a4b1ed58308b39c64224d349a935ab5039aa360942a48" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.228" +version = "1.0.229" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" +checksum = "e7a5d71263a5a7d47b41f6b3f06ba276f10cc18b0931f1799f710578e2309348" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 3.0.3", ] [[package]] @@ -759,6 +807,17 @@ dependencies = [ "digest", ] +[[package]] +name = "shake" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09057cb2149ad4cbd2da1e26b351f9a4c354219421229c69c3063e6f61947c4a" +dependencies = [ + "digest", + "keccak", + "sponge-cursor", +] + [[package]] name = "shlex" version = "2.0.1" @@ -785,6 +844,12 @@ dependencies = [ "der", ] +[[package]] +name = "sponge-cursor" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a0219bd7d979d58245a4f41f695e1ac9f8befdffadd7f61f1bae9e39abc6620" + [[package]] name = "subtle" version = "2.6.1" @@ -802,6 +867,17 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "syn" +version = "3.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53e9bae58849f64dfa4f5d5ae372c8341f7305f82a3868709269343628b659a3" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + [[package]] name = "typenum" version = "1.20.1" @@ -931,8 +1007,32 @@ dependencies = [ "zeroize", ] +[[package]] +name = "x448" +version = "0.14.0-pre.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c166d06b9fa4328c890d46bda797269fb6aeca96393aeaad0e74b4b11550e06" +dependencies = [ + "ed448-goldilocks", + "zeroize", +] + [[package]] name = "zeroize" version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e13c156562582aa81c60cb29407084cdb54c4164760106ab78e6c5b0858cf64e" +dependencies = [ + "zeroize_derive", +] + +[[package]] +name = "zeroize_derive" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c50655cbb0fe3fc43170059e702f1ce5e19b84cec58dc87b037a09935c2f328" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", +] diff --git a/Cargo.toml b/Cargo.toml index e8e5de0..0e0e345 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -26,6 +26,7 @@ der = { version = "0.8", default-features = false } digest = { version = "0.11", default-features = false } ecdsa = { version = "0.17", default-features = false, features = ["alloc"] } ed25519-dalek = { version = "3", default-features = false, features = ["pkcs8"] } +ed448-goldilocks = { version = "0.14.0-pre.15", default-features = false, features = ["pkcs8", "signing"] } getrandom = { version = "0.4", default-features = false, features = ["sys_rng"] } hmac = { version = "0.13", default-features = false } p256 = { version = "0.14", default-features = false, features = ["pem", "ecdsa", "ecdh"] } @@ -39,6 +40,7 @@ sec1 = { version = "0.8", default-features = false } sha2 = { version = "0.11", default-features = false } signature = { version = "3", default-features = false } x25519-dalek = { version = "3", default-features = false } +x448 = { version = "0.14.0-pre.12", default-features = false } [features] default = ["std", "tls12", "zeroize"] @@ -51,5 +53,5 @@ tls12 = ["rustls/tls12"] # TODO: go through all of these that what gets exposed re: std error type std = ["alloc", "pki-types/std", "rustls/std"] # TODO: go through all of these to ensure to_vec etc. impls are exposed -alloc = ["pki-types/alloc", "aead/alloc", "ed25519-dalek/alloc"] +alloc = ["pki-types/alloc", "aead/alloc", "ed25519-dalek/alloc", "ed448-goldilocks/alloc"] zeroize = ["ed25519-dalek/zeroize", "x25519-dalek/zeroize"] diff --git a/src/kx.rs b/src/kx.rs index 3f8a4dd..0dd3ae9 100644 --- a/src/kx.rs +++ b/src/kx.rs @@ -49,6 +49,49 @@ impl crypto::ActiveKeyExchange for X25519KeyExchange { } } +#[derive(Debug)] +pub struct X448; + +impl crypto::SupportedKxGroup for X448 { + fn name(&self) -> rustls::NamedGroup { + rustls::NamedGroup::X448 + } + + fn start(&self) -> Result, rustls::Error> { + let mut rng = UnwrapErr(getrandom::SysRng); + let priv_key = x448::EphemeralSecret::try_generate_from_rng(&mut rng) + .map_err(|_| rustls::Error::from(rustls::PeerMisbehaved::InvalidKeyShare))?; + let pub_key = x448::PublicKey::from(&priv_key); + Ok(Box::new(X448KeyExchange { priv_key, pub_key })) + } +} + +pub struct X448KeyExchange { + priv_key: x448::EphemeralSecret, + pub_key: x448::PublicKey, +} + +impl crypto::ActiveKeyExchange for X448KeyExchange { + fn complete(self: Box, peer: &[u8]) -> Result { + let peer_pub = x448::PublicKey::from_bytes(peer) + .ok_or_else(|| rustls::Error::from(rustls::PeerMisbehaved::InvalidKeyShare))?; + Ok(self + .priv_key + .diffie_hellman(&peer_pub) + .as_bytes() + .as_slice() + .into()) + } + + fn pub_key(&self) -> &[u8] { + self.pub_key.as_bytes() + } + + fn group(&self) -> rustls::NamedGroup { + X448.name() + } +} + macro_rules! impl_kx { ($name:ident, $kx_name:ty, $secret:ty, $public_key:ty) => { paste! { @@ -109,4 +152,4 @@ macro_rules! impl_kx { impl_kx! {SecP256R1, rustls::NamedGroup::secp256r1, p256::ecdh::EphemeralSecret, p256::PublicKey} impl_kx! {SecP384R1, rustls::NamedGroup::secp384r1, p384::ecdh::EphemeralSecret, p384::PublicKey} -pub const ALL_KX_GROUPS: &[&dyn SupportedKxGroup] = &[&X25519, &SecP256R1, &SecP384R1]; +pub const ALL_KX_GROUPS: &[&dyn SupportedKxGroup] = &[&X448, &X25519, &SecP256R1, &SecP384R1]; diff --git a/src/sign.rs b/src/sign.rs index cf30cc5..f07a504 100644 --- a/src/sign.rs +++ b/src/sign.rs @@ -3,7 +3,7 @@ use alloc::{sync::Arc, vec::Vec}; use core::marker::PhantomData; use self::ecdsa::{EcdsaSigningKeyP256, EcdsaSigningKeyP384}; -use self::eddsa::Ed25519SigningKey; +use self::eddsa::{Ed25519SigningKey, Ed448SigningKey}; use self::rsa::RsaSigningKey; use getrandom::rand_core::UnwrapErr; @@ -98,8 +98,9 @@ pub fn any_ecdsa_type(der: &PrivateKeyDer<'_>) -> Result, ru /// /// Returns an error if the key couldn't be decoded. pub fn any_eddsa_type(der: &PrivateKeyDer<'_>) -> Result, rustls::Error> { - // TODO: Add support for Ed448 - Ed25519SigningKey::try_from(der).map(|x| Arc::new(x) as _) + Ed25519SigningKey::try_from(der) + .map(|x| Arc::new(x) as _) + .or_else(|_| Ed448SigningKey::try_from(der).map(|x| Arc::new(x) as _)) } pub mod ecdsa; diff --git a/src/sign/eddsa.rs b/src/sign/eddsa.rs index 15f4fe8..e8cb34f 100644 --- a/src/sign/eddsa.rs +++ b/src/sign/eddsa.rs @@ -51,3 +51,48 @@ impl SigningKey for Ed25519SigningKey { SignatureAlgorithm::ED25519 } } + +#[derive(Debug)] +pub struct Ed448SigningKey { + key: Arc, + scheme: SignatureScheme, +} + +impl TryFrom<&PrivateKeyDer<'_>> for Ed448SigningKey { + type Error = rustls::Error; + + fn try_from(value: &PrivateKeyDer<'_>) -> Result { + let pkey = match value { + PrivateKeyDer::Pkcs8(der) => { + ed448_goldilocks::SigningKey::from_pkcs8_der(der.secret_pkcs8_der()) + .map_err(|e| format!("failed to decrypt private key: {e}")) + } + PrivateKeyDer::Pkcs1(_) => Err("ED448 does not support PKCS#1 key".to_string()), + PrivateKeyDer::Sec1(_) => Err("ED448 does not support SEC1 key".to_string()), + _ => Err("not supported".into()), + }; + pkey.map(|kp| Self { + key: Arc::new(kp), + scheme: SignatureScheme::ED448, + }) + .map_err(rustls::Error::General) + } +} + +impl SigningKey for Ed448SigningKey { + fn choose_scheme(&self, offered: &[SignatureScheme]) -> Option> { + if offered.contains(&self.scheme) { + Some(Box::new(super::GenericSigner { + _marker: PhantomData, + key: self.key.clone(), + scheme: self.scheme, + })) + } else { + None + } + } + + fn algorithm(&self) -> SignatureAlgorithm { + SignatureAlgorithm::ED448 + } +} diff --git a/src/verify.rs b/src/verify.rs index 2043b64..6b54c0c 100644 --- a/src/verify.rs +++ b/src/verify.rs @@ -2,7 +2,7 @@ use rustls::crypto::WebPkiSupportedAlgorithms; use rustls::SignatureScheme; use self::ecdsa::{ECDSA_P256_SHA256, ECDSA_P256_SHA384, ECDSA_P384_SHA256, ECDSA_P384_SHA384}; -use self::eddsa::ED25519; +use self::eddsa::{ED25519, ED448}; use self::rsa::{ RSA_PKCS1_SHA256, RSA_PKCS1_SHA384, RSA_PKCS1_SHA512, RSA_PSS_SHA256, RSA_PSS_SHA384, RSA_PSS_SHA512, @@ -15,6 +15,7 @@ pub static ALGORITHMS: WebPkiSupportedAlgorithms = WebPkiSupportedAlgorithms { ECDSA_P384_SHA256, ECDSA_P384_SHA384, ED25519, + ED448, RSA_PKCS1_SHA256, RSA_PKCS1_SHA384, RSA_PKCS1_SHA512, @@ -32,6 +33,7 @@ pub static ALGORITHMS: WebPkiSupportedAlgorithms = WebPkiSupportedAlgorithms { &[ECDSA_P256_SHA256, ECDSA_P384_SHA256], ), (SignatureScheme::ED25519, &[ED25519]), + (SignatureScheme::ED448, &[ED448]), (SignatureScheme::RSA_PKCS1_SHA256, &[RSA_PKCS1_SHA256]), (SignatureScheme::RSA_PKCS1_SHA384, &[RSA_PKCS1_SHA384]), (SignatureScheme::RSA_PKCS1_SHA512, &[RSA_PKCS1_SHA512]), diff --git a/src/verify/eddsa.rs b/src/verify/eddsa.rs index 61009e1..3ac4c12 100644 --- a/src/verify/eddsa.rs +++ b/src/verify/eddsa.rs @@ -30,3 +30,33 @@ impl SignatureVerificationAlgorithm for Ed25519Verify { } pub const ED25519: &dyn SignatureVerificationAlgorithm = &Ed25519Verify; + +#[derive(Debug)] +struct Ed448Verify; + +impl SignatureVerificationAlgorithm for Ed448Verify { + fn public_key_alg_id(&self) -> AlgorithmIdentifier { + alg_id::ED448 + } + + fn signature_alg_id(&self) -> AlgorithmIdentifier { + alg_id::ED448 + } + + fn verify_signature( + &self, + public_key: &[u8], + message: &[u8], + signature: &[u8], + ) -> Result<(), InvalidSignature> { + let public_key = public_key.try_into().map_err(|_| InvalidSignature)?; + let signature = + ed448_goldilocks::Signature::from_slice(signature).map_err(|_| InvalidSignature)?; + ed448_goldilocks::VerifyingKey::from_bytes(public_key) + .map_err(|_| InvalidSignature)? + .verify(message, &signature) + .map_err(|_| InvalidSignature) + } +} + +pub const ED448: &dyn SignatureVerificationAlgorithm = &Ed448Verify; diff --git a/validation/local_ping_pong_openssl/src/lib.rs b/validation/local_ping_pong_openssl/src/lib.rs index 14da95c..8ba3179 100644 --- a/validation/local_ping_pong_openssl/src/lib.rs +++ b/validation/local_ping_pong_openssl/src/lib.rs @@ -131,7 +131,6 @@ mod test { vs_openssl_as_client(group_list, OpenSslCipherSuites::default()); } #[test] - #[should_panic] // no support fn vs_openssl_as_client_group_x448() { let mut group_list = OpenSslGroupsList::all_false(); group_list.X448 = true;