def get_subnode(node, i): # Public Child key derivation (CKD) algorithm of BIP32 i_as_bytes = struct.pack(">L", i) if i & HARDENED_FLAG: raise ValueError("Prime derivation not supported") # Public derivation data = node.public_key + i_as_bytes I64 = hmac.HMAC(key=node.chain_code, msg=data, digestmod=hashlib.sha512).digest() I_left_as_exponent = int.from_bytes(I64[:32], "big") # BIP32 magic converts old public key to new public point point = SEC1Encoder.decode_public_key(node.public_key, secp256k1) result = I_left_as_exponent * secp256k1.G + point if point == Point.IDENTITY_ELEMENT: raise ValueError("Point cannot be INFINITY") # Convert public point to compressed public key public_key = SEC1Encoder.encode_public_key(result) return HDNodeType( depth=node.depth + 1, child_num=i, chain_code=I64[32:], fingerprint=hash_160(node.public_key)[:4], public_key=public_key, )
def test_decode_public_key(self): expected_public = Point( x=0xE5E2C01985AAFB6E2C3AD49F3DB5CCC54B2E63343AF405B521303D0F35835062, y=0x3DAD76DF888ABDE5ED0CC5AF1B83968EDFFCAE5D70BEDB24FDC18BB5F79499D0, curve=secp256k1, ) public_from_compressed = SEC1Encoder.decode_public_key( unhexlify(b"02e5e2c01985aafb6e2c3ad49f3db5ccc54b2e63343af405b521303d0f35835062"), secp256k1 ) public_from_uncompressed = SEC1Encoder.decode_public_key( unhexlify( b"04e5e2c01985aafb6e2c3ad49f3db5ccc54b2e63343af405b521303d0f3583506" b"23dad76df888abde5ed0cc5af1b83968edffcae5d70bedb24fdc18bb5f79499d0" ), secp256k1, ) # Same values as in `test_encode_public_key`, verified using openssl self.assertEqual(public_from_compressed, expected_public) self.assertEqual(public_from_uncompressed, expected_public) with self.assertRaises(InvalidSEC1PublicKey) as e: SEC1Encoder.decode_public_key(b"\x02", secp256k1) # invalid compressed length self.assertEqual(e.exception.args[0], "A compressed public key must be 33 bytes long") with self.assertRaises(InvalidSEC1PublicKey) as e: SEC1Encoder.decode_public_key(b"\x04", secp256k1) # invalid uncompressed length self.assertEqual(e.exception.args[0], "An uncompressed public key must be 65 bytes long") with self.assertRaises(InvalidSEC1PublicKey) as e: # invalid prefix value SEC1Encoder.decode_public_key( unhexlify(b"05e5e2c01985aafb6e2c3ad49f3db5ccc54b2e63343af405b521303d0f35835062"), secp256k1 ) self.assertEqual(e.exception.args[0], "Wrong key format") # With P256, same values as in `test_encode_public_key`, verified using openssl expected_P256 = Point( x=0x12C9DDF64B0D1F1D91D9BD729ABFB880079FA889D66604CC0B78C9CBC271824C, y=0x9A7D581BCF2ABA680B53CEDBADE03BE62FE95869DA04A168A458F369AC6A823E, curve=P256, ) public_from_compressed = SEC1Encoder.decode_public_key( unhexlify(b"0212c9ddf64b0d1f1d91d9bd729abfb880079fa889d66604cc0b78c9cbc271824c"), P256 ) self.assertEqual(public_from_compressed, expected_P256) # With secp192k1, same values as in `test_encode_public_key`, verified using openssl expected_secp192k1 = Point( x=0xA3BEC5FBA6D13E51FB55BD88DD097CB9B04F827BC151D22D, y=0xF07A73819149E8D903AA983E52AB1CFF38F0D381F940D361, curve=secp192k1, ) public_from_compressed = SEC1Encoder.decode_public_key( unhexlify(b"03a3bec5fba6d13e51fb55bd88dd097cb9b04f827bc151d22d"), secp192k1 ) self.assertEqual(public_from_compressed, expected_secp192k1)
def derive_revoke_keys(self, per_commitment_point, per_commitment_secret, revocation_basepoint, revocation_basepoint_secret): # revocationpubkey = revocation_basepoint * SHA256(revocation_basepoint || per_commitment_point) + # per_commitment_point * SHA256(per_commitment_point || revocation_basepoint) hash1 = hashlib.sha256() hash1.update(convert_to_bytes(revocation_basepoint)) hash1.update(convert_to_bytes(per_commitment_point)) rev_digest1 = hash1.digest() int_rev_digest1 = int.from_bytes(rev_digest1, byteorder='big') #print("rev_digest1 => ", rev_digest1.hex()) #print("int_rev_digest1 => ", int_rev_digest1) # revocation_basepoint * rev_digest1 R1 = revocation_basepoint * int_rev_digest1 if self.verbose: print("R1 => ", SEC1Encoder.encode_public_key(R1).hex()) hash2 = hashlib.sha256() hash2.update(convert_to_bytes(per_commitment_point)) hash2.update(convert_to_bytes(revocation_basepoint)) rev_digest2 = hash2.digest() int_rev_digest2 = int.from_bytes(rev_digest2, byteorder='big') # per_commitment_point * rev_digest2 R2 = per_commitment_point * int_rev_digest2 if self.verbose: print("R2 => ", SEC1Encoder.encode_public_key(R2).hex()) rev_pubkey = R1 + R2 if self.verbose: print("revocation_pubkey: 0x%s" % convert_to_bytes(rev_pubkey).hex()) rev_pubkey_hex = convert_to_bytes(rev_pubkey).hex() # revocationprivkey = revocation_basepoint_secret * SHA256(revocation_basepoint || per_commitment_point) + # per_commitment_secret * SHA256(per_commitment_point || revocation_basepoint) R3 = (revocation_basepoint_secret * int_rev_digest1) % curve.secp256k1.q if self.verbose: print("R3 => ", hex(R3)) R4 = (per_commitment_secret * int_rev_digest2) % curve.secp256k1.q if self.verbose: print("R4 => ", hex(R4)) rev_privkey = (R3 + R4) % curve.secp256k1.q if self.verbose: print("revocation_privkey: %s" % hex(rev_privkey)) rev_privkey_hex = hex(rev_privkey).lstrip("0x") return rev_pubkey_hex, rev_privkey_hex
def _derive_private_key(self, seed_pri, cred_id, rp_id): alg = cred_id[0] if alg != 0: raise UnknownKeyAgreementScheme(alg) eph_pub_enc = cred_id[1:][:65] eph_pub = SEC1Encoder.decode_public_key(eph_pub_enc, P256) ecdh_point = seed_pri * eph_pub ikm_x = fastecdsa.encoding.util.int_to_bytes(ecdh_point.x) cred_key = hkdf(ikm_x, binascii.a2b_hex('776562617574686e2e7265636f766572792e637265645f6b6579'), length=32) cred_key_int = int.from_bytes(cred_key, 'big', signed=False) mac_key = hkdf(ikm_x, binascii.a2b_hex('776562617574686e2e7265636f766572792e6d61635f6b6579'), length=32) full_mac = hmac(mac_key, struct.pack('>B65s32s', alg, eph_pub_enc, sha256(rp_id.encode('utf-8')))) mac = full_mac[0:16] recon_cred_id = struct.pack('>B65s16s', alg, eph_pub_enc, mac) if cred_id != recon_cred_id: raise RpIdMismatch() assert cred_key_int < N, "cred_key >= N: " + str(cred_key_int) cred_pri = cred_key_int + seed_pri % N return cred_pri
def from_secret_key(cls, secret_key: bytes, curve=b'ed'): """ Creates a key object from a secret exponent. :param secret_key: secret exponent or seed :param curve: an elliptic curve used, default is ed25519 """ # Ed25519 if curve == b'ed': # Dealing with secret key or seed? if len(secret_key) == 64: public_key = pysodium.crypto_sign_sk_to_pk(sk=secret_key) else: public_key, secret_key = pysodium.crypto_sign_seed_keypair(seed=secret_key) # Secp256k1 elif curve == b'sp': sk = secp256k1.PrivateKey(secret_key) public_key = sk.pubkey.serialize() # P256 elif curve == b'p2': pk = get_public_key(bytes_to_int(secret_key), curve=P256) public_key = SEC1Encoder.encode_public_key(pk) else: assert False return cls(public_key, secret_key, curve=curve)
def from_secret_exponent(cls, secret_exponent: bytes, curve=b'ed', activation_code=None): """ Creates a key object from a secret exponent. :param secret_exponent: secret exponent or seed :param curve: b'sp' for Secp251k1, b'p2' for P256/Secp256r1, b'ed' for Ed25519 (default) :param activation_code: secret for initializing account balance """ # Ed25519 if curve == b'ed': # Dealing with secret exponent or seed? if len(secret_exponent) == 64: public_point = pysodium.crypto_sign_sk_to_pk( sk=secret_exponent) else: public_point, secret_exponent = pysodium.crypto_sign_seed_keypair( seed=secret_exponent) # Secp256k1 elif curve == b'sp': sk = secp256k1.PrivateKey(secret_exponent) public_point = sk.pubkey.serialize() # P256 elif curve == b'p2': pk = get_public_key(bytes_to_int(secret_exponent), curve=P256) public_point = SEC1Encoder.encode_public_key(pk) else: assert False return cls(public_point, secret_exponent, curve=curve, activation_code=activation_code)
def did_callback_elaphant(request): if request.method == 'POST': response = json.loads(request.body) if request.content_type == "application/json" or 'Data' not in response.keys( ): HttpResponse(status=400) data = json.loads(response['Data']) sig = response['Sign'] client_public_key = data['PublicKey'] r, s = int(sig[:64], 16), int(sig[64:], 16) public_key = SEC1Encoder.decode_public_key( unhexlify(client_public_key), curve.P256) valid = ecdsa.verify((r, s), response['Data'], public_key) if not valid: return JsonResponse({'message': 'Unauthorized'}, status=401) try: recently_created_time = timezone.now() - timedelta(minutes=1) did_request_query_result = DIDRequest.objects.get( state=data["RandomNumber"], created_at__gte=recently_created_time) if not did_request_query_result: return JsonResponse({'message': 'Unauthorized'}, status=401) data["auth"] = True DIDRequest.objects.filter(state=data["RandomNumber"]).update( data=json.dumps(data)) except Exception as e: logging.debug(f"Method: did_callback_elaphant Error: {e}") return JsonResponse({'error': str(e)}, status=404) return JsonResponse({'result': True}, status=200)
def derive_pubkey(self, secret): P = keys.get_public_key(secret, curve.secp256k1) pub_key = SEC1Encoder.encode_public_key(P) #print("Pub key: ", pub_key) #print("pubkey : ", hexlify(pub_key)) # return hex encoding in bytes AND int encoding return hexlify(pub_key), P
def check_signature(self): if self._from == NULL_BYTE: return True if not self.is_signed(): return False to_sign = self.calculate_hash() return ecdsa.verify( self._signature, to_sign, SEC1Encoder.decode_public_key(self._from, curve=curve.P256))
def private_key_to_public_key(private_key): """ Accept a hex private key and convert it to its respective public key. Because converting a private key to a public key requires SECP256k1 ECDSA signing, this function is the most time consuming and is a bottleneck in the overall speed of the program. Average Time: 0.0031567731 seconds """ #pk = PrivateKey().fromString(bytes.fromhex(private_key)) pk2 = binascii.hexlify( SEC1Encoder.encode_public_key(secp256k1.G * int(private_key, 16), compressed=False)) #print('pubstark 04' + pk.publicKey().toString().hex().upper()) #print('pubfast ' + pk2.decode('utf-8').upper()) #return '04' + pk.publicKey().toString().hex().upper() return (pk2.decode('utf-8').upper())
def _test_runner(self, tests, curve, hashfunc): for test_group in tests: keybytes = unhexlify(test_group["key"]["uncompressed"]) public_key = SEC1Encoder.decode_public_key(keybytes, curve) for test in test_group["tests"]: try: message = unhexlify(test["msg"]) sigbytes = unhexlify(test["sig"]) signature = DEREncoder.decode_signature(sigbytes) expected = test["result"] == "valid" result = verify(signature, message, public_key, curve, hashfunc) self.assertEqual(result, expected, test) except: self.assertFalse(test["result"] == "valid", test)
def import_recovery_seed(self, exported_seed): payload = cbor.decode(exported_seed) alg = payload[1] aaguid = payload[3] sig = payload[4] assert isinstance(alg, int) assert isinstance(aaguid, bytes) assert isinstance(sig, bytes) if alg == 0: S_enc = payload[-1] assert isinstance(S_enc, bytes) S = SEC1Encoder().decode_public_key(S_enc, P256) self._seeds.append(BackupSeed(alg, aaguid, S)) else: raise UnknownKeyAgreementScheme(alg) self._state += 1
def did_callback(): if request.method == 'POST': if not request.json or not 'Data' in request.json: abort(400) data = request.json['Data'] data_json = json.loads(data) sig = request.json['Sign'] client_public_key = data_json['PublicKey'] r, s = int(sig[:64], 16), int(sig[64:], 16) public_key = SEC1Encoder.decode_public_key( unhexlify(client_public_key), curve.P256) valid = ecdsa.verify((r, s), data, public_key) if not valid: return jsonify({'message': 'Unauthorized'}), 401 try: c, conn = connection() x = c.execute( "SELECT data FROM didauth_requests WHERE state = %s AND created_at >= (NOW() - INTERVAL 1 MINUTE)", [thwart(data_json["RandomNumber"])]) if int(x) == 0: return jsonify({'message': 'Unauthorized'}), 401 auth_data = json.loads(c.fetchone()[0]) auth_data["auth"] = True auth_data = { key: value for (key, value) in (auth_data.items() + data_json.items()) } c.execute("UPDATE didauth_requests SET data = %s WHERE state = %s", (json.dumps(auth_data), data_json["RandomNumber"])) conn.commit() except Exception as e: return (str(e)) finally: if (conn): c.close() conn.close() gc.collect() return jsonify({'result': True}), 201
def verify(self, signature, message): """ Verify signature, raise exception if it is not valid :param message: sequance of bytes, raw format or hexadecimal notation :param signature: a signature in base58 encoding """ signature = scrub_input(signature) message = scrub_input(message) if not self.public_point: raise ValueError("Cannot verify without a public key") if signature[:3] != b'sig': # not generic if self.curve != signature[:2]: # "sp", "p2" "ed" raise ValueError("Signature and public key curves mismatch.") signature = base58_decode(signature) # Ed25519 if self.curve == b"ed": digest = pysodium.crypto_generichash(message) try: pysodium.crypto_sign_verify_detached(signature, digest, self.public_point) except ValueError: raise ValueError('Signature is invalid.') # Secp256k1 elif self.curve == b"sp": pk = secp256k1.PublicKey(self.public_point, raw=True) sig = pk.ecdsa_deserialize_compact(signature) if not pk.ecdsa_verify(message, sig, digest=blake2b_32): raise ValueError('Signature is invalid.') # P256 elif self.curve == b"p2": pk = SEC1Encoder.decode_public_key(self.public_point, curve=P256) r, s = bytes_to_int(signature[:32]), bytes_to_int(signature[32:]) if not verify(sig=(r, s), msg=message, Q=pk, hashfunc=blake2b_32): raise ValueError('Signature is invalid.') else: assert False
def test_encode_public_key(self): # 1/ PrivateKey generated using openssl "openssl ecparam -name secp256k1 -genkey -out ec-priv.pem" # 2/ Printed using "openssl ec -in ec-priv.pem -text -noout" and converted to numeric using "asn1._bytes_to_int" priv_key = 7002880736699640265110069622773736733141182416793484574964618597954446769264 pubkey_compressed = hexlify(SEC1Encoder.encode_public_key(secp256k1.G * priv_key)) pubkey_uncompressed = hexlify(SEC1Encoder.encode_public_key(secp256k1.G * priv_key, compressed=False)) # 3/ PublicKey extracted using "openssl ec -in ec-priv.pem -pubout -out ec-pub.pem" # 4/ Encoding verified using openssl "openssl ec -in ec-pub.pem -pubin -text -noout -conv_form compressed" self.assertEqual(pubkey_compressed, b"02e5e2c01985aafb6e2c3ad49f3db5ccc54b2e63343af405b521303d0f35835062") self.assertEqual( pubkey_uncompressed, b"04e5e2c01985aafb6e2c3ad49f3db5ccc54b2e63343af405b521303d0f3583506" b"23dad76df888abde5ed0cc5af1b83968edffcae5d70bedb24fdc18bb5f79499d0", ) # Same with P256 Curve priv_P256 = 807015861248675637760562792774171551137308512372870683367415858378856470633 pubkey_compressed = hexlify(SEC1Encoder.encode_public_key(P256.G * priv_P256)) pubkey_uncompressed = hexlify(SEC1Encoder.encode_public_key(P256.G * priv_P256, compressed=False)) self.assertEqual(pubkey_compressed, b"0212c9ddf64b0d1f1d91d9bd729abfb880079fa889d66604cc0b78c9cbc271824c") self.assertEqual( pubkey_uncompressed, b"0412c9ddf64b0d1f1d91d9bd729abfb880079fa889d66604cc0b78c9cbc271824" b"c9a7d581bcf2aba680b53cedbade03be62fe95869da04a168a458f369ac6a823e", ) # And secp192k1 Curve priv_secp192k1 = 5345863567856687638748079156318679969014620278806295592453 pubkey_compressed = hexlify(SEC1Encoder.encode_public_key(secp192k1.G * priv_secp192k1)) pubkey_uncompressed = hexlify(SEC1Encoder.encode_public_key(secp192k1.G * priv_secp192k1, compressed=False)) self.assertEqual(pubkey_compressed, b"03a3bec5fba6d13e51fb55bd88dd097cb9b04f827bc151d22d") self.assertEqual( pubkey_uncompressed, b"04a3bec5fba6d13e51fb55bd88dd097cb9b04f827bc151d22df07a73819149e8d903aa983e52ab1cff38f0d381f940d361", )
#* See the License for the specific language governing permissions and #* limitations under the License. #******************************************************************************** from ledgerblue.comm import getDongle from ledgerblue.commException import CommException from secp256k1 import PublicKey import bitcoin import struct from fastecdsa.encoding.sec1 import SEC1Encoder from fastecdsa import curve bipp44_path = ("8000002C" + "80000378" + "80000000" + "00000000" + "00000000") b = bytes.fromhex( "037edf1d72c29e6de321e95d1d0c2736223fe895009bf448e520c1333b05d6d6fd") p = SEC1Encoder.decode_public_key(b, curve=curve.P256) p2 = SEC1Encoder.encode_public_key(p, compressed=False).hex() payload = bytes.fromhex(p2 + bipp44_path) ecdhPayloadArray = [payload] dongle = getDongle(True) publicKey = dongle.exchange(bytes.fromhex("80040000FF" + bipp44_path)) print("got publicKey " + publicKey.hex()) print("compressed: " + bitcoin.compress(publicKey).hex()) print("requesting ecdh with: " + p2) for ecdhPayload in ecdhPayloadArray: try: offset = 0
def from_bytes(cls, data: bytes, curve: str) -> "ECCPublicKey": curve_type = infer_local_type(curve) public_key_impl = SEC1Encoder.decode_public_key(data, curve_type) return cls(public_key_impl, curve_type)
def to_bytes(self) -> bytes: return SEC1Encoder.encode_public_key(self.impl, compressed=False)
def encode_pub(pubkey): return SEC1Encoder().encode_public_key(pubkey, compressed=False)
def cose_key_to_point(cose): return SEC1Encoder.decode_public_key( b'\x04' + cose[-2] + cose[-3], P256 )
def convert_to_bytes(point): return SEC1Encoder.encode_public_key(point)
def convert_to_int(point): _point = SEC1Encoder.encode_public_key(point) return int.from_bytes(_point, byteorder='big')