def dispute(self, issuer_id, check_contract_phase=True): """ submits a dispute for the given (malicious) share issuer to the smart contract """ if check_contract_phase: assert self.contract.sharing_confirmed() issuer = self.nodes[issuer_id - 1] # distingush between two cases # a) the computed public key or (at least one) public coefficient is not a valid elliptic curve point for i, c in enumerate(issuer.public_coefficients): if not crypto.is_on_curve(c): self.contract.dispute_public_coefficient( issuer.account, issuer.encrypted_shares, utils.flatten(issuer.public_coefficients), i, transact={'from': self.account}, ) return # b) the (encrypted) share is invalid return self.contract.dispute_share( issuer.account, issuer.encrypted_shares, utils.flatten(issuer.public_coefficients), vss.shared_key(self.sk, issuer.pk), vss.shared_key_proof(self.sk, issuer.pk), transact={'from': self.account}, )
def load_shares(self, issuer_id: int, encrypted_shares: List[int], public_coefficients: List[PointG1]) -> None: """ 1. stores the given information 2. extracts and decrypts the share for the node itself 3. verifies this share and raises an ValueError if it is not valid """ assert len(encrypted_shares ) == self.n - 1, "invalid number of encrypted shares" assert len(public_coefficients ) == self.t - 1, "invalid number of public_coefficients" issuer = self.nodes[issuer_id - 1] issuer.encrypted_shares = encrypted_shares issuer.public_coefficients = public_coefficients share_idx = self.id - 1 if self.id < issuer_id else self.id - 2 encrypted_share = encrypted_shares[share_idx] issuer.share = vss.decrypt_share(encrypted_share, self.id, vss.shared_key(self.sk, issuer.pk)) # verify that coefficients are valid points issuer.coefficients_ok = all( crypto.is_on_curve(c) for c in issuer.public_coefficients) issuer.share_ok = issuer.coefficients_ok and vss.verify( self.id, issuer.share, public_coefficients=[issuer.pk] + issuer.public_coefficients) if not issuer.share_ok: raise ValueError("Share verification failed.")
def manipulate_share(node, sid): node.shares[sid - 1] += 1 node.encrypted_shares = [] for n, share in zip(node.nodes, node.shares): if n != node: shared_key = vss.shared_key(node.sk, n.pk) encrypted_share = vss.encrypt_share(share, n.id, shared_key) node.encrypted_shares.append(encrypted_share)
def test_verify_decryption_key(): sk1, sk2 = random_scalar(), random_scalar() pk1, pk2 = multiply(G1, sk1), multiply(G1, sk2) shared_key = vss.shared_key(sk1, pk2) chal, resp = vss.shared_key_proof(sk1, pk2) assert vss.dleq_verify(G1, pk1, pk2, shared_key, chal, resp) assert contract.verify_decryption_key(shared_key, [chal, resp], pk1, pk2)
def test_load_invalid_shares(): nodes = setup_and_register() bad_node = nodes[0] bad_node.encrypted_shares = [ vss.encrypt_share(share + 1, receiver.id, vss.shared_key(bad_node.sk, receiver.pk)) for share, receiver in zip(bad_node.shares[1:], nodes[1:]) ] for receiver in nodes[1:]: with pytest.raises(ValueError): receiver.load_shares(bad_node.id, bad_node.encrypted_shares, bad_node.public_coefficients)
def init_secret_sharing(self, nodes: List['Node'], id: int = None, threshold: int = None): """ 1. assign the id obtained from the registration to the node 2. derive/pick secret coefficients to initialize the node's secret polynomial 3. compute shares of all nodes 4. compute public coefficients 5. store the node's share for itself Args: id: the id the node got assigned during the registration nodes: list of all registered nodes threshold: number of collaborating nodes required to recover; set to floor(N/2) + 1 if not provided """ assert self.sk is not None, "call to keygen() is required before starting secret sharing" if id is not None: self.id = id assert self.id is not None, 'assignment of an id is required prior to the key sharing operation' self.n = len(nodes) self.t = threshold or (self.n // 2 + 1) self.nodes = [self if node.idx == self.idx else node for node in nodes] assert [node.id for node in nodes] == list(range( 1, self.n + 1)), 'use of the ids [1, 2, ..., n] is required' shares, public_coefficients = vss.share(self.sk, self.n, self.t) self.share = shares[self.idx] self.share_ok = True self.coefficients_ok = True self.shares = shares # encrypt all shares for OTHER nodes used individual shared keys self.encrypted_shares = [] for node, share in zip(self.nodes, shares): if node != self: shared_key = vss.shared_key(self.sk, node.pk) encrypted_share = vss.encrypt_share(share, node.id, shared_key) self.encrypted_shares.append(encrypted_share) # remove C0 to form the list of public_coefficients as commitments to the shares, # C0 is already given by the node's public keys self.public_coefficients = public_coefficients[1:]
def test_verification_of_invalid_shares(test_dispute=True): contract, nodes = setup_multiple(3, register=True) issuer, verifier = nodes[0], nodes[2] share = issuer.shares[verifier.id - 1] invalid_share = share + 1 invalid_encrypted_share = vss.encrypt_share( invalid_share, verifier.id, vss.shared_key(issuer.sk, verifier.pk)) issuer.encrypted_shares[-1] = invalid_encrypted_share utils.run([node.share_key for node in nodes]) utils.mine_blocks_until(contract.sharing_confirmed) with pytest.raises(ValueError): verifier.load_shares() # dispute should succeed and not raise any error if test_dispute: verifier.dispute(issuer_id=issuer.id)
def share_key(): logging.info('KEY SHARING PHASE STARTED') logging.info() node.init_secret_sharing() logging.info(f'loading registration data...') logging.info(f' assigned id for this node: {node.id}') logging.info(f' number of register nodes (n): {node.n}') logging.info(f' signing / key recovery threshold (t): {node.t}') logging.info() logging.info('generating key shares...') for n, s in zip(node.nodes, node.shares): logging.info(f' node {n.id}: {s}') logging.info() if args.send_invalid_shares is not None: sids = args.send_invalid_shares logging.info() for sid in sids: logging.info(f'MANIPULATING SHARE FOR NODE WITH ID {sid}') node.shares[sid - 1] += 1 logging.info() node.encrypted_shares = [] for n, share in zip(node.nodes, node.shares): if n != node: shared_key = vss.shared_key(node.sk, n.pk) encrypted_share = vss.encrypt_share(share, n.id, shared_key) node.encrypted_shares.append(encrypted_share) logging.info('encrypting key shares...') j = 0 for n in node.nodes: if n.id == node.id: logging.info(f' node {n.id}: <no encrypted share for oneself>') else: logging.info(f' node {n.id}: {node.shares[j]}') j += 1 logging.info() logging.info('sending key sharing transaction...') tx = node.share_key() log_tx(tx, 'key sharing transaction confirmed')