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:
@@ -9,6 +9,9 @@
|
||||
#include "os_io_seproxyhal.h"
|
||||
#include "network.h"
|
||||
#include "public_keys.h"
|
||||
#ifdef HAVE_LEDGER_PKI
|
||||
#include "os_pki.h"
|
||||
#endif
|
||||
|
||||
// Supported internal plugins
|
||||
#define ERC721_STR "ERC721"
|
||||
@@ -82,53 +85,51 @@ void handleSetPlugin(uint8_t p1,
|
||||
UNUSED(flags);
|
||||
PRINTF("Handling set Plugin\n");
|
||||
uint8_t hash[INT256_LENGTH] = {0};
|
||||
cx_ecfp_public_key_t pluginKey = {0};
|
||||
tokenContext_t *tokenContext = &dataContext.tokenContext;
|
||||
|
||||
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) {
|
||||
PRINTF("Data too small for headers: expected at least %d, got %d\n",
|
||||
HEADER_SIZE,
|
||||
dataLength);
|
||||
THROW(0x6A80);
|
||||
THROW(APDU_RESPONSE_INVALID_DATA);
|
||||
}
|
||||
|
||||
enum Type type = workBuffer[offset];
|
||||
PRINTF("Type: %d\n", type);
|
||||
switch (type) {
|
||||
case ETH_PLUGIN:
|
||||
break;
|
||||
default:
|
||||
PRINTF("Unsupported type %d\n", type);
|
||||
THROW(0x6a80);
|
||||
break;
|
||||
if (workBuffer[offset] != ETH_PLUGIN) {
|
||||
PRINTF("Unsupported type %d\n", workBuffer[offset]);
|
||||
THROW(APDU_RESPONSE_INVALID_DATA);
|
||||
}
|
||||
offset += TYPE_SIZE;
|
||||
|
||||
uint8_t version = workBuffer[offset];
|
||||
PRINTF("version: %d\n", version);
|
||||
switch (version) {
|
||||
case VERSION_1:
|
||||
break;
|
||||
default:
|
||||
PRINTF("Unsupported version %d\n", version);
|
||||
THROW(0x6a80);
|
||||
break;
|
||||
if (workBuffer[offset] != VERSION_1) {
|
||||
PRINTF("Unsupported version %d\n", workBuffer[offset]);
|
||||
THROW(APDU_RESPONSE_INVALID_DATA);
|
||||
}
|
||||
offset += VERSION_SIZE;
|
||||
|
||||
uint8_t pluginNameLength = workBuffer[offset];
|
||||
pluginNameLength = workBuffer[offset];
|
||||
offset += PLUGIN_NAME_LENGTH_SIZE;
|
||||
|
||||
// Size of the payload (everything except the signature)
|
||||
size_t payloadSize = HEADER_SIZE + pluginNameLength + ADDRESS_LENGTH + SELECTOR_SIZE +
|
||||
CHAIN_ID_SIZE + KEY_ID_SIZE + ALGORITHM_ID_SIZE;
|
||||
payloadSize = HEADER_SIZE + pluginNameLength + ADDRESS_LENGTH + SELECTOR_SIZE + CHAIN_ID_SIZE +
|
||||
KEY_ID_SIZE + ALGORITHM_ID_SIZE;
|
||||
if (dataLength < payloadSize) {
|
||||
PRINTF("Data too small for payload: expected at least %d, got %d\n",
|
||||
payloadSize,
|
||||
dataLength);
|
||||
THROW(0x6A80);
|
||||
THROW(APDU_RESPONSE_INVALID_DATA);
|
||||
}
|
||||
|
||||
// `+ 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",
|
||||
sizeof(dataContext.tokenContext.pluginName),
|
||||
pluginNameLength + 1);
|
||||
THROW(0x6A80);
|
||||
THROW(APDU_RESPONSE_INVALID_DATA);
|
||||
}
|
||||
|
||||
// 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);
|
||||
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
|
||||
// the buffer before the endianness swap
|
||||
PRINTF("ChainID: %.*H\n", sizeof(chain_id), (workBuffer + offset));
|
||||
@@ -165,105 +166,86 @@ void handleSetPlugin(uint8_t p1,
|
||||
}
|
||||
offset += CHAIN_ID_SIZE;
|
||||
|
||||
enum KeyId keyId = workBuffer[offset];
|
||||
uint8_t const *rawKey;
|
||||
uint8_t rawKeyLen;
|
||||
|
||||
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;
|
||||
keyId = workBuffer[offset];
|
||||
if (keyId != valid_keyId) {
|
||||
PRINTF("Unsupported KeyID %d\n", keyId);
|
||||
THROW(APDU_RESPONSE_INVALID_DATA);
|
||||
}
|
||||
|
||||
PRINTF("RawKey: %.*H\n", rawKeyLen, rawKey);
|
||||
offset += KEY_ID_SIZE;
|
||||
|
||||
uint8_t algorithmId = workBuffer[offset];
|
||||
PRINTF("Algorithm: %d\n", algorithmId);
|
||||
|
||||
if (algorithmId != ECC_SECG_P256K1__ECDSA_SHA_256) {
|
||||
PRINTF("Incorrect algorithmId %d\n", algorithmId);
|
||||
THROW(0x6a80);
|
||||
if (workBuffer[offset] != ECC_SECG_P256K1__ECDSA_SHA_256) {
|
||||
PRINTF("Incorrect algorithmId %d\n", workBuffer[offset]);
|
||||
THROW(APDU_RESPONSE_INVALID_DATA);
|
||||
}
|
||||
offset += ALGORITHM_ID_SIZE;
|
||||
|
||||
PRINTF("hashing: %.*H\n", payloadSize, workBuffer);
|
||||
cx_hash_sha256(workBuffer, payloadSize, hash, sizeof(hash));
|
||||
|
||||
if (dataLength < payloadSize + SIGNATURE_LENGTH_SIZE) {
|
||||
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);
|
||||
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",
|
||||
MIN_DER_SIG_SIZE,
|
||||
MAX_DER_SIG_SIZE,
|
||||
signatureLen);
|
||||
THROW(0x6a80);
|
||||
THROW(APDU_RESPONSE_INVALID_DATA);
|
||||
}
|
||||
offset += SIGNATURE_LENGTH_SIZE;
|
||||
|
||||
if (dataLength < payloadSize + SIGNATURE_LENGTH_SIZE + signatureLen) {
|
||||
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));
|
||||
if (!cx_ecdsa_verify_no_throw(&pluginKey,
|
||||
hash,
|
||||
sizeof(hash),
|
||||
(unsigned char *) (workBuffer + offset),
|
||||
signatureLen)) {
|
||||
#ifndef HAVE_BYPASS_SIGNATURES
|
||||
PRINTF("Invalid NFT signature\n");
|
||||
THROW(0x6A80);
|
||||
error = check_signature_with_pubkey("Set Plugin",
|
||||
hash,
|
||||
sizeof(hash),
|
||||
LEDGER_NFT_SELECTOR_PUBLIC_KEY,
|
||||
sizeof(LEDGER_NFT_SELECTOR_PUBLIC_KEY),
|
||||
#ifdef HAVE_LEDGER_PKI
|
||||
CERTIFICATE_PUBLIC_KEY_USAGE_PLUGIN_METADATA,
|
||||
#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);
|
||||
if (keyId == PROD_PLUGIN_KEY) {
|
||||
if (pluginType != ERC721 && pluginType != ERC1155) {
|
||||
PRINTF("AWS key must only be used to set NFT internal plugins\n");
|
||||
THROW(0x6A80);
|
||||
THROW(APDU_RESPONSE_INVALID_DATA);
|
||||
}
|
||||
}
|
||||
|
||||
switch (pluginType) {
|
||||
case EXTERNAL: {
|
||||
PRINTF("Check external plugin %s\n", tokenContext->pluginName);
|
||||
if (pluginType == EXTERNAL) {
|
||||
PRINTF("Check external plugin %s\n", tokenContext->pluginName);
|
||||
|
||||
// Check if the plugin is present on the device
|
||||
uint32_t params[2];
|
||||
params[0] = (uint32_t) tokenContext->pluginName;
|
||||
params[1] = ETH_PLUGIN_CHECK_PRESENCE;
|
||||
BEGIN_TRY {
|
||||
TRY {
|
||||
os_lib_call(params);
|
||||
}
|
||||
CATCH_OTHER(e) {
|
||||
PRINTF("%s external plugin is not present\n", tokenContext->pluginName);
|
||||
memset(tokenContext->pluginName, 0, sizeof(tokenContext->pluginName));
|
||||
THROW(0x6984);
|
||||
}
|
||||
FINALLY {
|
||||
}
|
||||
// Check if the plugin is present on the device
|
||||
params[0] = (uint32_t) tokenContext->pluginName;
|
||||
params[1] = ETH_PLUGIN_CHECK_PRESENCE;
|
||||
BEGIN_TRY {
|
||||
TRY {
|
||||
os_lib_call(params);
|
||||
}
|
||||
CATCH_OTHER(e) {
|
||||
PRINTF("%s external plugin is not present\n", tokenContext->pluginName);
|
||||
memset(tokenContext->pluginName, 0, sizeof(tokenContext->pluginName));
|
||||
THROW(0x6984);
|
||||
}
|
||||
FINALLY {
|
||||
}
|
||||
END_TRY;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
END_TRY;
|
||||
}
|
||||
|
||||
G_io_apdu_buffer[(*tx)++] = 0x90;
|
||||
|
||||
Reference in New Issue
Block a user