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_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', 'password_callback', 'PKey', 'PW_CALLBACK_FUNC']
15 class PKeyError(LibCryptoError):
16 """ Exception thrown if libcrypto finctions return an error """
19 PW_CALLBACK_FUNC = CFUNCTYPE(c_int, c_char_p, c_int, c_int, c_char_p)
20 """ Function type for pem password callback """
22 def password_callback(buf, length, rwflag, userdata):
24 Example password callback for private key. Assumes that
25 password is stored in the userdata parameter, so allows to pass password
26 from constructor arguments to the libcrypto keyloading functions
31 memmove(buf, userdata, cnt)
34 _cb = PW_CALLBACK_FUNC(password_callback)
38 Represents public/private key pair. Wrapper around EVP_PKEY
41 May contain either both private and public key (such objects can be
42 used for signing, deriving shared key as well as verifying or public
43 key only, which can be used for verifying or as peer key when
46 @var cansign is true key has private part.
47 @var key contain pointer to EVP_PKEY and should be passed to various
50 def __init__(self, ptr=None, privkey=None, pubkey=None, format="PEM",
51 cansign=False, password=None, callback=_cb):
54 self.cansign = cansign
55 if not privkey is None or not pubkey is None:
56 raise TypeError("Just one of ptr, pubkey or privkey can " +
58 elif not privkey is None:
59 if not pubkey is None:
60 raise TypeError("Just one of ptr, pubkey or privkey can " +
65 self.key = libcrypto.PEM_read_bio_PrivateKey(bio.bio, None,
69 self.key = libcrypto.d2i_PrivateKey_bio(bio.bio, None)
71 raise PKeyError("error parsing private key")
72 elif not pubkey is None:
76 self.key = libcrypto.PEM_read_bio_PUBKEY(bio.bio, None,
80 self.key = libcrypto.d2i_PUBKEY_bio(bio.bio, None)
82 raise PKeyError("error parsing public key")
84 raise TypeError("Neither public, nor private key is specified")
88 """ Frees EVP_PKEY object (note, it is reference counted) """
89 libcrypto.EVP_PKEY_free(self.key)
91 def __eq__(self, other):
92 """ Compares two public keys. If one has private key and other
93 doesn't it doesn't affect result of comparation
95 return libcrypto.EVP_PKEY_cmp(self.key, other.key) == 1
97 def __ne__(self, other):
98 """ Compares two public key for not-equality """
99 return not self.__eq__(other)
102 """ printable representation of public key """
104 libcrypto.EVP_PKEY_print_public(bio.bio, self.key, 0, None)
107 def sign(self, digest, **kwargs):
109 Signs given digest and retirns signature
110 Keyword arguments allows to set various algorithm-specific
111 parameters. See pkeyutl(1) manual.
113 ctx = libcrypto.EVP_PKEY_CTX_new(self.key, None)
115 raise PKeyError("Initailizing sign context")
116 if libcrypto.EVP_PKEY_sign_init(ctx) < 1:
117 raise PKeyError("sign_init")
118 self._configure_context(ctx, kwargs)
119 # Find out signature size
121 if libcrypto.EVP_PKEY_sign(ctx, None, byref(siglen), digest,
123 raise PKeyError("computing signature length")
124 sig = create_string_buffer(siglen.value)
125 if libcrypto.EVP_PKEY_sign(ctx, sig, byref(siglen), digest,
127 raise PKeyError("signing")
128 libcrypto.EVP_PKEY_CTX_free(ctx)
129 return sig.raw[:int(siglen.value)]
131 def verify(self, digest, signature, **kwargs):
133 Verifies given signature on given digest
134 Returns True if Ok, False if don't match
135 Keyword arguments allows to set algorithm-specific
138 ctx = libcrypto.EVP_PKEY_CTX_new(self.key, None)
140 raise PKeyError("Initailizing verify context")
141 if libcrypto.EVP_PKEY_verify_init(ctx) < 1:
142 raise PKeyError("verify_init")
143 self._configure_context(ctx, kwargs)
144 ret = libcrypto.EVP_PKEY_verify(ctx, signature, len(signature), digest,
147 raise PKeyError("Signature verification")
148 libcrypto.EVP_PKEY_CTX_free(ctx)
151 def derive(self, peerkey, **kwargs):
153 Derives shared key (DH,ECDH,VKO 34.10). Requires
154 private key available
156 @param peerkey - other key (may be public only)
158 Keyword parameters are algorithm-specific
161 raise ValueError("No private key available")
162 ctx = libcrypto.EVP_PKEY_CTX_new(self.key, None)
164 raise PKeyError("Initailizing derive context")
165 if libcrypto.EVP_PKEY_derive_init(ctx) < 1:
166 raise PKeyError("derive_init")
168 # This is workaround around missing functionality in GOST engine
169 # it provides only numeric control command to set UKM, not
171 self._configure_context(ctx, kwargs, ["ukm"])
172 if libcrypto.EVP_PKEY_derive_set_peer(ctx, peerkey.key) <= 0:
173 raise PKeyError("Cannot set peer key")
175 # We just hardcode numeric command to set UKM here
176 if libcrypto.EVP_PKEY_CTX_ctrl(ctx, -1, 1 << 10, 8, 8,
178 raise PKeyError("Cannot set UKM")
180 if libcrypto.EVP_PKEY_derive(ctx, None, byref(keylen)) <= 0:
181 raise PKeyError("computing shared key length")
182 buf = create_string_buffer(keylen.value)
183 if libcrypto.EVP_PKEY_derive(ctx, buf, byref(keylen)) <= 0:
184 raise PKeyError("computing actual shared key")
185 libcrypto.EVP_PKEY_CTX_free(ctx)
186 return buf.raw[:int(keylen.value)]
189 def generate(algorithm, **kwargs):
191 Generates new private-public key pair for given algorithm
192 (string like 'rsa','ec','gost2001') and algorithm-specific
195 Algorithm specific paramteers for RSA:
197 rsa_keygen_bits=number - size of key to be generated
198 rsa_keygen_pubexp - RSA public expontent(default 65537)
200 Algorithm specific parameters for DSA,DH and EC
202 paramsfrom=PKey object
204 copy parameters of newly generated key from existing key
206 Algorithm specific parameters for GOST2001
208 paramset= paramset name where name is one of
209 'A','B','C','XA','XB','test'
211 paramsfrom does work too
213 tmpeng = c_void_p(None)
214 ameth = libcrypto.EVP_PKEY_asn1_find_str(byref(tmpeng), algorithm, -1)
216 raise PKeyError("Algorithm %s not foind\n"%(algorithm))
219 libcrypto.EVP_PKEY_asn1_get0_info(byref(pkey_id), None, None, None,
221 #libcrypto.ENGINE_finish(tmpeng)
222 if "paramsfrom" in kwargs:
223 ctx = libcrypto.EVP_PKEY_CTX_new(kwargs["paramsfrom"].key, None)
225 ctx = libcrypto.EVP_PKEY_CTX_new_id(pkey_id, None)
226 # FIXME support EC curve as keyword param by invoking paramgen
229 raise PKeyError("Creating context for key type %d"%(pkey_id.value))
230 if libcrypto.EVP_PKEY_keygen_init(ctx) <= 0:
231 raise PKeyError("keygen_init")
232 PKey._configure_context(ctx, kwargs, ["paramsfrom"])
234 if libcrypto.EVP_PKEY_keygen(ctx, byref(key)) <= 0:
235 raise PKeyError("Error generating key")
236 libcrypto.EVP_PKEY_CTX_free(ctx)
237 return PKey(ptr=key, cansign=True)
239 def exportpub(self, format="PEM"):
241 Returns public key as PEM or DER structure.
245 retcode = libcrypto.PEM_write_bio_PUBKEY(bio.bio, self.key)
247 retcode = libcrypto.i2d_PUBKEY_bio(bio.bio, self.key)
249 raise PKeyError("error serializing public key")
252 def exportpriv(self, format="PEM", password=None, cipher=None,
255 Returns private key as PEM or DER Structure.
256 If password and cipher are specified, encrypts key
257 on given password, using given algorithm. Cipher must be
258 an ctypescrypto.cipher.CipherType object
264 evp_cipher = cipher.cipher
266 ret = libcrypto.PEM_write_bio_PrivateKey(bio.bio, self.key,
271 ret = libcrypto.i2d_PKCS8PrivateKey_bio(bio.bio, self.key,
276 raise PKeyError("error serializing private key")
280 def _configure_context(ctx, opts, skip=()):
282 Configures context of public key operations
283 @param ctx - context to configure
284 @param opts - dictionary of options (from kwargs of calling
286 @param skip - list of options which shouldn't be passed to
293 ret = libcrypto.EVP_PKEY_CTX_ctrl_str(ctx, oper, str(opts[oper]))
295 raise PKeyError("Parameter %s is not supported by key" % oper)
297 raise PKeyError("Error setting parameter %s" % oper)
298 # Declare function prototypes
299 libcrypto.EVP_PKEY_cmp.argtypes = (c_void_p, c_void_p)
300 libcrypto.PEM_read_bio_PrivateKey.restype = c_void_p
301 libcrypto.PEM_read_bio_PrivateKey.argtypes = (c_void_p, POINTER(c_void_p),
302 PW_CALLBACK_FUNC, c_char_p)
303 libcrypto.PEM_read_bio_PUBKEY.restype = c_void_p
304 libcrypto.PEM_read_bio_PUBKEY.argtypes = (c_void_p, POINTER(c_void_p),
305 PW_CALLBACK_FUNC, c_char_p)
306 libcrypto.d2i_PUBKEY_bio.restype = c_void_p
307 libcrypto.d2i_PUBKEY_bio.argtypes = (c_void_p, c_void_p)
308 libcrypto.d2i_PrivateKey_bio.restype = c_void_p
309 libcrypto.d2i_PrivateKey_bio.argtypes = (c_void_p, c_void_p)
310 libcrypto.EVP_PKEY_print_public.argtypes = (c_void_p, c_void_p, c_int, c_void_p)
311 libcrypto.EVP_PKEY_asn1_find_str.restype = c_void_p
312 libcrypto.EVP_PKEY_asn1_find_str.argtypes = (c_void_p, c_char_p, c_int)
313 libcrypto.EVP_PKEY_asn1_get0_info.restype = c_int
314 libcrypto.EVP_PKEY_asn1_get0_info.argtypes = (POINTER(c_int), POINTER(c_int),
315 POINTER(c_int), POINTER(c_char_p),
316 POINTER(c_char_p), c_void_p)
317 libcrypto.EVP_PKEY_cmp.restype = c_int
318 libcrypto.EVP_PKEY_cmp.argtypes = (c_void_p, c_void_p)
319 libcrypto.EVP_PKEY_CTX_ctrl_str.restype = c_int
320 libcrypto.EVP_PKEY_CTX_ctrl_str.argtypes = (c_void_p, c_void_p, c_void_p)
321 libcrypto.EVP_PKEY_CTX_ctrl.restype = c_int
322 libcrypto.EVP_PKEY_CTX_ctrl.argtypes = (c_void_p, c_int, c_int, c_int, c_int,
324 libcrypto.EVP_PKEY_CTX_free.argtypes = (c_void_p, )
325 libcrypto.EVP_PKEY_CTX_new.restype = c_void_p
326 libcrypto.EVP_PKEY_CTX_new.argtypes = (c_void_p, c_void_p)
327 libcrypto.EVP_PKEY_CTX_new_id.restype = c_void_p
328 libcrypto.EVP_PKEY_CTX_new_id.argtypes = (c_int, c_void_p)
329 libcrypto.EVP_PKEY_derive.restype = c_int
330 libcrypto.EVP_PKEY_derive.argtypes = (c_void_p, c_char_p, POINTER(c_long))
331 libcrypto.EVP_PKEY_derive_init.restype = c_int
332 libcrypto.EVP_PKEY_derive_init.argtypes = (c_void_p, )
333 libcrypto.EVP_PKEY_derive_set_peer.restype = c_int
334 libcrypto.EVP_PKEY_derive_set_peer.argtypes = (c_void_p, c_void_p)
335 libcrypto.EVP_PKEY_free.argtypes = (c_void_p,)
336 libcrypto.EVP_PKEY_keygen.restype = c_int
337 libcrypto.EVP_PKEY_keygen.argtypes = (c_void_p, c_void_p)
338 libcrypto.EVP_PKEY_keygen_init.restype = c_int
339 libcrypto.EVP_PKEY_keygen_init.argtypes = (c_void_p, )
340 libcrypto.EVP_PKEY_sign.restype = c_int
341 libcrypto.EVP_PKEY_sign.argtypes = (c_void_p, c_char_p, POINTER(c_long),
343 libcrypto.EVP_PKEY_sign_init.restype = c_int
344 libcrypto.EVP_PKEY_sign_init.argtypes = (c_void_p, )
345 libcrypto.EVP_PKEY_verify.restype = c_int
346 libcrypto.EVP_PKEY_verify.argtypes = (c_void_p, c_char_p, c_long, c_char_p,
348 libcrypto.EVP_PKEY_verify_init.restype = c_int
349 libcrypto.EVP_PKEY_verify_init.argtypes = (c_void_p, )
350 libcrypto.PEM_write_bio_PrivateKey.argtypes = (c_void_p, c_void_p, c_void_p,
352 PW_CALLBACK_FUNC, c_char_p)
353 libcrypto.PEM_write_bio_PUBKEY.argtypes = (c_void_p, c_void_p)
354 libcrypto.i2d_PUBKEY_bio.argtypes = (c_void_p, c_void_p)
355 libcrypto.i2d_PKCS8PrivateKey_bio.argtypes = (c_void_p, c_void_p, c_void_p,
357 PW_CALLBACK_FUNC, c_char_p)
358 libcrypto.ENGINE_finish.argtypes = (c_void_p, )