Skip to content

Commit 41ddfa0

Browse files
committed
Harden docs CI and API exports
1 parent 70fcf5e commit 41ddfa0

9 files changed

Lines changed: 124 additions & 27 deletions

File tree

.github/workflows/ci.yml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,12 @@ jobs:
8787
run: cargo clippy --all-targets -- -D warnings
8888
- name: cargo clippy --no-default-features (warnings)
8989
run: cargo clippy --no-default-features --all-targets -- -D warnings
90+
- name: cargo clippy --features fips (warnings)
91+
run: cargo clippy --features fips --all-targets -- -D warnings
92+
- name: cargo doc (warnings)
93+
run: cargo doc --no-deps
94+
env:
95+
RUSTDOCFLAGS: -D warnings
9096

9197
coverage:
9298
name: Coverage

Cargo.toml

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,12 @@ pkcs1 = { version = "0.7.5", features = ["std"] }
2020
pkcs8 = "0.11.0"
2121
rustls = { version = "0.23.20", default-features = false, features = ["std"] }
2222
sec1 = "0.8.1"
23+
zeroize = "1.8.1"
24+
25+
[target.'cfg(windows)'.dependencies]
2326
windows = { version = "0.61.3", features = [
2427
"Win32_Security_Cryptography",
25-
"Win32_System_WinRT",
2628
] }
27-
zeroize = "1.8.1"
2829

2930
[features]
3031
default = ["tls12"]

README.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,19 @@ Full test coverage requires Windows because the provider calls Windows CNG APIs:
2121
cargo test
2222
```
2323

24+
The default test suite is hermetic. The crates.io interoperability test is ignored by default because it requires live network access; run it explicitly when needed:
25+
26+
```bash
27+
cargo test test_to_internet -- --ignored
28+
```
29+
30+
This crate only builds for Windows targets. From non-Windows hosts, run check and documentation workflows with an explicit Windows target:
31+
32+
```bash
33+
cargo check --target x86_64-pc-windows-msvc
34+
RUSTDOCFLAGS='-D warnings' cargo doc --no-deps --target x86_64-pc-windows-msvc
35+
```
36+
2437
Run formatting checks before submitting changes:
2538

2639
```bash

build.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
// Unless explicitly stated otherwise all files in this repository are licensed under the MIT License.
2+
//
3+
// This product includes software developed at Datadog (https://www.datadoghq.com/)
4+
// Copyright 2026 Datadog, Inc.
5+
6+
fn main() {
7+
if std::env::var("CARGO_CFG_TARGET_OS").as_deref() != Ok("windows") {
8+
panic!(
9+
"rustls-cng-crypto uses Windows CNG APIs and only builds for Windows targets; \
10+
use --target x86_64-pc-windows-msvc when checking from non-Windows hosts"
11+
);
12+
}
13+
}

src/fips.rs

Lines changed: 15 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -3,27 +3,6 @@
33
// This product includes software developed at Datadog (https://www.datadoghq.com/)
44
// Copyright 2026 Datadog, Inc.
55

6-
//! # FIPS support
7-
//!
8-
//! To use rustls with this crate in FIPS mode, perform the following actions.
9-
//!
10-
//! ## 1. Enable FIPS mode for Windows
11-
//!
12-
//! See [Microsoft documentation](https://learn.microsoft.com/en-us/windows/security/security-foundations/certification/fips-140-validation).
13-
//!
14-
//! ## 2. Enable the `fips` feature, or explicitly use the [crate::fips_provider()] function
15-
//!
16-
//! The fips feature changes the behaviour of [crate::default_provider()] to use FIPS-approved cipher suites and key exchange groups.
17-
//! Or you can explicitly use the [crate::fips_provider()] function to create a provider with FIPS-approved cipher suites and key exchange groups.
18-
//! If Windows is not running in FIPS mode, the provider will be empty.
19-
//!
20-
//! ## 3. Specify `require_ems` when constructing [rustls::ClientConfig] or [rustls::ServerConfig]
21-
//!
22-
//! See [rustls documentation](https://docs.rs/rustls/latest/rustls/client/struct.ClientConfig.html#structfield.require_ems) for rationale.
23-
//!
24-
//! ## 4. Validate the FIPS status of your ClientConfig or ServerConfig at runtime
25-
//! See [rustls documentation on FIPS](https://docs.rs/rustls/latest/rustls/manual/_06_fips/index.html#3-validate-the-fips-status-of-your-clientconfigserverconfig-at-run-time).
26-
276
use rustls::crypto::CryptoProvider;
287
use windows::Win32::Security::Cryptography::BCryptGetFipsAlgorithmMode;
298

@@ -39,6 +18,21 @@ pub(crate) fn enabled() -> bool {
3918

4019
/// Returns a CNG-based [`CryptoProvider`] using FIPS-approved cipher suites and key exchange groups.
4120
///
21+
/// To use rustls with this provider in FIPS mode:
22+
///
23+
/// 1. Enable FIPS mode for Windows. See Microsoft's
24+
/// [FIPS 140 Validation](https://learn.microsoft.com/en-us/windows/security/security-foundations/certification/fips-140-validation)
25+
/// documentation.
26+
/// 2. Enable this crate's `fips` feature, or explicitly use [`crate::fips_provider()`]. The `fips`
27+
/// feature changes [`crate::default_provider()`] to use FIPS-approved cipher suites and key
28+
/// exchange groups.
29+
/// 3. Specify `require_ems` when constructing [`rustls::ClientConfig`] or
30+
/// [`rustls::ServerConfig`]. See the rustls
31+
/// [FIPS manual](https://docs.rs/rustls/latest/rustls/manual/_06_fips/index.html)
32+
/// for rationale.
33+
/// 4. Validate the FIPS status of your `ClientConfig` or `ServerConfig` at runtime. See the rustls
34+
/// [FIPS status documentation](https://docs.rs/rustls/latest/rustls/manual/_06_fips/index.html#3-validate-the-fips-status-of-your-clientconfigserverconfig-at-run-time).
35+
///
4236
/// Usage requires that Windows is running in FIPS mode, otherwise the provider will be empty.
4337
pub fn provider() -> CryptoProvider {
4438
CryptoProvider {

src/kx.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
// This product includes software developed at Datadog (https://www.datadoghq.com/)
44
// Copyright 2026 Datadog, Inc.
55

6+
#[cfg(not(feature = "fips"))]
67
use once_cell::sync::Lazy;
78
use rustls::crypto::{ActiveKeyExchange, SharedSecret, SupportedKxGroup};
89
use rustls::{Error, NamedGroup};
@@ -27,6 +28,7 @@ const MAX_SECRET_SIZE: usize = 48;
2728
/// * [SECP256R1]
2829
///
2930
pub const ALL_KX_GROUPS: &[&dyn SupportedKxGroup] = &[X25519, SECP256R1, SECP384R1];
31+
#[cfg(not(feature = "fips"))]
3032
static DEFAULT_KX_GROUPS: Lazy<Vec<&'static dyn SupportedKxGroup>> = Lazy::new(|| {
3133
ALL_KX_GROUPS
3234
.iter()
@@ -75,10 +77,12 @@ impl KxGroup {
7577
}
7678
}
7779

80+
#[cfg(not(feature = "fips"))]
7881
fn usable_kx_group(kx_group: &dyn SupportedKxGroup) -> bool {
7982
kx_group.name() != NamedGroup::X25519 || cng_supports_x25519()
8083
}
8184

85+
#[cfg(not(feature = "fips"))]
8286
fn cng_supports_x25519() -> bool {
8387
// Windows CNG's Curve25519 public-key import behavior differs by OS version. Windows Server
8488
// 2022 accepts the X25519 Wycheproof `u = 4` vector, but Windows Server 2025 rejects it with
@@ -112,6 +116,7 @@ pub const SECP256R1: &dyn SupportedKxGroup = &KxGroup::SECP256R1;
112116
pub const SECP384R1: &dyn SupportedKxGroup = &KxGroup::SECP384R1;
113117

114118
/// Returns key exchange groups usable by the host CNG implementation.
119+
#[cfg(not(feature = "fips"))]
115120
pub fn default_kx_groups() -> Vec<&'static dyn SupportedKxGroup> {
116121
DEFAULT_KX_GROUPS.clone()
117122
}
@@ -282,6 +287,7 @@ mod test {
282287

283288
use crate::{keys::import_ecdh_private_key, kx::EcKeyExchange};
284289

290+
#[cfg(not(feature = "fips"))]
285291
#[test]
286292
fn default_kx_groups_match_cng_x25519_support() {
287293
let advertises_x25519 = super::default_kx_groups()
@@ -325,6 +331,7 @@ mod test {
325331
}
326332
}
327333

334+
#[cfg(not(feature = "fips"))]
328335
#[test]
329336
fn x25519() {
330337
if !super::cng_supports_x25519() {

src/lib.rs

Lines changed: 45 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,8 @@
2424
//! * `TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384`
2525
//! * `TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256`
2626
//! * `TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256`
27-
//! * `TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256`
2827
//! * `TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384`
28+
//! * `TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256`
2929
//! * `TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256`
3030
//!
3131
//! ## Supported Key Exchanges
@@ -53,7 +53,31 @@
5353
//!
5454
//! # Features
5555
//! - `tls12`: Enables TLS 1.2 cipher suites. Enabled by default.
56-
//! - `fips`: Changes the default provider to use FIPS-approved cipher suites and key exchange groups. See [fips].
56+
//! - `fips`: Changes the default provider to use FIPS-approved cipher suites and key exchange groups.
57+
//! See [`fips_provider()`] and [FIPS support](#fips-support).
58+
//!
59+
//! ## FIPS support
60+
//!
61+
//! To use rustls with this crate in FIPS mode:
62+
//!
63+
//! 1. Enable FIPS mode for Windows. See Microsoft's
64+
//! [FIPS 140 Validation](https://learn.microsoft.com/en-us/windows/security/security-foundations/certification/fips-140-validation)
65+
//! documentation.
66+
//! 2. Enable this crate's `fips` feature, or explicitly use [`fips_provider()`]. The `fips`
67+
//! feature changes [`default_provider()`] to use FIPS-approved cipher suites and key exchange
68+
//! groups. If Windows is not running in FIPS mode, the provider will be empty.
69+
//! 3. Specify `require_ems` when constructing [`rustls::ClientConfig`] or
70+
//! [`rustls::ServerConfig`]. See the rustls
71+
//! [FIPS manual](https://docs.rs/rustls/latest/rustls/manual/_06_fips/index.html)
72+
//! for rationale.
73+
//! 4. Validate the FIPS status of your `ClientConfig` or `ServerConfig` at runtime. See the rustls
74+
//! [FIPS status documentation](https://docs.rs/rustls/latest/rustls/manual/_06_fips/index.html#3-validate-the-fips-status-of-your-clientconfigserverconfig-at-run-time).
75+
//!
76+
//! ## Platform support
77+
//!
78+
//! This crate uses Windows CNG APIs and only builds for Windows targets. From non-Windows hosts,
79+
//! run checks and documentation builds with an explicit Windows target such as
80+
//! `--target x86_64-pc-windows-msvc`.
5781
#![warn(missing_docs)]
5882
use rustls::crypto::{CryptoProvider, GetRandomFailed, SupportedKxGroup};
5983
use rustls::SupportedCipherSuite;
@@ -79,6 +103,21 @@ mod verify;
79103

80104
pub mod cipher_suite {
81105
//! Supported cipher suites.
106+
//!
107+
//! ```rust
108+
//! use rustls::CipherSuite;
109+
//! use rustls_cng_crypto::{cipher_suite, custom_provider, kx_group};
110+
//!
111+
//! let provider = custom_provider(
112+
//! vec![cipher_suite::TLS13_CHACHA20_POLY1305_SHA256],
113+
//! vec![kx_group::SECP256R1],
114+
//! );
115+
//!
116+
//! assert_eq!(
117+
//! provider.cipher_suites[0].suite(),
118+
//! CipherSuite::TLS13_CHACHA20_POLY1305_SHA256
119+
//! );
120+
//! ```
82121
#[cfg(feature = "tls12")]
83122
pub use super::tls12::{
84123
TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
@@ -88,7 +127,9 @@ pub mod cipher_suite {
88127
pub use super::tls12::{
89128
TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,
90129
};
91-
pub use super::tls13::{TLS13_AES_128_GCM_SHA256, TLS13_AES_256_GCM_SHA384};
130+
pub use super::tls13::{
131+
TLS13_AES_128_GCM_SHA256, TLS13_AES_256_GCM_SHA384, TLS13_CHACHA20_POLY1305_SHA256,
132+
};
92133
}
93134

94135
pub use alg::ShutdownHandle;
@@ -190,8 +231,8 @@ pub fn custom_provider(
190231
/// * `TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384`
191232
/// * `TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256`
192233
/// * `TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256`
193-
/// * `TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256`
194234
/// * `TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384`
235+
/// * `TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256`
195236
/// * `TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256`
196237
///
197238
/// If the default `tls12` feature is disabled then the TLS 1.2 cipher suites will not be included.

tests/api.rs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// Unless explicitly stated otherwise all files in this repository are licensed under the MIT License.
2+
//
3+
// This product includes software developed at Datadog (https://www.datadoghq.com/)
4+
// Copyright 2026 Datadog, Inc.
5+
6+
use rustls::CipherSuite;
7+
use rustls_cng_crypto::{custom_provider, kx_group};
8+
9+
#[test]
10+
fn tls13_chacha20_poly1305_sha256_is_available_for_custom_providers() {
11+
let provider = custom_provider(
12+
vec![rustls_cng_crypto::cipher_suite::TLS13_CHACHA20_POLY1305_SHA256],
13+
vec![kx_group::SECP256R1],
14+
);
15+
16+
assert_eq!(provider.cipher_suites.len(), 1);
17+
assert_eq!(
18+
provider.cipher_suites[0].suite(),
19+
CipherSuite::TLS13_CHACHA20_POLY1305_SHA256
20+
);
21+
}

tests/it.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,7 @@ fn test_client_and_server(
189189
rustls_cng_crypto::kx_group::SECP384R1,
190190
CipherSuite::TLS13_AES_256_GCM_SHA384
191191
)]
192+
#[ignore = "requires live access to index.crates.io"]
192193
fn test_to_internet(
193194
#[case] suite: SupportedCipherSuite,
194195
#[case] group: &'static dyn SupportedKxGroup,

0 commit comments

Comments
 (0)