Implement Ledger-PKI

- Update src code to adapt to new API 'os_pki_verify'
- Support both Ledger-PKI and legacy method
This commit is contained in:
Charles-Edouard de la Vergne
2024-06-11 10:55:11 +02:00
parent 1ac75092da
commit 2008307c0c
8 changed files with 291 additions and 217 deletions

76
src/ledger_pki.c Normal file
View File

@@ -0,0 +1,76 @@
#include "apdu_constants.h"
#include "public_keys.h"
#ifdef HAVE_LEDGER_PKI
#include "os_pki.h"
#endif
#define KEY_USAGE_STR(x) \
(x == CERTIFICATE_PUBLIC_KEY_USAGE_GENUINE_CHECK ? "GENUINE_CHECK" \
: x == CERTIFICATE_PUBLIC_KEY_USAGE_EXCHANGE_PAYLOAD ? "EXCHANGE_PAYLOAD" \
: x == CERTIFICATE_PUBLIC_KEY_USAGE_NFT_METADATA ? "NFT_METADATA" \
: x == CERTIFICATE_PUBLIC_KEY_USAGE_TRUSTED_NAME ? "TRUSTED_NAME" \
: x == CERTIFICATE_PUBLIC_KEY_USAGE_BACKUP_PROVIDER ? "BACKUP_PROVIDER" \
: x == CERTIFICATE_PUBLIC_KEY_USAGE_RECOVER_ORCHESTRATOR ? "RECOVER_ORCHESTRATOR" \
: x == CERTIFICATE_PUBLIC_KEY_USAGE_PLUGIN_METADATA ? "PLUGIN_METADATA" \
: x == CERTIFICATE_PUBLIC_KEY_USAGE_COIN_META ? "COIN_META" \
: x == CERTIFICATE_PUBLIC_KEY_USAGE_SEED_ID_AUTH ? "SEED_ID_AUTH" \
: "Unknown")
int check_signature_with_pubkey(const char *tag,
uint8_t *buffer,
const uint8_t bufLen,
const uint8_t *PubKey,
const uint8_t keyLen,
#ifdef HAVE_LEDGER_PKI
const uint8_t keyUsageExp,
#endif
uint8_t *signature,
const uint8_t sigLen) {
UNUSED(tag);
cx_ecfp_public_key_t verif_key = {0};
cx_err_t error = CX_INTERNAL_ERROR;
#ifdef HAVE_LEDGER_PKI
uint8_t key_usage = 0;
size_t trusted_name_len = 0;
uint8_t trusted_name[CERTIFICATE_TRUSTED_NAME_MAXLEN] = {0};
cx_ecfp_384_public_key_t public_key = {0};
#endif
PRINTF(
"[%s] "
"=======================================================================================\n",
tag);
#ifdef HAVE_LEDGER_PKI
error = os_pki_get_info(&key_usage, trusted_name, &trusted_name_len, &public_key);
if ((error == 0) && (key_usage == keyUsageExp)) {
PRINTF("[%s] Certificate '%s' loaded for usage 0x%x (%s)\n",
tag,
trusted_name,
key_usage,
KEY_USAGE_STR(key_usage));
// Checking the signature with PKI
if (!os_pki_verify(buffer, bufLen, signature, sigLen)) {
PRINTF("%s: Invalid signature\n", tag);
#ifndef HAVE_BYPASS_SIGNATURES
error = APDU_RESPONSE_INVALID_DATA;
goto end;
#endif
}
} else
#endif
{
PRINTF("[%s] ********** No certificate loaded. Using legacy path **********\n", tag);
CX_CHECK(cx_ecfp_init_public_key_no_throw(CX_CURVE_256K1, PubKey, keyLen, &verif_key));
if (!cx_ecdsa_verify_no_throw(&verif_key, buffer, bufLen, signature, sigLen)) {
PRINTF("%s: Invalid signature\n", tag);
#ifndef HAVE_BYPASS_SIGNATURES
error = APDU_RESPONSE_INVALID_DATA;
goto end;
#endif
}
}
error = CX_OK;
end:
return error;
}

View File

@@ -16,6 +16,7 @@
********************************************************************************/ ********************************************************************************/
#pragma once #pragma once
#include <stdint.h>
static const uint8_t LEDGER_SIGNATURE_PUBLIC_KEY[] = { static const uint8_t LEDGER_SIGNATURE_PUBLIC_KEY[] = {
#if defined(HAVE_CAL_TEST_KEY) #if defined(HAVE_CAL_TEST_KEY)
@@ -101,3 +102,14 @@ static const uint8_t LEDGER_NFT_SELECTOR_PUBLIC_KEY[] = {
0x92, 0xc7, 0xc6, 0x48, 0x0d, 0x39, 0xce, 0xbb, 0xa3 0x92, 0xc7, 0xc6, 0x48, 0x0d, 0x39, 0xce, 0xbb, 0xa3
#endif #endif
}; };
extern int check_signature_with_pubkey(const char *tag,
uint8_t *buffer,
const uint8_t bufLen,
const uint8_t *PubKey,
const uint8_t keyLen,
#ifdef HAVE_LEDGER_PKI
const uint8_t keyUsageExp,
#endif
uint8_t *signature,
const uint8_t sigLen);

View File

@@ -12,6 +12,9 @@
#include "hash_bytes.h" #include "hash_bytes.h"
#include "network.h" #include "network.h"
#include "public_keys.h" #include "public_keys.h"
#ifdef HAVE_LEDGER_PKI
#include "os_pki.h"
#endif
#define P1_FIRST_CHUNK 0x01 #define P1_FIRST_CHUNK 0x01
#define P1_FOLLOWING_CHUNK 0x00 #define P1_FOLLOWING_CHUNK 0x00
@@ -364,39 +367,36 @@ static bool handle_address(const s_tlv_data *data,
*/ */
static bool verify_signature(const s_sig_ctx *sig_ctx) { static bool verify_signature(const s_sig_ctx *sig_ctx) {
uint8_t hash[INT256_LENGTH]; uint8_t hash[INT256_LENGTH];
cx_ecfp_public_key_t verif_key;
cx_err_t error = CX_INTERNAL_ERROR; cx_err_t error = CX_INTERNAL_ERROR;
#ifdef HAVE_DOMAIN_NAME_TEST_KEY
e_key_id valid_key_id = KEY_ID_TEST;
#else
e_key_id valid_key_id = KEY_ID_PROD;
#endif
bool ret_code = false;
if (sig_ctx->key_id != valid_key_id) {
PRINTF("Error: Unknown metadata key ID %u\n", sig_ctx->key_id);
return false;
}
CX_CHECK( CX_CHECK(
cx_hash_no_throw((cx_hash_t *) &sig_ctx->hash_ctx, CX_LAST, NULL, 0, hash, INT256_LENGTH)); cx_hash_no_throw((cx_hash_t *) &sig_ctx->hash_ctx, CX_LAST, NULL, 0, hash, INT256_LENGTH));
switch (sig_ctx->key_id) {
#ifdef HAVE_DOMAIN_NAME_TEST_KEY CX_CHECK(check_signature_with_pubkey("Domain Name",
case KEY_ID_TEST: hash,
#else sizeof(hash),
case KEY_ID_PROD: DOMAIN_NAME_PUB_KEY,
sizeof(DOMAIN_NAME_PUB_KEY),
#ifdef HAVE_LEDGER_PKI
CERTIFICATE_PUBLIC_KEY_USAGE_COIN_META,
#endif #endif
CX_CHECK(cx_ecfp_init_public_key_no_throw(CX_CURVE_256K1, (uint8_t *) (sig_ctx->input_sig),
DOMAIN_NAME_PUB_KEY, sig_ctx->input_sig_size));
sizeof(DOMAIN_NAME_PUB_KEY),
&verif_key)); ret_code = true;
break;
default:
PRINTF("Error: Unknown metadata key ID %u\n", sig_ctx->key_id);
return false;
}
if (!cx_ecdsa_verify_no_throw(&verif_key,
hash,
sizeof(hash),
sig_ctx->input_sig,
sig_ctx->input_sig_size)) {
PRINTF("Domain name signature verification failed!\n");
#ifndef HAVE_BYPASS_SIGNATURES
return false;
#endif
}
return true;
end: end:
return false; return ret_code;
} }
/** /**

View File

@@ -5,6 +5,9 @@
#include "os_io_seproxyhal.h" #include "os_io_seproxyhal.h"
#include "network.h" #include "network.h"
#include "manage_asset_info.h" #include "manage_asset_info.h"
#ifdef HAVE_LEDGER_PKI
#include "os_pki.h"
#endif
void handleProvideErc20TokenInformation(uint8_t p1, void handleProvideErc20TokenInformation(uint8_t p1,
uint8_t p2, uint8_t p2,
@@ -20,22 +23,21 @@ void handleProvideErc20TokenInformation(uint8_t p1,
uint8_t tickerLength; uint8_t tickerLength;
uint64_t chain_id; uint64_t chain_id;
uint8_t hash[INT256_LENGTH]; uint8_t hash[INT256_LENGTH];
cx_ecfp_public_key_t tokenKey;
tokenDefinition_t *token = &get_current_asset_info()->token; tokenDefinition_t *token = &get_current_asset_info()->token;
cx_err_t error = CX_INTERNAL_ERROR;
PRINTF("Provisioning currentAssetIndex %d\n", tmpCtx.transactionContext.currentAssetIndex); PRINTF("Provisioning currentAssetIndex %d\n", tmpCtx.transactionContext.currentAssetIndex);
if (dataLength < 1) { if (dataLength < 1) {
THROW(0x6A80); THROW(APDU_RESPONSE_INVALID_DATA);
} }
tickerLength = workBuffer[offset++]; tickerLength = workBuffer[offset++];
dataLength--; dataLength--;
if ((tickerLength + 1) > sizeof(token->ticker)) { if ((tickerLength + 1) > sizeof(token->ticker)) {
THROW(0x6A80); THROW(APDU_RESPONSE_INVALID_DATA);
} }
if (dataLength < tickerLength + 20 + 4 + 4) { if (dataLength < tickerLength + 20 + 4 + 4) {
THROW(0x6A80); THROW(APDU_RESPONSE_INVALID_DATA);
} }
cx_hash_sha256(workBuffer + offset, tickerLength + 20 + 4 + 4, hash, 32); cx_hash_sha256(workBuffer + offset, tickerLength + 20 + 4 + 4, hash, 32);
memmove(token->ticker, workBuffer + offset, tickerLength); memmove(token->ticker, workBuffer + offset, tickerLength);
@@ -53,21 +55,26 @@ void handleProvideErc20TokenInformation(uint8_t p1,
chain_id = U4BE(workBuffer, offset); chain_id = U4BE(workBuffer, offset);
if (!app_compatible_with_chain_id(&chain_id)) { if (!app_compatible_with_chain_id(&chain_id)) {
UNSUPPORTED_CHAIN_ID_MSG(chain_id); UNSUPPORTED_CHAIN_ID_MSG(chain_id);
THROW(0x6A80); THROW(APDU_RESPONSE_INVALID_DATA);
} }
offset += 4; offset += 4;
dataLength -= 4; dataLength -= 4;
CX_ASSERT(cx_ecfp_init_public_key_no_throw(CX_CURVE_256K1, error = check_signature_with_pubkey("ERC20 Token Info",
LEDGER_SIGNATURE_PUBLIC_KEY, hash,
sizeof(LEDGER_SIGNATURE_PUBLIC_KEY), sizeof(hash),
&tokenKey)); LEDGER_SIGNATURE_PUBLIC_KEY,
if (!cx_ecdsa_verify_no_throw(&tokenKey, hash, 32, workBuffer + offset, dataLength)) { sizeof(LEDGER_SIGNATURE_PUBLIC_KEY),
#ifndef HAVE_BYPASS_SIGNATURES #ifdef HAVE_LEDGER_PKI
PRINTF("Invalid token signature\n"); CERTIFICATE_PUBLIC_KEY_USAGE_COIN_META,
THROW(0x6A80);
#endif #endif
(uint8_t *) (workBuffer + offset),
dataLength);
#ifndef HAVE_BYPASS_SIGNATURES
if (error != CX_OK) {
THROW(APDU_RESPONSE_INVALID_DATA);
} }
#endif
G_io_apdu_buffer[0] = tmpCtx.transactionContext.currentAssetIndex; G_io_apdu_buffer[0] = tmpCtx.transactionContext.currentAssetIndex;
validate_current_asset_info(); validate_current_asset_info();

View File

@@ -9,6 +9,9 @@
#include "network.h" #include "network.h"
#include "public_keys.h" #include "public_keys.h"
#include "manage_asset_info.h" #include "manage_asset_info.h"
#ifdef HAVE_LEDGER_PKI
#include "os_pki.h"
#endif
#define TYPE_SIZE 1 #define TYPE_SIZE 1
#define VERSION_SIZE 1 #define VERSION_SIZE 1
@@ -50,19 +53,29 @@ void handleProvideNFTInformation(uint8_t p1,
UNUSED(tx); UNUSED(tx);
UNUSED(flags); UNUSED(flags);
uint8_t hash[INT256_LENGTH]; uint8_t hash[INT256_LENGTH];
cx_ecfp_public_key_t nftKey; nftInfo_t *nft = NULL;
size_t offset = 0;
size_t payloadSize = 0;
uint8_t collectionNameLength = 0;
uint64_t chain_id = 0;
uint8_t signatureLen = 0;
cx_err_t error = CX_INTERNAL_ERROR;
#ifdef HAVE_NFT_STAGING_KEY
uint8_t valid_keyId = STAGING_NFT_METADATA_KEY;
#else
uint8_t valid_keyId = PROD_NFT_METADATA_KEY;
#endif
PRINTF("In handle provide NFTInformation\n"); PRINTF("In handle provide NFTInformation\n");
if ((pluginType != ERC721) && (pluginType != ERC1155)) { if ((pluginType != ERC721) && (pluginType != ERC1155)) {
PRINTF("NFT metadata provided without proper plugin loaded!\n"); PRINTF("NFT metadata provided without proper plugin loaded!\n");
THROW(0x6985); THROW(0x6985);
} }
nftInfo_t *nft = &get_current_asset_info()->nft; nft = &get_current_asset_info()->nft;
PRINTF("Provisioning currentAssetIndex %d\n", tmpCtx.transactionContext.currentAssetIndex); PRINTF("Provisioning currentAssetIndex %d\n", tmpCtx.transactionContext.currentAssetIndex);
size_t offset = 0;
if (dataLength <= HEADER_SIZE) { if (dataLength <= HEADER_SIZE) {
PRINTF("Data too small for headers: expected at least %d, got %d\n", PRINTF("Data too small for headers: expected at least %d, got %d\n",
HEADER_SIZE, HEADER_SIZE,
@@ -70,34 +83,24 @@ void handleProvideNFTInformation(uint8_t p1,
THROW(APDU_RESPONSE_INVALID_DATA); THROW(APDU_RESPONSE_INVALID_DATA);
} }
uint8_t type = workBuffer[offset]; if (workBuffer[offset] != TYPE_1) {
switch (type) { PRINTF("Unsupported type %d\n", workBuffer[offset]);
case TYPE_1: THROW(APDU_RESPONSE_INVALID_DATA);
break;
default:
PRINTF("Unsupported type %d\n", type);
THROW(APDU_RESPONSE_INVALID_DATA);
break;
} }
offset += TYPE_SIZE; offset += TYPE_SIZE;
uint8_t version = workBuffer[offset]; if (workBuffer[offset] != VERSION_1) {
switch (version) { PRINTF("Unsupported version %d\n", workBuffer[offset]);
case VERSION_1: THROW(APDU_RESPONSE_INVALID_DATA);
break;
default:
PRINTF("Unsupported version %d\n", version);
THROW(APDU_RESPONSE_INVALID_DATA);
break;
} }
offset += VERSION_SIZE; offset += VERSION_SIZE;
uint8_t collectionNameLength = workBuffer[offset]; collectionNameLength = workBuffer[offset];
offset += NAME_LENGTH_SIZE; offset += NAME_LENGTH_SIZE;
// Size of the payload (everything except the signature) // Size of the payload (everything except the signature)
size_t payloadSize = HEADER_SIZE + collectionNameLength + ADDRESS_LENGTH + CHAIN_ID_SIZE + payloadSize = HEADER_SIZE + collectionNameLength + ADDRESS_LENGTH + CHAIN_ID_SIZE +
KEY_ID_SIZE + ALGORITHM_ID_SIZE; KEY_ID_SIZE + ALGORITHM_ID_SIZE;
if (dataLength < payloadSize) { if (dataLength < payloadSize) {
PRINTF("Data too small for payload: expected at least %d, got %d\n", PRINTF("Data too small for payload: expected at least %d, got %d\n",
payloadSize, payloadSize,
@@ -124,7 +127,7 @@ void handleProvideNFTInformation(uint8_t p1,
PRINTF("Address: %.*H\n", ADDRESS_LENGTH, workBuffer + offset); PRINTF("Address: %.*H\n", ADDRESS_LENGTH, workBuffer + offset);
offset += ADDRESS_LENGTH; offset += ADDRESS_LENGTH;
uint64_t chain_id = u64_from_BE(workBuffer + offset, CHAIN_ID_SIZE); chain_id = u64_from_BE(workBuffer + offset, CHAIN_ID_SIZE);
// this prints raw data, so to have a more meaningful print, display // this prints raw data, so to have a more meaningful print, display
// the buffer before the endianness swap // the buffer before the endianness swap
PRINTF("ChainID: %.*H\n", sizeof(chain_id), (workBuffer + offset)); PRINTF("ChainID: %.*H\n", sizeof(chain_id), (workBuffer + offset));
@@ -134,35 +137,18 @@ void handleProvideNFTInformation(uint8_t p1,
} }
offset += CHAIN_ID_SIZE; offset += CHAIN_ID_SIZE;
uint8_t keyId = workBuffer[offset]; if (workBuffer[offset] != valid_keyId) {
const uint8_t *rawKey; PRINTF("Unsupported KeyID %d\n", workBuffer[offset]);
uint8_t rawKeyLen; THROW(APDU_RESPONSE_INVALID_DATA);
PRINTF("KeyID: %d\n", keyId);
switch (keyId) {
#ifdef HAVE_NFT_STAGING_KEY
case STAGING_NFT_METADATA_KEY:
#endif
case PROD_NFT_METADATA_KEY:
rawKey = LEDGER_NFT_METADATA_PUBLIC_KEY;
rawKeyLen = sizeof(LEDGER_NFT_METADATA_PUBLIC_KEY);
break;
default:
PRINTF("KeyID %d not supported\n", keyId);
THROW(APDU_RESPONSE_INVALID_DATA);
break;
} }
PRINTF("RawKey: %.*H\n", rawKeyLen, rawKey);
offset += KEY_ID_SIZE; offset += KEY_ID_SIZE;
uint8_t algorithmId = workBuffer[offset]; if (workBuffer[offset] != ALGORITHM_ID_1) {
PRINTF("Algorithm: %d\n", algorithmId); PRINTF("Incorrect algorithmId %d\n", workBuffer[offset]);
if (algorithmId != ALGORITHM_ID_1) {
PRINTF("Incorrect algorithmId %d\n", algorithmId);
THROW(APDU_RESPONSE_INVALID_DATA); THROW(APDU_RESPONSE_INVALID_DATA);
} }
offset += ALGORITHM_ID_SIZE; offset += ALGORITHM_ID_SIZE;
PRINTF("hashing: %.*H\n", payloadSize, workBuffer); PRINTF("hashing: %.*H\n", payloadSize, workBuffer);
cx_hash_sha256(workBuffer, payloadSize, hash, sizeof(hash)); cx_hash_sha256(workBuffer, payloadSize, hash, sizeof(hash));
@@ -171,7 +157,7 @@ void handleProvideNFTInformation(uint8_t p1,
THROW(APDU_RESPONSE_INVALID_DATA); THROW(APDU_RESPONSE_INVALID_DATA);
} }
uint8_t signatureLen = workBuffer[offset]; signatureLen = workBuffer[offset];
PRINTF("Signature len: %d\n", signatureLen); PRINTF("Signature len: %d\n", signatureLen);
if (signatureLen < MIN_DER_SIG_SIZE || signatureLen > MAX_DER_SIG_SIZE) { if (signatureLen < MIN_DER_SIG_SIZE || signatureLen > MAX_DER_SIG_SIZE) {
PRINTF("SignatureLen too big or too small. Must be between %d and %d, got %d\n", PRINTF("SignatureLen too big or too small. Must be between %d and %d, got %d\n",
@@ -187,17 +173,21 @@ void handleProvideNFTInformation(uint8_t p1,
THROW(APDU_RESPONSE_INVALID_DATA); THROW(APDU_RESPONSE_INVALID_DATA);
} }
CX_ASSERT(cx_ecfp_init_public_key_no_throw(CX_CURVE_256K1, rawKey, rawKeyLen, &nftKey)); error = check_signature_with_pubkey("NFT Info",
if (!cx_ecdsa_verify_no_throw(&nftKey, hash,
hash, sizeof(hash),
sizeof(hash), LEDGER_NFT_METADATA_PUBLIC_KEY,
(uint8_t *) workBuffer + offset, sizeof(LEDGER_NFT_METADATA_PUBLIC_KEY),
signatureLen)) { #ifdef HAVE_LEDGER_PKI
#ifndef HAVE_BYPASS_SIGNATURES CERTIFICATE_PUBLIC_KEY_USAGE_NFT_METADATA,
PRINTF("Invalid NFT signature\n");
THROW(APDU_RESPONSE_INVALID_DATA);
#endif #endif
(uint8_t *) (workBuffer + offset),
signatureLen);
#ifndef HAVE_BYPASS_SIGNATURES
if (error != CX_OK) {
THROW(APDU_RESPONSE_INVALID_DATA);
} }
#endif
G_io_apdu_buffer[0] = tmpCtx.transactionContext.currentAssetIndex; G_io_apdu_buffer[0] = tmpCtx.transactionContext.currentAssetIndex;
validate_current_asset_info(); validate_current_asset_info();

View File

@@ -6,6 +6,9 @@
#include "plugin_utils.h" #include "plugin_utils.h"
#include "common_ui.h" #include "common_ui.h"
#include "os_io_seproxyhal.h" #include "os_io_seproxyhal.h"
#ifdef HAVE_LEDGER_PKI
#include "os_pki.h"
#endif
void handleSetExternalPlugin(uint8_t p1, void handleSetExternalPlugin(uint8_t p1,
uint8_t p2, uint8_t p2,
@@ -18,41 +21,43 @@ void handleSetExternalPlugin(uint8_t p1,
UNUSED(flags); UNUSED(flags);
PRINTF("Handling set Plugin\n"); PRINTF("Handling set Plugin\n");
uint8_t hash[INT256_LENGTH]; uint8_t hash[INT256_LENGTH];
cx_ecfp_public_key_t tokenKey;
uint8_t pluginNameLength = *workBuffer; uint8_t pluginNameLength = *workBuffer;
uint32_t params[2];
cx_err_t error = CX_INTERNAL_ERROR;
PRINTF("plugin Name Length: %d\n", pluginNameLength); PRINTF("plugin Name Length: %d\n", pluginNameLength);
const size_t payload_size = 1 + pluginNameLength + ADDRESS_LENGTH + SELECTOR_SIZE; const size_t payload_size = 1 + pluginNameLength + ADDRESS_LENGTH + SELECTOR_SIZE;
if (dataLength <= payload_size) { if (dataLength <= payload_size) {
PRINTF("data too small: expected at least %d got %d\n", payload_size, dataLength); PRINTF("data too small: expected at least %d got %d\n", payload_size, dataLength);
THROW(0x6A80); THROW(APDU_RESPONSE_INVALID_DATA);
} }
if (pluginNameLength + 1 > sizeof(dataContext.tokenContext.pluginName)) { if (pluginNameLength + 1 > sizeof(dataContext.tokenContext.pluginName)) {
PRINTF("name length too big: expected max %d, got %d\n", PRINTF("name length too big: expected max %d, got %d\n",
sizeof(dataContext.tokenContext.pluginName), sizeof(dataContext.tokenContext.pluginName),
pluginNameLength + 1); pluginNameLength + 1);
THROW(0x6A80); THROW(APDU_RESPONSE_INVALID_DATA);
} }
// check Ledger's signature over the payload // check Ledger's signature over the payload
cx_hash_sha256(workBuffer, payload_size, hash, sizeof(hash)); cx_hash_sha256(workBuffer, payload_size, hash, sizeof(hash));
CX_ASSERT(cx_ecfp_init_public_key_no_throw(CX_CURVE_256K1,
LEDGER_SIGNATURE_PUBLIC_KEY, error = check_signature_with_pubkey("External Plugin",
sizeof(LEDGER_SIGNATURE_PUBLIC_KEY), hash,
&tokenKey)); sizeof(hash),
if (!cx_ecdsa_verify_no_throw(&tokenKey, LEDGER_SIGNATURE_PUBLIC_KEY,
hash, sizeof(LEDGER_SIGNATURE_PUBLIC_KEY),
sizeof(hash), #ifdef HAVE_LEDGER_PKI
workBuffer + payload_size, CERTIFICATE_PUBLIC_KEY_USAGE_COIN_META,
dataLength - payload_size)) {
#ifndef HAVE_BYPASS_SIGNATURES
PRINTF("Invalid plugin signature %.*H\n",
dataLength - payload_size,
workBuffer + payload_size);
THROW(0x6A80);
#endif #endif
(uint8_t *) (workBuffer + payload_size),
dataLength - payload_size);
#ifndef HAVE_BYPASS_SIGNATURES
if (error != CX_OK) {
THROW(APDU_RESPONSE_INVALID_DATA);
} }
#endif
// move on to the rest of the payload parsing // move on to the rest of the payload parsing
workBuffer++; workBuffer++;
@@ -63,7 +68,6 @@ void handleSetExternalPlugin(uint8_t p1,
PRINTF("Check external plugin %s\n", dataContext.tokenContext.pluginName); PRINTF("Check external plugin %s\n", dataContext.tokenContext.pluginName);
// Check if the plugin is present on the device // Check if the plugin is present on the device
uint32_t params[2];
params[0] = (uint32_t) dataContext.tokenContext.pluginName; params[0] = (uint32_t) dataContext.tokenContext.pluginName;
params[1] = ETH_PLUGIN_CHECK_PRESENCE; params[1] = ETH_PLUGIN_CHECK_PRESENCE;
BEGIN_TRY { BEGIN_TRY {

View File

@@ -9,6 +9,9 @@
#include "os_io_seproxyhal.h" #include "os_io_seproxyhal.h"
#include "network.h" #include "network.h"
#include "public_keys.h" #include "public_keys.h"
#ifdef HAVE_LEDGER_PKI
#include "os_pki.h"
#endif
// Supported internal plugins // Supported internal plugins
#define ERC721_STR "ERC721" #define ERC721_STR "ERC721"
@@ -82,53 +85,51 @@ void handleSetPlugin(uint8_t p1,
UNUSED(flags); UNUSED(flags);
PRINTF("Handling set Plugin\n"); PRINTF("Handling set Plugin\n");
uint8_t hash[INT256_LENGTH] = {0}; uint8_t hash[INT256_LENGTH] = {0};
cx_ecfp_public_key_t pluginKey = {0};
tokenContext_t *tokenContext = &dataContext.tokenContext; tokenContext_t *tokenContext = &dataContext.tokenContext;
size_t offset = 0; size_t offset = 0;
uint8_t pluginNameLength = 0;
size_t payloadSize = 0;
uint64_t chain_id = 0;
uint8_t signatureLen = 0;
cx_err_t error = CX_INTERNAL_ERROR;
#ifdef HAVE_NFT_STAGING_KEY
enum KeyId valid_keyId = TEST_PLUGIN_KEY;
#else
enum KeyId valid_keyId = PROD_PLUGIN_KEY;
#endif
enum KeyId keyId;
uint32_t params[2];
if (dataLength <= HEADER_SIZE) { if (dataLength <= HEADER_SIZE) {
PRINTF("Data too small for headers: expected at least %d, got %d\n", PRINTF("Data too small for headers: expected at least %d, got %d\n",
HEADER_SIZE, HEADER_SIZE,
dataLength); dataLength);
THROW(0x6A80); THROW(APDU_RESPONSE_INVALID_DATA);
} }
enum Type type = workBuffer[offset]; if (workBuffer[offset] != ETH_PLUGIN) {
PRINTF("Type: %d\n", type); PRINTF("Unsupported type %d\n", workBuffer[offset]);
switch (type) { THROW(APDU_RESPONSE_INVALID_DATA);
case ETH_PLUGIN:
break;
default:
PRINTF("Unsupported type %d\n", type);
THROW(0x6a80);
break;
} }
offset += TYPE_SIZE; offset += TYPE_SIZE;
uint8_t version = workBuffer[offset]; if (workBuffer[offset] != VERSION_1) {
PRINTF("version: %d\n", version); PRINTF("Unsupported version %d\n", workBuffer[offset]);
switch (version) { THROW(APDU_RESPONSE_INVALID_DATA);
case VERSION_1:
break;
default:
PRINTF("Unsupported version %d\n", version);
THROW(0x6a80);
break;
} }
offset += VERSION_SIZE; offset += VERSION_SIZE;
uint8_t pluginNameLength = workBuffer[offset]; pluginNameLength = workBuffer[offset];
offset += PLUGIN_NAME_LENGTH_SIZE; offset += PLUGIN_NAME_LENGTH_SIZE;
// Size of the payload (everything except the signature) // Size of the payload (everything except the signature)
size_t payloadSize = HEADER_SIZE + pluginNameLength + ADDRESS_LENGTH + SELECTOR_SIZE + payloadSize = HEADER_SIZE + pluginNameLength + ADDRESS_LENGTH + SELECTOR_SIZE + CHAIN_ID_SIZE +
CHAIN_ID_SIZE + KEY_ID_SIZE + ALGORITHM_ID_SIZE; KEY_ID_SIZE + ALGORITHM_ID_SIZE;
if (dataLength < payloadSize) { if (dataLength < payloadSize) {
PRINTF("Data too small for payload: expected at least %d, got %d\n", PRINTF("Data too small for payload: expected at least %d, got %d\n",
payloadSize, payloadSize,
dataLength); dataLength);
THROW(0x6A80); THROW(APDU_RESPONSE_INVALID_DATA);
} }
// `+ 1` because we want to add a null terminating character. // `+ 1` because we want to add a null terminating character.
@@ -136,7 +137,7 @@ void handleSetPlugin(uint8_t p1,
PRINTF("plugin name too big: expected max %d, got %d\n", PRINTF("plugin name too big: expected max %d, got %d\n",
sizeof(dataContext.tokenContext.pluginName), sizeof(dataContext.tokenContext.pluginName),
pluginNameLength + 1); pluginNameLength + 1);
THROW(0x6A80); THROW(APDU_RESPONSE_INVALID_DATA);
} }
// Safe because we've checked the size before. // Safe because we've checked the size before.
@@ -155,7 +156,7 @@ void handleSetPlugin(uint8_t p1,
PRINTF("Selector: %.*H\n", SELECTOR_SIZE, tokenContext->methodSelector); PRINTF("Selector: %.*H\n", SELECTOR_SIZE, tokenContext->methodSelector);
offset += SELECTOR_SIZE; offset += SELECTOR_SIZE;
uint64_t chain_id = u64_from_BE(workBuffer + offset, CHAIN_ID_SIZE); chain_id = u64_from_BE(workBuffer + offset, CHAIN_ID_SIZE);
// this prints raw data, so to have a more meaningful print, display // this prints raw data, so to have a more meaningful print, display
// the buffer before the endianness swap // the buffer before the endianness swap
PRINTF("ChainID: %.*H\n", sizeof(chain_id), (workBuffer + offset)); PRINTF("ChainID: %.*H\n", sizeof(chain_id), (workBuffer + offset));
@@ -165,105 +166,86 @@ void handleSetPlugin(uint8_t p1,
} }
offset += CHAIN_ID_SIZE; offset += CHAIN_ID_SIZE;
enum KeyId keyId = workBuffer[offset]; keyId = workBuffer[offset];
uint8_t const *rawKey; if (keyId != valid_keyId) {
uint8_t rawKeyLen; PRINTF("Unsupported KeyID %d\n", keyId);
THROW(APDU_RESPONSE_INVALID_DATA);
PRINTF("KeyID: %d\n", keyId);
switch (keyId) {
#ifdef HAVE_NFT_STAGING_KEY
case TEST_PLUGIN_KEY:
#endif
case PROD_PLUGIN_KEY:
rawKey = LEDGER_NFT_SELECTOR_PUBLIC_KEY;
rawKeyLen = sizeof(LEDGER_NFT_SELECTOR_PUBLIC_KEY);
break;
default:
PRINTF("KeyID %d not supported\n", keyId);
THROW(0x6A80);
break;
} }
PRINTF("RawKey: %.*H\n", rawKeyLen, rawKey);
offset += KEY_ID_SIZE; offset += KEY_ID_SIZE;
uint8_t algorithmId = workBuffer[offset]; if (workBuffer[offset] != ECC_SECG_P256K1__ECDSA_SHA_256) {
PRINTF("Algorithm: %d\n", algorithmId); PRINTF("Incorrect algorithmId %d\n", workBuffer[offset]);
THROW(APDU_RESPONSE_INVALID_DATA);
if (algorithmId != ECC_SECG_P256K1__ECDSA_SHA_256) {
PRINTF("Incorrect algorithmId %d\n", algorithmId);
THROW(0x6a80);
} }
offset += ALGORITHM_ID_SIZE; offset += ALGORITHM_ID_SIZE;
PRINTF("hashing: %.*H\n", payloadSize, workBuffer); PRINTF("hashing: %.*H\n", payloadSize, workBuffer);
cx_hash_sha256(workBuffer, payloadSize, hash, sizeof(hash)); cx_hash_sha256(workBuffer, payloadSize, hash, sizeof(hash));
if (dataLength < payloadSize + SIGNATURE_LENGTH_SIZE) { if (dataLength < payloadSize + SIGNATURE_LENGTH_SIZE) {
PRINTF("Data too short to hold signature length\n"); PRINTF("Data too short to hold signature length\n");
THROW(0x6a80); THROW(APDU_RESPONSE_INVALID_DATA);
} }
uint8_t signatureLen = workBuffer[offset]; signatureLen = workBuffer[offset];
PRINTF("Signature len: %d\n", signatureLen); PRINTF("Signature len: %d\n", signatureLen);
if (signatureLen < MIN_DER_SIG_SIZE || signatureLen > MAX_DER_SIG_SIZE) { if (signatureLen < MIN_DER_SIG_SIZE || signatureLen > MAX_DER_SIG_SIZE) {
PRINTF("SignatureLen too big or too small. Must be between %d and %d, got %d\n", PRINTF("SignatureLen too big or too small. Must be between %d and %d, got %d\n",
MIN_DER_SIG_SIZE, MIN_DER_SIG_SIZE,
MAX_DER_SIG_SIZE, MAX_DER_SIG_SIZE,
signatureLen); signatureLen);
THROW(0x6a80); THROW(APDU_RESPONSE_INVALID_DATA);
} }
offset += SIGNATURE_LENGTH_SIZE; offset += SIGNATURE_LENGTH_SIZE;
if (dataLength < payloadSize + SIGNATURE_LENGTH_SIZE + signatureLen) { if (dataLength < payloadSize + SIGNATURE_LENGTH_SIZE + signatureLen) {
PRINTF("Signature could not fit in data\n"); PRINTF("Signature could not fit in data\n");
THROW(0x6a80); THROW(APDU_RESPONSE_INVALID_DATA);
} }
CX_ASSERT(cx_ecfp_init_public_key_no_throw(CX_CURVE_256K1, rawKey, rawKeyLen, &pluginKey)); error = check_signature_with_pubkey("Set Plugin",
if (!cx_ecdsa_verify_no_throw(&pluginKey, hash,
hash, sizeof(hash),
sizeof(hash), LEDGER_NFT_SELECTOR_PUBLIC_KEY,
(unsigned char *) (workBuffer + offset), sizeof(LEDGER_NFT_SELECTOR_PUBLIC_KEY),
signatureLen)) { #ifdef HAVE_LEDGER_PKI
#ifndef HAVE_BYPASS_SIGNATURES CERTIFICATE_PUBLIC_KEY_USAGE_PLUGIN_METADATA,
PRINTF("Invalid NFT signature\n");
THROW(0x6A80);
#endif #endif
(uint8_t *) (workBuffer + offset),
signatureLen);
#ifndef HAVE_BYPASS_SIGNATURES
if (error != CX_OK) {
THROW(APDU_RESPONSE_INVALID_DATA);
} }
#endif
pluginType = getPluginType(tokenContext->pluginName, pluginNameLength); pluginType = getPluginType(tokenContext->pluginName, pluginNameLength);
if (keyId == PROD_PLUGIN_KEY) { if (keyId == PROD_PLUGIN_KEY) {
if (pluginType != ERC721 && pluginType != ERC1155) { if (pluginType != ERC721 && pluginType != ERC1155) {
PRINTF("AWS key must only be used to set NFT internal plugins\n"); PRINTF("AWS key must only be used to set NFT internal plugins\n");
THROW(0x6A80); THROW(APDU_RESPONSE_INVALID_DATA);
} }
} }
switch (pluginType) { if (pluginType == EXTERNAL) {
case EXTERNAL: { PRINTF("Check external plugin %s\n", tokenContext->pluginName);
PRINTF("Check external plugin %s\n", tokenContext->pluginName);
// Check if the plugin is present on the device // Check if the plugin is present on the device
uint32_t params[2]; params[0] = (uint32_t) tokenContext->pluginName;
params[0] = (uint32_t) tokenContext->pluginName; params[1] = ETH_PLUGIN_CHECK_PRESENCE;
params[1] = ETH_PLUGIN_CHECK_PRESENCE; BEGIN_TRY {
BEGIN_TRY { TRY {
TRY { os_lib_call(params);
os_lib_call(params); }
} CATCH_OTHER(e) {
CATCH_OTHER(e) { PRINTF("%s external plugin is not present\n", tokenContext->pluginName);
PRINTF("%s external plugin is not present\n", tokenContext->pluginName); memset(tokenContext->pluginName, 0, sizeof(tokenContext->pluginName));
memset(tokenContext->pluginName, 0, sizeof(tokenContext->pluginName)); THROW(0x6984);
THROW(0x6984); }
} FINALLY {
FINALLY {
}
} }
END_TRY;
break;
} }
default: END_TRY;
break;
} }
G_io_apdu_buffer[(*tx)++] = 0x90; G_io_apdu_buffer[(*tx)++] = 0x90;

View File

@@ -12,6 +12,9 @@
#include "path.h" #include "path.h"
#include "ui_logic.h" #include "ui_logic.h"
#include "filtering.h" #include "filtering.h"
#ifdef HAVE_LEDGER_PKI
#include "os_pki.h"
#endif
#define FILT_MAGIC_MESSAGE_INFO 183 #define FILT_MAGIC_MESSAGE_INFO 183
#define FILT_MAGIC_AMOUNT_JOIN_TOKEN 11 #define FILT_MAGIC_AMOUNT_JOIN_TOKEN 11
@@ -95,26 +98,26 @@ static bool sig_verif_start(cx_sha256_t *hash_ctx, uint8_t magic) {
*/ */
static bool sig_verif_end(cx_sha256_t *hash_ctx, const uint8_t *sig, uint8_t sig_length) { static bool sig_verif_end(cx_sha256_t *hash_ctx, const uint8_t *sig, uint8_t sig_length) {
uint8_t hash[INT256_LENGTH]; uint8_t hash[INT256_LENGTH];
cx_ecfp_public_key_t verifying_key;
cx_err_t error = CX_INTERNAL_ERROR; cx_err_t error = CX_INTERNAL_ERROR;
bool ret_code = false;
// Finalize hash // Finalize hash
CX_CHECK(cx_hash_no_throw((cx_hash_t *) hash_ctx, CX_LAST, NULL, 0, hash, INT256_LENGTH)); CX_CHECK(cx_hash_no_throw((cx_hash_t *) hash_ctx, CX_LAST, NULL, 0, hash, INT256_LENGTH));
CX_CHECK(cx_ecfp_init_public_key_no_throw(CX_CURVE_256K1, CX_CHECK(check_signature_with_pubkey("EIP712 Filtering",
LEDGER_SIGNATURE_PUBLIC_KEY, hash,
sizeof(LEDGER_SIGNATURE_PUBLIC_KEY), sizeof(hash),
&verifying_key)); LEDGER_SIGNATURE_PUBLIC_KEY,
if (!cx_ecdsa_verify_no_throw(&verifying_key, hash, sizeof(hash), sig, sig_length)) { sizeof(LEDGER_SIGNATURE_PUBLIC_KEY),
#ifndef HAVE_BYPASS_SIGNATURES #ifdef HAVE_LEDGER_PKI
PRINTF("Invalid EIP-712 filtering signature\n"); CERTIFICATE_PUBLIC_KEY_USAGE_COIN_META,
apdu_response_code = APDU_RESPONSE_INVALID_DATA;
return false;
#endif #endif
} (uint8_t *) (sig),
return true; sig_length));
ret_code = true;
end: end:
return false; return ret_code;
} }
/** /**