]> www.wagner.pp.ru Git - oss/ctypescrypto.git/blob - ctypescrypto/pkey.py
a561ddf23df08bbb7daea0e777ffe00c690b9595
[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         @staticmethod
179         def _configure_context(ctx,opts,skip=[]):
180                 """
181                         Configures context of public key operations
182                         @param ctx - context to configure
183                         @param opts - dictionary of options (from kwargs of calling
184                                 function)
185                         @param skip - list of options which shouldn't be passed to
186                                 context
187                 """
188
189                 for oper in opts:
190                         if oper in skip:
191                                 continue
192                         rv=libcrypto.EVP_PKEY_CTX_ctrl_str(ctx,oper,str(opts[oper]))
193                         if rv==-2:
194                                 raise PKeyError("Parameter %s is not supported by key"%(oper))
195                         if rv<1:
196                                 raise PKeyError("Error setting parameter %s"(oper))
197 # Declare function prototypes
198 libcrypto.EVP_PKEY_cmp.argtypes=(c_void_p,c_void_p)
199 libcrypto.PEM_read_bio_PrivateKey.restype=c_void_p
200 libcrypto.PEM_read_bio_PrivateKey.argtypes=(c_void_p,POINTER(c_void_p),CALLBACK_FUNC,c_char_p) 
201 libcrypto.PEM_read_bio_PUBKEY.restype=c_void_p
202 libcrypto.PEM_read_bio_PUBKEY.argtypes=(c_void_p,POINTER(c_void_p),CALLBACK_FUNC,c_char_p)
203 libcrypto.d2i_PUBKEY_bio.restype=c_void_p
204 libcrypto.d2i_PUBKEY_bio.argtypes=(c_void_p,c_void_p)
205 libcrypto.d2i_PrivateKey_bio.restype=c_void_p
206 libcrypto.d2i_PrivateKey_bio.argtypes=(c_void_p,c_void_p)
207 libcrypto.EVP_PKEY_print_public.argtypes=(c_void_p,c_void_p,c_int,c_void_p)
208 libcrypto.EVP_PKEY_asn1_find_str.restype=c_void_p
209 libcrypto.EVP_PKEY_asn1_find_str.argtypes=(c_void_p,c_char_p,c_int)
210 libcrypto.EVP_PKEY_asn1_get0_info.restype=c_int
211 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)
212 libcrypto.EVP_PKEY_cmp.restype=c_int
213 libcrypto.EVP_PKEY_cmp.argtypes=(c_void_p,c_void_p)
214 libcrypto.EVP_PKEY_CTX_ctrl_str.restype=c_int
215 libcrypto.EVP_PKEY_CTX_ctrl_str.argtypes=(c_void_p,)
216 libcrypto.EVP_PKEY_CTX_free.argtypes=(c_void_p,)
217 libcrypto.EVP_PKEY_CTX_new.restype=c_void_p
218 libcrypto.EVP_PKEY_CTX_new.argtypes=(c_void_p,c_void_p)
219 libcrypto.EVP_PKEY_CTX_new_id.restype=c_void_p
220 libcrypto.EVP_PKEY_CTX_new_id.argtypes=(c_int,c_void_p)
221 libcrypto.EVP_PKEY_derive.restype=c_int
222 libcrypto.EVP_PKEY_derive.argtypes=(c_void_p,c_char_p,POINTER(c_long))
223 libcrypto.EVP_PKEY_derive_init.restype=c_int
224 libcrypto.EVP_PKEY_derive_init.argtypes=(c_void_p,)
225 libcrypto.EVP_PKEY_derive_set_peer.restype=c_int
226 libcrypto.EVP_PKEY_derive_set_peer.argtypes=(c_void_p,c_void_p)
227 libcrypto.EVP_PKEY_free.argtypes=(c_void_p,)
228 libcrypto.EVP_PKEY_keygen.restype=c_int
229 libcrypto.EVP_PKEY_keygen.argtypes=(c_void_p,c_void_p)
230 libcrypto.EVP_PKEY_keygen_init.restype=c_int
231 libcrypto.EVP_PKEY_keygen_init.argtypes=(c_void_p,)
232 libcrypto.EVP_PKEY_sign.restype=c_int
233 libcrypto.EVP_PKEY_sign.argtypes=(c_void_p,c_char_p,POINTER(c_long),c_char_p,c_long)
234 libcrypto.EVP_PKEY_sign_init.restype=c_int
235 libcrypto.EVP_PKEY_sign_init.argtypes=(c_void_p,)
236 libcrypto.EVP_PKEY_verify.restype=c_int
237 libcrypto.EVP_PKEY_verify.argtypes=(c_void_p,c_char_p,c_long,c_char_p,c_long)
238 libcrypto.EVP_PKEY_verify_init.restype=c_int
239 libcrypto.EVP_PKEY_verify_init.argtypes=(c_void_p,)
240