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