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
13 class PKeyError(LibCryptoError):
16 CALLBACK_FUNC=CFUNCTYPE(c_int,c_char_p,c_int,c_int,c_char_p)
17 def password_callback(buf,length,rwflag,u):
19 Example password callback for private key. Assumes that
20 password is store in the userdata parameter, so allows to pass password
21 from constructor arguments to the libcrypto keyloading functions
29 _cb=CALLBACK_FUNC(password_callback)
32 def __init__(self,ptr=None,privkey=None,pubkey=None,format="PEM",cansign=False,password=None):
36 if not privkey is None or not pubkey is None:
37 raise TypeError("Just one of pubkey or privkey can be specified")
38 elif not privkey is None:
39 if not pubkey is None:
40 raise TypeError("Just one of pubkey or privkey can be specified")
44 self.key=libcrypto.PEM_read_bio_PrivateKey(b.bio,None,_cb,c_char_p(password))
46 self.key=libcrypto.d2i_PrivateKey_bio(b.bio,None)
48 raise PKeyError("error parsing private key")
49 elif not pubkey is None:
53 self.key=libcrypto.PEM_read_bio_PUBKEY(b.bio,None,_cb,None)
55 self.key=libcrypto.d2i_PUBKEY_bio(b.bio,None)
57 raise PKeyError("error parsing public key")
59 raise TypeError("Neither public, nor private key is specified")
63 libcrypto.EVP_PKEY_free(self.key)
64 def __eq__(self,other):
65 """ Compares two public keys. If one has private key and other
66 doesn't it doesn't affect result of comparation
68 return libcrypto.EVP_PKEY_cmp(self.key,other.key)==1
69 def __ne__(self,other):
70 return not self.__eq__(other)
72 """ printable representation of public key """
74 libcrypto.EVP_PKEY_print_public(b.bio,self.key,0,None)
77 def sign(self,digest,**kwargs):
79 Signs given digest and retirns signature
80 Keyword arguments allows to set various algorithm-specific
81 parameters. See pkeyutl(1) manual.
83 ctx=libcrypto.EVP_PKEY_CTX_new(self.key,None)
85 raise PKeyError("Initailizing sign context")
86 if libcrypto.EVP_PKEY_sign_init(ctx)<1:
87 raise PKeyError("sign_init")
88 self._configure_context(ctx,kwargs)
89 # Find out signature size
91 if libcrypto.EVP_PKEY_sign(ctx,None,byref(siglen),digest,len(digest))<1:
92 raise PKeyError("signing")
93 sig=create_string_buffer(siglen.value)
94 libcrypto.EVP_PKEY_sign(ctx,sig,byref(siglen),digest,len(digest))
95 libcrypto.EVP_PKEY_CTX_free(ctx)
96 return sig.raw[:siglen.value]
98 def verify(self,digest,signature,**kwargs):
100 Verifies given signature on given digest
101 Returns True if Ok, False if don't match
102 Keyword arguments allows to set algorithm-specific
105 ctx=libcrypto.EVP_PKEY_CTX_new(self.key,None)
107 raise PKeyError("Initailizing verify context")
108 if libcrypto.EVP_PKEY_verify_init(ctx)<1:
109 raise PKeyError("verify_init")
110 self._configure_context(ctx,kwargs)
111 rv=libcrypto.EVP_PKEY_verify(ctx,signature,len(signature),digest,len(digest))
113 raise PKeyError("Signature verification")
114 libcrypto.EVP_PKEY_CTX_free(ctx)
116 def derive(self,peerkey,**kwargs):
118 Derives shared key (DH,ECDH,VKO 34.10). Requires
119 private key available
121 @param peerkey - other key (may be public only)
123 Keyword parameters are algorithm-specific
125 ctx=libcrypto.EVP_PKEY_CTX_new(self.key,None)
127 raise PKeyError("Initailizing derive context")
128 if libcrypto.EVP_PKEY_derive_init(ctx)<1:
129 raise PKeyError("derive_init")
130 self._configure_context(self,ctx,kwargs)
131 if libcrypto.EVP_PKEY_derive_set_peer(ctx,peerkey.key)<=0:
132 raise PKeyError("Cannot set peer key")
134 if libcrypto.EVP_PKEY_derive(ctx,None,byref(keylen))<=0:
135 raise PKeyError("computing shared key length")
136 buf=create_string_buffer(keylen)
137 if libcrypto.EVP_PKEY_derive(ctx,buf,byref(keylen))<=0:
138 raise PKeyError("computing actual shared key")
139 libcrypto.EVP_PKEY_CTX_free(ctx)
140 return buf.raw[:keylen]
142 def generate(algorithm,**kwargs):
144 Generates new private-public key pair for given algorithm
145 (string like 'rsa','ec','gost2001') and algorithm-specific
148 Algorithm specific paramteers for RSA:
150 rsa_keygen_bits=number - size of key to be generated
151 rsa_keygen_pubexp - RSA public expontent(default 65537)
153 Algorithn specific parameters for DSA,DH and EC
155 paramsfrom=PKey object
157 copy parameters of newly generated key from existing key
159 Algorithm specific parameters for GOST2001
161 paramset= paramset name where name is one of
162 'A','B','C','XA','XB','test'
164 paramsfrom does work too
166 tmpeng=c_void_p(None)
167 ameth=libcrypto.EVP_PKEY_asn1_find_str(byref(tmpeng),algorithm,-1)
169 raise PKeyError("Algorithm %s not foind\n"%(algname))
172 libcrypto.EVP_PKEY_asn1_get0_info(byref(pkey_id),None,None,None,None,ameth)
173 libcrypto.ENGINE_finish(tmpeng)
174 if "paramsfrom" in kwargs:
175 ctx=libcrypto.EVP_PKEY_CTX_new(kwargs["paramsfrom"].key,None)
177 ctx=libcrypto.EVP_PKEY_CTX_new_id(pkey_id,None)
178 # FIXME support EC curve as keyword param by invoking paramgen
181 raise PKeyError("Creating context for key type %d"%(pkey_id.value))
182 if libcrypto.EVP_PKEY_keygen_init(ctx) <=0 :
183 raise PKeyError("keygen_init")
184 PKey._configure_context(ctx,kwargs,["paramsfrom"])
186 if libcrypto.EVP_PKEY_keygen(ctx,byref(key))<=0:
187 raise PKeyError("Error generating key")
188 libcrypto.EVP_PKEY_CTX_free(ctx)
189 return PKey(ptr=key,cansign=True)
190 def exportpub(self,format="PEM"):
192 Returns public key as PEM or DER structure.
196 r=libcrypto.PEM_write_bio_PUBKEY(b.bio,self.key)
198 r=libcrypto.i2d_PUBKEY_bio(b.bio,self.key)
200 raise PKeyError("error serializing public key")
202 def exportpriv(self,format="PEM",password=None,cipher=None):
204 Returns private key as PEM or DER Structure.
205 If password and cipher are specified, encrypts key
206 on given password, using given algorithm. Cipher must be
207 an ctypescrypto.cipher.CipherType object
214 raise NotImplementedError("Interactive password entry is not supported")
215 evp_cipher=cipher.cipher
217 r=libcrypto.PEM_write_bio_PrivateKey(b.bio,self.key,evp_cipher,None,0,_cb,
220 if cipher is not None:
221 raise NotImplementedError("Der-formatted encrypted keys are not supported")
222 r=libcrypto.i2d_PrivateKey_bio(b.bio,self.key)
224 raise PKeyError("error serializing private key")
227 def _configure_context(ctx,opts,skip=[]):
229 Configures context of public key operations
230 @param ctx - context to configure
231 @param opts - dictionary of options (from kwargs of calling
233 @param skip - list of options which shouldn't be passed to
240 rv=libcrypto.EVP_PKEY_CTX_ctrl_str(ctx,oper,str(opts[oper]))
242 raise PKeyError("Parameter %s is not supported by key"%(oper))
244 raise PKeyError("Error setting parameter %s"(oper))
245 # Declare function prototypes
246 libcrypto.EVP_PKEY_cmp.argtypes=(c_void_p,c_void_p)
247 libcrypto.PEM_read_bio_PrivateKey.restype=c_void_p
248 libcrypto.PEM_read_bio_PrivateKey.argtypes=(c_void_p,POINTER(c_void_p),CALLBACK_FUNC,c_char_p)
249 libcrypto.PEM_read_bio_PUBKEY.restype=c_void_p
250 libcrypto.PEM_read_bio_PUBKEY.argtypes=(c_void_p,POINTER(c_void_p),CALLBACK_FUNC,c_char_p)
251 libcrypto.d2i_PUBKEY_bio.restype=c_void_p
252 libcrypto.d2i_PUBKEY_bio.argtypes=(c_void_p,c_void_p)
253 libcrypto.d2i_PrivateKey_bio.restype=c_void_p
254 libcrypto.d2i_PrivateKey_bio.argtypes=(c_void_p,c_void_p)
255 libcrypto.EVP_PKEY_print_public.argtypes=(c_void_p,c_void_p,c_int,c_void_p)
256 libcrypto.EVP_PKEY_asn1_find_str.restype=c_void_p
257 libcrypto.EVP_PKEY_asn1_find_str.argtypes=(c_void_p,c_char_p,c_int)
258 libcrypto.EVP_PKEY_asn1_get0_info.restype=c_int
259 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)
260 libcrypto.EVP_PKEY_cmp.restype=c_int
261 libcrypto.EVP_PKEY_cmp.argtypes=(c_void_p,c_void_p)
262 libcrypto.EVP_PKEY_CTX_ctrl_str.restype=c_int
263 libcrypto.EVP_PKEY_CTX_ctrl_str.argtypes=(c_void_p,)
264 libcrypto.EVP_PKEY_CTX_free.argtypes=(c_void_p,)
265 libcrypto.EVP_PKEY_CTX_new.restype=c_void_p
266 libcrypto.EVP_PKEY_CTX_new.argtypes=(c_void_p,c_void_p)
267 libcrypto.EVP_PKEY_CTX_new_id.restype=c_void_p
268 libcrypto.EVP_PKEY_CTX_new_id.argtypes=(c_int,c_void_p)
269 libcrypto.EVP_PKEY_derive.restype=c_int
270 libcrypto.EVP_PKEY_derive.argtypes=(c_void_p,c_char_p,POINTER(c_long))
271 libcrypto.EVP_PKEY_derive_init.restype=c_int
272 libcrypto.EVP_PKEY_derive_init.argtypes=(c_void_p,)
273 libcrypto.EVP_PKEY_derive_set_peer.restype=c_int
274 libcrypto.EVP_PKEY_derive_set_peer.argtypes=(c_void_p,c_void_p)
275 libcrypto.EVP_PKEY_free.argtypes=(c_void_p,)
276 libcrypto.EVP_PKEY_keygen.restype=c_int
277 libcrypto.EVP_PKEY_keygen.argtypes=(c_void_p,c_void_p)
278 libcrypto.EVP_PKEY_keygen_init.restype=c_int
279 libcrypto.EVP_PKEY_keygen_init.argtypes=(c_void_p,)
280 libcrypto.EVP_PKEY_sign.restype=c_int
281 libcrypto.EVP_PKEY_sign.argtypes=(c_void_p,c_char_p,POINTER(c_long),c_char_p,c_long)
282 libcrypto.EVP_PKEY_sign_init.restype=c_int
283 libcrypto.EVP_PKEY_sign_init.argtypes=(c_void_p,)
284 libcrypto.EVP_PKEY_verify.restype=c_int
285 libcrypto.EVP_PKEY_verify.argtypes=(c_void_p,c_char_p,c_long,c_char_p,c_long)
286 libcrypto.EVP_PKEY_verify_init.restype=c_int
287 libcrypto.EVP_PKEY_verify_init.argtypes=(c_void_p,)
288 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)
289 libcrypto.PEM_write_bio_PUBKEY.argtypes=(c_void_p,c_void_p)
290 libcrypto.i2d_PUBKEY_bio.argtypes=(c_void_p,c_void_p)
291 libcrypto.i2d_PrivateKey_bio.argtypes=(c_void_p,c_void_p)