2 This module provides interface for low-level private/public keypair operation
4 PKey object of this module is wrapper around OpenSSL EVP_PKEY object.
8 from ctypes import c_char_p,c_void_p,byref,c_int,c_long, c_longlong, create_string_buffer,CFUNCTYPE,POINTER
9 from ctypescrypto import libcrypto
10 from ctypescrypto.exception import LibCryptoError,clear_err_stack
11 from ctypescrypto.bio import Membio
14 __all__ = ['PKeyError','password_callback','PKey']
15 class PKeyError(LibCryptoError):
18 CALLBACK_FUNC=CFUNCTYPE(c_int,c_char_p,c_int,c_int,c_char_p)
19 def password_callback(buf,length,rwflag,u):
21 Example password callback for private key. Assumes that
22 password is store in the userdata parameter, so allows to pass password
23 from constructor arguments to the libcrypto keyloading functions
31 _cb=CALLBACK_FUNC(password_callback)
34 def __init__(self,ptr=None,privkey=None,pubkey=None,format="PEM",cansign=False,password=None):
38 if not privkey is None or not pubkey is None:
39 raise TypeError("Just one of ptr, pubkey or privkey can be specified")
40 elif not privkey is None:
41 if not pubkey is None:
42 raise TypeError("Just one of ptr, pubkey or privkey can be specified")
46 self.key=libcrypto.PEM_read_bio_PrivateKey(b.bio,None,_cb,c_char_p(password))
48 self.key=libcrypto.d2i_PrivateKey_bio(b.bio,None)
50 raise PKeyError("error parsing private key")
51 elif not pubkey is None:
55 self.key=libcrypto.PEM_read_bio_PUBKEY(b.bio,None,_cb,None)
57 self.key=libcrypto.d2i_PUBKEY_bio(b.bio,None)
59 raise PKeyError("error parsing public key")
61 raise TypeError("Neither public, nor private key is specified")
65 libcrypto.EVP_PKEY_free(self.key)
66 def __eq__(self,other):
67 """ Compares two public keys. If one has private key and other
68 doesn't it doesn't affect result of comparation
70 return libcrypto.EVP_PKEY_cmp(self.key,other.key)==1
71 def __ne__(self,other):
72 return not self.__eq__(other)
74 """ printable representation of public key """
76 libcrypto.EVP_PKEY_print_public(b.bio,self.key,0,None)
79 def sign(self,digest,**kwargs):
81 Signs given digest and retirns signature
82 Keyword arguments allows to set various algorithm-specific
83 parameters. See pkeyutl(1) manual.
85 ctx=libcrypto.EVP_PKEY_CTX_new(self.key,None)
87 raise PKeyError("Initailizing sign context")
88 if libcrypto.EVP_PKEY_sign_init(ctx)<1:
89 raise PKeyError("sign_init")
90 self._configure_context(ctx,kwargs)
91 # Find out signature size
93 if libcrypto.EVP_PKEY_sign(ctx,None,byref(siglen),digest,len(digest))<1:
94 raise PKeyError("signing")
95 sig=create_string_buffer(siglen.value)
96 libcrypto.EVP_PKEY_sign(ctx,sig,byref(siglen),digest,len(digest))
97 libcrypto.EVP_PKEY_CTX_free(ctx)
98 return sig.raw[:siglen.value]
100 def verify(self,digest,signature,**kwargs):
102 Verifies given signature on given digest
103 Returns True if Ok, False if don't match
104 Keyword arguments allows to set algorithm-specific
107 ctx=libcrypto.EVP_PKEY_CTX_new(self.key,None)
109 raise PKeyError("Initailizing verify context")
110 if libcrypto.EVP_PKEY_verify_init(ctx)<1:
111 raise PKeyError("verify_init")
112 self._configure_context(ctx,kwargs)
113 rv=libcrypto.EVP_PKEY_verify(ctx,signature,len(signature),digest,len(digest))
115 raise PKeyError("Signature verification")
116 libcrypto.EVP_PKEY_CTX_free(ctx)
118 def derive(self,peerkey,**kwargs):
120 Derives shared key (DH,ECDH,VKO 34.10). Requires
121 private key available
123 @param peerkey - other key (may be public only)
125 Keyword parameters are algorithm-specific
127 ctx=libcrypto.EVP_PKEY_CTX_new(self.key,None)
129 raise PKeyError("Initailizing derive context")
130 if libcrypto.EVP_PKEY_derive_init(ctx)<1:
131 raise PKeyError("derive_init")
134 self._configure_context(self,ctx,kwargs,["ukm"])
135 if libcrypto.EVP_PKEY_derive_set_peer(ctx,peerkey.key)<=0:
136 raise PKeyError("Cannot set peer key")
138 if libcrypto.EVP_PKEY_CTX_ctrl(ctx,-1,1<<10,8,0,kwargs[ukm])<=0:
139 raise PKeyError("Cannot set UKM")
141 if libcrypto.EVP_PKEY_derive(ctx,None,byref(keylen))<=0:
142 raise PKeyError("computing shared key length")
143 buf=create_string_buffer(keylen)
144 if libcrypto.EVP_PKEY_derive(ctx,buf,byref(keylen))<=0:
145 raise PKeyError("computing actual shared key")
146 libcrypto.EVP_PKEY_CTX_free(ctx)
147 return buf.raw[:keylen]
149 def generate(algorithm,**kwargs):
151 Generates new private-public key pair for given algorithm
152 (string like 'rsa','ec','gost2001') and algorithm-specific
155 Algorithm specific paramteers for RSA:
157 rsa_keygen_bits=number - size of key to be generated
158 rsa_keygen_pubexp - RSA public expontent(default 65537)
160 Algorithm specific parameters for DSA,DH and EC
162 paramsfrom=PKey object
164 copy parameters of newly generated key from existing key
166 Algorithm specific parameters for GOST2001
168 paramset= paramset name where name is one of
169 'A','B','C','XA','XB','test'
171 paramsfrom does work too
173 tmpeng=c_void_p(None)
174 ameth=libcrypto.EVP_PKEY_asn1_find_str(byref(tmpeng),algorithm,-1)
176 raise PKeyError("Algorithm %s not foind\n"%(algname))
179 libcrypto.EVP_PKEY_asn1_get0_info(byref(pkey_id),None,None,None,None,ameth)
180 #libcrypto.ENGINE_finish(tmpeng)
181 if "paramsfrom" in kwargs:
182 ctx=libcrypto.EVP_PKEY_CTX_new(kwargs["paramsfrom"].key,None)
184 ctx=libcrypto.EVP_PKEY_CTX_new_id(pkey_id,None)
185 # FIXME support EC curve as keyword param by invoking paramgen
188 raise PKeyError("Creating context for key type %d"%(pkey_id.value))
189 if libcrypto.EVP_PKEY_keygen_init(ctx) <=0 :
190 raise PKeyError("keygen_init")
191 PKey._configure_context(ctx,kwargs,["paramsfrom"])
193 if libcrypto.EVP_PKEY_keygen(ctx,byref(key))<=0:
194 raise PKeyError("Error generating key")
195 libcrypto.EVP_PKEY_CTX_free(ctx)
196 return PKey(ptr=key,cansign=True)
197 def exportpub(self,format="PEM"):
199 Returns public key as PEM or DER structure.
203 r=libcrypto.PEM_write_bio_PUBKEY(b.bio,self.key)
205 r=libcrypto.i2d_PUBKEY_bio(b.bio,self.key)
207 raise PKeyError("error serializing public key")
209 def exportpriv(self,format="PEM",password=None,cipher=None):
211 Returns private key as PEM or DER Structure.
212 If password and cipher are specified, encrypts key
213 on given password, using given algorithm. Cipher must be
214 an ctypescrypto.cipher.CipherType object
221 raise NotImplementedError("Interactive password entry is not supported")
222 evp_cipher=cipher.cipher
224 r=libcrypto.PEM_write_bio_PrivateKey(b.bio,self.key,evp_cipher,None,0,_cb,
227 if cipher is not None:
228 raise NotImplementedError("Der-formatted encrypted keys are not supported")
229 r=libcrypto.i2d_PrivateKey_bio(b.bio,self.key)
231 raise PKeyError("error serializing private key")
234 def _configure_context(ctx,opts,skip=[]):
236 Configures context of public key operations
237 @param ctx - context to configure
238 @param opts - dictionary of options (from kwargs of calling
240 @param skip - list of options which shouldn't be passed to
247 rv=libcrypto.EVP_PKEY_CTX_ctrl_str(ctx,oper,str(opts[oper]))
249 raise PKeyError("Parameter %s is not supported by key"%(oper,))
251 raise PKeyError("Error setting parameter %s"%(oper,))
252 # Declare function prototypes
253 libcrypto.EVP_PKEY_cmp.argtypes=(c_void_p,c_void_p)
254 libcrypto.PEM_read_bio_PrivateKey.restype=c_void_p
255 libcrypto.PEM_read_bio_PrivateKey.argtypes=(c_void_p,POINTER(c_void_p),CALLBACK_FUNC,c_char_p)
256 libcrypto.PEM_read_bio_PUBKEY.restype=c_void_p
257 libcrypto.PEM_read_bio_PUBKEY.argtypes=(c_void_p,POINTER(c_void_p),CALLBACK_FUNC,c_char_p)
258 libcrypto.d2i_PUBKEY_bio.restype=c_void_p
259 libcrypto.d2i_PUBKEY_bio.argtypes=(c_void_p,c_void_p)
260 libcrypto.d2i_PrivateKey_bio.restype=c_void_p
261 libcrypto.d2i_PrivateKey_bio.argtypes=(c_void_p,c_void_p)
262 libcrypto.EVP_PKEY_print_public.argtypes=(c_void_p,c_void_p,c_int,c_void_p)
263 libcrypto.EVP_PKEY_asn1_find_str.restype=c_void_p
264 libcrypto.EVP_PKEY_asn1_find_str.argtypes=(c_void_p,c_char_p,c_int)
265 libcrypto.EVP_PKEY_asn1_get0_info.restype=c_int
266 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)
267 libcrypto.EVP_PKEY_cmp.restype=c_int
268 libcrypto.EVP_PKEY_cmp.argtypes=(c_void_p,c_void_p)
269 libcrypto.EVP_PKEY_CTX_ctrl_str.restype=c_int
270 libcrypto.EVP_PKEY_CTX_ctrl_str.argtypes=(c_void_p,c_void_p,c_void_p)
271 libcrypto.EVP_PKEY_CTX_ctrl.restype=c_int
272 libcrypto.EVP_PKEY_CTX_ctrl.argtypes=(c_void_p,c_int,c_int,c_int,c_int,c_void_p)
273 libcrypto.EVP_PKEY_CTX_free.argtypes=(c_void_p,)
274 libcrypto.EVP_PKEY_CTX_new.restype=c_void_p
275 libcrypto.EVP_PKEY_CTX_new.argtypes=(c_void_p,c_void_p)
276 libcrypto.EVP_PKEY_CTX_new_id.restype=c_void_p
277 libcrypto.EVP_PKEY_CTX_new_id.argtypes=(c_int,c_void_p)
278 libcrypto.EVP_PKEY_derive.restype=c_int
279 libcrypto.EVP_PKEY_derive.argtypes=(c_void_p,c_char_p,POINTER(c_long))
280 libcrypto.EVP_PKEY_derive_init.restype=c_int
281 libcrypto.EVP_PKEY_derive_init.argtypes=(c_void_p,)
282 libcrypto.EVP_PKEY_derive_set_peer.restype=c_int
283 libcrypto.EVP_PKEY_derive_set_peer.argtypes=(c_void_p,c_void_p)
284 libcrypto.EVP_PKEY_free.argtypes=(c_void_p,)
285 libcrypto.EVP_PKEY_keygen.restype=c_int
286 libcrypto.EVP_PKEY_keygen.argtypes=(c_void_p,c_void_p)
287 libcrypto.EVP_PKEY_keygen_init.restype=c_int
288 libcrypto.EVP_PKEY_keygen_init.argtypes=(c_void_p,)
289 libcrypto.EVP_PKEY_sign.restype=c_int
290 libcrypto.EVP_PKEY_sign.argtypes=(c_void_p,c_char_p,POINTER(c_long),c_char_p,c_long)
291 libcrypto.EVP_PKEY_sign_init.restype=c_int
292 libcrypto.EVP_PKEY_sign_init.argtypes=(c_void_p,)
293 libcrypto.EVP_PKEY_verify.restype=c_int
294 libcrypto.EVP_PKEY_verify.argtypes=(c_void_p,c_char_p,c_long,c_char_p,c_long)
295 libcrypto.EVP_PKEY_verify_init.restype=c_int
296 libcrypto.EVP_PKEY_verify_init.argtypes=(c_void_p,)
297 libcrypto.PEM_write_bio_PrivateKey.argtypes=(c_void_p,c_void_p,c_void_p,c_char_p,c_int,CALLBACK_FUNC,c_char_p)
298 libcrypto.PEM_write_bio_PUBKEY.argtypes=(c_void_p,c_void_p)
299 libcrypto.i2d_PUBKEY_bio.argtypes=(c_void_p,c_void_p)
300 libcrypto.i2d_PrivateKey_bio.argtypes=(c_void_p,c_void_p)
301 libcrypto.ENGINE_finish.argtypes=(c_void_p,)