Skip to content

Commit 3b6d7e8

Browse files
authored
Adds ability to specify derivation when importing a key (#4406)
1 parent e130b06 commit 3b6d7e8

File tree

8 files changed

+217
-24
lines changed

8 files changed

+217
-24
lines changed

include/TrustWalletCore/TWStoredKey.h

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,17 @@ struct TWStoredKey* _Nullable TWStoredKeyImportPrivateKey(TWData* _Nonnull priva
5151
TW_EXPORT_STATIC_METHOD
5252
struct TWStoredKey* _Nullable TWStoredKeyImportPrivateKeyWithEncryption(TWData* _Nonnull privateKey, TWString* _Nonnull name, TWData* _Nonnull password, enum TWCoinType coin, enum TWStoredKeyEncryption encryption);
5353

54+
/// Imports a private key.
55+
///
56+
/// \param privateKey Non-null Block of data private key
57+
/// \param name The name of the stored key to import as a non-null string
58+
/// \param password Non-null block of data, password of the stored key
59+
/// \param coin the coin type
60+
/// \param encryption cipher encryption mode
61+
/// \param derivation derivation of the given coin type
62+
TW_EXPORT_STATIC_METHOD
63+
struct TWStoredKey* _Nullable TWStoredKeyImportPrivateKeyWithEncryptionAndDerivation(TWData* _Nonnull privateKey, TWString* _Nonnull name, TWData* _Nonnull password, enum TWCoinType coin, enum TWStoredKeyEncryption encryption, enum TWDerivation derivation);
64+
5465
/// Imports an encoded private key.
5566
///
5667
/// \param privateKey Non-null encoded private key
@@ -73,6 +84,19 @@ struct TWStoredKey* _Nullable TWStoredKeyImportPrivateKeyEncoded(TWString* _Nonn
7384
TW_EXPORT_STATIC_METHOD
7485
struct TWStoredKey* _Nullable TWStoredKeyImportPrivateKeyEncodedWithEncryption(TWString* _Nonnull privateKey, TWString* _Nonnull name, TWData* _Nonnull password, enum TWCoinType coin, enum TWStoredKeyEncryption encryption);
7586

87+
/// Imports an encoded private key.
88+
///
89+
/// \param privateKey Non-null encoded private key
90+
/// \param name The name of the stored key to import as a non-null string
91+
/// \param password Non-null block of data, password of the stored key
92+
/// \param coin the coin type
93+
/// \param encryption cipher encryption mode
94+
/// \param derivation derivation of the given coin type
95+
/// \note Returned object needs to be deleted with \TWStoredKeyDelete
96+
/// \return Nullptr if the key can't be imported, the stored key otherwise
97+
TW_EXPORT_STATIC_METHOD
98+
struct TWStoredKey* _Nullable TWStoredKeyImportPrivateKeyEncodedWithEncryptionAndDerivation(TWString* _Nonnull privateKey, TWString* _Nonnull name, TWData* _Nonnull password, enum TWCoinType coin, enum TWStoredKeyEncryption encryption, enum TWDerivation derivation);
99+
76100
/// Imports an HD wallet.
77101
///
78102
/// \param mnemonic Non-null bip39 mnemonic

src/Keystore/StoredKey.cpp

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -52,30 +52,44 @@ StoredKey StoredKey::createWithPrivateKey(const std::string& name, const Data& p
5252
return StoredKey(StoredKeyType::privateKey, name, password, privateKeyData, TWStoredKeyEncryptionLevelDefault, encryption);
5353
}
5454

55-
StoredKey StoredKey::createWithPrivateKeyAddDefaultAddress(const std::string& name, const Data& password, TWCoinType coin, const Data& privateKeyData, TWStoredKeyEncryption encryption) {
55+
StoredKey StoredKey::createWithPrivateKeyAddDefaultAddress(
56+
const std::string& name,
57+
const Data& password,
58+
TWCoinType coin,
59+
const Data& privateKeyData,
60+
TWStoredKeyEncryption encryption,
61+
TWDerivation derivation
62+
) {
5663
const auto curve = TW::curve(coin);
5764
if (!PrivateKey::isValid(privateKeyData, curve)) {
5865
throw std::invalid_argument("Invalid private key data");
5966
}
6067

6168
StoredKey key = createWithPrivateKey(name, password, privateKeyData, encryption);
62-
const auto derivationPath = TW::derivationPath(coin);
69+
const auto derivationPath = TW::derivationPath(coin, derivation);
6370
const auto pubKeyType = TW::publicKeyType(coin);
6471
const auto pubKey = PrivateKey(privateKeyData, TWCoinTypeCurve(coin)).getPublicKey(pubKeyType);
65-
const auto address = TW::deriveAddress(coin, PrivateKey(privateKeyData));
66-
key.accounts.emplace_back(address, coin, TWDerivationDefault, derivationPath, hex(pubKey.bytes), "");
72+
const auto address = TW::deriveAddress(coin, PrivateKey(privateKeyData), derivation);
73+
key.accounts.emplace_back(address, coin, derivation, derivationPath, hex(pubKey.bytes), "");
6774
return key;
6875
}
6976

70-
StoredKey StoredKey::createWithEncodedPrivateKeyAddDefaultAddress(const std::string& name, const Data& password, TWCoinType coin, const std::string& encodedPrivateKey, TWStoredKeyEncryption encryption) {
77+
StoredKey StoredKey::createWithEncodedPrivateKeyAddDefaultAddress(
78+
const std::string& name,
79+
const Data& password,
80+
TWCoinType coin,
81+
const std::string& encodedPrivateKey,
82+
TWStoredKeyEncryption encryption,
83+
TWDerivation derivation
84+
) {
7185
const auto curve = TW::curve(coin);
7286
const auto privateKey = TW::decodePrivateKey(coin, encodedPrivateKey);
7387
StoredKey key = StoredKey(StoredKeyType::privateKey, name, password, privateKey.bytes, TWStoredKeyEncryptionLevelDefault, encryption, encodedPrivateKey);
74-
const auto derivationPath = TW::derivationPath(coin);
88+
const auto derivationPath = TW::derivationPath(coin, derivation);
7589
const auto pubKeyType = TW::publicKeyType(coin);
7690
const auto pubKey = privateKey.getPublicKey(pubKeyType);
77-
const auto address = TW::deriveAddress(coin, privateKey);
78-
key.accounts.emplace_back(address, coin, TWDerivationDefault, derivationPath, hex(pubKey.bytes), "");
91+
const auto address = TW::deriveAddress(coin, privateKey, derivation);
92+
key.accounts.emplace_back(address, coin, derivation, derivationPath, hex(pubKey.bytes), "");
7993
return key;
8094
}
8195

src/Keystore/StoredKey.h

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,11 +63,25 @@ class StoredKey {
6363

6464
/// Create a new StoredKey, with the given name and private key, and also add the default address for the given coin..
6565
/// @throws std::invalid_argument if privateKeyData is not a valid private key
66-
static StoredKey createWithPrivateKeyAddDefaultAddress(const std::string& name, const Data& password, TWCoinType coin, const Data& privateKeyData, TWStoredKeyEncryption encryption = TWStoredKeyEncryptionAes128Ctr);
66+
static StoredKey createWithPrivateKeyAddDefaultAddress(
67+
const std::string& name,
68+
const Data& password,
69+
TWCoinType coin,
70+
const Data& privateKeyData,
71+
TWStoredKeyEncryption encryption = TWStoredKeyEncryptionAes128Ctr,
72+
TWDerivation derivation = TWDerivationDefault
73+
);
6774

6875
/// Create a new StoredKey, with the given name and encoded private key, and also add the default address for the given coin..
6976
/// @throws std::invalid_argument if encodedPrivateKey is not a valid private key
70-
static StoredKey createWithEncodedPrivateKeyAddDefaultAddress(const std::string& name, const Data& password, TWCoinType coin, const std::string& encodedPrivateKey, TWStoredKeyEncryption encryption = TWStoredKeyEncryptionAes128Ctr);
77+
static StoredKey createWithEncodedPrivateKeyAddDefaultAddress(
78+
const std::string& name,
79+
const Data& password,
80+
TWCoinType coin,
81+
const std::string& encodedPrivateKey,
82+
TWStoredKeyEncryption encryption = TWStoredKeyEncryptionAes128Ctr,
83+
TWDerivation derivation = TWDerivationDefault
84+
);
7185

7286
/// Create a StoredKey from a JSON object.
7387
static StoredKey createWithJson(const nlohmann::json& json);

src/interface/TWStoredKey.cpp

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,24 @@ struct TWStoredKey* _Nullable TWStoredKeyImportPrivateKeyWithEncryption(TWData*
5656
}
5757
}
5858

59+
struct TWStoredKey* _Nullable TWStoredKeyImportPrivateKeyWithEncryptionAndDerivation(
60+
TWData* _Nonnull privateKey,
61+
TWString* _Nonnull name,
62+
TWData* _Nonnull password,
63+
enum TWCoinType coin,
64+
enum TWStoredKeyEncryption encryption,
65+
enum TWDerivation derivation
66+
) {
67+
try {
68+
const auto& privateKeyData = *reinterpret_cast<const TW::Data*>(privateKey);
69+
const auto& nameString = *reinterpret_cast<const std::string*>(name);
70+
const auto passwordData = TW::data(TWDataBytes(password), TWDataSize(password));
71+
return new TWStoredKey{ KeyStore::StoredKey::createWithPrivateKeyAddDefaultAddress(nameString, passwordData, coin, privateKeyData, encryption, derivation) };
72+
} catch (...) {
73+
return nullptr;
74+
}
75+
}
76+
5977
struct TWStoredKey* _Nullable TWStoredKeyImportPrivateKeyEncoded(TWString* _Nonnull privateKey, TWString* _Nonnull name, TWData* _Nonnull password, enum TWCoinType coin) {
6078
return TWStoredKeyImportPrivateKeyEncodedWithEncryption(privateKey, name, password, coin, TWStoredKeyEncryptionAes128Ctr);
6179
}
@@ -71,6 +89,24 @@ struct TWStoredKey* _Nullable TWStoredKeyImportPrivateKeyEncodedWithEncryption(T
7189
}
7290
}
7391

92+
struct TWStoredKey* _Nullable TWStoredKeyImportPrivateKeyEncodedWithEncryptionAndDerivation(
93+
TWString* _Nonnull privateKey,
94+
TWString* _Nonnull name,
95+
TWData* _Nonnull password,
96+
enum TWCoinType coin,
97+
enum TWStoredKeyEncryption encryption,
98+
enum TWDerivation derivation
99+
) {
100+
try {
101+
const auto& privateKeyString = *reinterpret_cast<const std::string*>(privateKey);
102+
const auto& nameString = *reinterpret_cast<const std::string*>(name);
103+
const auto passwordData = TW::data(TWDataBytes(password), TWDataSize(password));
104+
return new TWStoredKey{ KeyStore::StoredKey::createWithEncodedPrivateKeyAddDefaultAddress(nameString, passwordData, coin, privateKeyString, encryption, derivation) };
105+
} catch (...) {
106+
return nullptr;
107+
}
108+
}
109+
74110
struct TWStoredKey* _Nullable TWStoredKeyImportHDWallet(TWString* _Nonnull mnemonic, TWString* _Nonnull name, TWData* _Nonnull password, enum TWCoinType coin) {
75111
return TWStoredKeyImportHDWalletWithEncryption(mnemonic, name, password, coin, TWStoredKeyEncryptionAes128Ctr);
76112
}

tests/interface/TWStoredKeyTests.cpp

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,48 @@ TEST(TWStoredKey, importPrivateKeyAes256) {
9797
TWPrivateKeyDelete(privateKey3);
9898
}
9999

100+
TEST(TWStoredKey, importPrivateKeyAes256Legacy) {
101+
const auto privateKeyHex = "28071bf4e2b0340db41b807ed8a5514139e5d6427ff9d58dbd22b7ed187103a4";
102+
const auto privateKey = WRAPD(TWDataCreateWithHexString(WRAPS(TWStringCreateWithUTF8Bytes(privateKeyHex)).get()));
103+
const auto name = WRAPS(TWStringCreateWithUTF8Bytes("name"));
104+
const auto passwordString = WRAPS(TWStringCreateWithUTF8Bytes("password"));
105+
const auto password = WRAPD(TWDataCreateWithBytes(reinterpret_cast<const uint8_t *>(TWStringUTF8Bytes(passwordString.get())), TWStringSize(passwordString.get())));
106+
const auto coin = TWCoinTypeBitcoin;
107+
const auto key = WRAP(TWStoredKey, TWStoredKeyImportPrivateKeyWithEncryptionAndDerivation(privateKey.get(), name.get(), password.get(), coin, TWStoredKeyEncryptionAes256Ctr, TWDerivationBitcoinLegacy));
108+
const auto privateKey2 = WRAPD(TWStoredKeyDecryptPrivateKey(key.get(), password.get()));
109+
EXPECT_EQ(hex(data(TWDataBytes(privateKey2.get()), TWDataSize(privateKey2.get()))), privateKeyHex);
110+
111+
const auto privateKey3 = TWStoredKeyPrivateKey(key.get(), coin, password.get());
112+
const auto pkData3 = WRAPD(TWPrivateKeyData(privateKey3));
113+
EXPECT_EQ(hex(data(TWDataBytes(pkData3.get()), TWDataSize(pkData3.get()))), privateKeyHex);
114+
TWPrivateKeyDelete(privateKey3);
115+
116+
const auto accountCoin = WRAP(TWAccount, TWStoredKeyAccount(key.get(),0));
117+
const auto accountAddress = WRAPS(TWAccountAddress(accountCoin.get()));
118+
EXPECT_EQ(string(TWStringUTF8Bytes(accountAddress.get())), "1PeUvjuxyf31aJKX6kCXuaqxhmG78ZUdL1");
119+
}
120+
121+
TEST(TWStoredKey, importPrivateKeyAes256Taproot) {
122+
const auto privateKeyHex = "28071bf4e2b0340db41b807ed8a5514139e5d6427ff9d58dbd22b7ed187103a4";
123+
const auto privateKey = WRAPD(TWDataCreateWithHexString(WRAPS(TWStringCreateWithUTF8Bytes(privateKeyHex)).get()));
124+
const auto name = WRAPS(TWStringCreateWithUTF8Bytes("name"));
125+
const auto passwordString = WRAPS(TWStringCreateWithUTF8Bytes("password"));
126+
const auto password = WRAPD(TWDataCreateWithBytes(reinterpret_cast<const uint8_t *>(TWStringUTF8Bytes(passwordString.get())), TWStringSize(passwordString.get())));
127+
const auto coin = TWCoinTypeBitcoin;
128+
const auto key = WRAP(TWStoredKey, TWStoredKeyImportPrivateKeyWithEncryptionAndDerivation(privateKey.get(), name.get(), password.get(), coin, TWStoredKeyEncryptionAes256Ctr, TWDerivationBitcoinSegwit));
129+
const auto privateKey2 = WRAPD(TWStoredKeyDecryptPrivateKey(key.get(), password.get()));
130+
EXPECT_EQ(hex(data(TWDataBytes(privateKey2.get()), TWDataSize(privateKey2.get()))), privateKeyHex);
131+
132+
const auto privateKey3 = TWStoredKeyPrivateKey(key.get(), coin, password.get());
133+
const auto pkData3 = WRAPD(TWPrivateKeyData(privateKey3));
134+
EXPECT_EQ(hex(data(TWDataBytes(pkData3.get()), TWDataSize(pkData3.get()))), privateKeyHex);
135+
TWPrivateKeyDelete(privateKey3);
136+
137+
const auto accountCoin = WRAP(TWAccount, TWStoredKeyAccount(key.get(),0));
138+
const auto accountAddress = WRAPS(TWAccountAddress(accountCoin.get()));
139+
EXPECT_EQ(string(TWStringUTF8Bytes(accountAddress.get())), "bc1qlp5hssx3qstf3m0mt7fd6tzlh90ssm32u2llf4");
140+
}
141+
100142
TEST(TWStoredKey, importPrivateKeyHexButDecryptEncoded) {
101143
const auto privateKeyHex = "3a1076bf45ab87712ad64ccb3b10217737f7faacbf2872e88fdd9a537d8fe266";
102144
const auto privateKey = WRAPD(TWDataCreateWithHexString(WRAPS(TWStringCreateWithUTF8Bytes(privateKeyHex)).get()));
@@ -137,6 +179,30 @@ TEST(TWStoredKey, importPrivateKeyEncodedHex) {
137179
TWPrivateKeyDelete(privateKey3);
138180
}
139181

182+
TEST(TWStoredKey, importPrivateKeyEncodedHexLegacy) {
183+
const auto privateKeyHex = "28071bf4e2b0340db41b807ed8a5514139e5d6427ff9d58dbd22b7ed187103a4";
184+
const auto privateKey = WRAPS(TWStringCreateWithUTF8Bytes(privateKeyHex));
185+
const auto name = WRAPS(TWStringCreateWithUTF8Bytes("name"));
186+
const auto passwordString = WRAPS(TWStringCreateWithUTF8Bytes("password"));
187+
const auto password = WRAPD(TWDataCreateWithBytes(reinterpret_cast<const uint8_t *>(TWStringUTF8Bytes(passwordString.get())), TWStringSize(passwordString.get())));
188+
const auto coin = TWCoinTypeBitcoin;
189+
const auto key = WRAP(TWStoredKey, TWStoredKeyImportPrivateKeyEncodedWithEncryptionAndDerivation(privateKey.get(), name.get(), password.get(), coin, TWStoredKeyEncryptionAes128Ctr, TWDerivationBitcoinLegacy));
190+
const auto privateKey2 = WRAPD(TWStoredKeyDecryptPrivateKey(key.get(), password.get()));
191+
EXPECT_EQ(hex(data(TWDataBytes(privateKey2.get()), TWDataSize(privateKey2.get()))), privateKeyHex);
192+
EXPECT_TRUE(TWStoredKeyHasPrivateKeyEncoded(key.get()));
193+
const auto privateKey2Encoded = WRAPS(TWStoredKeyDecryptPrivateKeyEncoded(key.get(), password.get()));
194+
EXPECT_EQ(std::string(TWStringUTF8Bytes(privateKey2Encoded.get())), privateKeyHex);
195+
196+
const auto privateKey3 = TWStoredKeyPrivateKey(key.get(), coin, password.get());
197+
const auto pkData3 = WRAPD(TWPrivateKeyData(privateKey3));
198+
EXPECT_EQ(hex(data(TWDataBytes(pkData3.get()), TWDataSize(pkData3.get()))), privateKeyHex);
199+
TWPrivateKeyDelete(privateKey3);
200+
201+
const auto accountCoin = WRAP(TWAccount, TWStoredKeyAccount(key.get(),0));
202+
const auto accountAddress = WRAPS(TWAccountAddress(accountCoin.get()));
203+
EXPECT_EQ(string(TWStringUTF8Bytes(accountAddress.get())), "1PeUvjuxyf31aJKX6kCXuaqxhmG78ZUdL1");
204+
}
205+
140206
TEST(TWStoredKey, importPrivateKeyEncodedStellar) {
141207
const auto privateKeyEncoded = "SAV76USXIJOBMEQXPANUOQM6F5LIOTLPDIDVRJBFFE2MDJXG24TAPUU7";
142208
const auto decodedPrivateKeyHex = "2bff5257425c161217781b47419e2f56874d6f1a0758a4252934c1a6e6d72607";

wasm/src/keystore/default-impl.ts

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,8 @@ export class Default implements Types.IKeyStore {
8282
name: string,
8383
password: string,
8484
coin: CoinType,
85-
encryption: StoredKeyEncryption
85+
encryption: StoredKeyEncryption,
86+
derivation: Derivation
8687
): Promise<Types.Wallet> {
8788
return new Promise((resolve, reject) => {
8889
const { StoredKey, PrivateKey, Curve, StoredKeyEncryption } = this.core;
@@ -95,7 +96,7 @@ export class Default implements Types.IKeyStore {
9596
throw Types.Error.InvalidKey;
9697
}
9798
let pass = Buffer.from(password);
98-
let storedKey = StoredKey.importPrivateKeyWithEncryption(key, name, pass, coin, encryption);
99+
let storedKey = StoredKey.importPrivateKeyWithEncryptionAndDerivation(key, name, pass, coin, encryption, derivation);
99100
let wallet = this.mapWallet(storedKey);
100101
storedKey.delete();
101102
this.importWallet(wallet)
@@ -109,13 +110,14 @@ export class Default implements Types.IKeyStore {
109110
name: string,
110111
password: string,
111112
coin: CoinType,
112-
encryption: StoredKeyEncryption
113+
encryption: StoredKeyEncryption,
114+
derivation: Derivation
113115
): Promise<Types.Wallet> {
114116
return new Promise((resolve, reject) => {
115117
const { StoredKey, PrivateKey, Curve, StoredKeyEncryption } = this.core;
116118

117119
let pass = Buffer.from(password);
118-
let storedKey = StoredKey.importPrivateKeyEncodedWithEncryption(key, name, pass, coin, encryption);
120+
let storedKey = StoredKey.importPrivateKeyEncodedWithEncryptionAndDerivation(key, name, pass, coin, encryption, derivation);
119121
let wallet = this.mapWallet(storedKey);
120122
storedKey.delete();
121123
this.importWallet(wallet)

wasm/src/keystore/types.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,8 @@ export interface IKeyStore {
6868
name: string,
6969
password: string,
7070
coin: CoinType,
71-
encryption: StoredKeyEncryption
71+
encryption: StoredKeyEncryption,
72+
derivation: Derivation
7273
): Promise<Wallet>;
7374

7475
// Import a Wallet object directly

0 commit comments

Comments
 (0)