From 4dab8d07420afa882fee805d2a54142cbe3f5441 Mon Sep 17 00:00:00 2001 From: Jan Mazak Date: Wed, 11 Oct 2023 12:27:18 +0200 Subject: [PATCH] conway: add new certificates --- src/cardano.h | 11 + src/securityPolicy.c | 200 +++++++--- src/securityPolicy.h | 18 + src/signTx.c | 346 ++++++++++++++++- src/signTx.h | 31 +- src/signTxPoolRegistration_ui.c | 2 +- src/signTx_ui.c | 636 +++++++++++++++++++++++++++++--- src/signTx_ui.h | 60 ++- src/textUtils.c | 8 - src/textUtils.h | 2 - src/txHashBuilder.c | 323 ++++++++++++++-- src/txHashBuilder.h | 67 +++- src/txHashBuilder_test.c | 4 +- 13 files changed, 1559 insertions(+), 149 deletions(-) diff --git a/src/cardano.h b/src/cardano.h index 33bf1b8c..9a8c9119 100644 --- a/src/cardano.h +++ b/src/cardano.h @@ -24,6 +24,7 @@ STATIC_ASSERT(LOVELACE_MAX_SUPPLY < LOVELACE_INVALID, "bad LOVELACE_INVALID"); #define SCRIPT_HASH_LENGTH 28 #define SCRIPT_DATA_HASH_LENGTH 32 #define OUTPUT_DATUM_HASH_LENGTH 32 +#define ANCHOR_HASH_LENGTH 32 #define MINTING_POLICY_ID_SIZE (SCRIPT_HASH_LENGTH) #define ASSET_NAME_SIZE_MAX 32 @@ -100,6 +101,8 @@ typedef struct { // ============================== CERTIFICATES ============================== +#define ANCHOR_URL_LENGTH_MAX 64 + #define POOL_METADATA_URL_LENGTH_MAX 64 #define DNS_NAME_SIZE_MAX 64 @@ -116,6 +119,14 @@ typedef enum { CERTIFICATE_TYPE_STAKE_DELEGATION = 2, CERTIFICATE_TYPE_STAKE_POOL_REGISTRATION = 3, CERTIFICATE_TYPE_STAKE_POOL_RETIREMENT = 4, + CERTIFICATE_TYPE_STAKE_REG = 7, + CERTIFICATE_TYPE_STAKE_UNREG = 8, + CERTIFICATE_TYPE_VOTE_DELEG = 9, + CERTIFICATE_TYPE_COMMITTEE_AUTH = 14, + CERTIFICATE_TYPE_COMMITTEE_RESIGN = 15, + CERTIFICATE_TYPE_DREP_REG = 16, + CERTIFICATE_TYPE_DREP_UNREG = 17, + CERTIFICATE_TYPE_DREP_UPDATE = 18, } certificate_type_t; typedef enum { diff --git a/src/securityPolicy.c b/src/securityPolicy.c index 1333a9f8..fc728725 100644 --- a/src/securityPolicy.c +++ b/src/securityPolicy.c @@ -1018,6 +1018,74 @@ security_policy_t policyForSignTxCertificate( DENY(); // should not be reached } +// applicable to credentials that are witnessed in this tx +static bool forbiddenCredential( + sign_tx_signingmode_t txSigningMode, + const ext_credential_t* credential +) +{ + // certain combinations of tx signing mode and credential type are not allowed + // either because they don't make sense or are dangerous + switch (txSigningMode) { + case SIGN_TX_SIGNINGMODE_MULTISIG_TX: + switch (credential->type) { + case EXT_CREDENTIAL_KEY_PATH: + case EXT_CREDENTIAL_KEY_HASH: + // everything is expected to be governed by native scripts + return true; + default: + break; + } + break; + + case SIGN_TX_SIGNINGMODE_PLUTUS_TX: + // everything allowed, txs are too complex for a HW wallet to understand + // and there might be third-party key hashes in the tx + break; + + case SIGN_TX_SIGNINGMODE_ORDINARY_TX: + switch (credential->type) { + case EXT_CREDENTIAL_KEY_HASH: + case EXT_CREDENTIAL_SCRIPT_HASH: + // keys must be given by path, otherwise the user does not know + // if the hash corresponds to some of his keys, + // and might inadvertently sign several certificates with a single witness + return true; + default: + break; + } + break; + + default: + // this should not be called in POOL_REGISTRATION signing modes + ASSERT(false); + break; + } + + return false; +} + +security_policy_t _policyForSignTxCertificateStakeCredential( + sign_tx_signingmode_t txSigningMode, + const ext_credential_t* stakeCredential +) +{ + DENY_IF(forbiddenCredential(txSigningMode, stakeCredential)); + + switch (stakeCredential->type) { + case EXT_CREDENTIAL_KEY_PATH: + DENY_UNLESS(bip44_isOrdinaryStakingKeyPath(&stakeCredential->keyPath)); + DENY_IF(violatesSingleAccountOrStoreIt(&stakeCredential->keyPath)); + break; + + default: + // the rest is OK, forbidden credentials have been dealt with above + break; + } + + PROMPT(); +} + // for certificates concerning stake keys and stake delegation security_policy_t policyForSignTxCertificateStaking( sign_tx_signingmode_t txSigningMode, @@ -1027,7 +1095,9 @@ security_policy_t policyForSignTxCertificateStaking( { switch (certificateType) { case CERTIFICATE_TYPE_STAKE_REGISTRATION: + case CERTIFICATE_TYPE_STAKE_REG: case CERTIFICATE_TYPE_STAKE_DEREGISTRATION: + case CERTIFICATE_TYPE_STAKE_UNREG: case CERTIFICATE_TYPE_STAKE_DELEGATION: break; // these are allowed @@ -1035,72 +1105,100 @@ security_policy_t policyForSignTxCertificateStaking( ASSERT(false); } - switch (stakeCredential->type) { - case EXT_CREDENTIAL_KEY_PATH: - DENY_UNLESS(bip44_isOrdinaryStakingKeyPath(&stakeCredential->keyPath)); - DENY_IF(violatesSingleAccountOrStoreIt(&stakeCredential->keyPath)); - switch (txSigningMode) { - case SIGN_TX_SIGNINGMODE_ORDINARY_TX: - case SIGN_TX_SIGNINGMODE_PLUTUS_TX: - PROMPT(); - break; + return _policyForSignTxCertificateStakeCredential(txSigningMode, stakeCredential); +} - case SIGN_TX_SIGNINGMODE_MULTISIG_TX: - DENY(); - break; +security_policy_t policyForSignTxCertificateVoteDelegation( + sign_tx_signingmode_t txSigningMode, + const ext_credential_t* stakeCredential, + const ext_drep_t* drep +) +{ + // DRep can be anything, but if given by key path, it should be a valid path + if (drep->type == EXT_DREP_KEY_PATH) { + DENY_UNLESS(bip44_isDRepKeyPath(&drep->keyPath)); + } - default: - // in POOL_REGISTRATION signing modes, this certificate should have already been - // reported as invalid (only pool registration certificate is allowed) - ASSERT(false); - break; - } + return _policyForSignTxCertificateStakeCredential(txSigningMode, stakeCredential); +} + +security_policy_t policyForSignTxCertificateCommitteeAuth( + sign_tx_signingmode_t txSigningMode, + const ext_credential_t* coldCredential, + const ext_credential_t* hotCredential +) +{ + DENY_IF(forbiddenCredential(txSigningMode, coldCredential)); + + switch (coldCredential->type) { + case EXT_CREDENTIAL_KEY_PATH: + DENY_UNLESS(bip44_isCommitteeColdKeyPath(&coldCredential->keyPath)); + DENY_IF(violatesSingleAccountOrStoreIt(&coldCredential->keyPath)); break; + default: + // the rest is OK, forbidden credentials have been dealt with above + break; + } + + switch (hotCredential->type) { + + case EXT_CREDENTIAL_SCRIPT_HASH: case EXT_CREDENTIAL_KEY_HASH: - switch (txSigningMode) { - case SIGN_TX_SIGNINGMODE_PLUTUS_TX: - PROMPT(); - break; + // keys might be governed outside of this device + break; - case SIGN_TX_SIGNINGMODE_ORDINARY_TX: - case SIGN_TX_SIGNINGMODE_MULTISIG_TX: - DENY(); - break; + case EXT_CREDENTIAL_KEY_PATH: + DENY_UNLESS(bip44_isCommitteeHotKeyPath(&hotCredential->keyPath)); + break; - default: - // in POOL_REGISTRATION signing modes, this certificate should have already been - // reported as invalid (only pool registration certificate is allowed) - ASSERT(false); - break; - } + default: + ASSERT(false); + } + + PROMPT(); +} + +security_policy_t policyForSignTxCertificateCommitteeResign( + sign_tx_signingmode_t txSigningMode, + const ext_credential_t* coldCredential +) +{ + DENY_IF(forbiddenCredential(txSigningMode, coldCredential)); + + switch (coldCredential->type) { + case EXT_CREDENTIAL_KEY_PATH: + DENY_UNLESS(bip44_isCommitteeColdKeyPath(&coldCredential->keyPath)); + DENY_IF(violatesSingleAccountOrStoreIt(&coldCredential->keyPath)); break; - case EXT_CREDENTIAL_SCRIPT_HASH: - switch (txSigningMode) { - case SIGN_TX_SIGNINGMODE_MULTISIG_TX: - case SIGN_TX_SIGNINGMODE_PLUTUS_TX: - PROMPT(); - break; + default: + // the rest is OK, forbidden credentials have been dealt with above + break; + } - case SIGN_TX_SIGNINGMODE_ORDINARY_TX: - DENY(); - break; + PROMPT(); +} - default: - // in POOL_REGISTRATION signing modes, this certificate should have already been - // reported as invalid (only pool registration certificate is allowed) - ASSERT(false); - break; - } +security_policy_t policyForSignTxCertificateDRep( + sign_tx_signingmode_t txSigningMode, + const ext_credential_t* dRepCredential +) +{ + DENY_IF(forbiddenCredential(txSigningMode, dRepCredential)); + + switch (dRepCredential->type) { + case EXT_CREDENTIAL_KEY_PATH: + DENY_UNLESS(bip44_isDRepKeyPath(&dRepCredential->keyPath)); + DENY_IF(violatesSingleAccountOrStoreIt(&dRepCredential->keyPath)); break; default: - ASSERT(false); + // the rest is OK, forbidden credentials have been dealt with above break; } - DENY(); // should not be reached + PROMPT(); } #ifdef APP_FEATURE_POOL_RETIREMENT @@ -1416,7 +1514,6 @@ static inline security_policy_t _ordinaryWitnessPolicy(const bip44_path_t* path, case PATH_COMMITTEE_HOT_KEY: // these have to be shown because the tx might contain // an action proposal that cannot be fully shown on the device - // TODO what about violation of single account policy? DENY_IF(violatesSingleAccountOrStoreIt(path)); WARN_UNLESS(bip44_isPathReasonable(path)); SHOW(); @@ -1478,6 +1575,9 @@ static inline security_policy_t _plutusWitnessPolicy(const bip44_path_t* path, b case PATH_ORDINARY_STAKING_KEY: case PATH_MULTISIG_SPENDING_KEY: case PATH_MULTISIG_STAKING_KEY: + case PATH_DREP_KEY: + case PATH_COMMITTEE_COLD_KEY: + case PATH_COMMITTEE_HOT_KEY: WARN_UNLESS(bip44_isPathReasonable(path)); SHOW(); break; diff --git a/src/securityPolicy.h b/src/securityPolicy.h index f3669f52..a804fa0a 100644 --- a/src/securityPolicy.h +++ b/src/securityPolicy.h @@ -113,6 +113,24 @@ security_policy_t policyForSignTxCertificateStaking( const certificate_type_t certificateType, const ext_credential_t* stakeCredential ); +security_policy_t policyForSignTxCertificateVoteDelegation( + sign_tx_signingmode_t txSigningMode, + const ext_credential_t* stakeCredential, + const ext_drep_t* drep +); +security_policy_t policyForSignTxCertificateCommitteeAuth( + sign_tx_signingmode_t txSigningMode, + const ext_credential_t* coldCredential, + const ext_credential_t* hotCredential +); +security_policy_t policyForSignTxCertificateCommitteeResign( + sign_tx_signingmode_t txSigningMode, + const ext_credential_t* coldCredential +); +security_policy_t policyForSignTxCertificateDRep( + sign_tx_signingmode_t txSigningMode, + const ext_credential_t* drepCredential +); #ifdef APP_FEATURE_POOL_RETIREMENT security_policy_t policyForSignTxCertificateStakePoolRetirement( sign_tx_signingmode_t txSigningMode, diff --git a/src/signTx.c b/src/signTx.c index a07dc653..f775555c 100644 --- a/src/signTx.c +++ b/src/signTx.c @@ -929,6 +929,61 @@ static void _parseCredential(read_view_t* view, ext_credential_t* credential) } } +static void _parseDRep(read_view_t* view, ext_drep_t* drep) +{ + drep->type = parse_u1be(view); + switch (drep->type) { + case EXT_DREP_KEY_PATH: + _parsePathSpec(view, &drep->keyPath); + break; + case EXT_DREP_KEY_HASH: { + STATIC_ASSERT(SIZEOF(drep->keyHash) == ADDRESS_KEY_HASH_LENGTH, "bad key hash container size"); + view_parseBuffer(drep->keyHash, view, SIZEOF(drep->keyHash)); + break; + } + case EXT_DREP_SCRIPT_HASH: { + STATIC_ASSERT(SIZEOF(drep->scriptHash) == SCRIPT_HASH_LENGTH, "bad script hash container size"); + view_parseBuffer(drep->scriptHash, view, SIZEOF(drep->scriptHash)); + break; + } + case EXT_DREP_ALWAYS_ABSTAIN: + case EXT_DREP_ALWAYS_NO_CONFIDENCE: { + // nothing more to parse + break; + } + default: + THROW(ERR_INVALID_DATA); + } +} + +static void _parseAnchor(read_view_t* view, anchor_t* anchor) +{ + { + uint8_t includeAnchorByte = parse_u1be(view); + anchor->isIncluded = signTx_parseIncluded(includeAnchorByte); + + if (!anchor->isIncluded) { + VALIDATE(view_remainingSize(view) == 0, ERR_INVALID_DATA); + return; + } + } + { + STATIC_ASSERT(SIZEOF(anchor->hash) == ANCHOR_HASH_LENGTH, "wrong anchor buffer size"); + view_parseBuffer(anchor->hash, view, ANCHOR_HASH_LENGTH); + } + { + anchor->urlLength = view_remainingSize(view); + VALIDATE(anchor->urlLength <= ANCHOR_URL_LENGTH_MAX, ERR_INVALID_DATA); + STATIC_ASSERT(SIZEOF(anchor->url) >= ANCHOR_URL_LENGTH_MAX, "wrong anchor url length"); + view_parseBuffer(anchor->url, view, anchor->urlLength); + + // whitespace not allowed + VALIDATE(str_isPrintableAsciiWithoutSpaces(anchor->url, anchor->urlLength), ERR_INVALID_DATA); + } + + VALIDATE(view_remainingSize(view) == 0, ERR_INVALID_DATA); +} + static void _parseCertificateData(const uint8_t* wireDataBuffer, size_t wireDataSize, sign_tx_certificate_data_t* certificateData) { ASSERT(wireDataSize < BUFFER_SIZE_PARANOIA); @@ -941,11 +996,14 @@ static void _parseCertificateData(const uint8_t* wireDataBuffer, size_t wireData switch (certificateData->type) { case CERTIFICATE_TYPE_STAKE_REGISTRATION: + case CERTIFICATE_TYPE_STAKE_DEREGISTRATION: _parseCredential(&view, &certificateData->stakeCredential); break; - case CERTIFICATE_TYPE_STAKE_DEREGISTRATION: + case CERTIFICATE_TYPE_STAKE_REG: + case CERTIFICATE_TYPE_STAKE_UNREG: _parseCredential(&view, &certificateData->stakeCredential); + certificateData->coin = parse_u8be(&view); break; case CERTIFICATE_TYPE_STAKE_DELEGATION: @@ -956,6 +1014,37 @@ static void _parseCertificateData(const uint8_t* wireDataBuffer, size_t wireData view_parseBuffer(certificateData->poolCredential.keyHash, &view, POOL_KEY_HASH_LENGTH); break; + case CERTIFICATE_TYPE_VOTE_DELEG: + _parseCredential(&view, &certificateData->stakeCredential); + _parseDRep(&view, &certificateData->drep); + break; + + case CERTIFICATE_TYPE_COMMITTEE_AUTH: + _parseCredential(&view, &certificateData->committeeColdCredential); + _parseCredential(&view, &certificateData->committeeHotCredential); + break; + + case CERTIFICATE_TYPE_COMMITTEE_RESIGN: + _parseCredential(&view, &certificateData->committeeColdCredential); + _parseAnchor(&view, &certificateData->anchor); + break; + + case CERTIFICATE_TYPE_DREP_REG: + _parseCredential(&view, &certificateData->committeeColdCredential); + certificateData->coin = parse_u8be(&view); + _parseAnchor(&view, &certificateData->anchor); + break; + + case CERTIFICATE_TYPE_DREP_UNREG: + _parseCredential(&view, &certificateData->committeeColdCredential); + certificateData->coin = parse_u8be(&view); + break; + + case CERTIFICATE_TYPE_DREP_UPDATE: + _parseCredential(&view, &certificateData->committeeColdCredential); + _parseAnchor(&view, &certificateData->anchor); + break; + #ifdef APP_FEATURE_POOL_REGISTRATION case CERTIFICATE_TYPE_STAKE_POOL_REGISTRATION: @@ -1016,6 +1105,47 @@ static void _setCredential( } } +static void _setDRep( + drep_t* drep, + const ext_drep_t* extDRep +) +{ + switch (extDRep->type) { + + case EXT_DREP_KEY_PATH: + drep->type = DREP_KEY_HASH; + bip44_pathToKeyHash( + &extDRep->keyPath, + drep->keyHash, SIZEOF(drep->keyHash) + ); + break; + + case EXT_DREP_KEY_HASH: + drep->type = DREP_KEY_HASH; + STATIC_ASSERT(SIZEOF(drep->keyHash) == SIZEOF(extDRep->keyHash), "bad script hash container size"); + memmove(drep->keyHash, extDRep->keyHash, SIZEOF(extDRep->keyHash)); + break; + + case EXT_DREP_SCRIPT_HASH: + drep->type = DREP_SCRIPT_HASH; + STATIC_ASSERT(SIZEOF(drep->scriptHash) == SIZEOF(extDRep->scriptHash), "bad script hash container size"); + memmove(drep->scriptHash, extDRep->scriptHash, SIZEOF(extDRep->scriptHash)); + break; + + case EXT_DREP_ALWAYS_ABSTAIN: + drep->type = DREP_ALWAYS_ABSTAIN; + break; + + case EXT_DREP_ALWAYS_NO_CONFIDENCE: + drep->type = DREP_ALWAYS_NO_CONFIDENCE; + break; + + default: + ASSERT(false); + break; + } +} + __noinline_due_to_stack__ static void _addCertificateDataToTx( sign_tx_certificate_data_t* certificateData, @@ -1024,32 +1154,110 @@ static void _addCertificateDataToTx( { TRACE("Adding certificate (type %d) to tx hash", certificateData->type); - credential_t stakeCredential; + // declared here to save the stack space compiler allocates for this function + credential_t tmpCredential; switch (BODY_CTX->stageData.certificate.type) { case CERTIFICATE_TYPE_STAKE_REGISTRATION: case CERTIFICATE_TYPE_STAKE_DEREGISTRATION: { - _setCredential(&stakeCredential, &certificateData->stakeCredential); - txHashBuilder_addCertificate_stakingHash( + _setCredential(&tmpCredential, &certificateData->stakeCredential); + txHashBuilder_addCertificate_stakingOld( txHashBuilder, certificateData->type, - &stakeCredential + &tmpCredential + ); + break; + } + + case CERTIFICATE_TYPE_STAKE_REG: + case CERTIFICATE_TYPE_STAKE_UNREG: { + _setCredential(&tmpCredential, &certificateData->stakeCredential); + txHashBuilder_addCertificate_staking( + txHashBuilder, + certificateData->type, + &tmpCredential, + certificateData->coin ); break; } case CERTIFICATE_TYPE_STAKE_DELEGATION: { - _setCredential(&stakeCredential, &certificateData->stakeCredential); + _setCredential(&tmpCredential, &certificateData->stakeCredential); ASSERT(certificateData->poolCredential.type == EXT_CREDENTIAL_KEY_HASH); txHashBuilder_addCertificate_delegation( txHashBuilder, - &stakeCredential, + &tmpCredential, certificateData->poolCredential.keyHash, SIZEOF(certificateData->poolCredential.keyHash) ); break; } + case CERTIFICATE_TYPE_VOTE_DELEG: { + drep_t drep; + _setCredential(&tmpCredential, &certificateData->stakeCredential); + _setDRep(&drep, &certificateData->drep); + txHashBuilder_addCertificate_voteDeleg( + txHashBuilder, + &tmpCredential, + &drep + ); + break; + } + + case CERTIFICATE_TYPE_COMMITTEE_AUTH: { + credential_t hotCredential; + _setCredential(&tmpCredential, &certificateData->committeeColdCredential); + _setCredential(&hotCredential, &certificateData->committeeHotCredential); + txHashBuilder_addCertificate_committeeAuth( + txHashBuilder, + &tmpCredential, + &hotCredential + ); + break; + } + + case CERTIFICATE_TYPE_COMMITTEE_RESIGN: { + _setCredential(&tmpCredential, &certificateData->committeeColdCredential); + txHashBuilder_addCertificate_committeeResign( + txHashBuilder, + &tmpCredential, + &certificateData->anchor + ); + break; + } + + case CERTIFICATE_TYPE_DREP_REG: { + _setCredential(&tmpCredential, &certificateData->drepCredential); + txHashBuilder_addCertificate_dRepReg( + txHashBuilder, + &tmpCredential, + certificateData->coin, + &certificateData->anchor + ); + break; + } + + case CERTIFICATE_TYPE_DREP_UNREG: { + _setCredential(&tmpCredential, &certificateData->drepCredential); + txHashBuilder_addCertificate_dRepUnreg( + txHashBuilder, + &tmpCredential, + certificateData->coin + ); + break; + } + + case CERTIFICATE_TYPE_DREP_UPDATE: { + _setCredential(&tmpCredential, &certificateData->drepCredential); + txHashBuilder_addCertificate_dRepUpdate( + txHashBuilder, + &tmpCredential, + &certificateData->anchor + ); + break; + } + #ifdef APP_FEATURE_POOL_RETIREMENT case CERTIFICATE_TYPE_STAKE_POOL_RETIREMENT: { @@ -1111,14 +1319,108 @@ static void _handleCertificateStaking() switch (policy) { #define CASE(POLICY, UI_STEP) case POLICY: {ctx->ui_step=UI_STEP; break;} - CASE(POLICY_PROMPT_BEFORE_RESPONSE, HANDLE_CERTIFICATE_STEP_DISPLAY_OPERATION); - CASE(POLICY_ALLOW_WITHOUT_PROMPT, HANDLE_CERTIFICATE_STEP_RESPOND); + CASE(POLICY_PROMPT_BEFORE_RESPONSE, HANDLE_CERTIFICATE_STAKING_STEP_DISPLAY_OPERATION); + CASE(POLICY_ALLOW_WITHOUT_PROMPT, HANDLE_CERTIFICATE_STAKING_STEP_RESPOND); #undef CASE default: THROW(ERR_NOT_IMPLEMENTED); } - signTx_handleCertificate_ui_runStep(); + signTx_handleCertificateStaking_ui_runStep(); +} + +static void _handleCertificateVoteDeleg() +{ + security_policy_t policy = policyForSignTxCertificateVoteDelegation( + ctx->commonTxData.txSigningMode, + &BODY_CTX->stageData.certificate.stakeCredential, + &BODY_CTX->stageData.certificate.drep + ); + TRACE("Policy: %d", (int) policy); + ENSURE_NOT_DENIED(policy); + + _addCertificateDataToTx(&BODY_CTX->stageData.certificate, &BODY_CTX->txHashBuilder); + + switch (policy) { +#define CASE(POLICY, UI_STEP) case POLICY: {ctx->ui_step=UI_STEP; break;} + CASE(POLICY_PROMPT_BEFORE_RESPONSE, HANDLE_CERTIFICATE_VOTE_DELEG_STEP_DISPLAY_OPERATION); + CASE(POLICY_ALLOW_WITHOUT_PROMPT, HANDLE_CERTIFICATE_VOTE_DELEG_STEP_RESPOND); +#undef CASE + default: + THROW(ERR_NOT_IMPLEMENTED); + } + + signTx_handleCertificateVoteDeleg_ui_runStep(); +} + +static void _handleCertificateCommitteeAuth() +{ + security_policy_t policy = policyForSignTxCertificateCommitteeAuth( + ctx->commonTxData.txSigningMode, + &BODY_CTX->stageData.certificate.committeeColdCredential, + &BODY_CTX->stageData.certificate.committeeHotCredential + ); + TRACE("Policy: %d", (int) policy); + ENSURE_NOT_DENIED(policy); + + _addCertificateDataToTx(&BODY_CTX->stageData.certificate, &BODY_CTX->txHashBuilder); + + switch (policy) { +#define CASE(POLICY, UI_STEP) case POLICY: {ctx->ui_step=UI_STEP; break;} + CASE(POLICY_PROMPT_BEFORE_RESPONSE, HANDLE_CERTIFICATE_COMM_AUTH_STEP_DISPLAY_OPERATION); + CASE(POLICY_ALLOW_WITHOUT_PROMPT, HANDLE_CERTIFICATE_COMM_AUTH_STEP_RESPOND); +#undef CASE + default: + THROW(ERR_NOT_IMPLEMENTED); + } + + signTx_handleCertificateCommitteeAuth_ui_runStep(); +} + +static void _handleCertificateCommitteeResign() +{ + security_policy_t policy = policyForSignTxCertificateCommitteeResign( + ctx->commonTxData.txSigningMode, + &BODY_CTX->stageData.certificate.committeeColdCredential + ); + TRACE("Policy: %d", (int) policy); + ENSURE_NOT_DENIED(policy); + + _addCertificateDataToTx(&BODY_CTX->stageData.certificate, &BODY_CTX->txHashBuilder); + + switch (policy) { +#define CASE(POLICY, UI_STEP) case POLICY: {ctx->ui_step=UI_STEP; break;} + CASE(POLICY_PROMPT_BEFORE_RESPONSE, HANDLE_CERTIFICATE_COMM_RESIGN_STEP_DISPLAY_OPERATION); + CASE(POLICY_ALLOW_WITHOUT_PROMPT, HANDLE_CERTIFICATE_COMM_RESIGN_STEP_RESPOND); +#undef CASE + default: + THROW(ERR_NOT_IMPLEMENTED); + } + + signTx_handleCertificateCommitteeResign_ui_runStep(); +} + +static void _handleCertificateDRep() +{ + security_policy_t policy = policyForSignTxCertificateDRep( + ctx->commonTxData.txSigningMode, + &BODY_CTX->stageData.certificate.drepCredential + ); + TRACE("Policy: %d", (int) policy); + ENSURE_NOT_DENIED(policy); + + _addCertificateDataToTx(&BODY_CTX->stageData.certificate, &BODY_CTX->txHashBuilder); + + switch (policy) { +#define CASE(POLICY, UI_STEP) case POLICY: {ctx->ui_step=UI_STEP; break;} + CASE(POLICY_PROMPT_BEFORE_RESPONSE, HANDLE_CERTIFICATE_DREP_STEP_DISPLAY_OPERATION); + CASE(POLICY_ALLOW_WITHOUT_PROMPT, HANDLE_CERTIFICATE_DREP_STEP_RESPOND); +#undef CASE + default: + THROW(ERR_NOT_IMPLEMENTED); + } + + signTx_handleCertificateDRep_ui_runStep(); } #ifdef APP_FEATURE_POOL_REGISTRATION @@ -1198,11 +1500,35 @@ static void signTx_handleCertificateAPDU(uint8_t p2, const uint8_t* wireDataBuff switch (BODY_CTX->stageData.certificate.type) { case CERTIFICATE_TYPE_STAKE_REGISTRATION: case CERTIFICATE_TYPE_STAKE_DEREGISTRATION: + case CERTIFICATE_TYPE_STAKE_REG: + case CERTIFICATE_TYPE_STAKE_UNREG: case CERTIFICATE_TYPE_STAKE_DELEGATION: { _handleCertificateStaking(); return; } + case CERTIFICATE_TYPE_VOTE_DELEG: { + _handleCertificateVoteDeleg(); + return; + } + + case CERTIFICATE_TYPE_COMMITTEE_AUTH: { + _handleCertificateCommitteeAuth(); + return; + } + + case CERTIFICATE_TYPE_COMMITTEE_RESIGN: { + _handleCertificateCommitteeResign(); + return; + } + + case CERTIFICATE_TYPE_DREP_REG: + case CERTIFICATE_TYPE_DREP_UNREG: + case CERTIFICATE_TYPE_DREP_UPDATE: { + _handleCertificateDRep(); + return; + } + #ifdef APP_FEATURE_POOL_REGISTRATION case CERTIFICATE_TYPE_STAKE_POOL_REGISTRATION: { _handleCertificatePoolRegistration(); diff --git a/src/signTx.h b/src/signTx.h index 9bdb9929..b35e3b15 100644 --- a/src/signTx.h +++ b/src/signTx.h @@ -99,19 +99,42 @@ typedef struct { }; } ext_credential_t; +// DReps are extended to allow key derivation paths +typedef enum { + EXT_DREP_KEY_HASH = 0, + EXT_DREP_SCRIPT_HASH = 1, + EXT_DREP_ALWAYS_ABSTAIN = 2, + EXT_DREP_ALWAYS_NO_CONFIDENCE = 3, + EXT_DREP_KEY_PATH = 4, +} ext_drep_type_t; + +typedef struct { + ext_drep_type_t type; + union { + bip44_path_t keyPath; + uint8_t keyHash[ADDRESS_KEY_HASH_LENGTH]; + uint8_t scriptHash[SCRIPT_HASH_LENGTH]; + }; +} ext_drep_t; + typedef struct { certificate_type_t type; union { ext_credential_t stakeCredential; - // TODO ext_credential_t committeeColdCredential; + ext_credential_t committeeColdCredential; + ext_credential_t drepCredential; }; union { ext_credential_t poolCredential; - // TODO ext_credential_t drepCredential; - // TODO ext_credential_t committeeHotCredential; + ext_credential_t committeeHotCredential; + ext_drep_t drep; + anchor_t anchor; + }; + union { + uint64_t epoch; // in pool retirement + uint64_t coin; // not in pool retirement; represents deposit in certs }; - uint64_t epoch; } sign_tx_certificate_data_t; typedef struct { diff --git a/src/signTxPoolRegistration_ui.c b/src/signTxPoolRegistration_ui.c index 855d840b..433555d5 100644 --- a/src/signTxPoolRegistration_ui.c +++ b/src/signTxPoolRegistration_ui.c @@ -686,7 +686,7 @@ void handleMetadata_ui_runStep() UI_STEP(HANDLE_METADATA_STEP_DISPLAY_HASH) { char metadataHashHex[1 + 2 * POOL_METADATA_HASH_LENGTH] = {0}; explicit_bzero(metadataHashHex, SIZEOF(metadataHashHex)); - size_t len = str_formatMetadata( + size_t len = encode_hex( md->hash, SIZEOF(md->hash), metadataHashHex, SIZEOF(metadataHashHex) ); diff --git a/src/signTx_ui.c b/src/signTx_ui.c index 74be17dd..c1b4e9d8 100644 --- a/src/signTx_ui.c +++ b/src/signTx_ui.c @@ -353,22 +353,184 @@ void signTx_handleTtl_ui_runStep() #ifdef HAVE_NBGL static void signTx_handleCertificate_ui_delegation_cb(void) { + sign_tx_certificate_data_t* cert = &BODY_CTX->stageData.certificate; + char encodedStr[BECH32_STRING_SIZE_MAX] = {0}; - ui_getBech32Screen(encodedStr, SIZEOF(encodedStr), "pool", BODY_CTX->stageData.certificate.poolCredential.keyHash, SIZEOF(BODY_CTX->stageData.certificate.poolCredential.keyHash)); - fill_and_display_if_required("Pool", encodedStr, signTx_handleCertificate_ui_runStep, respond_with_user_reject); + ASSERT(cert->poolCredential.type == EXT_CREDENTIAL_KEY_HASH); + ui_getBech32Screen( + encodedStr, SIZEOF(encodedStr), + "pool", + cert->poolCredential.keyHash, SIZEOF(cert->poolCredential.keyHash) + ); + fill_and_display_if_required("Pool", encodedStr, signTx_handleCertificateStaking_ui_runStep, respond_with_user_reject); } #endif -void signTx_handleCertificate_ui_runStep() +static void _displayCredential( + ui_callback_fn_t* callback, + ext_credential_t* credential, + const char* keyPathLabel, + const char* keyHashLabel, + const char* keyHashPrefix, + const char* scriptHashLabel, + const char* scriptHashPrefix +) +{ + switch (credential->type) { + case EXT_CREDENTIAL_KEY_PATH: + #ifdef HAVE_BAGL + ui_displayPathScreen( + keyPathLabel, + &credential->keyPath, + callback + ); + #elif defined(HAVE_NBGL) + { + char pathStr[BIP44_PATH_STRING_SIZE_MAX + 1] = {0}; + ui_getPathScreen(pathStr, SIZEOF(pathStr), &credential->keyPath); + fill_and_display_if_required(keyPathLabel, pathStr, callback, respond_with_user_reject); + } + #endif // HAVE_BAGL + break; + case EXT_CREDENTIAL_KEY_HASH: + #ifdef HAVE_BAGL + ui_displayBech32Screen( + keyHashLabel, + keyHashPrefix, + credential->keyHash, + SIZEOF(credential->keyHash), + callback + ); + #elif defined(HAVE_NBGL) + { + char encodedStr[BECH32_STRING_SIZE_MAX] = {0}; + ui_getBech32Screen(encodedStr, SIZEOF(encodedStr), keyHashPrefix, credential->keyHash, SIZEOF(credential->keyHash)); + fill_and_display_if_required(keyHashLabel, encodedStr, callback, respond_with_user_reject); + } + #endif // HAVE_BAGL + break; + case EXT_CREDENTIAL_SCRIPT_HASH: + #ifdef HAVE_BAGL + ui_displayBech32Screen( + scriptHashLabel, + scriptHashPrefix, + credential->scriptHash, + SIZEOF(credential->scriptHash), + callback + ); + #elif defined(HAVE_NBGL) + { + char encodedStr[BECH32_STRING_SIZE_MAX] = {0}; + ui_getBech32Screen(encodedStr, SIZEOF(encodedStr), scriptHashPrefix, credential->scriptHash, SIZEOF(credential->scriptHash)); + fill_and_display_if_required(scriptHashLabel, encodedStr, callback, respond_with_user_reject); + } + #endif // HAVE_BAGL + break; + default: + ASSERT(false); + break; + } +} + +static void _displayDeposit( + ui_callback_fn_t* callback, + uint64_t coin +) +{ + #ifdef HAVE_BAGL + ui_displayAdaAmountScreen( + "Deposit", + coin, + callback + ); + #elif defined(HAVE_NBGL) + char adaAmountStr[50] = {0}; + ui_getAdaAmountScreen(adaAmountStr, SIZEOF(adaAmountStr), coin); + fill_and_display_if_required("Deposit", adaAmountStr, callback, respond_with_user_reject); + #endif // HAVE_BAGL +} + +static void _displayAnchorNull(ui_callback_fn_t* callback) +{ + #ifdef HAVE_BAGL + ui_displayPaginatedText( + "Anchor", + "null", + callback + ); + #elif defined(HAVE_NBGL) + fill_and_display_if_required( + "Anchor", + "null", + callback, + respond_with_user_reject + ); + #endif // HAVE_BAGL +} + +static void _displayAnchorUrl(ui_callback_fn_t* callback, anchor_t* anchor) +{ + char urlStr[1 + ANCHOR_URL_LENGTH_MAX] = {0}; + explicit_bzero(urlStr, SIZEOF(urlStr)); + ASSERT(anchor->urlLength <= ANCHOR_URL_LENGTH_MAX); + memmove(urlStr, anchor->url, anchor->urlLength); + urlStr[anchor->urlLength] = '\0'; + ASSERT(strlen(urlStr) == anchor->urlLength); + + #ifdef HAVE_BAGL + ui_displayPaginatedText( + "Anchor url", + urlStr, + callback + ); + #elif defined(HAVE_NBGL) + fill_and_display_if_required( + "Anchor url", + urlStr, + callback, + respond_with_user_reject + ); + #endif // HAVE_BAGL +} + +static void _displayAnchorHash(ui_callback_fn_t* callback, anchor_t* anchor) +{ + char hex[1 + 2 * ANCHOR_HASH_LENGTH] = {0}; + explicit_bzero(hex, SIZEOF(hex)); + size_t len = encode_hex( + anchor->hash, SIZEOF(anchor->hash), + hex, SIZEOF(hex) + ); + ASSERT(len + 1 == SIZEOF(hex)); + + #ifdef HAVE_BAGL + ui_displayPaginatedText( + "Anchor data hash", + hex, + callback + ); + #elif defined(HAVE_NBGL) + fill_and_display_if_required( + "Anchor data hash", + hex, + callback, + respond_with_user_reject + ); + #endif // HAVE_BAGL +} + +void signTx_handleCertificateStaking_ui_runStep() { TRACE("UI step %d", ctx->ui_step); - ui_callback_fn_t* this_fn = signTx_handleCertificate_ui_runStep; + ui_callback_fn_t* this_fn = signTx_handleCertificateStaking_ui_runStep; + sign_tx_certificate_data_t* cert = &BODY_CTX->stageData.certificate; UI_STEP_BEGIN(ctx->ui_step, this_fn); - UI_STEP(HANDLE_CERTIFICATE_STEP_DISPLAY_OPERATION) { - switch (BODY_CTX->stageData.certificate.type) { + UI_STEP(HANDLE_CERTIFICATE_STAKING_STEP_DISPLAY_OPERATION) { + switch (cert->type) { case CERTIFICATE_TYPE_STAKE_REGISTRATION: + case CERTIFICATE_TYPE_STAKE_REG: #ifdef HAVE_BAGL ui_displayPaginatedText( "Register", @@ -382,6 +544,7 @@ void signTx_handleCertificate_ui_runStep() break; case CERTIFICATE_TYPE_STAKE_DEREGISTRATION: + case CERTIFICATE_TYPE_STAKE_UNREG: #ifdef HAVE_BAGL ui_displayPaginatedText( "Deregister", @@ -396,10 +559,11 @@ void signTx_handleCertificate_ui_runStep() case CERTIFICATE_TYPE_STAKE_DELEGATION: #ifdef HAVE_BAGL + ASSERT(cert->poolCredential.type == EXT_CREDENTIAL_KEY_HASH); ui_displayBech32Screen( "Delegate stake", "to pool", - BODY_CTX->stageData.certificate.poolCredential.keyHash, SIZEOF(BODY_CTX->stageData.certificate.poolCredential.keyHash), + cert->poolCredential.keyHash, SIZEOF(cert->poolCredential.keyHash), this_fn ); #elif defined(HAVE_NBGL) @@ -415,68 +579,455 @@ void signTx_handleCertificate_ui_runStep() ASSERT(false); } } - UI_STEP(HANDLE_CERTIFICATE_STEP_DISPLAY_STAKING_KEY) { - switch (BODY_CTX->stageData.certificate.stakeCredential.type) { - case EXT_CREDENTIAL_KEY_PATH: + UI_STEP(HANDLE_CERTIFICATE_STAKING_STEP_DISPLAY_STAKE_CRED) { + _displayCredential( + this_fn, + &cert->stakeCredential, + "Stake key", + "Stake key hash", + "stake_vkh", + "Stake script hash", + "script" + ); + } + UI_STEP(HANDLE_CERTIFICATE_STAKING_STEP_DISPLAY_DEPOSIT) { + switch (cert->type) { + case CERTIFICATE_TYPE_STAKE_REGISTRATION: + case CERTIFICATE_TYPE_STAKE_DEREGISTRATION: + case CERTIFICATE_TYPE_STAKE_DELEGATION: + // no deposit in these + UI_STEP_JUMP(HANDLE_CERTIFICATE_STAKING_STEP_CONFIRM); + break; + + case CERTIFICATE_TYPE_STAKE_REG: + case CERTIFICATE_TYPE_STAKE_UNREG: + _displayDeposit(this_fn, cert->coin); + break; + + default: + ASSERT(false); + } + } + UI_STEP(HANDLE_CERTIFICATE_STAKING_STEP_CONFIRM) { + char description[50] = {0}; + explicit_bzero(description, SIZEOF(description)); + + switch (cert->type) { + case CERTIFICATE_TYPE_STAKE_REGISTRATION: + case CERTIFICATE_TYPE_STAKE_REG: + #ifdef HAVE_BAGL + snprintf(description, SIZEOF(description), "registration?"); + #elif defined(HAVE_NBGL) + display_confirmation("Confirm\nregistration", "", "REGISTRATION\nACCEPTED", "Registration\nrejected", this_fn, respond_with_user_reject); + #endif // HAVE_BAGL + break; + + case CERTIFICATE_TYPE_STAKE_DEREGISTRATION: + case CERTIFICATE_TYPE_STAKE_UNREG: + #ifdef HAVE_BAGL + snprintf(description, SIZEOF(description), "deregistration?"); + #elif defined(HAVE_NBGL) + display_confirmation("Confirm\nderegistration", "", "DEREGISTRATION\nACCEPTED", "Deregistration\nrejected", this_fn, respond_with_user_reject); + #endif // HAVE_BAGL + break; + + case CERTIFICATE_TYPE_STAKE_DELEGATION: + #ifdef HAVE_BAGL + snprintf(description, SIZEOF(description), "delegation?"); + #elif defined(HAVE_NBGL) + display_confirmation("Confirm\ndelegation", "", "DELEGATION\nACCEPTED", "Delegation\nrejected", this_fn, respond_with_user_reject); + #endif // HAVE_BAGL + break; + + default: + ASSERT(false); + } + // make sure all the information is displayed to the user + ASSERT(strlen(description) + 1 < SIZEOF(description)); + + #ifdef HAVE_BAGL + ui_displayPrompt( + "Confirm", + description, + this_fn, + respond_with_user_reject + ); + #elif defined(HAVE_NBGL) + #endif // HAVE_BAGL + } + UI_STEP(HANDLE_CERTIFICATE_STAKING_STEP_RESPOND) { + respondSuccessEmptyMsg(); + + tx_advanceCertificatesStateIfAppropriate(); + } + UI_STEP_END(HANDLE_CERTIFICATE_STAKING_STEP_INVALID); +} + +void signTx_handleCertificateVoteDeleg_ui_runStep() +{ + TRACE("UI step %d", ctx->ui_step); + ui_callback_fn_t* this_fn = signTx_handleCertificateVoteDeleg_ui_runStep; + sign_tx_certificate_data_t* cert = &BODY_CTX->stageData.certificate; + + UI_STEP_BEGIN(ctx->ui_step, this_fn); + + UI_STEP(HANDLE_CERTIFICATE_VOTE_DELEG_STEP_DISPLAY_OPERATION) { + #ifdef HAVE_BAGL + ui_displayPaginatedText( + "Delegate", + "vote", + this_fn + ); + #elif defined(HAVE_NBGL) + set_light_confirmation(true); + display_prompt("Delegate\nvote", "", this_fn, respond_with_user_reject); + #endif // HAVE_BAGL + } + UI_STEP(HANDLE_CERTIFICATE_VOTE_DELEG_STEP_DISPLAY_STAKE_CRED) { + _displayCredential( + this_fn, + &cert->stakeCredential, + "Stake key", + "Stake key hash", + "stake_vkh", + "Stake script hash", + "script" + ); + } + UI_STEP(HANDLE_CERTIFICATE_VOTE_DELEG_STEP_DISPLAY_DREP) { + switch (cert->drep.type) { + case EXT_DREP_KEY_PATH: #ifdef HAVE_BAGL ui_displayPathScreen( - "Stake key", - &BODY_CTX->stageData.certificate.stakeCredential.keyPath, + "DRep key", + &cert->drep.keyPath, this_fn ); #elif defined(HAVE_NBGL) { char pathStr[BIP44_PATH_STRING_SIZE_MAX + 1] = {0}; - ui_getPathScreen(pathStr, SIZEOF(pathStr), &BODY_CTX->stageData.certificate.stakeCredential.keyPath); - fill_and_display_if_required("Stake key", pathStr, this_fn, respond_with_user_reject); + ui_getPathScreen(pathStr, SIZEOF(pathStr), &cert->drep.keyPath); + fill_and_display_if_required("DRep key", pathStr, this_fn, respond_with_user_reject); } #endif // HAVE_BAGL break; - case EXT_CREDENTIAL_KEY_HASH: + case EXT_DREP_KEY_HASH: #ifdef HAVE_BAGL ui_displayBech32Screen( - "Stake key hash", - "stake_vkh", - BODY_CTX->stageData.certificate.stakeCredential.keyHash, - SIZEOF(BODY_CTX->stageData.certificate.stakeCredential.keyHash), + "DRep key hash", + "drep", + cert->drep.keyHash, + SIZEOF(cert->drep.keyHash), this_fn ); #elif defined(HAVE_NBGL) { char encodedStr[BECH32_STRING_SIZE_MAX] = {0}; - ui_getBech32Screen(encodedStr, SIZEOF(encodedStr), "stake_vkh", BODY_CTX->stageData.certificate.stakeCredential.keyHash, SIZEOF(BODY_CTX->stageData.certificate.stakeCredential.keyHash)); - fill_and_display_if_required("Stake key hash", encodedStr, this_fn, respond_with_user_reject); + ui_getBech32Screen(encodedStr, SIZEOF(encodedStr), "drep", cert->drep.keyHash, SIZEOF(cert->drep.keyHash)); + fill_and_display_if_required("DRep key hash", encodedStr, this_fn, respond_with_user_reject); } #endif // HAVE_BAGL break; - case EXT_CREDENTIAL_SCRIPT_HASH: + case EXT_DREP_SCRIPT_HASH: #ifdef HAVE_BAGL ui_displayBech32Screen( - "Stake script hash", - "script", - BODY_CTX->stageData.certificate.stakeCredential.scriptHash, - SIZEOF(BODY_CTX->stageData.certificate.stakeCredential.scriptHash), + "DRep script hash", + "drep", + cert->drep.scriptHash, + SIZEOF(cert->drep.scriptHash), this_fn ); #elif defined(HAVE_NBGL) { char encodedStr[BECH32_STRING_SIZE_MAX] = {0}; - ui_getBech32Screen(encodedStr, SIZEOF(encodedStr), "script", BODY_CTX->stageData.certificate.stakeCredential.scriptHash, SIZEOF(BODY_CTX->stageData.certificate.stakeCredential.scriptHash)); - fill_and_display_if_required("Stake script hash", encodedStr, this_fn, respond_with_user_reject); + ui_getBech32Screen(encodedStr, SIZEOF(encodedStr), "drep", cert->drep.scriptHash, SIZEOF(cert->drep.scriptHash)); + fill_and_display_if_required("DRep script hash", encodedStr, this_fn, respond_with_user_reject); } #endif // HAVE_BAGL break; + case DREP_ALWAYS_ABSTAIN: + #ifdef HAVE_BAGL + ui_displayPaginatedText( + "Always", + "abstain", + this_fn + ); + #elif defined(HAVE_NBGL) + set_light_confirmation(true); + display_prompt("Always\nabstain", "", this_fn, respond_with_user_reject); + #endif // HAVE_BAGL + break; + case DREP_ALWAYS_NO_CONFIDENCE: + #ifdef HAVE_BAGL + ui_displayPaginatedText( + "Always", + "no confidence", + this_fn + ); + #elif defined(HAVE_NBGL) + set_light_confirmation(true); + display_prompt("Always\nno confidence", "", this_fn, respond_with_user_reject); + #endif // HAVE_BAGL + break; + default: + ASSERT(false); + break; + } + } + UI_STEP(HANDLE_CERTIFICATE_VOTE_DELEG_STEP_CONFIRM) { + #ifdef HAVE_BAGL + ui_displayPrompt( + "Confirm vote", + "delegation", + this_fn, + respond_with_user_reject + ); + #elif defined(HAVE_NBGL) + display_confirmation("Confirm vote\ndelegation", "", "DELEGATION\nACCEPTED", "Delegation\nrejected", this_fn, respond_with_user_reject); + #endif // HAVE_BAGL + } + UI_STEP(HANDLE_CERTIFICATE_VOTE_DELEG_STEP_RESPOND) { + respondSuccessEmptyMsg(); + + tx_advanceCertificatesStateIfAppropriate(); + } + UI_STEP_END(HANDLE_CERTIFICATE_VOTE_DELEG_STEP_INVALID); +} + +void signTx_handleCertificateCommitteeAuth_ui_runStep() +{ + TRACE("UI step %d", ctx->ui_step); + ui_callback_fn_t* this_fn = signTx_handleCertificateCommitteeAuth_ui_runStep; + sign_tx_certificate_data_t* cert = &BODY_CTX->stageData.certificate; + + UI_STEP_BEGIN(ctx->ui_step, this_fn); + + UI_STEP(HANDLE_CERTIFICATE_COMM_AUTH_STEP_DISPLAY_OPERATION) { + #ifdef HAVE_BAGL + ui_displayPaginatedText( + "Authorize", + "committee", + this_fn + ); + #elif defined(HAVE_NBGL) + set_light_confirmation(true); + display_prompt("Authorize committee", "", this_fn, respond_with_user_reject); + #endif // HAVE_BAGL + } + UI_STEP(HANDLE_CERTIFICATE_COMM_AUTH_STEP_DISPLAY_COLD_CRED) { + _displayCredential( + this_fn, + &cert->committeeColdCredential, + "Comm. cold key", + "Comm. cold key hash", + "cc_cold", + "Comm. cold script", + "cc_cold" + ); + } + UI_STEP(HANDLE_CERTIFICATE_COMM_AUTH_STEP_DISPLAY_HOT_CRED) { + _displayCredential( + this_fn, + &cert->committeeHotCredential, + "Comm. hot key", + "Comm. hot key hash", + "cc_hot", + "Comm. hot script", + "cc_hot" + ); + } + UI_STEP(HANDLE_CERTIFICATE_COMM_AUTH_STEP_CONFIRM) { + #ifdef HAVE_BAGL + ui_displayPrompt( + "Confirm", + "authorization?", + this_fn, + respond_with_user_reject + ); + #elif defined(HAVE_NBGL) + display_confirmation("Confirm\nauthorization", "", "AUTHORIZATION\nACCEPTED", "Authorization\nrejected", this_fn, respond_with_user_reject); + #endif // HAVE_BAGL + } + UI_STEP(HANDLE_CERTIFICATE_COMM_AUTH_STEP_RESPOND) { + respondSuccessEmptyMsg(); + + tx_advanceCertificatesStateIfAppropriate(); + } + UI_STEP_END(HANDLE_CERTIFICATE_COMM_AUTH_STEP_INVALID); +} + +void signTx_handleCertificateCommitteeResign_ui_runStep() +{ + TRACE("UI step %d", ctx->ui_step); + ui_callback_fn_t* this_fn = signTx_handleCertificateCommitteeResign_ui_runStep; + sign_tx_certificate_data_t* cert = &BODY_CTX->stageData.certificate; + + UI_STEP_BEGIN(ctx->ui_step, this_fn); + + UI_STEP(HANDLE_CERTIFICATE_COMM_RESIGN_STEP_DISPLAY_OPERATION) { + #ifdef HAVE_BAGL + ui_displayPaginatedText( + "Resign from", + "committee", + this_fn + ); + #elif defined(HAVE_NBGL) + set_light_confirmation(true); + display_prompt("Resign from\ncommittee", "", this_fn, respond_with_user_reject); + #endif // HAVE_BAGL + } + UI_STEP(HANDLE_CERTIFICATE_COMM_RESIGN_STEP_DISPLAY_COLD_CRED) { + _displayCredential( + this_fn, + &cert->committeeColdCredential, + "Comm. cold key", + "Comm. cold key hash", + "cc_cold", + "Comm. cold script", + "cc_cold" + ); + } + UI_STEP(HANDLE_CERTIFICATE_COMM_RESIGN_STEP_DISPLAY_ANCHOR_NULL) { + if (cert->anchor.isIncluded) { + UI_STEP_JUMP(HANDLE_CERTIFICATE_COMM_RESIGN_STEP_DISPLAY_ANCHOR_URL); + } + _displayAnchorNull(this_fn); + } + UI_STEP(HANDLE_CERTIFICATE_COMM_RESIGN_STEP_DISPLAY_ANCHOR_URL) { + if (!cert->anchor.isIncluded) { + UI_STEP_JUMP(HANDLE_CERTIFICATE_COMM_RESIGN_STEP_CONFIRM); + } + _displayAnchorUrl(this_fn, &cert->anchor); + } + UI_STEP(HANDLE_CERTIFICATE_COMM_RESIGN_STEP_DISPLAY_ANCHOR_HASH) { + _displayAnchorHash(this_fn, &cert->anchor); + } + UI_STEP(HANDLE_CERTIFICATE_COMM_RESIGN_STEP_CONFIRM) { + #ifdef HAVE_BAGL + ui_displayPrompt( + "Confirm", + "resignation", + this_fn, + respond_with_user_reject + ); + #elif defined(HAVE_NBGL) + display_confirmation("Confirm\nresignation", "", "RESIGNATION\nACCEPTED", "Resignation\nrejected", this_fn, respond_with_user_reject); + #endif // HAVE_BAGL + } + UI_STEP(HANDLE_CERTIFICATE_COMM_RESIGN_STEP_RESPOND) { + respondSuccessEmptyMsg(); + + tx_advanceCertificatesStateIfAppropriate(); + } + UI_STEP_END(HANDLE_CERTIFICATE_COMM_RESIGN_STEP_INVALID); +} + +void signTx_handleCertificateDRep_ui_runStep() +{ + TRACE("UI step %d", ctx->ui_step); + ui_callback_fn_t* this_fn = signTx_handleCertificateDRep_ui_runStep; + sign_tx_certificate_data_t* cert = &BODY_CTX->stageData.certificate; + + UI_STEP_BEGIN(ctx->ui_step, this_fn); + + UI_STEP(HANDLE_CERTIFICATE_DREP_STEP_DISPLAY_OPERATION) { + switch (cert->type) { + case CERTIFICATE_TYPE_DREP_REG: + #ifdef HAVE_BAGL + ui_displayPaginatedText( + "Register", + "DRep", + this_fn + ); + #elif defined(HAVE_NBGL) + set_light_confirmation(true); + display_prompt("Register\nDRep", "", this_fn, respond_with_user_reject); + #endif // HAVE_BAGL + break; + + case CERTIFICATE_TYPE_DREP_UNREG: + case CERTIFICATE_TYPE_STAKE_UNREG: + #ifdef HAVE_BAGL + ui_displayPaginatedText( + "Deregister", + "DRep", + this_fn + ); + #elif defined(HAVE_NBGL) + set_light_confirmation(true); + display_prompt("Deregister\nDRep", "", this_fn, respond_with_user_reject); + #endif // HAVE_BAGL + break; + + case CERTIFICATE_TYPE_DREP_UPDATE: + #ifdef HAVE_BAGL + ui_displayPaginatedText( + "Update", + "DRep", + this_fn + ); + #elif defined(HAVE_NBGL) + set_light_confirmation(true); + display_prompt("Update\nDRep", "", this_fn, respond_with_user_reject); + #endif // HAVE_BAGL + break; + default: ASSERT(false); + } + } + UI_STEP(HANDLE_CERTIFICATE_DREP_STEP_DISPLAY_CREDENTIAL) { + _displayCredential( + this_fn, + &cert->drepCredential, + "DRep key", + "DRep key hash", + "drep", + "DRep script hash", + "drep" + ); + } + UI_STEP(HANDLE_CERTIFICATE_DREP_STEP_DISPLAY_DEPOSIT) { + switch (cert->type) { + case CERTIFICATE_TYPE_DREP_UPDATE: + // no deposit in these + UI_STEP_JUMP(HANDLE_CERTIFICATE_DREP_STEP_DISPLAY_ANCHOR_NULL); + break; + + case CERTIFICATE_TYPE_DREP_REG: + case CERTIFICATE_TYPE_DREP_UNREG: + _displayDeposit(this_fn, cert->coin); break; + + default: + ASSERT(false); + } + } + UI_STEP(HANDLE_CERTIFICATE_DREP_STEP_DISPLAY_ANCHOR_NULL) { + if (cert->type == CERTIFICATE_TYPE_DREP_UNREG) { + // no anchor for this type + UI_STEP_JUMP(HANDLE_CERTIFICATE_DREP_STEP_CONFIRM); + } + if (cert->anchor.isIncluded) { + UI_STEP_JUMP(HANDLE_CERTIFICATE_DREP_STEP_DISPLAY_ANCHOR_URL); } + _displayAnchorNull(this_fn); } - UI_STEP(HANDLE_CERTIFICATE_STEP_CONFIRM) { + UI_STEP(HANDLE_CERTIFICATE_DREP_STEP_DISPLAY_ANCHOR_URL) { + if (!cert->anchor.isIncluded) { + UI_STEP_JUMP(HANDLE_CERTIFICATE_DREP_STEP_CONFIRM); + } + _displayAnchorUrl(this_fn, &cert->anchor); + } + UI_STEP(HANDLE_CERTIFICATE_DREP_STEP_DISPLAY_ANCHOR_HASH) { + _displayAnchorHash(this_fn, &cert->anchor); + } + UI_STEP(HANDLE_CERTIFICATE_DREP_STEP_CONFIRM) { char description[50] = {0}; explicit_bzero(description, SIZEOF(description)); - switch (BODY_CTX->stageData.certificate.type) { - case CERTIFICATE_TYPE_STAKE_REGISTRATION: + switch (cert->type) { + case CERTIFICATE_TYPE_DREP_REG: #ifdef HAVE_BAGL snprintf(description, SIZEOF(description), "registration?"); #elif defined(HAVE_NBGL) @@ -484,7 +1035,7 @@ void signTx_handleCertificate_ui_runStep() #endif // HAVE_BAGL break; - case CERTIFICATE_TYPE_STAKE_DEREGISTRATION: + case CERTIFICATE_TYPE_DREP_UNREG: #ifdef HAVE_BAGL snprintf(description, SIZEOF(description), "deregistration?"); #elif defined(HAVE_NBGL) @@ -492,11 +1043,11 @@ void signTx_handleCertificate_ui_runStep() #endif // HAVE_BAGL break; - case CERTIFICATE_TYPE_STAKE_DELEGATION: + case CERTIFICATE_TYPE_DREP_UPDATE: #ifdef HAVE_BAGL - snprintf(description, SIZEOF(description), "delegation?"); + snprintf(description, SIZEOF(description), "update?"); #elif defined(HAVE_NBGL) - display_confirmation("Confirm\ndelegation", "", "DELEGATION\nACCEPTED", "Delegation\nrejected", this_fn, respond_with_user_reject); + display_confirmation("Confirm\nupdate", "", "UPDATE\nACCEPTED", "Update\nrejected", this_fn, respond_with_user_reject); #endif // HAVE_BAGL break; @@ -516,12 +1067,12 @@ void signTx_handleCertificate_ui_runStep() #elif defined(HAVE_NBGL) #endif // HAVE_BAGL } - UI_STEP(HANDLE_CERTIFICATE_STEP_RESPOND) { + UI_STEP(HANDLE_CERTIFICATE_DREP_STEP_RESPOND) { respondSuccessEmptyMsg(); tx_advanceCertificatesStateIfAppropriate(); } - UI_STEP_END(HANDLE_CERTIFICATE_STEP_INVALID); + UI_STEP_END(HANDLE_CERTIFICATE_DREP_STEP_INVALID); } #ifdef APP_FEATURE_POOL_RETIREMENT @@ -529,9 +1080,10 @@ void signTx_handleCertificate_ui_runStep() void signTx_handleCertificatePoolRetirement_ui_runStep() { TRACE("UI step %d", ctx->ui_step); - ASSERT(BODY_CTX->stageData.certificate.type == CERTIFICATE_TYPE_STAKE_POOL_RETIREMENT); ui_callback_fn_t* this_fn = signTx_handleCertificatePoolRetirement_ui_runStep; + sign_tx_certificate_data_t* cert = &BODY_CTX->stageData.certificate; + ASSERT(cert->type == CERTIFICATE_TYPE_STAKE_POOL_RETIREMENT); UI_STEP_BEGIN(ctx->ui_step, this_fn); @@ -540,13 +1092,13 @@ void signTx_handleCertificatePoolRetirement_ui_runStep() ui_displayBech32Screen( "Retire stake pool", "pool", - BODY_CTX->stageData.certificate.poolCredential.keyHash, SIZEOF(BODY_CTX->stageData.certificate.poolCredential.keyHash), + cert->poolCredential.keyHash, SIZEOF(cert->poolCredential.keyHash), this_fn ); #elif defined(HAVE_NBGL) set_light_confirmation(true); char encodedStr[BECH32_STRING_SIZE_MAX] = {0}; - ui_getBech32Screen(encodedStr, SIZEOF(encodedStr), "pool", BODY_CTX->stageData.certificate.poolCredential.keyHash, SIZEOF(BODY_CTX->stageData.certificate.poolCredential.keyHash)); + ui_getBech32Screen(encodedStr, SIZEOF(encodedStr), "pool", cert->poolCredential.keyHash, SIZEOF(cert->poolCredential.keyHash)); fill_and_display_if_required("Retire stake pool", encodedStr, this_fn, respond_with_user_reject); #endif // HAVE_BAGL } @@ -554,7 +1106,7 @@ void signTx_handleCertificatePoolRetirement_ui_runStep() #ifdef HAVE_BAGL ui_displayUint64Screen( "at the start of epoch", - BODY_CTX->stageData.certificate.epoch, + cert->epoch, this_fn ); #elif defined(HAVE_NBGL) @@ -562,7 +1114,7 @@ void signTx_handleCertificatePoolRetirement_ui_runStep() ui_getUint64Screen( line, SIZEOF(line), - BODY_CTX->stageData.certificate.epoch + cert->epoch ); fill_and_display_if_required("Start of epoch", line, this_fn, respond_with_user_reject); #endif // HAVE_BAGL diff --git a/src/signTx_ui.h b/src/signTx_ui.h index 775517d0..cbdf555a 100644 --- a/src/signTx_ui.h +++ b/src/signTx_ui.h @@ -67,23 +67,67 @@ void signTx_handleTtl_ui_runStep(); // ============================== CERTIFICATES ============================== enum { - HANDLE_CERTIFICATE_STEP_DISPLAY_OPERATION = 600, - HANDLE_CERTIFICATE_STEP_DISPLAY_STAKING_KEY, - HANDLE_CERTIFICATE_STEP_CONFIRM, - HANDLE_CERTIFICATE_STEP_RESPOND, - HANDLE_CERTIFICATE_STEP_INVALID, + HANDLE_CERTIFICATE_STAKING_STEP_DISPLAY_OPERATION = 600, + HANDLE_CERTIFICATE_STAKING_STEP_DISPLAY_STAKE_CRED, + HANDLE_CERTIFICATE_STAKING_STEP_DISPLAY_DEPOSIT, + HANDLE_CERTIFICATE_STAKING_STEP_CONFIRM, + HANDLE_CERTIFICATE_STAKING_STEP_RESPOND, + HANDLE_CERTIFICATE_STAKING_STEP_INVALID, }; +void signTx_handleCertificateStaking_ui_runStep(); -void signTx_handleCertificate_ui_runStep(); +enum { + HANDLE_CERTIFICATE_VOTE_DELEG_STEP_DISPLAY_OPERATION = 610, + HANDLE_CERTIFICATE_VOTE_DELEG_STEP_DISPLAY_STAKE_CRED, + HANDLE_CERTIFICATE_VOTE_DELEG_STEP_DISPLAY_DREP, + HANDLE_CERTIFICATE_VOTE_DELEG_STEP_CONFIRM, + HANDLE_CERTIFICATE_VOTE_DELEG_STEP_RESPOND, + HANDLE_CERTIFICATE_VOTE_DELEG_STEP_INVALID, +}; +void signTx_handleCertificateVoteDeleg_ui_runStep(); + +enum { + HANDLE_CERTIFICATE_COMM_AUTH_STEP_DISPLAY_OPERATION = 620, + HANDLE_CERTIFICATE_COMM_AUTH_STEP_DISPLAY_COLD_CRED, + HANDLE_CERTIFICATE_COMM_AUTH_STEP_DISPLAY_HOT_CRED, + HANDLE_CERTIFICATE_COMM_AUTH_STEP_CONFIRM, + HANDLE_CERTIFICATE_COMM_AUTH_STEP_RESPOND, + HANDLE_CERTIFICATE_COMM_AUTH_STEP_INVALID, +}; +void signTx_handleCertificateCommitteeAuth_ui_runStep(); + +enum { + HANDLE_CERTIFICATE_COMM_RESIGN_STEP_DISPLAY_OPERATION = 640, + HANDLE_CERTIFICATE_COMM_RESIGN_STEP_DISPLAY_COLD_CRED, + HANDLE_CERTIFICATE_COMM_RESIGN_STEP_DISPLAY_ANCHOR_NULL, + HANDLE_CERTIFICATE_COMM_RESIGN_STEP_DISPLAY_ANCHOR_URL, + HANDLE_CERTIFICATE_COMM_RESIGN_STEP_DISPLAY_ANCHOR_HASH, + HANDLE_CERTIFICATE_COMM_RESIGN_STEP_CONFIRM, + HANDLE_CERTIFICATE_COMM_RESIGN_STEP_RESPOND, + HANDLE_CERTIFICATE_COMM_RESIGN_STEP_INVALID, +}; +void signTx_handleCertificateCommitteeResign_ui_runStep(); + +enum { + HANDLE_CERTIFICATE_DREP_STEP_DISPLAY_OPERATION = 660, + HANDLE_CERTIFICATE_DREP_STEP_DISPLAY_CREDENTIAL, + HANDLE_CERTIFICATE_DREP_STEP_DISPLAY_DEPOSIT, + HANDLE_CERTIFICATE_DREP_STEP_DISPLAY_ANCHOR_NULL, + HANDLE_CERTIFICATE_DREP_STEP_DISPLAY_ANCHOR_URL, + HANDLE_CERTIFICATE_DREP_STEP_DISPLAY_ANCHOR_HASH, + HANDLE_CERTIFICATE_DREP_STEP_CONFIRM, + HANDLE_CERTIFICATE_DREP_STEP_RESPOND, + HANDLE_CERTIFICATE_DREP_STEP_INVALID, +}; +void signTx_handleCertificateDRep_ui_runStep(); enum { - HANDLE_CERTIFICATE_POOL_RETIREMENT_STEP_DISPLAY_OPERATION = 650, + HANDLE_CERTIFICATE_POOL_RETIREMENT_STEP_DISPLAY_OPERATION = 680, HANDLE_CERTIFICATE_POOL_RETIREMENT_STEP_DISPLAY_EPOCH, HANDLE_CERTIFICATE_POOL_RETIREMENT_STEP_CONFIRM, HANDLE_CERTIFICATE_POOL_RETIREMENT_STEP_RESPOND, HANDLE_CERTIFICATE_POOL_RETIREMENT_STEP_INVALID, }; - void signTx_handleCertificatePoolRetirement_ui_runStep(); // ============================== WITHDRAWALS ============================== diff --git a/src/textUtils.c b/src/textUtils.c index 1eac4c93..9f0e42e2 100644 --- a/src/textUtils.c +++ b/src/textUtils.c @@ -237,14 +237,6 @@ size_t str_formatValidityBoundary(uint64_t slotNumber, char* out, size_t outSize return strlen(out); } -// returns length of the resulting string -size_t str_formatMetadata(const uint8_t* metadataHash, size_t metadataHashSize, char* out, size_t outSize) -{ - ASSERT(outSize < BUFFER_SIZE_PARANOIA); - - return encode_hex(metadataHash, metadataHashSize, out, outSize); -} - // check if a non-null-terminated buffer contains printable ASCII between 33 and 126 (inclusive) bool str_isPrintableAsciiWithoutSpaces(const uint8_t* buffer, size_t bufferSize) { diff --git a/src/textUtils.h b/src/textUtils.h index 971f4807..1ce68ba2 100644 --- a/src/textUtils.h +++ b/src/textUtils.h @@ -43,8 +43,6 @@ void str_traceInt64(int64_t number); size_t str_formatValidityBoundary(uint64_t slotNumber, char* out, size_t outSize); -size_t str_formatMetadata(const uint8_t* metadataHash, size_t metadataHashSize, char* out, size_t outSize); - bool str_isPrintableAsciiWithoutSpaces(const uint8_t* buffer, size_t bufferSize); bool str_isPrintableAsciiWithSpaces(const uint8_t* buffer, size_t bufferSize); diff --git a/src/txHashBuilder.c b/src/txHashBuilder.c index 9b2e3001..1236e57b 100644 --- a/src/txHashBuilder.c +++ b/src/txHashBuilder.c @@ -751,7 +751,7 @@ static void _initNewCertificate(tx_hash_builder_t* builder) builder->remainingCertificates--; } -static const uint8_t* getCredentialHashBuffer(const credential_t* credential) +static const uint8_t* _getCredentialHashBuffer(const credential_t* credential) { switch (credential->type) { case CREDENTIAL_KEY_HASH: @@ -763,7 +763,7 @@ static const uint8_t* getCredentialHashBuffer(const credential_t* credential) } } -static size_t getCredentialHashSize(const credential_t* credential) +static size_t _getCredentialHashSize(const credential_t* credential) { switch (credential->type) { case CREDENTIAL_KEY_HASH: @@ -775,8 +775,25 @@ static size_t getCredentialHashSize(const credential_t* credential) } } +static void _appendCredential( + tx_hash_builder_t* builder, + const credential_t* credential +) +{ + BUILDER_APPEND_CBOR(CBOR_TYPE_ARRAY, 2); + { + BUILDER_APPEND_CBOR(CBOR_TYPE_UNSIGNED, credential->type); + } + { + const size_t size = _getCredentialHashSize(credential); + BUILDER_APPEND_CBOR(CBOR_TYPE_BYTES, size); + BUILDER_APPEND_DATA(_getCredentialHashBuffer(credential), size); + } +} + // stake key certificate registration or deregistration -void txHashBuilder_addCertificate_stakingHash( +// will be deprecated after Conway +void txHashBuilder_addCertificate_stakingOld( tx_hash_builder_t* builder, const certificate_type_t certificateType, const credential_t* stakeCredential @@ -800,15 +817,43 @@ void txHashBuilder_addCertificate_stakingHash( BUILDER_APPEND_CBOR(CBOR_TYPE_UNSIGNED, certificateType); } { - BUILDER_APPEND_CBOR(CBOR_TYPE_ARRAY, 2); - { - BUILDER_APPEND_CBOR(CBOR_TYPE_UNSIGNED, stakeCredential->type); - } - { - const size_t size = getCredentialHashSize(stakeCredential); - BUILDER_APPEND_CBOR(CBOR_TYPE_BYTES, size); - BUILDER_APPEND_DATA(getCredentialHashBuffer(stakeCredential), size); - } + _appendCredential(builder, stakeCredential); + } + } +} + +// stake key certificate registration or deregistration +// exists since Conway +void txHashBuilder_addCertificate_staking( + tx_hash_builder_t* builder, + const certificate_type_t certificateType, + const credential_t* stakeCredential, + uint64_t coin +) +{ + _initNewCertificate(builder); + + ASSERT((certificateType == CERTIFICATE_TYPE_STAKE_REG) + || (certificateType == CERTIFICATE_TYPE_STAKE_UNREG)); + + // Array(3)[ + // Unsigned[certificateType] + // Array(2)[ + // Unsigned[0] + // Bytes[stakingKeyHash] + // ] + // Unsigned[coin] + // ] + { + BUILDER_APPEND_CBOR(CBOR_TYPE_ARRAY, 3); + { + BUILDER_APPEND_CBOR(CBOR_TYPE_UNSIGNED, certificateType); + } + { + _appendCredential(builder, stakeCredential); + } + { + BUILDER_APPEND_CBOR(CBOR_TYPE_UNSIGNED, coin); } } } @@ -837,15 +882,7 @@ void txHashBuilder_addCertificate_delegation( BUILDER_APPEND_CBOR(CBOR_TYPE_UNSIGNED, 2); } { - BUILDER_APPEND_CBOR(CBOR_TYPE_ARRAY, 2); - { - BUILDER_APPEND_CBOR(CBOR_TYPE_UNSIGNED, stakeCredential->type); - } - { - const size_t size = getCredentialHashSize(stakeCredential); - BUILDER_APPEND_CBOR(CBOR_TYPE_BYTES, size); - BUILDER_APPEND_DATA(getCredentialHashBuffer(stakeCredential), size); - } + _appendCredential(builder, stakeCredential); } { BUILDER_APPEND_CBOR(CBOR_TYPE_BYTES, poolKeyHashSize); @@ -854,6 +891,250 @@ void txHashBuilder_addCertificate_delegation( } } +void txHashBuilder_addCertificate_voteDeleg( + tx_hash_builder_t* builder, + const credential_t* stakeCredential, + const drep_t* drep +) +{ + _initNewCertificate(builder); + + // Array(3)[ + // Unsigned[9] + // Array(2)[ + // Unsigned[0] + // Bytes[stakingKeyHash] + // ] + // Array(1 or 2)[ + // Unsigned[drep_type] + // ?Bytes[key/script hash] // optional + // ] + // ] + { + BUILDER_APPEND_CBOR(CBOR_TYPE_ARRAY, 3); + { + BUILDER_APPEND_CBOR(CBOR_TYPE_UNSIGNED, 9); + } + { + _appendCredential(builder, stakeCredential); + } + { + // DRep + switch (drep->type) { + case DREP_KEY_HASH: { + BUILDER_APPEND_CBOR(CBOR_TYPE_ARRAY, 2); + BUILDER_APPEND_CBOR(CBOR_TYPE_UNSIGNED, DREP_KEY_HASH); + BUILDER_APPEND_CBOR(CBOR_TYPE_BYTES, SIZEOF(drep->keyHash)); + BUILDER_APPEND_DATA(drep->keyHash, SIZEOF(drep->keyHash)); + break; + } + case DREP_SCRIPT_HASH: { + BUILDER_APPEND_CBOR(CBOR_TYPE_ARRAY, 2); + BUILDER_APPEND_CBOR(CBOR_TYPE_UNSIGNED, DREP_SCRIPT_HASH); + BUILDER_APPEND_CBOR(CBOR_TYPE_BYTES, SIZEOF(drep->scriptHash)); + BUILDER_APPEND_DATA(drep->scriptHash, SIZEOF(drep->scriptHash)); + break; + } + case DREP_ALWAYS_ABSTAIN: + case DREP_ALWAYS_NO_CONFIDENCE: { + BUILDER_APPEND_CBOR(CBOR_TYPE_ARRAY, 1); + BUILDER_APPEND_CBOR(CBOR_TYPE_UNSIGNED, drep->type); + break; + } + default: + ASSERT(false); + } + } + } +} + +void txHashBuilder_addCertificate_committeeAuth( + tx_hash_builder_t* builder, + const credential_t* coldCredential, + const credential_t* hotCredential +) +{ + _initNewCertificate(builder); + + // Array(3)[ + // Unsigned[14] + // Array(2)[ + // Unsigned[0 or 1] + // Bytes[stakingKeyHash] + // ] + // Array(2)[ + // Unsigned[0 or 1] + // Bytes[stakingKeyHash] + // ] + // ] + { + BUILDER_APPEND_CBOR(CBOR_TYPE_ARRAY, 3); + { + BUILDER_APPEND_CBOR(CBOR_TYPE_UNSIGNED, 14); + } + { + _appendCredential(builder, coldCredential); + } + { + _appendCredential(builder, hotCredential); + } + } +} + +static void _appendAnchor( + tx_hash_builder_t* builder, + const anchor_t* anchor +) +{ + if (anchor->isIncluded) { + BUILDER_APPEND_CBOR(CBOR_TYPE_ARRAY, 2); + { + BUILDER_APPEND_CBOR(CBOR_TYPE_TEXT, anchor->urlLength); + BUILDER_APPEND_DATA(anchor->url, anchor->urlLength); + } + { + BUILDER_APPEND_CBOR(CBOR_TYPE_BYTES, SIZEOF(anchor->hash)); + BUILDER_APPEND_DATA(anchor->hash, SIZEOF(anchor->hash)); + } + } else { + BUILDER_APPEND_CBOR(CBOR_TYPE_NULL, 0); + } +} + +void txHashBuilder_addCertificate_committeeResign( + tx_hash_builder_t* builder, + const credential_t* coldCredential, + const anchor_t* anchor +) +{ + _initNewCertificate(builder); + + // Array(3)[ + // Unsigned[15] + // Array(2)[ + // Unsigned[0 or 1] + // Bytes[stakingKeyHash] + // ] + // Array(2)[ + // Tstr[url] + // Bytes[32] + // ] + // ] + { + BUILDER_APPEND_CBOR(CBOR_TYPE_ARRAY, 3); + { + BUILDER_APPEND_CBOR(CBOR_TYPE_UNSIGNED, 15); + } + { + _appendCredential(builder, coldCredential); + } + { + _appendAnchor(builder, anchor); + } + } +} + +void txHashBuilder_addCertificate_dRepReg( + tx_hash_builder_t* builder, + const credential_t* drepCredential, + uint64_t coin, + const anchor_t* anchor +) +{ + _initNewCertificate(builder); + + // Array(4)[ + // Unsigned[16] + // Array(2)[ + // Unsigned[0/1] + // Bytes[key/script hash] + // ] + // Unsigned[coin] + // Array(2)[ + // Tstr[url] + // Bytes[32] + // ] + // ] + { + BUILDER_APPEND_CBOR(CBOR_TYPE_ARRAY, 3); + { + BUILDER_APPEND_CBOR(CBOR_TYPE_UNSIGNED, 16); + } + { + _appendCredential(builder, drepCredential); + } + { + BUILDER_APPEND_CBOR(CBOR_TYPE_UNSIGNED, coin); + } + { + _appendAnchor(builder, anchor); + } + } +} + +void txHashBuilder_addCertificate_dRepUnreg( + tx_hash_builder_t* builder, + const credential_t* drepCredential, + uint64_t coin +) +{ + _initNewCertificate(builder); + + // Array(3)[ + // Unsigned[16] + // Array(2)[ + // Unsigned[0/1] + // Bytes[key/script hash] + // ] + // Unsigned[coin] + // ] + { + BUILDER_APPEND_CBOR(CBOR_TYPE_ARRAY, 3); + { + BUILDER_APPEND_CBOR(CBOR_TYPE_UNSIGNED, 17); + } + { + _appendCredential(builder, drepCredential); + } + { + BUILDER_APPEND_CBOR(CBOR_TYPE_UNSIGNED, coin); + } + } +} + +void txHashBuilder_addCertificate_dRepUpdate( + tx_hash_builder_t* builder, + const credential_t* drepCredential, + const anchor_t* anchor +) +{ + _initNewCertificate(builder); + + // Array(3)[ + // Unsigned[16] + // Array(2)[ + // Unsigned[0/1] + // Bytes[key/script hash] + // ] + // Array(2)[ + // Tstr[url] + // Bytes[32] + // ] + // ] + { + BUILDER_APPEND_CBOR(CBOR_TYPE_ARRAY, 3); + { + BUILDER_APPEND_CBOR(CBOR_TYPE_UNSIGNED, 16); + } + { + _appendCredential(builder, drepCredential); + } + { + _appendAnchor(builder, anchor); + } + } +} + #ifdef APP_FEATURE_POOL_RETIREMENT void txHashBuilder_addCertificate_poolRetirement( diff --git a/src/txHashBuilder.h b/src/txHashBuilder.h index 075ae583..7cb6e3c2 100644 --- a/src/txHashBuilder.h +++ b/src/txHashBuilder.h @@ -18,6 +18,28 @@ typedef struct { }; } credential_t; +typedef enum { + DREP_KEY_HASH = 0, + DREP_SCRIPT_HASH = 1, + DREP_ALWAYS_ABSTAIN = 2, + DREP_ALWAYS_NO_CONFIDENCE = 3, +} drep_type_t; + +typedef struct { + drep_type_t type; + union { + uint8_t keyHash[ADDRESS_KEY_HASH_LENGTH]; + uint8_t scriptHash[SCRIPT_HASH_LENGTH]; + }; +} drep_t; + +typedef struct { + bool isIncluded; + uint8_t url[ANCHOR_URL_LENGTH_MAX]; + size_t urlLength; + uint8_t hash[ANCHOR_HASH_LENGTH]; +} anchor_t; + typedef enum { ARRAY_LEGACY = 0, // legacy_transaction_output MAP_BABBAGE = 1 // post_alonzo_transaction_output @@ -263,11 +285,17 @@ void txHashBuilder_addTtl(tx_hash_builder_t* builder, uint64_t ttl); void txHashBuilder_enterCertificates(tx_hash_builder_t* builder); -void txHashBuilder_addCertificate_stakingHash( +void txHashBuilder_addCertificate_stakingOld( tx_hash_builder_t* builder, const certificate_type_t certificateType, const credential_t* stakingCredential ); +void txHashBuilder_addCertificate_staking( + tx_hash_builder_t* builder, + const certificate_type_t certificateType, + const credential_t* stakeCredential, + uint64_t coin +); void txHashBuilder_addCertificate_delegation( tx_hash_builder_t* builder, @@ -275,6 +303,43 @@ void txHashBuilder_addCertificate_delegation( const uint8_t* poolKeyHash, size_t poolKeyHashSize ); +void txHashBuilder_addCertificate_voteDeleg( + tx_hash_builder_t* builder, + const credential_t* stakeCredential, + const drep_t* drep +); + +void txHashBuilder_addCertificate_committeeAuth( + tx_hash_builder_t* builder, + const credential_t* coldCredential, + const credential_t* hotCredential +); + +void txHashBuilder_addCertificate_committeeResign( + tx_hash_builder_t* builder, + const credential_t* coldCredential, + const anchor_t* anchor +); + +void txHashBuilder_addCertificate_dRepReg( + tx_hash_builder_t* builder, + const credential_t* drepCredential, + uint64_t coin, + const anchor_t* anchor +); + +void txHashBuilder_addCertificate_dRepUnreg( + tx_hash_builder_t* builder, + const credential_t* drepCredential, + uint64_t coin +); + +void txHashBuilder_addCertificate_dRepUpdate( + tx_hash_builder_t* builder, + const credential_t* drepCredential, + const anchor_t* anchor +); + #ifdef APP_FEATURE_POOL_RETIREMENT void txHashBuilder_addCertificate_poolRetirement( diff --git a/src/txHashBuilder_test.c b/src/txHashBuilder_test.c index 9fbb25df..6db3f290 100644 --- a/src/txHashBuilder_test.c +++ b/src/txHashBuilder_test.c @@ -401,7 +401,7 @@ static void addCertificates(tx_hash_builder_t* builder) .type = CREDENTIAL_KEY_HASH }; decode_hex(PTR_PIC(it->stakingKeyHash), credential.keyHash, SIZEOF(credential.keyHash)); - txHashBuilder_addCertificate_stakingHash( + txHashBuilder_addCertificate_stakingOld( builder, CERTIFICATE_TYPE_STAKE_REGISTRATION, &credential @@ -413,7 +413,7 @@ static void addCertificates(tx_hash_builder_t* builder) .type = CREDENTIAL_KEY_HASH }; decode_hex(PTR_PIC(it->stakingKeyHash), credential.keyHash, SIZEOF(credential.keyHash)); - txHashBuilder_addCertificate_stakingHash( + txHashBuilder_addCertificate_stakingOld( builder, CERTIFICATE_TYPE_STAKE_DEREGISTRATION, &credential