2 low-level private/public keypair operation
4 PKey object of this module is wrapper around OpenSSL EVP_PKEY object.
7 This module provides interface for
9 from ctypes import c_char_p,c_void_p,byref,c_int,c_long, c_longlong, create_string_buffer,CFUNCTYPE,POINTER
10 from ctypescrypto import libcrypto
11 from ctypescrypto.exception import LibCryptoError,clear_err_stack
12 from ctypescrypto.bio import Membio
14 class PKeyError(LibCryptoError):
17 CALLBACK_FUNC=CFUNCTYPE(c_int,c_char_p,c_int,c_int,c_char_p)
18 def password_callback(buf,length,rwflag,u):
20 Example password callback for private key. Assumes that
21 password is store in the userdata parameter, so allows to pass password
22 from constructor arguments to the libcrypto keyloading functions
30 _cb=CALLBACK_FUNC(password_callback)
33 def __init__(self,ptr=None,privkey=None,pubkey=None,format="PEM",cansign=False,password=None):
37 if not privkey is None or not pubkey is None:
38 raise TypeError("Just one of pubkey or privkey can be specified")
39 elif not privkey is None:
40 if not pubkey is None:
41 raise TypeError("Just one of pubkey or privkey can be specified")
45 self.key=libcrypto.PEM_read_bio_PrivateKey(b.bio,None,_cb,c_char_p(password))
47 self.key=libcrypto.d2i_PrivateKey_bio(b.bio,None)
49 raise PKeyError("error parsing private key")
50 elif not pubkey is None:
54 self.key=libcrypto.PEM_read_bio_PUBKEY(b.bio,None,_cb,None)
56 self.key=libcrypto.d2i_PUBKEY_bio(b.bio,None)
58 raise PKeyError("error parsing public key")
60 raise TypeError("Neither public, nor private key is specified")
64 libcrypto.EVP_PKEY_free(self.key)
65 def __eq__(self,other):
66 """ Compares two public keys. If one has private key and other
67 doesn't it doesn't affect result of comparation
69 return libcrypto.EVP_PKEY_cmp(self.key,other.key)==1
70 def __ne__(self,other):
71 return not self.__eq__(other)
73 """ printable representation of public key """
75 libcrypto.EVP_PKEY_print_public(b.bio,self.key,0,None)
78 def sign(self,digest,**kwargs):
80 Signs given digest and retirns signature
81 Keyword arguments allows to set various algorithm-specific
82 parameters. See pkeyutl(1) manual.
84 ctx=libcrypto.EVP_PKEY_CTX_new(self.key,None)
86 raise PKeyError("Initailizing sign context")
87 if libcrypto.EVP_PKEY_sign_init(ctx)<1:
88 raise PKeyError("sign_init")
89 self._configure_context(ctx,kwargs)
90 # Find out signature size
92 if libcrypto.EVP_PKEY_sign(ctx,None,byref(siglen),digest,len(digest))<1:
93 raise PKeyError("signing")
94 sig=create_string_buffer(siglen.value)
95 libcrypto.EVP_PKEY_sign(ctx,sig,byref(siglen),digest,len(digest))
96 libcrypto.EVP_PKEY_CTX_free(ctx)
97 return sig.raw[:siglen.value]
99 def verify(self,digest,signature,**kwargs):
101 Verifies given signature on given digest
102 Returns True if Ok, False if don't match
103 Keyword arguments allows to set algorithm-specific
106 ctx=libcrypto.EVP_PKEY_CTX_new(self.key,None)
108 raise PKeyError("Initailizing verify context")
109 if libcrypto.EVP_PKEY_verify_init(ctx)<1:
110 raise PKeyError("verify_init")
111 self._configure_context(ctx,kwargs)
112 rv=libcrypto.EVP_PKEY_verify(ctx,signature,len(signature),digest,len(digest))
114 raise PKeyError("Signature verification")
115 libcrypto.EVP_PKEY_CTX_free(ctx)
117 def derive(self,peerkey,**kwargs):
119 Derives shared key (DH,ECDH,VKO 34.10). Requires
120 private key available
122 @param peerkey - other key (may be public only)
124 Keyword parameters are algorithm-specific
126 ctx=libcrypto.EVP_PKEY_CTX_new(self.key,None)
128 raise PKeyError("Initailizing derive context")
129 if libcrypto.EVP_PKEY_derive_init(ctx)<1:
130 raise PKeyError("derive_init")
131 self._configure_context(self,ctx,kwargs)
132 if libcrypto.EVP_PKEY_derive_set_peer(ctx,peerkey.key)<=0:
133 raise PKeyError("Cannot set peer key")
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]
143 def generate(algorithm,**kwargs):
145 Generates new private-public key pair for given algorithm
146 (string like 'rsa','ec','gost2001') and algorithm-specific
149 Algorithm specific paramteers for RSA:
151 rsa_keygen_bits=number - size of key to be generated
152 rsa_keygen_pubexp - RSA public expontent(default 65537)
154 Algorithn specific parameters for DSA,DH and EC
156 paramsfrom=PKey object
158 copy parameters of newly generated key from existing key
160 Algorithm specific parameters for GOST2001
162 paramset= paramset name where name is one of
163 'A','B','C','XA','XB','test'
165 paramsfrom does work too
167 tmpeng=c_void_p(None)
168 ameth=libcrypto.EVP_PKEY_asn1_find_str(byref(tmpeng),algorithm,-1)
170 raise PKeyError("Algorithm %s not foind\n"%(algname))
173 libcrypto.EVP_PKEY_asn1_get0_info(byref(pkey_id),None,None,None,None,ameth)
174 libcrypto.ENGINE_finish(tmpeng)
175 if "paramsfrom" in kwargs:
176 ctx=libcrypto.EVP_PKEY_CTX_new(kwargs["paramsfrom"].key,None)
178 ctx=libcrypto.EVP_PKEY_CTX_new_id(pkey_id,None)
179 # FIXME support EC curve as keyword param by invoking paramgen
182 raise PKeyError("Creating context for key type %d"%(pkey_id.value))
183 if libcrypto.EVP_PKEY_keygen_init(ctx) <=0 :
184 raise PKeyError("keygen_init")
185 PKey._configure_context(ctx,kwargs,["paramsfrom"])
187 if libcrypto.EVP_PKEY_keygen(ctx,byref(key))<=0:
188 raise PKeyError("Error generating key")
189 libcrypto.EVP_PKEY_CTX_free(ctx)
190 return PKey(ptr=key,cansign=True)
191 def exportpub(self,format="PEM"):
193 Returns public key as PEM or DER structure.
197 r=libcrypto.PEM_write_bio_PUBKEY(b.bio,self.key)
199 r=libcrypto.i2d_PUBKEY_bio(b.bio,self.key)
201 raise PKeyError("error serializing public key")
203 def exportpriv(self,format="PEM",password=None,cipher=None):
205 Returns public key as PEM or DER Structure.
206 If password and cipher are specified, encrypts key
207 on given password, using given algorithm. Cipher must be
208 an ctypescrypto.cipher.CipherType object
215 raise NotImplementedError("Interactive password entry is not supported")
216 evp_cipher=cipher.cipher
218 r=libcrypto.PEM_write_bio_PrivateKey(b.bio,self.key,evp_cipher,_cb,
221 if cipher is not None:
222 raise NotImplementedError("Der-formatted encrypted keys are not supported")
223 r=libcrypto.i2d_PrivateKey_bio(b.bio,self.key)
225 raise PKeyError("error serializing private key")
228 def _configure_context(ctx,opts,skip=[]):
230 Configures context of public key operations
231 @param ctx - context to configure
232 @param opts - dictionary of options (from kwargs of calling
234 @param skip - list of options which shouldn't be passed to
241 rv=libcrypto.EVP_PKEY_CTX_ctrl_str(ctx,oper,str(opts[oper]))
243 raise PKeyError("Parameter %s is not supported by key"%(oper))
245 raise PKeyError("Error setting parameter %s"(oper))
246 # Declare function prototypes
247 libcrypto.EVP_PKEY_cmp.argtypes=(c_void_p,c_void_p)
248 libcrypto.PEM_read_bio_PrivateKey.restype=c_void_p
249 libcrypto.PEM_read_bio_PrivateKey.argtypes=(c_void_p,POINTER(c_void_p),CALLBACK_FUNC,c_char_p)
250 libcrypto.PEM_read_bio_PUBKEY.restype=c_void_p
251 libcrypto.PEM_read_bio_PUBKEY.argtypes=(c_void_p,POINTER(c_void_p),CALLBACK_FUNC,c_char_p)
252 libcrypto.d2i_PUBKEY_bio.restype=c_void_p
253 libcrypto.d2i_PUBKEY_bio.argtypes=(c_void_p,c_void_p)
254 libcrypto.d2i_PrivateKey_bio.restype=c_void_p
255 libcrypto.d2i_PrivateKey_bio.argtypes=(c_void_p,c_void_p)
256 libcrypto.EVP_PKEY_print_public.argtypes=(c_void_p,c_void_p,c_int,c_void_p)
257 libcrypto.EVP_PKEY_asn1_find_str.restype=c_void_p
258 libcrypto.EVP_PKEY_asn1_find_str.argtypes=(c_void_p,c_char_p,c_int)
259 libcrypto.EVP_PKEY_asn1_get0_info.restype=c_int
260 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)
261 libcrypto.EVP_PKEY_cmp.restype=c_int
262 libcrypto.EVP_PKEY_cmp.argtypes=(c_void_p,c_void_p)
263 libcrypto.EVP_PKEY_CTX_ctrl_str.restype=c_int
264 libcrypto.EVP_PKEY_CTX_ctrl_str.argtypes=(c_void_p,)
265 libcrypto.EVP_PKEY_CTX_free.argtypes=(c_void_p,)
266 libcrypto.EVP_PKEY_CTX_new.restype=c_void_p
267 libcrypto.EVP_PKEY_CTX_new.argtypes=(c_void_p,c_void_p)
268 libcrypto.EVP_PKEY_CTX_new_id.restype=c_void_p
269 libcrypto.EVP_PKEY_CTX_new_id.argtypes=(c_int,c_void_p)
270 libcrypto.EVP_PKEY_derive.restype=c_int
271 libcrypto.EVP_PKEY_derive.argtypes=(c_void_p,c_char_p,POINTER(c_long))
272 libcrypto.EVP_PKEY_derive_init.restype=c_int
273 libcrypto.EVP_PKEY_derive_init.argtypes=(c_void_p,)
274 libcrypto.EVP_PKEY_derive_set_peer.restype=c_int
275 libcrypto.EVP_PKEY_derive_set_peer.argtypes=(c_void_p,c_void_p)
276 libcrypto.EVP_PKEY_free.argtypes=(c_void_p,)
277 libcrypto.EVP_PKEY_keygen.restype=c_int
278 libcrypto.EVP_PKEY_keygen.argtypes=(c_void_p,c_void_p)
279 libcrypto.EVP_PKEY_keygen_init.restype=c_int
280 libcrypto.EVP_PKEY_keygen_init.argtypes=(c_void_p,)
281 libcrypto.EVP_PKEY_sign.restype=c_int
282 libcrypto.EVP_PKEY_sign.argtypes=(c_void_p,c_char_p,POINTER(c_long),c_char_p,c_long)
283 libcrypto.EVP_PKEY_sign_init.restype=c_int
284 libcrypto.EVP_PKEY_sign_init.argtypes=(c_void_p,)
285 libcrypto.EVP_PKEY_verify.restype=c_int
286 libcrypto.EVP_PKEY_verify.argtypes=(c_void_p,c_char_p,c_long,c_char_p,c_long)
287 libcrypto.EVP_PKEY_verify_init.restype=c_int
288 libcrypto.EVP_PKEY_verify_init.argtypes=(c_void_p,)
289 libcrypto.PEM_write_bio_PrivateKey.argtypes=(c_void_p,c_void_p,CALLBACK_FUNC,c_char_p)
290 libcrypto.PEM_write_bio_PUBKEY.argtypes=(c_void_p,c_void_p)
291 libcrypto.i2d_PUBKEY_bio.argtypes=(c_void_p,c_void_p)
292 libcrypto.i2d_PrivateKey_bio.argtypes=(c_void_p,c_void_p)