Skip to content

Commit 90317be

Browse files
authored
Add DTLS 1.2 ChaCha20 and X25519 support
1 parent 57218a5 commit 90317be

34 files changed

Lines changed: 637 additions & 219 deletions

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
# Unreleased
22

3+
* Add DTLS 1.2 ChaCha20 and X25519 support #77
34
* Bump MSRV to 1.85.0 #75
45
* Make cipher and kx configurable #73
56

Cargo.lock

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

Cargo.toml

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -38,23 +38,23 @@ _crypto-common = ["dep:der", "dep:pkcs8", "dep:sec1", "dep:signature", "dep:spki
3838
rcgen = ["dep:rcgen", "aws-lc-rs"]
3939

4040
[dependencies]
41-
log = "0.4.29"
41+
log = "0.4"
4242
nom = { version = "8", default-features = false, features = ["std"] }
43-
once_cell = "1.21.3"
43+
once_cell = "1"
4444
rand = "0.9"
4545
time = { version = "0.3", features = ["formatting"] }
46-
arrayvec = "0.7.6"
47-
subtle = "2.6"
46+
arrayvec = "0.7"
47+
subtle = "2"
4848

4949
der = { version = "0.7", optional = true }
5050
pkcs8 = { version = "0.10", features = ["pem"], optional = true }
5151
sec1 = { version = "0.7", optional = true }
52-
signature = { version = "2.2", optional = true }
52+
signature = { version = "2", optional = true }
5353
spki = { version = "0.7", optional = true }
5454
x509-cert = { version = "0.2", default-features = false, optional = true }
5555

5656
# aws-lc-rs backend
57-
aws-lc-rs = { version = "^1.16", default-features = false, features = ["aws-lc-sys", "prebuilt-nasm"], optional = true }
57+
aws-lc-rs = { version = "1", default-features = false, features = ["aws-lc-sys", "prebuilt-nasm"], optional = true }
5858

5959
# RustCrypto backend
6060
aes-gcm = { version = "0.10", optional = true }
@@ -71,15 +71,15 @@ chacha20 = { version = "0.9", optional = true }
7171
x25519-dalek = { version = "2", optional = true, features = ["static_secrets"] }
7272

7373
# certificate generation
74-
rcgen = { version = "0.14.7", default-features = false, features = ["aws_lc_rs"], optional = true }
74+
rcgen = { version = "0.14", default-features = false, features = ["aws_lc_rs"], optional = true }
7575

7676
[dev-dependencies]
77-
openssl = { version = "0.10.75", features = ["vendored"] }
77+
openssl = { version = "0.10", features = ["vendored"] }
7878
libc = "0.2"
79-
env_logger = "0.11.9"
79+
env_logger = "0.11"
8080
x509-parser = "0.18"
8181
bytes = "1"
8282

8383
# wolfssl doesn't build on Windows
8484
[target.'cfg(not(windows))'.dev-dependencies]
85-
wolfssl = "3.0.0"
85+
wolfssl = "3"

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ Three constructors control which DTLS version is used:
3333
- **Cipher suites (TLS 1.2 over DTLS)**
3434
- `ECDHE_ECDSA_AES256_GCM_SHA384`
3535
- `ECDHE_ECDSA_AES128_GCM_SHA256`
36+
- `ECDHE_ECDSA_CHACHA20_POLY1305_SHA256`
3637
- **Cipher suites (TLS 1.3 over DTLS)**
3738
- `TLS_AES_128_GCM_SHA256`
3839
- `TLS_AES_256_GCM_SHA384`
@@ -105,6 +106,7 @@ fn example_event_loop(mut dtls: Dtls) -> Result<(), dimpl::Error> {
105106
Output::ApplicationData(_data) => {
106107
// Deliver plaintext to application
107108
}
109+
_ => {}
108110
}
109111
}
110112

src/buffer.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ impl BufferPool {
3838
}
3939

4040
impl fmt::Debug for BufferPool {
41-
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
41+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
4242
f.debug_struct("BufferPool")
4343
.field("free", &self.free.len())
4444
.finish()
@@ -118,7 +118,7 @@ impl AsMut<[u8]> for Buf {
118118
}
119119

120120
impl fmt::Debug for Buf {
121-
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
121+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
122122
f.debug_struct("Buf").field("len", &self.0.len()).finish()
123123
}
124124
}

src/config.rs

Lines changed: 16 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -198,15 +198,6 @@ impl Config {
198198
None => true,
199199
})
200200
}
201-
202-
/// Allowed key exchange groups for DTLS 1.2.
203-
///
204-
/// Like [`kx_groups`](Self::kx_groups) but additionally restricted to
205-
/// groups that DTLS 1.2 supports (currently P-256 and P-384).
206-
pub fn dtls12_kx_groups(&self) -> impl Iterator<Item = &'static dyn SupportedKxGroup> + '_ {
207-
self.kx_groups()
208-
.filter(|kx| matches!(kx.name(), NamedGroup::Secp256r1 | NamedGroup::Secp384r1))
209-
}
210201
}
211202

212203
/// Builder for [`Config`]. See each setter for defaults.
@@ -446,13 +437,13 @@ impl ConfigBuilder {
446437
};
447438
if dtls12_count > 0 {
448439
let dtls12_kx_count = crypto_provider
449-
.supported_dtls12_kx_groups()
440+
.supported_kx_groups()
450441
.filter(|kx| filtered_kx(kx))
451442
.count();
452443
if dtls12_kx_count == 0 {
453444
return Err(Error::ConfigError(
454445
"DTLS 1.2 cipher suites are enabled but no compatible key exchange \
455-
groups remain after filtering. DTLS 1.2 requires P-256 or P-384."
446+
groups remain after filtering."
456447
.to_string(),
457448
));
458449
}
@@ -635,23 +626,15 @@ mod tests {
635626
}
636627

637628
#[test]
638-
fn x25519_only_rejected_for_dtls12() {
639-
// X25519 is not yet supported for DTLS 1.2, so filtering to X25519-only
640-
// while DTLS 1.2 suites are enabled should fail.
641-
match Config::builder()
629+
fn x25519_only_accepted_for_dtls12() {
630+
// X25519 is supported for DTLS 1.2 and should be accepted.
631+
let config = Config::builder()
642632
.dtls13_cipher_suites(&[])
643633
.kx_groups(&[NamedGroup::X25519])
644634
.build()
645-
{
646-
Err(Error::ConfigError(msg)) => {
647-
assert!(
648-
msg.contains("DTLS 1.2") && msg.contains("P-256 or P-384"),
649-
"error should mention DTLS 1.2 and required groups: {msg}"
650-
)
651-
}
652-
Err(other) => panic!("expected ConfigError, got: {other:?}"),
653-
Ok(_) => panic!("expected error for X25519-only with DTLS 1.2"),
654-
}
635+
.expect("X25519-only should be accepted for DTLS 1.2");
636+
let groups: Vec<_> = config.kx_groups().map(|g| g.name()).collect();
637+
assert_eq!(groups, &[NamedGroup::X25519]);
655638
}
656639

657640
#[test]
@@ -667,11 +650,15 @@ mod tests {
667650
}
668651

669652
#[test]
670-
fn dtls12_kx_groups_excludes_x25519() {
653+
fn kx_groups_match_provider_when_unfiltered() {
671654
let config = Config::default();
672-
let dtls12_groups: Vec<_> = config.dtls12_kx_groups().map(|g| g.name()).collect();
673-
assert!(!dtls12_groups.contains(&NamedGroup::X25519));
674-
assert!(dtls12_groups.contains(&NamedGroup::Secp256r1));
655+
let from_config: Vec<_> = config.kx_groups().map(|g| g.name()).collect();
656+
let from_provider: Vec<_> = config
657+
.crypto_provider()
658+
.supported_kx_groups()
659+
.map(|g| g.name())
660+
.collect();
661+
assert_eq!(from_config, from_provider);
675662
}
676663

677664
#[test]

src/crypto/aws_lc_rs/cipher_suite.rs

Lines changed: 52 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,14 @@ impl SupportedDtls12CipherSuite for Aes128GcmSha256 {
159159
(0, 16, 4) // (mac_key_len, enc_key_len, fixed_iv_len)
160160
}
161161

162+
fn explicit_nonce_len(&self) -> usize {
163+
8
164+
}
165+
166+
fn tag_len(&self) -> usize {
167+
16
168+
}
169+
162170
fn create_cipher(&self, key: &[u8]) -> Result<Box<dyn Cipher>, String> {
163171
Ok(Box::new(AesGcm::new(key)?))
164172
}
@@ -181,18 +189,60 @@ impl SupportedDtls12CipherSuite for Aes256GcmSha384 {
181189
(0, 32, 4) // (mac_key_len, enc_key_len, fixed_iv_len)
182190
}
183191

192+
fn explicit_nonce_len(&self) -> usize {
193+
8
194+
}
195+
196+
fn tag_len(&self) -> usize {
197+
16
198+
}
199+
184200
fn create_cipher(&self, key: &[u8]) -> Result<Box<dyn Cipher>, String> {
185201
Ok(Box::new(AesGcm::new(key)?))
186202
}
187203
}
188204

205+
/// TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256 cipher suite.
206+
#[derive(Debug)]
207+
struct ChaCha20Poly1305Sha256;
208+
209+
impl SupportedDtls12CipherSuite for ChaCha20Poly1305Sha256 {
210+
fn suite(&self) -> Dtls12CipherSuite {
211+
Dtls12CipherSuite::ECDHE_ECDSA_CHACHA20_POLY1305_SHA256
212+
}
213+
214+
fn hash_algorithm(&self) -> HashAlgorithm {
215+
HashAlgorithm::SHA256
216+
}
217+
218+
fn key_lengths(&self) -> (usize, usize, usize) {
219+
(0, 32, 12) // (mac_key_len, enc_key_len, fixed_iv_len)
220+
}
221+
222+
fn explicit_nonce_len(&self) -> usize {
223+
0
224+
}
225+
226+
fn tag_len(&self) -> usize {
227+
16
228+
}
229+
230+
fn create_cipher(&self, key: &[u8]) -> Result<Box<dyn Cipher>, String> {
231+
Ok(Box::new(ChaCha20Poly1305Cipher::new(key)?))
232+
}
233+
}
234+
189235
/// Static instances of supported DTLS 1.2 cipher suites.
190236
static AES_128_GCM_SHA256: Aes128GcmSha256 = Aes128GcmSha256;
191237
static AES_256_GCM_SHA384: Aes256GcmSha384 = Aes256GcmSha384;
238+
static CHACHA20_POLY1305_SHA256: ChaCha20Poly1305Sha256 = ChaCha20Poly1305Sha256;
192239

193240
/// All supported DTLS 1.2 cipher suites.
194-
pub(super) static ALL_CIPHER_SUITES: &[&dyn SupportedDtls12CipherSuite] =
195-
&[&AES_128_GCM_SHA256, &AES_256_GCM_SHA384];
241+
pub(super) static ALL_CIPHER_SUITES: &[&dyn SupportedDtls12CipherSuite] = &[
242+
&AES_128_GCM_SHA256,
243+
&AES_256_GCM_SHA384,
244+
&CHACHA20_POLY1305_SHA256,
245+
];
196246

197247
// ============================================================================
198248
// DTLS 1.3 Cipher Suites

src/crypto/aws_lc_rs/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,9 +77,11 @@ use super::CryptoProvider;
7777
///
7878
/// - `TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256` (0xC02B)
7979
/// - `TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384` (0xC02C)
80+
/// - `TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256` (0xCCA9)
8081
///
8182
/// # Supported Key Exchange Groups
8283
///
84+
/// - `x25519` (X25519 / Curve25519)
8385
/// - `secp256r1` (P-256, NIST Curve)
8486
/// - `secp384r1` (P-384, NIST Curve)
8587
///

0 commit comments

Comments
 (0)