rustls-ccm 0.2.0

CCM and CCM-8 cipher suites for rustls (TLS 1.2 and TLS 1.3)
Documentation
# rustls-ccm

AES-CCM cipher suites for [rustls](https://github.com/rustls/rustls).

Neither [aws-lc-rs](https://github.com/aws/aws-lc-rs) nor [ring](https://github.com/briansmith/ring) expose AES-CCM, so rustls's built-in providers cannot offer these suites. This crate fills the gap using the [RustCrypto](https://github.com/RustCrypto) `aes` + `ccm` crates, plugged in via rustls's [`CryptoProvider`](https://docs.rs/rustls/latest/rustls/crypto/struct.CryptoProvider.html) extension point.

## Why

CCM cipher suites are required or recommended by several IoT and energy protocols:

- **IEEE 2030.5** (Smart Energy) — mandates `TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8`
- **Matter / Thread** — uses TLS 1.3 `TLS_AES_128_CCM_SHA256`
- **RFC 7925** (constrained-device TLS) — profiles CCM and CCM-8 suites
- **CoAP over DTLS** — commonly uses CCM-8 for bandwidth efficiency

Without this crate, using rustls for these protocols requires falling back to OpenSSL or BoringSSL (which removed CCM entirely).

## Cipher suites

### TLS 1.2 ([RFC 7251]https://www.rfc-editor.org/rfc/rfc7251)

| Suite | Tag | Key |
|---|---|---|
| `TLS_ECDHE_ECDSA_WITH_AES_128_CCM` | 16 B | 128-bit |
| `TLS_ECDHE_ECDSA_WITH_AES_256_CCM` | 16 B | 256-bit |
| `TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8` | 8 B | 128-bit |
| `TLS_ECDHE_ECDSA_WITH_AES_256_CCM_8` | 8 B | 256-bit |

### TLS 1.3 ([RFC 8446]https://www.rfc-editor.org/rfc/rfc8446)

| Suite | Tag | Key |
|---|---|---|
| `TLS_AES_128_CCM_SHA256` | 16 B | 128-bit |
| `TLS_AES_128_CCM_8_SHA256` | 8 B | 128-bit |

## Usage

```toml
[dependencies]
rustls-ccm = "0.2"
rustls = "0.23"
```

### Quick start — all CCM suites

```rust
let provider = rustls_ccm::crypto_provider();
let config = rustls::ClientConfig::builder_with_provider(provider.into())
    .with_safe_default_protocol_versions()
    .unwrap();
```

The CCM suites are appended after the provider defaults, so AES-GCM /
ChaCha20-Poly1305 keep priority and CCM is negotiated only with peers that
offer nothing stronger. To *prefer* CCM (e.g. for a profile that mandates it),
put the suites you want in front, as below.

### Pick specific suites

```rust
use rustls::crypto::CryptoProvider;

let mut provider = rustls::crypto::aws_lc_rs::default_provider();
provider.cipher_suites.insert(0, *rustls_ccm::TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8);

let config = rustls::ClientConfig::builder_with_provider(provider.into())
    .with_safe_default_protocol_versions()
    .unwrap();
```

## Limitations

- Raw traffic-secret extraction for kTLS/hardware offload (`dangerous_extract_secrets()`) is not supported — rustls's `ConnectionTrafficSecrets` has no CCM variant, so `extract_keys` returns `UnsupportedOperationError` for all CCM suites. `SSLKEYLOGFILE`-style key logging (`rustls::KeyLog`) is unaffected and works normally.

## How it works

The crate borrows key exchange (ECDHE), signature (ECDSA), hash (SHA-256), PRF, and HKDF implementations from the aws-lc-rs provider. Only the AEAD algorithm is replaced with a CCM implementation built on RustCrypto's [`aes`](https://crates.io/crates/aes) and [`ccm`](https://crates.io/crates/ccm) crates.

## Testing

`cargo test` runs loopback handshake tests for every suite, plus
interoperability tests against `openssl s_server` covering all six suites in
both record directions. The interop tests need an OpenSSL 3.x binary
(`openssl` on `PATH`, a Homebrew `openssl@3` install, or the `OPENSSL` env
var) and are skipped with a notice when none is found — macOS's bundled
LibreSSL does not support the CCM suites.

## License

MIT OR Apache-2.0