]> www.wagner.pp.ru Git - oss/ctypescrypto.git/blob - ctypescrypto/pkey.py
56e8746ecb919201bf5fcdde568c91f3225c24e7
[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,)