Use libsecp256k1 instead of OpenSSL for key derivation:

While most of the code associated with secp256k1 operations had
been migrated to libsecp256k1, the deterministic key derivation
code was still using calls to OpenSSL.

If merged, this commit replaces the OpenSSL-based routines with
new libsecp256k1-based implementations. No functional change is
expected and the change should be transparent.

This commit also removes several support classes and utility
functions that wrapped or adapted various OpenSSL types that
are no longer needed.

A tip of the hat to the original author of this truly superb
library, Dr. Pieter Wuille, and to all other contributors.
This commit is contained in:
Nik Bougalis
2021-02-02 20:42:20 -08:00
parent b4699c3b46
commit a06525649d
12 changed files with 1389 additions and 1012 deletions

View File

@@ -20,7 +20,6 @@
#include <ripple/basics/contract.h>
#include <ripple/basics/strHex.h>
#include <ripple/beast/utility/rngfill.h>
#include <ripple/crypto/GenerateDeterministicKey.h>
#include <ripple/crypto/csprng.h>
#include <ripple/crypto/secure_erase.h>
#include <ripple/protocol/SecretKey.h>
@@ -54,45 +53,160 @@ SecretKey::to_string() const
return strHex(*this);
}
namespace detail {
void
copy_uint32(std::uint8_t* out, std::uint32_t v)
{
*out++ = v >> 24;
*out++ = (v >> 16) & 0xff;
*out++ = (v >> 8) & 0xff;
*out = v & 0xff;
}
uint256
deriveDeterministicRootKey(Seed const& seed)
{
// We fill this buffer with the seed and append a 32-bit "counter"
// that counts how many attempts we've had to make to generate a
// non-zero key that's less than the curve's order:
//
// 1 2
// 0 6 0
// buf |----------------|----|
// | seed | seq|
std::array<std::uint8_t, 20> buf;
std::copy(seed.begin(), seed.end(), buf.begin());
// The odds that this loop executes more than once are neglible
// but *just* in case someone managed to generate a key that required
// more iterations loop a few times.
for (std::uint32_t seq = 0; seq != 128; ++seq)
{
copy_uint32(buf.data() + 16, seq);
auto const ret = sha512Half(buf);
if (secp256k1_ec_seckey_verify(secp256k1Context(), ret.data()) == 1)
{
secure_erase(buf.data(), buf.size());
return ret;
}
}
Throw<std::runtime_error>("Unable to derive generator from seed");
}
//------------------------------------------------------------------------------
/** Produces a sequence of secp256k1 key pairs. */
/** Produces a sequence of secp256k1 key pairs.
The reference implementation of the XRP Ledger uses a custom derivation
algorithm which enables the derivation of an entire family of secp256k1
keypairs from a single 128-bit seed. The algorithm predates widely-used
standards like BIP-32 and BIP-44.
Important note to implementers:
Using this algorithm is not required: all valid secp256k1 keypairs will
work correctly. Third party implementations can use whatever mechanisms
they prefer. However, implementers of wallets or other tools that allow
users to use existing accounts should consider at least supporting this
derivation technique to make it easier for users to 'import' accounts.
For more details, please check out:
https://xrpl.org/cryptographic-keys.html#secp256k1-key-derivation
*/
class Generator
{
private:
Blob gen_; // VFALCO compile time size?
uint256 root_;
std::array<std::uint8_t, 33> generator_;
uint256
calculateTweak(std::uint32_t seq) const
{
// We fill the buffer with the generator, the provided sequence
// and a 32-bit counter tracking the number of attempts we have
// already made looking for a non-zero key that's less than the
// curve's order:
// 3 3 4
// 0 pubGen 3 7 1
// buf |---------------------------------|----|----|
// | generator | seq| cnt|
std::array<std::uint8_t, 41> buf;
std::copy(generator_.begin(), generator_.end(), buf.begin());
copy_uint32(buf.data() + 33, seq);
// The odds that this loop executes more than once are neglible
// but we impose a maximum limit just in case.
for (std::uint32_t subseq = 0; subseq != 128; ++subseq)
{
copy_uint32(buf.data() + 37, subseq);
auto const ret = sha512Half_s(buf);
if (secp256k1_ec_seckey_verify(secp256k1Context(), ret.data()) == 1)
{
secure_erase(buf.data(), buf.size());
return ret;
}
}
Throw<std::runtime_error>("Unable to derive generator from seed");
}
public:
explicit Generator(Seed const& seed)
: root_(deriveDeterministicRootKey(seed))
{
// FIXME: Avoid copying the seed into a uint128 key only to have
// generateRootDeterministicPublicKey copy out of it.
uint128 ui;
std::memcpy(ui.data(), seed.data(), seed.size());
gen_ = generateRootDeterministicPublicKey(ui);
secp256k1_pubkey pubkey;
if (secp256k1_ec_pubkey_create(
secp256k1Context(), &pubkey, root_.data()) != 1)
LogicError("derivePublicKey: secp256k1_ec_pubkey_create failed");
auto len = generator_.size();
if (secp256k1_ec_pubkey_serialize(
secp256k1Context(),
generator_.data(),
&len,
&pubkey,
SECP256K1_EC_COMPRESSED) != 1)
LogicError("derivePublicKey: secp256k1_ec_pubkey_serialize failed");
}
/** Generate the nth key pair.
The seed is required to produce the private key.
*/
std::pair<PublicKey, SecretKey>
operator()(Seed const& seed, std::size_t ordinal) const
~Generator()
{
// FIXME: Avoid copying the seed into a uint128 key only to have
// generatePrivateDeterministicKey copy out of it.
uint128 ui;
std::memcpy(ui.data(), seed.data(), seed.size());
auto gsk = generatePrivateDeterministicKey(gen_, ui, ordinal);
auto gpk = generatePublicDeterministicKey(gen_, ordinal);
SecretKey const sk(Slice{gsk.data(), gsk.size()});
PublicKey const pk(Slice{gpk.data(), gpk.size()});
secure_erase(ui.data(), ui.size());
secure_erase(gsk.data(), gsk.size());
return {pk, sk};
secure_erase(root_.data(), root_.size());
secure_erase(generator_.data(), generator_.size());
}
/** Generate the nth key pair. */
std::pair<PublicKey, SecretKey>
operator()(std::size_t ordinal) const
{
// Generates Nth secret key:
auto gsk = [this, tweak = calculateTweak(ordinal)]() {
auto rpk = root_;
if (secp256k1_ec_privkey_tweak_add(
secp256k1Context(), rpk.data(), tweak.data()) == 1)
{
SecretKey sk{Slice{rpk.data(), rpk.size()}};
secure_erase(rpk.data(), rpk.size());
return sk;
}
LogicError("Unable to add a tweak!");
}();
return {derivePublicKey(KeyType::secp256k1, gsk), gsk};
}
};
//------------------------------------------------------------------------------
} // namespace detail
Buffer
signDigest(PublicKey const& pk, SecretKey const& sk, uint256 const& digest)
@@ -173,28 +287,22 @@ randomSecretKey()
return sk;
}
// VFALCO TODO Rewrite all this without using OpenSSL
// or calling into GenerateDetermisticKey
SecretKey
generateSecretKey(KeyType type, Seed const& seed)
{
if (type == KeyType::ed25519)
{
auto key = sha512Half_s(Slice(seed.data(), seed.size()));
SecretKey sk = Slice{key.data(), key.size()};
SecretKey sk{Slice{key.data(), key.size()}};
secure_erase(key.data(), key.size());
return sk;
}
if (type == KeyType::secp256k1)
{
// FIXME: Avoid copying the seed into a uint128 key only to have
// generateRootDeterministicPrivateKey copy out of it.
uint128 ps;
std::memcpy(ps.data(), seed.data(), seed.size());
auto const upk = generateRootDeterministicPrivateKey(ps);
SecretKey sk = Slice{upk.data(), upk.size()};
secure_erase(ps.data(), ps.size());
auto key = detail::deriveDeterministicRootKey(seed);
SecretKey sk{Slice{key.data(), key.size()}};
secure_erase(key.data(), key.size());
return sk;
}
@@ -245,8 +353,8 @@ generateKeyPair(KeyType type, Seed const& seed)
switch (type)
{
case KeyType::secp256k1: {
Generator g(seed);
return g(seed, 0);
detail::Generator g(seed);
return g(0);
}
default:
case KeyType::ed25519: {