def test_verify(testerchain, signature_verifier): message = os.urandom(100) # Generate Umbral key umbral_privkey = UmbralPrivateKey.gen_key() umbral_pubkey = umbral_privkey.get_pubkey() umbral_pubkey_bytes = umbral_pubkey.to_bytes(is_compressed=False) # Sign message using SHA-256 hash signer = Signer(umbral_privkey) signature = signer(message) # Get recovery id (v) before using contract v = get_signature_recovery_value(message, signature, umbral_pubkey) recoverable_signature = bytes(signature) + v # Verify signature assert signature_verifier.functions.verify(message, recoverable_signature, umbral_pubkey_bytes[1:], ALGORITHM_SHA256).call() # Verify signature using wrong key umbral_privkey = UmbralPrivateKey.gen_key() umbral_pubkey_bytes = umbral_privkey.get_pubkey().to_bytes(is_compressed=False) assert not signature_verifier.functions.verify(message, recoverable_signature, umbral_pubkey_bytes[1:], ALGORITHM_SHA256).call()
def test_ecdsa_signature_recovery(execution_number): privkey = UmbralPrivateKey.gen_key() pubkey = privkey.get_pubkey() signer = Signer(private_key=privkey) message = b"peace at dawn" signature = signer(message=message) assert signature.verify(message, pubkey) v_value = 27 pubkey_bytes = recover_pubkey_from_signature(message=message, signature=signature, v_value_to_try=v_value) if not pubkey_bytes == pubkey.to_bytes(): v_value = 28 pubkey_bytes = recover_pubkey_from_signature(message=message, signature=signature, v_value_to_try=v_value) assert pubkey_bytes == pubkey.to_bytes() assert bytes([v_value - 27 ]) == get_signature_recovery_value(message, signature, pubkey) hash_function = hashes.Hash(hashes.SHA256(), backend=backend) hash_function.update(message) prehashed_message = hash_function.finalize() v_value = 27 pubkey_bytes = recover_pubkey_from_signature(message=prehashed_message, signature=signature, v_value_to_try=v_value, is_prehashed=True) if not pubkey_bytes == pubkey.to_bytes(): v_value = 28 pubkey_bytes = recover_pubkey_from_signature(message=prehashed_message, signature=signature, v_value_to_try=v_value, is_prehashed=True) assert pubkey_bytes == pubkey.to_bytes() assert bytes([v_value - 27 ]) == get_signature_recovery_value(prehashed_message, signature, pubkey, is_prehashed=True)
def test_recover(testerchain, signature_verifier): message = os.urandom(100) # Prepare message hash hash_ctx = hashes.Hash(hashes.SHA256(), backend=backend) hash_ctx.update(message) message_hash = hash_ctx.finalize() # Generate Umbral key and extract "address" from the public key umbral_privkey = UmbralPrivateKey.gen_key() umbral_pubkey = umbral_privkey.get_pubkey() umbral_pubkey_bytes = umbral_privkey.get_pubkey().to_bytes( is_compressed=False) signer_address = keccak_digest(umbral_pubkey_bytes[1:]) signer_address = to_normalized_address(signer_address[12:]) # Sign message signer = Signer(umbral_privkey) signature = signer(message) # Get recovery id (v) before using contract # If we don't have recovery id while signing then we should try to recover public key with different v # Only the correct v will match the correct public key v = get_signature_recovery_value(message, signature, umbral_pubkey) recoverable_signature = bytes(signature) + v # Check recovery method in the contract assert signer_address == to_normalized_address( signature_verifier.functions.recover(message_hash, recoverable_signature).call()) # Also numbers 27 and 28 can be used for v recoverable_signature = recoverable_signature[:-1] + bytes( [recoverable_signature[-1] + 27]) assert signer_address == to_normalized_address( signature_verifier.functions.recover(message_hash, recoverable_signature).call()) # Only number 0,1,27,28 are supported for v recoverable_signature = bytes(signature) + bytes([2]) with pytest.raises((TransactionFailed, ValueError)): signature_verifier.functions.recover(message_hash, recoverable_signature).call() # Signature must include r, s and v recoverable_signature = bytes(signature) with pytest.raises((TransactionFailed, ValueError)): signature_verifier.functions.recover(message_hash, recoverable_signature).call()
def precompute_values(self) -> bytes: capsule = self.task.capsule cfrag = self.task.cfrag umbral_params = default_params() e, v, _ = capsule.components() e1 = cfrag.point_e1 v1 = cfrag.point_v1 e2 = cfrag.proof.point_e2 v2 = cfrag.proof.point_v2 u = umbral_params.u u1 = cfrag.proof.point_kfrag_commitment u2 = cfrag.proof.point_kfrag_pok metadata = cfrag.proof.metadata h = self.get_proof_challenge_scalar() e1h = h * e1 v1h = h * v1 u1h = h * u1 z = cfrag.proof.bn_sig ez = z * e vz = z * v uz = z * u only_y_coord = dict(x_coord=False, y_coord=True) # E points e_y = get_coordinates_as_bytes(e, **only_y_coord) ez_xy = get_coordinates_as_bytes(ez) e1_y = get_coordinates_as_bytes(e1, **only_y_coord) e1h_xy = get_coordinates_as_bytes(e1h) e2_y = get_coordinates_as_bytes(e2, **only_y_coord) # V points v_y = get_coordinates_as_bytes(v, **only_y_coord) vz_xy = get_coordinates_as_bytes(vz) v1_y = get_coordinates_as_bytes(v1, **only_y_coord) v1h_xy = get_coordinates_as_bytes(v1h) v2_y = get_coordinates_as_bytes(v2, **only_y_coord) # U points uz_xy = get_coordinates_as_bytes(uz) u1_y = get_coordinates_as_bytes(u1, **only_y_coord) u1h_xy = get_coordinates_as_bytes(u1h) u2_y = get_coordinates_as_bytes(u2, **only_y_coord) # Get hashed KFrag validity message hash_function = hashes.Hash(hashes.SHA256(), backend=backend) kfrag_id = cfrag.kfrag_id precursor = cfrag.point_precursor delegating_pubkey = self.delegating_pubkey receiving_pubkey = self.receiving_pubkey validity_input = (kfrag_id, delegating_pubkey, receiving_pubkey, u1, precursor) kfrag_validity_message = bytes().join(bytes(item) for item in validity_input) hash_function.update(kfrag_validity_message) hashed_kfrag_validity_message = hash_function.finalize() # Get KFrag signature's v value kfrag_signature_v = get_signature_recovery_value(message=hashed_kfrag_validity_message, signature=cfrag.proof.kfrag_signature, public_key=self.verifying_pubkey, is_prehashed=True) cfrag_signature_v = get_signature_recovery_value(message=bytes(cfrag), signature=self.task.cfrag_signature, public_key=self.ursula_pubkey) metadata_signature_v = get_signature_recovery_value(message=self.task.signature, signature=metadata, public_key=self.ursula_pubkey) specification = self.task.get_specification(ursula_pubkey=self.ursula_pubkey, alice_address=self.alice_address, blockhash=self.blockhash, ursula_identity_evidence=self.ursula_identity_evidence) specification_signature_v = get_signature_recovery_value(message=specification, signature=self.task.signature, public_key=self.bob_verifying_key) ursula_pubkey_prefix_byte = bytes(self.ursula_pubkey)[0:1] # Bundle everything together pieces = ( e_y, ez_xy, e1_y, e1h_xy, e2_y, v_y, vz_xy, v1_y, v1h_xy, v2_y, uz_xy, u1_y, u1h_xy, u2_y, hashed_kfrag_validity_message, self.alice_address, # The following single-byte values are interpreted as a single bytes5 variable by the Solidity contract kfrag_signature_v, cfrag_signature_v, metadata_signature_v, specification_signature_v, ursula_pubkey_prefix_byte, ) return b''.join(pieces)