]> www.wagner.pp.ru Git - oss/ctypescrypto.git/blob - ctypescrypto/pkey.py
85b831d96c870a8e140cecf9f88b1ee00890a6f7
[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 pubkey or privkey can be specified")
40                 elif not privkey is None:
41                         if not pubkey is None:
42                                 raise TypeError("Just one of 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                 self._configure_context(self,ctx,kwargs)
133                 if libcrypto.EVP_PKEY_derive_set_peer(ctx,peerkey.key)<=0:
134                         raise PKeyError("Cannot set peer key")
135                 keylen=c_long(0)
136                 if libcrypto.EVP_PKEY_derive(ctx,None,byref(keylen))<=0:
137                         raise PKeyError("computing shared key length")
138                 buf=create_string_buffer(keylen)
139                 if libcrypto.EVP_PKEY_derive(ctx,buf,byref(keylen))<=0:
140                         raise PKeyError("computing actual shared key")
141                 libcrypto.EVP_PKEY_CTX_free(ctx)
142                 return buf.raw[:keylen]
143         @staticmethod
144         def generate(algorithm,**kwargs):
145                 """
146                         Generates new private-public key pair for given algorithm
147                         (string like 'rsa','ec','gost2001') and algorithm-specific
148                         parameters.
149
150                         Algorithm specific paramteers for RSA:
151
152                         rsa_keygen_bits=number - size of key to be generated
153                         rsa_keygen_pubexp - RSA public expontent(default 65537)
154
155                         Algorithn specific parameters for DSA,DH and EC
156
157                         paramsfrom=PKey object
158
159                         copy parameters of newly generated key from existing key
160
161                         Algorithm specific parameters for GOST2001
162
163                         paramset= paramset name where name is one of
164                         'A','B','C','XA','XB','test'
165
166                         paramsfrom does work too
167                 """
168                 tmpeng=c_void_p(None)
169                 ameth=libcrypto.EVP_PKEY_asn1_find_str(byref(tmpeng),algorithm,-1)
170                 if ameth is None:
171                         raise PKeyError("Algorithm %s not foind\n"%(algname))
172                 clear_err_stack()
173                 pkey_id=c_int(0)
174                 libcrypto.EVP_PKEY_asn1_get0_info(byref(pkey_id),None,None,None,None,ameth)
175                 libcrypto.ENGINE_finish(tmpeng)
176                 if "paramsfrom" in kwargs:
177                         ctx=libcrypto.EVP_PKEY_CTX_new(kwargs["paramsfrom"].key,None)
178                 else:
179                         ctx=libcrypto.EVP_PKEY_CTX_new_id(pkey_id,None)
180                 # FIXME support EC curve as keyword param by invoking paramgen
181                 # operation
182                 if ctx is None:
183                         raise PKeyError("Creating context for key type %d"%(pkey_id.value)) 
184                 if libcrypto.EVP_PKEY_keygen_init(ctx) <=0 :
185                         raise PKeyError("keygen_init")
186                 PKey._configure_context(ctx,kwargs,["paramsfrom"])
187                 key=c_void_p(None)
188                 if libcrypto.EVP_PKEY_keygen(ctx,byref(key))<=0:
189                         raise PKeyError("Error generating key")
190                 libcrypto.EVP_PKEY_CTX_free(ctx)
191                 return PKey(ptr=key,cansign=True)
192         def exportpub(self,format="PEM"):
193                 """
194                         Returns public key as PEM or DER structure.
195                 """
196                 b=Membio()
197                 if format == "PEM":
198                         r=libcrypto.PEM_write_bio_PUBKEY(b.bio,self.key)
199                 else:
200                         r=libcrypto.i2d_PUBKEY_bio(b.bio,self.key)
201                 if r==0:
202                         raise PKeyError("error serializing public key")
203                 return str(b)
204         def exportpriv(self,format="PEM",password=None,cipher=None):
205                 """
206                         Returns private key as PEM or DER Structure.
207                         If password and cipher are specified, encrypts key
208                         on given password, using given algorithm. Cipher must be
209                         an ctypescrypto.cipher.CipherType object
210                 """
211                 b=Membio()
212                 if cipher is None:
213                         evp_cipher=None
214                 else:
215                         if password is None:
216                                 raise NotImplementedError("Interactive password entry is not supported")
217                         evp_cipher=cipher.cipher
218                 if format == "PEM":
219                         r=libcrypto.PEM_write_bio_PrivateKey(b.bio,self.key,evp_cipher,None,0,_cb,
220                                 password)
221                 else:
222                         if cipher is not None:
223                                 raise NotImplementedError("Der-formatted encrypted keys are not supported")
224                         r=libcrypto.i2d_PrivateKey_bio(b.bio,self.key)
225                 if r==0:
226                         raise PKeyError("error serializing private key")
227                 return str(b)
228         @staticmethod
229         def _configure_context(ctx,opts,skip=[]):
230                 """
231                         Configures context of public key operations
232                         @param ctx - context to configure
233                         @param opts - dictionary of options (from kwargs of calling
234                                 function)
235                         @param skip - list of options which shouldn't be passed to
236                                 context
237                 """
238
239                 for oper in opts:
240                         if oper in skip:
241                                 continue
242                         rv=libcrypto.EVP_PKEY_CTX_ctrl_str(ctx,oper,str(opts[oper]))
243                         if rv==-2:
244                                 raise PKeyError("Parameter %s is not supported by key"%(oper))
245                         if rv<1:
246                                 raise PKeyError("Error setting parameter %s"(oper))
247 # Declare function prototypes
248 libcrypto.EVP_PKEY_cmp.argtypes=(c_void_p,c_void_p)
249 libcrypto.PEM_read_bio_PrivateKey.restype=c_void_p
250 libcrypto.PEM_read_bio_PrivateKey.argtypes=(c_void_p,POINTER(c_void_p),CALLBACK_FUNC,c_char_p) 
251 libcrypto.PEM_read_bio_PUBKEY.restype=c_void_p
252 libcrypto.PEM_read_bio_PUBKEY.argtypes=(c_void_p,POINTER(c_void_p),CALLBACK_FUNC,c_char_p)
253 libcrypto.d2i_PUBKEY_bio.restype=c_void_p
254 libcrypto.d2i_PUBKEY_bio.argtypes=(c_void_p,c_void_p)
255 libcrypto.d2i_PrivateKey_bio.restype=c_void_p
256 libcrypto.d2i_PrivateKey_bio.argtypes=(c_void_p,c_void_p)
257 libcrypto.EVP_PKEY_print_public.argtypes=(c_void_p,c_void_p,c_int,c_void_p)
258 libcrypto.EVP_PKEY_asn1_find_str.restype=c_void_p
259 libcrypto.EVP_PKEY_asn1_find_str.argtypes=(c_void_p,c_char_p,c_int)
260 libcrypto.EVP_PKEY_asn1_get0_info.restype=c_int
261 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)
262 libcrypto.EVP_PKEY_cmp.restype=c_int
263 libcrypto.EVP_PKEY_cmp.argtypes=(c_void_p,c_void_p)
264 libcrypto.EVP_PKEY_CTX_ctrl_str.restype=c_int
265 libcrypto.EVP_PKEY_CTX_ctrl_str.argtypes=(c_void_p,)
266 libcrypto.EVP_PKEY_CTX_free.argtypes=(c_void_p,)
267 libcrypto.EVP_PKEY_CTX_new.restype=c_void_p
268 libcrypto.EVP_PKEY_CTX_new.argtypes=(c_void_p,c_void_p)
269 libcrypto.EVP_PKEY_CTX_new_id.restype=c_void_p
270 libcrypto.EVP_PKEY_CTX_new_id.argtypes=(c_int,c_void_p)
271 libcrypto.EVP_PKEY_derive.restype=c_int
272 libcrypto.EVP_PKEY_derive.argtypes=(c_void_p,c_char_p,POINTER(c_long))
273 libcrypto.EVP_PKEY_derive_init.restype=c_int
274 libcrypto.EVP_PKEY_derive_init.argtypes=(c_void_p,)
275 libcrypto.EVP_PKEY_derive_set_peer.restype=c_int
276 libcrypto.EVP_PKEY_derive_set_peer.argtypes=(c_void_p,c_void_p)
277 libcrypto.EVP_PKEY_free.argtypes=(c_void_p,)
278 libcrypto.EVP_PKEY_keygen.restype=c_int
279 libcrypto.EVP_PKEY_keygen.argtypes=(c_void_p,c_void_p)
280 libcrypto.EVP_PKEY_keygen_init.restype=c_int
281 libcrypto.EVP_PKEY_keygen_init.argtypes=(c_void_p,)
282 libcrypto.EVP_PKEY_sign.restype=c_int
283 libcrypto.EVP_PKEY_sign.argtypes=(c_void_p,c_char_p,POINTER(c_long),c_char_p,c_long)
284 libcrypto.EVP_PKEY_sign_init.restype=c_int
285 libcrypto.EVP_PKEY_sign_init.argtypes=(c_void_p,)
286 libcrypto.EVP_PKEY_verify.restype=c_int
287 libcrypto.EVP_PKEY_verify.argtypes=(c_void_p,c_char_p,c_long,c_char_p,c_long)
288 libcrypto.EVP_PKEY_verify_init.restype=c_int
289 libcrypto.EVP_PKEY_verify_init.argtypes=(c_void_p,)
290 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)
291 libcrypto.PEM_write_bio_PUBKEY.argtypes=(c_void_p,c_void_p)
292 libcrypto.i2d_PUBKEY_bio.argtypes=(c_void_p,c_void_p)
293 libcrypto.i2d_PrivateKey_bio.argtypes=(c_void_p,c_void_p)