def load_config(n=None): if n is None: n = N from hydrand import merkle from hydrand.data import ShareCorrectnessProof, NodeInfo CONFIG_DIR = os.path.join(CONFIG_BASE_DIR, f"{n:03}") if not os.path.exists(CONFIG_DIR): assert NETWORK_CONFIG != "amazon", "do never generate config on the fly for amazon tests" logging.warning("config does not exist, generating one on the fly") return generate_sample_config() addresses, ports = load_network_config() node_infos = [] for node_id in range(n): if MODE == "testing" or NODE_ID == node_id: with open(os.path.join(CONFIG_DIR, f"{node_id:03}.secret_key"), "rb") as f: keypair = KeyPair(f.read()) public_key = keypair.public_key with open(os.path.join(CONFIG_DIR, f"{node_id:03}.initial_secret"), "rb") as f: initial_secret = Scalar.from_bytes(f.read()) else: keypair = None initial_secret = None with open(os.path.join(CONFIG_DIR, f"{node_id:03}.public_key"), "rb") as f: public_key = Point.from_bytes(f.read()) with open( os.path.join(CONFIG_DIR, f"{node_id:03}.initial_pvss_shares"), "rb") as f: shares = [Point.from_bytes(f.read(32)) for i in range(n - 1)] with open(os.path.join(CONFIG_DIR, f"{node_id:03}.initial_pvss_proof"), "rb") as f: proof = ShareCorrectnessProof( commitments=[ Point.from_bytes(f.read(32)) for i in range(n - 1) ], challenge=Scalar.from_bytes(f.read(32)), responses=[ Scalar.from_bytes(f.read(32)) for i in range(n - 1) ], ) merkle_root = merkle.compute_root( [merkle.Hash(bytes(es)) for es in shares]) node_infos.append( NodeInfo(node_id, addresses[node_id], ports[node_id], keypair, public_key, initial_secret, shares, proof, merkle_root)) return node_infos
def process_recover(self, msg: RecoverMessage): if self.recovery_certificate and self.beacon: return if not self.verify_recovery_certificate_signature( msg.recovery_certificate_signature, NODE_INFOS[msg.sender].public_key ): self.flag_adversary(msg.sender) return self.recover_messages[msg.sender] = msg if len(self.recover_messages) == F + 1: self.create_recovery_certificate() rs = msg.recovered_share if rs: root_hash = self.lookup_merkle_root() share_idx = msg.sender if msg.sender < self.leader else msg.sender - 1 enc_share = Point.from_bytes(merkle.get_leaf(rs.merkle_branch, share_idx, N - 1)) # lookup dataset (header) for the leader # at least a header does exists if not merkle.verify_branch(rs.merkle_branch, root_hash, share_idx, N - 1): # type: ignore return if not pvss.verify_decrypted_share(rs.share, enc_share, NODE_INFOS[msg.sender].public_key, rs.proof): self.flag_adversary(msg.sender) return self.shares_for_recovery[msg.sender] = rs.share self.logger.info("storing share for recovery, now %d shares in total", len(self.shares_for_recovery)) if len(self.shares_for_recovery) == F + 1: secret = self.recover_shared_secret() self.compute_beacon(recovered_secret=secret) else: self.logger.warning("RECOVER MESSAGE DID NOT INCLUDE A SHARE")
""" implementation of the publicly-verifiable secret sharing protocol described in Scrape we use the DDH variant from page 12 of the Scrape paper see: https://eprint.iacr.org/2017/216.pdf """ import hashlib from copy import copy from typing import List, Optional, Tuple from hydrand.data import ShareCorrectnessProof, ShareDecryptionProof from hydrand.ed25519 import Point, Scalar, GROUP_ORDER # initialize two independent generator points g, and h G = Point.from_uniform(hashlib.sha256(bytes(Point.B)).digest()) H = Point.B class Polynomial: def __init__(self, coeffs: List[Scalar]): self.coeffs = coeffs def __call__(self, arg: int) -> Scalar: x = Scalar(arg) result = self.coeffs[0] + (self.coeffs[1] * x) x_pow = copy(x) for i in range(2, len(self.coeffs)): x_pow *= x result += self.coeffs[i] * x_pow return result @staticmethod
def get(obj_type, num_elements=None, **kwargs): mapping = { Scalar: lambda: Scalar.random(), Point: lambda: Point.base_times(Scalar.random()), Hash: lambda: Hash(secrets.token_bytes(32)), Signature: lambda: Signature(secrets.token_bytes(64)), RecoveryCertificate: lambda: RecoveryCertificate( signers=random.sample(list(range(N)), F + 1), signatures=get(Signature, F + 1), ), ConfirmationCertificate: lambda: ConfirmationCertificate( dataset_header_digest=get(Hash), signers=random.sample(list(range(N)), F + 1), signatures=get(Signature, F + 1), ), ShareCorrectnessProof: lambda: ShareCorrectnessProof( commitments=get(Point, N - 1), challenge=get(Scalar), responses=get(Scalar, N - 1), ), ShareDecryptionProof: lambda: ShareDecryptionProof( challenge=get(Scalar), response=get(Scalar), ), RecoveredShare: lambda: RecoveredShare( share=get(Point), proof=get(ShareDecryptionProof), merkle_branch=get(Hash, merkle.branch_length(N - 1)), ), DatasetHeader: lambda **kws: DatasetHeader( round_idx=kws['round_idx'], prev_round_idx=kws['prev_round_idx'], revealed_secret=get(Scalar), beacon=get(Hash), recovered_beacons=get(Hash, kws['round_idx'] - kws['prev_round_idx'] - 1), merkle_root=get(Hash), ), Dataset: lambda **kws: Dataset( round_idx=kws['round_idx'], prev_round_idx=kws['prev_round_idx'], revealed_secret=get(Scalar), beacon=get(Hash), recovered_beacons=get(Hash, kws['round_idx'] - kws['prev_round_idx'] - 1), merkle_root=get(Hash), encrypted_shares=get(Point, N - 1), proof=get(ShareCorrectnessProof), confirmation_certificate=None if kws['prev_round_idx'] == 0 else get(ConfirmationCertificate), recovery_certificates=get(RecoveryCertificate, kws['round_idx'] - kws['prev_round_idx'] - 1), ), ProposeMessage: lambda **kws: ProposeMessage( sender=random.randint(0, N - 1), dataset=get(Dataset, **kws), dataset_header_signature=get(Signature), confirmation_certificate_signature=get(Signature), ), AcknowledgeMessage: lambda **kws: AcknowledgeMessage( sender=random.randint(0, N - 1), dataset_header=get(DatasetHeader, **kws), dataset_header_signature=get(Signature), ), ConfirmMessage: lambda **kws: ConfirmMessage( sender=random.randint(0, N - 1), round_idx=kws.get('round_idx', random.randint(0, 1000_000)), dataset_header_digest=get(Hash), ), RecoverMessage: lambda **kws: RecoverMessage( sender=random.randint(0, N - 1), round_idx=kws.get('round_idx', random.randint(0, 1000_000)), recovery_certificate_signature=get(Signature), recovered_share=get(RecoveredShare) if kws.get('add_recovered_share', True) else None ), SignedMessage: lambda **kws: SignedMessage( message=get(kws['msg_type'], **kws), signature=get(Signature), ) } if num_elements is None: return mapping[obj_type](**kwargs) return [mapping[obj_type](**kwargs) for _ in range(num_elements)]
def test_from_uniform(): digest = hashlib.sha256(b"some stuff").digest() p = Point.from_uniform(digest) assert p.is_valid()
def test_neg_base_point(): Bneg = Point(fe.recover_x(B.y, sign=1), B.y) assert Bneg != B assert Bneg.x != B.x assert Bneg.y == B.y assert Bneg.sign != B.sign
def test_base_times_zero_fails(): with pytest.raises(ValueError): Point.base_times(Scalar(0))
def test_base_multiply_by_one(): assert B == Point.base_times(Scalar(1))