diff options
Diffstat (limited to 'src/M2Crypto/EC.py')
-rw-r--r-- | src/M2Crypto/EC.py | 460 |
1 files changed, 460 insertions, 0 deletions
diff --git a/src/M2Crypto/EC.py b/src/M2Crypto/EC.py new file mode 100644 index 0000000..522092a --- /dev/null +++ b/src/M2Crypto/EC.py @@ -0,0 +1,460 @@ +from __future__ import absolute_import + +""" +M2Crypto wrapper for OpenSSL ECDH/ECDSA API. + +@requires: OpenSSL 0.9.8 or newer + +Copyright (c) 1999-2003 Ng Pheng Siong. All rights reserved. + +Portions copyright (c) 2005-2006 Vrije Universiteit Amsterdam. +All rights reserved.""" + +from M2Crypto import BIO, Err, EVP, m2, util +from typing import AnyStr, Callable, Dict, Optional, Tuple, Union # noqa +from M2Crypto.EVP import PKey + +EC_Key = bytes + + +class ECError(Exception): + pass + + +m2.ec_init(ECError) + +# Curve identifier constants +NID_secp112r1 = m2.NID_secp112r1 # type: int +NID_secp112r2 = m2.NID_secp112r2 # type: int +NID_secp128r1 = m2.NID_secp128r1 # type: int +NID_secp128r2 = m2.NID_secp128r2 # type: int +NID_secp160k1 = m2.NID_secp160k1 # type: int +NID_secp160r1 = m2.NID_secp160r1 # type: int +NID_secp160r2 = m2.NID_secp160r2 # type: int +NID_secp192k1 = m2.NID_secp192k1 # type: int +NID_secp224k1 = m2.NID_secp224k1 # type: int +NID_secp224r1 = m2.NID_secp224r1 # type: int +NID_secp256k1 = m2.NID_secp256k1 # type: int +NID_secp384r1 = m2.NID_secp384r1 # type: int +NID_secp521r1 = m2.NID_secp521r1 # type: int +NID_sect113r1 = m2.NID_sect113r1 # type: int +NID_sect113r2 = m2.NID_sect113r2 # type: int +NID_sect131r1 = m2.NID_sect131r1 # type: int +NID_sect131r2 = m2.NID_sect131r2 # type: int +NID_sect163k1 = m2.NID_sect163k1 # type: int +NID_sect163r1 = m2.NID_sect163r1 # type: int +NID_sect163r2 = m2.NID_sect163r2 # type: int +NID_sect193r1 = m2.NID_sect193r1 # type: int +NID_sect193r2 = m2.NID_sect193r2 # type: int +# default for secg.org TLS test server +NID_sect233k1 = m2.NID_sect233k1 # type: int +NID_sect233r1 = m2.NID_sect233r1 # type: int +NID_sect239k1 = m2.NID_sect239k1 # type: int +NID_sect283k1 = m2.NID_sect283k1 # type: int +NID_sect283r1 = m2.NID_sect283r1 # type: int +NID_sect409k1 = m2.NID_sect409k1 # type: int +NID_sect409r1 = m2.NID_sect409r1 # type: int +NID_sect571k1 = m2.NID_sect571k1 # type: int +NID_sect571r1 = m2.NID_sect571r1 # type: int + +NID_prime192v1 = m2.NID_X9_62_prime192v1 # type: int +NID_prime192v2 = m2.NID_X9_62_prime192v2 # type: int +NID_prime192v3 = m2.NID_X9_62_prime192v3 # type: int +NID_prime239v1 = m2.NID_X9_62_prime239v1 # type: int +NID_prime239v2 = m2.NID_X9_62_prime239v2 # type: int +NID_prime239v3 = m2.NID_X9_62_prime239v3 # type: int +NID_prime256v1 = m2.NID_X9_62_prime256v1 # type: int +NID_c2pnb163v1 = m2.NID_X9_62_c2pnb163v1 # type: int +NID_c2pnb163v2 = m2.NID_X9_62_c2pnb163v2 # type: int +NID_c2pnb163v3 = m2.NID_X9_62_c2pnb163v3 # type: int +NID_c2pnb176v1 = m2.NID_X9_62_c2pnb176v1 # type: int +NID_c2tnb191v1 = m2.NID_X9_62_c2tnb191v1 # type: int +NID_c2tnb191v2 = m2.NID_X9_62_c2tnb191v2 # type: int +NID_c2tnb191v3 = m2.NID_X9_62_c2tnb191v3 # type: int +NID_c2pnb208w1 = m2.NID_X9_62_c2pnb208w1 # type: int +NID_c2tnb239v1 = m2.NID_X9_62_c2tnb239v1 # type: int +NID_c2tnb239v2 = m2.NID_X9_62_c2tnb239v2 # type: int +NID_c2tnb239v3 = m2.NID_X9_62_c2tnb239v3 # type: int +NID_c2pnb272w1 = m2.NID_X9_62_c2pnb272w1 # type: int +NID_c2pnb304w1 = m2.NID_X9_62_c2pnb304w1 # type: int +NID_c2tnb359v1 = m2.NID_X9_62_c2tnb359v1 # type: int +NID_c2pnb368w1 = m2.NID_X9_62_c2pnb368w1 # type: int +NID_c2tnb431r1 = m2.NID_X9_62_c2tnb431r1 # type: int + +# To preserve compatibility with older names +NID_X9_62_prime192v1 = NID_prime192v1 # type: int +NID_X9_62_prime192v2 = NID_prime192v2 # type: int +NID_X9_62_prime192v3 = NID_prime192v3 # type: int +NID_X9_62_prime239v1 = NID_prime239v1 # type: int +NID_X9_62_prime239v2 = NID_prime239v2 # type: int +NID_X9_62_prime239v3 = NID_prime239v3 # type: int +NID_X9_62_prime256v1 = NID_prime256v1 # type: int +NID_X9_62_c2pnb163v1 = NID_c2pnb163v1 # type: int +NID_X9_62_c2pnb163v2 = NID_c2pnb163v2 # type: int +NID_X9_62_c2pnb163v3 = NID_c2pnb163v3 # type: int +NID_X9_62_c2pnb176v1 = NID_c2pnb176v1 # type: int +NID_X9_62_c2tnb191v1 = NID_c2tnb191v1 # type: int +NID_X9_62_c2tnb191v2 = NID_c2tnb191v2 # type: int +NID_X9_62_c2tnb191v3 = NID_c2tnb191v3 # type: int +NID_X9_62_c2pnb208w1 = NID_c2pnb208w1 # type: int +NID_X9_62_c2tnb239v1 = NID_c2tnb239v1 # type: int +NID_X9_62_c2tnb239v2 = NID_c2tnb239v2 # type: int +NID_X9_62_c2tnb239v3 = NID_c2tnb239v3 # type: int +NID_X9_62_c2pnb272w1 = NID_c2pnb272w1 # type: int +NID_X9_62_c2pnb304w1 = NID_c2pnb304w1 # type: int +NID_X9_62_c2tnb359v1 = NID_c2tnb359v1 # type: int +NID_X9_62_c2pnb368w1 = NID_c2pnb368w1 # type: int +NID_X9_62_c2tnb431r1 = NID_c2tnb431r1 # type: int + +NID_wap_wsg_idm_ecid_wtls1 = m2.NID_wap_wsg_idm_ecid_wtls1 # type: int +NID_wap_wsg_idm_ecid_wtls3 = m2.NID_wap_wsg_idm_ecid_wtls3 # type: int +NID_wap_wsg_idm_ecid_wtls4 = m2.NID_wap_wsg_idm_ecid_wtls4 # type: int +NID_wap_wsg_idm_ecid_wtls5 = m2.NID_wap_wsg_idm_ecid_wtls5 # type: int +NID_wap_wsg_idm_ecid_wtls6 = m2.NID_wap_wsg_idm_ecid_wtls6 # type: int +NID_wap_wsg_idm_ecid_wtls7 = m2.NID_wap_wsg_idm_ecid_wtls7 # type: int +NID_wap_wsg_idm_ecid_wtls8 = m2.NID_wap_wsg_idm_ecid_wtls8 # type: int +NID_wap_wsg_idm_ecid_wtls9 = m2.NID_wap_wsg_idm_ecid_wtls9 # type: int +NID_wap_wsg_idm_ecid_wtls10 = m2.NID_wap_wsg_idm_ecid_wtls10 # type: int +NID_wap_wsg_idm_ecid_wtls11 = m2.NID_wap_wsg_idm_ecid_wtls11 # type: int +NID_wap_wsg_idm_ecid_wtls12 = m2.NID_wap_wsg_idm_ecid_wtls12 # type: int + +# The following two curves, according to OpenSSL, have a +# "Questionable extension field!" and are not supported by +# the OpenSSL inverse function. ECError: no inverse. +# As such they cannot be used for signing. They might, +# however, be usable for encryption but that has not +# been tested. Until thir usefulness can be established, +# they are not supported at this time. +# NID_ipsec3 = m2.NID_ipsec3 +# NID_ipsec4 = m2.NID_ipsec4 + + +class EC(object): + + """ + Object interface to a EC key pair. + """ + + m2_ec_key_free = m2.ec_key_free + + def __init__(self, ec, _pyfree=0): + # type: (EC, int) -> None + assert m2.ec_key_type_check(ec), "'ec' type error" + self.ec = ec + self._pyfree = _pyfree + + def __del__(self): + # type: () -> None + if getattr(self, '_pyfree', 0): + self.m2_ec_key_free(self.ec) + + def __len__(self): + # type: () -> int + assert m2.ec_key_type_check(self.ec), "'ec' type error" + return m2.ec_key_keylen(self.ec) + + def gen_key(self): + # type: () -> int + """ + Generates the key pair from its parameters. Use:: + + keypair = EC.gen_params(curve) + keypair.gen_key() + + to create an EC key pair. + """ + assert m2.ec_key_type_check(self.ec), "'ec' type error" + m2.ec_key_gen_key(self.ec) + + def pub(self): + # type: () -> EC_pub + # Don't let python free + return EC_pub(self.ec, 0) + + def sign_dsa(self, digest): + # type: (bytes) -> Tuple[bytes, bytes] + """ + Sign the given digest using ECDSA. Returns a tuple (r,s), the two + ECDSA signature parameters. + """ + assert self._check_key_type(), "'ec' type error" + return m2.ecdsa_sign(self.ec, digest) + + def verify_dsa(self, digest, r, s): + # type: (bytes, bytes, bytes) -> int + """ + Verify the given digest using ECDSA. r and s are the ECDSA + signature parameters. + """ + assert self._check_key_type(), "'ec' type error" + return m2.ecdsa_verify(self.ec, digest, r, s) + + def sign_dsa_asn1(self, digest): + # type: (bytes) -> bytes + assert self._check_key_type(), "'ec' type error" + return m2.ecdsa_sign_asn1(self.ec, digest) + + def verify_dsa_asn1(self, digest, blob): + assert self._check_key_type(), "'ec' type error" + return m2.ecdsa_verify_asn1(self.ec, digest, blob) + + def compute_dh_key(self, pub_key): + # type: (EC) -> Optional[bytes] + """ + Compute the ECDH shared key of this key pair and the given public + key object. They must both use the same curve. Returns the + shared key in binary as a buffer object. No Key Derivation Function is + applied. + """ + assert self.check_key(), 'key is not initialised' + return m2.ecdh_compute_key(self.ec, pub_key.ec) + + def save_key_bio(self, bio, cipher='aes_128_cbc', + callback=util.passphrase_callback): + # type: (BIO.BIO, Optional[str], Callable) -> int + """ + Save the key pair to an M2Crypto.BIO.BIO object in PEM format. + + :param bio: M2Crypto.BIO.BIO object to save key to. + + :param cipher: Symmetric cipher to protect the key. The default + cipher is 'aes_128_cbc'. If cipher is None, then + the key is saved in the clear. + + :param callback: A Python callable object that is invoked + to acquire a passphrase with which to protect + the key. The default is + util.passphrase_callback. + """ + if cipher is None: + return m2.ec_key_write_bio_no_cipher(self.ec, bio._ptr(), callback) + else: + ciph = getattr(m2, cipher, None) + if ciph is None: + raise ValueError('not such cipher %s' % cipher) + return m2.ec_key_write_bio(self.ec, bio._ptr(), ciph(), callback) + + def save_key(self, file, cipher='aes_128_cbc', + callback=util.passphrase_callback): + # type: (AnyStr, Optional[str], Callable) -> int + """ + Save the key pair to a file in PEM format. + + :param file: Name of filename to save key to. + + :param cipher: Symmetric cipher to protect the key. The default + cipher is 'aes_128_cbc'. If cipher is None, then + the key is saved in the clear. + + :param callback: A Python callable object that is invoked + to acquire a passphrase with which to protect + the key. The default is + util.passphrase_callback. + """ + with BIO.openfile(file, 'wb') as bio: + return self.save_key_bio(bio, cipher, callback) + + def save_pub_key_bio(self, bio): + # type: (BIO.BIO) -> int + """ + Save the public key to an M2Crypto.BIO.BIO object in PEM format. + + :param bio: M2Crypto.BIO.BIO object to save key to. + """ + return m2.ec_key_write_pubkey(self.ec, bio._ptr()) + + def save_pub_key(self, file): + # type: (AnyStr) -> int + """ + Save the public key to a filename in PEM format. + + :param file: Name of filename to save key to. + """ + with BIO.openfile(file, 'wb') as bio: + return m2.ec_key_write_pubkey(self.ec, bio._ptr()) + + def as_pem(self, cipher='aes_128_cbc', callback=util.passphrase_callback): + """ + Returns the key(pair) as a string in PEM format. + If no password is passed and the cipher is set + it exits with error + """ + with BIO.MemoryBuffer() as bio: + self.save_key_bio(bio, cipher, callback) + return bio.read() + + def _check_key_type(self): + # type: () -> int + return m2.ec_key_type_check(self.ec) + + def check_key(self): + # type: () -> int + assert m2.ec_key_type_check(self.ec), "'ec' type error" + return m2.ec_key_check_key(self.ec) + + +class EC_pub(EC): + + """ + Object interface to an EC public key. + ((don't like this implementation inheritance)) + """ + def __init__(self, ec, _pyfree=0): + # type: (EC, int) -> None + EC.__init__(self, ec, _pyfree) + self.der = None # type: Optional[bytes] + + def get_der(self): + # type: () -> bytes + """ + Returns the public key in DER format as a buffer object. + """ + assert self.check_key(), 'key is not initialised' + if self.der is None: + self.der = m2.ec_key_get_public_der(self.ec) + return self.der + + def get_key(self): + # type: () -> bytes + """ + Returns the public key as a byte string. + """ + assert self.check_key(), 'key is not initialised' + return m2.ec_key_get_public_key(self.ec) + + save_key = EC.save_pub_key + + save_key_bio = EC.save_pub_key_bio + + +def gen_params(curve): + # type: (int) -> EC + """ + Factory function that generates EC parameters and + instantiates a EC object from the output. + + :param curve: This is the OpenSSL nid of the curve to use. + """ + assert curve in [x['NID'] for x in m2.ec_get_builtin_curves()], \ + 'Elliptic curve %s is not available on this system.' % \ + m2.obj_nid2sn(curve) + return EC(m2.ec_key_new_by_curve_name(curve), 1) + + +def load_key(file, callback=util.passphrase_callback): + # type: (AnyStr, Callable) -> EC + """ + Factory function that instantiates a EC object. + + :param file: Names the filename that contains the PEM representation + of the EC key pair. + + :param callback: Python callback object that will be invoked + if the EC key pair is passphrase-protected. + """ + with BIO.openfile(file) as bio: + return load_key_bio(bio, callback) + + +def load_key_string(string, callback=util.passphrase_callback): + # type: (str, Callable) -> EC + """ + Load an EC key pair from a string. + + :param string: String containing EC key pair in PEM format. + + :param callback: A Python callable object that is invoked + to acquire a passphrase with which to unlock the + key. The default is util.passphrase_callback. + + :return: M2Crypto.EC.EC object. + """ + with BIO.MemoryBuffer(string) as bio: + return load_key_bio(bio, callback) + + +def load_key_bio(bio, callback=util.passphrase_callback): + # type: (BIO.BIO, Callable) -> EC + """ + Factory function that instantiates a EC object. + + :param bio: M2Crypto.BIO object that contains the PEM + representation of the EC key pair. + + :param callback: Python callback object that will be invoked + if the EC key pair is passphrase-protected. + """ + return EC(m2.ec_key_read_bio(bio._ptr(), callback), 1) + + +def load_pub_key(file): + # type: (AnyStr) -> EC_pub + """ + Load an EC public key from filename. + + :param file: Name of filename containing EC public key in PEM + format. + + :return: M2Crypto.EC.EC_pub object. + """ + with BIO.openfile(file) as bio: + return load_pub_key_bio(bio) + + +def load_key_string_pubkey(string, callback=util.passphrase_callback): + # type: (str, Callable) -> PKey + """ + Load an M2Crypto.EC.PKey from a public key as a string. + + :param string: String containing the key in PEM format. + + :param callback: A Python callable object that is invoked + to acquire a passphrase with which to protect the + key. + + :return: M2Crypto.EC.PKey object. + """ + with BIO.MemoryBuffer(string) as bio: + return EVP.load_key_bio_pubkey(bio, callback) + + +def load_pub_key_bio(bio): + # type: (BIO.BIO) -> EC_pub + """ + Load an EC public key from an M2Crypto.BIO.BIO object. + + :param bio: M2Crypto.BIO.BIO object containing EC public key in PEM + format. + + :return: M2Crypto.EC.EC_pub object. + """ + ec = m2.ec_key_read_pubkey(bio._ptr()) + if ec is None: + ec_error() + return EC_pub(ec, 1) + + +def ec_error(): + # type: () -> ECError + raise ECError(Err.get_error_message()) + + +def pub_key_from_der(der): + # type: (bytes) -> EC_pub + """ + Create EC_pub from DER. + """ + return EC_pub(m2.ec_key_from_pubkey_der(der), 1) + + +def pub_key_from_params(curve, bytes): + # type: (bytes, bytes) -> EC_pub + """ + Create EC_pub from curve name and octet string. + """ + return EC_pub(m2.ec_key_from_pubkey_params(curve, bytes), 1) + + +def get_builtin_curves(): + # type: () -> Tuple[Dict[str, Union[int, str]]] + return m2.ec_get_builtin_curves() |