// /* // * This file is part of the Trezor project, https://trezor.io/ // * // * Copyright (C) 2014 Pavol Rusnak // * // * This library is free software: you can redistribute it and/or modify // * it under the terms of the GNU Lesser General Public License as published by // * the Free Software Foundation, either version 3 of the License, or // * (at your option) any later version. // * // * This library is distributed in the hope that it will be useful, // * but WITHOUT ANY WARRANTY; without even the implied warranty of // * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // * GNU Lesser General Public License for more details. // * // * You should have received a copy of the GNU Lesser General Public License // * along with this library. If not, see . // */ // #include "crypto.h" // #include // #include "../crypto/address.h" // #include "../crypto/aes/aes.h" // #include "../crypto/base58.h" // #include "../crypto/bip32.h" // #include "coins.h" // #include "../crypto/curves.h" // #include "../crypto/hmac.h" // //#include "layout.h" // #include "../crypto/pbkdf2.h" // #include "../crypto/secp256k1.h" // #include "../crypto/segwit_addr.h" // #include "../crypto/sha2.h" // #if !BITCOIN_ONLY // #include "cash_addr.h" // #endif // uint32_t ser_length(uint32_t len, uint8_t *out) { // if (len < 253) { // out[0] = len & 0xFF; // return 1; // } // if (len < 0x10000) { // out[0] = 253; // out[1] = len & 0xFF; // out[2] = (len >> 8) & 0xFF; // return 3; // } // out[0] = 254; // out[1] = len & 0xFF; // out[2] = (len >> 8) & 0xFF; // out[3] = (len >> 16) & 0xFF; // out[4] = (len >> 24) & 0xFF; // return 5; // } // uint32_t ser_length_hash(Hasher *hasher, uint32_t len) { // if (len < 253) { // hasher_Update(hasher, (const uint8_t *)&len, 1); // return 1; // } // if (len < 0x10000) { // uint8_t d = 253; // hasher_Update(hasher, &d, 1); // hasher_Update(hasher, (const uint8_t *)&len, 2); // return 3; // } // uint8_t d = 254; // hasher_Update(hasher, &d, 1); // hasher_Update(hasher, (const uint8_t *)&len, 4); // return 5; // } // uint32_t deser_length(const uint8_t *in, uint32_t *out) { // if (in[0] < 253) { // *out = in[0]; // return 1; // } // if (in[0] == 253) { // *out = in[1] + (in[2] << 8); // return 1 + 2; // } // if (in[0] == 254) { // *out = in[1] + (in[2] << 8) + (in[3] << 16) + ((uint32_t)in[4] << 24); // return 1 + 4; // } // *out = 0; // ignore 64 bit // return 1 + 8; // } // int sshMessageSign(HDNode *node, const uint8_t *message, size_t message_len, // uint8_t *signature) { // signature[0] = 0; // prefix: pad with zero, so all signatures are 65 bytes // return hdnode_sign(node, message, message_len, HASHER_SHA2, signature + 1, // NULL, NULL); // } // int gpgMessageSign(HDNode *node, const uint8_t *message, size_t message_len, // uint8_t *signature) { // signature[0] = 0; // prefix: pad with zero, so all signatures are 65 bytes // const curve_info *ed25519_curve_info = get_curve_by_name(ED25519_NAME); // if (ed25519_curve_info && node->curve == ed25519_curve_info) { // // GPG supports variable size digest for Ed25519 signatures // return hdnode_sign(node, message, message_len, 0, signature + 1, NULL, // NULL); // } else { // // Ensure 256-bit digest before proceeding // if (message_len != 32) { // return 1; // } // return hdnode_sign_digest(node, message, signature + 1, NULL, NULL); // } // } // int signifyMessageSign(HDNode *node, const uint8_t *message, size_t message_len, // uint8_t *signature) { // signature[0] = 0; // prefix: pad with zero, so all signatures are 65 bytes // const curve_info *ed25519_curve_info = get_curve_by_name(ED25519_NAME); // // only ed25519 is supported // if (!ed25519_curve_info || node->curve != ed25519_curve_info) { // return 1; // } // return hdnode_sign(node, message, message_len, 0, signature + 1, NULL, NULL); // } // static void cryptoMessageHash(const CoinInfo *coin, const uint8_t *message, // size_t message_len, // uint8_t hash[HASHER_DIGEST_LENGTH]) { // Hasher hasher = {0}; // hasher_Init(&hasher, coin->curve->hasher_sign); // hasher_Update(&hasher, (const uint8_t *)coin->signed_message_header, // strlen(coin->signed_message_header)); // uint8_t varint[5] = {0}; // uint32_t l = ser_length(message_len, varint); // hasher_Update(&hasher, varint, l); // hasher_Update(&hasher, message, message_len); // hasher_Final(&hasher, hash); // } // int cryptoMessageSign(const CoinInfo *coin, HDNode *node, // InputScriptType script_type, bool no_script_type, // const uint8_t *message, size_t message_len, // uint8_t *signature) { // uint8_t script_type_info = 0; // switch (script_type) { // case InputScriptType_SPENDADDRESS: // // p2pkh // script_type_info = 0; // break; // case InputScriptType_SPENDP2SHWITNESS: // // segwit-in-p2sh // script_type_info = 4; // break; // case InputScriptType_SPENDWITNESS: // // segwit // script_type_info = 8; // break; // default: // // unsupported script type // return 1; // } // if (no_script_type) { // script_type_info = 0; // } // uint8_t hash[HASHER_DIGEST_LENGTH] = {0}; // cryptoMessageHash(coin, message, message_len, hash); // uint8_t pby = 0; // int result = hdnode_sign_digest(node, hash, signature + 1, &pby, NULL); // if (result == 0) { // signature[0] = 31 + pby + script_type_info; // } // return result; // } // // Determines the script type from a non-multisig address. // static InputScriptType address_to_script_type(const CoinInfo *coin, // const char *address) { // uint8_t addr_raw[MAX_ADDR_RAW_SIZE] = {0}; // size_t addr_raw_len = 0; // // Native SegWit // if (coin->bech32_prefix) { // int witver = 0; // if (segwit_addr_decode(&witver, addr_raw, &addr_raw_len, // coin->bech32_prefix, address)) { // switch (witver) { // case 0: // return InputScriptType_SPENDWITNESS; // case 1: // return InputScriptType_SPENDTAPROOT; // default: // return InputScriptType_EXTERNAL; // unknown script type // } // } // } // #if !BITCOIN_ONLY // if (coin->cashaddr_prefix && // cash_addr_decode(addr_raw, &addr_raw_len, coin->cashaddr_prefix, // address)) { // return InputScriptType_SPENDADDRESS; // } // #endif // addr_raw_len = base58_decode_check(address, coin->curve->hasher_base58, // addr_raw, sizeof(addr_raw)); // // P2PKH // if (addr_raw_len > address_prefix_bytes_len(coin->address_type) && // address_check_prefix(addr_raw, coin->address_type)) { // return InputScriptType_SPENDADDRESS; // } // // P2SH // if (addr_raw_len > address_prefix_bytes_len(coin->address_type_p2sh) && // address_check_prefix(addr_raw, coin->address_type_p2sh)) { // return InputScriptType_SPENDP2SHWITNESS; // } // return InputScriptType_EXTERNAL; // unknown script type // } // int cryptoMessageVerify(const CoinInfo *coin, const uint8_t *message, // size_t message_len, const char *address, // const uint8_t *signature) { // // check if the address is correct // InputScriptType script_type = address_to_script_type(coin, address); // if (script_type == InputScriptType_EXTERNAL) { // return 1; // invalid address // } // if (signature[0] >= 27 && signature[0] <= 34) { // // p2pkh or no script type provided // // use the script type from the address // } else if (signature[0] >= 35 && signature[0] <= 38) { // // segwit-in-p2sh // if (script_type != InputScriptType_SPENDP2SHWITNESS) { // return 2; // script type mismatch // } // } else if (signature[0] >= 39 && signature[0] <= 42) { // // segwit // if (script_type != InputScriptType_SPENDWITNESS) { // return 2; // script type mismatch // } // } else { // return 3; // invalid signature prefix // } // uint8_t hash[HASHER_DIGEST_LENGTH] = {0}; // cryptoMessageHash(coin, message, message_len, hash); // uint8_t recid = (signature[0] - 27) % 4; // bool compressed = signature[0] >= 31; // // check if signature verifies the digest and recover the public key // uint8_t pubkey[65] = {0}; // if (ecdsa_recover_pub_from_sig(coin->curve->params, pubkey, signature + 1, // hash, recid) != 0) { // return 4; // invalid signature data // } // // convert public key to compressed pubkey if necessary // if (compressed) { // pubkey[0] = 0x02 | (pubkey[64] & 1); // } // uint8_t addr_raw[MAX_ADDR_RAW_SIZE] = {0}; // uint8_t recovered_raw[MAX_ADDR_RAW_SIZE] = {0}; // if (script_type == InputScriptType_SPENDADDRESS) { // // p2pkh // size_t len = 0; // #if !BITCOIN_ONLY // if (coin->cashaddr_prefix) { // if (!cash_addr_decode(addr_raw, &len, coin->cashaddr_prefix, address)) { // return 1; // invalid address // } // } else // #endif // { // len = base58_decode_check(address, coin->curve->hasher_base58, addr_raw, // MAX_ADDR_RAW_SIZE); // } // ecdsa_get_address_raw(pubkey, coin->address_type, // coin->curve->hasher_pubkey, recovered_raw); // if (memcmp(recovered_raw, addr_raw, len) != 0 || // len != address_prefix_bytes_len(coin->address_type) + 20) { // return 5; // signature does not match address and message // } // } else if (script_type == InputScriptType_SPENDP2SHWITNESS) { // // segwit-in-p2sh // size_t len = base58_decode_check(address, coin->curve->hasher_base58, // addr_raw, MAX_ADDR_RAW_SIZE); // ecdsa_get_address_segwit_p2sh_raw(pubkey, coin->address_type_p2sh, // coin->curve->hasher_pubkey, // recovered_raw); // if (memcmp(recovered_raw, addr_raw, len) != 0 || // len != address_prefix_bytes_len(coin->address_type_p2sh) + 20) { // return 5; // signature does not match address and message // } // } else if (script_type == InputScriptType_SPENDWITNESS) { // // segwit // int witver = 0; // size_t len = 0; // if (!coin->bech32_prefix || // !segwit_addr_decode(&witver, recovered_raw, &len, coin->bech32_prefix, // address)) { // return 1; // invalid address // } // ecdsa_get_pubkeyhash(pubkey, coin->curve->hasher_pubkey, addr_raw); // if (memcmp(recovered_raw, addr_raw, len) != 0 || witver != 0 || len != 20) { // return 5; // signature does not match address and message // } // } else { // return 1; // invalid address // } // return 0; // } // const HDNode *cryptoMultisigPubkey(const CoinInfo *coin, // const MultisigRedeemScriptType *multisig, // uint32_t index) { // const HDNodeType *node_ptr = NULL; // const uint32_t *address_n = NULL; // uint32_t address_n_count = 0; // if (multisig->nodes_count) { // use multisig->nodes // if (index >= multisig->nodes_count) { // return 0; // } // node_ptr = &(multisig->nodes[index]); // address_n = multisig->address_n; // address_n_count = multisig->address_n_count; // } else if (multisig->pubkeys_count) { // use multisig->pubkeys // if (index >= multisig->pubkeys_count) { // return 0; // } // node_ptr = &(multisig->pubkeys[index].node); // address_n = multisig->pubkeys[index].address_n; // address_n_count = multisig->pubkeys[index].address_n_count; // } else { // return 0; // } // if (node_ptr->chain_code.size != 32) return 0; // if (node_ptr->public_key.size != 33) return 0; // static HDNode node; // if (!hdnode_from_xpub(node_ptr->depth, node_ptr->child_num, // node_ptr->chain_code.bytes, node_ptr->public_key.bytes, // coin->curve_name, &node)) { // return 0; // } // //layoutProgressUpdate(true); // for (uint32_t i = 0; i < address_n_count; i++) { // if (!hdnode_public_ckd(&node, address_n[i])) { // return 0; // } // //layoutProgressUpdate(true); // } // return &node; // } // uint32_t cryptoMultisigPubkeyCount(const MultisigRedeemScriptType *multisig) { // return multisig->nodes_count ? multisig->nodes_count // : multisig->pubkeys_count; // } // int cryptoMultisigPubkeyIndex(const CoinInfo *coin, // const MultisigRedeemScriptType *multisig, // const uint8_t *pubkey) { // for (size_t i = 0; i < cryptoMultisigPubkeyCount(multisig); i++) { // const HDNode *pubnode = cryptoMultisigPubkey(coin, multisig, i); // if (pubnode && memcmp(pubnode->public_key, pubkey, 33) == 0) { // return i; // } // } // return -1; // } // int cryptoMultisigFingerprint(const MultisigRedeemScriptType *multisig, // uint8_t *hash) { // static const HDNodeType *pubnodes[15], *swap; // const uint32_t n = cryptoMultisigPubkeyCount(multisig); // if (n < 1 || n > 15) { // return 0; // } // if (multisig->m < 1 || multisig->m > 15) { // return 0; // } // for (uint32_t i = 0; i < n; i++) { // if (multisig->nodes_count) { // use multisig->nodes // pubnodes[i] = &(multisig->nodes[i]); // } else if (multisig->pubkeys_count) { // use multisig->pubkeys // pubnodes[i] = &(multisig->pubkeys[i].node); // } else { // return 0; // } // } // for (uint32_t i = 0; i < n; i++) { // if (pubnodes[i]->public_key.size != 33) return 0; // if (pubnodes[i]->chain_code.size != 32) return 0; // } // // minsort according to pubkey // for (uint32_t i = 0; i < n - 1; i++) { // for (uint32_t j = n - 1; j > i; j--) { // if (memcmp(pubnodes[i]->public_key.bytes, pubnodes[j]->public_key.bytes, // 33) > 0) { // swap = pubnodes[i]; // pubnodes[i] = pubnodes[j]; // pubnodes[j] = swap; // } // } // } // // hash sorted nodes // SHA256_CTX ctx = {0}; // sha256_Init(&ctx); // sha256_Update(&ctx, (const uint8_t *)&(multisig->m), sizeof(uint32_t)); // for (uint32_t i = 0; i < n; i++) { // sha256_Update(&ctx, (const uint8_t *)&(pubnodes[i]->depth), // sizeof(uint32_t)); // sha256_Update(&ctx, (const uint8_t *)&(pubnodes[i]->fingerprint), // sizeof(uint32_t)); // sha256_Update(&ctx, (const uint8_t *)&(pubnodes[i]->child_num), // sizeof(uint32_t)); // sha256_Update(&ctx, pubnodes[i]->chain_code.bytes, 32); // sha256_Update(&ctx, pubnodes[i]->public_key.bytes, 33); // } // sha256_Update(&ctx, (const uint8_t *)&n, sizeof(uint32_t)); // sha256_Final(&ctx, hash); // //layoutProgressUpdate(true); // return 1; // } // int cryptoIdentityFingerprint(const IdentityType *identity, uint8_t *hash) { // SHA256_CTX ctx = {0}; // sha256_Init(&ctx); // sha256_Update(&ctx, (const uint8_t *)&(identity->index), sizeof(uint32_t)); // if (identity->has_proto && identity->proto[0]) { // sha256_Update(&ctx, (const uint8_t *)(identity->proto), // strlen(identity->proto)); // sha256_Update(&ctx, (const uint8_t *)"://", 3); // } // if (identity->has_user && identity->user[0]) { // sha256_Update(&ctx, (const uint8_t *)(identity->user), // strlen(identity->user)); // sha256_Update(&ctx, (const uint8_t *)"@", 1); // } // if (identity->has_host && identity->host[0]) { // sha256_Update(&ctx, (const uint8_t *)(identity->host), // strlen(identity->host)); // } // if (identity->has_port && identity->port[0]) { // sha256_Update(&ctx, (const uint8_t *)":", 1); // sha256_Update(&ctx, (const uint8_t *)(identity->port), // strlen(identity->port)); // } // if (identity->has_path && identity->path[0]) { // sha256_Update(&ctx, (const uint8_t *)(identity->path), // strlen(identity->path)); // } // sha256_Final(&ctx, hash); // return 1; // } // static bool check_cointype(const CoinInfo *coin, uint32_t slip44, bool full) { // #if BITCOIN_ONLY // (void)full; // #else // if (!full) { // // Some wallets such as Electron-Cash (BCH) store coins on Bitcoin paths. // // We can allow spending these coins from Bitcoin paths if the coin has // // implemented strong replay protection via SIGHASH_FORKID. However, we // // cannot allow spending any testnet coins from Bitcoin paths, because // // otherwise an attacker could trick the user into spending BCH on a Bitcoin // // path by signing a seemingly harmless BCH Testnet transaction. // if (slip44 == SLIP44_BITCOIN && coin->has_fork_id && // coin->coin_type != SLIP44_TESTNET) { // return true; // } // } // #endif // return coin->coin_type == slip44; // } // bool coin_path_check(const CoinInfo *coin, InputScriptType script_type, // uint32_t address_n_count, const uint32_t *address_n, // bool has_multisig, bool full_check) { // // This function checks that the path is a recognized path for the given coin. // // Used by GetAddress to prevent ransom attacks where a user could be coerced // // to use an address with an unenumerable path and used by SignTx to ensure // // that a user cannot be coerced into signing a testnet transaction or a // // Litecoin transaction which in fact spends Bitcoin. If full_check is true, // // then this function also checks that the path fully matches the script type // // and coin type. This is used to determine whether a warning should be shown. // if (address_n_count == 0) { // return false; // } // bool valid = true; // // m/44' : BIP44 Legacy // // m / purpose' / coin_type' / account' / change / address_index // if (address_n[0] == PATH_HARDENED + 44) { // valid = valid && (address_n_count == 5); // valid = valid && check_cointype(coin, address_n[1], full_check); // valid = valid && (address_n[2] & PATH_HARDENED); // valid = valid && ((address_n[2] & PATH_UNHARDEN_MASK) <= PATH_MAX_ACCOUNT); // valid = valid && (address_n[3] <= PATH_MAX_CHANGE); // valid = valid && (address_n[4] <= PATH_MAX_ADDRESS_INDEX); // if (full_check) { // valid = valid && (script_type == InputScriptType_SPENDADDRESS); // valid = valid && (!has_multisig); // } // return valid; // } // if (address_n[0] == PATH_HARDENED + 45) { // if (address_n_count == 4) { // // m/45' - BIP45 Copay Abandoned Multisig P2SH // // m / purpose' / cosigner_index / change / address_index // // Patterns without a coin_type field must be treated as Bitcoin paths. // valid = valid && check_cointype(coin, SLIP44_BITCOIN, false); // valid = valid && (address_n[1] <= 100); // valid = valid && (address_n[2] <= PATH_MAX_CHANGE); // valid = valid && (address_n[3] <= PATH_MAX_ADDRESS_INDEX); // } else if (address_n_count == 5) { // // Unchained Capital compatibility pattern. Will be removed in the // // future. // // m / 45' / coin_type' / account' / [0-1000000] / address_index // valid = valid && check_cointype(coin, address_n[1], full_check); // valid = valid && (address_n[2] & PATH_HARDENED); // valid = // valid && ((address_n[2] & PATH_UNHARDEN_MASK) <= PATH_MAX_ACCOUNT); // valid = valid && (address_n[3] <= 1000000); // valid = valid && (address_n[4] <= PATH_MAX_ADDRESS_INDEX); // } else if (address_n_count == 6) { // // Unchained Capital compatibility pattern. Will be removed in the // // future. // // m/45'/coin_type'/account'/[0-1000000]/change/address_index // // m/45'/coin_type/account/[0-1000000]/change/address_index // valid = valid && // check_cointype(coin, PATH_HARDENED | address_n[1], full_check); // valid = valid && ((address_n[1] & PATH_HARDENED) == // (address_n[2] & PATH_HARDENED)); // valid = // valid && ((address_n[2] & PATH_UNHARDEN_MASK) <= PATH_MAX_ACCOUNT); // valid = valid && (address_n[3] <= 1000000); // valid = valid && (address_n[4] <= PATH_MAX_CHANGE); // valid = valid && (address_n[5] <= PATH_MAX_ADDRESS_INDEX); // } else { // return false; // } // if (full_check) { // valid = valid && (script_type == InputScriptType_SPENDADDRESS || // script_type == InputScriptType_SPENDMULTISIG); // valid = valid && has_multisig; // } // return valid; // } // if (address_n[0] == PATH_HARDENED + 48) { // valid = valid && (address_n_count == 5 || address_n_count == 6); // valid = valid && check_cointype(coin, address_n[1], full_check); // valid = valid && (address_n[2] & PATH_HARDENED); // valid = valid && ((address_n[2] & PATH_UNHARDEN_MASK) <= PATH_MAX_ACCOUNT); // if (address_n_count == 5) { // // [OBSOLETE] m/48' Copay Multisig P2SH // // m / purpose' / coin_type' / account' / change / address_index // // NOTE: this pattern is not recognized by trezor-core // valid = valid && (address_n[3] <= PATH_MAX_CHANGE); // valid = valid && (address_n[4] <= PATH_MAX_ADDRESS_INDEX); // if (full_check) { // valid = valid && has_multisig; // valid = valid && (script_type == InputScriptType_SPENDMULTISIG); // } // } else if (address_n_count == 6) { // // BIP-48: // // m / purpose' / coin_type' / account' / type' / change / address_index // valid = valid && (address_n[3] & PATH_HARDENED); // uint32_t type = address_n[3] & PATH_UNHARDEN_MASK; // valid = valid && (type <= 2); // valid = valid && (type == 0 || coin->has_segwit); // valid = valid && (address_n[4] <= PATH_MAX_CHANGE); // valid = valid && (address_n[5] <= PATH_MAX_ADDRESS_INDEX); // if (full_check) { // valid = valid && has_multisig; // switch (type) { // case 0: // valid = valid && (script_type == InputScriptType_SPENDMULTISIG || // script_type == InputScriptType_SPENDADDRESS); // break; // case 1: // valid = valid && (script_type == InputScriptType_SPENDP2SHWITNESS); // break; // case 2: // valid = valid && (script_type == InputScriptType_SPENDWITNESS); // break; // default: // return false; // } // } // } else { // return false; // } // return valid; // } // // m/49' : BIP49 SegWit // // m / purpose' / coin_type' / account' / change / address_index // if (address_n[0] == PATH_HARDENED + 49) { // valid = valid && coin->has_segwit; // valid = valid && (address_n_count == 5); // valid = valid && check_cointype(coin, address_n[1], full_check); // valid = valid && (address_n[2] & PATH_HARDENED); // valid = valid && ((address_n[2] & PATH_UNHARDEN_MASK) <= PATH_MAX_ACCOUNT); // valid = valid && (address_n[3] <= PATH_MAX_CHANGE); // valid = valid && (address_n[4] <= PATH_MAX_ADDRESS_INDEX); // if (full_check) { // valid = valid && (script_type == InputScriptType_SPENDP2SHWITNESS); // } // return valid; // } // // m/84' : BIP84 Native SegWit // // m / purpose' / coin_type' / account' / change / address_index // if (address_n[0] == PATH_HARDENED + 84) { // valid = valid && coin->has_segwit; // valid = valid && (coin->bech32_prefix != NULL); // valid = valid && (address_n_count == 5); // valid = valid && check_cointype(coin, address_n[1], full_check); // valid = valid && (address_n[2] & PATH_HARDENED); // valid = valid && ((address_n[2] & PATH_UNHARDEN_MASK) <= PATH_MAX_ACCOUNT); // valid = valid && (address_n[3] <= PATH_MAX_CHANGE); // valid = valid && (address_n[4] <= PATH_MAX_ADDRESS_INDEX); // if (full_check) { // valid = valid && (script_type == InputScriptType_SPENDWITNESS); // } // return valid; // } // // m/86' : BIP86 Taproot // // m / purpose' / coin_type' / account' / change / address_index // if (address_n[0] == PATH_HARDENED + 86) { // valid = valid && coin->has_taproot; // valid = valid && (coin->bech32_prefix != NULL); // valid = valid && (address_n_count == 5); // valid = valid && check_cointype(coin, address_n[1], full_check); // valid = valid && (address_n[2] & PATH_HARDENED); // valid = valid && ((address_n[2] & PATH_UNHARDEN_MASK) <= PATH_MAX_ACCOUNT); // valid = valid && (address_n[3] <= PATH_MAX_CHANGE); // valid = valid && (address_n[4] <= PATH_MAX_ADDRESS_INDEX); // if (full_check) { // // we do not support Multisig with Taproot yet // valid = valid && !has_multisig; // valid = valid && (script_type == InputScriptType_SPENDTAPROOT); // } // return valid; // } // // Green Address compatibility pattern. Will be removed in the future. // // m / [1,4] / address_index // if (address_n[0] == 1 || address_n[0] == 4) { // valid = valid && (coin->coin_type == SLIP44_BITCOIN); // valid = valid && (address_n_count == 2); // valid = valid && (address_n[1] <= PATH_MAX_ADDRESS_INDEX); // if (full_check) { // valid = valid && (script_type != InputScriptType_SPENDTAPROOT); // } // return valid; // } // // Green Address compatibility pattern. Will be removed in the future. // // m / 3' / [1-100]' / [1,4] / address_index // if (address_n[0] == PATH_HARDENED + 3) { // valid = valid && (coin->coin_type == SLIP44_BITCOIN); // valid = valid && (address_n_count == 4); // valid = valid && (address_n[1] & PATH_HARDENED); // valid = valid && ((address_n[1] & PATH_UNHARDEN_MASK) <= 100); // valid = valid && (address_n[2] == 1 || address_n[2] == 4); // valid = valid && (address_n[3] <= PATH_MAX_ADDRESS_INDEX); // if (full_check) { // valid = valid && (script_type != InputScriptType_SPENDTAPROOT); // } // return valid; // } // // Green Address compatibility patterns. Will be removed in the future. // // m / 1195487518 // // m / 1195487518 / 6 / address_index // if (address_n[0] == 1195487518) { // valid = valid && (coin->coin_type == SLIP44_BITCOIN); // if (address_n_count == 3) { // valid = valid && (address_n[1] == 6); // valid = valid && (address_n[2] <= PATH_MAX_ADDRESS_INDEX); // } else if (address_n_count != 1) { // return false; // } // if (full_check) { // return false; // } // return valid; // } // // Casa compatibility pattern. Will be removed in the future. // // m / 49 / coin_type / account / change / address_index // if (address_n[0] == 49) { // valid = valid && (address_n_count == 5); // valid = // valid && check_cointype(coin, PATH_HARDENED | address_n[1], full_check); // valid = valid && ((address_n[1] & PATH_HARDENED) == 0); // valid = valid && (address_n[2] <= PATH_MAX_ACCOUNT); // valid = valid && (address_n[3] <= PATH_MAX_CHANGE); // valid = valid && (address_n[4] <= PATH_MAX_ADDRESS_INDEX); // if (full_check) { // valid = valid && (script_type == InputScriptType_SPENDP2SHWITNESS); // } // return valid; // } // // unknown path // return false; // }