Skip to content

Commit

Permalink
Add pool cold public key derivation command
Browse files Browse the repository at this point in the history
  • Loading branch information
refi93 authored and janmazak committed Apr 10, 2021
1 parent 9fb7716 commit 913b823
Show file tree
Hide file tree
Showing 10 changed files with 226 additions and 5 deletions.
3 changes: 3 additions & 0 deletions Makefile
Expand Up @@ -37,6 +37,9 @@ APPVERSION_P = 0
APPVERSION = "$(APPVERSION_M).$(APPVERSION_N).$(APPVERSION_P)"

APP_LOAD_PARAMS =--appFlags 0x240 --curve ed25519 --path "44'/1815'" --path "1852'/1815'"
ifeq ($(POOL_OPERATOR_APP),1)
APP_LOAD_PARAMS += --path "1853'/1815'"
endif
APP_LOAD_PARAMS += $(COMMON_LOAD_PARAMS)

ifeq ($(TARGET_NAME),TARGET_NANOX)
Expand Down
53 changes: 51 additions & 2 deletions src/bip44.c
Expand Up @@ -9,6 +9,11 @@ static const uint32_t CARDANO_CHAIN_STAKING_KEY = 2;
static const uint32_t MAX_REASONABLE_ACCOUNT = 100;
static const uint32_t MAX_REASONABLE_ADDRESS = 1000000;

#ifdef POOL_OPERATOR_APP
static const uint32_t MAX_REASONABLE_COLD_KEY_INDEX = 100;
#endif // POOL_OPERATOR_APP


size_t bip44_parseFromWire(
bip44_path_t* pathSpec,
const uint8_t *dataBuffer, size_t dataSize
Expand Down Expand Up @@ -72,6 +77,18 @@ bool bip44_hasValidCardanoPrefix(const bip44_path_t* pathSpec)
return bip44_hasByronPrefix(pathSpec) || bip44_hasShelleyPrefix(pathSpec);
}

#ifdef POOL_OPERATOR_APP
bool bip44_hasValidCardanoPoolColdKeyPrefix(const bip44_path_t* pathSpec)
{
#define CHECK(cond) if (!(cond)) return false
CHECK(pathSpec->length > BIP44_I_COIN_TYPE);
CHECK(pathSpec->path[BIP44_I_PURPOSE] == (PURPOSE_POOL_COLD_KEY | HARDENED_BIP32));
CHECK(pathSpec->path[BIP44_I_COIN_TYPE] == (ADA_COIN_TYPE | HARDENED_BIP32));
return true;
#undef CHECK
}
#endif // POOL_OPERATOR_APP

// Account

bool bip44_containsAccount(const bip44_path_t* pathSpec)
Expand All @@ -85,6 +102,14 @@ uint32_t bip44_getAccount(const bip44_path_t* pathSpec)
return pathSpec->path[BIP44_I_ACCOUNT];
}

#ifdef POOL_OPERATOR_APP
uint32_t bip44_getColdKeyIndex(const bip44_path_t* pathSpec)
{
ASSERT(pathSpec->length > BIP44_I_POOL_COLD_KEY);
return pathSpec->path[BIP44_I_POOL_COLD_KEY];
}
#endif // POOL_OPERATOR_APP

bool bip44_containsMoreThanAccount(const bip44_path_t* pathSpec)
{
return (pathSpec->length > BIP44_I_ACCOUNT + 1);
Expand All @@ -98,6 +123,17 @@ bool bip44_hasReasonableAccount(const bip44_path_t* pathSpec)
return unharden(account) <= MAX_REASONABLE_ACCOUNT;
}

#ifdef POOL_OPERATOR_APP
bool bip44_hasReasonablePoolColdKeyIndex(const bip44_path_t* pathSpec)
{
if (!bip44_isValidPoolColdKeyPath(pathSpec)) return false;
uint32_t cold_key_index = bip44_getColdKeyIndex(pathSpec);

if (!isHardened(cold_key_index)) return false;
return unharden(cold_key_index) <= MAX_REASONABLE_COLD_KEY_INDEX;
}
#endif // POOL_OPERATOR_APP

// ChainType

bool bip44_containsChainType(const bip44_path_t* pathSpec)
Expand Down Expand Up @@ -142,7 +178,7 @@ bool bip44_hasReasonableAddress(const bip44_path_t* pathSpec)
// path is valid as the spending path in all addresses except REWARD
bool bip44_isValidAddressPath(const bip44_path_t* pathSpec)
{
return bip44_hasValidCardanoPrefix(pathSpec) &&
return bip44_hasValidCardanoWalletPrefix(pathSpec) &&
bip44_hasValidChainTypeForAddress(pathSpec) &&
bip44_containsAddress(pathSpec);
}
Expand All @@ -160,7 +196,20 @@ bool bip44_isValidStakingKeyPath(const bip44_path_t* pathSpec)
return (bip44_getAddressValue(pathSpec) == 0);
}

// Futher
#ifdef POOL_OPERATOR_APP
bool bip44_isValidPoolColdKeyPath(const bip44_path_t* pathSpec)
{
#define CHECK(cond) if (!(cond)) return false
CHECK(pathSpec->length == POOL_COLD_KEY_PATH_LENGTH);
CHECK(pathSpec->path[BIP44_I_PURPOSE] == (PURPOSE_POOL_COLD_KEY | HARDENED_BIP32));
CHECK(pathSpec->path[BIP44_I_COIN_TYPE] == (ADA_COIN_TYPE | HARDENED_BIP32));
CHECK(pathSpec->path[BIP44_I_POOL_COLD_KEY_USECASE] == 0 + HARDENED_BIP32);
CHECK(pathSpec->path[BIP44_I_POOL_COLD_KEY] >= HARDENED_BIP32);
return true;
#undef CHECK
}
#endif // POOL_OPERATOR_APP

bool bip44_containsMoreThanAddress(const bip44_path_t* pathSpec)
{
return (pathSpec->length > BIP44_I_ADDRESS + 1);
Expand Down
17 changes: 16 additions & 1 deletion src/bip44.h
Expand Up @@ -16,7 +16,13 @@ typedef struct {

static const uint32_t PURPOSE_BYRON = 44;
static const uint32_t PURPOSE_SHELLEY = 1852;

#ifdef POOL_OPERATOR_APP
static const uint32_t PURPOSE_POOL_COLD_KEY = 1853;
#endif // POOL_OPERATOR_APP

static const uint32_t ADA_COIN_TYPE = 1815;
static const uint32_t POOL_COLD_KEY_PATH_LENGTH = 4;

static const uint32_t HARDENED_BIP32 = ((uint32_t) 1 << 31);

Expand All @@ -33,12 +39,16 @@ enum {
BIP44_I_CHAIN = 3,
BIP44_I_ADDRESS = 4,
BIP44_I_REST = 5,

// cold key derivation path specific enums
BIP44_I_POOL_COLD_KEY_USECASE = 2,
BIP44_I_POOL_COLD_KEY = 3,
};


bool bip44_hasByronPrefix(const bip44_path_t* pathSpec);
bool bip44_hasShelleyPrefix(const bip44_path_t* pathSpec);
bool bip44_hasValidCardanoPrefix(const bip44_path_t* pathSpec);
bool bip44_hasValidCardanoWalletPrefix(const bip44_path_t* pathSpec);

bool bip44_containsAccount(const bip44_path_t* pathSpec);
uint32_t bip44_getAccount(const bip44_path_t* pathSpec);
Expand All @@ -55,6 +65,11 @@ bool bip44_isValidStakingKeyPath(const bip44_path_t* pathSpec);

bool bip44_containsMoreThanAddress(const bip44_path_t* pathSpec);

#ifdef POOL_OPERATOR_APP
bool bip44_isValidPoolColdKeyPath(const bip44_path_t* pathSpec);
bool bip44_hasReasonablePoolColdKeyIndex(const bip44_path_t* pathSpec);
#endif // POOL_OPERATOR_APP

bool isHardened(uint32_t value);
uint32_t unharden(uint32_t value);

Expand Down
110 changes: 110 additions & 0 deletions src/getPoolColdPublicKey.c
@@ -0,0 +1,110 @@
#ifdef POOL_OPERATOR_APP

#include "common.h"

#include "getPoolColdPublicKey.h"
#include "keyDerivation.h"
#include "endian.h"
#include "state.h"
#include "uiHelpers.h"
#include "uiScreens.h"
#include "securityPolicy.h"

static ins_get_pool_cold_pubkey_context_t* ctx = &(instructionState.getPoolColdPublicKeyContext);


static int16_t RESPONSE_READY_MAGIC = 31567;

// forward declaration
static void getPoolColdPublicKey_ui_runStep();
enum {
UI_STEP_WARNING = 100,
UI_STEP_DISPLAY_PATH,
UI_STEP_CONFIRM,
UI_STEP_RESPOND,
UI_STEP_INVALID,
};

void getPoolColdPublicKey_handleAPDU(
uint8_t p1,
uint8_t p2,
uint8_t *wireDataBuffer,
size_t wireDataSize,
bool isNewCall
)
{
// Initialize state
if (isNewCall) {
explicit_bzero(ctx, SIZEOF(*ctx));
}
ctx->responseReadyMagic = 0;

// Validate params
VALIDATE(p1 == P1_UNUSED, ERR_INVALID_REQUEST_PARAMETERS);
VALIDATE(p2 == P2_UNUSED, ERR_INVALID_REQUEST_PARAMETERS);

// Parse wire
size_t parsedSize = bip44_parseFromWire(&ctx->pathSpec, wireDataBuffer, wireDataSize);

if (parsedSize != wireDataSize) {
THROW(ERR_INVALID_DATA);
}

// Check security policy
security_policy_t policy = policyForGetPoolColdPublicKey(&ctx->pathSpec);
ENSURE_NOT_DENIED(policy);

// Calculation
deriveExtendedPublicKey(
&ctx->pathSpec,
&ctx->extPoolColdPubKey
);
ctx->responseReadyMagic = RESPONSE_READY_MAGIC;

switch (policy) {
# define CASE(policy, step) case policy: {ctx->ui_step = step; break;}
CASE(POLICY_PROMPT_WARN_UNUSUAL, UI_STEP_WARNING);
CASE(POLICY_PROMPT_BEFORE_RESPONSE, UI_STEP_DISPLAY_PATH);
CASE(POLICY_ALLOW_WITHOUT_PROMPT, UI_STEP_RESPOND);
# undef CASE
default:
ASSERT(false);
}
getPoolColdPublicKey_ui_runStep();
}

static void getPoolColdPublicKey_ui_runStep()
{
ui_callback_fn_t* this_fn = getPoolColdPublicKey_ui_runStep;

UI_STEP_BEGIN(ctx->ui_step);

UI_STEP(UI_STEP_WARNING) {
ui_displayPaginatedText(
"Unusual request",
"Proceed with care",
this_fn
);
}
UI_STEP(UI_STEP_DISPLAY_PATH) {
ui_displayPathScreen("Export cold public key", &ctx->pathSpec, this_fn);
}
UI_STEP(UI_STEP_CONFIRM) {
ui_displayPrompt(
"Confirm export",
"cold public key?",
this_fn,
respond_with_user_reject
);
}
UI_STEP(UI_STEP_RESPOND) {
ASSERT(ctx->responseReadyMagic == RESPONSE_READY_MAGIC);

io_send_buf(SUCCESS, (uint8_t*) &ctx->extPoolColdPubKey, SIZEOF(ctx->extPoolColdPubKey));
ui_idle();

}
UI_STEP_END(UI_STEP_INVALID);
}

#endif // POOL_OPERATOR_APP
22 changes: 22 additions & 0 deletions src/getPoolColdPublicKey.h
@@ -0,0 +1,22 @@
#ifdef POOL_OPERATOR_APP

#ifndef H_CARDANO_APP_GET_COLD_PUBLIC_KEY
#define H_CARDANO_APP_GET_COLD_PUBLIC_KEY

#include "common.h"
#include "handlers.h"
#include "bip44.h"
#include "keyDerivation.h"

handler_fn_t getPoolColdPublicKey_handleAPDU;

typedef struct {
int16_t responseReadyMagic;
bip44_path_t pathSpec;
extendedPublicKey_t extPoolColdPubKey;
int ui_step;
} ins_get_pool_cold_pubkey_context_t;

#endif // H_CARDANO_APP_GET_COLD_PUBLIC_KEY

#endif // POOL_OPERATOR_APP
7 changes: 6 additions & 1 deletion src/handlers.c
Expand Up @@ -7,6 +7,7 @@
#include "getVersion.h"
#include "getSerial.h"
#include "getPublicKeys.h"
#include "getPoolColdPublicKey.h"
#include "runTests.h"
#include "errors.h"
#include "deriveAddress.h"
Expand All @@ -27,7 +28,11 @@ handler_fn_t* lookupHandler(uint8_t ins)
CASE(0x10, getPublicKeys_handleAPDU);
CASE(0x11, deriveAddress_handleAPDU);

// 0x2* - signing-transaction related
#ifdef POOL_OPERATOR_APP
CASE(0x12, getPoolColdPublicKey_handleAPDU);
#endif // POOL_OPERATOR_APP

// 0x2* - signing related
CASE(0x21, signTx_handleAPDU);

#ifdef DEVEL
Expand Down
2 changes: 1 addition & 1 deletion src/keyDerivation.c
Expand Up @@ -18,7 +18,7 @@ void derivePrivateKey(
privateKey_t* privateKey
)
{
if (!bip44_hasValidCardanoPrefix(pathSpec)) {
if (!bip44_hasValidCardanoPrefix(pathSpec) && !bip44_isValidPoolColdKeyPath(pathSpec)) {
THROW(ERR_INVALID_BIP44_PATH);
}
// Sanity check
Expand Down
10 changes: 10 additions & 0 deletions src/securityPolicy.c
Expand Up @@ -149,6 +149,16 @@ security_policy_t policyForGetExtendedPublicKeyBulkExport(const bip44_path_t* pa
ALLOW();
}

#ifdef POOL_OPERATOR_APP
security_policy_t policyForGetPoolColdPublicKey(const bip44_path_t* pathSpec)
{
DENY_UNLESS(bip44_isValidPoolColdKeyPath(pathSpec));
WARN_UNLESS(bip44_hasReasonablePoolColdKeyIndex(pathSpec));

PROMPT_IF(true);
}
#endif // POOL_OPERATOR_APP

// Derive address and return it to the host
security_policy_t policyForReturnDeriveAddress(const addressParams_t* addressParams)
{
Expand Down
5 changes: 5 additions & 0 deletions src/securityPolicy.h
Expand Up @@ -13,6 +13,9 @@ security_policy_t policyForGetPublicKeysInit(size_t numPaths);
security_policy_t policyForGetExtendedPublicKey(const bip44_path_t* pathSpec);
security_policy_t policyForGetExtendedPublicKeyBulkExport(const bip44_path_t* pathSpec);

#ifdef POOL_OPERATOR_APP
security_policy_t policyForGetPoolColdPublicKey(const bip44_path_t* pathSpec);
#endif // POOL_OPERATOR_APP

security_policy_t policyForShowDeriveAddress(const addressParams_t* addressParams);
security_policy_t policyForReturnDeriveAddress(const addressParams_t* addressParams);
Expand Down Expand Up @@ -104,4 +107,6 @@ bool is_tx_network_verifiable(
uint16_t numWithdrawals
);

security_policy_t policyForGetPoolColdPublicKey(const bip44_path_t* pathSpec);

#endif // H_CARDANO_APP_SECURITY_POLICY
2 changes: 2 additions & 0 deletions src/state.h
Expand Up @@ -4,12 +4,14 @@
#include "getVersion.h"
#include "getPublicKeys.h"
#include "deriveAddress.h"
#include "getPoolColdPublicKey.h"
#include "signTx.h"


typedef union {
// Here should go states of all instructions
ins_get_keys_context_t getKeysContext;
ins_get_pool_cold_pubkey_context_t getPoolColdPublicKeyContext;
ins_derive_address_context_t deriveAddressContext;
ins_sign_tx_context_t signTxContext;
} instructionState_t;
Expand Down

0 comments on commit 913b823

Please sign in to comment.