Skip to content

Commit 9b97e53

Browse files
authored
Merge branch 'main' into dependabot/cargo/pkcs8-0.11.0
2 parents 8cb0b42 + e90b0df commit 9b97e53

7 files changed

Lines changed: 128 additions & 7 deletions

File tree

.github/workflows/ci.yml

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,28 @@ jobs:
3131
- name: Check third-party license inventory
3232
run: make check-licenses
3333

34+
license-policy:
35+
name: License Policy
36+
runs-on: ubuntu-latest
37+
steps:
38+
- name: Check out repository
39+
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
40+
- name: Install toolchain
41+
uses: dtolnay/rust-toolchain@e97e2d8cc328f1b50210efc529dca0028893a2d9 # v1
42+
with:
43+
toolchain: stable
44+
- name: Cache build artifacts
45+
uses: Swatinem/rust-cache@42dc69e1aa15d09112580998cf2ef0119e2e91ae # v2
46+
- name: Check dependency license policy
47+
run: make check-deny
48+
3449
test:
35-
name: Test
36-
runs-on: windows-2022
50+
name: Test (${{ matrix.runner }})
51+
runs-on: ${{ matrix.runner }}
52+
strategy:
53+
fail-fast: false
54+
matrix:
55+
runner: [windows-2022, windows-2025]
3756
steps:
3857
- name: Check out repository
3958
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4

Makefile

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,10 @@
66
.DEFAULT_GOAL := help
77

88
export CARGO_TOOL_VERSION_dd-rust-license-tool ?= 1.0.6
9+
export CARGO_TOOL_VERSION_cargo-deny ?= 0.18.9
910
export CARGO_BIN_DIR := $(shell echo "$${HOME}/.cargo/bin")
1011
DD_RUST_LICENSE_TOOL := $(CARGO_BIN_DIR)/dd-rust-license-tool
12+
CARGO_DENY := $(CARGO_BIN_DIR)/cargo-deny
1113

1214
.PHONY: help
1315
help:
@@ -17,6 +19,7 @@ help:
1719
@printf " %-20s %s\n" "test" "Run the test suite"
1820
@printf " %-20s %s\n" "sync-licenses" "Regenerate LICENSE-3rdparty.csv"
1921
@printf " %-20s %s\n" "check-licenses" "Verify LICENSE-3rdparty.csv is up to date"
22+
@printf " %-20s %s\n" "check-deny" "Check dependency licenses with cargo-deny"
2023

2124
.PHONY: cargo-install-dd-rust-license-tool
2225
cargo-install-dd-rust-license-tool:
@@ -25,6 +28,13 @@ cargo-install-dd-rust-license-tool:
2528
cargo install "dd-rust-license-tool@$(CARGO_TOOL_VERSION_dd-rust-license-tool)"; \
2629
fi
2730

31+
.PHONY: cargo-install-cargo-deny
32+
cargo-install-cargo-deny:
33+
@if [ ! -x "$(CARGO_DENY)" ] || ! "$(CARGO_DENY)" --version 2>/dev/null | grep -q "$(CARGO_TOOL_VERSION_cargo-deny)"; then \
34+
echo "[*] Installing cargo-deny $(CARGO_TOOL_VERSION_cargo-deny)..."; \
35+
cargo install "cargo-deny@$(CARGO_TOOL_VERSION_cargo-deny)"; \
36+
fi
37+
2838
.PHONY: fmt
2939
fmt:
3040
@echo "[*] Checking Rust source code formatting..."
@@ -49,3 +59,8 @@ sync-licenses: cargo-install-dd-rust-license-tool
4959
check-licenses: cargo-install-dd-rust-license-tool
5060
@echo "[*] Checking if third-party license file is up to date..."
5161
@"$(DD_RUST_LICENSE_TOOL)" check
62+
63+
.PHONY: check-deny
64+
check-deny: cargo-install-cargo-deny
65+
@echo "[*] Checking dependency licenses with cargo-deny..."
66+
@"$(CARGO_DENY)" check licenses

deny.toml

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
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+
[licenses]
7+
version = 2
8+
allow = [
9+
"Apache-2.0",
10+
"Apache-2.0 WITH LLVM-exception",
11+
"BSD-3-Clause",
12+
"ISC",
13+
"MIT",
14+
"MIT-0",
15+
"Unicode-3.0",
16+
]
17+
18+
[bans]
19+
multiple-versions = "allow"
20+
21+
[sources]
22+
unknown-registry = "deny"
23+
unknown-git = "deny"

src/kx.rs

Lines changed: 45 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+
use once_cell::sync::Lazy;
67
use rustls::crypto::{ActiveKeyExchange, SharedSecret, SupportedKxGroup};
78
use rustls::{Error, NamedGroup};
89
use windows::core::Owned;
@@ -26,6 +27,13 @@ const MAX_SECRET_SIZE: usize = 48;
2627
/// * [SECP256R1]
2728
///
2829
pub const ALL_KX_GROUPS: &[&dyn SupportedKxGroup] = &[X25519, SECP256R1, SECP384R1];
30+
static DEFAULT_KX_GROUPS: Lazy<Vec<&'static dyn SupportedKxGroup>> = Lazy::new(|| {
31+
ALL_KX_GROUPS
32+
.iter()
33+
.copied()
34+
.filter(|kx_group| usable_kx_group(*kx_group))
35+
.collect()
36+
});
2937

3038
#[derive(Debug, Copy, Clone)]
3139
enum KxGroup {
@@ -67,6 +75,26 @@ impl KxGroup {
6775
}
6876
}
6977

78+
fn usable_kx_group(kx_group: &dyn SupportedKxGroup) -> bool {
79+
kx_group.name() != NamedGroup::X25519 || cng_supports_x25519()
80+
}
81+
82+
fn cng_supports_x25519() -> bool {
83+
// Windows CNG's Curve25519 public-key import behavior differs by OS version. Windows Server
84+
// 2022 accepts the X25519 Wycheproof `u = 4` vector, but Windows Server 2025 rejects it with
85+
// STATUS_INVALID_PARAMETER even when the import blob includes a valid Montgomery `v`
86+
// coordinate. That vector is a valid X25519 input, so a CNG backend that rejects it should not
87+
// advertise X25519 for TLS negotiation. Probe the same public key shape used by the provider and
88+
// leave X25519 available only on hosts whose CNG implementation can import it.
89+
let u = [
90+
0x04, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
91+
0, 0, 0,
92+
];
93+
let y = [0; 32];
94+
95+
import_ecdh_public_key(KxGroup::X25519.alg_handle(), &u, &y).is_ok()
96+
}
97+
7098
struct EcKeyExchange {
7199
kx_group: KxGroup,
72100
key_handle: Owned<BCRYPT_KEY_HANDLE>,
@@ -83,6 +111,11 @@ pub const SECP256R1: &dyn SupportedKxGroup = &KxGroup::SECP256R1;
83111
/// secp384r1 key exchange group as registered with [IANA](https://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml#tls-parameters-8)
84112
pub const SECP384R1: &dyn SupportedKxGroup = &KxGroup::SECP384R1;
85113

114+
/// Returns key exchange groups usable by the host CNG implementation.
115+
pub fn default_kx_groups() -> Vec<&'static dyn SupportedKxGroup> {
116+
DEFAULT_KX_GROUPS.clone()
117+
}
118+
86119
impl SupportedKxGroup for KxGroup {
87120
fn start(&self) -> Result<Box<dyn ActiveKeyExchange>, Error> {
88121
let mut key_handle = Owned::default();
@@ -249,6 +282,14 @@ mod test {
249282

250283
use crate::{keys::import_ecdh_private_key, kx::EcKeyExchange};
251284

285+
#[test]
286+
fn default_kx_groups_match_cng_x25519_support() {
287+
let advertises_x25519 = super::default_kx_groups()
288+
.iter()
289+
.any(|kx_group| kx_group.name() == rustls::NamedGroup::X25519);
290+
assert_eq!(advertises_x25519, super::cng_supports_x25519());
291+
}
292+
252293
#[test]
253294
fn secp256r1() {
254295
let test_set = wycheproof::ecdh::TestSet::load(TestName::EcdhSecp256r1Ecpoint).unwrap();
@@ -286,6 +327,10 @@ mod test {
286327

287328
#[test]
288329
fn x25519() {
330+
if !super::cng_supports_x25519() {
331+
return;
332+
}
333+
289334
let test_set = wycheproof::xdh::TestSet::load(wycheproof::xdh::TestName::X25519).unwrap();
290335

291336
let mut counter = 0;

src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,7 @@ pub use verify::SUPPORTED_SIG_ALGS;
130130
pub fn default_provider() -> CryptoProvider {
131131
CryptoProvider {
132132
cipher_suites: ALL_CIPHER_SUITES.to_vec(),
133-
kx_groups: ALL_KX_GROUPS.to_vec(),
133+
kx_groups: kx::default_kx_groups(),
134134
signature_verification_algorithms: SUPPORTED_SIG_ALGS,
135135
secure_random: &SecureRandom,
136136
key_provider: &KeyProvider,

src/verify.rs

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -437,13 +437,21 @@ mod tests {
437437
for test_group in test_set.test_groups {
438438
for test in test_group.tests {
439439
let res = alg.verify_signature(&test_group.key.key, &test.msg, &test.sig);
440-
let expected_failure = test.flags.contains(&TestFlag::EdgeCaseShamirMultiplication);
441440

442-
match (&test.result, expected_failure) {
443-
(TestResult::Acceptable | TestResult::Valid, false) => {
441+
if test.result == TestResult::Valid
442+
&& test.flags.contains(&TestFlag::EdgeCaseShamirMultiplication)
443+
{
444+
// Windows CNG versions differ on these valid arithmetic edge cases:
445+
// Windows Server 2022 rejects them, while Windows Server 2025 accepts them.
446+
// Invalid signatures below must still be rejected.
447+
continue;
448+
}
449+
450+
match test.result {
451+
TestResult::Acceptable | TestResult::Valid => {
444452
assert!(res.is_ok(), "Failed test: {test:?}");
445453
}
446-
_ => {
454+
TestResult::Invalid => {
447455
assert!(res.is_err(), "Failed test: {test:?}");
448456
}
449457
}

tests/it.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,13 @@ use webpki::EndEntityCert;
1919

2020
pub mod server;
2121

22+
fn cng_supports_group(group: &'static dyn SupportedKxGroup) -> bool {
23+
default_provider()
24+
.kx_groups
25+
.iter()
26+
.any(|supported| supported.name() == group.name())
27+
}
28+
2229
fn test_with_provider(
2330
provider: CryptoProvider,
2431
port: u16,
@@ -157,6 +164,10 @@ fn test_client_and_server(
157164
#[case] alg: &'static rcgen::SignatureAlgorithm,
158165
#[case] expected: CipherSuite,
159166
) {
167+
if !cng_supports_group(group) {
168+
return;
169+
}
170+
160171
// Run against a server using our default provider
161172
let (port, certificate) = start_server(alg);
162173
let provider = custom_provider(vec![suite], vec![group]);

0 commit comments

Comments
 (0)