@@ -15,21 +15,20 @@ use crate::socket::{SelectKind, timeout_error_msg};
1515use crate :: vm:: VirtualMachine ;
1616use alloc:: sync:: Arc ;
1717use parking_lot:: RwLock as ParkingRwLock ;
18- use rustls:: RootCertStore ;
19- use rustls:: client:: ClientConfig ;
20- use rustls:: client:: ClientConnection ;
21- use rustls:: crypto:: SupportedKxGroup ;
18+ use rustls:: client:: { ClientConfig , ClientConnection } ;
19+ use rustls:: crypto:: { CryptoProvider , SupportedKxGroup } ;
2220use rustls:: pki_types:: { CertificateDer , CertificateRevocationListDer , PrivateKeyDer } ;
23- use rustls:: server:: ResolvesServerCert ;
24- use rustls:: server:: ServerConfig ;
25- use rustls:: server:: ServerConnection ;
26- use rustls:: sign:: CertifiedKey ;
21+ use rustls:: server:: {
22+ ProducesTickets , ResolvesServerCert , ServerConfig , ServerConnection , WebPkiClientVerifier ,
23+ } ;
24+ use rustls:: sign:: { CertifiedKey , SigningKey } ;
25+ use rustls:: { RootCertStore , SignatureScheme , SupportedCipherSuite } ;
2726use rustpython_vm:: builtins:: { PyBaseException , PyBaseExceptionRef } ;
2827use rustpython_vm:: convert:: IntoPyException ;
2928use rustpython_vm:: function:: ArgBytesLike ;
3029use rustpython_vm:: { AsObject , Py , PyObjectRef , PyPayload , PyResult , TryFromObject } ;
3130use std:: io:: Read ;
32- use std:: sync:: Once ;
31+ use std:: sync:: OnceLock ;
3332
3433// Import PySSLSocket from parent module
3534use super :: _ssl:: PySSLSocket ;
@@ -57,14 +56,114 @@ pub(super) const VERIFY_X509_PARTIAL_CHAIN: i32 = 0x80000;
5756/// This is necessary because rustls 0.23+ requires a process-level CryptoProvider
5857/// to be installed before using default_provider(). We use Once to ensure this
5958/// happens exactly once, even if called from multiple threads.
60- static INIT_PROVIDER : Once = Once :: new ( ) ;
61-
62- pub ( super ) fn ensure_default_provider ( ) {
63- INIT_PROVIDER . call_once ( || {
64- let _ = rustls:: crypto:: CryptoProvider :: install_default (
65- rustls:: crypto:: aws_lc_rs:: default_provider ( ) ,
66- ) ;
67- } ) ;
59+ pub ( super ) static CRYPTO_EXT : OnceLock < CryptoExt > = OnceLock :: new ( ) ;
60+
61+ #[ derive( Clone , Copy ) ]
62+ pub struct CryptoExt {
63+ pub all_cipher_suites : Option < & ' static [ SupportedCipherSuite ] > ,
64+ pub all_kx_groups : Option < & ' static [ & ' static dyn SupportedKxGroup ] > ,
65+ pub any_supported_key :
66+ Option < fn ( & PrivateKeyDer < ' _ > ) -> Result < Arc < dyn SigningKey > , rustls:: Error > > ,
67+ pub ticketer : fn ( ) -> Result < Arc < dyn ProducesTickets > , rustls:: Error > ,
68+ }
69+
70+ impl CryptoExt {
71+ /// Returns all [`SupportedCipherSuite`] or the provider's defaults.
72+ ///
73+ /// # Panics
74+ /// Panics if a [`CryptoProvider`] hasn't been set.
75+ pub fn all_ciphers_or_default ( & self ) -> & ' static [ SupportedCipherSuite ] {
76+ self . all_cipher_suites . unwrap_or_else ( || {
77+ CryptoProvider :: get_default ( )
78+ . expect ( "A CryptoProvider has been set if CryptoExt is set" )
79+ . cipher_suites
80+ . as_slice ( )
81+ } )
82+ }
83+
84+ /// Returns all [`SupportedKxGroup`] or the provider's defaults.
85+ ///
86+ /// # Panics
87+ /// Panics if a [`CryptoProvider`] hasn't been set.
88+ pub fn all_kx_or_default ( & self ) -> & ' static [ & ' static dyn SupportedKxGroup ] {
89+ self . all_kx_groups . unwrap_or_else ( || {
90+ CryptoProvider :: get_default ( )
91+ . expect ( "A CryptoProvider has been set if CryptoExt is set" )
92+ . kx_groups
93+ . as_slice ( )
94+ } )
95+ }
96+
97+ /// Return the first supported [`SigningKey`] for a [`PrivateKeyDer`].
98+ ///
99+ /// Ideally, this function should be provided by the backend implementation or
100+ /// the user. This fallback filters out insecure algorithms then picks the first available key
101+ /// if it exists.
102+ ///
103+ /// # Panics
104+ /// Panics if a [`CryptoProvider`] hasn't been set.
105+ pub fn any_supported_key (
106+ & self ,
107+ der : & PrivateKeyDer < ' _ > ,
108+ ) -> Result < Arc < dyn SigningKey > , rustls:: Error > {
109+ self . any_supported_key . map ( |f| f ( der) ) . unwrap_or_else ( || {
110+ let provider = CryptoProvider :: get_default ( )
111+ . expect ( "A CryptoProvider has been set if CryptoExt is set" ) ;
112+ let key = provider. key_provider . load_private_key ( der. clone_key ( ) ) ?;
113+
114+ for scheme in provider
115+ . signature_verification_algorithms
116+ . mapping
117+ . iter ( )
118+ . filter_map ( |( scheme, _) | {
119+ ( !matches ! (
120+ scheme,
121+ SignatureScheme :: RSA_PKCS1_SHA1
122+ | SignatureScheme :: ECDSA_SHA1_Legacy
123+ | SignatureScheme :: Unknown ( _) ,
124+ ) )
125+ . then_some ( * scheme)
126+ } )
127+ {
128+ if key. choose_scheme ( & [ scheme] ) . is_some ( ) {
129+ return Ok ( key) ;
130+ }
131+ }
132+
133+ Err ( rustls:: Error :: General (
134+ "failed to parse private key as RSA, ECDSA, or EcDSA" . into ( ) ,
135+ ) )
136+ } )
137+ }
138+ }
139+
140+ #[ cfg( all( feature = "ssl-rustls" , not( feature = "ssl-rustls-no-provider" ) ) ) ]
141+ pub fn set_aws_lc_provider ( ) -> Result < ( ) , rustls:: Error > {
142+ use rustls:: crypto:: aws_lc_rs;
143+ let provider = aws_lc_rs:: default_provider ( ) ;
144+ let ext = CryptoExt {
145+ all_cipher_suites : Some ( aws_lc_rs:: ALL_CIPHER_SUITES ) ,
146+ all_kx_groups : Some ( aws_lc_rs:: ALL_KX_GROUPS ) ,
147+ any_supported_key : Some ( aws_lc_rs:: sign:: any_supported_type) ,
148+ ticketer : aws_lc_rs:: Ticketer :: new,
149+ } ;
150+ set_crypto_provider ( provider, ext)
151+ }
152+
153+ /// Set a process [`CryptoProvider`].
154+ ///
155+ /// A provider must be set before any cryptographic operations. All crypto ops panic if a provider
156+ /// is unset. See [`set_aws_lc_provider`] for the default provider.
157+ pub fn set_crypto_provider (
158+ provider : CryptoProvider ,
159+ extension : CryptoExt ,
160+ ) -> Result < ( ) , rustls:: Error > {
161+ provider
162+ . install_default ( )
163+ . map_err ( |_| rustls:: Error :: General ( "A default CryptoProvider is already set" . into ( ) ) ) ?;
164+ CRYPTO_EXT
165+ . set ( extension)
166+ . map_err ( |_| rustls:: Error :: General ( "A CryptoExt is already set" . into ( ) ) )
68167}
69168
70169// OpenSSL Constants:
@@ -662,13 +761,13 @@ pub(super) struct ClientConfigOptions {
662761/// This helper function consolidates the duplicated CryptoProvider creation logic
663762/// for both server and client configurations.
664763fn create_custom_crypto_provider (
665- cipher_suites : Option < Vec < rustls :: SupportedCipherSuite > > ,
666- kx_groups : Option < Vec < & ' static dyn rustls :: crypto :: SupportedKxGroup > > ,
667- ) -> Arc < rustls :: crypto :: CryptoProvider > {
668- let default_provider = rustls :: crypto :: CryptoProvider :: get_default ( )
669- . expect ( "A CryptoProvider should have been set earlier " ) ;
764+ cipher_suites : Option < Vec < SupportedCipherSuite > > ,
765+ kx_groups : Option < Vec < & ' static dyn SupportedKxGroup > > ,
766+ ) -> Arc < CryptoProvider > {
767+ let default_provider =
768+ CryptoProvider :: get_default ( ) . expect ( "A CryptoProvider must be set before SSL " ) ;
670769
671- Arc :: new ( rustls :: crypto :: CryptoProvider {
770+ Arc :: new ( CryptoProvider {
672771 cipher_suites : cipher_suites. unwrap_or_else ( || default_provider. cipher_suites . clone ( ) ) ,
673772 kx_groups : kx_groups. unwrap_or_else ( || default_provider. kx_groups . clone ( ) ) ,
674773 signature_verification_algorithms : default_provider. signature_verification_algorithms ,
@@ -682,11 +781,6 @@ fn create_custom_crypto_provider(
682781/// This abstracts the complex rustls ServerConfig building logic,
683782/// matching SSL_CTX initialization for server sockets.
684783pub ( super ) fn create_server_config ( options : ServerConfigOptions ) -> Result < ServerConfig , String > {
685- use rustls:: server:: WebPkiClientVerifier ;
686-
687- // Ensure default CryptoProvider is installed
688- ensure_default_provider ( ) ;
689-
690784 // Create custom crypto provider using helper function
691785 let custom_provider = create_custom_crypto_provider (
692786 options. protocol_settings . cipher_suites . clone ( ) ,
@@ -864,9 +958,6 @@ fn apply_alpn_with_fallback(config_alpn: &mut Vec<Vec<u8>>, alpn_protocols: &[Ve
864958/// This abstracts the complex rustls ClientConfig building logic,
865959/// matching SSL_CTX initialization for client sockets.
866960pub ( super ) fn create_client_config ( options : ClientConfigOptions ) -> Result < ClientConfig , String > {
867- // Ensure default CryptoProvider is installed
868- ensure_default_provider ( ) ;
869-
870961 // Create custom crypto provider using helper function
871962 let custom_provider = create_custom_crypto_provider (
872963 options. protocol_settings . cipher_suites . clone ( ) ,
@@ -2365,9 +2456,10 @@ pub(super) fn curve_name_to_kx_group(
23652456 curve : & str ,
23662457) -> Result < Vec < & ' static dyn SupportedKxGroup > , String > {
23672458 // Get the default crypto provider's key exchange groups
2368- let provider = rustls:: crypto:: CryptoProvider :: get_default ( )
2369- . expect ( "A CryptoProvider should have been set earlier" ) ;
2370- let all_groups = & provider. kx_groups ;
2459+ let crypto_ext = CRYPTO_EXT
2460+ . get ( )
2461+ . expect ( "A CryptoProvider must be set before SSL" ) ;
2462+ let all_groups = crypto_ext. all_kx_or_default ( ) ;
23712463
23722464 match curve {
23732465 // P-256 (also known as secp256r1 or prime256v1)
0 commit comments