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