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