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
6 class PKeyError(LibCryptoError):
9 CALLBACK_FUNC=CFUNCTYPE(c_int,c_char_p,c_int,c_int,c_char_p)
10 def password_callback(buf,length,rwflag,u):
17 _cb=CALLBACK_FUNC(password_callback)
20 def __init__(self,ptr=None,privkey=None,pubkey=None,format="PEM",cansign=False,password=None):
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")
32 self.key=libcrypto.PEM_read_bio_PrivateKey(b.bio,None,_cb,c_char_p(password))
34 self.key=libcrypto.d2i_PrivateKey_bio(b.bio,None)
36 raise PKeyError("error parsing private key")
37 elif not pubkey is None:
41 self.key=libcrypto.PEM_read_bio_PUBKEY(b.bio,None,_cb,None)
43 self.key=libcrypto.d2i_PUBKEY_bio(b.bio,None)
45 raise PKeyError("error parsing public key")
47 raise TypeError("Neither public, nor private key is specified")
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
56 return libcrypto.EVP_PKEY_cmp(self.key,other.key)==1
57 def __ne__(self,other):
58 return not self.__eq__(other)
60 """ printable representation of public key """
62 libcrypto.EVP_PKEY_print_public(b.bio,self.key,0,None)
65 def sign(self,digest,**kwargs):
67 Signs given digest and retirns signature
68 Keyword arguments allows to set various algorithm-specific
69 parameters. See pkeyutl(1) manual.
71 ctx=libcrypto.EVP_PKEY_CTX_new(self.key,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
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]
86 def verify(self,digest,signature,**kwargs):
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
93 ctx=libcrypto.EVP_PKEY_CTX_new(self.key,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))
101 raise PKeyError("Signature verification")
102 libcrypto.EVP_PKEY_CTX_free(ctx)
104 def derive(self,peerkey,**kwargs):
106 Derives shared key (DH,ECDH,VKO 34.10). Requires
107 private key available
109 @param peerkey - other key (may be public only)
111 Keyword parameters are algorithm-specific
113 ctx=libcrypto.EVP_PKEY_CTX_new(self.key,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")
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]
130 def generate(algorithm,**kwargs):
132 Generates new private-public key pair for given algorithm
133 (string like 'rsa','ec','gost2001') and algorithm-specific
136 Algorithm specific paramteers for RSA:
138 rsa_keygen_bits=number - size of key to be generated
139 rsa_keygen_pubexp - RSA public expontent(default 65537)
141 Algorithn specific parameters for DSA,DH and EC
143 paramsfrom=PKey object
145 copy parameters of newly generated key from existing key
147 Algorithm specific parameters for GOST2001
149 paramset= paramset name where name is one of
150 'A','B','C','XA','XB','test'
152 paramsfrom does work too
154 tmpeng=c_void_p(None)
155 ameth=libcrypto.EVP_PKEY_asn1_find_str(byref(tmpeng),algorithm,-1)
157 raise PKeyError("Algorithm %s not foind\n"%(algname))
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)
165 ctx=libcrypto.EVP_PKEY_CTX_new_id(pkey_id,None)
166 # FIXME support EC curve as keyword param by invoking paramgen
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"])
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"):
180 Returns public key as PEM or DER structure.
184 r=libcrypto.PEM_write_bio_PUBKEY(b.bio,self.key)
186 r=libcrypto.i2d_PUBKEY_bio(b.bio,self.key)
188 raise PKeyError("error serializing public key")
190 def exportpriv(self,format="PEM",password=None,cipher=None):
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
202 raise NotImplementedError("Interactive password entry is not supported")
203 evp_cipher=cipher.cipher
205 r=libcrypto.PEM_write_bio_PrivateKey(b.bio,self.key,evp_cipher,_cb,
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)
212 raise PKeyError("error serializing private key")
215 def _configure_context(ctx,opts,skip=[]):
217 Configures context of public key operations
218 @param ctx - context to configure
219 @param opts - dictionary of options (from kwargs of calling
221 @param skip - list of options which shouldn't be passed to
228 rv=libcrypto.EVP_PKEY_CTX_ctrl_str(ctx,oper,str(opts[oper]))
230 raise PKeyError("Parameter %s is not supported by key"%(oper))
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)