Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Wallet openssl ed25519 #26770

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
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
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
3 changes: 1 addition & 2 deletions components/brave_wallet/browser/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -432,11 +432,10 @@ source_set("hd_keyring") {
"//brave/components/brave_wallet/common:buildflags",
"//brave/components/brave_wallet/common:common_constants",
"//brave/components/brave_wallet/common:mojom",
"//brave/components/brave_wallet/rust:rust_lib",
"//brave/components/filecoin/rs:rust_lib",
"//crypto",
]

public_deps = [ "//brave/components/brave_wallet/rust:rust_lib" ]
}

source_set("transaction") {
Expand Down
3 changes: 2 additions & 1 deletion components/brave_wallet/browser/internal/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ 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 All @@ -28,7 +30,6 @@ source_set("hd_key") {
"//third_party/boringssl",
]

public_deps = [ "//brave/components/brave_wallet/rust:rust_lib" ]
if (enable_orchard) {
sources += [
"hd_key_zip32.cc",
Expand Down
68 changes: 10 additions & 58 deletions components/brave_wallet/browser/internal/hd_key.cc
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
#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 @@ -35,7 +36,6 @@
#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,10 +47,7 @@ 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 @@ -136,19 +133,10 @@ std::unique_ptr<HDKey> HDKey::GenerateFromSeed(base::span<const uint8_t> seed) {
return nullptr;
}

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);
auto hmac = HmacSha512(base::byte_span_from_cstring(kMasterSecret), seed);
auto [IL, IR] = base::span(hmac).split_at(kSHA512HashLength / 2);

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 @@ -549,18 +537,8 @@ std::unique_ptr<HDKey> HDKey::DeriveChild(uint32_t index) {
data.push_back((index >> 8) & 0xFF);
data.push_back(index & 0xFF);

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);
auto hmac = HmacSha512(chain_code_, data);
auto [IL, IR] = base::span(hmac).split_at(kSHA512HashLength / 2);

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

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()) {
const auto hd_path = ParseFullHDPath(path);
if (!hd_path) {
return nullptr;
}

// 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;
}
std::unique_ptr<HDKey> hd_key = std::make_unique<HDKey>();
hd_key->SetPrivateKey(private_key_);
hd_key->SetChainCode(chain_code_);
hd_key->path_ = path_;

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);
for (auto node : *hd_path) {
hd_key = hd_key->DeriveChild(node);
if (!hd_key) {
return nullptr;
}
Expand Down
166 changes: 89 additions & 77 deletions components/brave_wallet/browser/internal/hd_key_ed25519.cc
Original file line number Diff line number Diff line change
Expand Up @@ -6,54 +6,66 @@
#include "brave/components/brave_wallet/browser/internal/hd_key_ed25519.h"

#include <utility>
#include <vector>

#include "base/check.h"
#include "base/logging.h"
#include "base/notreached.h"
#include "base/strings/strcat.h"
#include "base/containers/span.h"
#include "base/containers/span_writer.h"
#include "base/memory/ptr_util.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 "third_party/boringssl/src/include/openssl/curve25519.h"

namespace brave_wallet {
namespace {
constexpr char kMasterNode[] = "m";
}

HDKeyEd25519::HDKeyEd25519(
std::string path,
rust::Box<Ed25519DalekExtendedSecretKeyResult> private_key)
: path_(std::move(path)), private_key_(std::move(private_key)) {
CHECK(private_key_->is_ok());
constexpr char kMasterSecret[] = "ed25519 seed";

// Validate keypair by signing and verifying signature to ensure included
// private key matches public key.
bool ValidateKeypair(base::span<const uint8_t, kEd25519KeypairSize> key_pair) {
std::array<uint8_t, kEd25519SignatureSize> signature = {};
auto msg = base::byte_span_from_cstring("brave");

CHECK(
ED25519_sign(signature.data(), msg.data(), msg.size(), key_pair.data()));

return !!ED25519_verify(msg.data(), msg.size(), signature.data(),
key_pair.last<kEd25519PublicKeySize>().data());
}

} // namespace

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

// static
std::unique_ptr<HDKeyEd25519> HDKeyEd25519::GenerateFromSeed(
base::span<const uint8_t> seed) {
auto master_private_key = generate_ed25519_extended_secret_key_from_seed(
rust::Slice<const uint8_t>{seed.data(), seed.size()});
if (!master_private_key->is_ok()) {
VLOG(0) << std::string(master_private_key->error_message());
return nullptr;
}
return std::make_unique<HDKeyEd25519>(kMasterNode,
std::move(master_private_key));
// Child key derivation constructor.
// https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki#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 [il, ir] = base::span(hmac).split_at<32>();

// `public_key` is not used, we use key pair instead.
std::array<uint8_t, ED25519_PUBLIC_KEY_LEN> public_key;
ED25519_keypair_from_seed(public_key.data(), key_pair_.data(), il.data());

base::span(chain_code_).copy_from(ir);
}

// static
std::unique_ptr<HDKeyEd25519> HDKeyEd25519::GenerateFromPrivateKey(
base::span<const uint8_t> private_key) {
auto master_private_key = generate_ed25519_extended_secret_key_from_bytes(
rust::Slice<const uint8_t>{private_key.data(), private_key.size()});
if (!master_private_key->is_ok()) {
VLOG(0) << std::string(master_private_key->error_message());
std::unique_ptr<HDKeyEd25519> HDKeyEd25519::GenerateFromKeyPair(
base::span<const uint8_t, kEd25519KeypairSize> key_pair) {
if (!ValidateKeypair(key_pair)) {
return nullptr;
}
return std::make_unique<HDKeyEd25519>("", std::move(master_private_key));
}

std::string HDKeyEd25519::GetPath() const {
return path_;
auto result = std::make_unique<HDKeyEd25519>();
base::span(result->key_pair_).copy_from(key_pair);
return result;
}

// index should be 0 to 2^31-1
Expand All @@ -62,74 +74,74 @@ std::string HDKeyEd25519::GetPath() const {
// https://github.com/satoshilabs/slips/blob/master/slip-0010.md#private-parent-key--private-child-key
std::unique_ptr<HDKeyEd25519> HDKeyEd25519::DeriveHardenedChild(
uint32_t index) {
auto child_private_key = private_key_->unwrap().derive_hardened_child(index);
if (!child_private_key->is_ok()) {
VLOG(0) << std::string(child_private_key->error_message());
if (index >= kHardenedOffset) {
return nullptr;
}
auto child_path =
base::StrCat({path_, "/", base::NumberToString(index), "'"});
return std::make_unique<HDKeyEd25519>(std::move(child_path),
std::move(child_private_key));
return DeriveChild(kHardenedOffset + index);
}

std::unique_ptr<HDKeyEd25519> HDKeyEd25519::DeriveChild(uint32_t index) {
std::vector<uint8_t> hmac_payload(37);

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));

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

std::unique_ptr<HDKeyEd25519> HDKeyEd25519::DeriveChildFromPath(
const std::string& path) {
if (path_ != kMasterNode) {
VLOG(0) << "must derive only from master key";
// 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;
}

auto child_private_key = private_key_->unwrap().derive(path);
if (!child_private_key->is_ok()) {
VLOG(0) << std::string(child_private_key->error_message());
return nullptr;
for (auto index : *nodes) {
if (index < kHardenedOffset) {
return nullptr;
}
hd_key = hd_key->DeriveChild(index);
if (!hd_key) {
return nullptr;
}
}

return std::make_unique<HDKeyEd25519>(path, std::move(child_private_key));
return hd_key;
}

std::vector<uint8_t> HDKeyEd25519::Sign(base::span<const uint8_t> msg) {
auto signature_result = private_key_->unwrap().sign(
rust::Slice<const uint8_t>{msg.data(), msg.size()});
if (!signature_result->is_ok()) {
VLOG(0) << std::string(signature_result->error_message());
return std::vector<uint8_t>();
}
auto signature_bytes = signature_result->unwrap().to_bytes();
return std::vector<uint8_t>(signature_bytes.begin(), signature_bytes.end());
}
std::array<uint8_t, kEd25519SignatureSize> HDKeyEd25519::Sign(
base::span<const uint8_t> msg) {
std::array<uint8_t, kEd25519SignatureSize> signature = {};

bool HDKeyEd25519::VerifyForTesting(base::span<const uint8_t> msg,
base::span<const uint8_t> sig) {
auto verification_result = private_key_->unwrap().verify(
rust::Slice<const uint8_t>{msg.data(), msg.size()},
rust::Slice<const uint8_t>{sig.data(), sig.size()});
if (!verification_result->is_ok()) {
VLOG(0) << std::string(verification_result->error_message());
return false;
}
return true;
CHECK(
ED25519_sign(signature.data(), msg.data(), msg.size(), key_pair_.data()));
return signature;
}

std::vector<uint8_t> HDKeyEd25519::GetPrivateKeyBytes() const {
auto secret_key = private_key_->unwrap().secret_key_raw();
return {secret_key.begin(), secret_key.end()};
base::span<const uint8_t, kEd25519SecretKeySize>
HDKeyEd25519::GetPrivateKeyAsSpan() const {
return base::span(key_pair_).first<kEd25519SecretKeySize>();
}

std::vector<uint8_t> HDKeyEd25519::GetPublicKeyBytes() const {
auto public_key = private_key_->unwrap().public_key_raw();
return {public_key.begin(), public_key.end()};
base::span<const uint8_t, kEd25519PublicKeySize>
HDKeyEd25519::GetPublicKeyAsSpan() const {
return base::span(key_pair_).last<kEd25519PublicKeySize>();
}

std::string HDKeyEd25519::GetBase58EncodedPublicKey() const {
auto public_key = private_key_->unwrap().public_key_raw();
return EncodeBase58(public_key);
return EncodeBase58(GetPublicKeyAsSpan());
}

std::string HDKeyEd25519::GetBase58EncodedKeypair() const {
auto keypair = private_key_->unwrap().keypair_raw();
return EncodeBase58(keypair);
return EncodeBase58(key_pair_);
}

} // namespace brave_wallet
Loading
Loading