@@ -820,6 +820,14 @@ def explode(*args, **kwargs): # type: ignore[no-untyped-def]
820820 "Getting group name is not supported by the linked OpenSSL version" ,
821821)
822822
823+ _requires_client_hello_cb = _make_requires (
824+ getattr (_lib , "Cryptography_HAS_CLIENT_HELLO_CB" , 0 ),
825+ (
826+ "SSL client hello callback is not supported by the "
827+ "linked cryptographic library"
828+ ),
829+ )
830+
823831
824832class Session :
825833 """
@@ -905,6 +913,7 @@ def __init__(self, method: int) -> None:
905913 self ._info_callback = None
906914 self ._keylog_callback = None
907915 self ._tlsext_servername_callback = None
916+ self ._client_hello_callback = None
908917 self ._app_data = None
909918 self ._alpn_select_helper : _ALPNSelectHelper | None = None
910919 self ._alpn_select_callback : _ALPNSelectCallback | None = None
@@ -1762,6 +1771,33 @@ def wrapper(ssl, alert, arg): # type: ignore[no-untyped-def]
17621771 self ._context , self ._tlsext_servername_callback
17631772 )
17641773
1774+ @_requires_client_hello_cb
1775+ @_require_not_used
1776+ def set_ssl_ctx_client_hello_callback (
1777+ self , callback : Callable [[Connection ], None ]
1778+ ) -> None :
1779+ """
1780+ Specify a callback function to be called when the ClientHello
1781+ is received.
1782+
1783+ :param callback: The callback function. It will be invoked with one
1784+ argument, the Connection instance.
1785+
1786+ .. versionadded:: 0.13
1787+ """
1788+
1789+ @wraps (callback )
1790+ def wrapper (ssl , alert , arg ): # type: ignore[no-untyped-def]
1791+ callback (Connection ._reverse_mapping [ssl ])
1792+ return 1
1793+
1794+ self ._client_hello_callback = _ffi .callback (
1795+ "int (*)(SSL *, int *, void *)" , wrapper
1796+ )
1797+ _lib .SSL_CTX_set_client_hello_cb (
1798+ self ._context , self ._client_hello_callback , _ffi .NULL
1799+ )
1800+
17651801 @_require_not_used
17661802 def set_tlsext_use_srtp (self , profiles : bytes ) -> None :
17671803 """
@@ -3262,3 +3298,50 @@ def wrapper(ssl, where, return_code): # type: ignore[no-untyped-def]
32623298 "void (*)(const SSL *, int, int)" , wrapper
32633299 )
32643300 _lib .SSL_set_info_callback (self ._ssl , self ._info_callback )
3301+
3302+ @_requires_client_hello_cb
3303+ def get_client_hello_extension (self , type : int ) -> bytes :
3304+ """
3305+ Returns the client extension with the specified type. If the extensions
3306+ cannot be found an empty byte string is returned.
3307+
3308+ :param type: The type of extension to retrieve as integer.
3309+ :return: A byte array containing the extension or an empty byte array
3310+ if the extension is absent.
3311+ """
3312+ out = _ffi .new ("const unsigned char **" )
3313+ outlen = _ffi .new ("size_t *" )
3314+ _lib .SSL_client_hello_get0_ext (self ._ssl , type , out , outlen )
3315+
3316+ if not outlen :
3317+ return b""
3318+
3319+ return _ffi .buffer (out [0 ], outlen [0 ])[:]
3320+
3321+ @_requires_client_hello_cb
3322+ def get_client_hello_extensions_present (self ) -> list [int ]:
3323+ """
3324+ Returns a list of the types of the client hello extensions
3325+ that are present in the ClientHello message.
3326+ """
3327+ # SSL_client_hello_get1_extensions_present returns a new array
3328+ # allocated by OpenSSL_malloc
3329+ data = _ffi .new ("int **" )
3330+ data_len = _ffi .new ("size_t *" )
3331+ rc = _lib .SSL_client_hello_get1_extensions_present (
3332+ self ._ssl , data , data_len
3333+ )
3334+
3335+ _openssl_assert (rc == 1 )
3336+
3337+ if not data_len :
3338+ return []
3339+
3340+ # OpenSSL returns the number of items and FFI wants the numbers of
3341+ # types, so multiply it by the size of each item (int)
3342+ data_gc = _ffi .gc (data [0 ], _lib .OPENSSL_free )
3343+
3344+ buf = _ffi .buffer (data_gc , data_len [0 ] * _ffi .sizeof ("int" ))
3345+ retarray = _ffi .from_buffer ("int[]" , buf )
3346+
3347+ return list (retarray )
0 commit comments