Source code for bbclib.libs.bbclib_keypair

# -*- coding: utf-8 -*-
"""
Copyright (c) 2018 beyond-blockchain.org.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
"""
import sys
import os
import platform

from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import hashes, serialization
from cryptography.hazmat.primitives.asymmetric import ec, utils
from cryptography import x509
import cryptography

current_dir = os.path.abspath(os.path.dirname(__file__))
sys.path.append(os.path.join(current_dir, "../.."))


def _convert_binary_to_bigint(bindat):
    bn = BACKEND_KP.private_key_obj._backend._ffi.NULL
    bn_ptr = BACKEND_KP.private_key_obj._backend._lib.BN_bin2bn(bindat, len(bindat), bn)
    return BACKEND_KP.private_key_obj._backend._bn_to_int(bn_ptr)


[docs]class KeyType: NOT_INITIALIZED = 0 ECDSA_SECP256k1 = 1 ECDSA_P256v1 = 2
[docs]class KeyPairPy: POINT_CONVERSION_COMPRESSED = 2 # same as enum point_conversion_form_t in openssl/crypto/ec.h POINT_CONVERSION_UNCOMPRESSED = 4 # same as enum point_conversion_form_t in openssl/crypto/ec.h """Key pair container""" def __init__(self, curvetype=KeyType.ECDSA_P256v1, compression=False, privkey=None, pubkey=None): self.curvetype = curvetype self.private_key_len = None self.private_key = None self.private_key_obj = None self.public_key_obj = None self.public_key_len = None self.public_key = None if compression: self.key_compression = KeyPairPy.POINT_CONVERSION_COMPRESSED else: self.key_compression = KeyPairPy.POINT_CONVERSION_UNCOMPRESSED if privkey is not None: self.mk_keyobj_from_private_key(privkey) if pubkey is not None: self._mk_keyobj_from_public_key(pubkey) self.public_key_len = len(pubkey) self.public_key = pubkey def _get_naive_private_key_bytes(self): if self.private_key_obj is None: return if self.private_key_obj.curve.name == ec.SECP256R1().name: self.curvetype = KeyType.ECDSA_P256v1 elif self.private_key_obj.curve.name == ec.SECP256K1().name: self.curvetype = KeyType.ECDSA_SECP256k1 else: return bn = self.private_key_obj._backend._lib.EC_KEY_get0_private_key(self.private_key_obj._ec_key) bn_num_bytes = self.private_key_obj._backend._lib.BN_num_bytes(bn) bin_ptr = self.private_key_obj._backend._ffi.new("unsigned char[]", bn_num_bytes) bin_len = self.private_key_obj._backend._lib.BN_bn2bin(bn, bin_ptr) self.private_key = bytes(bin_ptr) self.private_key_len = bin_len def _get_naive_public_key_bytes(self): if self.public_key_obj is None: return if self.key_compression == KeyPairPy.POINT_CONVERSION_COMPRESSED: self.public_key = self.public_key_obj.public_bytes(serialization.Encoding.X962, serialization.PublicFormat.CompressedPoint) else: self.public_key = self.public_key_obj.public_bytes(serialization.Encoding.X962, serialization.PublicFormat.UncompressedPoint) self.public_key_len = len(self.public_key) def _mk_keyobj_from_public_key(self, pubkey): if self.curvetype == KeyType.ECDSA_P256v1: self.public_key_obj = ec.EllipticCurvePublicKey.from_encoded_point(ec.SECP256R1(), pubkey) elif self.curvetype == KeyType.ECDSA_SECP256k1: self.public_key_obj = ec.EllipticCurvePublicKey.from_encoded_point(ec.SECP256K1(), pubkey) else: return
[docs] def generate(self): """Generate a new key pair""" if self.curvetype == KeyType.ECDSA_P256v1: self.private_key_obj = ec.generate_private_key(ec.SECP256R1(), default_backend()) elif self.curvetype == KeyType.ECDSA_SECP256k1: self.private_key_obj = ec.generate_private_key(ec.SECP256K1(), default_backend()) self.public_key_obj = self.private_key_obj.public_key() self._get_naive_private_key_bytes() self._get_naive_public_key_bytes()
[docs] def mk_keyobj_from_private_key(self, privkey): """Make a keypair object from the binary data of private key""" bn = BACKEND_KP.private_key_obj._backend._ffi.NULL bn_ptr = BACKEND_KP.private_key_obj._backend._lib.BN_bin2bn(privkey, len(privkey), bn) secret_val = BACKEND_KP.private_key_obj._backend._bn_to_int(bn_ptr) if self.curvetype == KeyType.ECDSA_P256v1: self.private_key_obj = ec.derive_private_key(secret_val, ec.SECP256R1(), default_backend()) elif self.curvetype == KeyType.ECDSA_SECP256k1: self.private_key_obj = ec.derive_private_key(secret_val, ec.SECP256K1(), default_backend()) self._get_naive_private_key_bytes() self.public_key_obj = self.private_key_obj.public_key() self._get_naive_public_key_bytes()
[docs] def mk_keyobj_from_private_key_der(self, derdat): """Make a keypair object from the private key in DER format""" self.private_key_obj = serialization.load_der_private_key(derdat, password=None, backend=default_backend()) self._get_naive_private_key_bytes() self.curvetype = self.private_key self.public_key_obj = self.private_key_obj.public_key() self._get_naive_public_key_bytes()
[docs] def mk_keyobj_from_private_key_pem(self, pemdat_string): """Make a keypair object from the private key in PEM format""" self.private_key_obj = serialization.load_pem_private_key(pemdat_string, password=None, backend=default_backend()) self._get_naive_private_key_bytes() self.curvetype = self.private_key self.public_key_obj = self.private_key_obj.public_key() self._get_naive_public_key_bytes()
[docs] def import_publickey_cert_pem(self, cert_pemstring, privkey_pemstring=None): """Verify and import X509 public key certificate in pem format""" ## TODO: This method is not tested. It may have bugs. cert = x509.load_pem_x509_certificate(cert_pemstring.encode('utf-8'), default_backend()) fingerprint = cert.fingerprint(hashes.SHA256()) if privkey_pemstring is not None: self.mk_keyobj_from_private_key_pem(privkey_pemstring) sig = self.private_key_obj.sign(fingerprint, ec.ECDSA(utils.Prehashed(hashes.SHA256()))) public_key = cert.public_key() result = public_key.verify(sig, fingerprint, ec.ECDSA(hashes.SHA256())) if not result: return False self.private_key_obj = public_key self._get_naive_private_key_bytes() self._get_naive_public_key_bytes() return True
[docs] def to_binary(self, dat): byteval = bytearray() if self.public_key_len > 0: for i in range(self.public_key_len): byteval.append(dat % 256) dat = dat // 256 else: while True: byteval.append(dat % 256) dat = dat // 256 if dat == 0: break return byteval
[docs] def get_private_key_in_der(self): """Return private key in DER format""" serialized_private = self.private_key_obj.private_bytes( encoding=serialization.Encoding.DER, format=serialization.PrivateFormat.PKCS8, encryption_algorithm=serialization.NoEncryption() ) return serialized_private
[docs] def get_public_key_in_der(self): """Return private key in DER format""" serialized_public = self.public_key_obj.public_bytes( encoding=serialization.Encoding.DER, format=serialization.PublicFormat.SubjectPublicKeyInfo ) return serialized_public
[docs] def get_private_key_in_pem(self): """Return private key in PEM format""" serialized_private = self.private_key_obj.private_bytes( encoding=serialization.Encoding.PEM, format=serialization.PrivateFormat.PKCS8, encryption_algorithm=serialization.NoEncryption() ) return serialized_private
[docs] def get_public_key_in_pem(self): """Return public key in PEM format""" serialized_public = self.public_key_obj.public_bytes( encoding=serialization.Encoding.PEM, format=serialization.PublicFormat.SubjectPublicKeyInfo ) return serialized_public
[docs] def sign(self, digest): """Sign to the given value Args: digest (bytes): given value Returns: bytes: signature """ sig = self.private_key_obj.sign(digest, ec.ECDSA(utils.Prehashed(hashes.SHA256()))) sig_rs = utils.decode_dss_signature(sig) sig_r = int.to_bytes(sig_rs[0], 32, "big") sig_s = int.to_bytes(sig_rs[1], 32, "big") return bytes(bytearray(sig_r)+bytearray(sig_s))
[docs] def verify(self, digest, sig): """Verify the digest and the signature using the private key in this object""" sig_r = sig[:32] sig_s = sig[32:] signature = utils.encode_dss_signature(_convert_binary_to_bigint(sig_r), _convert_binary_to_bigint(sig_s)) try: self.public_key_obj.verify(signature, digest, ec.ECDSA(utils.Prehashed(hashes.SHA256()))) except cryptography.exceptions.InvalidSignature: return False return True
BACKEND_KP = KeyPairPy() BACKEND_KP.generate() try: directory, filename = os.path.split(os.path.realpath(__file__)) if platform.system() == "Windows": if not os.path.exists(os.path.join(directory, "libbbcsig.dll")): raise Exception("DLL not exists") elif platform.system() == "Darwin": if not os.path.exists(os.path.join(directory, "libbbcsig.dylib")): raise Exception("DLL not exists") else: if not os.path.exists(os.path.join(directory, "libbbcsig.so")): raise Exception("DLL not exists") from bbclib.libs import bbclib_keypair_fast KeyPair = bbclib_keypair_fast.KeyPairFast except: KeyPair = KeyPairPy