Skip to content

Commit

Permalink
nova initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
shufps committed Feb 9, 2024
1 parent 24a710e commit 9bebb7d
Show file tree
Hide file tree
Showing 12 changed files with 115 additions and 57 deletions.
39 changes: 36 additions & 3 deletions src/api.c
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
#include "abstraction.h"

#include "iota/constants.h"
#include "iota/blindsigning_stardust.h"
#include "iota/blindsigning.h"
#include "iota/signing.h"

#include "ui/nano/flow_user_confirm_transaction.h"
Expand Down Expand Up @@ -44,12 +44,16 @@ void api_initialize(APP_MODE_TYPE app_mode, uint32_t account_index)
// 0x80: unused (formerly IOTA + Chrysalis Testnet)
// 0x01: (107a) IOTA + Stardust
// 0x81: (1) IOTA + Stardust Testnet
// 0x04: (107a) IOTA + Nova
// 0x84: (1) IOTA + Nova Testnet

// Shimmer App
// 0x02: (107a) Shimmer Claiming (from IOTA)
// 0x82: (1) Shimmer Claiming (from IOTA) (Testnet)
// 0x03: (107b) Shimmer (default)
// 0x83: (1) Shimmer Testnet
// 0x05: (107a) Shimmer + Nova
// 0x85: (1) Shimmer + Nova Testnet

switch (app_mode & 0x7f) {
#if defined(APP_IOTA)
Expand All @@ -59,6 +63,12 @@ void api_initialize(APP_MODE_TYPE app_mode, uint32_t account_index)
api.protocol = PROTOCOL_STARDUST;
api.coin = COIN_IOTA;
break;
case APP_MODE_IOTA_NOVA:
// iota
api.bip32_path[BIP32_COIN_INDEX] = BIP32_COIN_IOTA;
api.protocol = PROTOCOL_NOVA;
api.coin = COIN_IOTA;
break;
#elif defined(APP_SHIMMER)
case APP_MODE_SHIMMER_CLAIMING:
// iota
Expand All @@ -72,6 +82,12 @@ void api_initialize(APP_MODE_TYPE app_mode, uint32_t account_index)
api.protocol = PROTOCOL_STARDUST;
api.coin = COIN_SHIMMER;
break;
case APP_MODE_SHIMMER_NOVA:
// shimmer
api.bip32_path[BIP32_COIN_INDEX] = BIP32_COIN_SHIMMER;
api.protocol = PROTOCOL_NOVA;
api.coin = COIN_SHIMMER;
break;
#else
#error unknown app
#endif
Expand Down Expand Up @@ -389,6 +405,11 @@ uint32_t api_generate_public_key(uint8_t show_on_screen, const uint8_t *data,
// maybe we need it ...
UNUSED(show_on_screen);

// only allow in nova protocol
if (api.protocol != PROTOCOL_NOVA) {
THROW(SW_COMMAND_NOT_ALLOWED);
}

// don't allow command if an interactive flow already is running
if (api.flow_locked) {
THROW(SW_COMMAND_NOT_ALLOWED);
Expand Down Expand Up @@ -526,7 +547,7 @@ uint32_t api_prepare_signing(uint8_t has_remainder, const uint8_t *data,
return 0;
}

uint32_t api_prepare_blindsigning()
uint32_t api_prepare_blindsigning(uint8_t num_hashes)
{
// when calling validation the buffer still is marked as empty
if (api.data.type != EMPTY) {
Expand All @@ -550,13 +571,25 @@ uint32_t api_prepare_blindsigning()
// set flag for blindsigning
api.essence.blindsigning = 1;

// multiple of 32byte chunks
uint16_t signing_input_len = (uint16_t)num_hashes << 5;

if ((api.protocol == PROTOCOL_STARDUST &&
signing_input_len != BLAKE2B_SIZE_BYTES) ||
(api.protocol == PROTOCOL_NOVA &&
(signing_input_len != SIGNING_INPUT_NOVE_32BYTE ||
signing_input_len != SIGNING_INPUT_NOVE_64BYTE))) {
THROW(SW_INCORRECT_P1P2);
}


// we allow to prepare without blindsigning enabled but the user will only
// get an error message that blindsigning is not enabled on the Nano when
// trying to sign what is the most consistent behaviour because the outcome
// is the same as rejecting the signing (the flow only has a reject button
// in this case and accepting is not possible) and we don't have to cope
// with additional errors.
if (!parse_and_validate_blindsigning(&api)) {
if (!parse_and_validate_blindsigning(&api, signing_input_len)) {
THROW(SW_COMMAND_INVALID_DATA);
}

Expand Down
13 changes: 8 additions & 5 deletions src/api.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,12 @@ typedef enum {
typedef enum {
APP_MODE_IOTA_STARDUST = 1,
APP_MODE_SHIMMER_CLAIMING = 2,
APP_MODE_SHIMMER = 3
APP_MODE_SHIMMER = 3,
APP_MODE_IOTA_NOVA = 4,
APP_MODE_SHIMMER_NOVA = 5,
} APP_MODE_TYPE;

typedef enum { PROTOCOL_STARDUST = 1 } PROTOCOL_TYPE;
typedef enum { PROTOCOL_STARDUST = 1, PROTOCOL_NOVA = 2 } PROTOCOL_TYPE;

typedef enum { COIN_IOTA = 0, COIN_SHIMMER = 1 } COIN_TYPE;

Expand Down Expand Up @@ -203,8 +205,9 @@ typedef struct {
// flag that signals that it's a sweeping transaction
uint8_t is_internal_transfer;

// hash of the essence
uint8_t hash[BLAKE2B_SIZE_BYTES];
// signing input (hash(s))
uint8_t signing_input[SIGNING_INPUT_MAX_BYTES];
uint16_t signing_input_len;
} ESSENCE;

typedef struct {
Expand Down Expand Up @@ -285,7 +288,7 @@ uint32_t api_clear_data_buffer(void);
uint32_t api_prepare_signing(uint8_t has_remainder, const uint8_t *data,
uint32_t len);

uint32_t api_prepare_blindsigning(void);
uint32_t api_prepare_blindsigning(uint8_t num_hashes);

uint32_t api_user_confirm_essence(void);

Expand Down
47 changes: 24 additions & 23 deletions src/iota/abstraction.c
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,10 @@ const uint8_t *get_output_address_ptr(const API_CTX *api, uint8_t index)
ret = &tmp[index].address_type;
break;
}
// not supported yet
case PROTOCOL_NOVA: {
THROW(SW_UNKNOWN);
}
default:
THROW(SW_UNKNOWN);
break;
Expand All @@ -51,6 +55,10 @@ uint64_t get_output_amount(const API_CTX *api, uint8_t index)
memcpy(&amount, &tmp[index].amount, sizeof(uint64_t));
break;
}
// not supported yet
case PROTOCOL_NOVA: {
THROW(SW_UNKNOWN);
}
default:
THROW(SW_UNKNOWN);
break;
Expand All @@ -61,28 +69,26 @@ uint64_t get_output_amount(const API_CTX *api, uint8_t index)
uint8_t address_encode_bech32(const API_CTX *api, const uint8_t *addr_with_type,
char *bech32, uint32_t bech32_max_length)
{
const char *hrp;

switch (api->coin) {
case COIN_IOTA: {
MUST(address_encode_bech32_hrp(
addr_with_type, bech32, bech32_max_length,
(api->app_mode & 0x80) ? COIN_HRP_IOTA_TESTNET : COIN_HRP_IOTA,
strlen(COIN_HRP_IOTA))); // strlen valid because HRP has the same
// length in testnet
hrp = (api->app_mode & 0x80) ? COIN_HRP_IOTA_TESTNET : COIN_HRP_IOTA;
break;
}
case COIN_SHIMMER: {
MUST(address_encode_bech32_hrp(
addr_with_type, bech32, bech32_max_length,
(api->app_mode & 0x80) ? COIN_HRP_SHIMMER_TESTNET
: COIN_HRP_SHIMMER,
strlen(COIN_HRP_SHIMMER))); // strlen valid because HRP has the same
// length in testnet
hrp = (api->app_mode & 0x80) ? COIN_HRP_SHIMMER_TESTNET
: COIN_HRP_SHIMMER;
break;
}
default:
THROW(SW_UNKNOWN);
break;
}

MUST(address_encode_bech32_hrp(addr_with_type, bech32, bech32_max_length,
hrp, strlen(hrp)));

return 1;
}

Expand All @@ -94,6 +100,10 @@ uint8_t essence_parse_and_validate(API_CTX *api)
MUST(essence_parse_and_validate_stardust(api));
break;
}
// not supported yet
case PROTOCOL_NOVA: {
THROW(SW_UNKNOWN);
}
default:
THROW(SW_UNKNOWN);
break;
Expand All @@ -108,17 +118,8 @@ uint8_t get_amount(const API_CTX *api, int index, char *dst, size_t dst_len)
// amount > 0 enforced by validation
MUST(amount = get_output_amount(api, index));

switch (api->coin) {
case COIN_IOTA: {
format_value_full_decimals(dst, dst_len, amount);
break;
}
case COIN_SHIMMER: {
format_value_full_decimals(dst, dst_len, amount);
break;
}
default:
THROW(SW_UNKNOWN);
}
format_value_full_decimals(dst, dst_len, amount);

return 1;
}

1 change: 1 addition & 0 deletions src/iota/abstraction.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,4 @@ uint8_t address_encode_bech32(const API_CTX *api, const uint8_t *addr_with_type,
uint8_t essence_parse_and_validate(API_CTX *api);

uint8_t get_amount(const API_CTX *api, int index, char *dst, size_t dst_len);

25 changes: 17 additions & 8 deletions src/iota/blindsigning_stardust.c → src/iota/blindsigning.c
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
#include "api.h"

#include "macros.h"
#include "blindsigning_stardust.h"
#include "blindsigning.h"

#pragma GCC diagnostic error "-Wall"
#pragma GCC diagnostic error "-Wextra"
Expand All @@ -32,16 +32,18 @@ static inline uint8_t get_uint16(const uint8_t *data, uint32_t *idx,

// validate essence hash
static uint8_t
validate_essence_hash(const uint8_t *data, uint32_t *idx,
uint8_t essence_hash[ESSENCE_HASH_SIZE_BYTES])
validate_signing_input(const uint8_t *data, uint32_t *idx,
uint8_t signing_input[SIGNING_INPUT_MAX_BYTES],
uint16_t signing_input_len)
{
MUST(*idx + ESSENCE_HASH_SIZE_BYTES < API_BUFFER_SIZE_BYTES);
MUST(*idx + signing_input_len < API_BUFFER_SIZE_BYTES);

// not much we can validate here since an hash are arbitrary bytes
// copy from buffer to essence
memcpy(essence_hash, &data[*idx], ESSENCE_HASH_SIZE_BYTES);
memcpy(signing_input, &data[*idx], signing_input_len);

*idx = *idx + ESSENCE_HASH_SIZE_BYTES;

*idx = *idx + signing_input_len;
return 1;
}

Expand Down Expand Up @@ -69,12 +71,19 @@ validate_inputs_bip32(const uint8_t *data, uint32_t *idx, uint16_t inputs_count,
}


uint8_t parse_and_validate_blindsigning(API_CTX *api)
uint8_t parse_and_validate_blindsigning(API_CTX *api,
uint16_t signing_input_len)
{
uint32_t idx = 0;

// validate signing input length
MUST(signing_input_len == ESSENCE_HASH_SIZE_BYTES);

api->essence.signing_input_len = signing_input_len;

// parse data
MUST(validate_essence_hash(api->data.buffer, &idx, api->essence.hash));
MUST(validate_signing_input(api->data.buffer, &idx,
api->essence.signing_input, signing_input_len));

// save essence length
api->essence.length = idx;
Expand Down
5 changes: 5 additions & 0 deletions src/iota/blindsigning.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
#pragma once

#include "api.h"

uint8_t parse_and_validate_blindsigning(API_CTX *api, uint16_t signing_input_len);
5 changes: 0 additions & 5 deletions src/iota/blindsigning_stardust.h

This file was deleted.

4 changes: 4 additions & 0 deletions src/iota/constants.h
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,10 @@
#define BLAKE2B_SIZE_BYTES 32
#define ESSENCE_HASH_SIZE_BYTES 32

#define SIGNING_INPUT_MAX_BYTES 64
#define SIGNING_INPUT_NOVE_32BYTE 32
#define SIGNING_INPUT_NOVE_64BYTE 32

// address type of ED25519 addresses
#define ADDRESS_TYPE_ED25519 0

Expand Down
6 changes: 4 additions & 2 deletions src/iota/essence_stardust.c
Original file line number Diff line number Diff line change
Expand Up @@ -257,8 +257,10 @@ static uint8_t essence_hash(API_CTX *api)
cx_blake2b_t blake2b;
MUST(cx_blake2b_init_no_throw(&blake2b, BLAKE2B_SIZE_BYTES * 8) == CX_OK);
MUST(cx_hash_no_throw(&blake2b.header, CX_LAST, api->data.buffer,
api->essence.length, api->essence.hash,
ADDRESS_SIZE_BYTES) == CX_OK);
api->essence.length, api->essence.signing_input,
BLAKE2B_SIZE_BYTES) == CX_OK);

api->essence.signing_input_len = BLAKE2B_SIZE_BYTES;
#else
(void)api;
#endif
Expand Down
19 changes: 11 additions & 8 deletions src/iota/signing.c
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@
#pragma GCC diagnostic error "-Wmissing-prototypes"

static uint16_t sign_signature(SIGNATURE_BLOCK *pBlock,
const uint8_t *essence_hash,
const uint8_t *signing_input,
uint16_t signing_input_len,
uint32_t *bip32_signing_path,
API_INPUT_BIP32_INDEX *input_bip32_index)
{
Expand All @@ -32,7 +33,7 @@ static uint16_t sign_signature(SIGNATURE_BLOCK *pBlock,

MUST(bip32_derive_with_seed_eddsa_sign_hash_256(
HDW_ED25519_SLIP10, CX_CURVE_Ed25519, bip32_signing_path,
BIP32_PATH_LEN, CX_SHA512, essence_hash, BLAKE2B_SIZE_BYTES,
BIP32_PATH_LEN, CX_SHA512, signing_input, signing_input_len,
pBlock->signature, &signature_length, NULL, 0) == CX_OK);

MUST(signature_length == SIGNATURE_SIZE_BYTES);
Expand All @@ -51,14 +52,15 @@ static uint16_t sign_signature(SIGNATURE_BLOCK *pBlock,
}

static uint16_t sign_signature_unlock_block(
SIGNATURE_UNLOCK_BLOCK *pBlock, const uint8_t *essence_hash,
uint32_t *bip32_signing_path, API_INPUT_BIP32_INDEX *input_bip32_index)
SIGNATURE_UNLOCK_BLOCK *pBlock, const uint8_t *signing_input,
uint16_t signing_input_len, uint32_t *bip32_signing_path,
API_INPUT_BIP32_INDEX *input_bip32_index)
{
pBlock->signature_type = SIGNATURE_TYPE_ED25519; // ED25519
pBlock->unlock_type = UNLOCK_TYPE_SIGNATURE; // signature

MUST(sign_signature(&pBlock->signature, essence_hash, bip32_signing_path,
input_bip32_index));
MUST(sign_signature(&pBlock->signature, signing_input, signing_input_len,
bip32_signing_path, input_bip32_index));

return (uint16_t)sizeof(SIGNATURE_UNLOCK_BLOCK);
}
Expand Down Expand Up @@ -115,8 +117,9 @@ uint16_t sign(API_CTX *api, uint8_t *output, uint32_t signature_index)
// 0xff if not a reference index block
if (reference_index == 0xff) {
signature_size = sign_signature_unlock_block(
(SIGNATURE_UNLOCK_BLOCK *)output, api->essence.hash,
api->bip32_path, &input_bip32_index);
(SIGNATURE_UNLOCK_BLOCK *)output, api->essence.signing_input,
api->essence.signing_input_len, api->bip32_path,
&input_bip32_index);
}
else {
signature_size = sign_reference_unlock_block(
Expand Down
2 changes: 1 addition & 1 deletion src/iota_io.c
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ unsigned int iota_dispatch(const uint8_t ins, const uint8_t p1,
return api_prepare_signing(p2, input_data, len);

case INS_PREPARE_BLINDSIGNING:
return api_prepare_blindsigning();
return api_prepare_blindsigning(p1);

case INS_GENERATE_ADDRESS:
return api_generate_address(p1, input_data, len);
Expand Down
Loading

0 comments on commit 9bebb7d

Please sign in to comment.