Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# Unreleased

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

Expand Down
44 changes: 22 additions & 22 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

20 changes: 10 additions & 10 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -38,23 +38,23 @@ _crypto-common = ["dep:der", "dep:pkcs8", "dep:sec1", "dep:signature", "dep:spki
rcgen = ["dep:rcgen", "aws-lc-rs"]

[dependencies]
log = "0.4.29"
log = "0.4"
nom = { version = "8", default-features = false, features = ["std"] }
once_cell = "1.21.3"
once_cell = "1"
rand = "0.9"
time = { version = "0.3", features = ["formatting"] }
arrayvec = "0.7.6"
subtle = "2.6"
arrayvec = "0.7"
subtle = "2"

der = { version = "0.7", optional = true }
pkcs8 = { version = "0.10", features = ["pem"], optional = true }
sec1 = { version = "0.7", optional = true }
signature = { version = "2.2", optional = true }
signature = { version = "2", optional = true }
spki = { version = "0.7", optional = true }
x509-cert = { version = "0.2", default-features = false, optional = true }

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

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

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

[dev-dependencies]
openssl = { version = "0.10.75", features = ["vendored"] }
openssl = { version = "0.10", features = ["vendored"] }
libc = "0.2"
env_logger = "0.11.9"
env_logger = "0.11"
x509-parser = "0.18"
bytes = "1"

# wolfssl doesn't build on Windows
[target.'cfg(not(windows))'.dev-dependencies]
wolfssl = "3.0.0"
wolfssl = "3"
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ Three constructors control which DTLS version is used:
- **Cipher suites (TLS 1.2 over DTLS)**
- `ECDHE_ECDSA_AES256_GCM_SHA384`
- `ECDHE_ECDSA_AES128_GCM_SHA256`
- `ECDHE_ECDSA_CHACHA20_POLY1305_SHA256`
- **Cipher suites (TLS 1.3 over DTLS)**
- `TLS_AES_128_GCM_SHA256`
- `TLS_AES_256_GCM_SHA384`
Expand Down Expand Up @@ -105,6 +106,7 @@ fn example_event_loop(mut dtls: Dtls) -> Result<(), dimpl::Error> {
Output::ApplicationData(_data) => {
// Deliver plaintext to application
}
_ => {}
}
}

Expand Down
4 changes: 2 additions & 2 deletions src/buffer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ impl BufferPool {
}

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

impl fmt::Debug for Buf {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Buf").field("len", &self.0.len()).finish()
}
}
Expand Down
45 changes: 16 additions & 29 deletions src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -198,15 +198,6 @@ impl Config {
None => true,
})
}

/// Allowed key exchange groups for DTLS 1.2.
///
/// Like [`kx_groups`](Self::kx_groups) but additionally restricted to
/// groups that DTLS 1.2 supports (currently P-256 and P-384).
pub fn dtls12_kx_groups(&self) -> impl Iterator<Item = &'static dyn SupportedKxGroup> + '_ {
self.kx_groups()
.filter(|kx| matches!(kx.name(), NamedGroup::Secp256r1 | NamedGroup::Secp384r1))
}
}

/// Builder for [`Config`]. See each setter for defaults.
Expand Down Expand Up @@ -446,13 +437,13 @@ impl ConfigBuilder {
};
if dtls12_count > 0 {
let dtls12_kx_count = crypto_provider
.supported_dtls12_kx_groups()
.supported_kx_groups()
.filter(|kx| filtered_kx(kx))
.count();
if dtls12_kx_count == 0 {
return Err(Error::ConfigError(
"DTLS 1.2 cipher suites are enabled but no compatible key exchange \
groups remain after filtering. DTLS 1.2 requires P-256 or P-384."
groups remain after filtering."
.to_string(),
));
}
Expand Down Expand Up @@ -635,23 +626,15 @@ mod tests {
}

#[test]
fn x25519_only_rejected_for_dtls12() {
// X25519 is not yet supported for DTLS 1.2, so filtering to X25519-only
// while DTLS 1.2 suites are enabled should fail.
match Config::builder()
fn x25519_only_accepted_for_dtls12() {
// X25519 is supported for DTLS 1.2 and should be accepted.
let config = Config::builder()
.dtls13_cipher_suites(&[])
.kx_groups(&[NamedGroup::X25519])
.build()
{
Err(Error::ConfigError(msg)) => {
assert!(
msg.contains("DTLS 1.2") && msg.contains("P-256 or P-384"),
"error should mention DTLS 1.2 and required groups: {msg}"
)
}
Err(other) => panic!("expected ConfigError, got: {other:?}"),
Ok(_) => panic!("expected error for X25519-only with DTLS 1.2"),
}
.expect("X25519-only should be accepted for DTLS 1.2");
let groups: Vec<_> = config.kx_groups().map(|g| g.name()).collect();
assert_eq!(groups, &[NamedGroup::X25519]);
}

#[test]
Expand All @@ -667,11 +650,15 @@ mod tests {
}

#[test]
fn dtls12_kx_groups_excludes_x25519() {
fn kx_groups_match_provider_when_unfiltered() {
let config = Config::default();
let dtls12_groups: Vec<_> = config.dtls12_kx_groups().map(|g| g.name()).collect();
assert!(!dtls12_groups.contains(&NamedGroup::X25519));
assert!(dtls12_groups.contains(&NamedGroup::Secp256r1));
let from_config: Vec<_> = config.kx_groups().map(|g| g.name()).collect();
let from_provider: Vec<_> = config
.crypto_provider()
.supported_kx_groups()
.map(|g| g.name())
.collect();
assert_eq!(from_config, from_provider);
}

#[test]
Expand Down
54 changes: 52 additions & 2 deletions src/crypto/aws_lc_rs/cipher_suite.rs
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,14 @@ impl SupportedDtls12CipherSuite for Aes128GcmSha256 {
(0, 16, 4) // (mac_key_len, enc_key_len, fixed_iv_len)
}

fn explicit_nonce_len(&self) -> usize {
8
}

fn tag_len(&self) -> usize {
16
}

fn create_cipher(&self, key: &[u8]) -> Result<Box<dyn Cipher>, String> {
Ok(Box::new(AesGcm::new(key)?))
}
Expand All @@ -181,18 +189,60 @@ impl SupportedDtls12CipherSuite for Aes256GcmSha384 {
(0, 32, 4) // (mac_key_len, enc_key_len, fixed_iv_len)
}

fn explicit_nonce_len(&self) -> usize {
8
}

fn tag_len(&self) -> usize {
16
}

fn create_cipher(&self, key: &[u8]) -> Result<Box<dyn Cipher>, String> {
Ok(Box::new(AesGcm::new(key)?))
}
}

/// TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256 cipher suite.
#[derive(Debug)]
struct ChaCha20Poly1305Sha256;

impl SupportedDtls12CipherSuite for ChaCha20Poly1305Sha256 {
fn suite(&self) -> Dtls12CipherSuite {
Dtls12CipherSuite::ECDHE_ECDSA_CHACHA20_POLY1305_SHA256
}

fn hash_algorithm(&self) -> HashAlgorithm {
HashAlgorithm::SHA256
}

fn key_lengths(&self) -> (usize, usize, usize) {
(0, 32, 12) // (mac_key_len, enc_key_len, fixed_iv_len)
}

fn explicit_nonce_len(&self) -> usize {
0
}

fn tag_len(&self) -> usize {
16
}

fn create_cipher(&self, key: &[u8]) -> Result<Box<dyn Cipher>, String> {
Ok(Box::new(ChaCha20Poly1305Cipher::new(key)?))
}
}

/// Static instances of supported DTLS 1.2 cipher suites.
static AES_128_GCM_SHA256: Aes128GcmSha256 = Aes128GcmSha256;
static AES_256_GCM_SHA384: Aes256GcmSha384 = Aes256GcmSha384;
static CHACHA20_POLY1305_SHA256: ChaCha20Poly1305Sha256 = ChaCha20Poly1305Sha256;

/// All supported DTLS 1.2 cipher suites.
pub(super) static ALL_CIPHER_SUITES: &[&dyn SupportedDtls12CipherSuite] =
&[&AES_128_GCM_SHA256, &AES_256_GCM_SHA384];
pub(super) static ALL_CIPHER_SUITES: &[&dyn SupportedDtls12CipherSuite] = &[
&AES_128_GCM_SHA256,
&AES_256_GCM_SHA384,
&CHACHA20_POLY1305_SHA256,
];

// ============================================================================
// DTLS 1.3 Cipher Suites
Expand Down
2 changes: 2 additions & 0 deletions src/crypto/aws_lc_rs/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -77,9 +77,11 @@ use super::CryptoProvider;
///
/// - `TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256` (0xC02B)
/// - `TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384` (0xC02C)
/// - `TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256` (0xCCA9)
///
/// # Supported Key Exchange Groups
///
/// - `x25519` (X25519 / Curve25519)
/// - `secp256r1` (P-256, NIST Curve)
/// - `secp384r1` (P-384, NIST Curve)
///
Expand Down
Loading