diff --git a/app/src/apdu_handler.c b/app/src/apdu_handler.c index 02f42b3c..4d4286c1 100644 --- a/app/src/apdu_handler.c +++ b/app/src/apdu_handler.c @@ -177,8 +177,27 @@ __Z_INLINE void handleInitMASPTransfer(volatile uint32_t *flags, } +__Z_INLINE void handleExtractSpendDataMASPTransfer(volatile uint32_t *tx, uint32_t rx) { + zemu_log("----[handleExtractSpendDataMASPTransfer]\n"); -// Get the sapling full viewing key (ak, nk, ovk) + *tx = 0; + if (rx != APDU_MIN_LENGTH || G_io_apdu_buffer[OFFSET_DATA_LEN] != 0) { + THROW(APDU_CODE_COMMAND_NOT_ALLOWED); + } + // TODO implement the following + zxerr_t err = crypto_extract_spend_proof_key_and_rnd(G_io_apdu_buffer, IO_APDU_BUFFER_SIZE - 2); + view_tx_state(); + if (err == zxerr_ok) { + *tx = 128; //SPEND_EXTRACT_LEN + THROW(APDU_CODE_OK); + } else { + *tx = 0; + THROW(APDU_CODE_DATA_INVALID); + } + +} + +// Get the sapling full viewing key fvk = (ak, nk, ovk, dk) __Z_INLINE void handleGetKeyFVK(volatile uint32_t *flags, volatile uint32_t *tx, uint32_t rx) { zemu_log("----[handleGetKeyFVK]\n"); @@ -310,6 +329,7 @@ void handleApdu(volatile uint32_t *flags, volatile uint32_t *tx, uint32_t rx) { } // MASP transactions + // Get full viewing key fvk = (ak, nk, ovk, dk) case INS_GET_FVK: { zemu_log("----[INS_GET_FVK]\n"); CHECK_PIN_VALIDATED() @@ -317,6 +337,10 @@ void handleApdu(volatile uint32_t *flags, volatile uint32_t *tx, uint32_t rx) { break; } + // Step 1 in signing a MASP transaction: + // the ledger receives an initial transaction blob + // and stores relevant information in flash memory, + // so that it can check consistency with what it signs later. case INS_INIT_MASP_TRANSFER: { zemu_log("----[INS_INIT_MASP_TRANSFER]\n"); CHECK_PIN_VALIDATED() @@ -324,6 +348,17 @@ void handleApdu(volatile uint32_t *flags, volatile uint32_t *tx, uint32_t rx) { break; } + // If there are any spends (= shielded inputs) this is + // Step 2 in signing a MASP transaction: + // the clients requests information to build SpendDescriptions. + // In particular, the ledger should answer with + // a proof generating key (PGK) and randomness (rcv and alpha) + // This APDU is called for each spend. + case INS_EXTRACT_SPEND: { + CHECK_PIN_VALIDATED() + handleExtractSpendDataMASPTransfer(tx, rx); + break; + } #if defined(APP_TESTING) case INS_TEST: { handleTest(flags, tx, rx); diff --git a/app/src/coin.h b/app/src/coin.h index 881fcb5c..a6139640 100644 --- a/app/src/coin.h +++ b/app/src/coin.h @@ -100,10 +100,15 @@ typedef enum { #define INS_SIGN_WRAPPER 0x02 #define INS_GET_SHIELDED_ADDRESS 0x10 + #define INS_INIT_MASP_TRANSFER 0xe0 +#define INS_EXTRACT_SPEND 0xe1 + + #define INS_GET_IVK 0xf0 #define INS_GET_OVK 0xf1 #define INS_GET_NF 0xf2 +#define INS_GET_FVK 0xf3 #define INS_GET_SIGNATURE 0x0A diff --git a/app/src/crypto.c b/app/src/crypto.c index 64e8e347..3f87a2f0 100644 --- a/app/src/crypto.c +++ b/app/src/crypto.c @@ -449,7 +449,7 @@ zxerr_t crypto_fillSaplingSeed(uint8_t *sk) { } -// handleGetKeyFVK: return the full viewing key for a given path +// handleGetKeyFVK: return the full viewing key (fvk = (ak, nk, ovk, dk)) for a given path zxerr_t crypto_fvk_sapling(uint8_t *buffer, uint16_t bufferLen, uint32_t p, uint16_t *replyLen) { zemu_log_stack("crypto_fvk_sapling"); @@ -648,5 +648,68 @@ zxerr_t crypto_hash_messagebuffer(uint8_t *buffer, uint16_t bufferLen, const uin return zxerr_unknown; } cx_hash_sha256(txdata, txdataLen, buffer, CX_SHA256_SIZE); // SHA256 + return zxerr_ok; +} + +typedef struct { + union { + // STEP 1 + struct { + uint8_t zip32_seed[ZIP32_SEED_SIZE]; + uint8_t sk[ED25519_SK_SIZE]; + } step1; + + struct { + uint8_t ask[ASK_SIZE]; + uint8_t nsk[NSK_SIZE]; + } step2; + }; +} tmp_spendinfo_s; + +// handleExtractSpendDataMASPTransfer +zxerr_t crypto_extract_spend_proof_key_and_rnd(uint8_t *buffer, uint16_t bufferLen){ + // First check that there a still items on the list of spends + // for which spend data has not yet been extracted + if(!spendlist_more_to_extract()){ + return zxerr_unknown; + } + + // Ensure that the INS_INIT_MASP_TRANSFER has been called + // and did not error. + if(get_state() != STATE_PROCESSED_INPUTS){ + return zxerr_unknown; + } + + uint8_t *out = (uint8_t *) buffer; + MEMZERO(out, bufferLen); + + // Get the next item from the list of spends + const spend_item_t *next = spendlist_extract_next(); + if (next == NULL){ + return zxerr_unknown; + } + + tmp_spendinfo_s tmp = {0}; + + zxerr_t error = crypto_fillSaplingSeed(tmp.step1.zip32_seed); + CHECK_APP_CANARY() + if(error != zxerr_ok){ + MEMZERO(buffer, bufferLen); + return error; + } + + // Get ak and nsk (the child proof key) + get_child_proof_key(tmp.step1.zip32_seed, next->path, out, out + AK_SIZE); + CHECK_APP_CANARY() + + MEMZERO(&tmp, sizeof(tmp_spendinfo_s)); + + MEMCPY(out+AK_SIZE+NSK_SIZE, next->rcmvalue, RCM_SIZE); + MEMCPY(out+AK_SIZE+NSK_SIZE+RCM_SIZE, next->alpha,ALPHA_SIZE); + + if(!spendlist_more_to_extract()){ + set_state(STATE_PROCESSED_SPEND_EXTRACTIONS); + } + return zxerr_ok; } \ No newline at end of file diff --git a/app/src/crypto.h b/app/src/crypto.h index 7d103bd7..b36ae3e7 100644 --- a/app/src/crypto.h +++ b/app/src/crypto.h @@ -50,6 +50,7 @@ zxerr_t crypto_getSignature(uint8_t *output, uint16_t outputLen, uint8_t slot); zxerr_t crypto_fvk_sapling(uint8_t *buffer, uint16_t bufferLen, uint32_t p, uint16_t *replyLen); zxerr_t crypto_extracttx_sapling(uint8_t *buffer, uint16_t bufferLen, const uint8_t *txdata, const uint16_t txdatalen); zxerr_t crypto_hash_messagebuffer(uint8_t *buffer, uint16_t bufferLen, const uint8_t *txdata, uint16_t txdataLen); +zxerr_t crypto_extract_spend_proof_key_and_rnd(uint8_t *buffer, uint16_t bufferLen); #ifdef __cplusplus } diff --git a/app/src/nvdata.c b/app/src/nvdata.c index 4e4de3c9..355d2bb5 100644 --- a/app/src/nvdata.c +++ b/app/src/nvdata.c @@ -163,6 +163,28 @@ uint8_t spendlist_len() { return transaction_header.spendlist_len; } +spend_item_t *spendlist_retrieve_item(uint8_t i) { + if (transaction_header.spendlist_len < i) { + return NULL; + } else { + return (spend_item_t *) &N_spendlist.items[i]; + } +} + +spend_item_t *spendlist_extract_next() { + if (transaction_header.spendlist_len <= transaction_header.spenddata_extract_index) { + return NULL; + } else { + spend_item_t *result = (spend_item_t *) &N_spendlist.items[transaction_header.spenddata_extract_index]; + transaction_header.spenddata_extract_index += 1; + return result; + } +} + +uint8_t spendlist_more_to_extract() { + return transaction_header.spendlist_len > transaction_header.spenddata_extract_index; +} + zxerr_t outputlist_append_item(uint8_t *d, uint8_t *pkd, uint64_t v, uint8_t memotype, uint8_t *ovk, uint8_t *rcmv, uint8_t *rseed) { if (transaction_header.outputlist_len >= OUTPUT_LIST_SIZE) { diff --git a/app/src/nvdata.h b/app/src/nvdata.h index efe6392a..7e7ff9e4 100644 --- a/app/src/nvdata.h +++ b/app/src/nvdata.h @@ -121,6 +121,9 @@ zxerr_t t_outlist_append_item(uint8_t *addr, uint64_t v); // spend list nvdata functions zxerr_t spendlist_append_item(uint32_t p, uint64_t v, uint8_t *div, uint8_t *pkd, uint8_t *rcm, uint8_t *alpha); uint8_t spendlist_len(); +spend_item_t *spendlist_retrieve_item(uint8_t i); +spend_item_t *spendlist_extract_next(); +uint8_t spendlist_more_to_extract(); // shielded output nvdata functions zxerr_t outputlist_append_item(uint8_t *d, uint8_t *pkd, uint64_t v, uint8_t memotype, uint8_t *ovk, uint8_t *rcmv, diff --git a/app/src/sapling.c b/app/src/sapling.c index f1eeedc2..d9fa68db 100644 --- a/app/src/sapling.c +++ b/app/src/sapling.c @@ -633,7 +633,7 @@ void derive_dummy_ask_and_nsk(uint8_t *key_in, uint8_t *ask_out, uint8_t *nsk_ou MEMZERO(buffer, sizeof(buffer)); } -void get_fvk(uint8_t *seed, uint32_t pos, full_viewing_key_t* out){ +void get_all_keys(uint8_t *seed, uint32_t pos, all_keys_t* out){ uint32_t path[3] = {FIRSTVALUE, COIN_TYPE, pos}; uint8_t master_spending_key[64] = {0}; @@ -669,9 +669,9 @@ void get_fvk(uint8_t *seed, uint32_t pos, full_viewing_key_t* out){ little_endian_write_u32((c+(1<<31)), le_i, sizeof (le_i)); uint8_t *start_esk = expandedSpendingKey.ask; masp_blake2b_expand_vec_three(chain, sizeof (chain), - tmp_const, sizeof (tmp_const), + tmp_const, sizeof (tmp_const), start_esk, sizeof (expandedSpendingKey), - le_i, sizeof (le_i), tmp); + le_i, sizeof (le_i), tmp); } else { full_viewing_key_t fvk; ask_to_ak(expandedSpendingKey.ask, fvk.ak); @@ -688,9 +688,9 @@ void get_fvk(uint8_t *seed, uint32_t pos, full_viewing_key_t* out){ little_endian_write_u32(c, le_i, sizeof (le_i)); uint8_t *start_fvk = fvk.ak; masp_blake2b_expand_vec_three(chain, sizeof (chain), - tmp_const, sizeof (tmp_const), + tmp_const, sizeof (tmp_const), start_fvk, sizeof (fvk), - le_i, sizeof (le_i), tmp); + le_i, sizeof (le_i), tmp); } memcpy(key, tmp, 32); memcpy(chain, tmp + 32, 32); @@ -764,12 +764,31 @@ void get_fvk(uint8_t *seed, uint32_t pos, full_viewing_key_t* out){ memcpy(expandedSpendingKey.dk, buffer, DK_SIZE); MEMZERO(buffer, sizeof(buffer)); } + memcpy(out->ask, ask_bytes, ASK_SIZE); + memcpy(out->nsk, nsk_bytes, NSK_SIZE); ask_to_ak(ask_bytes, out->ak); nsk_to_nk(nsk_bytes, out->nk); memcpy(out->ovk, expandedSpendingKey.ovk, OVK_SIZE); memcpy(out->dk, expandedSpendingKey.dk, DK_SIZE); } +void get_fvk(uint8_t *seed, uint32_t pos, full_viewing_key_t* out){ + all_keys_t *keys = NULL; + get_all_keys(seed,pos,keys); + memcpy(out->ak, keys->ak, AK_SIZE); + memcpy(out->nk, keys->nk, NK_SIZE); + memcpy(out->ovk, keys->ovk, OVK_SIZE); + memcpy(out->dk, keys->dk, DK_SIZE); +} + + +void get_child_proof_key(uint8_t *seed, uint32_t pos, uint8_t* ak_out, uint8_t* nsk_out){ + all_keys_t *keys = NULL; + get_all_keys(seed,pos,keys); + memcpy(ak_out, keys->ak, AK_SIZE); + memcpy(nsk_out, keys->nsk, NSK_SIZE); +} + void get_expanded_spending_key_from_seed(uint8_t *seed, expanded_spending_key_t* out){ uint8_t master_spending_key[64] = {0}; diff --git a/app/src/sapling.h b/app/src/sapling.h index 57848715..286a3c74 100644 --- a/app/src/sapling.h +++ b/app/src/sapling.h @@ -7,7 +7,10 @@ extern "C" { #include // uint*_t #include "sapling_keys.h" - void get_fvk(uint8_t *seed, uint32_t pos, full_viewing_key_t* out); + void get_fvk(uint8_t *seed, uint32_t pos, full_viewing_key_t* out); + + // Gets keys required for computing the zk proofs for spend descriptions (ak and nsk) + void get_child_proof_key(uint8_t *seed, uint32_t pos, uint8_t* ak_out, uint8_t* nsk_out); // Exists but not needed out of sapling.c yet: // static void get_expanded_spending_key_from_seed(uint8_t *seed, expanded_spending_key_t* out); diff --git a/app/src/sapling_keys.h b/app/src/sapling_keys.h index 1850beec..b8d92a11 100644 --- a/app/src/sapling_keys.h +++ b/app/src/sapling_keys.h @@ -21,4 +21,13 @@ typedef struct { uint8_t nk[NK_SIZE]; // nullifier deriving key uint8_t ovk[OVK_SIZE]; // outgoing viewing key uint8_t dk[DK_SIZE]; // diversifier key -} full_viewing_key_t; \ No newline at end of file +} full_viewing_key_t; + +typedef struct { + uint8_t ak[AK_SIZE]; + uint8_t ask[ASK_SIZE]; + uint8_t nk[NK_SIZE]; // nullifier deriving key + uint8_t nsk[NSK_SIZE]; + uint8_t ovk[OVK_SIZE]; // outgoing viewing key + uint8_t dk[DK_SIZE]; // diversifier key +} all_keys_t; \ No newline at end of file