Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Murisi/replay protection #12

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion app/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ endif

APP_LOAD_PARAMS = --curve ed25519 $(COMMON_LOAD_PARAMS) --path $(APPPATH)

NANOS_STACK_SIZE := 2850
NANOS_STACK_SIZE := 2640
include $(CURDIR)/../deps/ledger-zxlib/makefiles/Makefile.devices

$(info TARGET_NAME = [$(TARGET_NAME)])
Expand Down
2 changes: 1 addition & 1 deletion app/src/common/actions.h
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ __Z_INLINE void app_sign() {
set_code(G_io_apdu_buffer, 0, APDU_CODE_SIGN_VERIFY_ERROR);
io_exchange(CHANNEL_APDU | IO_RETURN_AFTER_TX, 2);
} else {
const uint16_t responseLen = PK_LEN_25519_PLUS_TAG + 2 * SALT_LEN + 2 * SIG_LEN_25519_PLUS_TAG;
const uint16_t responseLen = PK_LEN_25519_PLUS_TAG + 2 * SALT_LEN + 2 * SIG_LEN_25519_PLUS_TAG + 2 + 10;
set_code(G_io_apdu_buffer, responseLen, APDU_CODE_OK);
io_exchange(CHANNEL_APDU | IO_RETURN_AFTER_TX, responseLen + 2);
}
Expand Down
140 changes: 111 additions & 29 deletions app/src/crypto.c
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
#define SIGN_PREFIX_SIZE 11u
#define SIGN_PREHASH_SIZE (SIGN_PREFIX_SIZE + CX_SHA256_SIZE)

#define MAX_SIGNATURE_HASHES 6
#define MAX_SIGNATURE_HASHES 10

static zxerr_t crypto_extractPublicKey_ed25519(uint8_t *pubKey, uint16_t pubKeyLen) {
if (pubKey == NULL || pubKeyLen < PK_LEN_25519) {
Expand Down Expand Up @@ -154,7 +154,21 @@ zxerr_t crypto_fillAddress(signing_key_type_e addressKind, uint8_t *buffer, uint
}


static zxerr_t crypto_hashHeader(const header_t *header, uint8_t *output, uint32_t outputLen) {
static zxerr_t crypto_hashFeeHeader(const header_t *header, uint8_t *output, uint32_t outputLen) {
if (header == NULL || output == NULL || outputLen < CX_SHA256_SIZE) {
return zxerr_invalid_crypto_settings;
}
cx_sha256_t sha256 = {0};
cx_sha256_init(&sha256);
const uint8_t discriminant = 0x07;
cx_sha256_update(&sha256, &discriminant, sizeof(discriminant));
cx_sha256_update(&sha256, header->ext_bytes.ptr, header->ext_bytes.len);
cx_sha256_final(&sha256, output);
return zxerr_ok;
}


static zxerr_t crypto_hashRawHeader(const header_t *header, uint8_t *output, uint32_t outputLen) {
if (header == NULL || output == NULL || outputLen < CX_SHA256_SIZE) {
return zxerr_invalid_crypto_settings;
}
Expand All @@ -163,10 +177,13 @@ static zxerr_t crypto_hashHeader(const header_t *header, uint8_t *output, uint32
const uint8_t discriminant = 0x07;
cx_sha256_update(&sha256, &discriminant, sizeof(discriminant));
cx_sha256_update(&sha256, header->bytes.ptr, header->bytes.len);
const uint8_t header_discriminant = 0x00;
cx_sha256_update(&sha256, &header_discriminant, sizeof(header_discriminant));
cx_sha256_final(&sha256, output);
return zxerr_ok;
}


zxerr_t crypto_hashSigSection(const signature_section_t *signature_section, const uint8_t *prefix, uint32_t prefixLen, uint8_t *output, uint32_t outputLen) {
if (signature_section == NULL || output == NULL || outputLen < CX_SHA256_SIZE) {
return zxerr_invalid_crypto_settings;
Expand All @@ -177,14 +194,20 @@ zxerr_t crypto_hashSigSection(const signature_section_t *signature_section, cons
if (prefix != NULL) {
cx_sha256_update(&sha256, prefix, prefixLen);
}
cx_sha256_update(&sha256, signature_section->salt.ptr, signature_section->salt.len);
cx_sha256_update(&sha256, (uint8_t*) &signature_section->hashes.hashesLen, 4);
cx_sha256_update(&sha256, signature_section->hashes.hashes.ptr, HASH_LEN * signature_section->hashes.hashesLen);
cx_sha256_update(&sha256, signature_section->pubKey.ptr, signature_section->pubKey.len);
cx_sha256_update(&sha256, (const uint8_t*) &signature_section->has_signature, 1);
if(signature_section->has_signature) {
cx_sha256_update(&sha256, signature_section->signature.ptr, signature_section->signature.len);
cx_sha256_update(&sha256, (uint8_t*) &signature_section->signerDiscriminant, 1);
switch (signature_section->signerDiscriminant) {
case PubKeys:
cx_sha256_update(&sha256, (uint8_t*) &signature_section->pubKeysLen, 4);
cx_sha256_update(&sha256, signature_section->pubKeys.ptr, PK_LEN_25519_PLUS_TAG * signature_section->pubKeysLen);
break;
case Address:
cx_sha256_update(&sha256, (uint8_t*) &signature_section->address.ptr, signature_section->address.len);
break;
}
cx_sha256_update(&sha256, (const uint8_t*) &signature_section->signaturesLen, 4);
cx_sha256_update(&sha256, signature_section->indexedSignatures.ptr, (1+SIG_LEN_25519_PLUS_TAG) * signature_section->signaturesLen);
cx_sha256_final(&sha256, output);
return zxerr_ok;
}
Expand All @@ -198,24 +221,29 @@ static zxerr_t crypto_addTxnHashes(const parser_tx_t *txObj, concatenated_hashes
switch (txObj->typeTx) {
case InitAccount:
MEMCPY(hashes->hashes.ptr + hashes->hashesLen * HASH_LEN, txObj->initAccount.vp_type_sechash.ptr, HASH_LEN);
hashes->indices.ptr[hashes->hashesLen] = txObj->initAccount.vp_type_secidx;
hashes->hashesLen++;
break;

case InitValidator:
MEMCPY(hashes->hashes.ptr + hashes->hashesLen * HASH_LEN, txObj->initValidator.vp_type_sechash.ptr, HASH_LEN);
hashes->indices.ptr[hashes->hashesLen] = txObj->initValidator.vp_type_secidx;
hashes->hashesLen++;
break;

case UpdateVP:
MEMCPY(hashes->hashes.ptr + hashes->hashesLen * HASH_LEN, txObj->updateVp.vp_type_sechash.ptr, HASH_LEN);
hashes->indices.ptr[hashes->hashesLen] = txObj->updateVp.vp_type_secidx;
hashes->hashesLen++;
break;

case InitProposal:
MEMCPY(hashes->hashes.ptr + hashes->hashesLen * HASH_LEN, txObj->initProposal.content_sechash.ptr, HASH_LEN);
hashes->indices.ptr[hashes->hashesLen] = txObj->initProposal.content_secidx;
hashes->hashesLen++;
if (txObj->initProposal.has_proposal_code) {
MEMCPY(hashes->hashes.ptr + hashes->hashesLen * HASH_LEN, txObj->initProposal.proposal_code_sechash.ptr, HASH_LEN);
hashes->indices.ptr[hashes->hashesLen] = txObj->initProposal.proposal_code_secidx;
hashes->hashesLen++;
}
break;
Expand All @@ -230,7 +258,7 @@ static zxerr_t crypto_addTxnHashes(const parser_tx_t *txObj, concatenated_hashes


zxerr_t crypto_sign(const parser_tx_t *txObj, uint8_t *output, uint16_t outputLen) {
const uint16_t minimumBufferSize = PK_LEN_25519_PLUS_TAG + 2 * SALT_LEN + 2 * SIG_LEN_25519_PLUS_TAG;
const uint16_t minimumBufferSize = PK_LEN_25519_PLUS_TAG + 2 * SALT_LEN + 2 * SIG_LEN_25519_PLUS_TAG + 2 + 10;
if (txObj ==NULL || output == NULL || outputLen < minimumBufferSize) {
return zxerr_unknown;
}
Expand All @@ -240,20 +268,20 @@ zxerr_t crypto_sign(const parser_tx_t *txObj, uint8_t *output, uint16_t outputLe

// Hashes: code, data, (initAcc | initVali | updateVP = 1 / initProp = 2), raw_signature, header ---> MaxHashes = 6
uint8_t hashes_buffer[MAX_SIGNATURE_HASHES * HASH_LEN] = {0};
uint8_t indices_buffer[MAX_SIGNATURE_HASHES] = {0};
concatenated_hashes_t section_hashes = {
.hashes.ptr = hashes_buffer,
.hashes.len = sizeof(hashes_buffer),
.indices.ptr = indices_buffer,
.indices.len = sizeof(indices_buffer),
.hashesLen = 0
};

const section_t *data = &txObj->transaction.sections.data;
const section_t *code = &txObj->transaction.sections.code;
uint8_t *codeHash = section_hashes.hashes.ptr;
uint8_t *dataHash = section_hashes.hashes.ptr + HASH_LEN;
// Concatenate the code and data section hashes
CHECK_ZXERR(crypto_hashCodeSection(code, codeHash, HASH_LEN))
CHECK_ZXERR(crypto_hashDataSection(data, dataHash, HASH_LEN))
section_hashes.hashesLen = 2;
uint8_t *rawHeaderHash = section_hashes.hashes.ptr;
section_hashes.indices.ptr[0] = 255;
// Concatenate the raw header hash
CHECK_ZXERR(crypto_hashRawHeader(&txObj->transaction.header, rawHeaderHash, HASH_LEN))
section_hashes.hashesLen = 1;

CHECK_ZXERR(crypto_addTxnHashes(txObj, &section_hashes))

Expand All @@ -265,39 +293,87 @@ zxerr_t crypto_sign(const parser_tx_t *txObj, uint8_t *output, uint16_t outputLe
signature_section_t signature_section = {
.salt = salt,
.hashes = section_hashes,
.pubKey = pubkey,
.has_signature = false,
.signature = {NULL, 0},
.signerDiscriminant = PubKeys,
.pubKeysLen = 0,
.pubKeys = pubkey,
.signaturesLen = 0,
.indexedSignatures = {NULL, 0},
};

// Hash the unsigned signature section
uint8_t *raw_signature_hash = section_hashes.hashes.ptr + (section_hashes.hashesLen * HASH_LEN);
CHECK_ZXERR(crypto_hashSigSection(&signature_section, NULL, 0, raw_signature_hash, HASH_LEN))

// Sign over the hash of the unsigned signature section
uint8_t indexed_signatures_buffer[1+SIG_LEN_25519_PLUS_TAG] = {0};
CHECK_ZXERR(crypto_sign_ed25519(indexed_signatures_buffer + 2, ED25519_SIGNATURE_SIZE, raw_signature_hash, HASH_LEN))
uint8_t *raw = salt_buffer + SALT_LEN;
CHECK_ZXERR(crypto_sign_ed25519(raw + 1, ED25519_SIGNATURE_SIZE, raw_signature_hash, HASH_LEN))

MEMCPY(raw, indexed_signatures_buffer+1, SIG_LEN_25519_PLUS_TAG);
uint8_t raw_indices_len = section_hashes.hashesLen;
uint8_t raw_indices_buffer[MAX_SIGNATURE_HASHES] = {0};
MEMCPY(raw_indices_buffer, section_hashes.indices.ptr, section_hashes.hashesLen);

// ----------------------------------------------------------------------
// Start generating wrapper signature
// Affix the signature to make the signature section signed
signature_section.has_signature = true;
signature_section.signature.ptr = raw;
signature_section.signature.len = SIG_LEN_25519_PLUS_TAG;
signature_section.signaturesLen = 1;
signature_section.indexedSignatures.ptr = indexed_signatures_buffer;
signature_section.indexedSignatures.len = 1+SIG_LEN_25519_PLUS_TAG;
signature_section.pubKeysLen = 1;

// Compute the hash of the signed signature section and concatenate it
const uint8_t sig_sec_prefix = 0x03;
CHECK_ZXERR(crypto_hashSigSection(&signature_section, &sig_sec_prefix, 1, raw_signature_hash, HASH_LEN))
section_hashes.indices.ptr[section_hashes.hashesLen] = txObj->transaction.sections.sectionLen+1+0 /*signature_raw*/;
section_hashes.hashesLen++;
signature_section.hashes.hashesLen++;

/// Hash the header section
uint8_t *header_hash = section_hashes.hashes.ptr + (section_hashes.hashesLen * HASH_LEN);
CHECK_ZXERR(crypto_hashHeader(&txObj->transaction.header, header_hash, HASH_LEN))
section_hashes.hashesLen++;
signature_section.hashes.hashesLen++;
uint8_t *header_hash = section_hashes.hashes.ptr;
CHECK_ZXERR(crypto_hashFeeHeader(&txObj->transaction.header, header_hash, HASH_LEN))
section_hashes.indices.ptr[0] = 0;

// Hash the code and data sections
const section_t *data = &txObj->transaction.sections.data;
const section_t *code = &txObj->transaction.sections.code;
uint8_t *codeHash = section_hashes.hashes.ptr + (section_hashes.hashesLen * HASH_LEN);
uint8_t *dataHash = codeHash + HASH_LEN;
section_hashes.indices.ptr[section_hashes.hashesLen] = code->idx;
section_hashes.indices.ptr[section_hashes.hashesLen+1] = data->idx;
// Concatenate the code and data section hashes
CHECK_ZXERR(crypto_hashCodeSection(code, codeHash, HASH_LEN))
CHECK_ZXERR(crypto_hashDataSection(data, dataHash, HASH_LEN))
section_hashes.hashesLen += 2;
signature_section.hashes.hashesLen += 2;

// Hash the eligible signature sections
for (unsigned int i = 0; i < txObj->transaction.sections.signaturesLen; i++) {
const signature_section_t *prev_sig = &txObj->transaction.sections.signatures[i];
unsigned int j;
// Ensure that we recognize each hash that was signed over
for (j = 0; j < prev_sig->hashes.hashesLen; j++) {
unsigned int k;
// Check if we know the hash that was signed over
for (k = 0; k < signature_section.hashes.hashesLen; k++) {
if (!memcmp(prev_sig->hashes.hashes.ptr, signature_section.hashes.hashes.ptr, HASH_LEN)) {
break;
}
}
// If loop counter makes it to end, then this hash was not recognized
if (k == signature_section.hashes.hashesLen) break;
}
// If loop counter doesn't make it to end, then a hash was not recognized
if (j != prev_sig->hashes.hashesLen) continue;
// We sign over a signature if it signs over hashes that we recognize
uint8_t *prev_sig_hash = section_hashes.hashes.ptr + (section_hashes.hashesLen * HASH_LEN);
CHECK_ZXERR(crypto_hashSigSection(prev_sig, &sig_sec_prefix, 1, prev_sig_hash, HASH_LEN))
section_hashes.indices.ptr[section_hashes.hashesLen] = prev_sig->idx;
section_hashes.hashesLen++;
signature_section.hashes.hashesLen++;
}

signature_section.has_signature = false;
signature_section.signaturesLen = 0;
signature_section.pubKeysLen = 0;
// Hash the unsigned signature section into raw_sig_hash
uint8_t wrapper_sig_hash[HASH_LEN] = {0};
CHECK_ZXERR(crypto_hashSigSection(&signature_section, NULL, 0, wrapper_sig_hash, sizeof(wrapper_sig_hash)))
Expand All @@ -306,5 +382,11 @@ zxerr_t crypto_sign(const parser_tx_t *txObj, uint8_t *output, uint16_t outputLe
uint8_t *wrapper = raw + SALT_LEN + SIG_LEN_25519_PLUS_TAG;
CHECK_ZXERR(crypto_sign_ed25519(wrapper + 1, ED25519_SIGNATURE_SIZE, wrapper_sig_hash, sizeof(wrapper_sig_hash)))

uint8_t *indices = wrapper + SIG_LEN_25519_PLUS_TAG;
indices[0] = raw_indices_len;
MEMCPY(indices + 1, raw_indices_buffer, raw_indices_len);
indices += 1 + raw_indices_len;
indices[0] = section_hashes.hashesLen;
MEMCPY(indices + 1, section_hashes.indices.ptr, section_hashes.hashesLen);
return zxerr_ok;
}
43 changes: 36 additions & 7 deletions app/src/parser_impl.c
Original file line number Diff line number Diff line change
Expand Up @@ -54,19 +54,27 @@ parser_error_t getNumItems(const parser_context_t *ctx, uint8_t *numItems) {
break;

case InitAccount:
*numItems = (app_mode_expert() ? INIT_ACCOUNT_EXPERT_PARAMS : INIT_ACCOUNT_NORMAL_PARAMS);
{
const uint32_t pubkeys_num = ctx->tx_obj->initAccount.number_of_pubkeys;
const uint8_t expert_params_num = (uint8_t)(INIT_ACCOUNT_EXPERT_PARAMS + pubkeys_num);
const uint8_t normal_params_num = (uint8_t)(INIT_ACCOUNT_NORMAL_PARAMS + pubkeys_num);
*numItems = (app_mode_expert() ? expert_params_num : normal_params_num);
break;
}

case InitProposal:
*numItems = (app_mode_expert() ? INIT_PROPOSAL_EXPERT_PARAMS : INIT_PROPOSAL_NORMAL_PARAMS) + ctx->tx_obj->initProposal.has_id;
break;

case VoteProposal:
{
const uint8_t has_delegators = (ctx->tx_obj->voteProposal.number_of_delegations == 0)? 0 : 1;
const uint8_t num_councils = (ctx->tx_obj->voteProposal.number_of_councils > 0) ? (ctx->tx_obj->voteProposal.number_of_councils - 1) : 0;
//uint8_t has_yay_vote_details = ((ctx->tx_obj->voteProposal.vote_type_is_council) || (ctx->tx_obj->voteProposal.vote_type_is_eth_bridge)) ? 1 : 0;
*numItems = (app_mode_expert() ? VOTE_PROPOSAL_EXPERT_PARAMS : VOTE_PROPOSAL_NORMAL_PARAMS) + has_delegators + num_councils;
const uint32_t delegations_num = ctx->tx_obj->voteProposal.number_of_delegations;
const uint8_t normal_params_num = (uint8_t)(VOTE_PROPOSAL_NORMAL_PARAMS + delegations_num);
const uint8_t expert_params_num = (uint8_t)(VOTE_PROPOSAL_EXPERT_PARAMS + delegations_num);
*numItems = (app_mode_expert() ? expert_params_num : normal_params_num);
break;
}

case RevealPubkey:
*numItems = (app_mode_expert() ? REVEAL_PUBKEY_EXPERT_PARAMS : REVEAL_PUBKEY_NORMAL_PARAMS);
break;
Expand All @@ -80,11 +88,32 @@ parser_error_t getNumItems(const parser_context_t *ctx, uint8_t *numItems) {
break;

case InitValidator:
*numItems = (app_mode_expert() ? INIT_VALIDATOR_EXPERT_PARAMS : INIT_VALIDATOR_NORMAL_PARAMS);
{
const uint32_t account_keys_num = ctx->tx_obj->initValidator.number_of_account_keys;
const uint8_t normal_params_num = (uint8_t)(INIT_VALIDATOR_NORMAL_PARAMS + account_keys_num);
const uint8_t expert_params_num = (uint8_t)(INIT_VALIDATOR_EXPERT_PARAMS + account_keys_num);
*numItems = (app_mode_expert() ? expert_params_num : normal_params_num);
break;
}

case UpdateVP:
*numItems = (app_mode_expert() ? UPDATE_VP_EXPERT_PARAMS : UPDATE_VP_NORMAL_PARAMS);
{
const uint32_t pubkeys_num = ctx->tx_obj->updateVp.number_of_pubkeys;
const uint8_t has_threshold = ctx->tx_obj->updateVp.has_threshold;
const uint8_t normal_params_num =
(uint8_t)(UPDATE_VP_NORMAL_PARAMS + pubkeys_num + has_threshold);
const uint8_t expert_params_num =
(uint8_t)(UPDATE_VP_EXPERT_PARAMS + pubkeys_num + has_threshold);
*numItems = (app_mode_expert() ? expert_params_num : normal_params_num);
break;
}

case UnjailValidator:
*numItems = (app_mode_expert() ? UNJAIL_VALIDATOR_EXPERT_PARAMS : UNJAIL_VALIDATOR_NORMAL_PARAMS);
break;

case IBC:
*numItems = (app_mode_expert() ? IBC_EXPERT_PARAMS : IBC_NORMAL_PARAMS);
break;

default:
Expand Down
29 changes: 28 additions & 1 deletion app/src/parser_impl_common.c
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,14 @@
#define SIGN_MASK 0x80000000
#define SCALE_SHIFT 16

parser_error_t peekByte(const parser_context_t *ctx, uint8_t *byte) {
if (byte == NULL || ctx->offset >= ctx->bufferLen) {
return parser_unexpected_error;
}
*byte = *(ctx->buffer + ctx->offset);
return parser_ok;
}

parser_error_t readByte(parser_context_t *ctx, uint8_t *byte) {
if (byte == NULL || ctx->offset >= ctx->bufferLen) {
return parser_unexpected_error;
Expand Down Expand Up @@ -113,7 +121,6 @@ parser_error_t readDecimal(parser_context_t *ctx, serialized_decimal *value) {
return parser_ok;
}


parser_error_t readBytes(parser_context_t *ctx, const uint8_t **output, uint16_t outputLen) {
if (ctx->offset + outputLen > ctx->bufferLen) {
return parser_unexpected_buffer_end;
Expand All @@ -124,6 +131,26 @@ parser_error_t readBytes(parser_context_t *ctx, const uint8_t **output, uint16_t
return parser_ok;
}

parser_error_t readBytesBuf(parser_context_t *ctx, bytes_t *buf, uint16_t num_bytes) {
if (ctx->offset + num_bytes > ctx->bufferLen) {
return parser_unexpected_buffer_end;
}

buf->ptr = ctx->buffer + ctx->offset;
buf->len = num_bytes;
ctx->offset += num_bytes;
return parser_ok;
}

parser_error_t appendBytesBuf(parser_context_t *ctx, bytes_t *buf, uint16_t num_bytes) {
if (ctx->offset + num_bytes > ctx->bufferLen) {
return parser_unexpected_buffer_end;
}
buf->len += num_bytes;
ctx->offset += num_bytes;
return parser_ok;
}

parser_error_t readFieldSize(parser_context_t *ctx, uint32_t *size) {
uint8_t consumed = 0;
uint64_t tmpSize = 0;
Expand Down
Loading
Loading