Showing with 1,371 additions and 244 deletions.
  1. +4 −4 Package.swift
  2. +12 −7 ...p/src/androidTest/java/com/trustwallet/core/app/blockchains/ripple/TestRippleTransactionSigner.kt
  3. +11 −0 android/app/src/androidTest/java/com/trustwallet/core/app/utils/TestKeyStore.kt
  4. +7 −0 include/TrustWalletCore/TWBase.h
  5. +48 −1 include/TrustWalletCore/TWStoredKey.h
  6. +22 −0 include/TrustWalletCore/TWStoredKeyEncryption.h
  7. +7 −3 src/Bitcoin/Script.cpp
  8. +2 −2 src/Bitcoin/Script.h
  9. +13 −5 src/Bitcoin/TransactionBuilder.h
  10. +10 −2 src/Bitcoin/TransactionSigner.cpp
  11. +36 −5 src/Keystore/AESParameters.cpp
  12. +23 −6 src/Keystore/AESParameters.h
  13. +26 −14 src/Keystore/EncryptionParameters.cpp
  14. +21 −15 src/Keystore/EncryptionParameters.h
  15. +12 −12 src/Keystore/StoredKey.cpp
  16. +7 −6 src/Keystore/StoredKey.h
  17. +18 −4 src/XRP/BinaryCoding.h
  18. +84 −16 src/XRP/Signer.cpp
  19. +7 −0 src/XRP/Signer.h
  20. +152 −6 src/XRP/Transaction.cpp
  21. +127 −24 src/XRP/Transaction.h
  22. +7 −4 src/Zcash/TransactionBuilder.h
  23. +22 −5 src/interface/TWStoredKey.cpp
  24. +82 −15 src/proto/Ripple.proto
  25. +15 −15 swift/Sources/KeyStore.swift
  26. +21 −13 swift/Tests/Blockchains/RippleTests.swift
  27. +24 −0 swift/Tests/Keystore/KeyStoreTests.swift
  28. +9 −8 tests/chains/Bitcoin/BitcoinScriptTests.cpp
  29. +1 −1 tests/chains/Bitcoin/TWBitcoinSigningTests.cpp
  30. +46 −6 tests/chains/THORChain/SwapTests.cpp
  31. +162 −9 tests/chains/XRP/TWAnySignerTests.cpp
  32. +20 −10 tests/chains/XRP/TransactionTests.cpp
  33. +50 −1 tests/common/Keystore/StoredKeyTests.cpp
  34. +61 −5 tests/interface/TWStoredKeyTests.cpp
  35. +70 −6 wasm/src/Random.cpp
  36. +9 −7 wasm/src/keystore/default-impl.ts
  37. +5 −3 wasm/src/keystore/types.ts
  38. +58 −2 wasm/tests/KeyStore+extension.test.ts
  39. +60 −2 wasm/tests/KeyStore+fs.test.ts
8 changes: 4 additions & 4 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,13 @@ let package = Package(
targets: [
.binaryTarget(
name: "WalletCore",
url: "https://github.com/trustwallet/wallet-core/releases/download/3.0.6/WalletCore.xcframework.zip",
checksum: "a3df0c2b30fc59ede0a2600266fc19b8c0cf655dbef3fb832488c8ddedcb6b93"
url: "https://github.com/trustwallet/wallet-core/releases/download/3.1.0/WalletCore.xcframework.zip",
checksum: "2487af30a8f6f4775a41f29456f792f15269f4f4daae2da7e4b9ef8747613e3a"
),
.binaryTarget(
name: "SwiftProtobuf",
url: "https://github.com/trustwallet/wallet-core/releases/download/3.0.6/SwiftProtobuf.xcframework.zip",
checksum: "61fa8483d4bd43f1898db6997eff0279426f15f9e518e12db0d762ec5f927a9b"
url: "https://github.com/trustwallet/wallet-core/releases/download/3.1.0/SwiftProtobuf.xcframework.zip",
checksum: "2038b1d43c9f6aeb4957e3763382b8558983a1a811b168dca71be3636bb8350a"
)
]
)
Original file line number Diff line number Diff line change
Expand Up @@ -19,20 +19,25 @@ class TestRippleTransactionSigner {

@Test
fun testRippleTransactionSigning() {
val operation = Ripple.OperationPayment.newBuilder()
operation.apply {
amount = 10
destination = "rU893viamSnsfP3zjzM2KPxjqZjXSXK6VF"
}
val signingInput = Ripple.SigningInput.newBuilder()
signingInput.apply {
account = "rDpysuumkweqeC7XdNgYNtzL5GxbdsmrtF"
amount = 29_000_000
destination = "rU893viamSnsfP3zjzM2KPxjqZjXSXK6VF"
fee = 200_000
sequence = 1
privateKey = ByteString.copyFrom(PrivateKey("ba005cd605d8a02e3d5dfd04234cef3a3ee4f76bfbad2722d1fb5af8e12e6764".toHexByteArray()).data())
account = "rfxdLwsZnoespnTDDb1Xhvbc8EFNdztaoq"
fee = 10
sequence = 32268248
lastLedgerSequence = 32268269
privateKey = ByteString.copyFrom(PrivateKey("a5576c0f63da10e584568c8d134569ff44017b0a249eb70657127ae04f38cc77".toHexByteArray()).data())
opPayment = operation.build()
}

val sign = AnySigner.sign(signingInput.build(), XRP, SigningOutput.parser())
val signBytes = sign.encoded.toByteArray()

assertEquals(signBytes.toHex(), "0x12000022800000002400000001614000000001ba8140684000000000030d407321026cc34b92cefb3a4537b3edb0b6044c04af27c01583c577823ecc69a9a21119b6744630440220067f20b3eebfc7107dd0bcc72337a236ac3be042c0469f2341d76694a17d4bb9022048393d7ee7dcb729783b33f5038939ddce1bb8337e66d752974626854556bbb681148400b6b6d08d5d495653d73eda6804c249a5148883148132e4e20aecf29090ac428a9c43f230a829220d")
assertEquals(signBytes.toHex(), "0x12000022000000002401ec5fd8201b01ec5fed61400000000000000a68400000000000000a732103d13e1152965a51a4a9fd9a8b4ea3dd82a4eba6b25fcad5f460a2342bb650333f74463044022037d32835c9394f39b2cfd4eaf5b0a80e0db397ace06630fa2b099ff73e425dbc02205288f780330b7a88a1980fa83c647b5908502ad7de9a44500c08f0750b0d9e8481144c55f5a78067206507580be7bb2686c8460adff983148132e4e20aecf29090ac428a9c43f230a829220d")
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import org.junit.Assert.*
import org.junit.Test
import wallet.core.jni.StoredKey
import wallet.core.jni.CoinType
import wallet.core.jni.StoredKeyEncryption

class TestKeyStore {

Expand All @@ -21,6 +22,16 @@ class TestKeyStore {
assertNotNull(result2)
}

@Test
fun testDecryptMnemonicAes256() {
val keyStore = StoredKey("Test Wallet", "password".toByteArray(), StoredKeyEncryption.AES256CTR)
val result = keyStore.decryptMnemonic("wrong".toByteArray())
val result2 = keyStore.decryptMnemonic("password".toByteArray())

assertNull(result)
assertNotNull(result2)
}

@Test
fun testRemoveCoins() {
val password = "password".toByteArray()
Expand Down
7 changes: 7 additions & 0 deletions include/TrustWalletCore/TWBase.h
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,13 @@
#define TW_ASSUME_NONNULL_END
#endif

#if defined(__cplusplus) && (__cplusplus >= 201402L)
# define TW_DEPRECATED(since) [[deprecated("Since " #since)]]
# define TW_DEPRECATED_FOR(since, replacement) [[deprecated("Since " #since "; use " #replacement)]]
#else
# define TW_DEPRECATED(since)
# define TW_DEPRECATED_FOR(since, replacement)
#endif

#if !__has_feature(nullability)
#ifndef _Nullable
Expand Down
49 changes: 48 additions & 1 deletion include/TrustWalletCore/TWStoredKey.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
#include "TWHDWallet.h"
#include "TWPrivateKey.h"
#include "TWStoredKeyEncryptionLevel.h"
#include "TWStoredKeyEncryption.h"
#include "TWString.h"

TW_EXTERN_C_BEGIN
Expand Down Expand Up @@ -40,6 +41,18 @@ struct TWStoredKey* _Nullable TWStoredKeyLoad(TWString* _Nonnull path);
TW_EXPORT_STATIC_METHOD
struct TWStoredKey* _Nullable TWStoredKeyImportPrivateKey(TWData* _Nonnull privateKey, TWString* _Nonnull name, TWData* _Nonnull password, enum TWCoinType coin);

/// 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
/// \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 TWStoredKeyImportPrivateKeyWithEncryption(TWData* _Nonnull privateKey, TWString* _Nonnull name, TWData* _Nonnull password, enum TWCoinType coin, enum TWStoredKeyEncryption encryption);

/// Imports an HD wallet.
///
/// \param mnemonic Non-null bip39 mnemonic
Expand All @@ -51,6 +64,18 @@ struct TWStoredKey* _Nullable TWStoredKeyImportPrivateKey(TWData* _Nonnull priva
TW_EXPORT_STATIC_METHOD
struct TWStoredKey* _Nullable TWStoredKeyImportHDWallet(TWString* _Nonnull mnemonic, TWString* _Nonnull name, TWData* _Nonnull password, enum TWCoinType coin);

/// Imports an HD wallet.
///
/// \param mnemonic Non-null bip39 mnemonic
/// \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
/// \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 TWStoredKeyImportHDWalletWithEncryption(TWString* _Nonnull mnemonic, TWString* _Nonnull name, TWData* _Nonnull password, enum TWCoinType coin, enum TWStoredKeyEncryption encryption);

/// Imports a key from JSON.
///
/// \param json Json stored key import format as a non-null block of data
Expand All @@ -59,16 +84,28 @@ struct TWStoredKey* _Nullable TWStoredKeyImportHDWallet(TWString* _Nonnull mnemo
TW_EXPORT_STATIC_METHOD
struct TWStoredKey* _Nullable TWStoredKeyImportJSON(TWData* _Nonnull json);

/// Creates a new key, with given encryption strength level. Returned object needs to be deleted.
/// Creates a new key, with given encryption strength level. Returned object needs to be deleted.
///
/// \param name The name of the key to be stored
/// \param password Non-null block of data, password of the stored key
/// \param encryptionLevel The level of encryption, see \TWStoredKeyEncryptionLevel
/// \note Returned object needs to be deleted with \TWStoredKeyDelete
/// \return The stored key as a non-null pointer
TW_DEPRECATED_FOR("3.1.1", "TWStoredKeyCreateLevelAndEncryption")
TW_EXPORT_STATIC_METHOD
struct TWStoredKey* _Nonnull TWStoredKeyCreateLevel(TWString* _Nonnull name, TWData* _Nonnull password, enum TWStoredKeyEncryptionLevel encryptionLevel);

/// Creates a new key, with given encryption strength level. Returned object needs to be deleted.
///
/// \param name The name of the key to be stored
/// \param password Non-null block of data, password of the stored key
/// \param encryptionLevel The level of encryption, see \TWStoredKeyEncryptionLevel
/// \param encryption cipher encryption mode
/// \note Returned object needs to be deleted with \TWStoredKeyDelete
/// \return The stored key as a non-null pointer
TW_EXPORT_STATIC_METHOD
struct TWStoredKey* _Nonnull TWStoredKeyCreateLevelAndEncryption(TWString* _Nonnull name, TWData* _Nonnull password, enum TWStoredKeyEncryptionLevel encryptionLevel, enum TWStoredKeyEncryption encryption);

/// Creates a new key.
///
/// \deprecated use TWStoredKeyCreateLevel.
Expand All @@ -78,6 +115,16 @@ struct TWStoredKey* _Nonnull TWStoredKeyCreateLevel(TWString* _Nonnull name, TWD
/// \return The stored key as a non-null pointer
TW_EXPORT_STATIC_METHOD struct TWStoredKey* _Nonnull TWStoredKeyCreate(TWString* _Nonnull name, TWData* _Nonnull password);

/// Creates a new key.
///
/// \deprecated use TWStoredKeyCreateLevel.
/// \param name The name of the key to be stored
/// \param password Non-null block of data, password of the stored key
/// \param encryption cipher encryption mode
/// \note Returned object needs to be deleted with \TWStoredKeyDelete
/// \return The stored key as a non-null pointer
TW_EXPORT_STATIC_METHOD struct TWStoredKey* _Nonnull TWStoredKeyCreateEncryption(TWString* _Nonnull name, TWData* _Nonnull password, enum TWStoredKeyEncryption encryption);

/// Delete a stored key
///
/// \param key The key to be deleted
Expand Down
22 changes: 22 additions & 0 deletions include/TrustWalletCore/TWStoredKeyEncryption.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// Copyright © 2017-2022 Trust Wallet.
//
// This file is part of Trust. The full Trust copyright notice, including
// terms governing use, modification, and redistribution, is contained in the
// file LICENSE at the root of the source code distribution tree.

#pragma once

#include "TWBase.h"

TW_EXTERN_C_BEGIN

/// Preset encryption kind
TW_EXPORT_ENUM(uint32_t)
enum TWStoredKeyEncryption {
TWStoredKeyEncryptionAes128Ctr = 0,
TWStoredKeyEncryptionAes128Cbc = 1,
TWStoredKeyEncryptionAes192Ctr = 2,
TWStoredKeyEncryptionAes256Ctr = 3,
};

TW_EXTERN_C_END
10 changes: 7 additions & 3 deletions src/Bitcoin/Script.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -260,11 +260,15 @@ Script Script::buildPayToV1WitnessProgram(const Data& publicKey) {

Script Script::buildOpReturnScript(const Data& data) {
static const size_t MaxOpReturnLength = 80;
if (data.size() > MaxOpReturnLength) {
// data too long, cannot fit, fail (do not truncate)
return Script();
}
assert(data.size() <= MaxOpReturnLength);
Script script;
script.bytes.push_back(OP_RETURN);
size_t size = std::min(data.size(), MaxOpReturnLength);
script.bytes.push_back(static_cast<byte>(size));
script.bytes.insert(script.bytes.end(), data.begin(), data.begin() + size);
script.bytes.push_back(static_cast<byte>(data.size()));
script.bytes.insert(script.bytes.end(), data.begin(), data.begin() + data.size());
return script;
}

Expand Down
4 changes: 2 additions & 2 deletions src/Bitcoin/Script.h
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright © 2017-2021 Trust Wallet.
// Copyright © 2017-2022 Trust Wallet.
//
// This file is part of Trust. The full Trust copyright notice, including
// terms governing use, modification, and redistribution, is contained in the
Expand Down Expand Up @@ -91,7 +91,7 @@ class Script {
/// Builds a V1 pay-to-witness-program script, P2TR (from a 32-byte Schnorr public key).
static Script buildPayToV1WitnessProgram(const Data& publicKey);

/// Builds an OP_RETURN script with given data
/// Builds an OP_RETURN script with given data. Returns empty script on error, if data is too long (>80).
static Script buildOpReturnScript(const Data& data);

/// Builds a appropriate lock script for the given
Expand Down
18 changes: 13 additions & 5 deletions src/Bitcoin/TransactionBuilder.h
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright © 2017-2021 Trust Wallet.
// Copyright © 2017-2022 Trust Wallet.
//
// This file is part of Trust. The full Trust copyright notice, including
// terms governing use, modification, and redistribution, is contained in the
Expand All @@ -10,6 +10,7 @@
#include "Transaction.h"
#include "TransactionPlan.h"
#include "InputSelector.h"
#include "../Result.h"
#include "../proto/Bitcoin.pb.h"
#include <TrustWalletCore/TWCoinType.h>

Expand All @@ -25,18 +26,22 @@ class TransactionBuilder {

/// Builds a transaction with the selected input UTXOs, and one main output and an optional change output.
template <typename Transaction>
static Transaction build(const TransactionPlan& plan, const std::string& toAddress,
static Result<Transaction, Common::Proto::SigningError> build(const TransactionPlan& plan, const std::string& toAddress,
const std::string& changeAddress, enum TWCoinType coin, uint32_t lockTime) {
Transaction tx;
tx.lockTime = lockTime;

auto outputTo = prepareOutputWithScript(toAddress, plan.amount, coin);
if (!outputTo.has_value()) { return {}; }
if (!outputTo.has_value()) {
return Result<Transaction, Common::Proto::SigningError>::failure(Common::Proto::Error_invalid_address);
}
tx.outputs.push_back(outputTo.value());

if (plan.change > 0) {
auto outputChange = prepareOutputWithScript(changeAddress, plan.change, coin);
if (!outputChange.has_value()) { return {}; }
if (!outputChange.has_value()) {
return Result<Transaction, Common::Proto::SigningError>::failure(Common::Proto::Error_invalid_address);
}
tx.outputs.push_back(outputChange.value());
}

Expand All @@ -48,10 +53,13 @@ class TransactionBuilder {
// Optional OP_RETURN output
if (plan.outputOpReturn.size() > 0) {
auto lockingScriptOpReturn = Script::buildOpReturnScript(plan.outputOpReturn);
if (lockingScriptOpReturn.bytes.size() == 0) {
return Result<Transaction, Common::Proto::SigningError>::failure(Common::Proto::Error_invalid_memo);
}
tx.outputs.emplace_back(0, lockingScriptOpReturn);
}

return tx;
return Result<Transaction, Common::Proto::SigningError>(tx);
}

/// Prepares a TransactionOutput with given address and amount, prepares script for it
Expand Down
12 changes: 10 additions & 2 deletions src/Bitcoin/TransactionSigner.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,11 @@ Result<Transaction, Common::Proto::SigningError> TransactionSigner<Transaction,
} else {
plan = TransactionBuilder::plan(input);
}
auto transaction = TransactionBuilder::template build<Transaction>(plan, input.toAddress, input.changeAddress, input.coinType, input.lockTime);
auto tx_result = TransactionBuilder::template build<Transaction>(plan, input.toAddress, input.changeAddress, input.coinType, input.lockTime);
if (!tx_result) {
return Result<Transaction, Common::Proto::SigningError>::failure(tx_result.error());
}
Transaction transaction = tx_result.payload();
SigningMode signingMode =
estimationMode ? SigningMode_SizeEstimationOnly : optionalExternalSigs.has_value() ? SigningMode_External
: SigningMode_Normal;
Expand All @@ -43,7 +47,11 @@ Result<HashPubkeyList, Common::Proto::SigningError> TransactionSigner<Transactio
} else {
plan = TransactionBuilder::plan(input);
}
auto transaction = TransactionBuilder::template build<Transaction>(plan, input.toAddress, input.changeAddress, input.coinType, input.lockTime);
auto tx_result = TransactionBuilder::template build<Transaction>(plan, input.toAddress, input.changeAddress, input.coinType, input.lockTime);
if (!tx_result) {
return Result<HashPubkeyList, Common::Proto::SigningError>::failure(tx_result.error());
}
Transaction transaction = tx_result.payload();
SignatureBuilder<Transaction> signer(std::move(input), plan, transaction, SigningMode_HashOnly);
auto signResult = signer.sign();
if (!signResult) {
Expand Down
41 changes: 36 additions & 5 deletions src/Keystore/AESParameters.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,20 +12,44 @@

using namespace TW;

namespace TW::Keystore {
namespace {

AESParameters::AESParameters() {
iv = Data(blockSize, 0);
Data generateIv(std::size_t blockSize = TW::Keystore::gBlockSize) {
auto iv = Data(blockSize, 0);
random_buffer(iv.data(), blockSize);
return iv;
}

static TWStoredKeyEncryption getCipher(const std::string& cipher) {
if (cipher == Keystore::gAes128Ctr) {
return TWStoredKeyEncryption::TWStoredKeyEncryptionAes128Ctr;
} else if (cipher == Keystore::gAes192Ctr) {
return TWStoredKeyEncryption::TWStoredKeyEncryptionAes192Ctr;
} else if (cipher == Keystore::gAes256Ctr) {
return TWStoredKeyEncryption::TWStoredKeyEncryptionAes256Ctr;
}
return TWStoredKeyEncryptionAes128Ctr;
}

const std::unordered_map<TWStoredKeyEncryption, Keystore::AESParameters> gEncryptionRegistry{
{TWStoredKeyEncryptionAes128Ctr, Keystore::AESParameters{.mKeyLength = Keystore::A128, .mCipher = Keystore::gAes128Ctr, .mCipherEncryption = TWStoredKeyEncryptionAes128Ctr}},
{TWStoredKeyEncryptionAes128Cbc, Keystore::AESParameters{.mKeyLength = Keystore::A128, .mCipher = Keystore::gAes128Cbc, .mCipherEncryption = TWStoredKeyEncryptionAes128Cbc}},
{TWStoredKeyEncryptionAes192Ctr, Keystore::AESParameters{.mKeyLength = Keystore::A192, .mCipher = Keystore::gAes192Ctr, .mCipherEncryption = TWStoredKeyEncryptionAes192Ctr}},
{TWStoredKeyEncryptionAes256Ctr, Keystore::AESParameters{.mKeyLength = Keystore::A256, .mCipher = Keystore::gAes256Ctr, .mCipherEncryption = TWStoredKeyEncryptionAes256Ctr}}
};
} // namespace

namespace TW::Keystore {

namespace CodingKeys {
static const auto iv = "iv";
} // namespace CodingKeys

/// Initializes `AESParameters` with a JSON object.
AESParameters::AESParameters(const nlohmann::json& json) {
iv = parse_hex(json[CodingKeys::iv].get<std::string>());
AESParameters AESParameters::AESParametersFromJson(const nlohmann::json& json, const std::string& cipher) {
auto parameters = AESParameters::AESParametersFromEncryption(getCipher(cipher));
parameters.iv = parse_hex(json[CodingKeys::iv].get<std::string>());
return parameters;
}

/// Saves `this` as a JSON object.
Expand All @@ -35,4 +59,11 @@ nlohmann::json AESParameters::json() const {
return j;
}

AESParameters AESParameters::AESParametersFromEncryption(TWStoredKeyEncryption encryption) {
auto parameters = gEncryptionRegistry.at(encryption);
// be sure to regenerate an iv.
parameters.iv = generateIv();
return parameters;
}

} // namespace TW::Keystore
Loading