Skip to content

Commit 4f0a975

Browse files
committed
feat: add mldsa44_ed25519 key support
1 parent c05c0cd commit 4f0a975

26 files changed

Lines changed: 1322 additions & 8 deletions

Cargo.lock

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

ssh-key/Cargo.toml

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ dsa = { version = "0.7", optional = true, default-features = false, features = [
3232
ed25519-dalek = { version = "3", optional = true, default-features = false }
3333
hex = { version = "0.4", optional = true, default-features = false, features = ["alloc"] }
3434
hmac = { version = "0.13", optional = true }
35+
ml-dsa = { version = "0.1", optional = true, default-features = false, features = ["alloc", "zeroize"] }
3536
p256 = { version = "0.14", optional = true, default-features = false, features = ["ecdsa"] }
3637
p384 = { version = "0.14", optional = true, default-features = false, features = ["ecdsa"] }
3738
p521 = { version = "0.14", optional = true, default-features = false, features = ["ecdsa"] }
@@ -51,7 +52,7 @@ default = ["ecdsa", "rand_core", "std"]
5152
alloc = ["encoding/alloc", "signature/alloc", "zeroize/alloc", ]
5253
std = ["alloc"]
5354

54-
crypto = ["ed25519", "p256", "p384", "p521", "rsa"] # NOTE: `dsa` is obsolete/weak
55+
crypto = ["ed25519", "mldsa-eddsa", "p256", "p384", "p521", "rsa"] # NOTE: `dsa` is obsolete/weak
5556
dsa = ["dep:dsa", "dep:sha1", "alloc", "encoding/bigint", "signature/rand_core"]
5657
ecdsa = ["dep:sec1"]
5758
ed25519 = ["dep:ed25519-dalek", "rand_core"]
@@ -63,6 +64,9 @@ encryption = [
6364
"rand_core"
6465
]
6566
getrandom = ["cipher/getrandom", "rand_core"]
67+
# MLDSA44-Ed25519-SHA512 composite signatures, as implemented by OpenSSH. Based on
68+
# draft-miller-sshm-mldsa44-ed25519-composite-sigs, which is still a draft
69+
mldsa-eddsa = ["dep:ml-dsa", "ed25519", "alloc", "rand_core"]
6670
p256 = ["dep:p256", "ecdsa"]
6771
p384 = ["dep:p384", "ecdsa"]
6872
p521 = ["dep:p521", "ecdsa"]

ssh-key/README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ respective SSH key algorithm.
4242
- [x] OpenSSH certificate authority (CA) support i.e. cert builder/signer
4343
- [x] Private key encryption/decryption (`bcrypt-pbkdf` + `aes256-ctr` only)
4444
- [x] Private key generation support: DSA, Ed25519, ECDSA (P-256/P-384/P-521),
45-
and RSA
45+
RSA, and MLDSA44-Ed25519
4646
- [x] FIDO/U2F key support (`sk-*`) as specified in [PROTOCOL.u2f]
4747
- [x] Fingerprint support
4848
- [x] "randomart" fingerprint visualizations
@@ -73,6 +73,7 @@ respective SSH key algorithm.
7373
| `ssh‑rsa` |||| ✅️ | ✅️ || `rsa` | `alloc` |
7474
| `sk‑ecdsa‑sha2‑nistp256@openssh.com` ||||| ⛔️ | ✅️ || `alloc` |
7575
| `sk‑ssh‑ed25519@openssh.com` ||||| ⛔️ | ✅️️ | `ed25519` | `alloc` |
76+
| `ssh‑mldsa44‑ed25519@openssh.com` |||| ✅️ | ✅️ | ✅️ | `mldsa-eddsa` | `alloc` |
7677

7778
By default *no SSH signature algorithms are enabled* and you will get an
7879
`Error::AlgorithmUnsupported` error if you try to use them.

ssh-key/src/algorithm.rs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,9 @@ const CERT_SK_ECDSA_SHA2_P256: &str = "sk-ecdsa-sha2-nistp256-cert-v01@openssh.c
4747
/// OpenSSH certificate for Ed25519 U2F/FIDO security key
4848
const CERT_SK_SSH_ED25519: &str = "sk-ssh-ed25519-cert-v01@openssh.com";
4949

50+
/// OpenSSH certificate for MLDSA44-Ed25519 composite public key
51+
const CERT_MLDSA44_ED25519: &str = "ssh-mldsa44-ed25519-cert-v01@openssh.com";
52+
5053
/// ECDSA with SHA-256 + NIST P-256
5154
const ECDSA_SHA2_P256: &str = "ecdsa-sha2-nistp256";
5255

@@ -86,6 +89,9 @@ const SK_ECDSA_SHA2_P256: &str = "sk-ecdsa-sha2-nistp256@openssh.com";
8689
/// U2F/FIDO security key with Ed25519
8790
const SK_SSH_ED25519: &str = "sk-ssh-ed25519@openssh.com";
8891

92+
/// MLDSA44-Ed25519-SHA512 composite signature algorithm
93+
const SSH_MLDSA44_ED25519: &str = "ssh-mldsa44-ed25519@openssh.com";
94+
8995
/// SSH key algorithms, i.e. digital signature algorithms used with SSH private/public keys.
9096
#[derive(Clone, Debug, Default, Eq, Hash, PartialEq, PartialOrd, Ord)]
9197
#[non_exhaustive]
@@ -121,6 +127,9 @@ pub enum Algorithm {
121127
/// FIDO/U2F key with Ed25519
122128
SkEd25519,
123129

130+
/// MLDSA44-Ed25519-SHA512 composite signature algorithm.
131+
MlDsa44Ed25519,
132+
124133
/// Other
125134
#[cfg(feature = "alloc")]
126135
Other(AlgorithmName),
@@ -138,6 +147,7 @@ impl Algorithm {
138147
/// - `ssh-rsa`
139148
/// - `sk-ecdsa-sha2-nistp256@openssh.com` (FIDO/U2F key)
140149
/// - `sk-ssh-ed25519@openssh.com` (FIDO/U2F key)
150+
/// - `ssh-mldsa44-ed25519@openssh.com`
141151
///
142152
/// Any other algorithms are mapped to the [`Algorithm::Other`] variant.
143153
///
@@ -161,6 +171,7 @@ impl Algorithm {
161171
/// - `ssh-ed25519-cert-v01@openssh.com`
162172
/// - `sk-ecdsa-sha2-nistp256-cert-v01@openssh.com` (FIDO/U2F key)
163173
/// - `sk-ssh-ed25519-cert-v01@openssh.com` (FIDO/U2F key)
174+
/// - `ssh-mldsa44-ed25519-cert-v01@openssh.com`
164175
///
165176
/// Any other algorithms are mapped to the [`Algorithm::Other`] variant.
166177
///
@@ -190,6 +201,7 @@ impl Algorithm {
190201
}),
191202
CERT_SK_ECDSA_SHA2_P256 => Ok(Algorithm::SkEcdsaSha2NistP256),
192203
CERT_SK_SSH_ED25519 => Ok(Algorithm::SkEd25519),
204+
CERT_MLDSA44_ED25519 => Ok(Algorithm::MlDsa44Ed25519),
193205
#[cfg(feature = "alloc")]
194206
_ => Ok(Algorithm::Other(AlgorithmName::from_certificate_type(id)?)),
195207
#[cfg(not(feature = "alloc"))]
@@ -215,6 +227,7 @@ impl Algorithm {
215227
},
216228
Algorithm::SkEcdsaSha2NistP256 => SK_ECDSA_SHA2_P256,
217229
Algorithm::SkEd25519 => SK_SSH_ED25519,
230+
Algorithm::MlDsa44Ed25519 => SSH_MLDSA44_ED25519,
218231
#[cfg(feature = "alloc")]
219232
Algorithm::Other(algorithm) => algorithm.as_str(),
220233
}
@@ -247,6 +260,7 @@ impl Algorithm {
247260
} => CERT_RSA_SHA2_512,
248261
Algorithm::SkEcdsaSha2NistP256 => CERT_SK_ECDSA_SHA2_P256,
249262
Algorithm::SkEd25519 => CERT_SK_SSH_ED25519,
263+
Algorithm::MlDsa44Ed25519 => CERT_MLDSA44_ED25519,
250264
Algorithm::Other(algorithm) => return algorithm.certificate_type(),
251265
}
252266
.to_owned()
@@ -276,6 +290,12 @@ impl Algorithm {
276290
matches!(self, Algorithm::Rsa { .. })
277291
}
278292

293+
/// Is the algorithm the MLDSA44-Ed25519 composite scheme?
294+
#[must_use]
295+
pub fn is_mldsa44_ed25519(self) -> bool {
296+
matches!(self, Algorithm::MlDsa44Ed25519)
297+
}
298+
279299
/// Return an error indicating this algorithm is unsupported.
280300
#[allow(dead_code)]
281301
pub(crate) fn unsupported_error(self) -> Error {
@@ -322,6 +342,7 @@ impl str::FromStr for Algorithm {
322342
SSH_RSA => Ok(Algorithm::Rsa { hash: None }),
323343
SK_ECDSA_SHA2_P256 => Ok(Algorithm::SkEcdsaSha2NistP256),
324344
SK_SSH_ED25519 => Ok(Algorithm::SkEd25519),
345+
SSH_MLDSA44_ED25519 => Ok(Algorithm::MlDsa44Ed25519),
325346
#[cfg(feature = "alloc")]
326347
_ => Ok(Algorithm::Other(AlgorithmName::from_str(id)?)),
327348
#[cfg(not(feature = "alloc"))]

ssh-key/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,8 @@ mod kdf;
139139
mod comment;
140140
#[cfg(feature = "std")]
141141
mod dot_ssh;
142+
#[cfg(feature = "mldsa-eddsa")]
143+
mod mldsa_eddsa;
142144
#[cfg(feature = "ppk")]
143145
mod ppk;
144146
#[cfg(feature = "alloc")]

0 commit comments

Comments
 (0)