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