2 This module provides interface for low-level private/public keypair operation
4 PKey object of this module is wrapper around OpenSSL EVP_PKEY object.
8 from ctypes import c_char, c_char_p, c_void_p, c_int, c_long, POINTER
9 from ctypes import create_string_buffer, byref, memmove, CFUNCTYPE
10 from ctypescrypto import libcrypto,pyver,bintype,chartype
11 from ctypescrypto.exception import LibCryptoError, clear_err_stack
12 from ctypescrypto.bio import Membio
14 __all__ = ['PKeyError', 'PKey', 'PW_CALLBACK_FUNC']
15 class PKeyError(LibCryptoError):
16 """ Exception thrown if libcrypto finctions return an error """
19 PW_CALLBACK_FUNC = CFUNCTYPE(c_int, POINTER(c_char), c_int, c_int, c_char_p)
20 """ Function type for pem password callback """
22 def _password_callback(c):
24 Converts given user function or string to C password callback
25 function, passable to openssl.
27 IF function is passed, it would be called upon reading or writing
28 PEM format private key with one argument which is True if we are
29 writing key and should verify passphrase and false if we are reading
33 return PW_CALLBACK_FUNC(0)
36 def __cb(buf, length, rwflag, userdata):
38 cnt = min(len(pwd),length)
42 def __cb(buf, length, rwflag, userdata):
43 pwd = c(rwflag).encode("utf-8")
44 cnt = min(len(pwd),length)
50 def __cb(buf,length,rwflag,userdata):
51 cnt=min(len(c),length)
54 return PW_CALLBACK_FUNC(__cb)
56 def _keybio(blob, format):
57 # But DER string should be binary
58 if format == "PEM" and isinstance(blob,chartype):
59 return Membio(blob.encode("ascii"),clone=True)
60 elif isinstance(blob,bintype):
63 raise TypeError("Key should be either blob or PEM string")
67 Represents public/private key pair. Wrapper around EVP_PKEY
70 May contain either both private and public key (such objects can be
71 used for signing, deriving shared key as well as verifying or public
72 key only, which can be used for verifying or as peer key when
75 @var cansign is true key has private part.
76 @var key contain pointer to EVP_PKEY and should be passed to various
79 def __init__(self, ptr=None, privkey=None, pubkey=None, format="PEM",
80 cansign=False, password=None):
82 PKey object can be created from either private/public key blob or
83 from C language pointer, returned by some OpenSSL function
85 Following named arguments are recognized by constructor
87 privkey - private key blob. If this is specified, format and
88 password can be also specified
90 pubkey - public key blob. If this is specified, format can be
93 ptr - pointer, returned by openssl function. If it is specified,
94 cansign should be also specified.
96 These three arguments are mutually exclusive.
98 format - can be either 'PEM' or 'DER'. Specifies format of blob.
100 password - can be string with password for encrypted key, or
101 callable with one boolean argument, which returns password.
102 During constructor call this argument would be false.
104 If key is in PEM format, its encrypted status and format is
105 autodetected. If key is in DER format, than if password is
106 specified, key is assumed to be encrypted PKCS8 key otherwise
107 it is assumed to be unencrypted.
112 self.cansign = cansign
113 if not privkey is None or not pubkey is None:
114 raise TypeError("Just one of ptr, pubkey or privkey can " +
116 elif not privkey is None:
117 if not pubkey is None:
118 raise TypeError("Just one of ptr, pubkey or privkey can " +
120 bio=_keybio(privkey,format)
123 self.key = libcrypto.PEM_read_bio_PrivateKey(bio.bio, None,
124 _password_callback(password),
127 if password is not None:
128 self.key = libcrypto.d2i_PKCS8PrivateKey_bio(bio.bio,None,
129 _password_callback(password),
132 self.key = libcrypto.d2i_PrivateKey_bio(bio.bio, None)
134 raise PKeyError("error parsing private key")
135 elif not pubkey is None:
136 bio = _keybio(pubkey,format)
139 self.key = libcrypto.PEM_read_bio_PUBKEY(bio.bio, None,
140 _password_callback(password),
143 self.key = libcrypto.d2i_PUBKEY_bio(bio.bio, None)
145 raise PKeyError("error parsing public key")
147 raise TypeError("Neither public, nor private key is specified")
151 """ Frees EVP_PKEY object (note, it is reference counted) """
152 if hasattr(self,"key"):
153 libcrypto.EVP_PKEY_free(self.key)
155 def __eq__(self, other):
156 """ Compares two public keys. If one has private key and other
157 doesn't it doesn't affect result of comparation
159 return libcrypto.EVP_PKEY_cmp(self.key, other.key) == 1
161 def __ne__(self, other):
162 """ Compares two public key for not-equality """
163 return not self.__eq__(other)
166 """ printable representation of public key """
168 libcrypto.EVP_PKEY_print_public(bio.bio, self.key, 0, None)
171 def sign(self, digest, **kwargs):
173 Signs given digest and retirns signature
174 Keyword arguments allows to set various algorithm-specific
175 parameters. See pkeyutl(1) manual.
177 ctx = libcrypto.EVP_PKEY_CTX_new(self.key, None)
179 raise PKeyError("Initailizing sign context")
180 if libcrypto.EVP_PKEY_sign_init(ctx) < 1:
181 raise PKeyError("sign_init")
182 self._configure_context(ctx, kwargs)
183 # Find out signature size
185 if libcrypto.EVP_PKEY_sign(ctx, None, byref(siglen), digest,
187 raise PKeyError("computing signature length")
188 sig = create_string_buffer(siglen.value)
189 if libcrypto.EVP_PKEY_sign(ctx, sig, byref(siglen), digest,
191 raise PKeyError("signing")
192 libcrypto.EVP_PKEY_CTX_free(ctx)
193 return sig.raw[:int(siglen.value)]
195 def verify(self, digest, signature, **kwargs):
197 Verifies given signature on given digest
198 Returns True if Ok, False if don't match
199 Keyword arguments allows to set algorithm-specific
202 ctx = libcrypto.EVP_PKEY_CTX_new(self.key, None)
204 raise PKeyError("Initailizing verify context")
205 if libcrypto.EVP_PKEY_verify_init(ctx) < 1:
206 raise PKeyError("verify_init")
207 self._configure_context(ctx, kwargs)
208 ret = libcrypto.EVP_PKEY_verify(ctx, signature, len(signature), digest,
211 raise PKeyError("Signature verification")
212 libcrypto.EVP_PKEY_CTX_free(ctx)
215 def derive(self, peerkey, **kwargs):
217 Derives shared key (DH,ECDH,VKO 34.10). Requires
218 private key available
220 @param peerkey - other key (may be public only)
222 Keyword parameters are algorithm-specific
225 raise ValueError("No private key available")
226 ctx = libcrypto.EVP_PKEY_CTX_new(self.key, None)
228 raise PKeyError("Initailizing derive context")
229 if libcrypto.EVP_PKEY_derive_init(ctx) < 1:
230 raise PKeyError("derive_init")
232 # This is workaround around missing functionality in GOST engine
233 # it provides only numeric control command to set UKM, not
235 self._configure_context(ctx, kwargs, ["ukm"])
236 if libcrypto.EVP_PKEY_derive_set_peer(ctx, peerkey.key) <= 0:
237 raise PKeyError("Cannot set peer key")
239 # We just hardcode numeric command to set UKM here
240 if libcrypto.EVP_PKEY_CTX_ctrl(ctx, -1, 1 << 10, 8, 8,
242 raise PKeyError("Cannot set UKM")
244 if libcrypto.EVP_PKEY_derive(ctx, None, byref(keylen)) <= 0:
245 raise PKeyError("computing shared key length")
246 buf = create_string_buffer(keylen.value)
247 if libcrypto.EVP_PKEY_derive(ctx, buf, byref(keylen)) <= 0:
248 raise PKeyError("computing actual shared key")
249 libcrypto.EVP_PKEY_CTX_free(ctx)
250 return buf.raw[:int(keylen.value)]
253 def generate(algorithm, **kwargs):
255 Generates new private-public key pair for given algorithm
256 (string like 'rsa','ec','gost2001') and algorithm-specific
259 Algorithm specific paramteers for RSA:
261 rsa_keygen_bits=number - size of key to be generated
262 rsa_keygen_pubexp - RSA public expontent(default 65537)
264 Algorithm specific parameters for DSA,DH and EC
266 paramsfrom=PKey object
268 copy parameters of newly generated key from existing key
270 Algorithm specific parameters for GOST2001
272 paramset= paramset name where name is one of
273 'A','B','C','XA','XB','test'
275 paramsfrom does work too
277 tmpeng = c_void_p(None)
278 if isinstance(algorithm, chartype):
279 alg = algorithm.encode("ascii")
282 ameth = libcrypto.EVP_PKEY_asn1_find_str(byref(tmpeng), alg, -1)
284 raise PKeyError("Algorithm %s not foind\n"%(algorithm))
287 libcrypto.EVP_PKEY_asn1_get0_info(byref(pkey_id), None, None, None,
289 #libcrypto.ENGINE_finish(tmpeng)
290 if "paramsfrom" in kwargs:
291 ctx = libcrypto.EVP_PKEY_CTX_new(kwargs["paramsfrom"].key, None)
293 ctx = libcrypto.EVP_PKEY_CTX_new_id(pkey_id, None)
294 # FIXME support EC curve as keyword param by invoking paramgen
297 raise PKeyError("Creating context for key type %d"%(pkey_id.value))
298 if libcrypto.EVP_PKEY_keygen_init(ctx) <= 0:
299 raise PKeyError("keygen_init")
300 PKey._configure_context(ctx, kwargs, ["paramsfrom"])
302 if libcrypto.EVP_PKEY_keygen(ctx, byref(key)) <= 0:
303 raise PKeyError("Error generating key")
304 libcrypto.EVP_PKEY_CTX_free(ctx)
305 return PKey(ptr=key, cansign=True)
307 def exportpub(self, format="PEM"):
309 Returns public key as PEM or DER structure.
313 retcode = libcrypto.PEM_write_bio_PUBKEY(bio.bio, self.key)
315 retcode = libcrypto.i2d_PUBKEY_bio(bio.bio, self.key)
317 raise PKeyError("error serializing public key")
320 def exportpriv(self, format="PEM", password=None, cipher=None):
322 Returns private key as PEM or DER Structure.
323 If password and cipher are specified, encrypts key
324 on given password, using given algorithm. Cipher must be
325 an ctypescrypto.cipher.CipherType object
327 Password can be either string or function with one argument,
328 which returns password. It is called with argument True, which
329 means, that we are encrypting key, and password should be
330 verified (requested twice from user, for example).
336 evp_cipher = cipher.cipher
338 ret = libcrypto.PEM_write_bio_PrivateKey(bio.bio, self.key,
340 _password_callback(password),
343 raise PKeyError("error serializing private key")
346 ret = libcrypto.i2d_PKCS8PrivateKey_bio(bio.bio, self.key,
348 _password_callback(password),
351 raise PKeyError("error serializing private key")
355 def _configure_context(ctx, opts, skip=()):
357 Configures context of public key operations
358 @param ctx - context to configure
359 @param opts - dictionary of options (from kwargs of calling
361 @param skip - list of options which shouldn't be passed to
368 if isinstance(oper,chartype):
369 op = oper.encode("ascii")
372 if isinstance(opts[oper],chartype):
373 value = opts[oper].encode("ascii")
374 elif isinstance(opts[oper],bintype):
378 value = str(opts[oper])
380 value = str(opts[oper]).encode('ascii')
381 ret = libcrypto.EVP_PKEY_CTX_ctrl_str(ctx, op, value)
383 raise PKeyError("Parameter %s is not supported by key" % oper)
385 raise PKeyError("Error setting parameter %s" % oper)
386 # Declare function prototypes
387 libcrypto.EVP_PKEY_cmp.argtypes = (c_void_p, c_void_p)
388 libcrypto.PEM_read_bio_PrivateKey.restype = c_void_p
389 libcrypto.PEM_read_bio_PrivateKey.argtypes = (c_void_p, POINTER(c_void_p),
390 PW_CALLBACK_FUNC, c_char_p)
391 libcrypto.PEM_read_bio_PUBKEY.restype = c_void_p
392 libcrypto.PEM_read_bio_PUBKEY.argtypes = (c_void_p, POINTER(c_void_p),
393 PW_CALLBACK_FUNC, c_char_p)
394 libcrypto.d2i_PUBKEY_bio.restype = c_void_p
395 libcrypto.d2i_PUBKEY_bio.argtypes = (c_void_p, c_void_p)
396 libcrypto.d2i_PrivateKey_bio.restype = c_void_p
397 libcrypto.d2i_PrivateKey_bio.argtypes = (c_void_p, c_void_p)
398 libcrypto.EVP_PKEY_print_public.argtypes = (c_void_p, c_void_p, c_int, c_void_p)
399 libcrypto.EVP_PKEY_asn1_find_str.restype = c_void_p
400 libcrypto.EVP_PKEY_asn1_find_str.argtypes = (c_void_p, c_char_p, c_int)
401 libcrypto.EVP_PKEY_asn1_get0_info.restype = c_int
402 libcrypto.EVP_PKEY_asn1_get0_info.argtypes = (POINTER(c_int), POINTER(c_int),
403 POINTER(c_int), POINTER(c_char_p),
404 POINTER(c_char_p), c_void_p)
405 libcrypto.EVP_PKEY_cmp.restype = c_int
406 libcrypto.EVP_PKEY_cmp.argtypes = (c_void_p, c_void_p)
407 libcrypto.EVP_PKEY_CTX_ctrl_str.restype = c_int
408 libcrypto.EVP_PKEY_CTX_ctrl_str.argtypes = (c_void_p, c_void_p, c_void_p)
409 libcrypto.EVP_PKEY_CTX_ctrl.restype = c_int
410 libcrypto.EVP_PKEY_CTX_ctrl.argtypes = (c_void_p, c_int, c_int, c_int, c_int,
412 libcrypto.EVP_PKEY_CTX_free.argtypes = (c_void_p, )
413 libcrypto.EVP_PKEY_CTX_new.restype = c_void_p
414 libcrypto.EVP_PKEY_CTX_new.argtypes = (c_void_p, c_void_p)
415 libcrypto.EVP_PKEY_CTX_new_id.restype = c_void_p
416 libcrypto.EVP_PKEY_CTX_new_id.argtypes = (c_int, c_void_p)
417 libcrypto.EVP_PKEY_derive.restype = c_int
418 libcrypto.EVP_PKEY_derive.argtypes = (c_void_p, c_char_p, POINTER(c_long))
419 libcrypto.EVP_PKEY_derive_init.restype = c_int
420 libcrypto.EVP_PKEY_derive_init.argtypes = (c_void_p, )
421 libcrypto.EVP_PKEY_derive_set_peer.restype = c_int
422 libcrypto.EVP_PKEY_derive_set_peer.argtypes = (c_void_p, c_void_p)
423 libcrypto.EVP_PKEY_free.argtypes = (c_void_p,)
424 libcrypto.EVP_PKEY_keygen.restype = c_int
425 libcrypto.EVP_PKEY_keygen.argtypes = (c_void_p, c_void_p)
426 libcrypto.EVP_PKEY_keygen_init.restype = c_int
427 libcrypto.EVP_PKEY_keygen_init.argtypes = (c_void_p, )
428 libcrypto.EVP_PKEY_sign.restype = c_int
429 libcrypto.EVP_PKEY_sign.argtypes = (c_void_p, c_char_p, POINTER(c_long),
431 libcrypto.EVP_PKEY_sign_init.restype = c_int
432 libcrypto.EVP_PKEY_sign_init.argtypes = (c_void_p, )
433 libcrypto.EVP_PKEY_verify.restype = c_int
434 libcrypto.EVP_PKEY_verify.argtypes = (c_void_p, c_char_p, c_long, c_char_p,
436 libcrypto.EVP_PKEY_verify_init.restype = c_int
437 libcrypto.EVP_PKEY_verify_init.argtypes = (c_void_p, )
438 libcrypto.PEM_write_bio_PrivateKey.argtypes = (c_void_p, c_void_p, c_void_p,
440 PW_CALLBACK_FUNC, c_char_p)
441 libcrypto.PEM_write_bio_PUBKEY.argtypes = (c_void_p, c_void_p)
442 libcrypto.i2d_PUBKEY_bio.argtypes = (c_void_p, c_void_p)
443 libcrypto.i2d_PKCS8PrivateKey_bio.argtypes = (c_void_p, c_void_p, c_void_p,
445 PW_CALLBACK_FUNC, c_char_p)
446 libcrypto.d2i_PKCS8PrivateKey_bio.restype = c_void_p
447 libcrypto.d2i_PKCS8PrivateKey_bio.argtypes = (c_void_p,c_void_p,
448 PW_CALLBACK_FUNC,c_void_p)
449 libcrypto.ENGINE_finish.argtypes = (c_void_p, )