Skip to content

Commit

Permalink
[tests] add tests for bls/tz4
Browse files Browse the repository at this point in the history
  • Loading branch information
spalmer25 committed Oct 22, 2024
1 parent a0c644a commit c990563
Show file tree
Hide file tree
Showing 222 changed files with 114 additions and 12 deletions.
14 changes: 13 additions & 1 deletion test/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,13 @@
2
)

TZ4_ACCOUNT = Account(
"m/44'/1729'/0'/0'",
SigScheme.BLS,
"BLsk2Q1AdMSKNJbM1fLqHEWgaEDUz2odGrgPuCV1bxtzAedVEC2RSz",
3
)

LONG_TZ1_ACCOUNT = Account(
"m/9'/12'/13'/8'/78'",
SigScheme.ED25519,
Expand All @@ -75,13 +82,18 @@
TZ3_ACCOUNT,
]

ACCOUNTS = TZ1_ACCOUNTS + TZ2_ACCOUNTS + TZ3_ACCOUNTS
TZ4_ACCOUNTS = [
TZ4_ACCOUNT,
]

ACCOUNTS = TZ1_ACCOUNTS + TZ2_ACCOUNTS + TZ3_ACCOUNTS + TZ4_ACCOUNTS

ZEBRA_ACCOUNTS = [
TZ1_ACCOUNT,
TZ2_ACCOUNT,
TZ3_ACCOUNT,
BIP32_TZ1_ACCOUNT,
TZ4_ACCOUNT,
]

EMPTY_PATH = BipPath.from_string("m")
17 changes: 17 additions & 0 deletions test/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,14 @@

"""Pytest configuration file."""

from functools import wraps
import pytest

from ragger.backend import BackendInterface
from ragger.conftest import configuration
from ragger.firmware import Firmware
from ragger.navigator import Navigator
from utils.account import SigScheme
from utils.client import TezosClient
from utils.navigator import TezosNavigator
from common import DEFAULT_SEED
Expand All @@ -44,3 +47,17 @@ def tezos_navigator(
test_name: str) -> TezosNavigator:
"""Get a tezos navigator."""
return TezosNavigator(backend, firmware, client, navigator, golden_run, test_name)

def skip_nanos_bls(func):
"""Allows to skip tests with BLS `account` on NanoS device"""
@wraps(func)
def wrap(*args, **kwargs):
account = kwargs.get("account", None)
firmware = kwargs.get("firmware", None)
if firmware is not None \
and firmware.name == "nanos" \
and account is not None \
and account.sig_scheme == SigScheme.BLS:
pytest.skip("NanoS does not support BLS")
return func(*args, **kwargs)
return wrap
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
31 changes: 29 additions & 2 deletions test/test_instructions.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,14 @@
import hmac
import time

from functools import wraps
import pytest
from conftest import skip_nanos_bls

from ragger.backend import BackendInterface
from ragger.firmware import Firmware
from utils.client import TezosClient, Version, Hwm, StatusCode
from utils.account import Account, PublicKey
from utils.account import Account, PublicKey, SigScheme
from utils.helper import get_current_commit
from utils.message import (
Message,
Expand Down Expand Up @@ -502,6 +504,7 @@ def test_ledger_screensaver(firmware: Firmware,
assert (res.find("y") != -1), "Ledger screensaver should have activated"


@skip_nanos_bls
@pytest.mark.parametrize("account", ZEBRA_ACCOUNTS)
def test_benchmark_attestation_time(account: Account,
client: TezosClient,
Expand Down Expand Up @@ -797,6 +800,23 @@ def build_block(level, current_round, chain_id):
)


def catch_nanos_bls(func):
"""Allows to catch the error to sign with BLS `account` on NanoS device"""
@wraps(func)
def wrap(*args, **kwargs):
account = kwargs.get("account", None)
firmware = kwargs.get("firmware", None)
if firmware is not None \
and firmware.name == "nanos" \
and account is not None \
and account.sig_scheme == SigScheme.BLS:
with StatusCode.CX_ERR.expected():
func(*args, **kwargs)
else:
func(*args, **kwargs)
return wrap

@catch_nanos_bls
@pytest.mark.parametrize("account", ACCOUNTS)
@pytest.mark.parametrize("with_hash", [False, True])
def test_sign_preattestation(
Expand Down Expand Up @@ -854,6 +874,7 @@ def test_sign_preattestation(
)


@catch_nanos_bls
@pytest.mark.parametrize("account", ACCOUNTS)
@pytest.mark.parametrize("with_hash", [False, True])
def test_sign_attestation(
Expand Down Expand Up @@ -911,6 +932,7 @@ def test_sign_attestation(
)


@catch_nanos_bls
@pytest.mark.parametrize("account", ACCOUNTS)
@pytest.mark.parametrize("with_hash", [False, True])
def test_sign_attestation_dal(
Expand Down Expand Up @@ -968,6 +990,7 @@ def test_sign_attestation_dal(
)


@catch_nanos_bls
@pytest.mark.parametrize("account", ACCOUNTS)
@pytest.mark.parametrize("with_hash", [False, True])
def test_sign_block(
Expand Down Expand Up @@ -1126,11 +1149,13 @@ def test_sign_level_authorized(
client.sign_message(account, message_2)


@catch_nanos_bls
@pytest.mark.parametrize("account", ACCOUNTS)
@pytest.mark.parametrize("with_hash", [False, True])
def test_sign_delegation(
account: Account,
with_hash: bool,
firmware: Firmware,
tezos_navigator: TezosNavigator) -> None:
"""Test the SIGN(_WITH_HASH) instruction on delegation."""
snap_path = Path(f"{account}")
Expand Down Expand Up @@ -1263,9 +1288,11 @@ def test_sign_delegation_constraints(

@pytest.mark.parametrize("account", ACCOUNTS)
@pytest.mark.parametrize("with_hash", [False, True])
@catch_nanos_bls
def test_sign_reveal(
account: Account,
with_hash: bool,
firmware: Firmware,
client: TezosClient,
tezos_navigator: TezosNavigator) -> None:
"""Test the SIGN(_WITH_HASH) instruction on reveal."""
Expand Down Expand Up @@ -1778,7 +1805,7 @@ def get_hmac_key(account):
("0123456789abcdef0123456789abcdef0123456789abcdef")
]

# This HMAC test don't pass with tz2 and tz3
# This HMAC test don't pass with tz2, tz3 and tz4
@pytest.mark.parametrize("account", TZ1_ACCOUNTS)
@pytest.mark.parametrize("message_hex", HMAC_TEST_SET)
def test_hmac(
Expand Down
63 changes: 54 additions & 9 deletions test/utils/account.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,12 @@
from enum import IntEnum
from typing import Union

from hashlib import blake2b
import base58
import pysodium
import secp256k1
import fastecdsa
from py_ecc.bls import G2MessageAugmentation as bls

import pytezos
from pytezos.crypto.encoding import base58_encode
Expand All @@ -36,6 +38,7 @@ class SigScheme(IntEnum):
SECP256K1 = 0x01
SECP256R1 = 0x02
BIP32_ED25519 = 0x03
BLS = 0x04
DEFAULT = ED25519

class BipPath:
Expand Down Expand Up @@ -127,6 +130,8 @@ def from_bytes(cls, data: bytes, sig_scheme: SigScheme) -> str:
prefix = bytes([13, 115, 101, 19, 63]) if sig_scheme == SigScheme.SECP256K1 \
else bytes([54, 240, 44, 52])
data = Signature.from_secp256_tlv(data)
elif sig_scheme == SigScheme.BLS:
prefix = bytes([40, 171, 64, 207])
else:
assert False, f"Wrong signature type: {sig_scheme}"

Expand Down Expand Up @@ -180,11 +185,25 @@ def from_bytes(data: bytes, sig_scheme: SigScheme) -> str:
data = bytes(kind) + data[:32]
return base58_encode(data, prefix).decode()

# BLS
if sig_scheme == SigScheme.BLS:
assert kind == PublicKey.CompressionKind.UNCOMPRESSED, \
f"Wrong BLS public key compression kind: {kind}"
assert len(data) == 2 * 48, \
f"Wrong BLS public key length: {len(data)}"
return base58.b58encode_check(bytes([6, 149, 135, 204]) + data[:48]).decode()

assert False, f"Wrong signature type: {sig_scheme}"

class Account:
"""Class representing account."""

path: BipPath
sig_scheme: SigScheme
# key: str == BLS.sk since pytezos do not support BLS for now
key: Union[pytezos.Key, str]
nanos_screens: int

def __init__(self,
path: Union[BipPath, str, bytes],
sig_scheme: SigScheme,
Expand All @@ -195,31 +214,44 @@ def __init__(self,
BipPath.from_bytes(path) if isinstance(path, bytes) else \
path
self.sig_scheme: SigScheme = sig_scheme
self.key: pytezos.Key = pytezos.pytezos.using(key=key).key
if self.sig_scheme == SigScheme.BLS:
self.key: str = key
else:
self.key: pytezos.Key = pytezos.pytezos.using(key=key).key
self.nanos_screens: int = nanos_screens

@property
def public_key_hash(self) -> str:
"""public_key_hash of the account."""
if isinstance(self.key, str):
pk_raw = base58.b58decode_check(self.public_key.encode())[4:]
pkh_raw = blake2b(pk_raw, digest_size=20).digest()
return base58.b58encode_check(bytes([6, 161, 166]) + pkh_raw).decode()

return self.key.public_key_hash()

@property
def public_key(self) -> str:
"""public_key of the account."""
if isinstance(self.key, str):
sk_raw = base58.b58decode_check(self.secret_key.encode())[4:]
sk_int = int.from_bytes(sk_raw, 'little')
pk_raw = bls.SkToPk(sk_int)
return base58.b58encode_check(bytes([6, 149, 135, 204]) + pk_raw).decode()

return self.key.public_key()

@property
def secret_key(self) -> str:
"""secret_key of the account."""
if isinstance(self.key, str):
return self.key

return self.key.secret_key()

def __repr__(self) -> str:
return f"{self.sig_scheme.name}_{self.public_key_hash}"

def sign(self, message: Union[str, bytes], generic: bool = False) -> bytes:
"""Sign a raw sequence of bytes."""
return self.key.sign(message, generic)

def sign_prehashed_message(self, prehashed_message: bytes) -> bytes:
"""Sign a raw sequence of bytes already hashed."""
if self.sig_scheme in [
Expand All @@ -245,6 +277,11 @@ def sign_prehashed_message(self, prehashed_message: bytes) -> bytes:
prehashed=True
)
return r.to_bytes(32, 'big') + s.to_bytes(32, 'big')
if self.sig_scheme == SigScheme.BLS:
sk_raw = base58.b58decode_check(self.secret_key.encode())[4:]
sk_int = int.from_bytes(sk_raw, 'little') # Tezos encode it in 'little'
return bls.Sign(sk_int, prehashed_message)

raise ValueError(f"Account do not have a right signature type: {self.sig_scheme}")

def check_signature(self,
Expand All @@ -255,7 +292,15 @@ def check_signature(self,
message = bytes.fromhex(message)
if isinstance(signature, bytes):
signature = Signature.from_bytes(signature, self.sig_scheme)
assert self.key.verify(signature.encode(), message), \
f"Fail to verify signature {signature}, \n\
with account {self} \n\
and message {message.hex()}"
if isinstance(self.key, str):
pk_raw = base58.b58decode_check(self.public_key.encode())[4:]
signature_raw = base58.b58decode_check(signature.encode())[4:]
assert bls.Verify(pk_raw, message, signature_raw), \
f"Fail to verify signature {signature}, \n\
with account {self} \n\
and message {message.hex()}"
else:
assert self.key.verify(signature.encode(), message), \
f"Fail to verify signature {signature}, \n\
with account {self} \n\
and message {message.hex()}"
1 change: 1 addition & 0 deletions test/utils/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,7 @@ class StatusCode(IntEnum):
HID_REQUIRED = 0x6983
CLASS = 0x6e00
MEMORY_ERROR = 0x9200
CX_ERR = 0x9001

@contextmanager
def expected(self) -> Generator[None, None, None]:
Expand Down

0 comments on commit c990563

Please sign in to comment.