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
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)
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)
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()
def blake2b_32(v=b''): """ Get a BLAKE2B hash of bytes """ return blake2b(scrub_input(v), digest_size=32)
def test_scrub_input(self, input_data, expected): self.assertEqual(expected, scrub_input(input_data))