Skip to content

Commit

Permalink
include script data hash in transactions
Browse files Browse the repository at this point in the history
  • Loading branch information
mkv-vcm committed Oct 11, 2021
1 parent 5c13344 commit 640d15b
Show file tree
Hide file tree
Showing 8 changed files with 189 additions and 23 deletions.
1 change: 1 addition & 0 deletions src/cardano.h
Expand Up @@ -21,6 +21,7 @@ STATIC_ASSERT(LOVELACE_MAX_SUPPLY < LOVELACE_INVALID, "bad LOVELACE_INVALID");
#define CATALYST_REGISTRATION_PAYLOAD_HASH_LENGTH 32
#define ED25519_SIGNATURE_LENGTH 64
#define SCRIPT_HASH_LENGTH 28
#define SCRIPT_DATA_HASH_LENGTH 32

#define MINTING_POLICY_ID_SIZE 28
#define ASSET_NAME_SIZE_MAX 32
Expand Down
19 changes: 19 additions & 0 deletions src/securityPolicy.c
Expand Up @@ -821,6 +821,25 @@ security_policy_t policyForSignTxMintConfirm(security_policy_t outputPolicy)
DENY(); // should not be reached
}

security_policy_t policyForSignTxScriptDataHash(const sign_tx_signingmode_t txSigningMode)
{
switch (txSigningMode) {
case SIGN_TX_SIGNINGMODE_ORDINARY_TX:
case SIGN_TX_SIGNINGMODE_MULTISIG_TX:
SHOW();
break;

case SIGN_TX_SIGNINGMODE_POOL_REGISTRATION_OWNER:
case SIGN_TX_SIGNINGMODE_POOL_REGISTRATION_OPERATOR:
DENY();
break;
default:
ASSERT(false);
}

DENY(); // should not be reached
}

security_policy_t policyForSignTxConfirm()
{
PROMPT();
Expand Down
2 changes: 2 additions & 0 deletions src/securityPolicy.h
Expand Up @@ -100,6 +100,8 @@ security_policy_t policyForSignTxValidityIntervalStart();
security_policy_t policyForSignTxMintInit(const sign_tx_signingmode_t txSigningMode);
security_policy_t policyForSignTxMintConfirm(security_policy_t outputPolicy);

security_policy_t policyForSignTxScriptDataHash(const sign_tx_signingmode_t txSigningMode);

security_policy_t policyForSignTxWitness(
sign_tx_signingmode_t txSigningMode,
const bip44_path_t* pathSpec,
Expand Down
103 changes: 96 additions & 7 deletions src/signTx.c
Expand Up @@ -89,7 +89,8 @@ static inline void advanceStage()
ctx->numWithdrawals,
ctx->includeAuxData,
ctx->includeValidityIntervalStart,
ctx->includeMint
ctx->includeMint,
ctx->includeScriptDataHash
);
txHashBuilder_enterInputs(&BODY_CTX->txHashBuilder);
}
Expand Down Expand Up @@ -193,12 +194,23 @@ static inline void advanceStage()
if (ctx->includeMint) {
ASSERT(BODY_CTX->mintReceived);
}
ctx->stage = SIGN_STAGE_BODY_SCRIPT_DATA_HASH;
if (ctx->includeScriptDataHash) {
break;
}

// intentional fallthrough

case SIGN_STAGE_BODY_SCRIPT_DATA_HASH:
if (ctx->includeScriptDataHash) {
ASSERT(BODY_CTX->scriptDataHashReceived);
}
txHashBuilder_addNetworkId(&BODY_CTX->txHashBuilder, ctx->commonTxData.networkId);
ctx->stage = SIGN_STAGE_CONFIRM;
break;

case SIGN_STAGE_CONFIRM:
ctx->stage = SIGN_STAGE_WITNESSES;
txHashBuilder_addNetworkId(&BODY_CTX->txHashBuilder, ctx->commonTxData.networkId);
initTxWitnessCtx();

if (ctx->numWitnesses > 0) {
Expand Down Expand Up @@ -344,9 +356,9 @@ static void signTx_handleInit_ui_runStep()
"New transaction";

ui_displayNetworkParamsScreen(
header,
ctx->commonTxData.networkId, ctx->commonTxData.protocolMagic,
this_fn
header,
ctx->commonTxData.networkId, ctx->commonTxData.protocolMagic,
this_fn
);
}

Expand Down Expand Up @@ -391,6 +403,7 @@ static void signTx_handleInitAPDU(uint8_t p2, uint8_t* wireDataBuffer, size_t wi
uint8_t includeAuxData;
uint8_t includeValidityIntervalStart;
uint8_t includeMint;
uint8_t includeScriptDataHash;
uint8_t txSigningMode;

uint8_t numInputs[4];
Expand Down Expand Up @@ -423,6 +436,9 @@ static void signTx_handleInitAPDU(uint8_t p2, uint8_t* wireDataBuffer, size_t wi
ctx->includeMint = signTx_parseIncluded(wireHeader->includeMint);
TRACE("Include mint %d", ctx->includeMint);

ctx->includeScriptDataHash = signTx_parseIncluded(wireHeader->includeScriptDataHash);
TRACE("Include script data hash %d", ctx->includeScriptDataHash);

ctx->commonTxData.txSigningMode = wireHeader->txSigningMode;
TRACE("Signing mode %d", (int) ctx->commonTxData.txSigningMode);
switch (ctx->commonTxData.txSigningMode) {
Expand Down Expand Up @@ -1576,6 +1592,77 @@ static void signTx_handleMintAPDU(uint8_t p2, uint8_t* wireDataBuffer, size_t wi
signTxMint_handleAPDU(p2, wireDataBuffer, wireDataSize);
}

// ========================= SCRIPT DATA HASH ==========================


enum {
HANDLE_SCRIPT_DATA_HASH_STEP_DISPLAY = 1200,
HANDLE_SCRIPT_DATA_HASH_STEP_RESPOND,
HANDLE_SCRIPT_DATA_HASH_STEP_INVALID,
};

static void signTx_handleScriptDataHash_ui_runStep()
{
TRACE("UI step %d", ctx->ui_step);
ui_callback_fn_t* this_fn = signTx_handleScriptDataHash_ui_runStep;

UI_STEP_BEGIN(ctx->ui_step, this_fn);

UI_STEP(HANDLE_SCRIPT_DATA_HASH_STEP_DISPLAY) {
ui_displayHexBufferScreen("Script data hash", BODY_CTX->stageData.scriptDataHash, SCRIPT_DATA_HASH_LENGTH, this_fn);
}
UI_STEP(HANDLE_SCRIPT_DATA_HASH_STEP_RESPOND) {
respondSuccessEmptyMsg();
advanceStage();
}
UI_STEP_END(HANDLE_FEE_STEP_INVALID);
}

static void signTx_handleScriptDataHashAPDU(uint8_t p2, uint8_t* wireDataBuffer, size_t wireDataSize)
{
{
// sanity checks
CHECK_STAGE(SIGN_STAGE_BODY_SCRIPT_DATA_HASH);

VALIDATE(p2 == P2_UNUSED, ERR_INVALID_REQUEST_PARAMETERS);
ASSERT(wireDataSize < BUFFER_SIZE_PARANOIA);
}
{
// parse data
TRACE_BUFFER(wireDataBuffer, wireDataSize);

read_view_t view = make_read_view(wireDataBuffer, wireDataBuffer + wireDataSize);
STATIC_ASSERT(SIZEOF(BODY_CTX->stageData.scriptDataHash) == SCRIPT_DATA_HASH_LENGTH, "wrong script data hash length");
view_copyWireToBuffer(BODY_CTX->stageData.scriptDataHash, &view, SCRIPT_DATA_HASH_LENGTH);
VALIDATE(view_remainingSize(&view) == 0, ERR_INVALID_DATA);

BODY_CTX->scriptDataHashReceived = true;
}

{
// add to tx
TRACE("Adding script data hash to tx hash");
txHashBuilder_addScriptDataHash(&BODY_CTX->txHashBuilder, BODY_CTX->stageData.scriptDataHash, SIZEOF(BODY_CTX->stageData.scriptDataHash));
}

security_policy_t policy = policyForSignTxScriptDataHash(ctx->commonTxData.txSigningMode);
TRACE("Policy: %d", (int) policy);
ENSURE_NOT_DENIED(policy);

{
// select UI steps
switch (policy) {
# define CASE(POLICY, UI_STEP) case POLICY: {ctx->ui_step=UI_STEP; break;}
CASE(POLICY_SHOW_BEFORE_RESPONSE, HANDLE_SCRIPT_DATA_HASH_STEP_DISPLAY);
CASE(POLICY_ALLOW_WITHOUT_PROMPT, HANDLE_SCRIPT_DATA_HASH_STEP_RESPOND);
# undef CASE
default:
THROW(ERR_NOT_IMPLEMENTED);
}
}

signTx_handleScriptDataHash_ui_runStep();
}

// ============================== CONFIRM ==============================

Expand Down Expand Up @@ -1798,6 +1885,7 @@ static subhandler_fn_t* lookup_subhandler(uint8_t p1)
CASE(0x07, signTx_handleWithdrawalAPDU);
CASE(0x09, signTx_handleValidityIntervalStartAPDU);
CASE(0x0b, signTx_handleMintAPDU);
CASE(0x0c, signTx_handleScriptDataHashAPDU);
CASE(0x0a, signTx_handleConfirmAPDU);
CASE(0x0f, signTx_handleWitnessAPDU);
DEFAULT(NULL)
Expand Down Expand Up @@ -1825,7 +1913,6 @@ void signTx_handleAPDU(
// advance stage if a state sub-machine has finished
checkForFinishedSubmachines();

// TODO should be replaced by checking which txPartCtx is in use, see https://github.com/vacuumlabs/ledger-app-cardano-shelley/issues/66
switch (ctx->stage) {
case SIGN_STAGE_BODY_INPUTS:
case SIGN_STAGE_BODY_OUTPUTS:
Expand All @@ -1837,7 +1924,8 @@ void signTx_handleAPDU(
case SIGN_STAGE_BODY_WITHDRAWALS:
case SIGN_STAGE_BODY_VALIDITY_INTERVAL:
case SIGN_STAGE_BODY_MINT:
case SIGN_STAGE_BODY_MINT_SUBMACHINE: {
case SIGN_STAGE_BODY_MINT_SUBMACHINE:
case SIGN_STAGE_BODY_SCRIPT_DATA_HASH: {
explicit_bzero(&BODY_CTX->stageData, SIZEOF(BODY_CTX->stageData));
break;
}
Expand Down Expand Up @@ -1881,6 +1969,7 @@ ins_sign_tx_body_context_t* accessBodyContext()
case SIGN_STAGE_BODY_VALIDITY_INTERVAL:
case SIGN_STAGE_BODY_MINT:
case SIGN_STAGE_BODY_MINT_SUBMACHINE:
case SIGN_STAGE_BODY_SCRIPT_DATA_HASH:
case SIGN_STAGE_CONFIRM:
return &(ctx->txPartCtx.body_ctx);

Expand Down
8 changes: 6 additions & 2 deletions src/signTx.h
Expand Up @@ -37,8 +37,9 @@ typedef enum {
SIGN_STAGE_BODY_VALIDITY_INTERVAL = 34,
SIGN_STAGE_BODY_MINT = 35,
SIGN_STAGE_BODY_MINT_SUBMACHINE = 36,
SIGN_STAGE_CONFIRM = 37,
SIGN_STAGE_WITNESSES = 38,
SIGN_STAGE_BODY_SCRIPT_DATA_HASH = 37,
SIGN_STAGE_CONFIRM = 38,
SIGN_STAGE_WITNESSES = 39,
} sign_tx_stage_t;

enum {
Expand Down Expand Up @@ -108,6 +109,7 @@ typedef struct {
bool ttlReceived;
bool validityIntervalStartReceived;
bool mintReceived;
bool scriptDataHashReceived;

// TODO move these to commonTxData?
tx_hash_builder_t txHashBuilder;
Expand All @@ -119,6 +121,7 @@ typedef struct {
sign_tx_certificate_data_t certificate;
sign_tx_withdrawal_data_t withdrawal;
uint64_t validityIntervalStart;
uint8_t scriptDataHash[SCRIPT_DATA_HASH_LENGTH];
} stageData; // TODO rename to reflect single-APDU scope

union {
Expand Down Expand Up @@ -148,6 +151,7 @@ typedef struct {
uint16_t numWithdrawals; // reward withdrawals
bool includeValidityIntervalStart;
bool includeMint;
bool includeScriptDataHash;
uint16_t numWitnesses;

uint8_t auxDataHash[AUX_DATA_HASH_LENGTH];
Expand Down
61 changes: 51 additions & 10 deletions src/txHashBuilder.c
Expand Up @@ -64,7 +64,8 @@ void txHashBuilder_init(
uint16_t numWithdrawals,
bool includeAuxData,
bool includeValidityIntervalStart,
bool includeMint
bool includeMint,
bool includeScriptDataHash
)
{
TRACE("numInputs = %u", numInputs);
Expand Down Expand Up @@ -108,10 +109,13 @@ void txHashBuilder_init(
builder->includeMint = includeMint;
if (includeMint) numItems++;

builder->includeScriptDataHash = includeScriptDataHash;
if (includeScriptDataHash) numItems++;

// network id always included
numItems++;

ASSERT((4 <= numItems) && (numItems <= 10));
ASSERT((4 <= numItems) && (numItems <= 11));

_TRACE("Serializing tx body with %u items", numItems);
BUILDER_APPEND_CBOR(CBOR_TYPE_MAP, numItems);
Expand Down Expand Up @@ -1182,11 +1186,54 @@ static void txHashBuilder_assertCanLeaveMint(tx_hash_builder_t* builder)
}
}

void txHashBuilder_addNetworkId(tx_hash_builder_t* builder, uint8_t networkId)
void txHashBuilder_addScriptDataHash(
tx_hash_builder_t* builder,
const uint8_t* scriptHashData, size_t scriptHashDataSize
)
{
_TRACE("state = %d", builder->state);

ASSERT(scriptHashDataSize == SCRIPT_DATA_HASH_LENGTH);
txHashBuilder_assertCanLeaveMint(builder);
ASSERT(builder->includeScriptDataHash);

{
BUILDER_APPEND_CBOR(CBOR_TYPE_UNSIGNED, TX_BODY_KEY_SCRIPT_HASH_DATA);
BUILDER_APPEND_CBOR(CBOR_TYPE_BYTES, scriptHashDataSize);
BUILDER_APPEND_DATA(scriptHashData, scriptHashDataSize);
}
builder->state = TX_HASH_BUILDER_IN_SCRIPT_HASH_DATA;
}

static void txHashBuilder_assertCanLeaveScriptDataHash(tx_hash_builder_t* builder)
{
_TRACE("state = %u", builder->state);

switch (builder->state) {
case TX_HASH_BUILDER_IN_SCRIPT_HASH_DATA:
break;

case TX_HASH_BUILDER_IN_MINT:
case TX_HASH_BUILDER_IN_VALIDITY_INTERVAL_START:
case TX_HASH_BUILDER_IN_AUX_DATA:
case TX_HASH_BUILDER_IN_WITHDRAWALS:
case TX_HASH_BUILDER_IN_CERTIFICATES:
case TX_HASH_BUILDER_IN_TTL:
case TX_HASH_BUILDER_IN_FEE:
txHashBuilder_assertCanLeaveMint(builder);
ASSERT(!builder->includeScriptDataHash);
break;

default:
ASSERT(false);
}
}

void txHashBuilder_addNetworkId(tx_hash_builder_t* builder, uint8_t networkId)
{
_TRACE("state = %d", builder->state);

txHashBuilder_assertCanLeaveScriptDataHash(builder);

// add network id item into the main tx body map
BUILDER_APPEND_CBOR(CBOR_TYPE_UNSIGNED, TX_BODY_KEY_NETWORK_ID);
Expand All @@ -1199,13 +1246,7 @@ static void txHashBuilder_assertCanLeaveNetworkId(tx_hash_builder_t* builder)
{
_TRACE("state = %d", builder->state);

switch (builder->state) {
case TX_HASH_BUILDER_IN_NETWORK_ID:
break;

default:
ASSERT(false);
}
ASSERT(builder->state == TX_HASH_BUILDER_IN_NETWORK_ID);
}

void txHashBuilder_finalize(tx_hash_builder_t* builder, uint8_t* outBuffer, size_t outSize)
Expand Down

0 comments on commit 640d15b

Please sign in to comment.