Skip to content

Commit

Permalink
fix review
Browse files Browse the repository at this point in the history
  • Loading branch information
supermassive committed Jan 14, 2025
1 parent 5a5e21b commit 74e5c72
Show file tree
Hide file tree
Showing 6 changed files with 87 additions and 81 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -11,28 +11,43 @@
#pragma allow_unsafe_buffers
#endif

static int IsScalarPruned(const uint8_t scalar[32]) {
return (scalar[0] & 0b00000111) == 0b00000000 &&
(scalar[31] & 0b11000000) == 0b01000000;
}

// Produces pubkey form scalar.
// `scalar` must be pruned. https://www.rfc-editor.org/rfc/rfc8032.html#section-5.1.5
// Function fails if `scalar` is not pruned. https://www.rfc-editor.org/rfc/rfc8032.html#section-5.1.5
// See `ED25519_keypair_from_seed` as origin.
void ED25519_pubkey_from_scalar(uint8_t out_public_key[32],
const uint8_t scalar[32]) {
int ED25519_pubkey_from_scalar(uint8_t out_public_key[32],
const uint8_t scalar[32]) {
if (!IsScalarPruned(scalar)) {
return 0;
}

ge_p3 A;

x25519_ge_scalarmult_base(&A, scalar);
ge_p3_tobytes(out_public_key, &A);

CONSTTIME_DECLASSIFY(out_public_key, 32);

return 1;
}

// Same as `ED25519_sign` but without hashing private key. `scalar` and `prefix`
// come from ED25519_BIP32 algorithm.
// `scalar` must be pruned. https://www.rfc-editor.org/rfc/rfc8032.html#section-5.1.5
// Function fails if `scalar` is not pruned. https://www.rfc-editor.org/rfc/rfc8032.html#section-5.1.5
int ED25519_sign_with_scalar_and_prefix(uint8_t out_sig[64],
const uint8_t* message,
size_t message_len,
const uint8_t scalar[32],
const uint8_t prefix[32],
const uint8_t public_key[32]) {
if (!IsScalarPruned(scalar)) {
return 0;
}

SHA512_CTX hash_ctx;
SHA512_Init(&hash_ctx);
SHA512_Update(&hash_ctx, prefix, 32);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@
extern "C" {
#endif

OPENSSL_EXPORT void ED25519_pubkey_from_scalar(uint8_t out_public_key[32],
const uint8_t scalar[32]);
OPENSSL_EXPORT int ED25519_pubkey_from_scalar(uint8_t out_public_key[32],
const uint8_t scalar[32]);
OPENSSL_EXPORT int ED25519_sign_with_scalar_and_prefix(
uint8_t out_sig[64],
const uint8_t* message,
Expand Down
6 changes: 5 additions & 1 deletion components/brave_wallet/browser/internal/hd_key_common.cc
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,11 @@ DerivationIndex DerivationIndex::Hardened(uint32_t index) {

// static
DerivationIndex DerivationIndex::FromRawValueForTesting(uint32_t index) {
return DerivationIndex(index % kHardenedOffset, index / kHardenedOffset);
if (index >= kHardenedOffset) {
return DerivationIndex(index - kHardenedOffset, true);
} else {
return DerivationIndex(index, false);
}
}

bool DerivationIndex::IsValid() const {
Expand Down
124 changes: 55 additions & 69 deletions components/brave_wallet/browser/internal/hd_key_ed25519_slip23.cc
Original file line number Diff line number Diff line change
Expand Up @@ -14,18 +14,13 @@
#include "base/containers/span_writer.h"
#include "base/memory/ptr_util.h"
#include "base/strings/string_number_conversions.h"
#include "brave/components/brave_wallet/common/hash_utils.h"
#include "crypto/hmac.h"
#include "third_party/boringssl/src/include/openssl/curve25519.h"
#include "third_party/boringssl/src/include/openssl/evp.h"

namespace brave_wallet {
namespace {

struct DerivedHmacs {
std::array<uint8_t, crypto::hash::kSha512Size> z_hmac = {};
std::array<uint8_t, crypto::hash::kSha512Size> cc_hmac = {};
};
namespace {

// https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-eddsa-05#section-5.1.5
// requires scalar to follow this requirements 'The lowest 3 bits of the first
Expand Down Expand Up @@ -55,58 +50,16 @@ bool IsValidEd25519Scalar(base::span<const uint8_t, kSlip23ScalarSize> scalar) {
(scalar[31] & 0b1100'0000) == 0b0100'0000;
}

std::array<uint8_t, 32> PubkeyFromScalar(base::span<const uint8_t, 32> scalar) {
std::optional<std::array<uint8_t, 32>> PubkeyFromScalar(
base::span<const uint8_t, 32> scalar) {
DCHECK(IsValidEd25519Scalar(scalar));
std::array<uint8_t, 32> public_key;
ED25519_pubkey_from_scalar(public_key.data(), scalar.data());
if (!ED25519_pubkey_from_scalar(public_key.data(), scalar.data())) {
return std::nullopt;
}
return public_key;
}

DerivedHmacs DeriveHardenedHmacs(
uint32_t index,
base::span<const uint8_t, kSlip23ScalarSize> scalar,
base::span<const uint8_t, kSlip23PrefixSize> prefix,
base::span<const uint8_t> cc) {
std::array<uint8_t, 1 + 32 + 32 + 4> data;
auto span_writer = base::SpanWriter(base::span(data));
span_writer.Skip(1u);
span_writer.Write(scalar);
span_writer.Write(prefix);
span_writer.WriteU32LittleEndian(index);

DerivedHmacs result;

data[0] = 0x00;
result.z_hmac = crypto::hmac::SignSha512(cc, data);

data[0] = 0x01;
result.cc_hmac = crypto::hmac::SignSha512(cc, data);

return result;
}

DerivedHmacs DeriveNormalHmacs(
uint32_t index,
base::span<const uint8_t, kSlip23ScalarSize> scalar,
base::span<const uint8_t, kEd25519PublicKeySize> public_key,
base::span<const uint8_t> cc) {
std::array<uint8_t, 1 + 32 + 4> data;
auto span_writer = base::SpanWriter(base::span(data));
span_writer.Skip(1u);
span_writer.Write(public_key);
span_writer.WriteU32LittleEndian(index);

DerivedHmacs result;

data[0] = 0x02;
result.z_hmac = crypto::hmac::SignSha512(cc, data);

data[0] = 0x03;
result.cc_hmac = crypto::hmac::SignSha512(cc, data);

return result;
}

std::array<uint8_t, kSlip23ScalarSize> CalculateDerivedScalar(
base::span<const uint8_t, kSlip23ScalarSize> parent_scalar,
base::span<const uint8_t, kSlip23DerivationScalarSize> zl) {
Expand Down Expand Up @@ -158,11 +111,12 @@ HDKeyEd25519Slip23::~HDKeyEd25519Slip23() = default;
HDKeyEd25519Slip23::HDKeyEd25519Slip23(
base::span<const uint8_t, kSlip23ScalarSize> scalar,
base::span<const uint8_t, kSlip23PrefixSize> prefix,
base::span<const uint8_t, kSlip23ChainCodeSize> chain_code) {
base::span<const uint8_t, kSlip23ChainCodeSize> chain_code,
base::span<const uint8_t, kEd25519PublicKeySize> public_key) {
base::span(scalar_).copy_from(scalar);
base::span(prefix_).copy_from(prefix);
base::span(chain_code_).copy_from(chain_code);
public_key_ = PubkeyFromScalar(scalar_);
base::span(public_key_).copy_from(public_key);
}

std::unique_ptr<HDKeyEd25519Slip23> HDKeyEd25519Slip23::DeriveChild(
Expand All @@ -172,19 +126,47 @@ std::unique_ptr<HDKeyEd25519Slip23> HDKeyEd25519Slip23::DeriveChild(
return nullptr;
}

auto derived =
index.is_hardened()
? DeriveHardenedHmacs(*raw_index_value, scalar_, prefix_, chain_code_)
: DeriveNormalHmacs(*raw_index_value, scalar_, public_key_,
chain_code_);
std::array<uint8_t, crypto::hash::kSha512Size> z_hmac = {};
std::array<uint8_t, crypto::hash::kSha512Size> cc_hmac = {};

if (index.is_hardened()) {
std::array<uint8_t, 1 + 32 + 32 + 4> data;
auto span_writer = base::SpanWriter(base::span(data));
span_writer.Skip(1u);
span_writer.Write(scalar_);
span_writer.Write(prefix_);
span_writer.WriteU32LittleEndian(*raw_index_value);

data[0] = 0x00;
z_hmac = crypto::hmac::SignSha512(chain_code_, data);
data[0] = 0x01;
cc_hmac = crypto::hmac::SignSha512(chain_code_, data);
} else {
std::array<uint8_t, 1 + 32 + 4> data;
auto span_writer = base::SpanWriter(base::span(data));
span_writer.Skip(1u);
span_writer.Write(public_key_);
span_writer.WriteU32LittleEndian(*raw_index_value);

data[0] = 0x02;
z_hmac = crypto::hmac::SignSha512(chain_code_, data);
data[0] = 0x03;
cc_hmac = crypto::hmac::SignSha512(chain_code_, data);
}

auto derived_scalar = CalculateDerivedScalar(
scalar_, base::span(z_hmac).first<kSlip23DerivationScalarSize>());

auto pubkey = PubkeyFromScalar(derived_scalar);
if (!pubkey) {
return nullptr;
}

return base::WrapUnique(new HDKeyEd25519Slip23(
CalculateDerivedScalar(
scalar_,
base::span(derived.z_hmac).first<kSlip23DerivationScalarSize>()),
CalculateDerivedPrefix(
prefix_, base::span(derived.z_hmac).last<kSlip23PrefixSize>()),
CalculateDerivedChainCode(derived.cc_hmac)));
derived_scalar,
CalculateDerivedPrefix(prefix_,
base::span(z_hmac).last<kSlip23PrefixSize>()),
CalculateDerivedChainCode(cc_hmac), *pubkey));
}

// static
Expand All @@ -204,11 +186,15 @@ HDKeyEd25519Slip23::GenerateMasterKeyFromBip39Entropy(
}

auto xprv_span = base::span(xprv);
auto scalar = ClampScalarEd25519Bip32(xprv_span.first<kSlip23ScalarSize>());
auto pubkey = PubkeyFromScalar(scalar);
if (!pubkey) {
return nullptr;
}

return base::WrapUnique(new HDKeyEd25519Slip23(
ClampScalarEd25519Bip32(xprv_span.first<kSlip23ScalarSize>()),
xprv_span.subspan<kSlip23ScalarSize, kSlip23PrefixSize>(),
xprv_span.last<kSlip23ChainCodeSize>()));
scalar, xprv_span.subspan<kSlip23ScalarSize, kSlip23PrefixSize>(),
xprv_span.last<kSlip23ChainCodeSize>(), *pubkey));
}

std::optional<std::array<uint8_t, kEd25519SignatureSize>>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
#define BRAVE_COMPONENTS_BRAVE_WALLET_BROWSER_INTERNAL_HD_KEY_ED25519_SLIP23_H_

#include <memory>
#include <string>
#include <string_view>

#include "base/containers/span.h"
#include "base/gtest_prod_util.h"
Expand Down Expand Up @@ -52,7 +52,8 @@ class HDKeyEd25519Slip23 {
HDKeyEd25519Slip23(
base::span<const uint8_t, kSlip23ScalarSize> scalar,
base::span<const uint8_t, kSlip23PrefixSize> prefix,
base::span<const uint8_t, kSlip23ChainCodeSize> chain_code);
base::span<const uint8_t, kSlip23ChainCodeSize> chain_code,
base::span<const uint8_t, kEd25519PublicKeySize> public_key);

static std::unique_ptr<HDKeyEd25519Slip23> FromBip32Entropy(
base::span<const uint8_t> seed,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -228,18 +228,18 @@ TEST(HDKeyEd25519Slip23UnitTest, CardanoSdkCryptoSlip23) {
}

// Reference implementation encodes pubkey as `pubkey|chain_code`.
EXPECT_EQ(*test_dict.FindString("pubkey"),
ASSERT_EQ(*test_dict.FindString("pubkey"),
HexEncodeLower(key->GetPublicKeyAsSpan()) +
HexEncodeLower(key->GetChainCodeAsSpanForTesting()));

// Reference implementation encodes private key as
// `scalar|prefix|chain_code`.
EXPECT_EQ(*test_dict.FindString("privatekey"),
ASSERT_EQ(*test_dict.FindString("privatekey"),
HexEncodeLower(key->GetScalarAsSpanForTesting()) +
HexEncodeLower(key->GetPrefixAsSpanForTesting()) +
HexEncodeLower(key->GetChainCodeAsSpanForTesting()));

EXPECT_EQ(
ASSERT_EQ(
*test_dict.FindString("signature"),
HexEncodeLower(*key->Sign(base::byte_span_from_cstring("message"))));
}
Expand Down

0 comments on commit 74e5c72

Please sign in to comment.