Bump boring to v5, align FIPS to SP 800-52r2, clean up features
- Bump boring/boring-sys from v4 to v5 (zero API breaks) - Merge fips/fips-only into a single fips feature that both enables FIPS-validated BoringSSL and restricts algorithms to SP 800-52r2 - Tighten FIPS KX groups to P-256 and P-384 only (aligned with boring's fips202205 compliance policy) - Remove ECDSA_P521_SHA512 from FIPS signature verification set - Simplify fips feature to forward boring/fips only (drop redundant boring-sys/fips) - Add fips-precompiled as deprecated alias matching boring's naming - Change default features to empty (TLS 1.2 now requires explicit tls12 feature opt-in) - Gate TLS 1.2 code paths properly so the crate compiles and passes tests with default (TLS 1.3 only) features - Update README to reflect current state: boring v5, feature docs, FIPS mode documentation, workspace structure
This commit is contained in:
parent
490340afa7
commit
271acbb315
11 changed files with 273 additions and 77 deletions
63
.github/workflows/ci.yml
vendored
63
.github/workflows/ci.yml
vendored
|
|
@ -9,10 +9,9 @@ on:
|
||||||
env:
|
env:
|
||||||
CARGO_TERM_COLOR: always
|
CARGO_TERM_COLOR: always
|
||||||
RUSTFLAGS: -Dwarnings
|
RUSTFLAGS: -Dwarnings
|
||||||
FEATURES: "logging,tls12"
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
fmt:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
|
|
@ -21,9 +20,57 @@ jobs:
|
||||||
run: sudo apt-get install -y cmake clang
|
run: sudo apt-get install -y cmake clang
|
||||||
- name: Check fmt
|
- name: Check fmt
|
||||||
run: make fmt
|
run: make fmt
|
||||||
- name: Lint
|
|
||||||
run: make lint
|
test-default:
|
||||||
- name: Tests usual
|
runs-on: ubuntu-latest
|
||||||
run: make test
|
|
||||||
- name: Build usual
|
steps:
|
||||||
run: make build
|
- uses: actions/checkout@v3
|
||||||
|
- name: Install dependencies
|
||||||
|
run: sudo apt-get install -y cmake clang
|
||||||
|
- name: Lint (default features)
|
||||||
|
run: make lint FEATURES=""
|
||||||
|
- name: Test (default features)
|
||||||
|
run: make test FEATURES=""
|
||||||
|
- name: Build (default features)
|
||||||
|
run: make build FEATURES=""
|
||||||
|
|
||||||
|
test-tls12:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
- name: Install dependencies
|
||||||
|
run: sudo apt-get install -y cmake clang
|
||||||
|
- name: Lint (tls12)
|
||||||
|
run: make lint FEATURES="tls12"
|
||||||
|
- name: Test (tls12)
|
||||||
|
run: make test FEATURES="tls12"
|
||||||
|
- name: Build (tls12)
|
||||||
|
run: make build FEATURES="tls12"
|
||||||
|
|
||||||
|
test-logging-tls12:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
- name: Install dependencies
|
||||||
|
run: sudo apt-get install -y cmake clang
|
||||||
|
- name: Lint (logging,tls12)
|
||||||
|
run: make lint FEATURES="logging,tls12"
|
||||||
|
- name: Test (logging,tls12)
|
||||||
|
run: make test FEATURES="logging,tls12"
|
||||||
|
- name: Build (logging,tls12)
|
||||||
|
run: make build FEATURES="logging,tls12"
|
||||||
|
|
||||||
|
check-fips:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
- name: Install dependencies
|
||||||
|
run: sudo apt-get install -y cmake clang
|
||||||
|
- name: Check (fips)
|
||||||
|
run: cargo check -p boring-rustls-provider --all-targets --features fips
|
||||||
|
- name: Check (fips-precompiled)
|
||||||
|
run: cargo check -p boring-rustls-provider --all-targets --features fips-precompiled
|
||||||
|
|
|
||||||
|
|
@ -17,8 +17,8 @@ default-members = [
|
||||||
resolver = "2"
|
resolver = "2"
|
||||||
|
|
||||||
[workspace.dependencies]
|
[workspace.dependencies]
|
||||||
boring = { version = "4", default-features = false }
|
boring = { version = "5", default-features = false }
|
||||||
boring-sys = { version = "4", default-features = false }
|
boring-sys = { version = "5", default-features = false }
|
||||||
rustls = { version = "0.23", default-features = false }
|
rustls = { version = "0.23", default-features = false }
|
||||||
rustls-pemfile = { version = "2" }
|
rustls-pemfile = { version = "2" }
|
||||||
rustls-pki-types = { version = "1" }
|
rustls-pki-types = { version = "1" }
|
||||||
|
|
|
||||||
11
Makefile
11
Makefile
|
|
@ -1,4 +1,5 @@
|
||||||
FEATURES ?= logging,tls12
|
FEATURES ?= logging,tls12
|
||||||
|
CARGO_FEATURES := $(if $(strip $(FEATURES)),-F "$(FEATURES)",)
|
||||||
|
|
||||||
|
|
||||||
.PHONY: fmt
|
.PHONY: fmt
|
||||||
|
|
@ -7,12 +8,16 @@ fmt:
|
||||||
|
|
||||||
.PHONY: lint
|
.PHONY: lint
|
||||||
lint:
|
lint:
|
||||||
cargo clippy --workspace --all-targets -F "$(FEATURES)"
|
cargo clippy --workspace --all-targets $(CARGO_FEATURES)
|
||||||
|
|
||||||
|
.PHONY: check
|
||||||
|
check:
|
||||||
|
cargo check --workspace --all-targets $(CARGO_FEATURES)
|
||||||
|
|
||||||
.PHONY: test
|
.PHONY: test
|
||||||
test:
|
test:
|
||||||
cargo test --all-targets -F "$(FEATURES)"
|
cargo test --all-targets $(CARGO_FEATURES)
|
||||||
|
|
||||||
.PHONY: build
|
.PHONY: build
|
||||||
build:
|
build:
|
||||||
cargo build --all-targets -F "$(FEATURES)"
|
cargo build --all-targets $(CARGO_FEATURES)
|
||||||
|
|
|
||||||
71
Readme.md
71
Readme.md
|
|
@ -2,55 +2,60 @@
|
||||||
|
|
||||||
[](https://github.com/janrueth/boring-rustls-provider/actions/workflows/ci.yml?query=branch%3Amain)
|
[](https://github.com/janrueth/boring-rustls-provider/actions/workflows/ci.yml?query=branch%3Amain)
|
||||||
|
|
||||||
This is supposed to be the start to a [boringssl](https://github.com/cloudflare/boring)-based [rustls](https://github.com/rustls/rustls) crypto provider.
|
A [BoringSSL](https://github.com/cloudflare/boring)-based [rustls](https://github.com/rustls/rustls) crypto provider.
|
||||||
|
|
||||||
## Status
|
Built on `boring` v5 and `rustls` 0.23.
|
||||||
This is just a dump of me figuring out how to interface with boring and rustls.
|
|
||||||
It works to establish a connection and exchange data but I haven't written real tests yet, nor did I cleanup the code or made the effort to make it look nice.
|
|
||||||
There is probably some code in here that should rather live in the `boring` crate.
|
|
||||||
|
|
||||||
Further, the rustls crypto provider API is still not stable it seems. This works currently with `rustls = 0.22.0-alpha.5`.
|
## Features
|
||||||
|
|
||||||
### Supported ciphers
|
No features are enabled by default. The provider ships with TLS 1.3 support
|
||||||
Currently, supports only TLS 1.3:
|
out of the box; additional capabilities are opt-in.
|
||||||
|
|
||||||
|
| Feature | Description |
|
||||||
|
|---|---|
|
||||||
|
| `fips` | Build against FIPS-validated BoringSSL and restrict the provider to FIPS-approved algorithms only (SP 800-52r2). See [FIPS mode](#fips-mode) below. |
|
||||||
|
| `fips-precompiled` | Deprecated alias for `fips`. Matches the `boring` crate's feature name. |
|
||||||
|
| `tls12` | Enable TLS 1.2 cipher suites (`ECDHE-ECDSA` and `ECDHE-RSA` with AES-GCM and ChaCha20-Poly1305). Without this only TLS 1.3 is available. |
|
||||||
|
| `logging` | Enable debug logging of BoringSSL errors and provider internals via the `log` crate. |
|
||||||
|
|
||||||
|
## Supported Algorithms
|
||||||
|
|
||||||
|
### Cipher Suites
|
||||||
|
|
||||||
|
TLS 1.3 (always available):
|
||||||
```
|
```
|
||||||
AES_128_GCM_SHA256
|
AES_128_GCM_SHA256
|
||||||
AES_256_GCM_SHA384
|
AES_256_GCM_SHA384
|
||||||
CHACHA20_POLY1305_SHA256
|
CHACHA20_POLY1305_SHA256
|
||||||
```
|
```
|
||||||
|
|
||||||
QUIC: not yet supported
|
TLS 1.2 (requires `tls12` feature):
|
||||||
|
|
||||||
TLS 1.2:
|
|
||||||
```
|
```
|
||||||
ECDHE_ECDSA_AES128_GCM_SHA256
|
ECDHE_ECDSA_AES128_GCM_SHA256
|
||||||
ECDHE_RSA_AES128_GCM_SHA256
|
ECDHE_RSA_AES128_GCM_SHA256
|
||||||
|
|
||||||
ECDHE_ECDSA_AES256_GCM_SHA384
|
ECDHE_ECDSA_AES256_GCM_SHA384
|
||||||
ECDHE_RSA_AES256_GCM_SHA384
|
ECDHE_RSA_AES256_GCM_SHA384
|
||||||
|
|
||||||
ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256
|
ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256
|
||||||
ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256
|
ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256
|
||||||
```
|
```
|
||||||
|
|
||||||
### Key Exchange Algorithms
|
### Key Exchange Groups
|
||||||
|
|
||||||
`ECDHE` with curves:
|
ECDHE:
|
||||||
```
|
```
|
||||||
X25519
|
X25519
|
||||||
X448
|
X448
|
||||||
secp256r1
|
secp256r1 (P-256)
|
||||||
secp384r1
|
secp384r1 (P-384)
|
||||||
secp521r1
|
secp521r1 (P-521)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
FFDHE:
|
||||||
`FFDHE` with:
|
|
||||||
```
|
```
|
||||||
ffdhe2048
|
ffdhe2048
|
||||||
```
|
```
|
||||||
|
|
||||||
### Signature Generation / Verification
|
### Signature Algorithms
|
||||||
|
|
||||||
```
|
```
|
||||||
RSA_PKCS1_SHA256
|
RSA_PKCS1_SHA256
|
||||||
|
|
@ -66,6 +71,30 @@ ED25519
|
||||||
ED448
|
ED448
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## FIPS Mode
|
||||||
|
|
||||||
|
When the `fips` feature is enabled the provider builds against a FIPS-validated
|
||||||
|
version of BoringSSL and restricts all algorithm selections to those approved
|
||||||
|
under [SP 800-52r2](https://doi.org/10.6028/NIST.SP.800-52r2), aligned with
|
||||||
|
boring's `fips202205` compliance policy:
|
||||||
|
|
||||||
|
- **Cipher suites**: AES-GCM only (no ChaCha20-Poly1305).
|
||||||
|
- **Key exchange groups**: P-256 and P-384 only (no X25519, X448, P-521, or FFDHE).
|
||||||
|
- **Signature algorithms**: RSA PKCS#1 / PSS and ECDSA with P-256 or P-384 only
|
||||||
|
(no P-521, Ed25519, or Ed448).
|
||||||
|
|
||||||
|
Post-quantum hybrid key exchange (`P256Kyber768Draft00`) is planned for the
|
||||||
|
FIPS group set but not yet implemented.
|
||||||
|
|
||||||
|
## Workspace Structure
|
||||||
|
|
||||||
|
| Crate | Purpose |
|
||||||
|
|---|---|
|
||||||
|
| `boring-rustls-provider` | The main rustls crypto provider. |
|
||||||
|
| `boring-additions` | Safe Rust wrappers for BoringSSL APIs not yet exposed by the `boring` crate (AEAD, EVP_PKEY_CTX, HMAC_CTX). Intended for upstreaming. |
|
||||||
|
| `boring-sys-additions` | Raw FFI binding for `CRYPTO_tls1_prf` (internal BoringSSL symbol used for FIPS-compliant TLS 1.2 PRF). Intended for upstreaming. |
|
||||||
|
| `examples` | Example client binary. |
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
MIT
|
MIT
|
||||||
|
|
|
||||||
|
|
@ -8,13 +8,30 @@ description = "Boringssl rustls provider"
|
||||||
publish = false
|
publish = false
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["tls12"]
|
default = []
|
||||||
# Use a FIPS-validated version of boringssl.
|
|
||||||
fips = ["boring/fips", "boring-sys/fips"]
|
# Build against a FIPS-validated version of BoringSSL and restrict the
|
||||||
logging = ["log"]
|
# provider to FIPS-approved algorithms only. This affects:
|
||||||
fips-only = ["boring/fips", "boring-sys/fips"]
|
# - Cipher suites: AES-GCM only (no ChaCha20-Poly1305).
|
||||||
|
# - Key exchange groups: P-256 and P-384 only (no X25519, X448, P-521,
|
||||||
|
# or FFDHE). P256Kyber768Draft00 will be added once implemented.
|
||||||
|
# - Signature algorithms: RSA PKCS#1 / PSS and ECDSA with P-256/P-384
|
||||||
|
# only (no P-521, Ed25519, or Ed448).
|
||||||
|
# Aligned with boring's `fips202205` compliance policy (SP 800-52r2).
|
||||||
|
fips = ["boring/fips"]
|
||||||
|
|
||||||
|
# Deprecated alias for `fips`. Matches the boring crate's feature name
|
||||||
|
# for backwards compatibility.
|
||||||
|
fips-precompiled = ["fips"]
|
||||||
|
|
||||||
|
# Enable TLS 1.2 cipher suites (ECDHE-ECDSA and ECDHE-RSA with AES-GCM
|
||||||
|
# and ChaCha20-Poly1305). Without this feature only TLS 1.3 is available.
|
||||||
tls12 = ["rustls/tls12"]
|
tls12 = ["rustls/tls12"]
|
||||||
|
|
||||||
|
# Enable debug logging of BoringSSL errors and provider internals via
|
||||||
|
# the `log` crate. Useful for diagnosing handshake failures.
|
||||||
|
logging = ["log"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
aead = {version = "0.5", default-features = false, features = ["alloc"] }
|
aead = {version = "0.5", default-features = false, features = ["alloc"] }
|
||||||
boring = { workspace = true }
|
boring = { workspace = true }
|
||||||
|
|
|
||||||
|
|
@ -3,9 +3,9 @@ use std::marker::PhantomData;
|
||||||
use aead::{AeadCore, AeadInPlace, Buffer, Nonce, Tag};
|
use aead::{AeadCore, AeadInPlace, Buffer, Nonce, Tag};
|
||||||
use boring::error::ErrorStack;
|
use boring::error::ErrorStack;
|
||||||
use boring_additions::aead::Algorithm;
|
use boring_additions::aead::Algorithm;
|
||||||
use rustls::crypto::cipher::{
|
#[cfg(feature = "tls12")]
|
||||||
self, make_tls12_aad, make_tls13_aad, BorrowedPayload, Iv, PrefixedPayload,
|
use rustls::crypto::cipher::make_tls12_aad;
|
||||||
};
|
use rustls::crypto::cipher::{self, make_tls13_aad, BorrowedPayload, Iv, PrefixedPayload};
|
||||||
use rustls::{ConnectionTrafficSecrets, ContentType, ProtocolVersion};
|
use rustls::{ConnectionTrafficSecrets, ContentType, ProtocolVersion};
|
||||||
|
|
||||||
use crate::helper::log_and_map;
|
use crate::helper::log_and_map;
|
||||||
|
|
@ -20,6 +20,8 @@ pub(crate) trait BoringCipher {
|
||||||
/// The IV's fixed length (Not the full IV length, only the part that doesn't change).
|
/// The IV's fixed length (Not the full IV length, only the part that doesn't change).
|
||||||
/// Together with [`BoringCipher::explicit_nonce_len`] it determines the total
|
/// Together with [`BoringCipher::explicit_nonce_len`] it determines the total
|
||||||
/// lengths of the used nonce.
|
/// lengths of the used nonce.
|
||||||
|
/// Used only by TLS 1.2 code paths.
|
||||||
|
#[cfg(feature = "tls12")]
|
||||||
const FIXED_IV_LEN: usize;
|
const FIXED_IV_LEN: usize;
|
||||||
/// The key size in bytes
|
/// The key size in bytes
|
||||||
const KEY_SIZE: usize;
|
const KEY_SIZE: usize;
|
||||||
|
|
@ -569,7 +571,8 @@ impl Buffer for EncryptBufferAdapter<'_> {
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use hex_literal::hex;
|
use hex_literal::hex;
|
||||||
use rustls::crypto::cipher::{AeadKey, Iv};
|
use rustls::crypto::cipher::AeadKey;
|
||||||
|
use rustls::crypto::cipher::Iv;
|
||||||
|
|
||||||
use crate::aead::BoringAeadCrypter;
|
use crate::aead::BoringAeadCrypter;
|
||||||
use rustls::quic::PacketKey;
|
use rustls::quic::PacketKey;
|
||||||
|
|
@ -611,7 +614,7 @@ mod tests {
|
||||||
let unprotected_header = hex!("4200bff4");
|
let unprotected_header = hex!("4200bff4");
|
||||||
|
|
||||||
let protector = BoringAeadCrypter::<ChaCha20Poly1305>::new(
|
let protector = BoringAeadCrypter::<ChaCha20Poly1305>::new(
|
||||||
Iv::new(iv),
|
Iv::from(iv),
|
||||||
&key,
|
&key,
|
||||||
rustls::ProtocolVersion::TLSv1_3,
|
rustls::ProtocolVersion::TLSv1_3,
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@ impl BoringAead for Aes128 {}
|
||||||
impl BoringCipher for Aes128 {
|
impl BoringCipher for Aes128 {
|
||||||
const EXPLICIT_NONCE_LEN: usize = 8;
|
const EXPLICIT_NONCE_LEN: usize = 8;
|
||||||
|
|
||||||
|
#[cfg(feature = "tls12")]
|
||||||
const FIXED_IV_LEN: usize = 4;
|
const FIXED_IV_LEN: usize = 4;
|
||||||
|
|
||||||
const KEY_SIZE: usize = 16;
|
const KEY_SIZE: usize = 16;
|
||||||
|
|
@ -55,6 +56,7 @@ impl BoringAead for Aes256 {}
|
||||||
impl BoringCipher for Aes256 {
|
impl BoringCipher for Aes256 {
|
||||||
const EXPLICIT_NONCE_LEN: usize = 8;
|
const EXPLICIT_NONCE_LEN: usize = 8;
|
||||||
|
|
||||||
|
#[cfg(feature = "tls12")]
|
||||||
const FIXED_IV_LEN: usize = 4;
|
const FIXED_IV_LEN: usize = 4;
|
||||||
|
|
||||||
const KEY_SIZE: usize = 32;
|
const KEY_SIZE: usize = 32;
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,7 @@ impl BoringAead for ChaCha20Poly1305 {}
|
||||||
impl BoringCipher for ChaCha20Poly1305 {
|
impl BoringCipher for ChaCha20Poly1305 {
|
||||||
const EXPLICIT_NONCE_LEN: usize = 0;
|
const EXPLICIT_NONCE_LEN: usize = 0;
|
||||||
|
|
||||||
|
#[cfg(feature = "tls12")]
|
||||||
const FIXED_IV_LEN: usize = 12;
|
const FIXED_IV_LEN: usize = 12;
|
||||||
|
|
||||||
const KEY_SIZE: usize = 32;
|
const KEY_SIZE: usize = 32;
|
||||||
|
|
|
||||||
|
|
@ -22,11 +22,11 @@ pub mod tls13;
|
||||||
pub mod verify;
|
pub mod verify;
|
||||||
|
|
||||||
pub fn provider() -> CryptoProvider {
|
pub fn provider() -> CryptoProvider {
|
||||||
#[cfg(feature = "fips-only")]
|
#[cfg(feature = "fips")]
|
||||||
{
|
{
|
||||||
provider_with_ciphers(ALL_FIPS_CIPHER_SUITES.to_vec())
|
provider_with_ciphers(ALL_FIPS_CIPHER_SUITES.to_vec())
|
||||||
}
|
}
|
||||||
#[cfg(not(feature = "fips-only"))]
|
#[cfg(not(feature = "fips"))]
|
||||||
{
|
{
|
||||||
provider_with_ciphers(ALL_CIPHER_SUITES.to_vec())
|
provider_with_ciphers(ALL_CIPHER_SUITES.to_vec())
|
||||||
}
|
}
|
||||||
|
|
@ -35,13 +35,13 @@ pub fn provider() -> CryptoProvider {
|
||||||
pub fn provider_with_ciphers(ciphers: Vec<rustls::SupportedCipherSuite>) -> CryptoProvider {
|
pub fn provider_with_ciphers(ciphers: Vec<rustls::SupportedCipherSuite>) -> CryptoProvider {
|
||||||
CryptoProvider {
|
CryptoProvider {
|
||||||
cipher_suites: ciphers,
|
cipher_suites: ciphers,
|
||||||
#[cfg(feature = "fips-only")]
|
#[cfg(feature = "fips")]
|
||||||
kx_groups: ALL_FIPS_KX_GROUPS.to_vec(),
|
kx_groups: ALL_FIPS_KX_GROUPS.to_vec(),
|
||||||
#[cfg(not(feature = "fips-only"))]
|
#[cfg(not(feature = "fips"))]
|
||||||
kx_groups: ALL_KX_GROUPS.to_vec(),
|
kx_groups: ALL_KX_GROUPS.to_vec(),
|
||||||
#[cfg(feature = "fips-only")]
|
#[cfg(feature = "fips")]
|
||||||
signature_verification_algorithms: verify::ALL_FIPS_ALGORITHMS,
|
signature_verification_algorithms: verify::ALL_FIPS_ALGORITHMS,
|
||||||
#[cfg(not(feature = "fips-only"))]
|
#[cfg(not(feature = "fips"))]
|
||||||
signature_verification_algorithms: verify::ALL_ALGORITHMS,
|
signature_verification_algorithms: verify::ALL_ALGORITHMS,
|
||||||
secure_random: &Provider,
|
secure_random: &Provider,
|
||||||
key_provider: &Provider,
|
key_provider: &Provider,
|
||||||
|
|
@ -99,18 +99,15 @@ static ALL_CIPHER_SUITES: &[SupportedCipherSuite] = &[
|
||||||
SupportedCipherSuite::Tls12(&tls12::ECDHE_RSA_AES128_GCM_SHA256),
|
SupportedCipherSuite::Tls12(&tls12::ECDHE_RSA_AES128_GCM_SHA256),
|
||||||
];
|
];
|
||||||
|
|
||||||
/// Allowed KX curves for FIPS are recommended
|
/// Allowed KX groups for FIPS per [SP 800-52r2](https://doi.org/10.6028/NIST.SP.800-52r2),
|
||||||
/// in [NIST SP 800-186](https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-186.pdf)
|
/// aligned with boring's `fips202205` compliance policy.
|
||||||
///
|
///
|
||||||
/// See Sec. 3.1.2 Table 2
|
/// See Section 3.3.1 and 3.4.2.2.
|
||||||
/// Ordered in decending order of security strength
|
// TODO: Add P256Kyber768Draft00 once the PQ hybrid KEM is implemented (Step 3).
|
||||||
#[allow(unused)]
|
#[allow(unused)]
|
||||||
pub const ALL_FIPS_KX_GROUPS: &[&dyn SupportedKxGroup] = &[
|
pub const ALL_FIPS_KX_GROUPS: &[&dyn SupportedKxGroup] = &[
|
||||||
&kx::Secp521r1 as _, // P-521 in FIPS lingo
|
&kx::Secp256r1 as _, // P-256
|
||||||
&kx::X448 as _, // Curve448 in FIPS lingo
|
&kx::Secp384r1 as _, // P-384
|
||||||
&kx::Secp384r1 as _, // P-384 in FIPS lingo
|
|
||||||
&kx::X25519 as _, // Curve25519 in FIPS lingo
|
|
||||||
&kx::Secp256r1 as _, // P-256 in FIPS lingo
|
|
||||||
];
|
];
|
||||||
|
|
||||||
#[allow(unused)]
|
#[allow(unused)]
|
||||||
|
|
|
||||||
|
|
@ -61,6 +61,13 @@ pub static ALL_ALGORITHMS: WebPkiSupportedAlgorithms = WebPkiSupportedAlgorithms
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// FIPS-approved signature verification algorithms per SP 800-52r2.
|
||||||
|
///
|
||||||
|
/// Aligned with boring's `fips202205` compliance policy:
|
||||||
|
/// - RSA: PKCS#1 v1.5 and PSS with SHA-256/384/512
|
||||||
|
/// - ECDSA: P-256 with SHA-256 and P-384 with SHA-384 only
|
||||||
|
/// (SP 800-52r2 Table 4.1: "The curve should be P-256 or P-384")
|
||||||
|
/// - No P-521, Ed25519, or Ed448
|
||||||
#[allow(unused)]
|
#[allow(unused)]
|
||||||
pub static ALL_FIPS_ALGORITHMS: WebPkiSupportedAlgorithms = WebPkiSupportedAlgorithms {
|
pub static ALL_FIPS_ALGORITHMS: WebPkiSupportedAlgorithms = WebPkiSupportedAlgorithms {
|
||||||
all: &[
|
all: &[
|
||||||
|
|
@ -72,9 +79,6 @@ pub static ALL_FIPS_ALGORITHMS: WebPkiSupportedAlgorithms = WebPkiSupportedAlgor
|
||||||
&rsa::BoringRsaVerifier::RSA_PSS_SHA512,
|
&rsa::BoringRsaVerifier::RSA_PSS_SHA512,
|
||||||
&ec::BoringEcVerifier::ECDSA_NISTP256_SHA256,
|
&ec::BoringEcVerifier::ECDSA_NISTP256_SHA256,
|
||||||
&ec::BoringEcVerifier::ECDSA_NISTP384_SHA384,
|
&ec::BoringEcVerifier::ECDSA_NISTP384_SHA384,
|
||||||
&ec::BoringEcVerifier::ECDSA_NISTP521_SHA512,
|
|
||||||
//&ed::BoringEdVerifier::ED25519, // FIPS 186-5: requires SHA512 but boring doesn't want us to set a digest, correct?
|
|
||||||
//&ed::BoringEdVerifier::ED448, // FIPS 186-5: requires SHAKE256 but boring doesn't want us to set a digest, correct?
|
|
||||||
],
|
],
|
||||||
mapping: &[
|
mapping: &[
|
||||||
(
|
(
|
||||||
|
|
@ -109,11 +113,5 @@ pub static ALL_FIPS_ALGORITHMS: WebPkiSupportedAlgorithms = WebPkiSupportedAlgor
|
||||||
SignatureScheme::ECDSA_NISTP384_SHA384,
|
SignatureScheme::ECDSA_NISTP384_SHA384,
|
||||||
&[&ec::BoringEcVerifier::ECDSA_NISTP384_SHA384],
|
&[&ec::BoringEcVerifier::ECDSA_NISTP384_SHA384],
|
||||||
),
|
),
|
||||||
(
|
|
||||||
SignatureScheme::ECDSA_NISTP521_SHA512,
|
|
||||||
&[&ec::BoringEcVerifier::ECDSA_NISTP521_SHA512],
|
|
||||||
),
|
|
||||||
// (SignatureScheme::ED25519, &[&ed::BoringEdVerifier::ED25519]),
|
|
||||||
// (SignatureScheme::ED448, &[&ed::BoringEdVerifier::ED448]),
|
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -5,11 +5,12 @@ use tokio::{
|
||||||
net::TcpStream,
|
net::TcpStream,
|
||||||
};
|
};
|
||||||
|
|
||||||
use boring_rustls_provider::{tls12, tls13};
|
#[cfg(feature = "tls12")]
|
||||||
use rustls::{
|
use boring_rustls_provider::tls12;
|
||||||
version::{TLS12, TLS13},
|
use boring_rustls_provider::tls13;
|
||||||
ClientConfig, ServerConfig, SupportedCipherSuite,
|
#[cfg(feature = "tls12")]
|
||||||
};
|
use rustls::version::TLS12;
|
||||||
|
use rustls::{version::TLS13, ClientConfig, ServerConfig, SupportedCipherSuite};
|
||||||
use rustls_pki_types::{CertificateDer, PrivateKeyDer, PrivatePkcs8KeyDer};
|
use rustls_pki_types::{CertificateDer, PrivateKeyDer, PrivatePkcs8KeyDer};
|
||||||
use tokio::net::TcpListener;
|
use tokio::net::TcpListener;
|
||||||
use tokio_rustls::{TlsAcceptor, TlsConnector};
|
use tokio_rustls::{TlsAcceptor, TlsConnector};
|
||||||
|
|
@ -41,17 +42,107 @@ async fn test_tls13_crypto() {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg(any(feature = "fips", feature = "fips-only"))]
|
#[cfg(feature = "fips")]
|
||||||
fn is_fips_enabled() {
|
fn is_fips_enabled() {
|
||||||
assert!(boring::fips::enabled());
|
assert!(boring::fips::enabled());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg(not(any(feature = "fips", feature = "fips-only")))]
|
#[cfg(feature = "fips")]
|
||||||
|
fn fips_provider_excludes_chacha20_cipher_suites() {
|
||||||
|
use rustls::CipherSuite;
|
||||||
|
|
||||||
|
let provider = boring_rustls_provider::provider();
|
||||||
|
let disallowed = [
|
||||||
|
CipherSuite::TLS13_CHACHA20_POLY1305_SHA256,
|
||||||
|
CipherSuite::TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256,
|
||||||
|
CipherSuite::TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,
|
||||||
|
];
|
||||||
|
|
||||||
|
for suite in provider.cipher_suites {
|
||||||
|
let selected = suite.suite();
|
||||||
|
assert!(
|
||||||
|
!disallowed.contains(&selected),
|
||||||
|
"FIPS provider exposed disallowed cipher suite: {selected:?}"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[cfg(feature = "fips")]
|
||||||
|
fn fips_provider_restricts_kx_groups() {
|
||||||
|
use rustls::NamedGroup;
|
||||||
|
|
||||||
|
let provider = boring_rustls_provider::provider();
|
||||||
|
let groups = provider
|
||||||
|
.kx_groups
|
||||||
|
.iter()
|
||||||
|
.map(|group| group.name())
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
assert!(groups.contains(&NamedGroup::secp256r1));
|
||||||
|
assert!(groups.contains(&NamedGroup::secp384r1));
|
||||||
|
for group in groups {
|
||||||
|
assert!(
|
||||||
|
matches!(group, NamedGroup::secp256r1 | NamedGroup::secp384r1),
|
||||||
|
"FIPS provider exposed disallowed KX group: {group:?}"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[cfg(feature = "fips")]
|
||||||
|
fn fips_provider_excludes_disallowed_signature_schemes() {
|
||||||
|
use rustls::SignatureScheme;
|
||||||
|
|
||||||
|
let provider = boring_rustls_provider::provider();
|
||||||
|
let schemes = provider
|
||||||
|
.signature_verification_algorithms
|
||||||
|
.mapping
|
||||||
|
.iter()
|
||||||
|
.map(|(scheme, _)| *scheme)
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
assert!(schemes.contains(&SignatureScheme::RSA_PSS_SHA256));
|
||||||
|
assert!(schemes.contains(&SignatureScheme::ECDSA_NISTP256_SHA256));
|
||||||
|
|
||||||
|
for disallowed in [
|
||||||
|
SignatureScheme::ECDSA_NISTP521_SHA512,
|
||||||
|
SignatureScheme::ED25519,
|
||||||
|
SignatureScheme::ED448,
|
||||||
|
] {
|
||||||
|
assert!(
|
||||||
|
!schemes.contains(&disallowed),
|
||||||
|
"FIPS provider exposed disallowed signature scheme: {disallowed:?}"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[cfg(not(feature = "fips"))]
|
||||||
fn is_fips_disabled() {
|
fn is_fips_disabled() {
|
||||||
assert!(!boring::fips::enabled());
|
assert!(!boring::fips::enabled());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[cfg(not(feature = "fips"))]
|
||||||
|
fn non_fips_provider_keeps_non_fips_algorithms() {
|
||||||
|
use rustls::{CipherSuite, NamedGroup};
|
||||||
|
|
||||||
|
let provider = boring_rustls_provider::provider();
|
||||||
|
|
||||||
|
assert!(provider
|
||||||
|
.cipher_suites
|
||||||
|
.iter()
|
||||||
|
.any(|suite| { suite.suite() == CipherSuite::TLS13_CHACHA20_POLY1305_SHA256 }));
|
||||||
|
|
||||||
|
assert!(provider
|
||||||
|
.kx_groups
|
||||||
|
.iter()
|
||||||
|
.any(|group| group.name() == NamedGroup::X25519));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "tls12")]
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn test_tls12_ec_crypto() {
|
async fn test_tls12_ec_crypto() {
|
||||||
let pki = TestPki::new(&rcgen::PKCS_ECDSA_P256_SHA256);
|
let pki = TestPki::new(&rcgen::PKCS_ECDSA_P256_SHA256);
|
||||||
|
|
@ -78,6 +169,7 @@ async fn test_tls12_ec_crypto() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "tls12")]
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn test_tls12_rsa_crypto() {
|
async fn test_tls12_rsa_crypto() {
|
||||||
let pki = TestPki::new(&rcgen::PKCS_RSA_SHA256);
|
let pki = TestPki::new(&rcgen::PKCS_RSA_SHA256);
|
||||||
|
|
@ -188,9 +280,14 @@ impl TestPki {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn server_config(self) -> Arc<ServerConfig> {
|
fn server_config(self) -> Arc<ServerConfig> {
|
||||||
|
#[cfg(feature = "tls12")]
|
||||||
|
let versions: &[&'static rustls::SupportedProtocolVersion] = &[&TLS12, &TLS13];
|
||||||
|
#[cfg(not(feature = "tls12"))]
|
||||||
|
let versions: &[&'static rustls::SupportedProtocolVersion] = &[&TLS13];
|
||||||
|
|
||||||
let mut server_config =
|
let mut server_config =
|
||||||
ServerConfig::builder_with_provider(Arc::new(boring_rustls_provider::provider()))
|
ServerConfig::builder_with_provider(Arc::new(boring_rustls_provider::provider()))
|
||||||
.with_protocol_versions(&[&TLS12, &TLS13])
|
.with_protocol_versions(versions)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.with_no_client_auth()
|
.with_no_client_auth()
|
||||||
.with_single_cert(vec![self.server_cert_der], self.server_key_der)
|
.with_single_cert(vec![self.server_cert_der], self.server_key_der)
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue