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

Implement SLIP-0023 for Cardano #27152

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open

Implement SLIP-0023 for Cardano #27152

wants to merge 5 commits into from

Conversation

supermassive
Copy link
Collaborator

@supermassive supermassive commented Jan 8, 2025

Resolves brave/brave-browser#43201
Sec review: https://github.com/brave/reviews/issues/1840

The PR includes:

  • Master key generation from bip-39 entropy.
  • Private child key generation for normal and hardened indexes.
  • Adjusted BoringSSL/OpenSSL methods for pubkey derivation and signing:
    • ED25519_pubkey_from_scalar. Generates pubkey from scalar Ai = [kL]B. Same as part of ED25519_keypair_from_seed but scalar comes from Ed25519_BIP32 algorithm instead of hashed seed
    • ED25519_sign_with_scalar_and_prefix. Same as a part of ED25519_sign. For ED25519_sign private_key[0..31] -> SHA512 -> [scalar, prefix] and private_key[32..63] -> public_key. For ED25519_sign_with_scalar_and_prefix scalar, prefix, public_key come from Ed25519_BIP32 algorithm
    • Also added pseudo random test vectors generated with @cardano-sdk/crypto library

Reference implementations:

Submitter Checklist:

  • I confirm that no security/privacy review is needed and no other type of reviews are needed, or that I have requested them
  • There is a ticket for my issue
  • Used Github auto-closing keywords in the PR description above
  • Wrote a good PR/commit description
  • Squashed any review feedback or "fixup" commits before merge, so that history is a record of what happened in the repo, not your PR
  • Added appropriate labels (QA/Yes or QA/No; release-notes/include or release-notes/exclude; OS/...) to the associated issue
  • Checked the PR locally:
    • npm run test -- brave_browser_tests, npm run test -- brave_unit_tests wiki
    • npm run presubmit wiki, npm run gn_check, npm run tslint
  • Ran git rebase master (if needed)

Reviewer Checklist:

  • A security review is not needed, or a link to one is included in the PR description
  • New files have MPL-2.0 license header
  • Adequate test coverage exists to prevent regressions
  • Major classes, functions and non-trivial code blocks are well-commented
  • Changes in component dependencies are properly reflected in gn
  • Code follows the style guide
  • Test plan is specified in PR before merging

After-merge Checklist:

Test Plan:

@supermassive supermassive force-pushed the slip23 branch 2 times, most recently from 542f8b5 to b039567 Compare January 9, 2025 09:17
@supermassive supermassive changed the title Slip23 Implement SLIP-0023 for Cardano Jan 9, 2025
@supermassive supermassive marked this pull request as ready for review January 9, 2025 11:35
@supermassive supermassive requested review from a team as code owners January 9, 2025 11:35
@supermassive supermassive force-pushed the slip23 branch 2 times, most recently from 8d42cbd to a639e0a Compare January 13, 2025 07:47
Copy link
Member

@kdenhartog kdenhartog left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would be good to clarify what's going on here since SLIP0023 is referencing papers that aren't considered secure anymore.


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(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It may be useful to clarify when one or the other should be used here

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

added comment

@@ -23,6 +23,15 @@ DerivationIndex DerivationIndex::Hardened(uint32_t index) {
return DerivationIndex(index, true);
}

// static
DerivationIndex DerivationIndex::FromRawValueForTesting(uint32_t index) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IIRC, we don't want to allow ED25519 to use non-hardened options as it will allow for that attack I investigated previously. Can you clarify what's this for then since I assume it's not for that given we just recently looked at it?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is not about ED25519 itself, but the way private keys / scalars are hierarchically generated. For SLIP10 it is not possible to do normal derivations, but for SLIP23 it is.

z_hmac = crypto::hmac::SignSha512(chain_code_, data);
data[0] = 0x01;
cc_hmac = crypto::hmac::SignSha512(chain_code_, data);
} else {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn't this branch something we're not supposed to support according to SLIP10? That seems it was the way that we avoided any concerns for the paper that's broken and referenced in SLIP0023 (see here)

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SLIP10 does not support normal derivations but SLIP23 does

Copy link
Member

@goodov goodov left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

chromium_src lgtm. I don't see any other good way of implementing custom ed25519 sign without exposing static internals of boringssl.

#include "src/third_party/boringssl/src/crypto/curve25519/curve25519.c"

#ifdef UNSAFE_BUFFERS_BUILD
#pragma allow_unsafe_buffers
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is this necessary? I assume src/third_party/boringssl/src/crypto/curve25519/curve25519.c either has this already or is built without unsafe buffers feature.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These lines trigger error otherwise:
(scalar[31] & 0b11000000) == 0b01000000;
sc_muladd(out_sig + 32, hram, scalar, nonce);
Chromium turns off checks for almost whole third_party
https://source.chromium.org/chromium/chromium/src/+/main:build/config/unsafe_buffers_paths.txt;l=67-68 but it is enabled for brave/chromium_src/

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if this is a new file, please do not introduce unsafe buffers.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wanted this file to be consistent with chromium one so didn't split out_sig into two parts and didn't add tempting refactoring.

Alos it seems to me
brave/chromium_src/third_party/boringssl/
src/third_party/boringssl/
and in general
brave/chromium_src/something
src/something
the first ones should follow upstream for unsafe buffer checks policy.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think consistency is an issue here, as most things are being migrated gradually, however I'm now wondering //third_party/boringssl can depend on //base. Better leave it as you did it, as this is shadowing a .c file for now anyway.

Comment on lines 14 to 17
// https://datatracker.ietf.org/doc/html/rfc8032#section-5.1.5
// requires scalar to follow this requirements 'The lowest 3 bits of the first
// octet are cleared, the highest bit of the last octet is cleared, and the
// second highest bit of the last octet is set'.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These comments should be on the header file IMO.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Moved to header

Comment on lines +1 to +4
/* Copyright (c) 2025 The Brave Authors. All rights reserved.
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at https://mozilla.org/MPL/2.0/. */
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The upstream file seems to be:

third_party/boringssl/src/crypto/curve25519/curve25519.cc

However this one is:

chromium_src/third_party/boringssl/src/crypto/curve25519/curve25519.c

Is there any particular reason to mark this as a C file, rather than do like upstream and use a C++ file?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

upstream is
third_party/boringssl/src/crypto/curve25519/curve25519.c

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, fair enough.

int ED25519_is_scalar_pruned(const uint8_t scalar[32]) {
return (scalar[0] & 0b00000111) == 0b00000000 &&
(scalar[31] & 0b11000000) == 0b01000000;
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If possible to change this file from .c to .cc please spanify.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

would that work having that base already depends on boringssl?
https://source.chromium.org/chromium/chromium/src/+/main:base/BUILD.gn;l=1510;bpv=1

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Explained above, but yeah, it seems to be an obstacle. We could use std::span, but not with the file still being a .c in master.

Comment on lines +26 to +21
int ED25519_pubkey_from_scalar(uint8_t out_public_key[32],
const uint8_t scalar[32]) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ditto.

Comment on lines +45 to +41
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]) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ditto

Comment on lines 193 to 195
return base::WrapUnique(new HDKeyEd25519Slip23(
scalar, xprv_span.subspan<kSlip23ScalarSize, kSlip23PrefixSize>(),
xprv_span.last<kSlip23ChainCodeSize>(), *pubkey));
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please use std::make_unique here, and use the passkey idiom to have constructor only callable from inside HDKeyEd25519Slip23.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done!

@cdesouza-chromium
Copy link
Collaborator

Pretty good work... I left a few comments.

@supermassive supermassive force-pushed the slip23 branch 2 times, most recently from b5ddf06 to 3679f95 Compare January 21, 2025 07:11
public:
using PassKey = base::PassKey<HDKeyEd25519Slip23>;

HDKeyEd25519Slip23();
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I may be wrong but it looks like this default constructor is not used, and it feels like it shouldn't be here?

Copy link
Collaborator

@cdesouza-chromium cdesouza-chromium left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

Copy link
Member

@darkdh darkdh left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nice work!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Implement SLIP-0023 for Cardano
5 participants