def verify(self, signature, message): """ 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 """ signature = scrub_input(signature) message = scrub_input(message) if not self.public_point: raise ValueError("Cannot verify without a public key") if signature[:3] != b'sig': # not generic if self.curve != signature[:2]: # "sp", "p2" "ed" raise ValueError("Signature and public key curves mismatch.") signature = base58_decode(signature) # Ed25519 if self.curve == b"ed": digest = pysodium.crypto_generichash(message) try: pysodium.crypto_sign_verify_detached(signature, digest, self.public_point) except ValueError: raise ValueError('Signature is invalid.') # Secp256k1 elif self.curve == b"sp": pk = secp256k1.PublicKey(self.public_point, raw=True) sig = pk.ecdsa_deserialize_compact(signature) if not pk.ecdsa_verify(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(signature[:32]), bytes_to_int(signature[32:]) if not fastecdsa.ecdsa.verify( sig=(r, s), msg=message, Q=pk, hashfunc=blake2b_32): raise ValueError('Signature is invalid.') else: assert False
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.") salt, encrypted_sk = key[:8], key[8:] encryption_key = hashlib.pbkdf2_hmac( hash_name="sha512", password=scrub_input(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 test_b58_decode_encode(self, string, prefix): data = base58_decode(string) result = base58_encode(data, prefix) self.assertEqual(string, result)
def watermark(self): """ Chain watermark, hex encoded """ data = self.chain_id() return hexlify(base58_decode(data.encode())).decode()
def signed_bytes(self): signature_bytes = hexlify(base58_decode(self.get('signature'))).decode() return self.forge() + signature_bytes
def test_transaction_low_level(self): try: head_hash = self.shell.head.hash() contract = self.shell.head.context.contracts[self.pkh1] counter = int(contract.counter()) + 1 constants = self.shell.head.context.constants() #logger.debug(f"constants = {pformat(constants)}") gas_limit = constants['hard_gas_limit_per_operation'] storage_limit = constants['hard_storage_limit_per_operation'] trans_oper = tezos.make_transaction_operation( self.pkh1, self.pkh2, 17, head_hash, signature=self.fake_sig, counter=counter, gas_limit=gas_limit) #print(f"oper = {pformat(trans_oper)}") resp = self.node.post( "/chains/main/blocks/head/helpers/scripts/run_operation", json=trans_oper) trans_oper = trans_oper['contents'][0] resp_oper = resp['contents'][0] self.assertEqual(resp_oper['counter'], trans_oper['counter']) self.assertEqual(resp_oper['kind'], "transaction") result = resp_oper['metadata']['operation_result'] self.assertEqual(result['status'], 'applied', f'result = {pformat(result)}') consumed_gas = int(result['consumed_gas']) self.assertGreater(consumed_gas, 1000) storage_used = 0 # TODO: how to get this? not appearing in results from scriptless address protocols = self.shell.head.protocols() protocol = protocols.get("protocol") self.assertRegex(protocol, r"^P") trans_oper2 = tezos.make_transaction_operation( self.pkh1, self.pkh2, 17, head_hash, counter=counter, gas_limit=consumed_gas + 100, storage_limit=0) resp2 = self.node.post( "/chains/main/blocks/head/helpers/forge/operations", json=trans_oper2) self.assertRegex(resp2, r"^[a-f0-9]+$") oper_hex = resp2 signature = self.key1.sign("03" + oper_hex) self.assertRegex(signature, r"^edsig") trans_oper3 = tezos.make_transaction_operation( self.pkh1, self.pkh2, 17, head_hash, counter=counter, gas_limit=consumed_gas + 100, storage_limit=0, protocol=protocol, signature=signature) resp3 = self.node.post( "/chains/main/blocks/head/helpers/preapply/operations", json=[trans_oper3]) raw_signature = base58_decode(signature.encode()) hex_signature = raw_signature.hex() signed_oper_hex = oper_hex + hex_signature resp4 = self.node.post("/injection/operation?chain=main", signed_oper_hex) logger.debug(f"\nresp4 = {pformat(resp4)}\n") except RpcError as exc: formatted_error_str = json.dumps(json.loads(exc.res.text), indent=2) logger.error(f"\nRpcError: {formatted_error_str}\n")
def get_chain_watermark(self): assert isinstance(self, RpcQuery) data = self._node.get(f'chains/{self.get_chain_id()}/chain_id') return hexlify(base58_decode(data.encode())).decode()