33// This product includes software developed at Datadog (https://www.datadoghq.com/)
44// Copyright 2026 Datadog, Inc.
55
6+ use once_cell:: sync:: Lazy ;
67use rustls:: crypto:: { ActiveKeyExchange , SharedSecret , SupportedKxGroup } ;
78use rustls:: { Error , NamedGroup } ;
89use windows:: core:: Owned ;
@@ -26,6 +27,13 @@ const MAX_SECRET_SIZE: usize = 48;
2627/// * [SECP256R1]
2728///
2829pub 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 ) ]
3139enum KxGroup {
@@ -67,6 +75,20 @@ 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+ let u = [
84+ 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 ,
85+ 0 , 0 , 0 ,
86+ ] ;
87+ let y = [ 0 ; 32 ] ;
88+
89+ import_ecdh_public_key ( KxGroup :: X25519 . alg_handle ( ) , & u, & y) . is_ok ( )
90+ }
91+
7092struct EcKeyExchange {
7193 kx_group : KxGroup ,
7294 key_handle : Owned < BCRYPT_KEY_HANDLE > ,
@@ -83,6 +105,11 @@ pub const SECP256R1: &dyn SupportedKxGroup = &KxGroup::SECP256R1;
83105/// secp384r1 key exchange group as registered with [IANA](https://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml#tls-parameters-8)
84106pub const SECP384R1 : & dyn SupportedKxGroup = & KxGroup :: SECP384R1 ;
85107
108+ /// Returns key exchange groups usable by the host CNG implementation.
109+ pub fn default_kx_groups ( ) -> Vec < & ' static dyn SupportedKxGroup > {
110+ DEFAULT_KX_GROUPS . clone ( )
111+ }
112+
86113impl SupportedKxGroup for KxGroup {
87114 fn start ( & self ) -> Result < Box < dyn ActiveKeyExchange > , Error > {
88115 let mut key_handle = Owned :: default ( ) ;
@@ -247,10 +274,15 @@ mod test {
247274 use windows:: core:: Owned ;
248275 use wycheproof:: { ecdh:: TestName , TestResult } ;
249276
250- use crate :: {
251- keys:: { import_ecdh_private_key, import_ecdh_public_key} ,
252- kx:: EcKeyExchange ,
253- } ;
277+ use crate :: { keys:: import_ecdh_private_key, kx:: EcKeyExchange } ;
278+
279+ #[ test]
280+ fn default_kx_groups_match_cng_x25519_support ( ) {
281+ let advertises_x25519 = super :: default_kx_groups ( )
282+ . iter ( )
283+ . any ( |kx_group| kx_group. name ( ) == rustls:: NamedGroup :: X25519 ) ;
284+ assert_eq ! ( advertises_x25519, super :: cng_supports_x25519( ) ) ;
285+ }
254286
255287 #[ test]
256288 fn secp256r1 ( ) {
@@ -288,53 +320,11 @@ mod test {
288320 }
289321
290322 #[ test]
291- fn x25519_cng_import_diagnostics ( ) {
292- let cases = [
293- (
294- "tc1 normal" ,
295- "504a36999f489cd2fdbc08baff3d88fa00569ba986cba22548ffde80f9806829" ,
296- Some ( "98c969d09aeecfe56f44da8c143e7c739afc6d3ac26cd099a73436fec147a908" ) ,
297- Some ( "08a947c1fe3634a799d06cc23a6dfc9a737c3e148cda446fe5cfee9ad069c998" ) ,
298- ) ,
299- (
300- "tc2 twist" ,
301- "63aa40c6e38346c5caf23a6df0a5e6c80889a08647e551b3563449befcfc9733" ,
302- None ,
303- None ,
304- ) ,
305- (
306- "tc34 special-valid-u4" ,
307- "0400000000000000000000000000000000000000000000000000000000000000" ,
308- Some ( "a0d42a061659386f4553ce7564094b15d73bc8a36340191b74c1e56f8a050469" ) ,
309- Some ( "6904058a6fe5c1741b194063a3c83bd7154b096475ce53456f385916062ad4a0" ) ,
310- ) ,
311- ] ;
312- let zero_y = [ 0u8 ; 32 ] ;
313-
314- for ( name, x_hex, y_le_hex, y_be_hex) in cases {
315- let x = hex:: decode ( x_hex) . unwrap ( ) ;
316- println ! ( "x25519 import diagnostic: {name}" ) ;
317-
318- let zero_res =
319- import_ecdh_public_key ( crate :: kx:: KxGroup :: X25519 . alg_handle ( ) , & x, & zero_y) ;
320- println ! ( " y=zero: {:?}" , zero_res. as_ref( ) . map( |_| ( ) ) ) ;
321-
322- if let Some ( y_hex) = y_le_hex {
323- let y = hex:: decode ( y_hex) . unwrap ( ) ;
324- let res = import_ecdh_public_key ( crate :: kx:: KxGroup :: X25519 . alg_handle ( ) , & x, & y) ;
325- println ! ( " y=sqrt-le: {:?}" , res. as_ref( ) . map( |_| ( ) ) ) ;
326- }
327-
328- if let Some ( y_hex) = y_be_hex {
329- let y = hex:: decode ( y_hex) . unwrap ( ) ;
330- let res = import_ecdh_public_key ( crate :: kx:: KxGroup :: X25519 . alg_handle ( ) , & x, & y) ;
331- println ! ( " y=sqrt-be: {:?}" , res. as_ref( ) . map( |_| ( ) ) ) ;
332- }
323+ fn x25519 ( ) {
324+ if !super :: cng_supports_x25519 ( ) {
325+ return ;
333326 }
334- }
335327
336- #[ test]
337- fn x25519 ( ) {
338328 let test_set = wycheproof:: xdh:: TestSet :: load ( wycheproof:: xdh:: TestName :: X25519 ) . unwrap ( ) ;
339329
340330 let mut counter = 0 ;
0 commit comments