]> www.wagner.pp.ru Git - oss/ctypescrypto.git/blob - ctypescrypto/pkey.py
bb995331bb8d4a757b7a9db149399e7a4a55a21e
[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
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                 for oper in kwargs:
77                         rv=libcrypto.EVP_PKEY_CTX_ctrl_str(ctx,oper,kwargs[oper])
78                         if rv==-2:
79                                 raise PKeyError("Parameter %s is not supported by key"%(oper))
80                         if rv<1:
81                                 raise PKeyError("Error setting parameter %s"(oper))
82                 # Find out signature size
83                 siglen=c_long(0)
84                 if libcrypto.EVP_PKEY_sign(ctx,None,byref(siglen),digest,len(digest))<1:
85                         raise PKeyError("signing")      
86                 sig=create_string_buffer(siglen.value)
87                 libcrypto.EVP_PKEY_sign(ctx,sig,byref(siglen),digest,len(digest))
88                 libcrypto.EVP_PKEY_CTX_free(ctx)
89                 return sig.raw[:siglen.value]
90
91         def verify(self,digest,signature,**kwargs):
92                 """
93                         Verifies given signature on given digest
94                         Returns True if Ok, False if don't match
95                 """
96                 ctx=libcrypto.EVP_PKEY_CTX_new(self.key,None)
97                 if ctx is None:
98                         raise PKeyError("Initailizing verify context")
99                 if libcrypto.EVP_PKEY_verify_init(ctx)<1:
100                         raise PKeyError("verify_init")
101                 for oper in kwargs:
102                         rv=libcrypto.EVP_PKEY_CTX_ctrl_str(ctx,oper,kwargs[oper])
103                         if rv==-2:
104                                 raise PKeyError("Parameter %s is not supported by key"%(oper))
105                         if rv<1:
106                                 raise PKeyError("Error setting parameter %s"(oper))
107                 rv=libcrypto.EVP_PKEY_verify(ctx,signature,len(signature),digest,len(digest))
108                 if rv<0:
109                         raise PKeyError("Signature verification")
110                 libcrypto.EVP_PKEY_CTX_free(ctx)
111                 return rv>0
112         def derive(self,peerkey,**kwargs):
113                 """
114                         Derives shared key (DH,ECDH,VKO 34.10). Requires
115                         private key available
116
117                         @param peerkey - other key (may be public only)
118
119                         Keyword parameters are algorithm-specific
120                 """
121                 ctx=libcrypto.EVP_PKEY_CTX_new(self.key,None)
122                 if ctx is None:
123                         raise PKeyError("Initailizing derive context")
124                 if libcrypto.EVP_PKEY_derive_init(ctx)<1:
125                         raise PKeyError("derive_init")
126                 for oper in kwargs:
127                         rv=libcrypto.EVP_PKEY_CTX_ctrl_str(ctx,oper,kwargs[oper])
128                         if rv==-2:
129                                 raise PKeyError("Parameter %s is not supported by key"%(oper))
130                         if rv<1:
131                                 raise PKeyError("Error setting parameter %s"(oper))
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         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                 tmpeng=c_void_p(None)
149                 ameth=libcrypto.EVP_PKEY_asn1_find_str(byref(tmpeng),algorithm,-1)
150                 if ameth is None:
151                         raise PKeyError("Algorithm %s not foind\n"%(algname))
152                 clear_err_stack()
153                 pkey_id=c_int(0)
154                 libcrypto.EVP_PKEY_asn1_get0_info(byref(pkey_id),None,None,None,None,ameth)
155                 libcrypto.ENGINE_finish(tmpeng)
156                 ctx=libcrypto.EVP_PKEY_CTX_new_id(pkey_id)
157                 if ctx is None:
158                         raise PKeyError("Creating context for key type %d"%(pkey_id.value)) 
159                 if libcrypto.EVP_PKEY_keygen_init(ctx) <=0 :
160                         raise PKeyError("keygen_init")
161                 for oper in kwargs:
162                         rv=libcrypto.EVP_PKEY_CTX_ctrl_str(ctx,oper,kwargs[oper])
163                         if rw==-2:
164                                 raise PKeyError("Parameter %s is not supported by key"%(oper))
165                         if rv<1:
166                                 raise PKeyError("Error setting parameter %s"(oper))
167                 key=c_void_p(None)
168                 if libcrypto.EVP_PKEY_keygen(ctx,byref(key))<=0:
169                         raise PKeyError("Error generating key")
170                 libcrypto.EVP_PKEY_CTX_free(ctx)
171                 return PKey(ptr=key,cansign=True)
172
173 # Declare function prototypes
174 libcrypto.EVP_PKEY_cmp.argtypes=(c_void_p,c_void_p)
175 libcrypto.PEM_read_bio_PrivateKey.restype=c_void_p
176 libcrypto.PEM_read_bio_PrivateKey.argtypes=(c_void_p,POINTER(c_void_p),CALLBACK_FUNC,c_char_p) 
177 libcrypto.d2i_PKCS8PrivateKey_bio.restype=c_void_p
178 libcrypto.d2i_PKCS8PrivateKey_bio.argtypes=(c_void_p,POINTER(c_void_p),CALLBACK_FUNC,c_char_p)
179 libcrypto.PEM_read_bio_PUBKEY.restype=c_void_p
180 libcrypto.PEM_read_bio_PUBKEY.argtypes=(c_void_p,POINTER(c_void_p),CALLBACK_FUNC,c_char_p)
181 libcrypto.d2i_PUBKEY_bio.restype=c_void_p
182 libcrypto.d2i_PUBKEY_bio.argtypes=(c_void_p,c_void_p)
183 libcrypto.EVP_PKEY_print_public.argtypes=(c_void_p,c_void_p,c_int,c_void_p)
184