Skip to content

Commit

Permalink
fix review
Browse files Browse the repository at this point in the history
  • Loading branch information
supermassive committed Dec 20, 2024
1 parent dab3027 commit 2d23f5a
Show file tree
Hide file tree
Showing 15 changed files with 263 additions and 454 deletions.
2 changes: 0 additions & 2 deletions components/brave_wallet/browser/internal/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,6 @@ source_set("hd_key") {
"hd_key.h",
"hd_key_ed25519.cc",
"hd_key_ed25519.h",
"hd_key_utils.cc",
"hd_key_utils.h",
]

visibility = [
Expand Down
70 changes: 58 additions & 12 deletions components/brave_wallet/browser/internal/hd_key.cc
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_split.h"
#include "base/strings/string_util.h"
#include "brave/components/brave_wallet/browser/internal/hd_key_utils.h"
#include "brave/components/brave_wallet/common/bitcoin_utils.h"
#include "brave/components/brave_wallet/common/hash_utils.h"
#include "brave/components/brave_wallet/common/hex_utils.h"
Expand All @@ -36,6 +35,7 @@
#include "crypto/process_bound_string.h"
#include "crypto/random.h"
#include "crypto/symmetric_key.h"
#include "third_party/boringssl/src/include/openssl/hmac.h"

#define SECP256K1_BUILD // This effectively turns off export attributes.
#include "brave/third_party/bitcoin-core/src/src/secp256k1/include/secp256k1.h"
Expand All @@ -47,7 +47,10 @@ using crypto::SymmetricKey;
namespace brave_wallet {

namespace {
constexpr char kMasterNode[] = "m";
constexpr char kMasterSecret[] = "Bitcoin seed";
constexpr size_t kSHA512Length = 64;
constexpr uint32_t kHardenedOffset = 0x80000000;
constexpr size_t kSerializationLength = 78;
constexpr size_t kMaxDerSignatureSize = 72;
constexpr size_t kContextRandomizeSize = 32;
Expand Down Expand Up @@ -133,11 +136,19 @@ std::unique_ptr<HDKey> HDKey::GenerateFromSeed(base::span<const uint8_t> seed) {
return nullptr;
}

auto hmac = HmacSha512(base::byte_span_from_cstring(kMasterSecret), seed);
auto scoped_zero_span = ScopedSecureZeroSpan(hmac);
auto [IL, IR] = base::span(hmac).split_at(kSHA512HashLength / 2);
SecureVector hmac(kSHA512Length);
unsigned int out_len;
if (!HMAC(EVP_sha512(), kMasterSecret, sizeof(kMasterSecret), seed.data(),
seed.size(), hmac.data(), &out_len)) {
LOG(ERROR) << __func__ << ": HMAC_SHA512 failed";
return nullptr;
}
DCHECK(out_len == kSHA512Length);

std::unique_ptr<HDKey> hdkey = std::make_unique<HDKey>();
auto hmac_span = base::make_span(hmac);
auto IL = hmac_span.first(kSHA512Length / 2);
auto IR = hmac_span.last(kSHA512Length / 2);
hdkey->SetPrivateKey(IL);
hdkey->SetChainCode(IR);
hdkey->path_ = kMasterNode;
Expand Down Expand Up @@ -538,9 +549,18 @@ std::unique_ptr<HDKey> HDKey::DeriveChild(uint32_t index) {
data.push_back((index >> 8) & 0xFF);
data.push_back(index & 0xFF);

auto hmac = HmacSha512(chain_code_, data);
auto scoped_zero_span = ScopedSecureZeroSpan(hmac);
auto [IL, IR] = base::span(hmac).split_at(kSHA512HashLength / 2);
SecureVector hmac(kSHA512Length);
unsigned int out_len;
if (!HMAC(EVP_sha512(), chain_code_.data(), chain_code_.size(), data.data(),
data.size(), hmac.data(), &out_len)) {
LOG(ERROR) << __func__ << ": HMAC_SHA512 failed";
return nullptr;
}
DCHECK(out_len == kSHA512Length);

auto hmac_span = base::make_span(hmac);
auto IL = hmac_span.first(kSHA512Length / 2);
auto IR = hmac_span.last(kSHA512Length / 2);

std::unique_ptr<HDKey> hdkey = std::make_unique<HDKey>();
hdkey->SetChainCode(IR);
Expand Down Expand Up @@ -605,18 +625,44 @@ std::unique_ptr<HDKey> HDKey::DeriveChildFromPath(const std::string& path) {
return nullptr;
}

const auto hd_path = ParseFullHDPath(path);
if (!hd_path) {
std::unique_ptr<HDKey> hd_key = std::make_unique<HDKey>();
std::vector<std::string> entries =
base::SplitString(path, "/", base::WhitespaceHandling::TRIM_WHITESPACE,
base::SplitResult::SPLIT_WANT_NONEMPTY);
if (entries.empty()) {
return nullptr;
}

std::unique_ptr<HDKey> hd_key = std::make_unique<HDKey>();
// Starting with 'm' node and effectively copying `*this` into `hd_key`.
if (entries[0] != kMasterNode) {
LOG(ERROR) << __func__ << ": path must start with \"m\"";
return nullptr;
}
hd_key->SetPrivateKey(private_key_);
hd_key->SetChainCode(chain_code_);
hd_key->path_ = path_;

for (auto node : *hd_path) {
hd_key = hd_key->DeriveChild(node);
for (size_t i = 1; i < entries.size(); ++i) {
std::string entry = entries[i];

bool is_hardened = entry.length() > 1 && entry.back() == '\'';
if (is_hardened) {
entry.pop_back();
}
unsigned child_index = 0;
if (!base::StringToUint(entry, &child_index)) {
LOG(ERROR) << __func__ << ": path must contain number or number'";
return nullptr;
}
if (child_index >= kHardenedOffset) {
LOG(ERROR) << __func__ << ": index must be less than " << kHardenedOffset;
return nullptr;
}
if (is_hardened) {
child_index += kHardenedOffset;
}

hd_key = hd_key->DeriveChild(child_index);
if (!hd_key) {
return nullptr;
}
Expand Down
142 changes: 59 additions & 83 deletions components/brave_wallet/browser/internal/hd_key_ed25519.cc
Original file line number Diff line number Diff line change
Expand Up @@ -5,79 +5,82 @@

#include "brave/components/brave_wallet/browser/internal/hd_key_ed25519.h"

#include <memory>
#include <utility>

#include "base/check.h"
#include "base/containers/span.h"
#include "base/containers/span_writer.h"
#include "base/memory/ptr_util.h"
#include "base/containers/to_vector.h"
#include "base/numerics/byte_conversions.h"
#include "base/strings/string_number_conversions.h"
#include "brave/components/brave_wallet/browser/internal/hd_key_utils.h"
#include "brave/components/brave_wallet/common/hash_utils.h"
#include "brave/third_party/bitcoin-core/src/src/base58.h"
#include "crypto/hmac.h"
#include "third_party/boringssl/src/include/openssl/curve25519.h"

namespace brave_wallet {
namespace {

constexpr char kMasterSecret[] = "ed25519 seed";

// Validate keypair(private key matches public key) by using first half as a
// seed.
bool ValidateKeypair(base::span<const uint8_t, kEd25519KeypairSize> key_pair) {
std::array<uint8_t, kEd25519KeypairSize> validated_key_pair;

std::array<uint8_t, ED25519_PUBLIC_KEY_LEN> public_key;
ED25519_keypair_from_seed(public_key.data(), validated_key_pair.data(),
key_pair.first<ED25519_PRIVATE_KEY_LEN>().data());

return validated_key_pair == key_pair;
}
inline constexpr char kMasterSecret[] = "ed25519 seed";
inline constexpr uint32_t kHardenedOffset = 0x80000000;

// Returns concatenation of (private key, pubic key)
std::array<uint8_t, kEd25519KeypairSize> MakeKeyPair(
std::array<uint8_t, ED25519_PRIVATE_KEY_LEN> MakeKeyPair(
base::span<const uint8_t> private_key,
base::span<const uint8_t> public_key) {
std::array<uint8_t, kEd25519KeypairSize> key_pair = {};
std::array<uint8_t, ED25519_PRIVATE_KEY_LEN> key_pair = {};
base::span(key_pair).first<kEd25519PrivateKeySize>().copy_from(private_key);
base::span(key_pair).last<kEd25519PublicKeySize>().copy_from(public_key);
return key_pair;
}

std::array<uint8_t, kEd25519PublicKeySize> DerivePubkeyFromPrivateKey(
base::span<const uint8_t> private_key) {
std::array<uint8_t, kEd25519PublicKeySize> public_key = {};

// `key_pair` is not used and discarded, we need only public key.
std::array<uint8_t, ED25519_PRIVATE_KEY_LEN> key_pair;
ED25519_keypair_from_seed(public_key.data(), key_pair.data(),
private_key.data());

return public_key;
}

} // namespace

HDKeyEd25519::HDKeyEd25519() = default;
HDKeyEd25519::~HDKeyEd25519() = default;

// Child key derivation constructor.
// Child key derivation.
// https://github.com/satoshilabs/slips/blob/master/slip-0010.md#private-parent-key--private-child-key
HDKeyEd25519::HDKeyEd25519(base::span<const uint8_t> key,
base::span<const uint8_t> data) {
auto hmac = HmacSha512(key, data);
auto scoped_zero_span = ScopedSecureZeroSpan(hmac);
auto [il, ir] = base::span(hmac).split_at<32>();

private_key_.AsSpan().copy_from(il);
chain_code_.AsSpan().copy_from(ir);

// `key_pair` is not used, we need only public key.
std::array<uint8_t, kEd25519KeypairSize> key_pair;
ED25519_keypair_from_seed(public_key_.data(), key_pair.data(), il.data());
// static
std::unique_ptr<HDKeyEd25519> HDKeyEd25519::DeriveFromHmacPayload(
base::span<const uint8_t> key,
base::span<const uint8_t> data) {
auto hmac = crypto::hmac::SignSha512(key, data);
auto hmac_span = base::span(hmac);

auto result = std::make_unique<HDKeyEd25519>();
base::span(result->private_key_)
.copy_from(hmac_span.first<kEd25519PrivateKeySize>());
base::span(result->chain_code_)
.copy_from(hmac_span.last<kSlip10ChainCodeSize>());
result->public_key_ = DerivePubkeyFromPrivateKey(result->private_key_);
return result;
}

// static
std::unique_ptr<HDKeyEd25519> HDKeyEd25519::GenerateFromKeyPair(
base::span<const uint8_t, kEd25519KeypairSize> key_pair) {
if (!ValidateKeypair(key_pair)) {
std::unique_ptr<HDKeyEd25519> HDKeyEd25519::GenerateFromPrivateKey(
base::span<const uint8_t> private_key) {
auto private_key_fixed_size =
private_key.to_fixed_extent<kEd25519PrivateKeySize>();
if (!private_key_fixed_size) {
return nullptr;
}

auto result = std::make_unique<HDKeyEd25519>();
result->private_key_.AsSpan().copy_from(
key_pair.first<kEd25519PrivateKeySize>());
base::span(result->public_key_)
.copy_from(key_pair.last<kEd25519PublicKeySize>());
base::span(result->private_key_).copy_from(*private_key_fixed_size);
result->public_key_ = DerivePubkeyFromPrivateKey(result->private_key_);
return result;
}

Expand All @@ -90,80 +93,53 @@ std::unique_ptr<HDKeyEd25519> HDKeyEd25519::DeriveHardenedChild(
if (index >= kHardenedOffset) {
return nullptr;
}
return DeriveChild(kHardenedOffset + index);
}

std::unique_ptr<HDKeyEd25519> HDKeyEd25519::DeriveChild(uint32_t index) {
CHECK_GE(index, kHardenedOffset);

std::array<uint8_t, 37> hmac_payload = {};

auto span_writer = base::SpanWriter(base::span(hmac_payload));
// https://github.com/satoshilabs/slips/blob/master/slip-0010.md#private-parent-key--private-child-key
span_writer.Write(base::U8ToBigEndian(0));
span_writer.Write(GetPrivateKeyAsSpan());
span_writer.Write(base::U32ToBigEndian(index));
span_writer.Write(private_key_);
span_writer.Write(base::U32ToBigEndian(index + kHardenedOffset));
DCHECK_EQ(span_writer.remaining(), 0u);

return base::WrapUnique(new HDKeyEd25519(chain_code_.AsSpan(), hmac_payload));
return DeriveFromHmacPayload(chain_code_, hmac_payload);
}

// static
std::unique_ptr<HDKeyEd25519> HDKeyEd25519::GenerateFromSeedAndPath(
base::span<const uint8_t> seed,
std::string_view hd_path) {
auto hd_key = base::WrapUnique(
new HDKeyEd25519(base::byte_span_from_cstring(kMasterSecret), seed));

auto nodes = ParseFullHDPath(hd_path);
if (!nodes) {
return nullptr;
}

for (auto index : *nodes) {
if (index < kHardenedOffset) {
return nullptr;
}
hd_key = hd_key->DeriveChild(index);
if (!hd_key) {
return nullptr;
}
}

return hd_key;
std::unique_ptr<HDKeyEd25519> HDKeyEd25519::GenerateFromSeed(
base::span<const uint8_t> seed) {
return DeriveFromHmacPayload(base::byte_span_from_cstring(kMasterSecret),
seed);
}

std::optional<std::array<uint8_t, kEd25519SignatureSize>> HDKeyEd25519::Sign(
base::span<const uint8_t> msg) {
std::vector<uint8_t> HDKeyEd25519::Sign(base::span<const uint8_t> msg) {
// For the sake of performance `ED25519_sign` does not derive public key from
// private key, but assumes they are both already available as key_pair.
std::array<uint8_t, kEd25519KeypairSize> key_pair =
MakeKeyPair(private_key_.AsSpan(), public_key_);
std::array<uint8_t, ED25519_PRIVATE_KEY_LEN> key_pair =
MakeKeyPair(private_key_, public_key_);

std::array<uint8_t, kEd25519SignatureSize> signature = {};
std::vector<uint8_t> signature(ED25519_SIGNATURE_LEN);
if (!ED25519_sign(signature.data(), msg.data(), msg.size(),
key_pair.data())) {
return std::nullopt;
return {};
}
return signature;
}

base::span<const uint8_t, kEd25519PrivateKeySize>
HDKeyEd25519::GetPrivateKeyAsSpan() const {
return private_key_.AsSpan();
std::vector<uint8_t> HDKeyEd25519::GetPrivateKeyBytes() const {
return base::ToVector(private_key_);
}

base::span<const uint8_t, kEd25519PublicKeySize>
HDKeyEd25519::GetPublicKeyAsSpan() const {
return public_key_;
std::vector<uint8_t> HDKeyEd25519::GetPublicKeyBytes() const {
return base::ToVector(public_key_);
}

std::string HDKeyEd25519::GetBase58EncodedPublicKey() const {
return EncodeBase58(GetPublicKeyAsSpan());
return EncodeBase58(public_key_);
}

std::string HDKeyEd25519::GetBase58EncodedKeypair() const {
return EncodeBase58(MakeKeyPair(private_key_.AsSpan(), public_key_));
return EncodeBase58(MakeKeyPair(private_key_, public_key_));
}

} // namespace brave_wallet
Loading

0 comments on commit 2d23f5a

Please sign in to comment.