From 537cadc192afc778a261487ff5f4ea98c2a7a7f1 Mon Sep 17 00:00:00 2001 From: Jan Mazak Date: Fri, 6 Oct 2023 00:56:20 +0200 Subject: [PATCH] conway: new key derivation schemas --- src/bip44.c | 65 ++++++++++++++++++++++++++++++++++++++++++-- src/bip44.h | 16 +++++++++++ src/securityPolicy.c | 38 ++++++++++++++++++++++++-- 3 files changed, 113 insertions(+), 6 deletions(-) diff --git a/src/bip44.c b/src/bip44.c index 883a98e9..a4fc59fc 100644 --- a/src/bip44.c +++ b/src/bip44.c @@ -4,9 +4,12 @@ #include "hash.h" #include "keyDerivation.h" -#define CARDANO_CHAIN_EXTERNAL 0 -#define CARDANO_CHAIN_INTERNAL 1 -#define CARDANO_CHAIN_STAKING_KEY 2 +static const uint32_t CARDANO_CHAIN_EXTERNAL = 0; +static const uint32_t CARDANO_CHAIN_INTERNAL = 1; +static const uint32_t CARDANO_CHAIN_STAKING_KEY = 2; +static const uint32_t CARDANO_CHAIN_DREP_KEY = 3; +static const uint32_t CARDANO_CHAIN_COMMITTEE_COLD_KEY = 4; +static const uint32_t CARDANO_CHAIN_COMMITTEE_HOT_KEY = 5; static const uint32_t MAX_REASONABLE_ACCOUNT = 100; static const uint32_t MAX_REASONABLE_ADDRESS = 1000000; @@ -252,6 +255,42 @@ bool bip44_isMultidelegationStakingKeyPath(const bip44_path_t* pathSpec) && (bip44_getAddressValue(pathSpec) > 0); } +bool bip44_isDRepKeyPath(const bip44_path_t* pathSpec) +{ +#define CHECK(cond) if (!(cond)) return false + CHECK(pathSpec->length == BIP44_I_ADDRESS + 1); + CHECK(bip44_hasShelleyPrefix(pathSpec)); + CHECK(isHardened(bip44_getAccount(pathSpec))); + CHECK(bip44_getChainTypeValue(pathSpec) == CARDANO_CHAIN_DREP_KEY); + CHECK(bip44_getAddressValue(pathSpec) == 0); // TODO allow other values and check for hardened only? + return true; +#undef CHECK +} + +bool bip44_isCommitteeColdKeyPath(const bip44_path_t* pathSpec) +{ +#define CHECK(cond) if (!(cond)) return false + CHECK(pathSpec->length == BIP44_I_ADDRESS + 1); + CHECK(bip44_hasShelleyPrefix(pathSpec)); + CHECK(isHardened(bip44_getAccount(pathSpec))); + CHECK(bip44_getChainTypeValue(pathSpec) == CARDANO_CHAIN_COMMITTEE_COLD_KEY); + CHECK(bip44_getAddressValue(pathSpec) == 0); // TODO allow other values and check for hardened only? + return true; +#undef CHECK +} + +bool bip44_isCommitteeHotKeyPath(const bip44_path_t* pathSpec) +{ +#define CHECK(cond) if (!(cond)) return false + CHECK(pathSpec->length == BIP44_I_ADDRESS + 1); + CHECK(bip44_hasShelleyPrefix(pathSpec)); + CHECK(isHardened(bip44_getAccount(pathSpec))); + CHECK(bip44_getChainTypeValue(pathSpec) == CARDANO_CHAIN_COMMITTEE_HOT_KEY); + CHECK(bip44_getAddressValue(pathSpec) == 0); // TODO allow other values and check for hardened only? + return true; +#undef CHECK +} + bool bip44_isMintKeyPath(const bip44_path_t* pathSpec) { #define CHECK(cond) if (!(cond)) return false @@ -367,6 +406,21 @@ static bip44_path_type_t bip44_classifyOrdinaryWalletPath(const bip44_path_t* pa PATH_ORDINARY_STAKING_KEY : PATH_INVALID; + case CARDANO_CHAIN_DREP_KEY: + return bip44_isDRepKeyPath(pathSpec) ? + PATH_DREP_KEY : + PATH_INVALID; + + case CARDANO_CHAIN_COMMITTEE_COLD_KEY: + return bip44_isCommitteeColdKeyPath(pathSpec) ? + PATH_COMMITTEE_COLD_KEY : + PATH_INVALID; + + case CARDANO_CHAIN_COMMITTEE_HOT_KEY: + return bip44_isCommitteeHotKeyPath(pathSpec) ? + PATH_COMMITTEE_HOT_KEY : + PATH_INVALID; + default: return PATH_INVALID; } @@ -492,6 +546,11 @@ bool bip44_isPathReasonable(const bip44_path_t* pathSpec) case PATH_MULTISIG_STAKING_KEY: return bip44_hasReasonableAccount(pathSpec) && bip44_hasReasonableAddress(pathSpec); + case PATH_DREP_KEY: + case PATH_COMMITTEE_COLD_KEY: + case PATH_COMMITTEE_HOT_KEY: + return bip44_hasReasonableAccount(pathSpec); + case PATH_MINT_KEY: return bip44_hasReasonableMintPolicy(pathSpec); diff --git a/src/bip44.h b/src/bip44.h index f8629356..6047e7db 100644 --- a/src/bip44.h +++ b/src/bip44.h @@ -79,12 +79,17 @@ bool bip44_isOrdinaryStakingKeyPath(const bip44_path_t* pathSpec); bool bip44_isMultisigStakingKeyPath(const bip44_path_t* pathSpec); bool bip44_isMultidelegationStakingKeyPath(const bip44_path_t* pathSpec); +bool bip44_isDRepKeyPath(const bip44_path_t* pathSpec); +bool bip44_isCommitteeColdKeyPath(const bip44_path_t* pathSpec); +bool bip44_isCommitteeHotKeyPath(const bip44_path_t* pathSpec); + bool bip44_isMintKeyPath(const bip44_path_t* pathSpec); bool bip44_isPoolColdKeyPath(const bip44_path_t* pathSpec); bool bip44_isCVoteKeyPath(const bip44_path_t* pathSpec); + size_t bip44_printToStr(const bip44_path_t*, char* out, size_t outSize); @@ -101,6 +106,17 @@ typedef enum { PATH_ORDINARY_STAKING_KEY, PATH_MULTISIG_STAKING_KEY, + // DRep key + // m / 1852' / 1815' / account' / 3 / address_index + PATH_DREP_KEY, + + // constitutional committee hot key TODO not approved yet https://github.com/Ryun1/CIPs/blob/conway-keys/CIP-conway-keys/README.md + // m / 1852' / 1815' / account' / 4 / address_index + PATH_COMMITTEE_COLD_KEY, + // constitutional committee cold key TODO not approved yet + // m / 1852' / 1815' / account' / 5 / address_index + PATH_COMMITTEE_HOT_KEY, + // native token minting/burning PATH_MINT_KEY, diff --git a/src/securityPolicy.c b/src/securityPolicy.c index ce55724c..1333a9f8 100644 --- a/src/securityPolicy.c +++ b/src/securityPolicy.c @@ -85,6 +85,10 @@ security_policy_t policyForDerivePrivateKey(const bip44_path_t* path) case PATH_MULTISIG_SPENDING_KEY: case PATH_MULTISIG_STAKING_KEY: + case PATH_DREP_KEY: + case PATH_COMMITTEE_COLD_KEY: + case PATH_COMMITTEE_HOT_KEY: + case PATH_MINT_KEY: case PATH_POOL_COLD_KEY: @@ -162,6 +166,9 @@ security_policy_t policyForGetExtendedPublicKey(const bip44_path_t* pathSpec) case PATH_ORDINARY_STAKING_KEY: case PATH_MULTISIG_SPENDING_KEY: case PATH_MULTISIG_STAKING_KEY: + case PATH_DREP_KEY: + case PATH_COMMITTEE_COLD_KEY: + case PATH_COMMITTEE_HOT_KEY: case PATH_CVOTE_KEY: WARN_UNLESS(bip44_isPathReasonable(pathSpec)); // ask for permission (it is unusual if client asks this instead of the account key) @@ -187,6 +194,9 @@ security_policy_t policyForGetExtendedPublicKeyBulkExport(const bip44_path_t* pa case PATH_MULTISIG_ACCOUNT: case PATH_MULTISIG_SPENDING_KEY: case PATH_MULTISIG_STAKING_KEY: + case PATH_DREP_KEY: + case PATH_COMMITTEE_COLD_KEY: + case PATH_COMMITTEE_HOT_KEY: case PATH_MINT_KEY: case PATH_CVOTE_ACCOUNT: case PATH_CVOTE_KEY: @@ -197,7 +207,7 @@ security_policy_t policyForGetExtendedPublicKeyBulkExport(const bip44_path_t* pa case PATH_POOL_COLD_KEY: WARN_UNLESS(bip44_isPathReasonable(pathSpec)); - // but ask for permission when pool cold key is requested + // but ask for permission PROMPT(); break; @@ -1391,15 +1401,30 @@ static inline security_policy_t _ordinaryWitnessPolicy(const bip44_path_t* path, switch (bip44_classifyPath(path)) { case PATH_ORDINARY_SPENDING_KEY: case PATH_ORDINARY_STAKING_KEY: + // ordinary key paths can be hidden if they are not unusual + // (the user saw all outputs not belonging to him, withdrawals and certificates, + // those belong to him in an ORDINARY txs thanks to + // keys being displayed by paths instead of hashes) DENY_IF(violatesSingleAccountOrStoreIt(path)); WARN_UNLESS(bip44_isPathReasonable(path)); SHOW_IF(app_mode_expert()); ALLOW(); break; + case PATH_DREP_KEY: + case PATH_COMMITTEE_COLD_KEY: + case PATH_COMMITTEE_HOT_KEY: + // these have to be shown because the tx might contain + // an action proposal that cannot be fully shown on the device + // TODO what about violation of single account policy? + DENY_IF(violatesSingleAccountOrStoreIt(path)); + WARN_UNLESS(bip44_isPathReasonable(path)); + SHOW(); + break; + case PATH_POOL_COLD_KEY: - // ordinary key paths and pool cold key paths can be hidden if they are not unusual - // (the user saw all outputs, withdrawals and pool certificates and they all belong to him) + // could be hidden perhaps, but it's safer to let the user to know + // the SW wallet wants to sign with the stake pool key WARN_UNLESS(bip44_isPathReasonable(path)); SHOW(); break; @@ -1439,6 +1464,7 @@ static inline security_policy_t _multisigWitnessPolicy(const bip44_path_t* path, default: // ordinary and pool cold keys forbidden + // DRep and committee keys forbidden DENY(); break; } @@ -1709,6 +1735,12 @@ static bool is_required_signer_allowed(bip44_path_t* path) case PATH_MULTISIG_STAKING_KEY: return true; + case PATH_DREP_KEY: + case PATH_COMMITTEE_COLD_KEY: + case PATH_COMMITTEE_HOT_KEY: + // no known use case, but also no reason to deny + return true; + case PATH_MINT_KEY: return true;