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
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)
35 def __cb(buf, length, rwflag, userdata):
37 cnt = min(len(pwd),length)
41 def __cb(buf,length,rwflag,userdata):
42 cnt=min(len(c),length)
45 return PW_CALLBACK_FUNC(__cb)
50 Represents public/private key pair. Wrapper around EVP_PKEY
53 May contain either both private and public key (such objects can be
54 used for signing, deriving shared key as well as verifying or public
55 key only, which can be used for verifying or as peer key when
58 @var cansign is true key has private part.
59 @var key contain pointer to EVP_PKEY and should be passed to various
62 def __init__(self, ptr=None, privkey=None, pubkey=None, format="PEM",
63 cansign=False, password=None):
65 PKey object can be created from either private/public key blob or
66 from C language pointer, returned by some OpenSSL function
68 Following named arguments are recognized by constructor
70 privkey - private key blob. If this is specified, format and
71 password can be also specified
73 pubkey - public key blob. If this is specified, format can be
76 ptr - pointer, returned by openssl function. If it is specified,
77 cansign should be also specified.
79 These three arguments are mutually exclusive.
81 format - can be either 'PEM' or 'DER'. Specifies format of blob.
83 password - can be string with password for encrypted key, or
84 callable with one boolean argument, which returns password.
85 During constructor call this argument would be false.
87 If key is in PEM format, its encrypted status and format is
88 autodetected. If key is in DER format, than if password is
89 specified, key is assumed to be encrypted PKCS8 key otherwise
90 it is assumed to be unencrypted.
95 self.cansign = cansign
96 if not privkey is None or not pubkey is None:
97 raise TypeError("Just one of ptr, pubkey or privkey can " +
99 elif not privkey is None:
100 if not pubkey is None:
101 raise TypeError("Just one of ptr, pubkey or privkey can " +
103 bio = Membio(privkey)
106 self.key = libcrypto.PEM_read_bio_PrivateKey(bio.bio, None,
107 _password_callback(password),
110 if password is not None:
111 self.key = libcrypto.d2i_PKCS8PrivateKey_bio(bio.bio,None,
112 _password_callback(password),
115 self.key = libcrypto.d2i_PrivateKey_bio(bio.bio, None)
117 raise PKeyError("error parsing private key")
118 elif not pubkey is None:
122 self.key = libcrypto.PEM_read_bio_PUBKEY(bio.bio, None,
123 _password_callback(password),
126 self.key = libcrypto.d2i_PUBKEY_bio(bio.bio, None)
128 raise PKeyError("error parsing public key")
130 raise TypeError("Neither public, nor private key is specified")
134 """ Frees EVP_PKEY object (note, it is reference counted) """
135 libcrypto.EVP_PKEY_free(self.key)
137 def __eq__(self, other):
138 """ Compares two public keys. If one has private key and other
139 doesn't it doesn't affect result of comparation
141 return libcrypto.EVP_PKEY_cmp(self.key, other.key) == 1
143 def __ne__(self, other):
144 """ Compares two public key for not-equality """
145 return not self.__eq__(other)
148 """ printable representation of public key """
150 libcrypto.EVP_PKEY_print_public(bio.bio, self.key, 0, None)
153 def sign(self, digest, **kwargs):
155 Signs given digest and retirns signature
156 Keyword arguments allows to set various algorithm-specific
157 parameters. See pkeyutl(1) manual.
159 ctx = libcrypto.EVP_PKEY_CTX_new(self.key, None)
161 raise PKeyError("Initailizing sign context")
162 if libcrypto.EVP_PKEY_sign_init(ctx) < 1:
163 raise PKeyError("sign_init")
164 self._configure_context(ctx, kwargs)
165 # Find out signature size
167 if libcrypto.EVP_PKEY_sign(ctx, None, byref(siglen), digest,
169 raise PKeyError("computing signature length")
170 sig = create_string_buffer(siglen.value)
171 if libcrypto.EVP_PKEY_sign(ctx, sig, byref(siglen), digest,
173 raise PKeyError("signing")
174 libcrypto.EVP_PKEY_CTX_free(ctx)
175 return sig.raw[:int(siglen.value)]
177 def verify(self, digest, signature, **kwargs):
179 Verifies given signature on given digest
180 Returns True if Ok, False if don't match
181 Keyword arguments allows to set algorithm-specific
184 ctx = libcrypto.EVP_PKEY_CTX_new(self.key, None)
186 raise PKeyError("Initailizing verify context")
187 if libcrypto.EVP_PKEY_verify_init(ctx) < 1:
188 raise PKeyError("verify_init")
189 self._configure_context(ctx, kwargs)
190 ret = libcrypto.EVP_PKEY_verify(ctx, signature, len(signature), digest,
193 raise PKeyError("Signature verification")
194 libcrypto.EVP_PKEY_CTX_free(ctx)
197 def derive(self, peerkey, **kwargs):
199 Derives shared key (DH,ECDH,VKO 34.10). Requires
200 private key available
202 @param peerkey - other key (may be public only)
204 Keyword parameters are algorithm-specific
207 raise ValueError("No private key available")
208 ctx = libcrypto.EVP_PKEY_CTX_new(self.key, None)
210 raise PKeyError("Initailizing derive context")
211 if libcrypto.EVP_PKEY_derive_init(ctx) < 1:
212 raise PKeyError("derive_init")
214 # This is workaround around missing functionality in GOST engine
215 # it provides only numeric control command to set UKM, not
217 self._configure_context(ctx, kwargs, ["ukm"])
218 if libcrypto.EVP_PKEY_derive_set_peer(ctx, peerkey.key) <= 0:
219 raise PKeyError("Cannot set peer key")
221 # We just hardcode numeric command to set UKM here
222 if libcrypto.EVP_PKEY_CTX_ctrl(ctx, -1, 1 << 10, 8, 8,
224 raise PKeyError("Cannot set UKM")
226 if libcrypto.EVP_PKEY_derive(ctx, None, byref(keylen)) <= 0:
227 raise PKeyError("computing shared key length")
228 buf = create_string_buffer(keylen.value)
229 if libcrypto.EVP_PKEY_derive(ctx, buf, byref(keylen)) <= 0:
230 raise PKeyError("computing actual shared key")
231 libcrypto.EVP_PKEY_CTX_free(ctx)
232 return buf.raw[:int(keylen.value)]
235 def generate(algorithm, **kwargs):
237 Generates new private-public key pair for given algorithm
238 (string like 'rsa','ec','gost2001') and algorithm-specific
241 Algorithm specific paramteers for RSA:
243 rsa_keygen_bits=number - size of key to be generated
244 rsa_keygen_pubexp - RSA public expontent(default 65537)
246 Algorithm specific parameters for DSA,DH and EC
248 paramsfrom=PKey object
250 copy parameters of newly generated key from existing key
252 Algorithm specific parameters for GOST2001
254 paramset= paramset name where name is one of
255 'A','B','C','XA','XB','test'
257 paramsfrom does work too
259 tmpeng = c_void_p(None)
260 ameth = libcrypto.EVP_PKEY_asn1_find_str(byref(tmpeng), algorithm, -1)
262 raise PKeyError("Algorithm %s not foind\n"%(algorithm))
265 libcrypto.EVP_PKEY_asn1_get0_info(byref(pkey_id), None, None, None,
267 #libcrypto.ENGINE_finish(tmpeng)
268 if "paramsfrom" in kwargs:
269 ctx = libcrypto.EVP_PKEY_CTX_new(kwargs["paramsfrom"].key, None)
271 ctx = libcrypto.EVP_PKEY_CTX_new_id(pkey_id, None)
272 # FIXME support EC curve as keyword param by invoking paramgen
275 raise PKeyError("Creating context for key type %d"%(pkey_id.value))
276 if libcrypto.EVP_PKEY_keygen_init(ctx) <= 0:
277 raise PKeyError("keygen_init")
278 PKey._configure_context(ctx, kwargs, ["paramsfrom"])
280 if libcrypto.EVP_PKEY_keygen(ctx, byref(key)) <= 0:
281 raise PKeyError("Error generating key")
282 libcrypto.EVP_PKEY_CTX_free(ctx)
283 return PKey(ptr=key, cansign=True)
285 def exportpub(self, format="PEM"):
287 Returns public key as PEM or DER structure.
291 retcode = libcrypto.PEM_write_bio_PUBKEY(bio.bio, self.key)
293 retcode = libcrypto.i2d_PUBKEY_bio(bio.bio, self.key)
295 raise PKeyError("error serializing public key")
298 def exportpriv(self, format="PEM", password=None, cipher=None):
300 Returns private key as PEM or DER Structure.
301 If password and cipher are specified, encrypts key
302 on given password, using given algorithm. Cipher must be
303 an ctypescrypto.cipher.CipherType object
305 Password can be either string or function with one argument,
306 which returns password. It is called with argument True, which
307 means, that we are encrypting key, and password should be
308 verified (requested twice from user, for example).
314 evp_cipher = cipher.cipher
316 ret = libcrypto.PEM_write_bio_PrivateKey(bio.bio, self.key,
318 _password_callback(password),
321 ret = libcrypto.i2d_PKCS8PrivateKey_bio(bio.bio, self.key,
323 _password_callback(password),
326 raise PKeyError("error serializing private key")
330 def _configure_context(ctx, opts, skip=()):
332 Configures context of public key operations
333 @param ctx - context to configure
334 @param opts - dictionary of options (from kwargs of calling
336 @param skip - list of options which shouldn't be passed to
343 ret = libcrypto.EVP_PKEY_CTX_ctrl_str(ctx, oper, str(opts[oper]))
345 raise PKeyError("Parameter %s is not supported by key" % oper)
347 raise PKeyError("Error setting parameter %s" % oper)
348 # Declare function prototypes
349 libcrypto.EVP_PKEY_cmp.argtypes = (c_void_p, c_void_p)
350 libcrypto.PEM_read_bio_PrivateKey.restype = c_void_p
351 libcrypto.PEM_read_bio_PrivateKey.argtypes = (c_void_p, POINTER(c_void_p),
352 PW_CALLBACK_FUNC, c_char_p)
353 libcrypto.PEM_read_bio_PUBKEY.restype = c_void_p
354 libcrypto.PEM_read_bio_PUBKEY.argtypes = (c_void_p, POINTER(c_void_p),
355 PW_CALLBACK_FUNC, c_char_p)
356 libcrypto.d2i_PUBKEY_bio.restype = c_void_p
357 libcrypto.d2i_PUBKEY_bio.argtypes = (c_void_p, c_void_p)
358 libcrypto.d2i_PrivateKey_bio.restype = c_void_p
359 libcrypto.d2i_PrivateKey_bio.argtypes = (c_void_p, c_void_p)
360 libcrypto.EVP_PKEY_print_public.argtypes = (c_void_p, c_void_p, c_int, c_void_p)
361 libcrypto.EVP_PKEY_asn1_find_str.restype = c_void_p
362 libcrypto.EVP_PKEY_asn1_find_str.argtypes = (c_void_p, c_char_p, c_int)
363 libcrypto.EVP_PKEY_asn1_get0_info.restype = c_int
364 libcrypto.EVP_PKEY_asn1_get0_info.argtypes = (POINTER(c_int), POINTER(c_int),
365 POINTER(c_int), POINTER(c_char_p),
366 POINTER(c_char_p), c_void_p)
367 libcrypto.EVP_PKEY_cmp.restype = c_int
368 libcrypto.EVP_PKEY_cmp.argtypes = (c_void_p, c_void_p)
369 libcrypto.EVP_PKEY_CTX_ctrl_str.restype = c_int
370 libcrypto.EVP_PKEY_CTX_ctrl_str.argtypes = (c_void_p, c_void_p, c_void_p)
371 libcrypto.EVP_PKEY_CTX_ctrl.restype = c_int
372 libcrypto.EVP_PKEY_CTX_ctrl.argtypes = (c_void_p, c_int, c_int, c_int, c_int,
374 libcrypto.EVP_PKEY_CTX_free.argtypes = (c_void_p, )
375 libcrypto.EVP_PKEY_CTX_new.restype = c_void_p
376 libcrypto.EVP_PKEY_CTX_new.argtypes = (c_void_p, c_void_p)
377 libcrypto.EVP_PKEY_CTX_new_id.restype = c_void_p
378 libcrypto.EVP_PKEY_CTX_new_id.argtypes = (c_int, c_void_p)
379 libcrypto.EVP_PKEY_derive.restype = c_int
380 libcrypto.EVP_PKEY_derive.argtypes = (c_void_p, c_char_p, POINTER(c_long))
381 libcrypto.EVP_PKEY_derive_init.restype = c_int
382 libcrypto.EVP_PKEY_derive_init.argtypes = (c_void_p, )
383 libcrypto.EVP_PKEY_derive_set_peer.restype = c_int
384 libcrypto.EVP_PKEY_derive_set_peer.argtypes = (c_void_p, c_void_p)
385 libcrypto.EVP_PKEY_free.argtypes = (c_void_p,)
386 libcrypto.EVP_PKEY_keygen.restype = c_int
387 libcrypto.EVP_PKEY_keygen.argtypes = (c_void_p, c_void_p)
388 libcrypto.EVP_PKEY_keygen_init.restype = c_int
389 libcrypto.EVP_PKEY_keygen_init.argtypes = (c_void_p, )
390 libcrypto.EVP_PKEY_sign.restype = c_int
391 libcrypto.EVP_PKEY_sign.argtypes = (c_void_p, c_char_p, POINTER(c_long),
393 libcrypto.EVP_PKEY_sign_init.restype = c_int
394 libcrypto.EVP_PKEY_sign_init.argtypes = (c_void_p, )
395 libcrypto.EVP_PKEY_verify.restype = c_int
396 libcrypto.EVP_PKEY_verify.argtypes = (c_void_p, c_char_p, c_long, c_char_p,
398 libcrypto.EVP_PKEY_verify_init.restype = c_int
399 libcrypto.EVP_PKEY_verify_init.argtypes = (c_void_p, )
400 libcrypto.PEM_write_bio_PrivateKey.argtypes = (c_void_p, c_void_p, c_void_p,
402 PW_CALLBACK_FUNC, c_char_p)
403 libcrypto.PEM_write_bio_PUBKEY.argtypes = (c_void_p, c_void_p)
404 libcrypto.i2d_PUBKEY_bio.argtypes = (c_void_p, c_void_p)
405 libcrypto.i2d_PKCS8PrivateKey_bio.argtypes = (c_void_p, c_void_p, c_void_p,
407 PW_CALLBACK_FUNC, c_char_p)
408 libcrypto.d2i_PKCS8PrivateKey_bio.restype = c_void_p
409 libcrypto.d2i_PKCS8PrivateKey_bio.argtypes = (c_void_p,c_void_p,
410 PW_CALLBACK_FUNC,c_void_p)
411 libcrypto.ENGINE_finish.argtypes = (c_void_p, )