Пример #1
0
    def verify(self, signature: Union[str, bytes],
               message: Union[str, bytes]) -> None:
        """Verify signature, raise exception if it is not valid.

        :param message: sequance of bytes, raw format or hexadecimal notation
        :param signature: a signature in base58 encoding
        :raises: ValueError if signature is not valid
        """
        encoded_signature = scrub_input(signature)
        encoded_message = scrub_input(message)

        if not self.public_point:
            raise ValueError("Cannot verify without a public key")

        if encoded_signature[:3] != b'sig':  # not generic
            if self.curve != encoded_signature[:2]:  # "sp", "p2" "ed"
                raise ValueError("Signature and public key curves mismatch.")

        decoded_signature = base58_decode(encoded_signature)

        # Ed25519
        if self.curve == b"ed":
            digest = pysodium.crypto_generichash(encoded_message)
            try:
                pysodium.crypto_sign_verify_detached(decoded_signature, digest,
                                                     self.public_point)
            except ValueError as exc:
                raise ValueError('Signature is invalid.') from exc
        # Secp256k1
        elif self.curve == b"sp":
            pk = secp256k1.PublicKey(self.public_point, raw=True)
            sig = pk.ecdsa_deserialize_compact(decoded_signature)
            if not pk.ecdsa_verify(encoded_message, sig, digest=blake2b_32):
                raise ValueError('Signature is invalid.')
        # P256
        elif self.curve == b"p2":
            pk = fastecdsa.encoding.sec1.SEC1Encoder.decode_public_key(
                self.public_point, curve=fastecdsa.curve.P256)
            r, s = bytes_to_int(decoded_signature[:32]), bytes_to_int(
                decoded_signature[32:])
            if not fastecdsa.ecdsa.verify(
                    sig=(r, s), msg=encoded_message, Q=pk,
                    hashfunc=blake2b_32):
                raise ValueError('Signature is invalid.')
        else:
            raise Exception(
                f'Unknown elliptic curve {self.curve}')  # type: ignore
Пример #2
0
    def from_encoded_key(cls, key, passphrase=''):
        """ Creates a key object from a base58 encoded key.

        :param key: a public or secret key in base58 encoding
        :param passphrase: the passphrase used if the key provided is an encrypted private key
        """
        key = scrub_input(key)

        curve = key[:2]  # "sp", "p2" "ed"
        if curve not in [b'sp', b'p2', b'ed']:
            raise ValueError("Invalid prefix for a key encoding.")
        if not len(key) in [54, 55, 88, 98]:
            raise ValueError("Invalid length for a key encoding.")

        encrypted = (key[2:3] == b'e')
        public_or_secret = key[3:5] if encrypted else key[2:4]
        if public_or_secret not in [b'pk', b'sk']:
            raise Exception("Invalid prefix for a key encoding.")

        key = base58_decode(key)
        is_secret = (public_or_secret == b'sk')
        if not is_secret:
            return cls.from_public_point(key, curve)

        if encrypted:
            if not passphrase:
                raise ValueError(
                    "Encrypted key provided without a passphrase.")
            if isinstance(passphrase, str):
                passphrase = passphrase.encode()
            assert isinstance(
                passphrase, bytes
            ), f'expected bytes or str, got {type(passphrase).__name__}'

            salt, encrypted_sk = key[:8], key[8:]
            encryption_key = hashlib.pbkdf2_hmac(hash_name="sha512",
                                                 password=passphrase,
                                                 salt=salt,
                                                 iterations=32768,
                                                 dklen=32)
            key = pysodium.crypto_secretbox_open(c=encrypted_sk,
                                                 nonce=b'\000' * 24,
                                                 k=encryption_key)
            del passphrase

        return cls.from_secret_exponent(key, curve)
Пример #3
0
    def from_encoded_key(
        cls,
        key: Union[str, bytes],
        passphrase: PassphraseInput = None,
    ) -> 'Key':
        """Creates a key object from a base58 encoded key.

        :param key: a public or secret key in base58 encoding
        :param passphrase: the passphrase used if the key provided is an encrypted private key,
            if not set value from from PYTEZOS_PASSPHRASE env variable will be used or promted dynamically
        """
        encoded_key = scrub_input(key)

        curve = encoded_key[:2]  # "sp", "p2" "ed"
        if curve not in [b'sp', b'p2', b'ed']:
            raise ValueError("Invalid prefix for a key encoding.")
        if not len(encoded_key) in [54, 55, 88, 98]:
            raise ValueError("Invalid length for a key encoding.")

        encrypted = encoded_key[2:3] == b'e'
        public_or_secret = encoded_key[3:5] if encrypted else encoded_key[2:4]
        if public_or_secret not in [b'pk', b'sk']:
            raise Exception("Invalid prefix for a key encoding.")

        encoded_key = base58_decode(encoded_key)
        is_secret = public_or_secret == b'sk'
        if not is_secret:
            return cls.from_public_point(encoded_key, curve)

        if encrypted:
            passphrase = get_passphrase(passphrase)

            salt, encrypted_sk = encoded_key[:8], encoded_key[8:]
            encryption_key = hashlib.pbkdf2_hmac(hash_name="sha512",
                                                 password=passphrase,
                                                 salt=salt,
                                                 iterations=32768,
                                                 dklen=32)
            encoded_key = pysodium.crypto_secretbox_open(
                c=encrypted_sk,
                nonce=b'\000' * 24,
                k=encryption_key,
            )
            del passphrase

        return cls.from_secret_exponent(encoded_key, curve)
Пример #4
0
    def sign(self, message, generic=False):
        """ Sign a raw sequence of bytes.

        :param message: sequence of bytes, raw format or hexadecimal notation
        :param generic: do not specify elliptic curve if set to True
        :returns: signature in base58 encoding
        """
        message = scrub_input(message)

        if not self.secret_exponent:
            raise ValueError("Cannot sign without a secret key.")

        # Ed25519
        if self.curve == b"ed":
            digest = pysodium.crypto_generichash(message)
            signature = pysodium.crypto_sign_detached(digest,
                                                      self.secret_exponent)
        # Secp256k1
        elif self.curve == b"sp":
            pk = secp256k1.PrivateKey(self.secret_exponent)
            signature = pk.ecdsa_serialize_compact(
                pk.ecdsa_sign(message, digest=blake2b_32))
        # P256
        elif self.curve == b"p2":
            r, s = fastecdsa.ecdsa.sign(msg=message,
                                        d=bytes_to_int(self.secret_exponent),
                                        hashfunc=blake2b_32)
            signature = r.to_bytes(32, 'big') + s.to_bytes(32, 'big')
        else:
            assert False

        if generic:
            prefix = b'sig'
        else:
            prefix = self.curve + b'sig'

        return base58_encode(signature, prefix).decode()
Пример #5
0
def blake2b_32(v=b''):
    """ Get a BLAKE2B hash of bytes
    """
    return blake2b(scrub_input(v), digest_size=32)
Пример #6
0
 def test_scrub_input(self, input_data, expected):
     self.assertEqual(expected, scrub_input(input_data))