@@ -87,6 +87,8 @@ use crate::pkey::{HasPrivate, PKeyRef, Params, Private};
8787use crate :: srtp:: { SrtpProtectionProfile , SrtpProtectionProfileRef } ;
8888use crate :: ssl:: bio:: BioMethod ;
8989use crate :: ssl:: callbacks:: * ;
90+ #[ cfg( not( feature = "fips" ) ) ]
91+ use crate :: ssl:: ech:: SslEchKeys ;
9092use crate :: ssl:: error:: InnerError ;
9193use crate :: stack:: { Stack , StackRef , Stackable } ;
9294use crate :: x509:: store:: { X509Store , X509StoreBuilderRef , X509StoreRef } ;
@@ -110,6 +112,8 @@ mod async_callbacks;
110112mod bio;
111113mod callbacks;
112114mod connector;
115+ #[ cfg( not( feature = "fips" ) ) ]
116+ mod ech;
113117mod error;
114118mod mut_only;
115119#[ cfg( test) ]
@@ -1956,6 +1960,16 @@ impl SslContextBuilder {
19561960 }
19571961 }
19581962
1963+ /// Registers a list of ECH keys on the context. This list should contain new and old
1964+ /// ECHConfigs to allow stale DNS caches to update. Unlike most `SSL_CTX` APIs, this function
1965+ /// is safe to call even after the `SSL_CTX` has been associated with connections on various
1966+ /// threads.
1967+ #[ cfg( not( feature = "fips" ) ) ]
1968+ #[ corresponds( SSL_CTX_set1_ech_keys ) ]
1969+ pub fn set_ech_keys ( & mut self , keys : SslEchKeys ) -> Result < ( ) , ErrorStack > {
1970+ unsafe { cvt ( ffi:: SSL_CTX_set1_ech_keys ( self . as_ptr ( ) , keys. as_ptr ( ) ) ) . map ( |_| ( ) ) }
1971+ }
1972+
19591973 /// Consumes the builder, returning a new `SslContext`.
19601974 pub fn build ( self ) -> SslContext {
19611975 self . ctx
@@ -3623,6 +3637,77 @@ impl SslRef {
36233637 pub fn add_chain_cert ( & mut self , cert : & X509Ref ) -> Result < ( ) , ErrorStack > {
36243638 unsafe { cvt ( ffi:: SSL_add1_chain_cert ( self . as_ptr ( ) , cert. as_ptr ( ) ) ) . map ( |_| ( ) ) }
36253639 }
3640+
3641+ /// Configures `ech_config_list` on `SSL` for offering ECH during handshakes. If the server
3642+ /// cannot decrypt the encrypted ClientHello, `SSL` will instead handshake using
3643+ /// the cleartext parameters of the ClientHelloOuter.
3644+ ///
3645+ /// Clients should use `get_ech_name_override` to verify the server certificate in case of ECH
3646+ /// rejection, and follow up with `get_ech_retry_configs` to retry the connection with a fresh
3647+ /// set of ECHConfigs. If the retry also fails, clients should report a connection failure.
3648+ #[ cfg( not( feature = "fips" ) ) ]
3649+ #[ corresponds( SSL_set1_ech_config_list ) ]
3650+ pub fn set_ech_config_list ( & mut self , ech_config_list : & [ u8 ] ) -> Result < ( ) , ErrorStack > {
3651+ unsafe {
3652+ cvt_0i ( ffi:: SSL_set1_ech_config_list (
3653+ self . as_ptr ( ) ,
3654+ ech_config_list. as_ptr ( ) ,
3655+ ech_config_list. len ( ) ,
3656+ ) )
3657+ . map ( |_| ( ) )
3658+ }
3659+ }
3660+
3661+ /// This function returns a serialized `ECHConfigList` as provided by the
3662+ /// server, if one exists.
3663+ ///
3664+ /// Clients should call this function when handling an `SSL_R_ECH_REJECTED` error code to
3665+ /// recover from potential key mismatches. If the result is `Some`, the client should retry the
3666+ /// connection using the returned `ECHConfigList`.
3667+ #[ cfg( not( feature = "fips" ) ) ]
3668+ #[ corresponds( SSL_get0_ech_retry_configs ) ]
3669+ pub fn get_ech_retry_configs ( & self ) -> Option < & [ u8 ] > {
3670+ unsafe {
3671+ let mut data = ptr:: null ( ) ;
3672+ let mut len: usize = 0 ;
3673+ ffi:: SSL_get0_ech_retry_configs ( self . as_ptr ( ) , & mut data, & mut len) ;
3674+
3675+ if data. is_null ( ) {
3676+ None
3677+ } else {
3678+ Some ( slice:: from_raw_parts ( data, len) )
3679+ }
3680+ }
3681+ }
3682+
3683+ /// If `SSL` is a client and the server rejects ECH, this function returns the public name
3684+ /// associated with the ECHConfig that was used to attempt ECH.
3685+ ///
3686+ /// Clients should call this function during the certificate verification callback to
3687+ /// ensure the server's certificate is valid for the public name, which is required to
3688+ /// authenticate retry configs.
3689+ #[ cfg( not( feature = "fips" ) ) ]
3690+ #[ corresponds( SSL_get0_ech_name_override ) ]
3691+ pub fn get_ech_name_override ( & self ) -> Option < & [ u8 ] > {
3692+ unsafe {
3693+ let mut data: * const c_char = ptr:: null ( ) ;
3694+ let mut len: usize = 0 ;
3695+ ffi:: SSL_get0_ech_name_override ( self . as_ptr ( ) , & mut data, & mut len) ;
3696+
3697+ if data. is_null ( ) {
3698+ None
3699+ } else {
3700+ Some ( slice:: from_raw_parts ( data as * const u8 , len) )
3701+ }
3702+ }
3703+ }
3704+
3705+ // Whether or not `SSL` negotiated ECH.
3706+ #[ cfg( not( feature = "fips" ) ) ]
3707+ #[ corresponds( SSL_ech_accepted ) ]
3708+ pub fn ech_accepted ( & self ) -> bool {
3709+ unsafe { ffi:: SSL_ech_accepted ( self . as_ptr ( ) ) != 0 }
3710+ }
36263711}
36273712
36283713/// An SSL stream midway through the handshake process.
0 commit comments