Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions include/TrustWalletCore/TWStoredKey.h
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,17 @@ struct TWStoredKey* _Nullable TWStoredKeyImportPrivateKey(TWData* _Nonnull priva
TW_EXPORT_STATIC_METHOD
struct TWStoredKey* _Nullable TWStoredKeyImportPrivateKeyWithEncryption(TWData* _Nonnull privateKey, TWString* _Nonnull name, TWData* _Nonnull password, enum TWCoinType coin, enum TWStoredKeyEncryption encryption);

/// Imports a private key.
///
/// \param privateKey Non-null Block of data private key
/// \param name The name of the stored key to import as a non-null string
/// \param password Non-null block of data, password of the stored key
/// \param coin the coin type
/// \param encryption cipher encryption mode
/// \param derivation derivation of the given coin type
TW_EXPORT_STATIC_METHOD
struct TWStoredKey* _Nullable TWStoredKeyImportPrivateKeyWithEncryptionAndDerivation(TWData* _Nonnull privateKey, TWString* _Nonnull name, TWData* _Nonnull password, enum TWCoinType coin, enum TWStoredKeyEncryption encryption, enum TWDerivation derivation);

/// Imports an encoded private key.
///
/// \param privateKey Non-null encoded private key
Expand All @@ -73,6 +84,19 @@ struct TWStoredKey* _Nullable TWStoredKeyImportPrivateKeyEncoded(TWString* _Nonn
TW_EXPORT_STATIC_METHOD
struct TWStoredKey* _Nullable TWStoredKeyImportPrivateKeyEncodedWithEncryption(TWString* _Nonnull privateKey, TWString* _Nonnull name, TWData* _Nonnull password, enum TWCoinType coin, enum TWStoredKeyEncryption encryption);

/// Imports an encoded private key.
///
/// \param privateKey Non-null encoded private key
/// \param name The name of the stored key to import as a non-null string
/// \param password Non-null block of data, password of the stored key
/// \param coin the coin type
/// \param encryption cipher encryption mode
/// \param derivation derivation of the given coin type
/// \note Returned object needs to be deleted with \TWStoredKeyDelete
/// \return Nullptr if the key can't be imported, the stored key otherwise
TW_EXPORT_STATIC_METHOD
struct TWStoredKey* _Nullable TWStoredKeyImportPrivateKeyEncodedWithEncryptionAndDerivation(TWString* _Nonnull privateKey, TWString* _Nonnull name, TWData* _Nonnull password, enum TWCoinType coin, enum TWStoredKeyEncryption encryption, enum TWDerivation derivation);

/// Imports an HD wallet.
///
/// \param mnemonic Non-null bip39 mnemonic
Expand Down
30 changes: 22 additions & 8 deletions src/Keystore/StoredKey.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -52,30 +52,44 @@ StoredKey StoredKey::createWithPrivateKey(const std::string& name, const Data& p
return StoredKey(StoredKeyType::privateKey, name, password, privateKeyData, TWStoredKeyEncryptionLevelDefault, encryption);
}

StoredKey StoredKey::createWithPrivateKeyAddDefaultAddress(const std::string& name, const Data& password, TWCoinType coin, const Data& privateKeyData, TWStoredKeyEncryption encryption) {
StoredKey StoredKey::createWithPrivateKeyAddDefaultAddress(
const std::string& name,
const Data& password,
TWCoinType coin,
const Data& privateKeyData,
TWStoredKeyEncryption encryption,
TWDerivation derivation
) {
const auto curve = TW::curve(coin);
if (!PrivateKey::isValid(privateKeyData, curve)) {
throw std::invalid_argument("Invalid private key data");
}

StoredKey key = createWithPrivateKey(name, password, privateKeyData, encryption);
const auto derivationPath = TW::derivationPath(coin);
const auto derivationPath = TW::derivationPath(coin, derivation);
const auto pubKeyType = TW::publicKeyType(coin);
const auto pubKey = PrivateKey(privateKeyData, TWCoinTypeCurve(coin)).getPublicKey(pubKeyType);
const auto address = TW::deriveAddress(coin, PrivateKey(privateKeyData));
key.accounts.emplace_back(address, coin, TWDerivationDefault, derivationPath, hex(pubKey.bytes), "");
const auto address = TW::deriveAddress(coin, PrivateKey(privateKeyData), derivation);
key.accounts.emplace_back(address, coin, derivation, derivationPath, hex(pubKey.bytes), "");
return key;
}

StoredKey StoredKey::createWithEncodedPrivateKeyAddDefaultAddress(const std::string& name, const Data& password, TWCoinType coin, const std::string& encodedPrivateKey, TWStoredKeyEncryption encryption) {
StoredKey StoredKey::createWithEncodedPrivateKeyAddDefaultAddress(
const std::string& name,
const Data& password,
TWCoinType coin,
const std::string& encodedPrivateKey,
TWStoredKeyEncryption encryption,
TWDerivation derivation
) {
const auto curve = TW::curve(coin);
const auto privateKey = TW::decodePrivateKey(coin, encodedPrivateKey);
StoredKey key = StoredKey(StoredKeyType::privateKey, name, password, privateKey.bytes, TWStoredKeyEncryptionLevelDefault, encryption, encodedPrivateKey);
const auto derivationPath = TW::derivationPath(coin);
const auto derivationPath = TW::derivationPath(coin, derivation);
const auto pubKeyType = TW::publicKeyType(coin);
const auto pubKey = privateKey.getPublicKey(pubKeyType);
const auto address = TW::deriveAddress(coin, privateKey);
key.accounts.emplace_back(address, coin, TWDerivationDefault, derivationPath, hex(pubKey.bytes), "");
const auto address = TW::deriveAddress(coin, privateKey, derivation);
key.accounts.emplace_back(address, coin, derivation, derivationPath, hex(pubKey.bytes), "");
return key;
}

Expand Down
18 changes: 16 additions & 2 deletions src/Keystore/StoredKey.h
Original file line number Diff line number Diff line change
Expand Up @@ -63,11 +63,25 @@ class StoredKey {

/// Create a new StoredKey, with the given name and private key, and also add the default address for the given coin..
/// @throws std::invalid_argument if privateKeyData is not a valid private key
static StoredKey createWithPrivateKeyAddDefaultAddress(const std::string& name, const Data& password, TWCoinType coin, const Data& privateKeyData, TWStoredKeyEncryption encryption = TWStoredKeyEncryptionAes128Ctr);
static StoredKey createWithPrivateKeyAddDefaultAddress(
const std::string& name,
const Data& password,
TWCoinType coin,
const Data& privateKeyData,
TWStoredKeyEncryption encryption = TWStoredKeyEncryptionAes128Ctr,
TWDerivation derivation = TWDerivationDefault
);

/// Create a new StoredKey, with the given name and encoded private key, and also add the default address for the given coin..
/// @throws std::invalid_argument if encodedPrivateKey is not a valid private key
static StoredKey createWithEncodedPrivateKeyAddDefaultAddress(const std::string& name, const Data& password, TWCoinType coin, const std::string& encodedPrivateKey, TWStoredKeyEncryption encryption = TWStoredKeyEncryptionAes128Ctr);
static StoredKey createWithEncodedPrivateKeyAddDefaultAddress(
const std::string& name,
const Data& password,
TWCoinType coin,
const std::string& encodedPrivateKey,
TWStoredKeyEncryption encryption = TWStoredKeyEncryptionAes128Ctr,
TWDerivation derivation = TWDerivationDefault
);

/// Create a StoredKey from a JSON object.
static StoredKey createWithJson(const nlohmann::json& json);
Expand Down
36 changes: 36 additions & 0 deletions src/interface/TWStoredKey.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,24 @@ struct TWStoredKey* _Nullable TWStoredKeyImportPrivateKeyWithEncryption(TWData*
}
}

struct TWStoredKey* _Nullable TWStoredKeyImportPrivateKeyWithEncryptionAndDerivation(
TWData* _Nonnull privateKey,
TWString* _Nonnull name,
TWData* _Nonnull password,
enum TWCoinType coin,
enum TWStoredKeyEncryption encryption,
enum TWDerivation derivation
) {
try {
const auto& privateKeyData = *reinterpret_cast<const TW::Data*>(privateKey);
const auto& nameString = *reinterpret_cast<const std::string*>(name);
const auto passwordData = TW::data(TWDataBytes(password), TWDataSize(password));
return new TWStoredKey{ KeyStore::StoredKey::createWithPrivateKeyAddDefaultAddress(nameString, passwordData, coin, privateKeyData, encryption, derivation) };
} catch (...) {
return nullptr;
}
}

struct TWStoredKey* _Nullable TWStoredKeyImportPrivateKeyEncoded(TWString* _Nonnull privateKey, TWString* _Nonnull name, TWData* _Nonnull password, enum TWCoinType coin) {
return TWStoredKeyImportPrivateKeyEncodedWithEncryption(privateKey, name, password, coin, TWStoredKeyEncryptionAes128Ctr);
}
Expand All @@ -71,6 +89,24 @@ struct TWStoredKey* _Nullable TWStoredKeyImportPrivateKeyEncodedWithEncryption(T
}
}

struct TWStoredKey* _Nullable TWStoredKeyImportPrivateKeyEncodedWithEncryptionAndDerivation(
TWString* _Nonnull privateKey,
TWString* _Nonnull name,
TWData* _Nonnull password,
enum TWCoinType coin,
enum TWStoredKeyEncryption encryption,
enum TWDerivation derivation
) {
try {
const auto& privateKeyString = *reinterpret_cast<const std::string*>(privateKey);
const auto& nameString = *reinterpret_cast<const std::string*>(name);
const auto passwordData = TW::data(TWDataBytes(password), TWDataSize(password));
return new TWStoredKey{ KeyStore::StoredKey::createWithEncodedPrivateKeyAddDefaultAddress(nameString, passwordData, coin, privateKeyString, encryption, derivation) };
} catch (...) {
return nullptr;
}
}

struct TWStoredKey* _Nullable TWStoredKeyImportHDWallet(TWString* _Nonnull mnemonic, TWString* _Nonnull name, TWData* _Nonnull password, enum TWCoinType coin) {
return TWStoredKeyImportHDWalletWithEncryption(mnemonic, name, password, coin, TWStoredKeyEncryptionAes128Ctr);
}
Expand Down
66 changes: 66 additions & 0 deletions tests/interface/TWStoredKeyTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,48 @@ TEST(TWStoredKey, importPrivateKeyAes256) {
TWPrivateKeyDelete(privateKey3);
}

TEST(TWStoredKey, importPrivateKeyAes256Legacy) {
const auto privateKeyHex = "28071bf4e2b0340db41b807ed8a5514139e5d6427ff9d58dbd22b7ed187103a4";
const auto privateKey = WRAPD(TWDataCreateWithHexString(WRAPS(TWStringCreateWithUTF8Bytes(privateKeyHex)).get()));
const auto name = WRAPS(TWStringCreateWithUTF8Bytes("name"));
const auto passwordString = WRAPS(TWStringCreateWithUTF8Bytes("password"));
const auto password = WRAPD(TWDataCreateWithBytes(reinterpret_cast<const uint8_t *>(TWStringUTF8Bytes(passwordString.get())), TWStringSize(passwordString.get())));
const auto coin = TWCoinTypeBitcoin;
const auto key = WRAP(TWStoredKey, TWStoredKeyImportPrivateKeyWithEncryptionAndDerivation(privateKey.get(), name.get(), password.get(), coin, TWStoredKeyEncryptionAes256Ctr, TWDerivationBitcoinLegacy));
const auto privateKey2 = WRAPD(TWStoredKeyDecryptPrivateKey(key.get(), password.get()));
EXPECT_EQ(hex(data(TWDataBytes(privateKey2.get()), TWDataSize(privateKey2.get()))), privateKeyHex);

const auto privateKey3 = TWStoredKeyPrivateKey(key.get(), coin, password.get());
const auto pkData3 = WRAPD(TWPrivateKeyData(privateKey3));
EXPECT_EQ(hex(data(TWDataBytes(pkData3.get()), TWDataSize(pkData3.get()))), privateKeyHex);
TWPrivateKeyDelete(privateKey3);

const auto accountCoin = WRAP(TWAccount, TWStoredKeyAccount(key.get(),0));
const auto accountAddress = WRAPS(TWAccountAddress(accountCoin.get()));
EXPECT_EQ(string(TWStringUTF8Bytes(accountAddress.get())), "1PeUvjuxyf31aJKX6kCXuaqxhmG78ZUdL1");
}

TEST(TWStoredKey, importPrivateKeyAes256Taproot) {
const auto privateKeyHex = "28071bf4e2b0340db41b807ed8a5514139e5d6427ff9d58dbd22b7ed187103a4";
const auto privateKey = WRAPD(TWDataCreateWithHexString(WRAPS(TWStringCreateWithUTF8Bytes(privateKeyHex)).get()));
const auto name = WRAPS(TWStringCreateWithUTF8Bytes("name"));
const auto passwordString = WRAPS(TWStringCreateWithUTF8Bytes("password"));
const auto password = WRAPD(TWDataCreateWithBytes(reinterpret_cast<const uint8_t *>(TWStringUTF8Bytes(passwordString.get())), TWStringSize(passwordString.get())));
const auto coin = TWCoinTypeBitcoin;
const auto key = WRAP(TWStoredKey, TWStoredKeyImportPrivateKeyWithEncryptionAndDerivation(privateKey.get(), name.get(), password.get(), coin, TWStoredKeyEncryptionAes256Ctr, TWDerivationBitcoinSegwit));
const auto privateKey2 = WRAPD(TWStoredKeyDecryptPrivateKey(key.get(), password.get()));
EXPECT_EQ(hex(data(TWDataBytes(privateKey2.get()), TWDataSize(privateKey2.get()))), privateKeyHex);

const auto privateKey3 = TWStoredKeyPrivateKey(key.get(), coin, password.get());
const auto pkData3 = WRAPD(TWPrivateKeyData(privateKey3));
EXPECT_EQ(hex(data(TWDataBytes(pkData3.get()), TWDataSize(pkData3.get()))), privateKeyHex);
TWPrivateKeyDelete(privateKey3);

const auto accountCoin = WRAP(TWAccount, TWStoredKeyAccount(key.get(),0));
const auto accountAddress = WRAPS(TWAccountAddress(accountCoin.get()));
EXPECT_EQ(string(TWStringUTF8Bytes(accountAddress.get())), "bc1qlp5hssx3qstf3m0mt7fd6tzlh90ssm32u2llf4");
}

TEST(TWStoredKey, importPrivateKeyHexButDecryptEncoded) {
const auto privateKeyHex = "3a1076bf45ab87712ad64ccb3b10217737f7faacbf2872e88fdd9a537d8fe266";
const auto privateKey = WRAPD(TWDataCreateWithHexString(WRAPS(TWStringCreateWithUTF8Bytes(privateKeyHex)).get()));
Expand Down Expand Up @@ -137,6 +179,30 @@ TEST(TWStoredKey, importPrivateKeyEncodedHex) {
TWPrivateKeyDelete(privateKey3);
}

TEST(TWStoredKey, importPrivateKeyEncodedHexLegacy) {
const auto privateKeyHex = "28071bf4e2b0340db41b807ed8a5514139e5d6427ff9d58dbd22b7ed187103a4";
const auto privateKey = WRAPS(TWStringCreateWithUTF8Bytes(privateKeyHex));
const auto name = WRAPS(TWStringCreateWithUTF8Bytes("name"));
const auto passwordString = WRAPS(TWStringCreateWithUTF8Bytes("password"));
const auto password = WRAPD(TWDataCreateWithBytes(reinterpret_cast<const uint8_t *>(TWStringUTF8Bytes(passwordString.get())), TWStringSize(passwordString.get())));
const auto coin = TWCoinTypeBitcoin;
const auto key = WRAP(TWStoredKey, TWStoredKeyImportPrivateKeyEncodedWithEncryptionAndDerivation(privateKey.get(), name.get(), password.get(), coin, TWStoredKeyEncryptionAes128Ctr, TWDerivationBitcoinLegacy));
const auto privateKey2 = WRAPD(TWStoredKeyDecryptPrivateKey(key.get(), password.get()));
EXPECT_EQ(hex(data(TWDataBytes(privateKey2.get()), TWDataSize(privateKey2.get()))), privateKeyHex);
EXPECT_TRUE(TWStoredKeyHasPrivateKeyEncoded(key.get()));
const auto privateKey2Encoded = WRAPS(TWStoredKeyDecryptPrivateKeyEncoded(key.get(), password.get()));
EXPECT_EQ(std::string(TWStringUTF8Bytes(privateKey2Encoded.get())), privateKeyHex);

const auto privateKey3 = TWStoredKeyPrivateKey(key.get(), coin, password.get());
const auto pkData3 = WRAPD(TWPrivateKeyData(privateKey3));
EXPECT_EQ(hex(data(TWDataBytes(pkData3.get()), TWDataSize(pkData3.get()))), privateKeyHex);
TWPrivateKeyDelete(privateKey3);

const auto accountCoin = WRAP(TWAccount, TWStoredKeyAccount(key.get(),0));
const auto accountAddress = WRAPS(TWAccountAddress(accountCoin.get()));
EXPECT_EQ(string(TWStringUTF8Bytes(accountAddress.get())), "1PeUvjuxyf31aJKX6kCXuaqxhmG78ZUdL1");
}

TEST(TWStoredKey, importPrivateKeyEncodedStellar) {
const auto privateKeyEncoded = "SAV76USXIJOBMEQXPANUOQM6F5LIOTLPDIDVRJBFFE2MDJXG24TAPUU7";
const auto decodedPrivateKeyHex = "2bff5257425c161217781b47419e2f56874d6f1a0758a4252934c1a6e6d72607";
Expand Down
10 changes: 6 additions & 4 deletions wasm/src/keystore/default-impl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,8 @@ export class Default implements Types.IKeyStore {
name: string,
password: string,
coin: CoinType,
encryption: StoredKeyEncryption
encryption: StoredKeyEncryption,
derivation: Derivation
): Promise<Types.Wallet> {
return new Promise((resolve, reject) => {
const { StoredKey, PrivateKey, Curve, StoredKeyEncryption } = this.core;
Expand All @@ -95,7 +96,7 @@ export class Default implements Types.IKeyStore {
throw Types.Error.InvalidKey;
}
let pass = Buffer.from(password);
let storedKey = StoredKey.importPrivateKeyWithEncryption(key, name, pass, coin, encryption);
let storedKey = StoredKey.importPrivateKeyWithEncryptionAndDerivation(key, name, pass, coin, encryption, derivation);
let wallet = this.mapWallet(storedKey);
storedKey.delete();
this.importWallet(wallet)
Expand All @@ -109,13 +110,14 @@ export class Default implements Types.IKeyStore {
name: string,
password: string,
coin: CoinType,
encryption: StoredKeyEncryption
encryption: StoredKeyEncryption,
derivation: Derivation
): Promise<Types.Wallet> {
return new Promise((resolve, reject) => {
const { StoredKey, PrivateKey, Curve, StoredKeyEncryption } = this.core;

let pass = Buffer.from(password);
let storedKey = StoredKey.importPrivateKeyEncodedWithEncryption(key, name, pass, coin, encryption);
let storedKey = StoredKey.importPrivateKeyEncodedWithEncryptionAndDerivation(key, name, pass, coin, encryption, derivation);
let wallet = this.mapWallet(storedKey);
storedKey.delete();
this.importWallet(wallet)
Expand Down
3 changes: 2 additions & 1 deletion wasm/src/keystore/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,8 @@ export interface IKeyStore {
name: string,
password: string,
coin: CoinType,
encryption: StoredKeyEncryption
encryption: StoredKeyEncryption,
derivation: Derivation
): Promise<Wallet>;

// Import a Wallet object directly
Expand Down
Loading
Loading