Skip to content

Commit

Permalink
Merge pull request #22 from blooo-io/feat/make-it-work-on-bagl
Browse files Browse the repository at this point in the history
LDG-510 + LDG-528: Implement Sign Transfer & Sign Transfer with memo
  • Loading branch information
keiff3r authored Dec 5, 2024
2 parents a8db6ba + 50b40ac commit 79a9d11
Show file tree
Hide file tree
Showing 62 changed files with 167 additions and 114 deletions.
4 changes: 2 additions & 2 deletions src/common/util.c
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ int hashHeaderAndType(uint8_t *cdata, uint8_t headerLength, uint8_t validType) {
int hashAccountTransactionHeaderAndKind(uint8_t *cdata, uint8_t validTransactionKind) {
// Parse the account sender address from the transaction header, so it can be shown.
size_t outputSize = sizeof(accountSender->sender);
if (base58check_encode(cdata, 32, accountSender->sender, &outputSize) != 0) {
if (base58check_encode(cdata, 32, accountSender->sender, &outputSize) == -1) {
// The received address bytes are not a valid base58 encoding.
THROW(ERROR_INVALID_TRANSACTION);
}
Expand Down Expand Up @@ -107,7 +107,7 @@ int handleHeaderAndToAddress(uint8_t *cdata,

// The recipient address is in a base58 format, so we need to encode it to be
// able to display in a human-readable way.
if (base58check_encode(toAddress, sizeof(toAddress), recipientDst, &recipientSize) != 0) {
if (base58check_encode(toAddress, sizeof(toAddress), recipientDst, &recipientSize) == -1) {
// The received address bytes are not a valid base58 encoding.
THROW(ERROR_INVALID_TRANSACTION);
}
Expand Down
2 changes: 1 addition & 1 deletion src/signCredentialDeployment.c
Original file line number Diff line number Diff line change
Expand Up @@ -367,7 +367,7 @@ void handleSignCredentialDeployment(uint8_t *dataBuffer,
if (base58check_encode(accountAddress,
sizeof(accountAddress),
ctx->accountAddress,
&outputSize) != 0) {
&outputSize) == -1) {
// The received address bytes are not a valid base58 encoding.
THROW(ERROR_INVALID_TRANSACTION);
}
Expand Down
62 changes: 61 additions & 1 deletion tests/application_client/boilerplate_command_sender.py
Original file line number Diff line number Diff line change
Expand Up @@ -140,14 +140,74 @@ def verify_address(
) as response:
yield response

@contextmanager
def sign_simple_transfer(
self, path: str, transaction: bytes
) -> Generator[None, None, None]:
data = pack_derivation_path(path)
data += transaction

index = P1.P1_NONE + 1
with self.backend.exchange_async(
cla=CLA,
ins=InsType.SIGN_TRANSFER,
p1=index,
p2=P2.P2_NONE,
data=data,
) as response:
yield response

@contextmanager
def sign_simple_transfer_with_memo(
self, path: str, header_and_to_address: bytes, memo: bytes, amount: bytes
) -> Generator[None, None, None]:
data = pack_derivation_path(path)
data += header_and_to_address
print("km------------data", data.hex())
index = P1.P1_NONE + 1
# Get memo length in bytes
memo_length = len(memo)
print("km------------memo_length", memo_length)
# memo length has to take 2 bytes
memo_length_bytes = memo_length.to_bytes(2, byteorder="big")
print("km------------memo_length_bytes", memo_length_bytes.hex())
data += memo_length_bytes
print("km------------data", data.hex())
self.backend.exchange(
cla=CLA,
ins=InsType.SIGN_TRANSFER_WITH_MEMO,
p1=index,
p2=P2.P2_NONE,
data=data,
)
index += 1
memo_chunk = split_message(memo, MAX_APDU_LEN)
for chunk in memo_chunk:
self.backend.exchange(
cla=CLA,
ins=InsType.SIGN_TRANSFER_WITH_MEMO,
p1=index,
p2=P2.P2_NONE,
data=chunk,
)
index += 1
with self.backend.exchange_async(
cla=CLA,
ins=InsType.SIGN_TRANSFER_WITH_MEMO,
p1=index,
p2=P2.P2_NONE,
data=amount,
) as response:
yield response

# @contextmanager
# def sign_tx(
# self, path: str, tx_type_ins: InsType, transaction: bytes
# ) -> Generator[None, None, None]:

# self.backend.exchange(
# cla=CLA,
# ins=tx_type_ins,
# ins=InsType.SIGN_TRANSFER,
# p1=P1.P1_START,
# p2=P2.P2_MORE,
# data=pack_derivation_path(path),
Expand Down
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.
213 changes: 103 additions & 110 deletions tests/test_sign_cmd.py
Original file line number Diff line number Diff line change
@@ -1,139 +1,132 @@
import pytest

from application_client.boilerplate_transaction import Transaction
from application_client.boilerplate_command_sender import BoilerplateCommandSender, Errors, InsType
from application_client.boilerplate_response_unpacker import unpack_get_public_key_response, unpack_sign_tx_response
from application_client.boilerplate_command_sender import (
BoilerplateCommandSender,
Errors,
InsType,
)
from application_client.boilerplate_response_unpacker import (
unpack_get_public_key_response,
unpack_sign_tx_response,
)
from ragger.error import ExceptionRAPDU
from ragger.navigator import NavInsID
from utils import check_signature_validity

# In this tests we check the behavior of the device when asked to sign a transaction
from utils import navigate_until_text_and_compare


# In this test we send to the device a transaction to sign and validate it on screen
# The transaction is short and will be sent in one chunk
# We will ensure that the displayed information is correct by using screenshots comparison
def test_sign_tx_simple_transfer(backend, scenario_navigator):
@pytest.mark.active_test_scope
def test_sign_tx_simple_transfer_legacy_path(
backend, firmware, navigator, default_screenshot_path, test_name
):
# Use the app interface instead of raw interface
client = BoilerplateCommandSender(backend)
# The path used for this entire test
path: str = "m/1105/0/0/0/0/2/0/0"

# First we need to get the public key of the device in order to build the transaction
# rapdu = client.get_public_key(path=path)
# _, public_key, _, _ = unpack_get_public_key_response(rapdu.data)

# Create the transaction that will be sent to the device for signing
transaction = '20a845815bd43a1999e90fbf971537a70392eb38f89e6bd32b3dd70e1a9551d7000000000000000a0000000000000064000000290000000063de5da70320a845815bd43a1999e90fbf971537a70392eb38f89e6bd32b3dd70e1a9551d7ffffffffffffffff'
transaction = "20a845815bd43a1999e90fbf971537a70392eb38f89e6bd32b3dd70e1a9551d7000000000000000a0000000000000064000000290000000063de5da70320a845815bd43a1999e90fbf971537a70392eb38f89e6bd32b3dd70e1a9551d7ffffffffffffffff"
transaction = bytes.fromhex(transaction)

# Send the sign device instruction.
# As it requires on-screen validation, the function is asynchronous.
# It will yield the result when the navigation is done
ins_type = InsType.SIGN_SIMPLE_TRANSFER
with client.sign_tx(path=path, tx_type_ins=ins_type, transaction=transaction):
with client.sign_simple_transfer(path=path, transaction=transaction):
# Validate the on-screen request by performing the navigation appropriate for this device
scenario_navigator.review_approve()
navigate_until_text_and_compare(
firmware, navigator, "Sign", default_screenshot_path, test_name
)

# The device as yielded the result, parse it and ensure that the signature is correct
response = client.get_async_response().data
response_hex = response.hex()
print('response', response_hex)
assert response_hex == "d1617ee706805c0bc6a43260ece93a7ceba37aaefa303251cf19bdcbbe88c0a3d3878dcb965cdb88ff380fdb1aa4b321671f365d7258e878d18fa1b398a1a10f"
print("response", response_hex)
assert (
response_hex
== "d1617ee706805c0bc6a43260ece93a7ceba37aaefa303251cf19bdcbbe88c0a3d3878dcb965cdb88ff380fdb1aa4b321671f365d7258e878d18fa1b398a1a10f"
)
# assert check_signature_validity(public_key, der_sig, transaction)


# # In this test we send to the device a transaction to trig a blind-signing flow
# # The transaction is short and will be sent in one chunk
# # We will ensure that the displayed information is correct by using screenshots comparison
# def test_sign_tx_short_tx_blind_sign(firmware, navigator, backend, scenario_navigator, test_name, default_screenshot_path):
# if firmware.is_nano:
# pytest.skip("Not supported on Nano devices")

# # Use the app interface instead of raw interface
# client = BoilerplateCommandSender(backend)
# # The path used for this entire test
# path: str = "m/44'/919'/0'/0/0"

# # First we need to get the public key of the device in order to build the transaction
# rapdu = client.get_public_key(path=path)
# _, public_key, _, _ = unpack_get_public_key_response(rapdu.data)

# # Create the transaction that will be sent to the device for signing
# transaction = Transaction(
# nonce=1,
# to="0x0000000000000000000000000000000000000000",
# value=0,
# memo="Blind-sign"
# ).serialize()

# # Send the sign device instruction.
# # As it requires on-screen validation, the function is asynchronous.
# # It will yield the result when the navigation is done
# with client.sign_tx(path=path, transaction=transaction):
# navigator.navigate_and_compare(default_screenshot_path,
# test_name+"/part1",
# [NavInsID.USE_CASE_CHOICE_REJECT],
# screen_change_after_last_instruction=False)

# # Validate the on-screen request by performing the navigation appropriate for this device
# scenario_navigator.review_approve()

# # The device as yielded the result, parse it and ensure that the signature is correct
# response = client.get_async_response().data
# _, der_sig, _ = unpack_sign_tx_response(response)
# assert check_signature_validity(public_key, der_sig, transaction)

# # In this test se send to the device a transaction to sign and validate it on screen
# # This test is mostly the same as the previous one but with different values.
# # In particular the long memo will force the transaction to be sent in multiple chunks
# def test_sign_tx_long_tx(backend, scenario_navigator):
# # Use the app interface instead of raw interface
# client = BoilerplateCommandSender(backend)
# path: str = "m/44'/919'/0'/0/0"

# rapdu = client.get_public_key(path=path)
# _, public_key, _, _ = unpack_get_public_key_response(rapdu.data)

# transaction = Transaction(
# nonce=1,
# to="0xde0b295669a9fd93d5f28d9ec85e40f4cb697bae",
# value=666,
# memo=("This is a very long memo. "
# "It will force the app client to send the serialized transaction to be sent in chunk. "
# "As the maximum chunk size is 255 bytes we will make this memo greater than 255 characters. "
# "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed non risus. Suspendisse lectus tortor, dignissim sit amet, adipiscing nec, ultricies sed, dolor. Cras elementum ultrices diam.")
# ).serialize()

# with client.sign_tx(path=path, transaction=transaction):
# scenario_navigator.review_approve()

# response = client.get_async_response().data
# _, der_sig, _ = unpack_sign_tx_response(response)
# assert check_signature_validity(public_key, der_sig, transaction)


# # Transaction signature refused test
# # The test will ask for a transaction signature that will be refused on screen
# def test_sign_tx_refused(backend, scenario_navigator):
# # Use the app interface instead of raw interface
# client = BoilerplateCommandSender(backend)
# path: str = "m/44'/919'/0'/0/0"

# rapdu = client.get_public_key(path=path)
# _, pub_key, _, _ = unpack_get_public_key_response(rapdu.data)

# transaction = Transaction(
# nonce=1,
# to="0xde0b295669a9fd93d5f28d9ec85e40f4cb697bae",
# value=666,
# memo="This transaction will be refused by the user"
# ).serialize()

# with pytest.raises(ExceptionRAPDU) as e:
# with client.sign_tx(path=path, transaction=transaction):
# scenario_navigator.review_reject()

# # Assert that we have received a refusal
# assert e.value.status == Errors.SW_DENY
# assert len(e.value.data) == 0
# In this test we send to the device a transaction to sign and validate it on screen
# The transaction is short and will be sent in one chunk
# We will ensure that the displayed information is correct by using screenshots comparison
@pytest.mark.active_test_scope
def test_sign_tx_simple_transfer_new_path(
backend, firmware, navigator, default_screenshot_path, test_name
):
# Use the app interface instead of raw interface
client = BoilerplateCommandSender(backend)
# The path used for this entire test
path: str = "m/44/919/0/0/0/0"

# Create the transaction that will be sent to the device for signing
transaction = "20a845815bd43a1999e90fbf971537a70392eb38f89e6bd32b3dd70e1a9551d7000000000000000a0000000000000064000000290000000063de5da70320a845815bd43a1999e90fbf971537a70392eb38f89e6bd32b3dd70e1a9551d7ffffffffffffffff"
transaction = bytes.fromhex(transaction)

# Send the sign device instruction.
# As it requires on-screen validation, the function is asynchronous.
# It will yield the result when the navigation is done
with client.sign_simple_transfer(path=path, transaction=transaction):
# Validate the on-screen request by performing the navigation appropriate for this device
navigate_until_text_and_compare(
firmware, navigator, "Sign", default_screenshot_path, test_name
)

# The device as yielded the result, parse it and ensure that the signature is correct
response = client.get_async_response().data
response_hex = response.hex()
print("response", response_hex)
assert (
response_hex
== "e5f112237d58f908c44385827e71048869db7e8f513e2ceb5da6a6370e2088f4371f93d6e08f9f6c1dd92c74fe565727b8f81600541e817d35cfeec4cc3bc408"
)


# In this test we send to the device a transaction to sign and validate it on screen
# The transaction is short and will be sent in one chunk
# We will ensure that the displayed information is correct by using screenshots comparison
@pytest.mark.active_test_scope
def test_sign_tx_simple_transfer_with_memo_legacy_path(
backend, firmware, navigator, default_screenshot_path, test_name
):
# Use the app interface instead of raw interface
client = BoilerplateCommandSender(backend)
# The path used for this entire test
path: str = "m/1105/0/0/0/0/2/0/0"

# Create the transaction that will be sent to the device for signing
header_and_to_address = "20a845815bd43a1999e90fbf971537a70392eb38f89e6bd32b3dd70e1a9551d7000000000000000a0000000000000064000000290000000063de5da71620a845815bd43a1999e90fbf971537a70392eb38f89e6bd32b3dd70e1a9551d7"
header_and_to_address = bytes.fromhex(header_and_to_address)
memo = "6474657374"
memo = bytes.fromhex(memo)
amount = "ffffffffffffffff"
amount = bytes.fromhex(amount)

# Send the sign device instruction.
# As it requires on-screen validation, the function is asynchronous.
# It will yield the result when the navigation is done
with client.sign_simple_transfer_with_memo(
path=path,
header_and_to_address=header_and_to_address,
memo=memo,
amount=amount,
):
# Validate the on-screen request by performing the navigation appropriate for this device
navigate_until_text_and_compare(
firmware, navigator, "Sign", default_screenshot_path, test_name
)

# The device as yielded the result, parse it and ensure that the signature is correct
response = client.get_async_response().data
response_hex = response.hex()
print("response", response_hex)
# TODO: verify the signature
assert (
response_hex
== "a588094eef4ed6053df2ab4b851bc5ec09b311c204d2fa94a9c7d7c8feebf74de26d2d2a547f18c4e959b24388394305ebd3dca99653de1cb1aa689bb6674207"
)
# assert check_signature_validity(public_key, der_sig, transaction)

0 comments on commit 79a9d11

Please sign in to comment.