mirror of
https://github.com/XRPLF/rippled.git
synced 2026-09-27 07:26:51 +00:00
# Conflicts: # include/xrpl/protocol/Indexes.h # include/xrpl/protocol/KeyType.h # include/xrpl/protocol/PublicKey.h # src/libxrpl/protocol/Indexes.cpp # src/libxrpl/protocol/PublicKey.cpp # src/libxrpl/protocol/SecretKey.cpp
858 lines
25 KiB
C++
858 lines
25 KiB
C++
#include <xrpl/protocol/SecretKey.h>
|
|
|
|
#include <xrpl/basics/Buffer.h>
|
|
#include <xrpl/basics/Slice.h>
|
|
#include <xrpl/basics/base_uint.h>
|
|
#include <xrpl/basics/contract.h>
|
|
#include <xrpl/basics/strHex.h>
|
|
#include <xrpl/beast/utility/rngfill.h>
|
|
#include <xrpl/crypto/csprng.h>
|
|
#include <xrpl/crypto/secure_erase.h>
|
|
#include <xrpl/protocol/KeyType.h>
|
|
#include <xrpl/protocol/PublicKey.h>
|
|
#include <xrpl/protocol/Seed.h>
|
|
#include <xrpl/protocol/detail/secp256k1.h>
|
|
#include <xrpl/protocol/digest.h>
|
|
#include <xrpl/protocol/tokens.h>
|
|
|
|
#include <boost/utility/string_view.hpp>
|
|
|
|
#include <openssl/bn.h>
|
|
#include <openssl/ec.h>
|
|
#include <openssl/ecdsa.h>
|
|
#include <openssl/obj_mac.h>
|
|
|
|
#include <ed25519.h>
|
|
#include <secp256k1.h>
|
|
|
|
#include <algorithm>
|
|
#include <array>
|
|
#include <cstddef>
|
|
#include <cstdint>
|
|
#include <cstring>
|
|
#include <optional>
|
|
#include <stdexcept>
|
|
#include <utility>
|
|
|
|
|
|
#pragma push_macro("L")
|
|
#pragma push_macro("K")
|
|
#pragma push_macro("N")
|
|
#pragma push_macro("S")
|
|
#pragma push_macro("U")
|
|
#pragma push_macro("D")
|
|
#undef L
|
|
#undef K
|
|
#undef N
|
|
#undef S
|
|
#undef U
|
|
#undef D
|
|
|
|
extern "C" {
|
|
#include "api.h"
|
|
#include "fips202.h"
|
|
#include "packing.h"
|
|
#include "params.h"
|
|
#include "poly.h"
|
|
#include "polyvec.h"
|
|
#include "sign.h"
|
|
}
|
|
|
|
#include <iomanip>
|
|
#include <iostream>
|
|
#include <iterator>
|
|
#include <ostream>
|
|
#include <sstream>
|
|
#include <stdexcept>
|
|
|
|
// Define the dilithium functions and sizes with respect to functions named here
|
|
#ifndef CRYPTO_PUBLICKEYBYTES
|
|
#define CRYPTO_PUBLICKEYBYTES pqcrystals_dilithium2_PUBLICKEYBYTES
|
|
#endif
|
|
|
|
#ifndef CRYPTO_SECRETKEYBYTES
|
|
#define CRYPTO_SECRETKEYBYTES pqcrystals_dilithium2_SECRETKEYBYTES
|
|
#endif
|
|
|
|
#ifndef CRYPTO_BYTES
|
|
#define CRYPTO_BYTES pqcrystals_dilithium2_BYTES
|
|
#endif
|
|
|
|
#ifndef crypto_sign_keypair
|
|
#define crypto_sign_keypair pqcrystals_dilithium2_ref_keypair
|
|
#endif
|
|
|
|
#ifndef crypto_sign_signature
|
|
#define crypto_sign_signature pqcrystals_dilithium2_ref_signature
|
|
#endif
|
|
|
|
extern "C" void randombytes(uint8_t* buf, size_t size)
|
|
{
|
|
beast::rngfill(buf, size, xrpl::cryptoPrng());
|
|
}
|
|
|
|
namespace xrpl {
|
|
|
|
SecretKey::~SecretKey()
|
|
{
|
|
secureErase(buf_, sizeof(buf_));
|
|
}
|
|
|
|
SecretKey::SecretKey(std::array<std::uint8_t, 32> const& key)
|
|
{
|
|
size_ = 32;
|
|
std::memcpy(buf_, key.data(), key.size());
|
|
}
|
|
|
|
SecretKey::SecretKey(std::array<std::uint8_t, 2560> const& key)
|
|
{
|
|
size_ = 2560;
|
|
std::memcpy(buf_, key.data(), key.size());
|
|
}
|
|
|
|
SecretKey::SecretKey(Slice const& slice)
|
|
{
|
|
if (slice.size() != 32 && slice.size() != 2560)
|
|
logicError("SecretKey::SecretKey: invalid size");
|
|
size_ = slice.size();
|
|
std::memcpy(buf_, slice.data(), size_);
|
|
}
|
|
|
|
std::string
|
|
SecretKey::toString() const
|
|
{
|
|
return strHex(*this);
|
|
}
|
|
|
|
namespace detail {
|
|
|
|
void
|
|
copyUInt32(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::ranges::copy(seed, buf.begin());
|
|
|
|
// The odds that this loop executes more than once are negligible
|
|
// 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)
|
|
{
|
|
copyUInt32(buf.data() + 16, seq);
|
|
|
|
auto const ret = sha512Half(buf);
|
|
|
|
if (secp256k1_ec_seckey_verify(secp256k1Context(), ret.data()) == 1)
|
|
{
|
|
secureErase(buf.data(), buf.size());
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
Throw<std::runtime_error>("Unable to derive generator from seed");
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
/**
|
|
* 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:
|
|
uint256 root_;
|
|
std::array<std::uint8_t, 33> generator_{};
|
|
|
|
[[nodiscard]] 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::ranges::copy(generator_, buf.begin());
|
|
copyUInt32(buf.data() + 33, seq);
|
|
|
|
// The odds that this loop executes more than once are negligible
|
|
// but we impose a maximum limit just in case.
|
|
for (std::uint32_t subseq = 0; subseq != 128; ++subseq)
|
|
{
|
|
copyUInt32(buf.data() + 37, subseq);
|
|
|
|
auto const ret = sha512HalfS(buf);
|
|
|
|
if (secp256k1_ec_seckey_verify(secp256k1Context(), ret.data()) == 1)
|
|
{
|
|
secureErase(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))
|
|
{
|
|
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");
|
|
}
|
|
|
|
~Generator()
|
|
{
|
|
secureErase(root_.data(), root_.size());
|
|
secureErase(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_seckey_tweak_add(secp256k1Context(), rpk.data(), tweak.data()) == 1)
|
|
{
|
|
SecretKey const sk{Slice{rpk.data(), rpk.size()}};
|
|
secureErase(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)
|
|
{
|
|
auto const type = publicKeyType(pk.slice());
|
|
if (!type)
|
|
logicError("signDigest: invalid key type");
|
|
|
|
switch (*type)
|
|
{
|
|
case KeyType::Secp256k1: {
|
|
BOOST_ASSERT(sk.size() == 32);
|
|
secp256k1_ecdsa_signature sigImp;
|
|
if (secp256k1_ecdsa_sign(
|
|
secp256k1Context(),
|
|
&sigImp,
|
|
reinterpret_cast<unsigned char const*>(digest.data()),
|
|
reinterpret_cast<unsigned char const*>(sk.data()),
|
|
secp256k1_nonce_function_rfc6979,
|
|
nullptr) != 1)
|
|
logicError("sign: secp256k1_ecdsa_sign failed");
|
|
|
|
unsigned char sig[72];
|
|
size_t len = sizeof(sig);
|
|
if (secp256k1_ecdsa_signature_serialize_der(
|
|
secp256k1Context(), sig, &len, &sigImp) != 1)
|
|
logicError("sign: secp256k1_ecdsa_signature_serialize_der failed");
|
|
|
|
return Buffer{sig, len};
|
|
}
|
|
case KeyType::Dilithium: {
|
|
uint8_t sig[CRYPTO_BYTES];
|
|
size_t len = 0;
|
|
uint8_t ctx[] = {};
|
|
size_t ctxlen = 0;
|
|
// Sign the digest data directly
|
|
crypto_sign_signature(
|
|
sig,
|
|
&len,
|
|
reinterpret_cast<unsigned char const*>(digest.data()),
|
|
digest.size(),
|
|
ctx,
|
|
ctxlen,
|
|
sk.data());
|
|
return Buffer{sig, len};
|
|
}
|
|
default:
|
|
logicError("signDigest: unsupported key type");
|
|
}
|
|
}
|
|
|
|
std::string
|
|
toHexString(const uint8_t* data, size_t length)
|
|
{
|
|
std::ostringstream oss;
|
|
for (size_t i = 0; i < length; ++i)
|
|
{
|
|
oss << std::uppercase << std::hex << std::setw(2) << std::setfill('0')
|
|
<< static_cast<int>(data[i]);
|
|
}
|
|
return oss.str();
|
|
}
|
|
|
|
Buffer
|
|
sign(PublicKey const& pk, SecretKey const& sk, Slice const& m)
|
|
{
|
|
auto const type = publicKeyType(pk.slice());
|
|
if (!type)
|
|
logicError("sign: invalid type");
|
|
switch (*type)
|
|
{
|
|
case KeyType::Ed25519: {
|
|
Buffer b(64);
|
|
ed25519_sign(m.data(), m.size(), sk.data(), pk.data() + 1, b.data());
|
|
return b;
|
|
}
|
|
case KeyType::Secp256k1: {
|
|
sha512_half_hasher h;
|
|
h(m.data(), m.size());
|
|
auto const digest = sha512_half_hasher::result_type(h);
|
|
|
|
secp256k1_ecdsa_signature sigImp;
|
|
if (secp256k1_ecdsa_sign(
|
|
secp256k1Context(),
|
|
&sigImp,
|
|
reinterpret_cast<unsigned char const*>(digest.data()),
|
|
reinterpret_cast<unsigned char const*>(sk.data()),
|
|
secp256k1_nonce_function_rfc6979,
|
|
nullptr) != 1)
|
|
logicError("sign: secp256k1_ecdsa_sign failed");
|
|
|
|
unsigned char sig[72];
|
|
size_t len = sizeof(sig);
|
|
if (secp256k1_ecdsa_signature_serialize_der(
|
|
secp256k1Context(), sig, &len, &sigImp) != 1)
|
|
logicError("sign: secp256k1_ecdsa_signature_serialize_der failed");
|
|
|
|
return Buffer{sig, len};
|
|
}
|
|
case KeyType::Dilithium: {
|
|
uint8_t sig[CRYPTO_BYTES];
|
|
size_t len = 0;
|
|
uint8_t ctx[] = {};
|
|
size_t ctxlen = 0;
|
|
crypto_sign_signature(sig, &len, m.data(), m.size(), ctx, ctxlen, sk.data());
|
|
return Buffer{sig, len};
|
|
}
|
|
case KeyType::P256: {
|
|
// Hash the message with SHA-256 (P-256 uses ECDSA-SHA256)
|
|
auto digest = sha256(m);
|
|
|
|
// Create curve object
|
|
EC_GROUP* group = EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1);
|
|
if (!group)
|
|
logicError("sign: EC_GROUP_new_by_curve_name failed");
|
|
|
|
// Create EC_KEY and set the group
|
|
EC_KEY* key = EC_KEY_new();
|
|
if (!key)
|
|
{
|
|
EC_GROUP_free(group);
|
|
logicError("sign: EC_KEY_new failed");
|
|
}
|
|
|
|
if (EC_KEY_set_group(key, group) != 1)
|
|
{
|
|
EC_KEY_free(key);
|
|
EC_GROUP_free(group);
|
|
logicError("sign: EC_KEY_set_group failed");
|
|
}
|
|
|
|
// Convert secret key to BIGNUM and set as private key
|
|
BIGNUM* privKey =
|
|
BN_bin2bn(reinterpret_cast<unsigned char const*>(sk.data()), sk.size(), nullptr);
|
|
|
|
if (!privKey || EC_KEY_set_private_key(key, privKey) != 1)
|
|
{
|
|
BN_free(privKey);
|
|
EC_KEY_free(key);
|
|
EC_GROUP_free(group);
|
|
logicError("sign: failed to set private key");
|
|
}
|
|
|
|
// Sign the digest
|
|
ECDSA_SIG* sigObj = ECDSA_do_sign(
|
|
reinterpret_cast<unsigned char const*>(digest.data()), digest.size(), key);
|
|
|
|
if (!sigObj)
|
|
{
|
|
BN_free(privKey);
|
|
EC_KEY_free(key);
|
|
EC_GROUP_free(group);
|
|
logicError("sign: ECDSA_do_sign failed");
|
|
}
|
|
|
|
// Convert signature to DER format
|
|
unsigned char sig[72];
|
|
int len = i2d_ECDSA_SIG(sigObj, nullptr);
|
|
if (len <= 0 || len > 72)
|
|
{
|
|
ECDSA_SIG_free(sigObj);
|
|
BN_free(privKey);
|
|
EC_KEY_free(key);
|
|
EC_GROUP_free(group);
|
|
logicError("sign: i2d_ECDSA_SIG length check failed");
|
|
}
|
|
|
|
unsigned char* sigPtr = sig;
|
|
if (i2d_ECDSA_SIG(sigObj, &sigPtr) != len)
|
|
{
|
|
ECDSA_SIG_free(sigObj);
|
|
BN_free(privKey);
|
|
EC_KEY_free(key);
|
|
EC_GROUP_free(group);
|
|
logicError("sign: i2d_ECDSA_SIG serialization failed");
|
|
}
|
|
|
|
// Cleanup
|
|
ECDSA_SIG_free(sigObj);
|
|
BN_free(privKey);
|
|
EC_KEY_free(key);
|
|
EC_GROUP_free(group);
|
|
|
|
return Buffer{sig, static_cast<size_t>(len)};
|
|
}
|
|
default:
|
|
logicError("sign: invalid type");
|
|
}
|
|
}
|
|
|
|
SecretKey
|
|
randomSecretKey()
|
|
{
|
|
return randomSecretKey(KeyType::Secp256k1);
|
|
}
|
|
|
|
SecretKey
|
|
randomSecretKey(KeyType type)
|
|
{
|
|
switch (type)
|
|
{
|
|
case KeyType::Ed25519:
|
|
case KeyType::Secp256k1: {
|
|
std::uint8_t buf[32];
|
|
beast::rngfill(buf, sizeof(buf), cryptoPrng());
|
|
SecretKey const sk(Slice{buf, sizeof(buf)});
|
|
secureErase(buf, sizeof(buf));
|
|
return sk;
|
|
}
|
|
case KeyType::Dilithium: {
|
|
uint8_t pk[CRYPTO_PUBLICKEYBYTES];
|
|
uint8_t buf[CRYPTO_SECRETKEYBYTES];
|
|
crypto_sign_keypair(pk, buf);
|
|
SecretKey const sk(Slice{buf, CRYPTO_SECRETKEYBYTES});
|
|
secureErase(buf, sizeof(buf));
|
|
return sk;
|
|
}
|
|
default:
|
|
logicError("randomSecretKey: invalid KeyType");
|
|
}
|
|
}
|
|
|
|
void
|
|
expand_mat(polyvecl mat[K], const uint8_t rho[SEEDBYTES])
|
|
{
|
|
unsigned int i, j;
|
|
uint16_t nonce;
|
|
|
|
for (i = 0; i < K; ++i)
|
|
{
|
|
for (j = 0; j < L; ++j)
|
|
{
|
|
nonce = (i << 8) + j; // Combine indices i and j into a nonce
|
|
poly_uniform(&mat[i].vec[j], rho, nonce);
|
|
}
|
|
}
|
|
}
|
|
|
|
int
|
|
pqcrystals_dilithium2_ref_keypair_seed(
|
|
uint8_t* pk,
|
|
uint8_t* sk,
|
|
const uint8_t* seed)
|
|
{
|
|
uint8_t seedbuf[3 * SEEDBYTES];
|
|
uint8_t tr[CRHBYTES];
|
|
const uint8_t* rho;
|
|
const uint8_t* rhoprime;
|
|
const uint8_t* key;
|
|
polyvecl mat[K], s1, s1hat;
|
|
polyveck t1, t0, s2;
|
|
unsigned int i;
|
|
|
|
/* Use the provided seed to generate rho, rhoprime, and key */
|
|
shake256(seedbuf, 3 * SEEDBYTES, seed, SEEDBYTES);
|
|
rho = seedbuf;
|
|
rhoprime = rho + SEEDBYTES;
|
|
key = rhoprime + SEEDBYTES;
|
|
|
|
/* Expand matrix */
|
|
expand_mat(mat, rho);
|
|
|
|
/* Sample short vectors s1 and s2 using rhoprime */
|
|
polyvecl_uniform_eta(&s1, rhoprime, 0);
|
|
polyveck_uniform_eta(&s2, rhoprime, L);
|
|
|
|
/* Compute t = As1 + s2 */
|
|
s1hat = s1;
|
|
polyvecl_ntt(&s1hat);
|
|
for (i = 0; i < K; ++i)
|
|
{
|
|
polyvecl_pointwise_acc_montgomery(&t1.vec[i], &mat[i], &s1hat);
|
|
poly_invntt_tomont(&t1.vec[i]);
|
|
}
|
|
polyveck_add(&t1, &t1, &s2);
|
|
|
|
/* Extract t1 and write public key */
|
|
polyveck_caddq(&t1);
|
|
polyveck_power2round(&t1, &t0, &t1);
|
|
pack_pk(pk, rho, &t1);
|
|
|
|
/* Hash rho and t1 to obtain tr */
|
|
uint8_t buf[CRYPTO_PUBLICKEYBYTES];
|
|
memcpy(buf, pk, CRYPTO_PUBLICKEYBYTES);
|
|
shake256(tr, CRHBYTES, buf, CRYPTO_PUBLICKEYBYTES);
|
|
|
|
/* Pack secret key */
|
|
pack_sk(sk, rho, tr, key, &t0, &s1, &s2);
|
|
|
|
/* Clean sensitive data */
|
|
secureErase(seedbuf, sizeof(seedbuf));
|
|
secureErase((void*)&s1, sizeof(s1));
|
|
secureErase((void*)&s1hat, sizeof(s1hat));
|
|
secureErase((void*)&s2, sizeof(s2));
|
|
secureErase((void*)&t0, sizeof(t0));
|
|
secureErase((void*)&t1, sizeof(t1));
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
pqcrystals_dilithium2_ref_publickey(uint8_t* pk, const uint8_t* sk)
|
|
{
|
|
uint8_t seedbuf[3 * SEEDBYTES + 2 * CRHBYTES];
|
|
uint8_t *rho, *tr, *key;
|
|
polyvecl mat[K], s1, s1hat;
|
|
polyveck t0, t1, s2;
|
|
|
|
rho = seedbuf;
|
|
tr = rho + SEEDBYTES;
|
|
key = tr + SEEDBYTES;
|
|
unpack_sk(rho, tr, key, &t0, &s1, &s2, sk);
|
|
|
|
/* Expand matrix */
|
|
polyvec_matrix_expand(mat, rho);
|
|
|
|
/* Matrix-vector multiplication */
|
|
s1hat = s1;
|
|
polyvecl_ntt(&s1hat);
|
|
polyvec_matrix_pointwise_montgomery(&t1, mat, &s1hat);
|
|
polyveck_reduce(&t1);
|
|
polyveck_invntt_tomont(&t1);
|
|
|
|
/* Add error vector s2 */
|
|
polyveck_add(&t1, &t1, &s2);
|
|
|
|
/* Extract t1 and write public key */
|
|
polyveck_caddq(&t1);
|
|
polyveck_power2round(&t1, &t0, &t1);
|
|
pack_pk(pk, rho, &t1);
|
|
|
|
return 1;
|
|
}
|
|
|
|
SecretKey
|
|
generateSecretKey(KeyType type, Seed const& seed)
|
|
{
|
|
if (type == KeyType::Ed25519)
|
|
{
|
|
auto key = sha512HalfS(Slice(seed.data(), seed.size()));
|
|
SecretKey const sk{Slice{key.data(), key.size()}};
|
|
secureErase(key.data(), key.size());
|
|
return sk;
|
|
}
|
|
|
|
if (type == KeyType::Secp256k1)
|
|
{
|
|
auto key = detail::deriveDeterministicRootKey(seed);
|
|
SecretKey const sk{Slice{key.data(), key.size()}};
|
|
secureErase(key.data(), key.size());
|
|
return sk;
|
|
}
|
|
|
|
if (type == KeyType::Dilithium)
|
|
{
|
|
uint8_t pk[CRYPTO_PUBLICKEYBYTES];
|
|
uint8_t buf[CRYPTO_SECRETKEYBYTES];
|
|
auto key = sha512HalfS(Slice(seed.data(), seed.size()));
|
|
pqcrystals_dilithium2_ref_keypair_seed(pk, buf, key.data());
|
|
SecretKey const sk{Slice{buf, CRYPTO_SECRETKEYBYTES}};
|
|
secureErase(buf, CRYPTO_SECRETKEYBYTES);
|
|
return sk;
|
|
}
|
|
|
|
if (type == KeyType::P256)
|
|
{
|
|
auto key = detail::deriveDeterministicRootKey(seed);
|
|
SecretKey const sk{Slice{key.data(), key.size()}};
|
|
secureErase(key.data(), key.size());
|
|
return sk;
|
|
}
|
|
|
|
logicError("generateSecretKey: unknown key type");
|
|
}
|
|
|
|
PublicKey
|
|
derivePublicKey(KeyType type, SecretKey const& sk)
|
|
{
|
|
switch (type)
|
|
{
|
|
case KeyType::Secp256k1: {
|
|
secp256k1_pubkey pubkeyImp;
|
|
if (secp256k1_ec_pubkey_create(
|
|
secp256k1Context(),
|
|
&pubkeyImp,
|
|
reinterpret_cast<unsigned char const*>(sk.data())) != 1)
|
|
logicError("derivePublicKey: secp256k1_ec_pubkey_create failed");
|
|
|
|
unsigned char pubkey[33];
|
|
std::size_t len = sizeof(pubkey);
|
|
if (secp256k1_ec_pubkey_serialize(
|
|
secp256k1Context(), pubkey, &len, &pubkeyImp, SECP256K1_EC_COMPRESSED) != 1)
|
|
logicError("derivePublicKey: secp256k1_ec_pubkey_serialize failed");
|
|
|
|
return PublicKey{Slice{pubkey, len}};
|
|
}
|
|
case KeyType::Ed25519: {
|
|
unsigned char buf[33];
|
|
buf[0] = 0xED;
|
|
ed25519_publickey(sk.data(), &buf[1]);
|
|
return PublicKey(Slice{buf, sizeof(buf)});
|
|
}
|
|
case KeyType::Dilithium: {
|
|
uint8_t pk_data[CRYPTO_PUBLICKEYBYTES];
|
|
if (pqcrystals_dilithium2_ref_publickey(pk_data, sk.data()) != 1)
|
|
logicError(
|
|
"derivePublicKey: secp256k1_ec_pubkey_serialize failed");
|
|
|
|
return PublicKey{Slice{pk_data, CRYPTO_PUBLICKEYBYTES}};
|
|
}
|
|
case KeyType::P256: {
|
|
// Create curve object
|
|
EC_GROUP* group = EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1);
|
|
if (!group)
|
|
logicError("derivePublicKey: EC_GROUP_new_by_curve_name failed");
|
|
|
|
// Create EC_KEY and set the group
|
|
EC_KEY* key = EC_KEY_new();
|
|
if (!key)
|
|
{
|
|
EC_GROUP_free(group);
|
|
logicError("derivePublicKey: EC_KEY_new failed");
|
|
}
|
|
|
|
if (EC_KEY_set_group(key, group) != 1)
|
|
{
|
|
EC_KEY_free(key);
|
|
EC_GROUP_free(group);
|
|
logicError("derivePublicKey: EC_KEY_set_group failed");
|
|
}
|
|
|
|
// Convert secret key to BIGNUM
|
|
BIGNUM* privKey =
|
|
BN_bin2bn(reinterpret_cast<unsigned char const*>(sk.data()), sk.size(), nullptr);
|
|
|
|
if (!privKey)
|
|
{
|
|
EC_KEY_free(key);
|
|
EC_GROUP_free(group);
|
|
logicError("derivePublicKey: BN_bin2bn failed");
|
|
}
|
|
|
|
// Set the private key
|
|
if (EC_KEY_set_private_key(key, privKey) != 1)
|
|
{
|
|
BN_free(privKey);
|
|
EC_KEY_free(key);
|
|
EC_GROUP_free(group);
|
|
logicError("derivePublicKey: EC_KEY_set_private_key failed");
|
|
}
|
|
|
|
// Generate the public key from the private key
|
|
EC_POINT* pubKeyPoint = EC_POINT_new(group);
|
|
if (!pubKeyPoint)
|
|
{
|
|
BN_free(privKey);
|
|
EC_KEY_free(key);
|
|
EC_GROUP_free(group);
|
|
logicError("derivePublicKey: EC_POINT_new failed");
|
|
}
|
|
|
|
if (EC_POINT_mul(group, pubKeyPoint, privKey, nullptr, nullptr, nullptr) != 1)
|
|
{
|
|
EC_POINT_free(pubKeyPoint);
|
|
BN_free(privKey);
|
|
EC_KEY_free(key);
|
|
EC_GROUP_free(group);
|
|
logicError("derivePublicKey: EC_POINT_mul failed");
|
|
}
|
|
|
|
// Extract x and y coordinates
|
|
BIGNUM* x = BN_new();
|
|
BIGNUM* y = BN_new();
|
|
if (!x || !y ||
|
|
EC_POINT_get_affine_coordinates_GFp(group, pubKeyPoint, x, y, nullptr) != 1)
|
|
{
|
|
BN_free(x);
|
|
BN_free(y);
|
|
EC_POINT_free(pubKeyPoint);
|
|
BN_free(privKey);
|
|
EC_KEY_free(key);
|
|
EC_GROUP_free(group);
|
|
logicError("derivePublicKey: EC_POINT_get_affine_coordinates_GFp failed");
|
|
}
|
|
|
|
// Convert coordinates to bytes
|
|
unsigned char buf[65]; // 1 prefix + 32-byte x + 32-byte y
|
|
buf[0] = 0xF6; // P-256 prefix byte
|
|
|
|
// Convert x coordinate to 32 bytes
|
|
if (BN_bn2binpad(x, &buf[1], 32) != 32)
|
|
{
|
|
BN_free(x);
|
|
BN_free(y);
|
|
EC_POINT_free(pubKeyPoint);
|
|
BN_free(privKey);
|
|
EC_KEY_free(key);
|
|
EC_GROUP_free(group);
|
|
logicError("derivePublicKey: BN_bn2binpad failed for x coordinate");
|
|
}
|
|
|
|
// Convert y coordinate to 32 bytes
|
|
if (BN_bn2binpad(y, &buf[33], 32) != 32)
|
|
{
|
|
BN_free(x);
|
|
BN_free(y);
|
|
EC_POINT_free(pubKeyPoint);
|
|
BN_free(privKey);
|
|
EC_KEY_free(key);
|
|
EC_GROUP_free(group);
|
|
logicError("derivePublicKey: BN_bn2binpad failed for y coordinate");
|
|
}
|
|
|
|
// Cleanup
|
|
BN_free(x);
|
|
BN_free(y);
|
|
EC_POINT_free(pubKeyPoint);
|
|
BN_free(privKey);
|
|
EC_KEY_free(key);
|
|
EC_GROUP_free(group);
|
|
|
|
return PublicKey{Slice{buf, sizeof(buf)}};
|
|
}
|
|
default:
|
|
logicError("derivePublicKey: bad key type");
|
|
};
|
|
}
|
|
|
|
std::pair<PublicKey, SecretKey>
|
|
generateKeyPair(KeyType type, Seed const& seed)
|
|
{
|
|
switch (type)
|
|
{
|
|
case KeyType::Secp256k1: {
|
|
detail::Generator const g(seed);
|
|
return g(0);
|
|
}
|
|
case KeyType::P256: {
|
|
auto const sk = generateSecretKey(type, seed);
|
|
return {derivePublicKey(type, sk), sk};
|
|
}
|
|
case KeyType::Ed25519: {
|
|
auto const sk = generateSecretKey(type, seed);
|
|
return {derivePublicKey(type, sk), sk};
|
|
}
|
|
case KeyType::Dilithium: {
|
|
auto const sk = generateSecretKey(type, seed);
|
|
return {derivePublicKey(type, sk), sk};
|
|
}
|
|
default:
|
|
throw std::invalid_argument("Unsupported key type");
|
|
}
|
|
}
|
|
|
|
std::pair<PublicKey, SecretKey>
|
|
randomKeyPair(KeyType type)
|
|
{
|
|
auto const sk = randomSecretKey(type);
|
|
return {derivePublicKey(type, sk), sk};
|
|
}
|
|
|
|
template <>
|
|
std::optional<SecretKey>
|
|
parseBase58(TokenType type, std::string const& s)
|
|
{
|
|
auto const result = decodeBase58Token(s, type);
|
|
if (result.empty())
|
|
return std::nullopt;
|
|
if (result.size() != 32 && result.size() != 2560)
|
|
return std::nullopt;
|
|
return SecretKey(makeSlice(result));
|
|
}
|
|
|
|
} // namespace xrpl
|
|
|
|
#pragma pop_macro("K")
|
|
#pragma pop_macro("L")
|
|
#pragma pop_macro("N")
|
|
#pragma pop_macro("S")
|
|
#pragma pop_macro("U")
|
|
#pragma pop_macro("D")
|