- pass
-
-class PKey:
- def __init__(self,ptr,cansign)
- self.key=ptr:
- self.cansign=cansign
- def __del__(self):
- libcrypto.EVP_PKEY_free(self.key)
- def __eq__(self,other):
- """ Compares two public keys. If one has private key and other
- doesn't it doesn't affect result of comparation
- """
- return libcrypto.EVP_PKEY_cmp(self.key,other.key)==1
- def __ne__(self,other):
- return not self.__eq__(other)
- def __str__(self):
- """ printable representation of public key """
- b=Membio()
- libcrypto.EVP_PKEY_print_public(b.bio,self.key,0,NULL)
- return str(b)
- def privpem(s,password=None):
- """ Class method for load from the pem string of private key """
- b=Membio(s)
- return PKey(libcrypto.PEM_read_bio_PrivateKey(b.bio,NULL,cb,c_char_p(password))
-
- def privder(s):
- """ Class method for load from the binary ASN1 structure of private key """
- b=Membio(s)
- return PKey(libcrypto.d2i_PrivateKey_bio(b.bio,NULL),True)
- def pubpem(s):
- """ Class method for load from public key pem string"""
- b=Membio(s)
- return PKey(libcrypto.PEM_read_bio_PUBKEY(b.bio,NULL,cb,c_char_p(password)),False)
- def pubder(s):
- """ Class method for load from the binary ASN1 structure """
- b=Membio(s)
- return PKey(libcrypto.d2i_PUBKEY_bio(b.bio,NULL),False)
- def sign(self,digest,**kwargs):
- """
- Signs given digest and retirns signature
- Keyword arguments allows to set various algorithm-specific
- parameters. See pkeyutl(1) manual.
- """
- ctx=libcrypto.EVP_PKEY_CTX_new(self.key,None)
- if ctx is None:
- raise PkeyError("Initailizing sign context")
- if libcrypto.EVP_PKEY_sign_init(ctx)<1:
- raise PkeyError("sign_init")
- for oper in kwargs:
- rv=libcrypto.EVP_PKEY_CTX_ctrl_str(ctx,oper,kwargs[oper])
- if rw=-2:
- raise PKeyError("Parameter %s is not supported by key"%(oper))
- if rv<1:
- raise PKeyError("Error setting parameter %s"(oper))
- # Find out signature size
- siglen=c_long(0)
- if libcrypto.EVP_PKEY_sign(ctx,None,byref(siglen),digest,len(digest))<1:
- raise PkeyError("signing")
- sig=create_string_buffer(siglen.value)
- libcrypto.EVP_PKEY_sign(ctx,sig,byref(signlen),digest,len(digest)
- libcrypto.EVP_PKEY_CTX_free(ctx)
- return sig.value[:siglen.value]
-
- def verify(self,digest,signature,**kwargs):
- """
- Verifies given signature on given digest
- Returns True if Ok, False if don't match
- """
- ctx=libcrypto.EVP_PKEY_CTX_new(self.key,None)
- if ctx is None:
- raise PkeyError("Initailizing verify context")
- if libcrypto.EVP_PKEY_verify_init(ctx)<1:
- raise PkeyError("verify_init")
- for oper in kwargs:
- rv=libcrypto.EVP_PKEY_CTX_ctrl_str(ctx,oper,kwargs[oper])
- if rw=-2:
- raise PKeyError("Parameter %s is not supported by key"%(oper))
- if rv<1:
- raise PKeyError("Error setting parameter %s"(oper))
- rv=libcrypto.EVP_PKEY_verify(ctx,signature,len(signature),digest,len(digest))
- if rv<0:
- raise PKeyError("Signature verification")
- libcrypto=EVP_PKEY_CTX_free(ctx)
- return rv>0
- def generate(algorithm,**kwargs):
- """
- Generates new private-public key pair for given algorithm
- (string like 'rsa','ec','gost2001') and algorithm-specific
- parameters
- """
- 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))
- clear_err_stack()
- pkey_id=c_int(0)
- libcrypto.EVP_PKEY_asn1_get0_info(byref(pkey_id),None,None,None,None,ameth)
- libcrypto.ENGINE_finish(tmpeng)
- ctx=libcrypto.EVP_PKEY_CTX_new_id(pkey_id)
- 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("keygen_init")
- for oper in kwargs:
- rv=libcrypto.EVP_PKEY_CTX_ctrl_str(ctx,oper,kwargs[oper])
- if rw=-2:
- raise PKeyError("Parameter %s is not supported by key"%(oper))
- if rv<1:
- raise PKeyError("Error setting parameter %s"(oper))
- 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(key,True)
-
-class X509:
- def __init__(self,ptr):
- self.cert = ptr
- def __del__(self):
- libcrypto.X509_free(self.cert)
- def __str__(self):
- """ Returns der string of the certificate """
- def pubkey(self):
- """ Returns EVP PKEy object of certificate public key"""
- return PKey(libcrypto.X509_get_pubkey(self.cert,False)
- def verify(self,key):
- """ Verify self on given issuer key """
- def frompem(s):
- """ Create X509 object from pem string """
- def fromder(s):
- """ Create X509 object from der string """
-
-class Verifier:
- def __init__(self,filename):
-
- def verify_cert(self,cert):
-
-class Signer:
- def __init__(self,key):
- self.key = key
- def sign(self,digest):
- if not self.key.cansign:
- raise ValueError("Current PKey doesn't contain private part")
- def verify(self,signature,digest):
+ """ Exception thrown if libcrypto finctions return an error """
+ pass
+
+PW_CALLBACK_FUNC = CFUNCTYPE(c_int, c_char_p, c_int, c_int, c_char_p)
+""" Function type for pem password callback """
+
+def password_callback(buf, length, rwflag, userdata):
+ """
+ Example password callback for private key. Assumes that
+ password is stored in the userdata parameter, so allows to pass password
+ from constructor arguments to the libcrypto keyloading functions
+ """
+ cnt = len(userdata)
+ if length < cnt:
+ cnt = length
+ memmove(buf, userdata, cnt)
+ return cnt
+
+_cb = PW_CALLBACK_FUNC(password_callback)
+
+class PKey(object):
+ """
+ Represents public/private key pair. Wrapper around EVP_PKEY
+ libcrypto object.
+
+ May contain either both private and public key (such objects can be
+ used for signing, deriving shared key as well as verifying or public
+ key only, which can be used for verifying or as peer key when
+ deriving.
+
+ @var cansign is true key has private part.
+ @var key contain pointer to EVP_PKEY and should be passed to various
+ libcrypto routines
+ """
+ def __init__(self, ptr=None, privkey=None, pubkey=None, format="PEM",
+ cansign=False, password=None, callback=_cb):
+ if not ptr is None:
+ self.key = ptr
+ self.cansign = cansign
+ if not privkey is None or not pubkey is None:
+ raise TypeError("Just one of ptr, pubkey or privkey can " +
+ "be specified")
+ elif not privkey is None:
+ if not pubkey is None:
+ raise TypeError("Just one of ptr, pubkey or privkey can " +
+ "be specified")
+ bio = Membio(privkey)
+ self.cansign = True
+ if format == "PEM":
+ self.key = libcrypto.PEM_read_bio_PrivateKey(bio.bio, None,
+ callback,
+ c_char_p(password))
+ else:
+ self.key = libcrypto.d2i_PrivateKey_bio(bio.bio, None)
+ if self.key is None:
+ raise PKeyError("error parsing private key")
+ elif not pubkey is None:
+ bio = Membio(pubkey)
+ self.cansign = False
+ if format == "PEM":
+ self.key = libcrypto.PEM_read_bio_PUBKEY(bio.bio, None,
+ callback,
+ c_char_p(password))
+ else:
+ self.key = libcrypto.d2i_PUBKEY_bio(bio.bio, None)
+ if self.key is None:
+ raise PKeyError("error parsing public key")
+ else:
+ raise TypeError("Neither public, nor private key is specified")
+
+
+ def __del__(self):
+ """ Frees EVP_PKEY object (note, it is reference counted) """
+ libcrypto.EVP_PKEY_free(self.key)
+
+ def __eq__(self, other):
+ """ Compares two public keys. If one has private key and other
+ doesn't it doesn't affect result of comparation
+ """
+ return libcrypto.EVP_PKEY_cmp(self.key, other.key) == 1
+
+ def __ne__(self, other):
+ """ Compares two public key for not-equality """
+ return not self.__eq__(other)
+
+ def __str__(self):
+ """ printable representation of public key """
+ bio = Membio()
+ libcrypto.EVP_PKEY_print_public(bio.bio, self.key, 0, None)
+ return str(bio)
+
+ def sign(self, digest, **kwargs):
+ """
+ Signs given digest and retirns signature
+ Keyword arguments allows to set various algorithm-specific
+ parameters. See pkeyutl(1) manual.
+ """
+ ctx = libcrypto.EVP_PKEY_CTX_new(self.key, None)
+ if ctx is None:
+ raise PKeyError("Initailizing sign context")
+ if libcrypto.EVP_PKEY_sign_init(ctx) < 1:
+ raise PKeyError("sign_init")
+ self._configure_context(ctx, kwargs)
+ # Find out signature size
+ siglen = c_long(0)
+ if libcrypto.EVP_PKEY_sign(ctx, None, byref(siglen), digest,
+ len(digest)) < 1:
+ raise PKeyError("computing signature length")
+ sig = create_string_buffer(siglen.value)
+ if libcrypto.EVP_PKEY_sign(ctx, sig, byref(siglen), digest,
+ len(digest)) < 1:
+ raise PKeyError("signing")
+ libcrypto.EVP_PKEY_CTX_free(ctx)
+ return sig.raw[:int(siglen.value)]
+
+ def verify(self, digest, signature, **kwargs):
+ """
+ Verifies given signature on given digest
+ Returns True if Ok, False if don't match
+ Keyword arguments allows to set algorithm-specific
+ parameters
+ """
+ ctx = libcrypto.EVP_PKEY_CTX_new(self.key, None)
+ if ctx is None:
+ raise PKeyError("Initailizing verify context")
+ if libcrypto.EVP_PKEY_verify_init(ctx) < 1:
+ raise PKeyError("verify_init")
+ self._configure_context(ctx, kwargs)
+ ret = libcrypto.EVP_PKEY_verify(ctx, signature, len(signature), digest,
+ len(digest))
+ if ret < 0:
+ raise PKeyError("Signature verification")
+ libcrypto.EVP_PKEY_CTX_free(ctx)
+ return ret > 0
+
+ def derive(self, peerkey, **kwargs):
+ """
+ Derives shared key (DH,ECDH,VKO 34.10). Requires
+ private key available
+
+ @param peerkey - other key (may be public only)
+
+ Keyword parameters are algorithm-specific
+ """
+ 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:
+ raise PKeyError("derive_init")
+
+ # 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:
+ # 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:
+ raise PKeyError("computing shared key length")
+ 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[:int(keylen.value)]
+
+ @staticmethod
+ def generate(algorithm, **kwargs):
+ """
+ Generates new private-public key pair for given algorithm
+ (string like 'rsa','ec','gost2001') and algorithm-specific
+ parameters.
+
+ Algorithm specific paramteers for RSA:
+
+ 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
+
+ paramsfrom=PKey object
+
+ copy parameters of newly generated key from existing key
+
+ Algorithm specific parameters for GOST2001
+
+ paramset= paramset name where name is one of
+ 'A','B','C','XA','XB','test'
+
+ paramsfrom does work too
+ """
+ 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"%(algorithm))
+ clear_err_stack()
+ 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)
+ else:
+ 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("keygen_init")
+ 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"):
+ """
+ Returns public key as PEM or DER structure.
+ """
+ bio = Membio()
+ if format == "PEM":
+ retcode = libcrypto.PEM_write_bio_PUBKEY(bio.bio, self.key)
+ else:
+ retcode = libcrypto.i2d_PUBKEY_bio(bio.bio, self.key)
+ if retcode == 0:
+ raise PKeyError("error serializing public key")
+ return str(bio)
+
+ def exportpriv(self, format="PEM", password=None, cipher=None,
+ callback=_cb):
+ """
+ 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
+ """
+ bio = Membio()
+ if cipher is None:
+ evp_cipher = None
+ else:
+ evp_cipher = cipher.cipher
+ if format == "PEM":
+ ret = libcrypto.PEM_write_bio_PrivateKey(bio.bio, self.key,
+ evp_cipher, None, 0,
+ callback,
+ c_char_p(password))
+ else:
+ ret = libcrypto.i2d_PKCS8PrivateKey_bio(bio.bio, self.key,
+ evp_cipher, None, 0,
+ callback,
+ c_char_p(password))
+ if ret == 0:
+ raise PKeyError("error serializing private key")
+ return str(bio)
+
+ @staticmethod
+ 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
+ """