Skip to content

Commit f404d58

Browse files
committed
Add unstable support for ML-DSA algorithms
1 parent 620e219 commit f404d58

File tree

7 files changed

+144
-14
lines changed

7 files changed

+144
-14
lines changed

.github/workflows/ci.yml

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -27,12 +27,15 @@ jobs:
2727
with:
2828
components: clippy, rustfmt
2929
- run: cargo fmt -- --check
30-
- run: cargo clippy --all-features --all-targets
30+
# `fips` and `aws_lc_rs_unstable` cannot be used together, so avoid `--all-features`
31+
- run: cargo clippy --features ring,pem,x509-parser --all-targets
3132
# rustls-cert-gen require either aws_lc_rs or ring feature
3233
- run: cargo clippy -p rcgen --no-default-features --all-targets
3334
- run: cargo clippy --no-default-features --features ring --all-targets
3435
- run: cargo clippy --no-default-features --features aws_lc_rs --all-targets
36+
- run: cargo clippy --no-default-features --features aws_lc_rs_unstable --all-targets
3537
- run: cargo clippy --no-default-features --features aws_lc_rs,pem --all-targets
38+
- run: cargo clippy --no-default-features --features fips --all-targets
3639

3740
rustdoc:
3841
name: Documentation
@@ -49,8 +52,16 @@ jobs:
4952
uses: dtolnay/rust-toolchain@master
5053
with:
5154
toolchain: ${{ matrix.toolchain }}
52-
- name: cargo doc (all features)
53-
run: cargo doc --all-features --document-private-items
55+
- name: cargo doc (ring)
56+
run: cargo doc --ring,pem,x509-parser --document-private-items
57+
env:
58+
RUSTDOCFLAGS: ${{ matrix.toolchain == 'nightly' && '-Dwarnings --cfg=docsrs' || '-Dwarnings' }}
59+
- name: cargo doc (aws_lc_rs_unstable)
60+
run: cargo doc --features aws_lc_rs_unstable,pem,x509-parser --document-private-items
61+
env:
62+
RUSTDOCFLAGS: ${{ matrix.toolchain == 'nightly' && '-Dwarnings --cfg=docsrs' || '-Dwarnings' }}
63+
- name: cargo doc (fips)
64+
run: cargo doc --no-default-features --features fips --document-private-items
5465
env:
5566
RUSTDOCFLAGS: ${{ matrix.toolchain == 'nightly' && '-Dwarnings --cfg=docsrs' || '-Dwarnings' }}
5667

@@ -70,7 +81,10 @@ jobs:
7081
- run: cargo install --locked cargo-check-external-types
7182
- name: run cargo-check-external-types for rcgen/
7283
working-directory: rcgen/
73-
run: cargo check-external-types --all-features
84+
run: cargo check-external-types --features ring,pem,x509-parser
85+
- name: run cargo-check-external-types for rcgen/
86+
working-directory: rcgen/
87+
run: cargo check-external-types --features aws_lc_rs_unstable,pem,x509-parser
7488

7589
semver:
7690
name: Check semver compatibility
@@ -94,7 +108,9 @@ jobs:
94108
- uses: dtolnay/rust-toolchain@master
95109
with:
96110
toolchain: 1.71.0
97-
- run: cargo check --locked --lib --all-features
111+
- run: cargo check --locked --lib --features ring,pem,x509-parser
112+
- run: cargo check --locked --lib --features aws_lc_rs_unstable
113+
- run: cargo check --locked --lib --features fips
98114

99115
build-windows:
100116
runs-on: windows-latest
@@ -214,7 +230,7 @@ jobs:
214230
with:
215231
components: llvm-tools
216232
- name: Measure coverage
217-
run: cargo llvm-cov --all-features --lcov --output-path ./lcov.info
233+
run: cargo llvm-cov --features ring,pem,x509-parser --lcov --output-path ./lcov.info
218234
- name: Report to codecov.io
219235
uses: codecov/codecov-action@v5
220236
with:

Cargo.lock

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

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ members = ["rcgen", "rustls-cert-gen"]
33
resolver = "2"
44

55
[workspace.dependencies]
6-
aws-lc-rs = { version = "1.6.0", default-features = false }
6+
aws-lc-rs = { version = "1.13.3", default-features = false }
77
pem = "3.0.2"
88
pki-types = { package = "rustls-pki-types", version = "1.4.1" }
99
ring = "0.17"

rcgen/Cargo.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ zeroize = { version = "1.2", optional = true }
3636
default = ["crypto", "pem", "ring"]
3737
crypto = []
3838
aws_lc_rs = ["crypto", "dep:aws-lc-rs", "aws-lc-rs/aws-lc-sys"]
39+
aws_lc_rs_unstable = ["aws_lc_rs", "aws-lc-rs/unstable"]
3940
ring = ["crypto", "dep:ring"]
4041
fips = ["crypto", "dep:aws-lc-rs", "aws-lc-rs/fips"]
4142

@@ -52,7 +53,7 @@ allowed_external_types = [
5253
[dev-dependencies]
5354
pki-types = { package = "rustls-pki-types", version = "1" }
5455
x509-parser = { workspace = true, features = ["verify"] }
55-
rustls-webpki = { version = "0.103", features = ["ring", "std"] }
56+
rustls-webpki = { version = "0.103.4", features = ["aws-lc-rs-unstable", "ring", "std"] }
5657
botan = { version = "0.11", features = ["vendored"] }
5758
ring = { workspace = true }
5859

rcgen/src/key_pair.rs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ use crate::sign_algo::{algo::*, SignAlgo};
2727
#[cfg(feature = "pem")]
2828
use crate::ENCODE_CONFIG;
2929
use crate::{sign_algo::SignatureAlgorithm, Error};
30+
#[cfg(all(feature = "aws_lc_rs_unstable", not(feature = "fips")))]
31+
use aws_lc_rs::unstable::signature::PqdsaKeyPair;
3032

3133
/// A key pair variant
3234
#[allow(clippy::large_enum_variant)]
@@ -36,6 +38,9 @@ pub(crate) enum KeyPairKind {
3638
Ec(EcdsaKeyPair),
3739
/// A Ed25519 key pair
3840
Ed(Ed25519KeyPair),
41+
/// A Pqdsa key pair
42+
#[cfg(all(feature = "aws_lc_rs_unstable", not(feature = "fips")))]
43+
Pq(PqdsaKeyPair),
3944
/// A RSA key pair
4045
Rsa(RsaKeyPair, &'static dyn RsaEncoding),
4146
}
@@ -46,6 +51,8 @@ impl fmt::Debug for KeyPairKind {
4651
match self {
4752
Self::Ec(key_pair) => write!(f, "{key_pair:?}"),
4853
Self::Ed(key_pair) => write!(f, "{key_pair:?}"),
54+
#[cfg(all(feature = "aws_lc_rs_unstable", not(feature = "fips")))]
55+
Self::Pq(key_pair) => write!(f, "{key_pair:?}"),
4956
Self::Rsa(key_pair, _) => write!(f, "{key_pair:?}"),
5057
}
5158
}
@@ -117,6 +124,17 @@ impl KeyPair {
117124
serialized_der: key_pair_serialized,
118125
})
119126
},
127+
#[cfg(all(feature = "aws_lc_rs_unstable", not(feature = "fips")))]
128+
SignAlgo::PqDsa(sign_alg) => {
129+
let key_pair = PqdsaKeyPair::generate(sign_alg)._err()?;
130+
let key_pair_serialized = key_pair.to_pkcs8()._err()?.as_ref().to_vec();
131+
132+
Ok(KeyPair {
133+
kind: KeyPairKind::Pq(key_pair),
134+
alg,
135+
serialized_der: key_pair_serialized,
136+
})
137+
},
120138
#[cfg(feature = "aws_lc_rs")]
121139
SignAlgo::Rsa(sign_alg) => Self::generate_rsa_inner(alg, sign_alg, KeySize::Rsa2048),
122140
// Ring doesn't have RSA key generation yet:
@@ -436,6 +454,12 @@ impl SigningKey for KeyPair {
436454
signature.as_ref().to_owned()
437455
},
438456
KeyPairKind::Ed(kp) => kp.sign(msg).as_ref().to_owned(),
457+
#[cfg(all(feature = "aws_lc_rs_unstable", not(feature = "fips")))]
458+
KeyPairKind::Pq(kp) => {
459+
let mut signature = vec![0; kp.algorithm().signature_len()];
460+
kp.sign(msg, &mut signature)._err()?;
461+
signature
462+
},
439463
KeyPairKind::Rsa(kp, padding_alg) => {
440464
let system_random = SystemRandom::new();
441465
let mut signature = vec![0; rsa_key_pair_public_modulus_len(kp)];
@@ -453,6 +477,8 @@ impl PublicKeyData for KeyPair {
453477
match &self.kind {
454478
KeyPairKind::Ec(kp) => kp.public_key().as_ref(),
455479
KeyPairKind::Ed(kp) => kp.public_key().as_ref(),
480+
#[cfg(all(feature = "aws_lc_rs_unstable", not(feature = "fips")))]
481+
KeyPairKind::Pq(kp) => kp.public_key().as_ref(),
456482
KeyPairKind::Rsa(kp, _) => kp.public_key().as_ref(),
457483
}
458484
}

rcgen/src/sign_algo.rs

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,18 @@ use yasna::Tag;
88
#[cfg(feature = "crypto")]
99
use crate::ring_like::signature::{self, EcdsaSigningAlgorithm, EdDSAParameters, RsaEncoding};
1010
use crate::Error;
11+
#[cfg(all(feature = "aws_lc_rs_unstable", not(feature = "fips")))]
12+
use aws_lc_rs::unstable::signature::{
13+
PqdsaSigningAlgorithm, ML_DSA_44_SIGNING, ML_DSA_65_SIGNING, ML_DSA_87_SIGNING,
14+
};
1115

1216
#[cfg(feature = "crypto")]
1317
#[derive(Clone, Copy, Debug)]
1418
pub(crate) enum SignAlgo {
1519
EcDsa(&'static EcdsaSigningAlgorithm),
1620
EdDsa(&'static EdDSAParameters),
21+
#[cfg(all(feature = "aws_lc_rs_unstable", not(feature = "fips")))]
22+
PqDsa(&'static PqdsaSigningAlgorithm),
1723
Rsa(&'static dyn RsaEncoding),
1824
}
1925

@@ -209,6 +215,36 @@ pub(crate) mod algo {
209215
oid_components: &[1, 3, 101, 112],
210216
params: SignatureAlgorithmParams::None,
211217
};
218+
219+
/// ML-DSA-44 signing as per <https://www.ietf.org/archive/id/draft-ietf-lamps-dilithium-certificates-07.html#name-identifiers>.
220+
#[cfg(all(feature = "aws_lc_rs_unstable", not(feature = "fips")))]
221+
pub static PKCS_ML_DSA_44: SignatureAlgorithm = SignatureAlgorithm {
222+
oids_sign_alg: &[&[2, 16, 840, 1, 101, 3, 4, 3, 17]],
223+
#[cfg(all(feature = "crypto", feature = "aws_lc_rs_unstable"))]
224+
sign_alg: SignAlgo::PqDsa(&ML_DSA_44_SIGNING),
225+
oid_components: &[2, 16, 840, 1, 101, 3, 4, 3, 17],
226+
params: SignatureAlgorithmParams::None,
227+
};
228+
229+
/// ML-DSA-44 signing as per <https://www.ietf.org/archive/id/draft-ietf-lamps-dilithium-certificates-07.html#name-identifiers>.
230+
#[cfg(all(feature = "aws_lc_rs_unstable", not(feature = "fips")))]
231+
pub static PKCS_ML_DSA_65: SignatureAlgorithm = SignatureAlgorithm {
232+
oids_sign_alg: &[&[2, 16, 840, 1, 101, 3, 4, 3, 18]],
233+
#[cfg(all(feature = "crypto", feature = "aws_lc_rs_unstable"))]
234+
sign_alg: SignAlgo::PqDsa(&ML_DSA_65_SIGNING),
235+
oid_components: &[2, 16, 840, 1, 101, 3, 4, 3, 18],
236+
params: SignatureAlgorithmParams::None,
237+
};
238+
239+
/// ML-DSA-44 signing as per <https://www.ietf.org/archive/id/draft-ietf-lamps-dilithium-certificates-07.html#name-identifiers>.
240+
#[cfg(all(feature = "aws_lc_rs_unstable", not(feature = "fips")))]
241+
pub static PKCS_ML_DSA_87: SignatureAlgorithm = SignatureAlgorithm {
242+
oids_sign_alg: &[&[2, 16, 840, 1, 101, 3, 4, 3, 19]],
243+
#[cfg(all(feature = "crypto", feature = "aws_lc_rs_unstable"))]
244+
sign_alg: SignAlgo::PqDsa(&ML_DSA_87_SIGNING),
245+
oid_components: &[2, 16, 840, 1, 101, 3, 4, 3, 19],
246+
params: SignatureAlgorithmParams::None,
247+
};
212248
}
213249
// Signature algorithm IDs as per https://tools.ietf.org/html/rfc4055
214250
impl SignatureAlgorithm {

rcgen/tests/webpki.rs

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@
22

33
use std::time::Duration as StdDuration;
44

5+
#[cfg(feature = "aws_lc_rs_unstable")]
6+
use aws_lc_rs::unstable::signature::{
7+
PqdsaKeyPair, PqdsaSigningAlgorithm, ML_DSA_44_SIGNING, ML_DSA_65_SIGNING, ML_DSA_87_SIGNING,
8+
};
59
use pki_types::{CertificateDer, ServerName, SignatureVerificationAlgorithm, UnixTime};
610
use ring::rand::SystemRandom;
711
use ring::signature::{self, EcdsaKeyPair, EcdsaSigningAlgorithm, Ed25519KeyPair, KeyPair as _};
@@ -40,6 +44,15 @@ fn sign_msg_ed25519(key_pair: &KeyPair, msg: &[u8]) -> Vec<u8> {
4044
signature.as_ref().to_vec()
4145
}
4246

47+
#[cfg(feature = "aws_lc_rs_unstable")]
48+
fn sign_msg_pq(key_pair: &KeyPair, msg: &[u8], alg: &'static PqdsaSigningAlgorithm) -> Vec<u8> {
49+
let pk_der = key_pair.serialize_der();
50+
let key_pair = PqdsaKeyPair::from_pkcs8(alg, &pk_der).unwrap();
51+
let mut sig = vec![0; alg.signature_len()];
52+
key_pair.sign(msg, &mut sig).unwrap();
53+
sig.to_owned()
54+
}
55+
4356
#[cfg(feature = "pem")]
4457
fn sign_msg_rsa(key_pair: &KeyPair, msg: &[u8], encoding: &'static dyn RsaEncoding) -> Vec<u8> {
4558
let pk_der = key_pair.serialize_der();
@@ -226,6 +239,43 @@ fn test_webpki_rsa_given() {
226239
);
227240
}
228241

242+
#[cfg(all(feature = "pem", feature = "aws_lc_rs_unstable"))]
243+
#[test]
244+
fn test_webpki_ml_dsa() {
245+
let (params, _) = util::default_params();
246+
for (rcgen_alg, webpki_alg, signing_alg) in ML_DSA_ALGS {
247+
let key_pair = KeyPair::generate_for(rcgen_alg).unwrap();
248+
let cert = params.self_signed(&key_pair).unwrap();
249+
250+
// Now verify the certificate.
251+
let sign_fn = |cert, msg| sign_msg_pq(cert, msg, signing_alg);
252+
check_cert(cert.der(), &cert, &key_pair, *webpki_alg, sign_fn);
253+
}
254+
}
255+
256+
#[cfg(feature = "aws_lc_rs_unstable")]
257+
const ML_DSA_ALGS: &[(
258+
&'static rcgen::SignatureAlgorithm,
259+
&'static dyn SignatureVerificationAlgorithm,
260+
&'static PqdsaSigningAlgorithm,
261+
)] = &[
262+
(
263+
&rcgen::PKCS_ML_DSA_44,
264+
webpki::aws_lc_rs::ML_DSA_44,
265+
&ML_DSA_44_SIGNING,
266+
),
267+
(
268+
&rcgen::PKCS_ML_DSA_65,
269+
webpki::aws_lc_rs::ML_DSA_65,
270+
&ML_DSA_65_SIGNING,
271+
),
272+
(
273+
&rcgen::PKCS_ML_DSA_87,
274+
webpki::aws_lc_rs::ML_DSA_87,
275+
&ML_DSA_87_SIGNING,
276+
),
277+
];
278+
229279
#[cfg(feature = "pem")]
230280
#[test]
231281
fn test_webpki_rsa_combinations_given() {

0 commit comments

Comments
 (0)