X-Git-Url: https://www.wagner.pp.ru/gitweb/?a=blobdiff_plain;f=ctypescrypto%2Fpkey.py;h=af31a6725bc5ed04726ee7c401bed94e3ee331e6;hb=06ce3387113bc54ba6dcd24095d29e2a0010e0e4;hp=1e7078463820f9d9011ba4526fddf4ad888e6057;hpb=954b6dc9e3312f8d8b49f20f8466e6d2a8342f35;p=oss%2Fctypescrypto.git diff --git a/ctypescrypto/pkey.py b/ctypescrypto/pkey.py index 1e70784..af31a67 100644 --- a/ctypescrypto/pkey.py +++ b/ctypescrypto/pkey.py @@ -5,297 +5,407 @@ PKey object of this module is wrapper around OpenSSL EVP_PKEY object. """ -from ctypes import c_char_p,c_void_p,byref,c_int,c_long, c_longlong, create_string_buffer,CFUNCTYPE,POINTER +from ctypes import c_char, c_char_p, c_void_p, c_int, c_long, POINTER +from ctypes import create_string_buffer, byref, memmove, CFUNCTYPE from ctypescrypto import libcrypto -from ctypescrypto.exception import LibCryptoError,clear_err_stack +from ctypescrypto.exception import LibCryptoError, clear_err_stack from ctypescrypto.bio import Membio -import sys -__all__ = ['PKeyError','password_callback','PKey'] +__all__ = ['PKeyError', 'PKey', 'PW_CALLBACK_FUNC'] class PKeyError(LibCryptoError): + """ Exception thrown if libcrypto finctions return an error """ pass -CALLBACK_FUNC=CFUNCTYPE(c_int,c_char_p,c_int,c_int,c_char_p) -def password_callback(buf,length,rwflag,u): +PW_CALLBACK_FUNC = CFUNCTYPE(c_int, POINTER(c_char), c_int, c_int, c_char_p) +""" Function type for pem password callback """ + +def _password_callback(c): """ - Example password callback for private key. Assumes that - password is store in the userdata parameter, so allows to pass password - from constructor arguments to the libcrypto keyloading functions + Converts given user function or string to C password callback + function, passable to openssl. + + IF function is passed, it would be called upon reading or writing + PEM format private key with one argument which is True if we are + writing key and should verify passphrase and false if we are reading + """ - cnt=len(u) - if length0 - def derive(self,peerkey,**kwargs): + return ret > 0 + + def derive(self, peerkey, **kwargs): """ - Derives shared key (DH,ECDH,VKO 34.10). Requires - private key available + Derives shared key (DH,ECDH,VKO 34.10). Requires + private key available - @param peerkey - other key (may be public only) + @param peerkey - other key (may be public only) - Keyword parameters are algorithm-specific + Keyword parameters are algorithm-specific """ - ctx=libcrypto.EVP_PKEY_CTX_new(self.key,None) + if not self.cansign: + raise ValueError("No private key available") + ctx = libcrypto.EVP_PKEY_CTX_new(self.key, None) if ctx is None: raise PKeyError("Initailizing derive context") - if libcrypto.EVP_PKEY_derive_init(ctx)<1: + if libcrypto.EVP_PKEY_derive_init(ctx) < 1: raise PKeyError("derive_init") - - self._configure_context(ctx,kwargs,["ukm"]) - if libcrypto.EVP_PKEY_derive_set_peer(ctx,peerkey.key)<=0: + # This is workaround around missing functionality in GOST engine + # it provides only numeric control command to set UKM, not + # string one. + self._configure_context(ctx, kwargs, ["ukm"]) + if libcrypto.EVP_PKEY_derive_set_peer(ctx, peerkey.key) <= 0: raise PKeyError("Cannot set peer key") if "ukm" in kwargs: - if libcrypto.EVP_PKEY_CTX_ctrl(ctx,-1,1<<10,8,8,kwargs["ukm"])<=0: + # We just hardcode numeric command to set UKM here + if libcrypto.EVP_PKEY_CTX_ctrl(ctx, -1, 1 << 10, 8, 8, + kwargs["ukm"]) <= 0: raise PKeyError("Cannot set UKM") - keylen=c_long(0) - if libcrypto.EVP_PKEY_derive(ctx,None,byref(keylen))<=0: + keylen = c_long(0) + if libcrypto.EVP_PKEY_derive(ctx, None, byref(keylen)) <= 0: raise PKeyError("computing shared key length") - buf=create_string_buffer(keylen.value) - if libcrypto.EVP_PKEY_derive(ctx,buf,byref(keylen))<=0: + buf = create_string_buffer(keylen.value) + if libcrypto.EVP_PKEY_derive(ctx, buf, byref(keylen)) <= 0: raise PKeyError("computing actual shared key") libcrypto.EVP_PKEY_CTX_free(ctx) - return buf.raw[:keylen.value] + return buf.raw[:int(keylen.value)] + @staticmethod - def generate(algorithm,**kwargs): + def generate(algorithm, **kwargs): """ - Generates new private-public key pair for given algorithm - (string like 'rsa','ec','gost2001') and algorithm-specific - parameters. + Generates new private-public key pair for given algorithm + (string like 'rsa','ec','gost2001') and algorithm-specific + parameters. - Algorithm specific paramteers for RSA: + Algorithm specific paramteers for RSA: - rsa_keygen_bits=number - size of key to be generated - rsa_keygen_pubexp - RSA public expontent(default 65537) + rsa_keygen_bits=number - size of key to be generated + rsa_keygen_pubexp - RSA public expontent(default 65537) - Algorithm specific parameters for DSA,DH and EC + Algorithm specific parameters for DSA,DH and EC - paramsfrom=PKey object + paramsfrom=PKey object - copy parameters of newly generated key from existing key + copy parameters of newly generated key from existing key - Algorithm specific parameters for GOST2001 + Algorithm specific parameters for GOST2001 - paramset= paramset name where name is one of - 'A','B','C','XA','XB','test' + paramset= paramset name where name is one of + 'A','B','C','XA','XB','test' - paramsfrom does work too + paramsfrom does work too """ - tmpeng=c_void_p(None) - ameth=libcrypto.EVP_PKEY_asn1_find_str(byref(tmpeng),algorithm,-1) + tmpeng = c_void_p(None) + ameth = libcrypto.EVP_PKEY_asn1_find_str(byref(tmpeng), algorithm, -1) if ameth is None: - raise PKeyError("Algorithm %s not foind\n"%(algname)) + raise PKeyError("Algorithm %s not foind\n"%(algorithm)) clear_err_stack() - pkey_id=c_int(0) - libcrypto.EVP_PKEY_asn1_get0_info(byref(pkey_id),None,None,None,None,ameth) + pkey_id = c_int(0) + libcrypto.EVP_PKEY_asn1_get0_info(byref(pkey_id), None, None, None, + None, ameth) #libcrypto.ENGINE_finish(tmpeng) if "paramsfrom" in kwargs: - ctx=libcrypto.EVP_PKEY_CTX_new(kwargs["paramsfrom"].key,None) + ctx = libcrypto.EVP_PKEY_CTX_new(kwargs["paramsfrom"].key, None) else: - ctx=libcrypto.EVP_PKEY_CTX_new_id(pkey_id,None) + ctx = libcrypto.EVP_PKEY_CTX_new_id(pkey_id, None) # FIXME support EC curve as keyword param by invoking paramgen # operation if ctx is None: - raise PKeyError("Creating context for key type %d"%(pkey_id.value)) - if libcrypto.EVP_PKEY_keygen_init(ctx) <=0 : + raise PKeyError("Creating context for key type %d"%(pkey_id.value)) + if libcrypto.EVP_PKEY_keygen_init(ctx) <= 0: raise PKeyError("keygen_init") - PKey._configure_context(ctx,kwargs,["paramsfrom"]) - key=c_void_p(None) - if libcrypto.EVP_PKEY_keygen(ctx,byref(key))<=0: + PKey._configure_context(ctx, kwargs, ["paramsfrom"]) + key = c_void_p(None) + if libcrypto.EVP_PKEY_keygen(ctx, byref(key)) <= 0: raise PKeyError("Error generating key") libcrypto.EVP_PKEY_CTX_free(ctx) - return PKey(ptr=key,cansign=True) - def exportpub(self,format="PEM"): + return PKey(ptr=key, cansign=True) + + def exportpub(self, format="PEM"): """ - Returns public key as PEM or DER structure. + Returns public key as PEM or DER structure. """ - b=Membio() + bio = Membio() if format == "PEM": - r=libcrypto.PEM_write_bio_PUBKEY(b.bio,self.key) + retcode = libcrypto.PEM_write_bio_PUBKEY(bio.bio, self.key) else: - r=libcrypto.i2d_PUBKEY_bio(b.bio,self.key) - if r==0: + retcode = libcrypto.i2d_PUBKEY_bio(bio.bio, self.key) + if retcode == 0: raise PKeyError("error serializing public key") - return str(b) - def exportpriv(self,format="PEM",password=None,cipher=None): + return str(bio) + + def exportpriv(self, format="PEM", password=None, cipher=None): """ - Returns private key as PEM or DER Structure. - If password and cipher are specified, encrypts key - on given password, using given algorithm. Cipher must be - an ctypescrypto.cipher.CipherType object + Returns private key as PEM or DER Structure. + If password and cipher are specified, encrypts key + on given password, using given algorithm. Cipher must be + an ctypescrypto.cipher.CipherType object + + Password can be either string or function with one argument, + which returns password. It is called with argument True, which + means, that we are encrypting key, and password should be + verified (requested twice from user, for example). """ - b=Membio() + bio = Membio() if cipher is None: - evp_cipher=None + evp_cipher = None else: - if password is None: - raise NotImplementedError("Interactive password entry is not supported") - evp_cipher=cipher.cipher + evp_cipher = cipher.cipher if format == "PEM": - r=libcrypto.PEM_write_bio_PrivateKey(b.bio,self.key,evp_cipher,None,0,_cb, - password) + ret = libcrypto.PEM_write_bio_PrivateKey(bio.bio, self.key, + evp_cipher, None, 0, + _password_callback(password), + None) else: - if cipher is not None: - raise NotImplementedError("Der-formatted encrypted keys are not supported") - r=libcrypto.i2d_PrivateKey_bio(b.bio,self.key) - if r==0: + ret = libcrypto.i2d_PKCS8PrivateKey_bio(bio.bio, self.key, + evp_cipher, None, 0, + _password_callback(password), + None) + if ret == 0: raise PKeyError("error serializing private key") - return str(b) + return str(bio) + @staticmethod - def _configure_context(ctx,opts,skip=[]): + def _configure_context(ctx, opts, skip=()): """ - Configures context of public key operations - @param ctx - context to configure - @param opts - dictionary of options (from kwargs of calling - function) - @param skip - list of options which shouldn't be passed to - context + Configures context of public key operations + @param ctx - context to configure + @param opts - dictionary of options (from kwargs of calling + function) + @param skip - list of options which shouldn't be passed to + context """ for oper in opts: if oper in skip: continue - rv=libcrypto.EVP_PKEY_CTX_ctrl_str(ctx,oper,str(opts[oper])) - if rv==-2: - raise PKeyError("Parameter %s is not supported by key"%(oper,)) - if rv<1: - raise PKeyError("Error setting parameter %s"%(oper,)) + ret = libcrypto.EVP_PKEY_CTX_ctrl_str(ctx, oper, str(opts[oper])) + if ret == -2: + raise PKeyError("Parameter %s is not supported by key" % oper) + if ret < 1: + raise PKeyError("Error setting parameter %s" % oper) # Declare function prototypes -libcrypto.EVP_PKEY_cmp.argtypes=(c_void_p,c_void_p) -libcrypto.PEM_read_bio_PrivateKey.restype=c_void_p -libcrypto.PEM_read_bio_PrivateKey.argtypes=(c_void_p,POINTER(c_void_p),CALLBACK_FUNC,c_char_p) -libcrypto.PEM_read_bio_PUBKEY.restype=c_void_p -libcrypto.PEM_read_bio_PUBKEY.argtypes=(c_void_p,POINTER(c_void_p),CALLBACK_FUNC,c_char_p) -libcrypto.d2i_PUBKEY_bio.restype=c_void_p -libcrypto.d2i_PUBKEY_bio.argtypes=(c_void_p,c_void_p) -libcrypto.d2i_PrivateKey_bio.restype=c_void_p -libcrypto.d2i_PrivateKey_bio.argtypes=(c_void_p,c_void_p) -libcrypto.EVP_PKEY_print_public.argtypes=(c_void_p,c_void_p,c_int,c_void_p) -libcrypto.EVP_PKEY_asn1_find_str.restype=c_void_p -libcrypto.EVP_PKEY_asn1_find_str.argtypes=(c_void_p,c_char_p,c_int) -libcrypto.EVP_PKEY_asn1_get0_info.restype=c_int -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) -libcrypto.EVP_PKEY_cmp.restype=c_int -libcrypto.EVP_PKEY_cmp.argtypes=(c_void_p,c_void_p) -libcrypto.EVP_PKEY_CTX_ctrl_str.restype=c_int -libcrypto.EVP_PKEY_CTX_ctrl_str.argtypes=(c_void_p,c_void_p,c_void_p) -libcrypto.EVP_PKEY_CTX_ctrl.restype=c_int -libcrypto.EVP_PKEY_CTX_ctrl.argtypes=(c_void_p,c_int,c_int,c_int,c_int,c_void_p) -libcrypto.EVP_PKEY_CTX_free.argtypes=(c_void_p,) -libcrypto.EVP_PKEY_CTX_new.restype=c_void_p -libcrypto.EVP_PKEY_CTX_new.argtypes=(c_void_p,c_void_p) -libcrypto.EVP_PKEY_CTX_new_id.restype=c_void_p -libcrypto.EVP_PKEY_CTX_new_id.argtypes=(c_int,c_void_p) -libcrypto.EVP_PKEY_derive.restype=c_int -libcrypto.EVP_PKEY_derive.argtypes=(c_void_p,c_char_p,POINTER(c_long)) -libcrypto.EVP_PKEY_derive_init.restype=c_int -libcrypto.EVP_PKEY_derive_init.argtypes=(c_void_p,) -libcrypto.EVP_PKEY_derive_set_peer.restype=c_int -libcrypto.EVP_PKEY_derive_set_peer.argtypes=(c_void_p,c_void_p) -libcrypto.EVP_PKEY_free.argtypes=(c_void_p,) -libcrypto.EVP_PKEY_keygen.restype=c_int -libcrypto.EVP_PKEY_keygen.argtypes=(c_void_p,c_void_p) -libcrypto.EVP_PKEY_keygen_init.restype=c_int -libcrypto.EVP_PKEY_keygen_init.argtypes=(c_void_p,) -libcrypto.EVP_PKEY_sign.restype=c_int -libcrypto.EVP_PKEY_sign.argtypes=(c_void_p,c_char_p,POINTER(c_long),c_char_p,c_long) -libcrypto.EVP_PKEY_sign_init.restype=c_int -libcrypto.EVP_PKEY_sign_init.argtypes=(c_void_p,) -libcrypto.EVP_PKEY_verify.restype=c_int -libcrypto.EVP_PKEY_verify.argtypes=(c_void_p,c_char_p,c_long,c_char_p,c_long) -libcrypto.EVP_PKEY_verify_init.restype=c_int -libcrypto.EVP_PKEY_verify_init.argtypes=(c_void_p,) -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) -libcrypto.PEM_write_bio_PUBKEY.argtypes=(c_void_p,c_void_p) -libcrypto.i2d_PUBKEY_bio.argtypes=(c_void_p,c_void_p) -libcrypto.i2d_PrivateKey_bio.argtypes=(c_void_p,c_void_p) -libcrypto.ENGINE_finish.argtypes=(c_void_p,) +libcrypto.EVP_PKEY_cmp.argtypes = (c_void_p, c_void_p) +libcrypto.PEM_read_bio_PrivateKey.restype = c_void_p +libcrypto.PEM_read_bio_PrivateKey.argtypes = (c_void_p, POINTER(c_void_p), + PW_CALLBACK_FUNC, c_char_p) +libcrypto.PEM_read_bio_PUBKEY.restype = c_void_p +libcrypto.PEM_read_bio_PUBKEY.argtypes = (c_void_p, POINTER(c_void_p), + PW_CALLBACK_FUNC, c_char_p) +libcrypto.d2i_PUBKEY_bio.restype = c_void_p +libcrypto.d2i_PUBKEY_bio.argtypes = (c_void_p, c_void_p) +libcrypto.d2i_PrivateKey_bio.restype = c_void_p +libcrypto.d2i_PrivateKey_bio.argtypes = (c_void_p, c_void_p) +libcrypto.EVP_PKEY_print_public.argtypes = (c_void_p, c_void_p, c_int, c_void_p) +libcrypto.EVP_PKEY_asn1_find_str.restype = c_void_p +libcrypto.EVP_PKEY_asn1_find_str.argtypes = (c_void_p, c_char_p, c_int) +libcrypto.EVP_PKEY_asn1_get0_info.restype = c_int +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) +libcrypto.EVP_PKEY_cmp.restype = c_int +libcrypto.EVP_PKEY_cmp.argtypes = (c_void_p, c_void_p) +libcrypto.EVP_PKEY_CTX_ctrl_str.restype = c_int +libcrypto.EVP_PKEY_CTX_ctrl_str.argtypes = (c_void_p, c_void_p, c_void_p) +libcrypto.EVP_PKEY_CTX_ctrl.restype = c_int +libcrypto.EVP_PKEY_CTX_ctrl.argtypes = (c_void_p, c_int, c_int, c_int, c_int, + c_void_p) +libcrypto.EVP_PKEY_CTX_free.argtypes = (c_void_p, ) +libcrypto.EVP_PKEY_CTX_new.restype = c_void_p +libcrypto.EVP_PKEY_CTX_new.argtypes = (c_void_p, c_void_p) +libcrypto.EVP_PKEY_CTX_new_id.restype = c_void_p +libcrypto.EVP_PKEY_CTX_new_id.argtypes = (c_int, c_void_p) +libcrypto.EVP_PKEY_derive.restype = c_int +libcrypto.EVP_PKEY_derive.argtypes = (c_void_p, c_char_p, POINTER(c_long)) +libcrypto.EVP_PKEY_derive_init.restype = c_int +libcrypto.EVP_PKEY_derive_init.argtypes = (c_void_p, ) +libcrypto.EVP_PKEY_derive_set_peer.restype = c_int +libcrypto.EVP_PKEY_derive_set_peer.argtypes = (c_void_p, c_void_p) +libcrypto.EVP_PKEY_free.argtypes = (c_void_p,) +libcrypto.EVP_PKEY_keygen.restype = c_int +libcrypto.EVP_PKEY_keygen.argtypes = (c_void_p, c_void_p) +libcrypto.EVP_PKEY_keygen_init.restype = c_int +libcrypto.EVP_PKEY_keygen_init.argtypes = (c_void_p, ) +libcrypto.EVP_PKEY_sign.restype = c_int +libcrypto.EVP_PKEY_sign.argtypes = (c_void_p, c_char_p, POINTER(c_long), + c_char_p, c_long) +libcrypto.EVP_PKEY_sign_init.restype = c_int +libcrypto.EVP_PKEY_sign_init.argtypes = (c_void_p, ) +libcrypto.EVP_PKEY_verify.restype = c_int +libcrypto.EVP_PKEY_verify.argtypes = (c_void_p, c_char_p, c_long, c_char_p, + c_long) +libcrypto.EVP_PKEY_verify_init.restype = c_int +libcrypto.EVP_PKEY_verify_init.argtypes = (c_void_p, ) +libcrypto.PEM_write_bio_PrivateKey.argtypes = (c_void_p, c_void_p, c_void_p, + c_char_p, c_int, + PW_CALLBACK_FUNC, c_char_p) +libcrypto.PEM_write_bio_PUBKEY.argtypes = (c_void_p, c_void_p) +libcrypto.i2d_PUBKEY_bio.argtypes = (c_void_p, c_void_p) +libcrypto.i2d_PKCS8PrivateKey_bio.argtypes = (c_void_p, c_void_p, c_void_p, + c_char_p, c_int, + PW_CALLBACK_FUNC, c_char_p) +libcrypto.d2i_PKCS8PrivateKey_bio.restype = c_void_p +libcrypto.d2i_PKCS8PrivateKey_bio.argtypes = (c_void_p,c_void_p, + PW_CALLBACK_FUNC,c_void_p) +libcrypto.ENGINE_finish.argtypes = (c_void_p, )