Skip to content

Commit 95604a5

Browse files
committed
Add support for X448/Ed448 and P521
1 parent 60695e7 commit 95604a5

9 files changed

Lines changed: 258 additions & 295 deletions

File tree

Cargo.lock

Lines changed: 127 additions & 287 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ getrandom = { version = "0.4", default-features = false, features = ["sys_rng"]
3030
hmac = { version = "0.13", default-features = false }
3131
p256 = { version = "0.14.0-rc.9", default-features = false, features = ["pem", "ecdsa", "ecdh"] }
3232
p384 = { version = "0.14.0-rc.9", default-features = false, features = ["pem", "ecdsa", "ecdh"] }
33+
p521 = { version = "0.14.0-rc.9", default-features = false, features = ["pem", "ecdsa", "ecdh"] }
3334
paste = { version = "1", default-features = false }
3435
pkcs8 = { version = "0.11", default-features = false }
3536
pki-types = { package = "rustls-pki-types", version = "1", default-features = false }
@@ -39,6 +40,8 @@ sec1 = { version = "0.8", default-features = false }
3940
sha2 = { version = "0.11", default-features = false }
4041
signature = { version = "3", default-features = false }
4142
x25519-dalek = { version = "3.0.0-rc.0", default-features = false }
43+
ed448-goldilocks = { version = "0.14.0-pre.15", default-features = false, features = ["signing", "pkcs8"] }
44+
x448 = { version = "0.14.0-pre.12", default-features = false }
4245

4346
[features]
4447
default = ["std", "tls12", "zeroize"]

src/kx.rs

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,44 @@ impl crypto::ActiveKeyExchange for X25519KeyExchange {
4949
}
5050
}
5151

52+
#[derive(Debug)]
53+
pub struct X448;
54+
55+
impl crypto::SupportedKxGroup for X448 {
56+
fn name(&self) -> rustls::NamedGroup {
57+
rustls::NamedGroup::X448
58+
}
59+
60+
fn start(&self) -> Result<Box<dyn crypto::ActiveKeyExchange>, rustls::Error> {
61+
let mut rng = UnwrapErr(getrandom::SysRng);
62+
let priv_key = x448::EphemeralSecret::try_generate_from_rng(&mut rng)
63+
.map_err(|_| rustls::Error::from(rustls::PeerMisbehaved::InvalidKeyShare))?;
64+
let pub_key = (&priv_key).into();
65+
Ok(Box::new(X448KeyExchange { priv_key, pub_key }))
66+
}
67+
}
68+
69+
pub struct X448KeyExchange {
70+
priv_key: x448::EphemeralSecret,
71+
pub_key: x448::PublicKey,
72+
}
73+
74+
impl crypto::ActiveKeyExchange for X448KeyExchange {
75+
fn complete(self: Box<X448KeyExchange>, peer: &[u8]) -> Result<SharedSecret, rustls::Error> {
76+
let peer = x448::PublicKey::from_bytes(peer)
77+
.ok_or_else(|| rustls::Error::from(rustls::PeerMisbehaved::InvalidKeyShare))?;
78+
Ok(self.priv_key.diffie_hellman(&peer).as_bytes()[..].into())
79+
}
80+
81+
fn pub_key(&self) -> &[u8] {
82+
self.pub_key.as_bytes()
83+
}
84+
85+
fn group(&self) -> rustls::NamedGroup {
86+
X448.name()
87+
}
88+
}
89+
5290
macro_rules! impl_kx {
5391
($name:ident, $kx_name:ty, $secret:ty, $public_key:ty) => {
5492
paste! {
@@ -108,5 +146,6 @@ macro_rules! impl_kx {
108146

109147
impl_kx! {SecP256R1, rustls::NamedGroup::secp256r1, p256::ecdh::EphemeralSecret, p256::PublicKey}
110148
impl_kx! {SecP384R1, rustls::NamedGroup::secp384r1, p384::ecdh::EphemeralSecret, p384::PublicKey}
149+
impl_kx! {SecP521R1, rustls::NamedGroup::secp521r1, p521::ecdh::EphemeralSecret, p521::PublicKey}
111150

112-
pub const ALL_KX_GROUPS: &[&dyn SupportedKxGroup] = &[&X25519, &SecP256R1, &SecP384R1];
151+
pub const ALL_KX_GROUPS: &[&dyn SupportedKxGroup] = &[&X25519, &X448, &SecP256R1, &SecP384R1, &SecP521R1];

src/sign.rs

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@
22
use alloc::{sync::Arc, vec::Vec};
33
use core::marker::PhantomData;
44

5-
use self::ecdsa::{EcdsaSigningKeyP256, EcdsaSigningKeyP384};
6-
use self::eddsa::Ed25519SigningKey;
5+
use self::ecdsa::{EcdsaSigningKeyP256, EcdsaSigningKeyP384, EcdsaSigningKeyP521};
6+
use self::eddsa::{Ed25519SigningKey, Ed448SigningKey};
77
use self::rsa::RsaSigningKey;
88

99
use getrandom::rand_core::UnwrapErr;
@@ -89,7 +89,8 @@ pub fn any_supported_type(der: &PrivateKeyDer<'_>) -> Result<Arc<dyn SigningKey>
8989
pub fn any_ecdsa_type(der: &PrivateKeyDer<'_>) -> Result<Arc<dyn SigningKey>, rustls::Error> {
9090
let p256 = |_| EcdsaSigningKeyP256::try_from(der).map(|x| Arc::new(x) as _);
9191
let p384 = |_| EcdsaSigningKeyP384::try_from(der).map(|x| Arc::new(x) as _);
92-
p256(()).or_else(p384)
92+
let p521 = |_| EcdsaSigningKeyP521::try_from(der).map(|x| Arc::new(x) as _);
93+
p256(()).or_else(p384).or_else(p521)
9394
}
9495

9596
/// Extract any supported EDDSA key from the given DER input.
@@ -98,8 +99,9 @@ pub fn any_ecdsa_type(der: &PrivateKeyDer<'_>) -> Result<Arc<dyn SigningKey>, ru
9899
///
99100
/// Returns an error if the key couldn't be decoded.
100101
pub fn any_eddsa_type(der: &PrivateKeyDer<'_>) -> Result<Arc<dyn SigningKey>, rustls::Error> {
101-
// TODO: Add support for Ed448
102-
Ed25519SigningKey::try_from(der).map(|x| Arc::new(x) as _)
102+
let ed25519 = |_| Ed25519SigningKey::try_from(der).map(|x| Arc::new(x) as _);
103+
let ed448 = |_| Ed448SigningKey::try_from(der).map(|x| Arc::new(x) as _);
104+
ed25519(()).or_else(ed448)
103105
}
104106

105107
pub mod ecdsa;

src/sign/ecdsa.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,3 +69,4 @@ macro_rules! impl_ecdsa {
6969

7070
impl_ecdsa! {P256, SignatureScheme::ECDSA_NISTP256_SHA256, p256::ecdsa::SigningKey, p256::ecdsa::DerSignature}
7171
impl_ecdsa! {P384, SignatureScheme::ECDSA_NISTP384_SHA384, p384::ecdsa::SigningKey, p384::ecdsa::DerSignature}
72+
impl_ecdsa! {P521, SignatureScheme::ECDSA_NISTP521_SHA512, p521::ecdsa::SigningKey, p521::ecdsa::DerSignature}

src/sign/eddsa.rs

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,3 +51,48 @@ impl SigningKey for Ed25519SigningKey {
5151
SignatureAlgorithm::ED25519
5252
}
5353
}
54+
55+
#[derive(Debug)]
56+
pub struct Ed448SigningKey {
57+
key: Arc<ed448_goldilocks::SigningKey>,
58+
scheme: SignatureScheme,
59+
}
60+
61+
impl TryFrom<&PrivateKeyDer<'_>> for Ed448SigningKey {
62+
type Error = rustls::Error;
63+
64+
fn try_from(value: &PrivateKeyDer<'_>) -> Result<Self, Self::Error> {
65+
let pkey = match value {
66+
PrivateKeyDer::Pkcs8(der) => {
67+
ed448_goldilocks::SigningKey::from_pkcs8_der(der.secret_pkcs8_der())
68+
.map_err(|e| format!("failed to decrypt private key: {e}"))
69+
}
70+
PrivateKeyDer::Pkcs1(_) => Err("ED448 does not support PKCS#1 key".to_string()),
71+
PrivateKeyDer::Sec1(_) => Err("ED448 does not support SEC1 key".to_string()),
72+
_ => Err("not supported".into()),
73+
};
74+
pkey.map(|kp| Self {
75+
key: Arc::new(kp),
76+
scheme: SignatureScheme::ED448,
77+
})
78+
.map_err(rustls::Error::General)
79+
}
80+
}
81+
82+
impl SigningKey for Ed448SigningKey {
83+
fn choose_scheme(&self, offered: &[SignatureScheme]) -> Option<Box<dyn Signer>> {
84+
if offered.contains(&self.scheme) {
85+
Some(Box::new(super::GenericSigner {
86+
_marker: PhantomData,
87+
key: self.key.clone(),
88+
scheme: self.scheme,
89+
}))
90+
} else {
91+
None
92+
}
93+
}
94+
95+
fn algorithm(&self) -> SignatureAlgorithm {
96+
SignatureAlgorithm::ED448
97+
}
98+
}

src/verify.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
use rustls::crypto::WebPkiSupportedAlgorithms;
22
use rustls::SignatureScheme;
33

4-
use self::ecdsa::{ECDSA_P256_SHA256, ECDSA_P256_SHA384, ECDSA_P384_SHA256, ECDSA_P384_SHA384};
5-
use self::eddsa::ED25519;
4+
use self::ecdsa::{ECDSA_P256_SHA256, ECDSA_P256_SHA384, ECDSA_P384_SHA256, ECDSA_P384_SHA384, ECDSA_P521_SHA512};
5+
use self::eddsa::{ED25519, ED448};
66
use self::rsa::{
77
RSA_PKCS1_SHA256, RSA_PKCS1_SHA384, RSA_PKCS1_SHA512, RSA_PSS_SHA256, RSA_PSS_SHA384,
88
RSA_PSS_SHA512,
@@ -31,7 +31,9 @@ pub static ALGORITHMS: WebPkiSupportedAlgorithms = WebPkiSupportedAlgorithms {
3131
SignatureScheme::ECDSA_NISTP256_SHA256,
3232
&[ECDSA_P256_SHA256, ECDSA_P384_SHA256],
3333
),
34+
(SignatureScheme::ECDSA_NISTP521_SHA512, &[ECDSA_P521_SHA512]),
3435
(SignatureScheme::ED25519, &[ED25519]),
36+
(SignatureScheme::ED448, &[ED448]),
3537
(SignatureScheme::RSA_PKCS1_SHA256, &[RSA_PKCS1_SHA256]),
3638
(SignatureScheme::RSA_PKCS1_SHA384, &[RSA_PKCS1_SHA384]),
3739
(SignatureScheme::RSA_PKCS1_SHA512, &[RSA_PKCS1_SHA512]),

src/verify/ecdsa.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,3 +51,4 @@ impl_generic_ecdsa_verifer! {ECDSA_P256_SHA256, alg_id::ECDSA_P256, alg_id::ECDS
5151
impl_generic_ecdsa_verifer! {ECDSA_P256_SHA384, alg_id::ECDSA_P256, alg_id::ECDSA_SHA384, p256::ecdsa::VerifyingKey, p256::ecdsa::DerSignature, sha2::Sha384}
5252
impl_generic_ecdsa_verifer! {ECDSA_P384_SHA256, alg_id::ECDSA_P384, alg_id::ECDSA_SHA256, p384::ecdsa::VerifyingKey, p384::ecdsa::DerSignature, sha2::Sha256}
5353
impl_generic_ecdsa_verifer! {ECDSA_P384_SHA384, alg_id::ECDSA_P384, alg_id::ECDSA_SHA384, p384::ecdsa::VerifyingKey, p384::ecdsa::DerSignature, sha2::Sha384}
54+
impl_generic_ecdsa_verifer! {ECDSA_P521_SHA512, alg_id::ECDSA_P521, alg_id::ECDSA_SHA512, p521::ecdsa::VerifyingKey, p521::ecdsa::DerSignature, sha2::Sha512}

src/verify/eddsa.rs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,3 +30,33 @@ impl SignatureVerificationAlgorithm for Ed25519Verify {
3030
}
3131

3232
pub const ED25519: &dyn SignatureVerificationAlgorithm = &Ed25519Verify;
33+
34+
#[derive(Debug)]
35+
struct Ed448Verify;
36+
37+
impl SignatureVerificationAlgorithm for Ed448Verify {
38+
fn public_key_alg_id(&self) -> AlgorithmIdentifier {
39+
alg_id::ED448
40+
}
41+
42+
fn signature_alg_id(&self) -> AlgorithmIdentifier {
43+
alg_id::ED448
44+
}
45+
46+
fn verify_signature(
47+
&self,
48+
public_key: &[u8],
49+
message: &[u8],
50+
signature: &[u8],
51+
) -> Result<(), InvalidSignature> {
52+
let public_key = public_key.try_into().map_err(|_| InvalidSignature)?;
53+
let signature =
54+
ed448_goldilocks::Signature::from_slice(signature).map_err(|_| InvalidSignature)?;
55+
ed448_goldilocks::VerifyingKey::from_bytes(public_key)
56+
.map_err(|_| InvalidSignature)?
57+
.verify(message, &signature)
58+
.map_err(|_| InvalidSignature)
59+
}
60+
}
61+
62+
pub const ED448: &dyn SignatureVerificationAlgorithm = &Ed448Verify;

0 commit comments

Comments
 (0)