]> www.wagner.pp.ru Git - oss/ctypescrypto.git/blob - ctypescrypto/pkey.py
59da723f7b93f926a44b2d766c19c00842ea939a
[oss/ctypescrypto.git] / ctypescrypto / pkey.py
1 """
2 This module provides interface for low-level private/public keypair operation
3
4 PKey object of this module is wrapper around OpenSSL EVP_PKEY object.
5 """
6
7
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
13
14 __all__ = ['PKeyError', 'password_callback', 'PKey', 'PW_CALLBACK_FUNC']
15 class PKeyError(LibCryptoError):
16     """ Exception thrown if libcrypto finctions return an error """
17     pass
18
19 PW_CALLBACK_FUNC = CFUNCTYPE(c_int, c_char_p, c_int, c_int, c_char_p)
20 """ Function type for pem password callback """
21
22 def password_callback(buf, length, rwflag, userdata):
23     """
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
27     """
28     cnt = len(userdata)
29     if length < cnt:
30         cnt = length
31     memmove(buf, userdata, cnt)
32     return cnt
33
34 _cb = PW_CALLBACK_FUNC(password_callback)
35
36 class PKey(object):
37     """
38     Represents public/private key pair. Wrapper around EVP_PKEY
39     libcrypto object.
40
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
44     deriving.
45
46     @var cansign is true key has private part.
47     @var key contain pointer to EVP_PKEY and should be passed to various
48          libcrypto routines
49     """
50     def __init__(self, ptr=None, privkey=None, pubkey=None, format="PEM",
51                  cansign=False, password=None, callback=_cb):
52         if not ptr is None:
53             self.key = ptr
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 " +
57                                 "be specified")
58         elif not privkey is None:
59             if not pubkey is None:
60                 raise TypeError("Just one of ptr, pubkey or privkey can " +
61                                 "be specified")
62             bio = Membio(privkey)
63             self.cansign = True
64             if format == "PEM":
65                 self.key = libcrypto.PEM_read_bio_PrivateKey(bio.bio, None,
66                                                              callback,
67                                                              c_char_p(password))
68             else:
69                 self.key = libcrypto.d2i_PrivateKey_bio(bio.bio, None)
70             if self.key is None:
71                 raise PKeyError("error parsing private key")
72         elif not pubkey is None:
73             bio = Membio(pubkey)
74             self.cansign = False
75             if format == "PEM":
76                 self.key = libcrypto.PEM_read_bio_PUBKEY(bio.bio, None,
77                                                          callback,
78                                                          c_char_p(password))
79             else:
80                 self.key = libcrypto.d2i_PUBKEY_bio(bio.bio, None)
81             if self.key is None:
82                 raise PKeyError("error parsing public key")
83         else:
84             raise TypeError("Neither public, nor private key is specified")
85
86
87     def __del__(self):
88         """ Frees EVP_PKEY object (note, it is reference counted) """
89         libcrypto.EVP_PKEY_free(self.key)
90
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
94         """
95         return libcrypto.EVP_PKEY_cmp(self.key, other.key) == 1
96
97     def __ne__(self, other):
98         """ Compares two public key for not-equality """
99         return not self.__eq__(other)
100
101     def __str__(self):
102         """ printable representation of public key """
103         bio = Membio()
104         libcrypto.EVP_PKEY_print_public(bio.bio, self.key, 0, None)
105         return str(bio)
106
107     def sign(self, digest, **kwargs):
108         """
109         Signs given digest and retirns signature
110         Keyword arguments allows to set various algorithm-specific
111         parameters. See pkeyutl(1) manual.
112         """
113         ctx = libcrypto.EVP_PKEY_CTX_new(self.key, None)
114         if ctx is 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
120         siglen = c_long(0)
121         if libcrypto.EVP_PKEY_sign(ctx, None, byref(siglen), digest,
122                                    len(digest)) < 1:
123             raise PKeyError("computing signature length")
124         sig = create_string_buffer(siglen.value)
125         if libcrypto.EVP_PKEY_sign(ctx, sig, byref(siglen), digest,
126                                    len(digest)) < 1:
127             raise PKeyError("signing")
128         libcrypto.EVP_PKEY_CTX_free(ctx)
129         return sig.raw[:int(siglen.value)]
130
131     def verify(self, digest, signature, **kwargs):
132         """
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
136         parameters
137         """
138         ctx = libcrypto.EVP_PKEY_CTX_new(self.key, None)
139         if ctx is 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,
145                                         len(digest))
146         if ret < 0:
147             raise PKeyError("Signature verification")
148         libcrypto.EVP_PKEY_CTX_free(ctx)
149         return ret > 0
150
151     def derive(self, peerkey, **kwargs):
152         """
153         Derives shared key (DH,ECDH,VKO 34.10). Requires
154         private key available
155
156         @param peerkey - other key (may be public only)
157
158         Keyword parameters are algorithm-specific
159         """
160         if not self.cansign:
161             raise ValueError("No private key available")
162         ctx = libcrypto.EVP_PKEY_CTX_new(self.key, None)
163         if ctx is None:
164             raise PKeyError("Initailizing derive context")
165         if libcrypto.EVP_PKEY_derive_init(ctx) < 1:
166             raise PKeyError("derive_init")
167
168         # This is workaround around missing functionality in GOST engine
169         # it provides only numeric control command to set UKM, not
170         # string one.
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")
174         if "ukm" in kwargs:
175             # We just hardcode numeric command to set UKM here
176             if libcrypto.EVP_PKEY_CTX_ctrl(ctx, -1, 1 << 10, 8, 8,
177                                            kwargs["ukm"]) <= 0:
178                 raise PKeyError("Cannot set UKM")
179         keylen = c_long(0)
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)]
187
188     @staticmethod
189     def generate(algorithm, **kwargs):
190         """
191         Generates new private-public key pair for given algorithm
192         (string like 'rsa','ec','gost2001') and algorithm-specific
193         parameters.
194
195         Algorithm specific paramteers for RSA:
196
197         rsa_keygen_bits=number - size of key to be generated
198         rsa_keygen_pubexp - RSA public expontent(default 65537)
199
200         Algorithm specific parameters for DSA,DH and EC
201
202         paramsfrom=PKey object
203
204         copy parameters of newly generated key from existing key
205
206         Algorithm specific parameters for GOST2001
207
208         paramset= paramset name where name is one of
209         'A','B','C','XA','XB','test'
210
211         paramsfrom does work too
212         """
213         tmpeng = c_void_p(None)
214         ameth = libcrypto.EVP_PKEY_asn1_find_str(byref(tmpeng), algorithm, -1)
215         if ameth is None:
216             raise PKeyError("Algorithm %s not foind\n"%(algorithm))
217         clear_err_stack()
218         pkey_id = c_int(0)
219         libcrypto.EVP_PKEY_asn1_get0_info(byref(pkey_id), None, None, None,
220                                           None, ameth)
221         #libcrypto.ENGINE_finish(tmpeng)
222         if "paramsfrom" in kwargs:
223             ctx = libcrypto.EVP_PKEY_CTX_new(kwargs["paramsfrom"].key, None)
224         else:
225             ctx = libcrypto.EVP_PKEY_CTX_new_id(pkey_id, None)
226         # FIXME support EC curve as keyword param by invoking paramgen
227         # operation
228         if ctx is None:
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"])
233         key = c_void_p(None)
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)
238
239     def exportpub(self, format="PEM"):
240         """
241         Returns public key as PEM or DER structure.
242         """
243         bio = Membio()
244         if format == "PEM":
245             retcode = libcrypto.PEM_write_bio_PUBKEY(bio.bio, self.key)
246         else:
247             retcode = libcrypto.i2d_PUBKEY_bio(bio.bio, self.key)
248         if retcode == 0:
249             raise PKeyError("error serializing public key")
250         return str(bio)
251
252     def exportpriv(self, format="PEM", password=None, cipher=None,
253                    callback=_cb):
254         """
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
259         """
260         bio = Membio()
261         if cipher is None:
262             evp_cipher = None
263         else:
264             evp_cipher = cipher.cipher
265         if format == "PEM":
266             ret = libcrypto.PEM_write_bio_PrivateKey(bio.bio, self.key,
267                                                      evp_cipher, None, 0,
268                                                      callback,
269                                                      c_char_p(password))
270         else:
271             ret = libcrypto.i2d_PKCS8PrivateKey_bio(bio.bio, self.key,
272                                                     evp_cipher, None, 0,
273                                                     callback,
274                                                     c_char_p(password))
275         if ret == 0:
276             raise PKeyError("error serializing private key")
277         return str(bio)
278
279     @staticmethod
280     def _configure_context(ctx, opts, skip=()):
281         """
282         Configures context of public key operations
283         @param ctx - context to configure
284         @param opts - dictionary of options (from kwargs of calling
285             function)
286         @param skip - list of options which shouldn't be passed to
287             context
288         """
289
290         for oper in opts:
291             if oper in skip:
292                 continue
293             ret = libcrypto.EVP_PKEY_CTX_ctrl_str(ctx, oper, str(opts[oper]))
294             if ret == -2:
295                 raise PKeyError("Parameter %s is not supported by key" % oper)
296             if ret < 1:
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,
323                                         c_void_p)
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),
342                                     c_char_p, 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,
347                                       c_long)
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,
351                                                c_char_p, c_int,
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,
356                                               c_char_p, c_int,
357                                               PW_CALLBACK_FUNC, c_char_p)
358 libcrypto.ENGINE_finish.argtypes = (c_void_p, )