def derive_key_pair(seed: Seed) -> t.Tuple[PrivateKey, PublicKey]: private_key = hashes.sha512half(seed) public_key = nacl.signing.SigningKey(private_key).verify_key._key[:32] # pylint: disable=protected-access return ( t.cast(PrivateKey, private_key), t.cast(PublicKey, KEY_PREFIX + public_key), )
def derive_private_key(seed: bytes) -> int: sequence = 0 while True: buffer = seed + to_bytes(sequence, 4) private_key = from_bytes(hashes.sha512half(buffer)) if private_key != 0 and private_key < GROUP_ORDER: return private_key sequence += 1
def sign_transaction(self, transaction: Transaction) -> SignedTransaction: result = { **transaction, 'SigningPubKey': self.public_key.hex().upper() } blob = serialize_transaction(result, signing=True) signature = self.sign(PREFIX_TRANSACTION_SIGNATURE + blob) result['TxnSignature'] = signature.hex().upper() blob = serialize_transaction(result) digest = sha512half(PREFIX_TRANSACTION_ID + blob) result['hash'] = digest.hex().upper() return result
def verify(message: bytes, signature: Signature, public_key: PublicKey) -> bool: digest = hashes.sha512half(message) # We need `ecdsa` to decompress the public key. # Would be nice if fastecdsa could do this. # TODO: Implement it ourselves. # https://github.com/AntonKueltz/fastecdsa/issues/47 verifying_key = VerifyingKey.from_string(public_key, curves.SECP256k1) return verifying_key.verify( signature, digest, hashfunc=hashes.IdentityHash, sigdecode=sigdecode_der, )
def sign(message: bytes, private_key: PrivateKey) -> Signature: digest = hashes.sha512half(message) signing_key = int.from_bytes(private_key, byteorder='big') r, s = ecdsa.sign( digest.hex(), signing_key, curve=curve.secp256k1, prehashed=True, ) # Both (r, s) and (r, -s mod G = G - s) are valid, canonical signatures. # (r, s) is fully canonical only when s <= G - s. s_inverse = GROUP_ORDER - s if s > s_inverse: s = s_inverse signature = DEREncoder.encode_signature(r, s) return t.cast(Signature, signature)
def test_hash_transaction(transaction, blob_hex): if 'hash' not in transaction: return blob = serialization.PREFIX_TRANSACTION_ID + bytes.fromhex(blob_hex) digest = hashes.sha512half(blob) assert digest.hex().upper() == transaction['hash']
def test_sha512half(): """Just a sanity check that we get the same result.""" message = b'test message' stdlib = hashlib.sha512(message).digest()[:32] nacl = hashes.sha512half(message) assert stdlib == nacl
def digest(self) -> bytes: return hashes.sha512half(self.data)