Skip to content

Commit

Permalink
feat(deploy): implement deployModule instruction and test
Browse files Browse the repository at this point in the history
- Add new instruction INS_DEPLOY_MODULE (0x06) to handle module deployment
- Create deployModule.c/.h files with handler implementation
- Add UI flow for displaying module version and transaction details
- Add new error code ERROR_INVALID_SOURCE_LENGTH
- Implement Python test client for deployModule instruction
- Add test case for module deployment flow
- Update handler.c to support new instruction

The implementation allows users to deploy modules by processing the version
and source data in chunks, with appropriate UI confirmation steps.
  • Loading branch information
keiff3r committed Dec 10, 2024
1 parent 0602cd0 commit 04c3d10
Show file tree
Hide file tree
Showing 22 changed files with 177 additions and 21 deletions.
3 changes: 3 additions & 0 deletions src/common/handler.c
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,9 @@ int handler(uint8_t INS,
case INS_GET_APP_NAME:
return handleGetAppName();
break;
case INS_DEPLOY_MODULE:
handleDeployModule(cdata, p1, lc);
break;
default:
THROW(ERROR_INVALID_INSTRUCTION);
break;
Expand Down
2 changes: 2 additions & 0 deletions src/common/handler.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@

#define INS_EXPORT_PRIVATE_KEY 0x05

#define INS_DEPLOY_MODULE 0x06

#define INS_ENCRYPTED_AMOUNT_TRANSFER 0x10
#define INS_TRANSFER_TO_ENCRYPTED 0x11
#define INS_TRANSFER_TO_PUBLIC 0x12
Expand Down
1 change: 1 addition & 0 deletions src/common/responseCodes.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ enum {
ERROR_BUFFER_OVERFLOW = 0x6B06,
ERROR_FAILED_CX_OPERATION = 0x6B07,
ERROR_INVALID_INSTRUCTION = 0x6D00,
ERROR_INVALID_SOURCE_LENGTH = 0x6B08,

// Error codes from the Ledger firmware
ERROR_DEVICE_LOCKED = 0x530C,
Expand Down
2 changes: 2 additions & 0 deletions src/common/ui/display.h
Original file line number Diff line number Diff line change
Expand Up @@ -80,3 +80,5 @@ extern const ux_flow_step_t *ux_sign_scheduled_amount_transfer[8];
void startInitialScheduledTransferDisplay(bool displayMemo);
void uiSignScheduledTransferPairFlowSignDisplay(void);
void uiSignScheduledTransferPairFlowDisplay(void);

void uiDeployModuleDisplay(void);
19 changes: 19 additions & 0 deletions src/common/ui/display_bagl.c
Original file line number Diff line number Diff line change
Expand Up @@ -794,4 +794,23 @@ void uiSignScheduledTransferPairFlowDisplay(void) {
ux_flow_init(0, ux_sign_scheduled_transfer_pair_flow, NULL);
}

// Deploy Module
UX_STEP_NOCB(ux_deploy_module_1_step,
bnnn_paging,
{.title = "Version", .text = (char *) global.deployModule.versionDisplay});
UX_STEP_NOCB(ux_deploy_module_2_step,
bnnn_paging,
{.title = "TX hash", .text = (char *) global.deployModule.sourceHashDisplay});
UX_FLOW(ux_deploy_module,
&ux_sign_flow_shared_review,
&ux_sign_flow_account_sender_view,
&ux_deploy_module_1_step,
// &ux_deploy_module_2_step,
&ux_sign_flow_shared_sign,
&ux_sign_flow_shared_decline);

void uiDeployModuleDisplay() {
ux_flow_init(0, ux_deploy_module, NULL);
}

#endif
48 changes: 48 additions & 0 deletions src/deployModule.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
#include "os.h"
#include "common/ui/display.h"
#include "common/responseCodes.h"
#include "common/sign.h"
#include "common/util.h"
#include "deployModule.h"

static deployModule_t *ctx_deploy_module = &global.deployModule;
static tx_state_t *tx_state = &global_tx_state;

#define P1_INITIAL 0x00
#define P1_SOURCE 0x01

void handleDeployModule(uint8_t *cdata, uint8_t p1, uint8_t lc) {
if (p1 == P1_INITIAL) {
cdata += parseKeyDerivationPath(cdata);
cx_sha256_init(&tx_state->hash);
cdata += hashAccountTransactionHeaderAndKind(cdata, DEPLOY_MODULE);

// hash the version and source length
updateHash((cx_hash_t *) &tx_state->hash, cdata, 8);
ctx_deploy_module->version = U4BE(cdata, 0);
ctx_deploy_module->sourceLength = U4BE(cdata, 4);
ctx_deploy_module->remainingSourceLength = ctx_deploy_module->sourceLength;
// TODO: Format the version
numberToText((uint8_t *) ctx_deploy_module->versionDisplay,
sizeof(ctx_deploy_module->versionDisplay),
ctx_deploy_module->version);
sendSuccessNoIdle();
}

else if (p1 == P1_SOURCE && ctx_deploy_module->remainingSourceLength > 0) {
if (ctx_deploy_module->remainingSourceLength < lc) {
THROW(ERROR_INVALID_SOURCE_LENGTH);
}

updateHash((cx_hash_t *) &tx_state->hash, cdata, lc);
ctx_deploy_module->remainingSourceLength -= lc;
if (ctx_deploy_module->remainingSourceLength > 0) {
sendSuccessNoIdle();
} else if (ctx_deploy_module->remainingSourceLength == 0) {
uiDeployModuleDisplay();
}

} else {
THROW(ERROR_INVALID_STATE);
}
}
25 changes: 25 additions & 0 deletions src/deployModule.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
#pragma once

#include <stdint.h>
#include <stdbool.h>

/**
* Handles the DEPLOY_MODULE instruction, which deploys a module
*
*
*/
void handleDeployModule(uint8_t *cdata, uint8_t p1, uint8_t p2);

typedef struct {
uint32_t version;
uint32_t sourceLength;
uint32_t remainingSourceLength;
uint8_t sourceHash[32];
char sourceHashDisplay[65];
char versionDisplay[11];
} deployModule_t;

// typedef struct {
// uint8_t version[32];
// uint8_t sourceLength[32];
// } deployModuleBlob_t;

Check notice

Code scanning / CodeQL

Commented-out code Note

This comment appears to contain commented-out code.
4 changes: 2 additions & 2 deletions src/globals.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
#include "signTransferToPublic.h"
#include "signTransferWithSchedule.h"
#include "signRegisterData.h"

#include "deployModule.h"
#include "ux.h"

#define LEGACY_PURPOSE 1105
Expand Down Expand Up @@ -124,7 +124,7 @@ typedef union {
signTransferToPublic_t signTransferToPublic;
signConfigureBaker_t signConfigureBaker;
signConfigureDelegationContext_t signConfigureDelegation;

deployModule_t deployModule;
transactionWithDataBlob_t withDataBlob;
} instructionContext;
extern instructionContext global;
Expand Down
60 changes: 41 additions & 19 deletions tests/application_client/boilerplate_command_sender.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,10 @@ class P1(IntEnum):
P1_PROOFS = 0x08 # Sent for proof bytes
P1_NEW_OR_EXISTING = 0x09 # Sent for new/existing credential flag

# Parameter 1 for deploy module
P1_DEPLOY_MODULE_INITIAL = 0x00
P1_DEPLOY_MODULE_SOURCE = 0x01


class P2(IntEnum):
# Parameter 2 for sign for GET_PUBLIC_KEY.
Expand Down Expand Up @@ -78,6 +82,7 @@ class InsType(IntEnum):
SIGN_TRANSFER_WITH_SCHEDULE = 0x03
CREDENTIAL_DEPLOYMENT = 0x04
EXPORT_PRIVATE_KEY = 0x05
DEPLOY_MODULE = 0x06
ENCRYPTED_AMOUNT_TRANSFER = 0x10
TRANSFER_TO_ENCRYPTED = 0x11
TRANSFER_TO_PUBLIC = 0x12
Expand Down Expand Up @@ -431,37 +436,54 @@ def export_private_key(
) as response:
yield response

def credential_deployment_part_1(
@contextmanager
def credential_deployment(
self,
path: str,
number_of_keys: int,
) -> bool:
# send derivation path (no display)
header_and_type: bytes,
version: int,
source: bytes,
) -> Generator[None, None, None]:

if version > 0xFFFFFFFF:
raise ValueError("Version must be less than 4294967296")
if len(source) > 0xFFFFFFFF:
raise ValueError("Source length must be less than 4294967296")

data = pack_derivation_path(path)
data += header_and_type
data += version.to_bytes(4, byteorder="big")
data += len(source).to_bytes(4, byteorder="big")
temp_response = self.backend.exchange(
cla=CLA,
ins=InsType.CREDENTIAL_DEPLOYMENT,
p1=P1.P1_INITIAL_PACKET,
ins=InsType.DEPLOY_MODULE,
p1=P1.P1_DEPLOY_MODULE_INITIAL,
p2=P2.P2_NONE,
data=data,
)
print("km--------sent derivation path", temp_response)
if temp_response.status != 0x9000:
raise ExceptionRAPDU(temp_response.status)
# handle credential deployment keys
## send number of keys
data = number_of_keys.to_bytes(1, byteorder="big")
temp_response = self.backend.exchange(

source_chunks = split_message(source, MAX_APDU_LEN)
last_chunk = source_chunks.pop()
for chunk in source_chunks:
temp_response = self.backend.exchange(
cla=CLA,
ins=InsType.DEPLOY_MODULE,
p1=P1.P1_DEPLOY_MODULE_SOURCE,
p2=P2.P2_NONE,
data=chunk,
)
if temp_response.status != 0x9000:
raise ExceptionRAPDU(temp_response.status)
with self.backend.exchange_async(
cla=CLA,
ins=InsType.CREDENTIAL_DEPLOYMENT,
p1=P1.P1_VERIFICATION_KEY_LENGTH,
ins=InsType.DEPLOY_MODULE,
p1=P1.P1_DEPLOY_MODULE_SOURCE,
p2=P2.P2_NONE,
data=data,
)
print("km--------sent number of keys", temp_response)
if temp_response.status != 0x9000:
raise ExceptionRAPDU(temp_response.status)
return True
data=last_chunk,
) as response:
yield response

@contextmanager
def credential_deployment_part_2(self, key_index: int, key: bytes):
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.
34 changes: 34 additions & 0 deletions tests/test_deploy_module.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import pytest

from application_client.boilerplate_command_sender import (
BoilerplateCommandSender,
Errors,
)
from application_client.boilerplate_response_unpacker import (
unpack_get_public_key_response,
)
from ragger.bip import calculate_public_key_and_chaincode, CurveChoice
from ragger.error import ExceptionRAPDU
from ragger.navigator import NavInsID, NavIns
from ragger.firmware import Firmware
from utils import navigate_until_text_and_compare, instructions_builder


@pytest.mark.active_test_scope
def test_credential_deployment(
backend, firmware, navigator, test_name, default_screenshot_path
):
client = BoilerplateCommandSender(backend)
path = "m/1105/0/0/0/0/2/0/0"
header_and_type = bytes.fromhex(
"20a845815bd43a1999e90fbf971537a70392eb38f89e6bd32b3dd70e1a9551d7000000000000000a0000000000000064000000290000000063de5da700"
)
version = 1
source = b"source"

with client.credential_deployment(
path=path, header_and_type=header_and_type, version=version, source=source
):
navigate_until_text_and_compare(
firmware, navigator, "Sign", default_screenshot_path, test_name
)

0 comments on commit 04c3d10

Please sign in to comment.