2525# pylint: disable=too-many-public-methods, too-many-statements
2626
2727import sys
28+ from functools import wraps
2829import errno
2930from socket import (
3031 socket , AF_INET , SOCK_STREAM , SOL_SOCKET , SO_TYPE , error as socket_error
@@ -130,7 +131,6 @@ def get_der(self):
130131
131132 return derBytes
132133
133-
134134class SSLContext (object ):
135135 """
136136 An SSLContext holds various SSL-related configuration options and
@@ -144,6 +144,8 @@ def __init__(self, protocol, server_side=None):
144144 self ._server_side = server_side
145145 self ._verify_mode = None
146146 self ._check_hostname = False
147+ self ._passwd_cb = None
148+ self ._passwd_userdata = None
147149 self .native_object = _lib .wolfSSL_CTX_new (method .native_object )
148150
149151 # wolfSSL_CTX_new() takes ownership of the method.
@@ -269,7 +271,6 @@ def use_sni(self, server_hostname):
269271 if ret != _SSL_SUCCESS :
270272 raise SSLError ("Unable to set wolfSSL CTX SNI" )
271273
272-
273274 def load_cert_chain (self , certfile , keyfile = None , password = None ):
274275 """
275276 Load a private key and the corresponding certificate. The certfile
@@ -280,32 +281,30 @@ def load_cert_chain(self, certfile, keyfile=None, password=None):
280281 The keyfile string, if present, must point to a file containing the
281282 private key in.
282283
283- The password parameter is not supported yet.
284+ If you are using a key protected cert or key file, you must call
285+ set_passwd_cb before calling load_cert_chain because wolfSSL
286+ validates the provided file the first time it is loaded.
287+
284288
285289 wolfSSL does not support loading a certificate file that contains
286290 both the certificate AND private key. In this case, users should
287291 split them into two separate files and load using the certfile
288292 and keyfile parameters, respectively.
289293 """
290-
291- if password is not None :
292- raise NotImplementedError ("password callback support not "
293- "implemented yet" )
294-
295294 if certfile is not None :
296295 ret = _lib .wolfSSL_CTX_use_certificate_chain_file (
297296 self .native_object , t2b (certfile ))
298297 if ret != _SSL_SUCCESS :
299298 raise SSLError (
300- "Unnable to load certificate chain. E(%d)" % ret )
299+ "Unable to load certificate chain. E(%d)" % ret )
301300 else :
302301 raise TypeError ("certfile should be a valid filesystem path" )
303302
304303 if keyfile is not None :
305304 ret = _lib .wolfSSL_CTX_use_PrivateKey_file (
306305 self .native_object , t2b (keyfile ), _SSL_FILETYPE_PEM )
307306 if ret != _SSL_SUCCESS :
308- raise SSLError ("Unnable to load private key. E(%d)" % ret )
307+ raise SSLError ("Unable to load private key. E(%d)" % ret )
309308
310309 def load_verify_locations (self , cafile = None , capath = None , cadata = None ):
311310 """
@@ -330,16 +329,39 @@ def load_verify_locations(self, cafile=None, capath=None, cadata=None):
330329 t2b (capath ) if capath else _ffi .NULL )
331330
332331 if ret != _SSL_SUCCESS :
333- raise SSLError ("Unnable to load verify locations. E(%d)" % ret )
332+ raise SSLError ("Unable to load verify locations. E(%d)" % ret )
334333
335334 if cadata is not None :
336335 ret = _lib .wolfSSL_CTX_load_verify_buffer (
337336 self .native_object , t2b (cadata ),
338337 len (cadata ), _SSL_FILETYPE_PEM )
339338
340339 if ret != _SSL_SUCCESS :
341- raise SSLError ("Unnable to load verify locations. E(%d)" % ret )
340+ raise SSLError ("Unable to load verify locations. E(%d)" % ret )
341+
342+ def set_passwd_cb (self , callback , userdata = None ):
343+ """
344+ This function can be called before loading a private key with a password.
345+ For example,
346+ password = "funPassphrase"
347+ self._ctx.set_passwd_cb(lambda *_: password)
348+ ...
349+ self._ctx.load_cert_chain()
350+ """
351+ if not callable (callback ):
352+ raise TypeError ("The specified callback must be callable" )
342353
354+ _passwd_helper = self ._wrap_cb (callback )
355+ self ._passwd_cb = _passwd_helper .callback
356+ _lib .wolfSSL_CTX_set_default_passwd_cb (self .native_object ,
357+ self ._passwd_cb )
358+ self ._passwd_userdata = userdata # keep it alive
359+
360+ def _wrap_cb (self , callback ):
361+ @wraps (callback )
362+ def wrapper (sz , rw , userdata ):
363+ return callback (sz , rw , self ._passwd_userdata )
364+ return WolfsslPwd_cb (wrapper )
343365
344366class SSLSocket (object ):
345367 """
@@ -826,12 +848,12 @@ def wrap_socket(sock, keyfile=None, certfile=None, server_side=False,
826848 ciphers = None ):
827849 """
828850 Takes an instance sock of socket.socket, and returns an instance of
829- wolfssl.SSLSocket, wraping the underlying socket in an SSL context.
851+ wolfssl.SSLSocket, wrapping the underlying socket in an SSL context.
830852
831853 The sock parameter must be a SOCK_STREAM socket; other socket types are
832854 unsupported.
833855
834- The keyfile and certfile parameters specify optional files whith proper
856+ The keyfile and certfile parameters specify optional files with proper
835857 key and the certificates used to identify the local side of the connection.
836858
837859 The parameter server_side is a boolean which identifies whether server-side
@@ -902,3 +924,29 @@ def wrap_socket(sock, keyfile=None, certfile=None, server_side=False,
902924 do_handshake_on_connect = do_handshake_on_connect ,
903925 suppress_ragged_eofs = suppress_ragged_eofs ,
904926 ciphers = ciphers )
927+
928+ class WolfsslPwd_cb (object ):
929+ def __init__ (self , password ):
930+ self ._passwd_wrapper = password
931+
932+ @property
933+ def callback (self ):
934+ if self ._passwd_wrapper is None or isinstance (self ._passwd_wrapper , bytes ):
935+ return _ffi .NULL
936+ elif callable (self ._passwd_wrapper ):
937+ return _ffi .callback ("pem_password_cb" , self ._get_passwd )
938+ else :
939+ raise TypeError ("Not callable or missing arguments" )
940+
941+ def _get_passwd (self , passwd , sz , rw , userdata ):
942+ try :
943+ result = self ._passwd_wrapper (sz , rw , userdata )
944+ if not isinstance (result , bytes ):
945+ raise ValueError ("Problem, expected String, not bytes" )
946+ if len (result ) > sz :
947+ raise ValueError ("Problem with password returned being long" )
948+ for i in range (len (result )):
949+ passwd [i ] = result [i :i + 1 ]
950+ return len (result )
951+ except Exception as e :
952+ raise ValueError ("Problem getting password from callback" )
0 commit comments