]> www.wagner.pp.ru Git - oss/ctypescrypto.git/blob - ctypescrypto/pkey.py
Converted tabs to spaces to make pylint happy
[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,byref,c_int,c_long, c_longlong, create_string_buffer,CFUNCTYPE,POINTER
9 from ctypescrypto import libcrypto
10 from ctypescrypto.exception import LibCryptoError,clear_err_stack
11 from ctypescrypto.bio import Membio
12 import sys
13
14 __all__ = ['PKeyError','password_callback','PKey']
15 class PKeyError(LibCryptoError):
16     pass
17
18 CALLBACK_FUNC=CFUNCTYPE(c_int,c_char_p,c_int,c_int,c_char_p)
19 def password_callback(buf,length,rwflag,u):
20     """
21     Example password callback for private key. Assumes that 
22     password is store in the userdata parameter, so allows to pass password
23     from constructor arguments to the libcrypto keyloading functions
24     """
25     cnt=len(u)
26     if length<cnt:
27         cnt=length
28     memmove(buf,u,cnt)
29     return cnt
30
31 _cb=CALLBACK_FUNC(password_callback)
32
33 class PKey(object):
34     def __init__(self,ptr=None,privkey=None,pubkey=None,format="PEM",cansign=False,password=None):
35         if not ptr is None:
36             self.key=ptr
37             self.cansign=cansign
38             if not privkey is None or not pubkey is None:
39                 raise TypeError("Just one of ptr, pubkey or privkey can be specified")
40         elif not privkey is None:
41             if not pubkey is None:
42                 raise TypeError("Just one of ptr, pubkey or privkey can be specified")
43             b=Membio(privkey)
44             self.cansign=True
45             if format == "PEM":
46                 self.key=libcrypto.PEM_read_bio_PrivateKey(b.bio,None,_cb,c_char_p(password))
47             else: 
48                 self.key=libcrypto.d2i_PrivateKey_bio(b.bio,None)
49             if self.key is None:
50                 raise PKeyError("error parsing private key")
51         elif not pubkey is None:
52             b=Membio(pubkey)
53             self.cansign=False
54             if format == "PEM":
55                 self.key=libcrypto.PEM_read_bio_PUBKEY(b.bio,None,_cb,None)
56             else:
57                 self.key=libcrypto.d2i_PUBKEY_bio(b.bio,None)
58             if self.key is None:
59                 raise PKeyError("error parsing public key")
60         else:
61             raise TypeError("Neither public, nor private key is specified")
62             
63
64     def __del__(self):
65         libcrypto.EVP_PKEY_free(self.key)
66     def __eq__(self,other):
67         """ Compares two public keys. If one has private key and other
68             doesn't it doesn't affect result of comparation
69         """
70         return libcrypto.EVP_PKEY_cmp(self.key,other.key)==1
71     def __ne__(self,other):
72         return not self.__eq__(other)
73     def __str__(self):
74         """ printable representation of public key """  
75         b=Membio()
76         libcrypto.EVP_PKEY_print_public(b.bio,self.key,0,None)
77         return str(b)
78
79     def sign(self,digest,**kwargs):
80         """
81             Signs given digest and retirns signature
82             Keyword arguments allows to set various algorithm-specific
83             parameters. See pkeyutl(1) manual.
84         """
85         ctx=libcrypto.EVP_PKEY_CTX_new(self.key,None)
86         if ctx is None:
87             raise PKeyError("Initailizing sign context")
88         if libcrypto.EVP_PKEY_sign_init(ctx)<1:
89             raise PKeyError("sign_init")
90         self._configure_context(ctx,kwargs)
91         # Find out signature size
92         siglen=c_long(0)
93         if libcrypto.EVP_PKEY_sign(ctx,None,byref(siglen),digest,len(digest))<1:
94             raise PKeyError("signing")  
95         sig=create_string_buffer(siglen.value)
96         libcrypto.EVP_PKEY_sign(ctx,sig,byref(siglen),digest,len(digest))
97         libcrypto.EVP_PKEY_CTX_free(ctx)
98         return sig.raw[:siglen.value]
99
100     def verify(self,digest,signature,**kwargs):
101         """
102             Verifies given signature on given digest
103             Returns True if Ok, False if don't match
104             Keyword arguments allows to set algorithm-specific
105             parameters
106         """
107         ctx=libcrypto.EVP_PKEY_CTX_new(self.key,None)
108         if ctx is None:
109             raise PKeyError("Initailizing verify context")
110         if libcrypto.EVP_PKEY_verify_init(ctx)<1:
111             raise PKeyError("verify_init")
112         self._configure_context(ctx,kwargs)
113         rv=libcrypto.EVP_PKEY_verify(ctx,signature,len(signature),digest,len(digest))
114         if rv<0:
115             raise PKeyError("Signature verification")
116         libcrypto.EVP_PKEY_CTX_free(ctx)
117         return rv>0
118     def derive(self,peerkey,**kwargs):
119         """
120             Derives shared key (DH,ECDH,VKO 34.10). Requires
121             private key available
122
123             @param peerkey - other key (may be public only)
124
125             Keyword parameters are algorithm-specific
126         """
127         ctx=libcrypto.EVP_PKEY_CTX_new(self.key,None)
128         if ctx is None:
129             raise PKeyError("Initailizing derive context")
130         if libcrypto.EVP_PKEY_derive_init(ctx)<1:
131             raise PKeyError("derive_init")
132
133         
134         self._configure_context(ctx,kwargs,["ukm"])
135         if libcrypto.EVP_PKEY_derive_set_peer(ctx,peerkey.key)<=0:
136             raise PKeyError("Cannot set peer key")
137         if "ukm" in kwargs:
138              if libcrypto.EVP_PKEY_CTX_ctrl(ctx,-1,1<<10,8,8,kwargs["ukm"])<=0:
139                 raise PKeyError("Cannot set UKM")
140         keylen=c_long(0)
141         if libcrypto.EVP_PKEY_derive(ctx,None,byref(keylen))<=0:
142             raise PKeyError("computing shared key length")
143         buf=create_string_buffer(keylen.value)
144         if libcrypto.EVP_PKEY_derive(ctx,buf,byref(keylen))<=0:
145             raise PKeyError("computing actual shared key")
146         libcrypto.EVP_PKEY_CTX_free(ctx)
147         return buf.raw[:keylen.value]
148     @staticmethod
149     def generate(algorithm,**kwargs):
150         """
151             Generates new private-public key pair for given algorithm
152             (string like 'rsa','ec','gost2001') and algorithm-specific
153             parameters.
154
155             Algorithm specific paramteers for RSA:
156
157             rsa_keygen_bits=number - size of key to be generated
158             rsa_keygen_pubexp - RSA public expontent(default 65537)
159
160             Algorithm specific parameters for DSA,DH and EC
161
162             paramsfrom=PKey object
163
164             copy parameters of newly generated key from existing key
165
166             Algorithm specific parameters for GOST2001
167
168             paramset= paramset name where name is one of
169             'A','B','C','XA','XB','test'
170
171             paramsfrom does work too
172         """
173         tmpeng=c_void_p(None)
174         ameth=libcrypto.EVP_PKEY_asn1_find_str(byref(tmpeng),algorithm,-1)
175         if ameth is None:
176             raise PKeyError("Algorithm %s not foind\n"%(algname))
177         clear_err_stack()
178         pkey_id=c_int(0)
179         libcrypto.EVP_PKEY_asn1_get0_info(byref(pkey_id),None,None,None,None,ameth)
180         #libcrypto.ENGINE_finish(tmpeng)
181         if "paramsfrom" in kwargs:
182             ctx=libcrypto.EVP_PKEY_CTX_new(kwargs["paramsfrom"].key,None)
183         else:
184             ctx=libcrypto.EVP_PKEY_CTX_new_id(pkey_id,None)
185         # FIXME support EC curve as keyword param by invoking paramgen
186         # operation
187         if ctx is None:
188             raise PKeyError("Creating context for key type %d"%(pkey_id.value)) 
189         if libcrypto.EVP_PKEY_keygen_init(ctx) <=0 :
190             raise PKeyError("keygen_init")
191         PKey._configure_context(ctx,kwargs,["paramsfrom"])
192         key=c_void_p(None)
193         if libcrypto.EVP_PKEY_keygen(ctx,byref(key))<=0:
194             raise PKeyError("Error generating key")
195         libcrypto.EVP_PKEY_CTX_free(ctx)
196         return PKey(ptr=key,cansign=True)
197     def exportpub(self,format="PEM"):
198         """
199             Returns public key as PEM or DER structure.
200         """
201         b=Membio()
202         if format == "PEM":
203             r=libcrypto.PEM_write_bio_PUBKEY(b.bio,self.key)
204         else:
205             r=libcrypto.i2d_PUBKEY_bio(b.bio,self.key)
206         if r==0:
207             raise PKeyError("error serializing public key")
208         return str(b)
209     def exportpriv(self,format="PEM",password=None,cipher=None):
210         """
211             Returns private key as PEM or DER Structure.
212             If password and cipher are specified, encrypts key
213             on given password, using given algorithm. Cipher must be
214             an ctypescrypto.cipher.CipherType object
215         """
216         b=Membio()
217         if cipher is None:
218             evp_cipher=None
219         else:
220             if password is None:
221                 raise NotImplementedError("Interactive password entry is not supported")
222             evp_cipher=cipher.cipher
223         if format == "PEM":
224             r=libcrypto.PEM_write_bio_PrivateKey(b.bio,self.key,evp_cipher,None,0,_cb,
225                 password)
226         else:
227             if cipher is not None:
228                 raise NotImplementedError("Der-formatted encrypted keys are not supported")
229             r=libcrypto.i2d_PrivateKey_bio(b.bio,self.key)
230         if r==0:
231             raise PKeyError("error serializing private key")
232         return str(b)
233     @staticmethod
234     def _configure_context(ctx,opts,skip=[]):
235         """
236             Configures context of public key operations
237             @param ctx - context to configure
238             @param opts - dictionary of options (from kwargs of calling
239                 function)
240             @param skip - list of options which shouldn't be passed to
241                 context
242         """
243
244         for oper in opts:
245             if oper in skip:
246                 continue
247             rv=libcrypto.EVP_PKEY_CTX_ctrl_str(ctx,oper,str(opts[oper]))
248             if rv==-2:
249                 raise PKeyError("Parameter %s is not supported by key"%(oper,))
250             if rv<1:
251                 raise PKeyError("Error setting parameter %s"%(oper,))
252 # Declare function prototypes
253 libcrypto.EVP_PKEY_cmp.argtypes=(c_void_p,c_void_p)
254 libcrypto.PEM_read_bio_PrivateKey.restype=c_void_p
255 libcrypto.PEM_read_bio_PrivateKey.argtypes=(c_void_p,POINTER(c_void_p),CALLBACK_FUNC,c_char_p) 
256 libcrypto.PEM_read_bio_PUBKEY.restype=c_void_p
257 libcrypto.PEM_read_bio_PUBKEY.argtypes=(c_void_p,POINTER(c_void_p),CALLBACK_FUNC,c_char_p)
258 libcrypto.d2i_PUBKEY_bio.restype=c_void_p
259 libcrypto.d2i_PUBKEY_bio.argtypes=(c_void_p,c_void_p)
260 libcrypto.d2i_PrivateKey_bio.restype=c_void_p
261 libcrypto.d2i_PrivateKey_bio.argtypes=(c_void_p,c_void_p)
262 libcrypto.EVP_PKEY_print_public.argtypes=(c_void_p,c_void_p,c_int,c_void_p)
263 libcrypto.EVP_PKEY_asn1_find_str.restype=c_void_p
264 libcrypto.EVP_PKEY_asn1_find_str.argtypes=(c_void_p,c_char_p,c_int)
265 libcrypto.EVP_PKEY_asn1_get0_info.restype=c_int
266 libcrypto.EVP_PKEY_asn1_get0_info.argtypes=(POINTER(c_int),POINTER(c_int),POINTER(c_int),POINTER(c_char_p), POINTER(c_char_p),c_void_p)
267 libcrypto.EVP_PKEY_cmp.restype=c_int
268 libcrypto.EVP_PKEY_cmp.argtypes=(c_void_p,c_void_p)
269 libcrypto.EVP_PKEY_CTX_ctrl_str.restype=c_int
270 libcrypto.EVP_PKEY_CTX_ctrl_str.argtypes=(c_void_p,c_void_p,c_void_p)
271 libcrypto.EVP_PKEY_CTX_ctrl.restype=c_int
272 libcrypto.EVP_PKEY_CTX_ctrl.argtypes=(c_void_p,c_int,c_int,c_int,c_int,c_void_p)
273 libcrypto.EVP_PKEY_CTX_free.argtypes=(c_void_p,)
274 libcrypto.EVP_PKEY_CTX_new.restype=c_void_p
275 libcrypto.EVP_PKEY_CTX_new.argtypes=(c_void_p,c_void_p)
276 libcrypto.EVP_PKEY_CTX_new_id.restype=c_void_p
277 libcrypto.EVP_PKEY_CTX_new_id.argtypes=(c_int,c_void_p)
278 libcrypto.EVP_PKEY_derive.restype=c_int
279 libcrypto.EVP_PKEY_derive.argtypes=(c_void_p,c_char_p,POINTER(c_long))
280 libcrypto.EVP_PKEY_derive_init.restype=c_int
281 libcrypto.EVP_PKEY_derive_init.argtypes=(c_void_p,)
282 libcrypto.EVP_PKEY_derive_set_peer.restype=c_int
283 libcrypto.EVP_PKEY_derive_set_peer.argtypes=(c_void_p,c_void_p)
284 libcrypto.EVP_PKEY_free.argtypes=(c_void_p,)
285 libcrypto.EVP_PKEY_keygen.restype=c_int
286 libcrypto.EVP_PKEY_keygen.argtypes=(c_void_p,c_void_p)
287 libcrypto.EVP_PKEY_keygen_init.restype=c_int
288 libcrypto.EVP_PKEY_keygen_init.argtypes=(c_void_p,)
289 libcrypto.EVP_PKEY_sign.restype=c_int
290 libcrypto.EVP_PKEY_sign.argtypes=(c_void_p,c_char_p,POINTER(c_long),c_char_p,c_long)
291 libcrypto.EVP_PKEY_sign_init.restype=c_int
292 libcrypto.EVP_PKEY_sign_init.argtypes=(c_void_p,)
293 libcrypto.EVP_PKEY_verify.restype=c_int
294 libcrypto.EVP_PKEY_verify.argtypes=(c_void_p,c_char_p,c_long,c_char_p,c_long)
295 libcrypto.EVP_PKEY_verify_init.restype=c_int
296 libcrypto.EVP_PKEY_verify_init.argtypes=(c_void_p,)
297 libcrypto.PEM_write_bio_PrivateKey.argtypes=(c_void_p,c_void_p,c_void_p,c_char_p,c_int,CALLBACK_FUNC,c_char_p)
298 libcrypto.PEM_write_bio_PUBKEY.argtypes=(c_void_p,c_void_p)
299 libcrypto.i2d_PUBKEY_bio.argtypes=(c_void_p,c_void_p)
300 libcrypto.i2d_PrivateKey_bio.argtypes=(c_void_p,c_void_p)
301 libcrypto.ENGINE_finish.argtypes=(c_void_p,)