Пример #1
0
def get_signature_recovery_value(message: bytes,
                                 signature: Union[bytes, Signature],
                                 public_key: Union[bytes, UmbralPublicKey],
                                 is_prehashed: bool = False) -> bytes:
    """
    Obtains the recovery value of a standard ECDSA signature.

    :param message: Signed message
    :param signature: The signature from which the pubkey is recovered
    :param public_key: The public key for verifying the signature
    :param is_prehashed: True if the message is already pre-hashed. Default is False, and message will be hashed with SHA256
    :return: The compressed byte-serialized representation of the recovered public key
    """

    signature = bytes(signature)
    ecdsa_signature_size = Signature.expected_bytes_length()
    if len(signature) != ecdsa_signature_size:
        raise ValueError(
            f"The signature size should be {ecdsa_signature_size} B.")

    kwargs = dict(hasher=None) if is_prehashed else {}
    for v in (0, 1):
        v_byte = bytes([v])
        recovered_pubkey = PublicKey.from_signature_and_message(
            serialized_sig=signature + v_byte, message=message, **kwargs)
        if bytes(public_key) == recovered_pubkey.format(compressed=True):
            return v_byte
    else:
        raise ValueError(
            "Signature recovery failed. "
            "Either the message, the signature or the public key is not correct"
        )
Пример #2
0
 def _deserialize(self, value, attr, data, **kwargs):
     if isinstance(value, bytes):
         return value
     try:
         return Signature.from_bytes(b64decode(value))
     except InvalidNativeDataTypes as e:
         raise InvalidInputData(f"Could not parse {self.name}: {e}")
Пример #3
0
def recover_pubkey_from_signature(message: bytes,
                                  signature: Union[bytes, Signature],
                                  v_value_to_try: int,
                                  is_prehashed: bool = False) -> bytes:
    """
    Recovers a serialized, compressed public key from a signature.
    It allows to specify a potential v value, in which case it assumes the signature
    has the traditional (r,s) raw format. If a v value is not present, it assumes
    the signature has the recoverable format (r, s, v).

    :param message: Signed message
    :param signature: The signature from which the pubkey is recovered
    :param v_value_to_try: A potential v value to try
    :param is_prehashed: True if the message is already pre-hashed. Default is False, and message will be hashed with SHA256
    :return: The compressed byte-serialized representation of the recovered public key
    """

    signature = bytes(signature)
    expected_signature_size = Signature.expected_bytes_length()
    if not len(signature) == expected_signature_size:
        raise ValueError(
            f"The signature size should be {expected_signature_size} B.")

    if v_value_to_try in (0, 1, 27, 28):
        if v_value_to_try >= 27:
            v_value_to_try -= 27
        signature = signature + v_value_to_try.to_bytes(1, 'big')
    else:
        raise ValueError("Wrong v value. It should be 0, 1, 27 or 28.")

    kwargs = dict(hasher=None) if is_prehashed else {}
    pubkey = PublicKey.from_signature_and_message(serialized_sig=signature,
                                                  message=message,
                                                  **kwargs)
    return pubkey.format(compressed=True)
Пример #4
0
def test_verify(testerchain, signature_verifier):
    message = os.urandom(100)

    # Generate Umbral key
    umbral_privkey = UmbralPrivateKey.gen_key()
    umbral_pubkey_bytes = umbral_privkey.get_pubkey().to_bytes(is_compressed=False)

    # Sign message using SHA-256 hash
    cryptography_priv_key = umbral_privkey.to_cryptography_privkey()
    signature_der_bytes = cryptography_priv_key.sign(message, ec.ECDSA(hashes.SHA256()))
    signature = Signature.from_bytes(signature_der_bytes, der_encoded=True)

    # Prepare message hash
    hash_ctx = hashes.Hash(hashes.SHA256(), backend=backend)
    hash_ctx.update(message)
    message_hash = hash_ctx.finalize()

    # Get recovery id (v) before using contract
    # First try v = 0
    recoverable_signature = bytes(signature) + bytes([0])
    pubkey_bytes = coincurve.PublicKey.from_signature_and_message(recoverable_signature, message_hash, hasher=None) \
        .format(compressed=False)
    if pubkey_bytes != umbral_pubkey_bytes:
        # Extracted public key is not ours, that means v = 1
        recoverable_signature = bytes(signature) + bytes([1])

    # 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()
Пример #5
0
class NotAPrivateKey:
    params = default_params()

    fake_signature = Signature.from_bytes(
        b'@\xbfS&\x97\xb3\x9e\x9e\xd3\\j\x9f\x0e\x8fY\x0c\xbeS\x08d\x0b%s\xf6\x17\xe2\xb6\xcd\x95u\xaapON\xd9E\xb3\x10M\xe1\xf4u\x0bL\x99q\xd6\r\x8e_\xe5I\x1e\xe5\xa2\xcf\xe5\x8be_\x077Gz'
    )

    def public_key(self):
        return NotAPublicKey()

    def get_pubkey(self, *args, **kwargs):
        return self.public_key()

    def to_cryptography_privkey(self, *args, **kwargs):
        return self

    def sign(self, *args, **kwargs):
        return b'0D\x02 @\xbfS&\x97\xb3\x9e\x9e\xd3\\j\x9f\x0e\x8fY\x0c\xbeS\x08d\x0b%s\xf6\x17\xe2\xb6\xcd\x95u\xaap\x02 ON\xd9E\xb3\x10M\xe1\xf4u\x0bL\x99q\xd6\r\x8e_\xe5I\x1e\xe5\xa2\xcf\xe5\x8be_\x077Gz'

    @classmethod
    def stamp(cls, *args, **kwargs):
        return cls.fake_signature

    @classmethod
    def signature_bytes(cls, *args, **kwargs):
        return b'@\xbfS&\x97\xb3\x9e\x9e\xd3\\j\x9f\x0e\x8fY\x0c\xbeS\x08d\x0b%s\xf6\x17\xe2\xb6\xcd\x95u\xaapON\xd9E\xb3\x10M\xe1\xf4u\x0bL\x99q\xd6\r\x8e_\xe5I\x1e\xe5\xa2\xcf\xe5\x8be_\x077Gz'
Пример #6
0
    def from_bytes(cls, data: bytes, curve: Optional[Curve] = None) -> 'KFrag':
        """
        Instantiate a KFrag object from the serialized data.
        """
        curve = curve if curve is not None else default_curve()

        bn_size = CurveBN.expected_bytes_length(curve)
        point_size = Point.expected_bytes_length(curve)
        signature_size = Signature.expected_bytes_length(curve)
        arguments = {'curve': curve}

        splitter = BytestringSplitter(
            bn_size,  # id
            (CurveBN, bn_size, arguments),  # bn_key
            (Point, point_size, arguments),  # point_commitment
            (Point, point_size, arguments),  # point_precursor
            1,  # keys_in_signature
            (Signature, signature_size, arguments),  # signature_for_proxy
            (Signature, signature_size, arguments),  # signature_for_bob
        )
        components = splitter(data)

        return cls(identifier=components[0],
                   bn_key=components[1],
                   point_commitment=components[2],
                   point_precursor=components[3],
                   keys_in_signature=components[4],
                   signature_for_proxy=components[5],
                   signature_for_bob=components[6])
Пример #7
0
def test_sign_and_verify(execution_number):
    privkey = UmbralPrivateKey.gen_key()
    pubkey = privkey.get_pubkey()
    signer = Signer(private_key=privkey)
    message = b"peace at dawn"
    signature = signer(message=message)

    # Basic signature verification
    assert signature.verify(message, pubkey)
    assert not signature.verify(b"another message", pubkey)
    another_pubkey = UmbralPrivateKey.gen_key().pubkey
    assert not signature.verify(message, another_pubkey)

    # Signature serialization
    sig_bytes = bytes(signature)
    assert len(sig_bytes) == Signature.expected_bytes_length()
    restored_signature = Signature.from_bytes(sig_bytes)
    assert restored_signature == signature
    assert restored_signature.verify(message, pubkey)
Пример #8
0
    def sign(self, message: bytes) -> bytes:
        """
        Signs a hashed message and returns a signature.

        :param message: The message to sign

        :return: Signature in bytes
        """
        signature_der_bytes = API.ecdsa_sign(message, self._privkey)
        return Signature.from_bytes(signature_der_bytes, der_encoded=True)
Пример #9
0
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_bytes = umbral_privkey.get_pubkey().to_bytes(is_compressed=False)
    signer_address = bytearray(testerchain.interface.w3.solidityKeccak(['bytes32'], [umbral_pubkey_bytes[1:]]))
    signer_address = to_normalized_address(signer_address[12:])

    # Sign message using SHA-256 hash (because only 32 bytes hash can be used in the `ecrecover` method)
    cryptography_priv_key = umbral_privkey.to_cryptography_privkey()
    signature_der_bytes = cryptography_priv_key.sign(message, ec.ECDSA(hashes.SHA256()))
    signature = Signature.from_bytes(signature_der_bytes, der_encoded=True)

    # Get recovery id (v) before using contract
    # If we don't have recovery id while signing than we should try to recover public key with different v
    # Only the correct v will match the correct public key
    # First try v = 0
    recoverable_signature = bytes(signature) + bytes([0])
    pubkey_bytes = coincurve.PublicKey.from_signature_and_message(recoverable_signature, message_hash, hasher=None)\
        .format(compressed=False)
    if pubkey_bytes != umbral_pubkey_bytes:
        # Extracted public key is not ours, that means v = 1
        recoverable_signature = bytes(signature) + bytes([1])
        pubkey_bytes = coincurve.PublicKey.from_signature_and_message(recoverable_signature, message_hash, hasher=None)\
            .format(compressed=False)

    # Check that recovery was ok
    assert umbral_pubkey_bytes == pubkey_bytes
    # 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[0:-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()
Пример #10
0
def test_sign_serialize_and_verify(execution_number):
    sk = SecretKey.random()
    pk = sk.public_key()
    signer = Signer(sk)

    message = b"peace at dawn" + str(execution_number).encode()

    signature = signer.sign(message)

    signature_bytes = bytes(signature)
    signature_restored = Signature.from_bytes(signature_bytes)

    assert signature_restored.verify(pk, message)
Пример #11
0
def sign_data(data, umbral_privkey):
    umbral_pubkey_bytes = umbral_privkey.get_pubkey().to_bytes(is_compressed=False)

    # Prepare hash of the data
    hash_ctx = hashes.Hash(hashes.SHA256(), backend=backend)
    hash_ctx.update(data)
    data_hash = hash_ctx.finalize()

    # Sign data and calculate recoverable signature
    cryptography_priv_key = umbral_privkey.to_cryptography_privkey()
    signature_der_bytes = cryptography_priv_key.sign(data, ec.ECDSA(hashes.SHA256()))
    signature = Signature.from_bytes(signature_der_bytes, der_encoded=True)
    recoverable_signature = make_recoverable_signature(data_hash, signature, umbral_pubkey_bytes)
    return recoverable_signature
Пример #12
0
def test_verification_fail():
    sk = SecretKey.random()
    pk = sk.public_key()
    signer = Signer(sk)

    message = b"peace at dawn"
    signature = signer.sign(message)

    # wrong message
    wrong_message = b"no peace at dawn"
    assert not signature.verify(pk, wrong_message)

    # bad signature
    signature_bytes = bytes(signature)
    signature_bytes = b'\x00' + signature_bytes[1:]
    signature_restored = Signature.from_bytes(signature_bytes)

    assert not signature_restored.verify(pk, message)
Пример #13
0
    def from_bytes(cls, data: bytes, curve: ec.EllipticCurve = None):
        """
        Instantiate a KFrag object from the serialized data.
        """
        curve = curve if curve is not None else default_curve()

        bn_size = CurveBN.expected_bytes_length(curve)
        point_size = Point.expected_bytes_length(curve)

        splitter = BytestringSplitter(
            bn_size,  # id
            (CurveBN, bn_size),  # bn_key
            (Point, point_size),  # point_noninteractive
            (Point, point_size),  # point_commitment
            (Point, point_size),  # point_xcoord
            (Signature, Signature.expected_bytes_length(curve)))
        components = splitter(data)

        return cls(*components)
Пример #14
0
    def sign_data(data, umbral_privkey):
        umbral_pubkey_bytes = umbral_privkey.get_pubkey().to_bytes(
            is_compressed=False)

        # Prepare hash of the data
        hash_ctx = hashes.Hash(hashes.SHA256(), backend=backend)
        hash_ctx.update(data)
        data_hash = hash_ctx.finalize()

        # Sign data and calculate recoverable signature
        cryptography_priv_key = umbral_privkey.to_cryptography_privkey()
        signature_der_bytes = cryptography_priv_key.sign(
            data, ec.ECDSA(hashes.SHA256()))
        signature = Signature.from_bytes(signature_der_bytes, der_encoded=True)
        recoverable_signature = bytes(signature) + bytes([0])
        pubkey_bytes = coincurve.PublicKey.from_signature_and_message(recoverable_signature, data_hash, hasher=None) \
            .format(compressed=False)
        if pubkey_bytes != umbral_pubkey_bytes:
            recoverable_signature = bytes(signature) + bytes([1])
        return recoverable_signature
Пример #15
0
def test_signature_is_hashable():
    sk = SecretKey.random()
    pk = sk.public_key()
    signer = Signer(sk)

    message = b'peace at dawn'
    message2 = b'no peace at dawn'

    signature = signer.sign(message)
    signature2 = signer.sign(message2)

    assert hash(signature) != hash(signature2)

    signature_restored = Signature.from_bytes(bytes(signature))
    assert signature == signature_restored
    assert hash(signature) == hash(signature_restored)

    # Different hash, since signing involves some randomness
    signature3 = signer.sign(message)
    assert hash(signature) != hash(signature3)
Пример #16
0
    def from_bytes(cls, data: bytes, curve: Optional[Curve] = None) -> 'CorrectnessProof':
        """
        Instantiate CorrectnessProof from serialized data.
        """
        curve = curve if curve is not None else default_curve()
        bn_size = CurveBN.expected_bytes_length(curve)
        point_size = Point.expected_bytes_length(curve)
        arguments = {'curve': curve}
        splitter = BytestringSplitter(
            (Point, point_size, arguments),  # point_e2
            (Point, point_size, arguments),  # point_v2
            (Point, point_size, arguments),  # point_kfrag_commitment
            (Point, point_size, arguments),  # point_kfrag_pok
            (CurveBN, bn_size, arguments),  # bn_sig
            (Signature, Signature.expected_bytes_length(curve), arguments),  # kfrag_signature
        )
        components = splitter(data, return_remainder=True)
        components.append(components.pop() or None)

        return cls(*components)
Пример #17
0
def recover_pubkey_from_signature(prehashed_message,
                                  signature,
                                  v_value_to_try=None) -> bytes:
    """
    Recovers a serialized, compressed public key from a signature.
    It allows to specify a potential v value, in which case it assumes the signature
    has the traditional (r,s) raw format. If a v value is not present, it assumes
    the signature has the recoverable format (r, s, v).

    :param prehashed_message: Prehashed message
    :param signature: The signature from which the pubkey is recovered
    :param v_value_to_try: A potential v value to try
    :return: The compressed byte-serialized representation of the recovered public key
    """

    signature = bytes(signature)
    ecdsa_signature_size = Signature.expected_bytes_length()

    if not v_value_to_try:
        expected_signature_size = ecdsa_signature_size + 1
        if not len(signature) == expected_signature_size:
            raise ValueError(
                f"When not passing a v value, "
                f"the signature size should be {expected_signature_size} B.")
    elif v_value_to_try in (0, 1, 27, 28):
        expected_signature_size = ecdsa_signature_size
        if not len(signature) == expected_signature_size:
            raise ValueError(
                f"When passing a v value, "
                f"the signature size should be {expected_signature_size} B.")
        if v_value_to_try >= 27:
            v_value_to_try -= 27
        signature = signature + v_value_to_try.to_bytes(1, 'big')
    else:
        raise ValueError("Wrong v value. It should be 0, 1, 27 or 28.")

    pubkey = PublicKey.from_signature_and_message(serialized_sig=signature,
                                                  message=prehashed_message,
                                                  hasher=None)
    return pubkey.format(compressed=True)
Пример #18
0
 def _validate(self, value):
     try:
         Signature.from_bytes(value)
     except InvalidNativeDataTypes as e:
         raise InvalidInputData(f"Could not parse {self.name}: {e}")