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

@@ -111,11 +111,8 @@ target_sources (xrpl_core PRIVATE
main sources: main sources:
subdir: crypto subdir: crypto
#]===============================] #]===============================]
src/ripple/crypto/impl/GenerateDeterministicKey.cpp
src/ripple/crypto/impl/RFC1751.cpp src/ripple/crypto/impl/RFC1751.cpp
src/ripple/crypto/impl/csprng.cpp src/ripple/crypto/impl/csprng.cpp
src/ripple/crypto/impl/ec_key.cpp
src/ripple/crypto/impl/openssl.cpp
src/ripple/crypto/impl/secure_erase.cpp) src/ripple/crypto/impl/secure_erase.cpp)
add_library (Ripple::xrpl_core ALIAS xrpl_core) add_library (Ripple::xrpl_core ALIAS xrpl_core)
@@ -174,16 +171,10 @@ install (
DESTINATION include/ripple/basics) DESTINATION include/ripple/basics)
install ( install (
FILES FILES
src/ripple/crypto/GenerateDeterministicKey.h
src/ripple/crypto/RFC1751.h src/ripple/crypto/RFC1751.h
src/ripple/crypto/csprng.h src/ripple/crypto/csprng.h
src/ripple/crypto/secure_erase.h src/ripple/crypto/secure_erase.h
DESTINATION include/ripple/crypto) DESTINATION include/ripple/crypto)
install (
FILES
src/ripple/crypto/impl/ec_key.h
src/ripple/crypto/impl/openssl.h
DESTINATION include/ripple/crypto/impl)
install ( install (
FILES FILES
src/ripple/json/JsonPropertyStream.h src/ripple/json/JsonPropertyStream.h
@@ -770,11 +761,6 @@ target_sources (rippled PRIVATE
src/test/core/SociDB_test.cpp src/test/core/SociDB_test.cpp
src/test/core/Stoppable_test.cpp src/test/core/Stoppable_test.cpp
src/test/core/Workers_test.cpp src/test/core/Workers_test.cpp
#[===============================[
test sources:
subdir: crypto
#]===============================]
src/test/crypto/Openssl_test.cpp
#[===============================[ #[===============================[
test sources: test sources:
subdir: csf subdir: csf

View File

@@ -34,9 +34,6 @@ Loop: ripple.basics ripple.rpc
Loop: ripple.core ripple.net Loop: ripple.core ripple.net
ripple.net > ripple.core ripple.net > ripple.core
Loop: ripple.crypto ripple.protocol
ripple.protocol > ripple.crypto
Loop: ripple.net ripple.rpc Loop: ripple.net ripple.rpc
ripple.rpc > ripple.net ripple.rpc > ripple.net

View File

@@ -48,6 +48,7 @@ ripple.peerfinder > ripple.beast
ripple.peerfinder > ripple.core ripple.peerfinder > ripple.core
ripple.peerfinder > ripple.protocol ripple.peerfinder > ripple.protocol
ripple.protocol > ripple.beast ripple.protocol > ripple.beast
ripple.protocol > ripple.crypto
ripple.protocol > ripple.json ripple.protocol > ripple.json
ripple.resource > ripple.basics ripple.resource > ripple.basics
ripple.resource > ripple.beast ripple.resource > ripple.beast
@@ -117,8 +118,6 @@ test.core > ripple.server
test.core > test.jtx test.core > test.jtx
test.core > test.toplevel test.core > test.toplevel
test.core > test.unit_test test.core > test.unit_test
test.crypto > ripple.beast
test.crypto > ripple.crypto
test.csf > ripple.basics test.csf > ripple.basics
test.csf > ripple.beast test.csf > ripple.beast
test.csf > ripple.consensus test.csf > ripple.consensus

View File

@@ -1,47 +0,0 @@
//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/ripple/rippled
Copyright (c) 2012, 2013 Ripple Labs Inc.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
// Copyright (c) 2009-2010 Satoshi Nakamoto
// Copyright (c) 2011 The Bitcoin developers
// Distributed under the MIT/X11 software license, see the accompanying
// file license.txt or http://www.opensource.org/licenses/mit-license.php.
#ifndef RIPPLE_CRYPTO_GENERATEDETERMINISTICKEY_H_INCLUDED
#define RIPPLE_CRYPTO_GENERATEDETERMINISTICKEY_H_INCLUDED
#include <ripple/basics/Blob.h>
#include <ripple/basics/base_uint.h>
namespace ripple {
Blob
generateRootDeterministicPublicKey(uint128 const& seed);
uint256
generateRootDeterministicPrivateKey(uint128 const& seed);
Blob
generatePublicDeterministicKey(Blob const& generator, int n);
uint256
generatePrivateDeterministicKey(Blob const& family, uint128 const& seed, int n);
} // namespace ripple
#endif

View File

@@ -1,217 +0,0 @@
//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/ripple/rippled
Copyright (c) 2012, 2013 Ripple Labs Inc.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
#include <ripple/basics/contract.h>
#include <ripple/crypto/GenerateDeterministicKey.h>
#include <ripple/crypto/impl/ec_key.h>
#include <ripple/crypto/impl/openssl.h>
#include <ripple/crypto/secure_erase.h>
#include <ripple/protocol/digest.h>
#include <array>
#include <openssl/pem.h>
#include <string>
namespace ripple {
namespace openssl {
struct secp256k1_data
{
EC_GROUP const* group;
bignum order;
secp256k1_data()
{
group = EC_GROUP_new_by_curve_name(NID_secp256k1);
if (!group)
LogicError(
"The OpenSSL library on this system lacks elliptic curve "
"support.");
bn_ctx ctx;
order = get_order(group, ctx);
}
};
static secp256k1_data const&
secp256k1curve()
{
static secp256k1_data const curve{};
return curve;
}
} // namespace openssl
using namespace openssl;
static Blob
serialize_ec_point(ec_point const& point)
{
Blob result(33);
serialize_ec_point(point, &result[0]);
return result;
}
template <class FwdIt>
void
copy_uint32(FwdIt out, std::uint32_t v)
{
*out++ = v >> 24;
*out++ = (v >> 16) & 0xff;
*out++ = (v >> 8) & 0xff;
*out = v & 0xff;
}
// Functions to add support for deterministic EC keys
// --> seed
// <-- private root generator + public root generator
static bignum
generateRootDeterministicKey(uint128 const& seed)
{
// find non-zero private key less than the curve's order
bignum privKey;
std::uint32_t seq = 0;
do
{
// buf: 0 seed 16 seq 20
// |<--------------------------------->|<------>|
std::array<std::uint8_t, 20> buf;
std::copy(seed.begin(), seed.end(), buf.begin());
copy_uint32(buf.begin() + 16, seq++);
auto root = sha512Half(buf);
secure_erase(buf.data(), buf.size());
privKey.assign(root.data(), root.size());
secure_erase(root.data(), root.size());
} while (privKey.is_zero() || privKey >= secp256k1curve().order);
secure_erase(&seq, sizeof(seq));
return privKey;
}
// --> seed
// <-- private root generator + public root generator
Blob
generateRootDeterministicPublicKey(uint128 const& seed)
{
bn_ctx ctx;
bignum privKey = generateRootDeterministicKey(seed);
// compute the corresponding public key point
ec_point pubKey = multiply(secp256k1curve().group, privKey, ctx);
privKey.clear(); // security erase
return serialize_ec_point(pubKey);
}
uint256
generateRootDeterministicPrivateKey(uint128 const& seed)
{
bignum key = generateRootDeterministicKey(seed);
return uint256_from_bignum_clear(key);
}
// Take ripple address.
// --> root public generator (consumes)
// <-- root public generator in EC format
static ec_point
generateRootPubKey(bignum&& pubGenerator)
{
ec_point pubPoint = bn2point(secp256k1curve().group, pubGenerator.get());
return pubPoint;
}
// --> public generator
static bignum
makeHash(Blob const& pubGen, int seq, bignum const& order)
{
int subSeq = 0;
bignum result;
assert(pubGen.size() == 33);
do
{
// buf: 0 pubGen 33 seq 37 subSeq 41
// |<--------------------------->|<------>|<-------->|
std::array<std::uint8_t, 41> buf;
std::copy(pubGen.begin(), pubGen.end(), buf.begin());
copy_uint32(buf.begin() + 33, seq);
copy_uint32(buf.begin() + 37, subSeq++);
auto root = sha512Half_s(buf);
secure_erase(buf.data(), buf.size());
result.assign(root.data(), root.size());
secure_erase(root.data(), root.size());
} while (result.is_zero() || result >= order);
return result;
}
// --> public generator
Blob
generatePublicDeterministicKey(Blob const& pubGen, int seq)
{
// publicKey(n) = rootPublicKey EC_POINT_+ Hash(pubHash|seq)*point
ec_point rootPubKey = generateRootPubKey(bignum(pubGen));
bn_ctx ctx;
// Calculate the private additional key.
bignum hash = makeHash(pubGen, seq, secp256k1curve().order);
// Calculate the corresponding public key.
ec_point newPoint = multiply(secp256k1curve().group, hash, ctx);
// Add the master public key and set.
add_to(secp256k1curve().group, rootPubKey, newPoint, ctx);
return serialize_ec_point(newPoint);
}
// --> root private key
uint256
generatePrivateDeterministicKey(
Blob const& pubGen,
uint128 const& seed,
int seq)
{
// privateKey(n) = (rootPrivateKey + Hash(pubHash|seq)) % order
bignum rootPrivKey = generateRootDeterministicKey(seed);
bn_ctx ctx;
// calculate the private additional key
bignum privKey = makeHash(pubGen, seq, secp256k1curve().order);
// calculate the final private key
add_to(rootPrivKey, privKey, secp256k1curve().order, ctx);
rootPrivKey.clear(); // security erase
return uint256_from_bignum_clear(privKey);
}
} // namespace ripple

View File

@@ -1,65 +0,0 @@
//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/ripple/rippled
Copyright (c) 2012, 2013 Ripple Labs Inc.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
// Copyright (c) 2009-2010 Satoshi Nakamoto
// Copyright (c) 2011 The Bitcoin developers
// Distributed under the MIT/X11 software license, see the accompanying
// file license.txt or http://www.opensource.org/licenses/mit-license.php.
#include <ripple/basics/contract.h>
#include <ripple/crypto/impl/ec_key.h>
#include <openssl/ec.h>
namespace ripple {
namespace openssl {
static inline EC_KEY*
get_EC_KEY(const ec_key& that)
{
return (EC_KEY*)that.get();
}
ec_key::ec_key(const ec_key& that)
{
if (that.ptr == nullptr)
{
ptr = nullptr;
return;
}
ptr = (pointer_t)EC_KEY_dup(get_EC_KEY(that));
if (ptr == nullptr)
Throw<std::runtime_error>("ec_key::ec_key() : EC_KEY_dup failed");
EC_KEY_set_conv_form(get_EC_KEY(*this), POINT_CONVERSION_COMPRESSED);
}
void
ec_key::destroy()
{
if (ptr != nullptr)
{
EC_KEY_free(get_EC_KEY(*this));
ptr = nullptr;
}
}
} // namespace openssl
} // namespace ripple

View File

@@ -1,84 +0,0 @@
//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/ripple/rippled
Copyright (c) 2014 Ripple Labs Inc.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
#ifndef RIPPLE_CRYPTO_EC_KEY_H_INCLUDED
#define RIPPLE_CRYPTO_EC_KEY_H_INCLUDED
#include <ripple/basics/base_uint.h>
#include <cstdint>
namespace ripple {
namespace openssl {
class ec_key
{
public:
using pointer_t = struct opaque_EC_KEY*;
ec_key() : ptr(nullptr)
{
}
ec_key(pointer_t raw) : ptr(raw)
{
}
~ec_key()
{
destroy();
}
bool
valid() const
{
return ptr != nullptr;
}
pointer_t
get() const
{
return ptr;
}
ec_key(const ec_key&);
pointer_t
release()
{
pointer_t released = ptr;
ptr = nullptr;
return released;
}
private:
pointer_t ptr;
void
destroy();
ec_key&
operator=(const ec_key&) = delete;
};
} // namespace openssl
} // namespace ripple
#endif

View File

@@ -1,141 +0,0 @@
//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/ripple/rippled
Copyright (c) 2014 Ripple Labs Inc.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
#include <ripple/basics/contract.h>
#include <ripple/crypto/impl/openssl.h>
#include <openssl/hmac.h>
namespace ripple {
namespace openssl {
bignum::bignum()
{
ptr = BN_new();
if (ptr == nullptr)
Throw<std::runtime_error>("BN_new() failed");
}
void
bignum::assign(uint8_t const* data, size_t size)
{
// This reuses and assigns ptr
BIGNUM* bn = BN_bin2bn(data, size, ptr);
if (bn == nullptr)
Throw<std::runtime_error>("BN_bin2bn() failed");
}
void
bignum::assign_new(uint8_t const* data, size_t size)
{
// ptr must not be allocated
ptr = BN_bin2bn(data, size, nullptr);
if (ptr == nullptr)
Throw<std::runtime_error>("BN_bin2bn() failed");
}
bn_ctx::bn_ctx()
{
ptr = BN_CTX_new();
if (ptr == nullptr)
Throw<std::runtime_error>("BN_CTX_new() failed");
}
bignum
get_order(EC_GROUP const* group, bn_ctx& ctx)
{
bignum result;
if (!EC_GROUP_get_order(group, result.get(), ctx.get()))
Throw<std::runtime_error>("EC_GROUP_get_order() failed");
return result;
}
ec_point::ec_point(EC_GROUP const* group)
{
ptr = EC_POINT_new(group);
if (ptr == nullptr)
Throw<std::runtime_error>("EC_POINT_new() failed");
}
void
add_to(EC_GROUP const* group, ec_point const& a, ec_point& b, bn_ctx& ctx)
{
if (!EC_POINT_add(group, b.get(), a.get(), b.get(), ctx.get()))
Throw<std::runtime_error>("EC_POINT_add() failed");
}
ec_point
multiply(EC_GROUP const* group, bignum const& n, bn_ctx& ctx)
{
ec_point result(group);
if (!EC_POINT_mul(
group, result.get(), n.get(), nullptr, nullptr, ctx.get()))
Throw<std::runtime_error>("EC_POINT_mul() failed");
return result;
}
ec_point
bn2point(EC_GROUP const* group, BIGNUM const* number)
{
EC_POINT* result = EC_POINT_bn2point(group, number, nullptr, nullptr);
if (result == nullptr)
Throw<std::runtime_error>("EC_POINT_bn2point() failed");
return ec_point::acquire(result);
}
static ec_key
ec_key_new_secp256k1_compressed()
{
EC_KEY* key = EC_KEY_new_by_curve_name(NID_secp256k1);
if (key == nullptr)
Throw<std::runtime_error>("EC_KEY_new_by_curve_name() failed");
EC_KEY_set_conv_form(key, POINT_CONVERSION_COMPRESSED);
return ec_key((ec_key::pointer_t)key);
}
void
serialize_ec_point(ec_point const& point, std::uint8_t* ptr)
{
ec_key key = ec_key_new_secp256k1_compressed();
if (EC_KEY_set_public_key((EC_KEY*)key.get(), point.get()) <= 0)
Throw<std::runtime_error>("EC_KEY_set_public_key() failed");
int const size = i2o_ECPublicKey((EC_KEY*)key.get(), &ptr);
assert(size <= 33);
(void)size;
}
} // namespace openssl
} // namespace ripple
#include <stdio.h>
#ifdef _MSC_VER
FILE _iob[] = {*stdin, *stdout, *stderr};
extern "C" FILE* __cdecl __iob_func(void)
{
return _iob;
}
#endif

View File

@@ -1,236 +0,0 @@
//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/ripple/rippled
Copyright (c) 2014 Ripple Labs Inc.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
#ifndef RIPPLE_OPENSSL_H
#define RIPPLE_OPENSSL_H
#include <ripple/basics/base_uint.h>
#include <ripple/crypto/impl/ec_key.h>
#include <openssl/bn.h>
#include <openssl/ec.h>
namespace ripple {
namespace openssl {
class bignum
{
private:
BIGNUM* ptr;
// non-copyable
bignum(bignum const&) = delete;
bignum&
operator=(bignum const&) = delete;
void
assign_new(uint8_t const* data, size_t size);
public:
bignum();
~bignum()
{
if (ptr != nullptr)
{
BN_free(ptr);
}
}
bignum(uint8_t const* data, size_t size)
{
assign_new(data, size);
}
template <class T>
explicit bignum(T const& thing)
{
assign_new(thing.data(), thing.size());
}
bignum(bignum&& that) noexcept : ptr(that.ptr)
{
that.ptr = nullptr;
}
bignum&
operator=(bignum&& that) noexcept
{
using std::swap;
swap(ptr, that.ptr);
return *this;
}
BIGNUM*
get()
{
return ptr;
}
BIGNUM const*
get() const
{
return ptr;
}
bool
is_zero() const
{
return BN_is_zero(ptr);
}
void
clear()
{
BN_clear(ptr);
}
void
assign(uint8_t const* data, size_t size);
};
inline bool
operator<(bignum const& a, bignum const& b)
{
return BN_cmp(a.get(), b.get()) < 0;
}
inline bool
operator>=(bignum const& a, bignum const& b)
{
return !(a < b);
}
inline uint256
uint256_from_bignum_clear(bignum& number)
{
uint256 result;
result.zero();
BN_bn2bin(number.get(), result.end() - BN_num_bytes(number.get()));
number.clear();
return result;
}
class bn_ctx
{
private:
BN_CTX* ptr;
// non-copyable
bn_ctx(bn_ctx const&);
bn_ctx&
operator=(bn_ctx const&);
public:
bn_ctx();
~bn_ctx()
{
BN_CTX_free(ptr);
}
BN_CTX*
get()
{
return ptr;
}
BN_CTX const*
get() const
{
return ptr;
}
};
bignum
get_order(EC_GROUP const* group, bn_ctx& ctx);
inline void
add_to(bignum const& a, bignum& b, bignum const& modulus, bn_ctx& ctx)
{
BN_mod_add(b.get(), a.get(), b.get(), modulus.get(), ctx.get());
}
class ec_point
{
public:
using pointer_t = EC_POINT*;
private:
pointer_t ptr;
ec_point(pointer_t raw) : ptr(raw)
{
}
public:
static ec_point
acquire(pointer_t raw)
{
return ec_point(raw);
}
ec_point(EC_GROUP const* group);
~ec_point()
{
EC_POINT_free(ptr);
}
ec_point(ec_point const&) = delete;
ec_point&
operator=(ec_point const&) = delete;
ec_point(ec_point&& that) noexcept
{
ptr = that.ptr;
that.ptr = nullptr;
}
EC_POINT*
get()
{
return ptr;
}
EC_POINT const*
get() const
{
return ptr;
}
};
void
add_to(EC_GROUP const* group, ec_point const& a, ec_point& b, bn_ctx& ctx);
ec_point
multiply(EC_GROUP const* group, bignum const& n, bn_ctx& ctx);
ec_point
bn2point(EC_GROUP const* group, BIGNUM const* number);
// output buffer must hold 33 bytes
void
serialize_ec_point(ec_point const& point, std::uint8_t* ptr);
} // namespace openssl
} // namespace ripple
#endif

View File

@@ -20,7 +20,6 @@
#include <ripple/basics/contract.h> #include <ripple/basics/contract.h>
#include <ripple/basics/strHex.h> #include <ripple/basics/strHex.h>
#include <ripple/beast/utility/rngfill.h> #include <ripple/beast/utility/rngfill.h>
#include <ripple/crypto/GenerateDeterministicKey.h>
#include <ripple/crypto/csprng.h> #include <ripple/crypto/csprng.h>
#include <ripple/crypto/secure_erase.h> #include <ripple/crypto/secure_erase.h>
#include <ripple/protocol/SecretKey.h> #include <ripple/protocol/SecretKey.h>
@@ -54,45 +53,160 @@ SecretKey::to_string() const
return strHex(*this); 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 class Generator
{ {
private: 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: public:
explicit Generator(Seed const& seed) explicit Generator(Seed const& seed)
: root_(deriveDeterministicRootKey(seed))
{ {
// FIXME: Avoid copying the seed into a uint128 key only to have secp256k1_pubkey pubkey;
// generateRootDeterministicPublicKey copy out of it. if (secp256k1_ec_pubkey_create(
uint128 ui; secp256k1Context(), &pubkey, root_.data()) != 1)
std::memcpy(ui.data(), seed.data(), seed.size()); LogicError("derivePublicKey: secp256k1_ec_pubkey_create failed");
gen_ = generateRootDeterministicPublicKey(ui);
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. ~Generator()
The seed is required to produce the private key.
*/
std::pair<PublicKey, SecretKey>
operator()(Seed const& seed, std::size_t ordinal) const
{ {
// FIXME: Avoid copying the seed into a uint128 key only to have secure_erase(root_.data(), root_.size());
// generatePrivateDeterministicKey copy out of it. secure_erase(generator_.data(), generator_.size());
uint128 ui; }
std::memcpy(ui.data(), seed.data(), seed.size());
auto gsk = generatePrivateDeterministicKey(gen_, ui, ordinal); /** Generate the nth key pair. */
auto gpk = generatePublicDeterministicKey(gen_, ordinal); std::pair<PublicKey, SecretKey>
SecretKey const sk(Slice{gsk.data(), gsk.size()}); operator()(std::size_t ordinal) const
PublicKey const pk(Slice{gpk.data(), gpk.size()}); {
secure_erase(ui.data(), ui.size()); // Generates Nth secret key:
secure_erase(gsk.data(), gsk.size()); auto gsk = [this, tweak = calculateTweak(ordinal)]() {
return {pk, sk}; 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 Buffer
signDigest(PublicKey const& pk, SecretKey const& sk, uint256 const& digest) signDigest(PublicKey const& pk, SecretKey const& sk, uint256 const& digest)
@@ -173,28 +287,22 @@ randomSecretKey()
return sk; return sk;
} }
// VFALCO TODO Rewrite all this without using OpenSSL
// or calling into GenerateDetermisticKey
SecretKey SecretKey
generateSecretKey(KeyType type, Seed const& seed) generateSecretKey(KeyType type, Seed const& seed)
{ {
if (type == KeyType::ed25519) if (type == KeyType::ed25519)
{ {
auto key = sha512Half_s(Slice(seed.data(), seed.size())); 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()); secure_erase(key.data(), key.size());
return sk; return sk;
} }
if (type == KeyType::secp256k1) if (type == KeyType::secp256k1)
{ {
// FIXME: Avoid copying the seed into a uint128 key only to have auto key = detail::deriveDeterministicRootKey(seed);
// generateRootDeterministicPrivateKey copy out of it. SecretKey sk{Slice{key.data(), key.size()}};
uint128 ps; secure_erase(key.data(), key.size());
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());
return sk; return sk;
} }
@@ -245,8 +353,8 @@ generateKeyPair(KeyType type, Seed const& seed)
switch (type) switch (type)
{ {
case KeyType::secp256k1: { case KeyType::secp256k1: {
Generator g(seed); detail::Generator g(seed);
return g(seed, 0); return g(0);
} }
default: default:
case KeyType::ed25519: { case KeyType::ed25519: {

View File

@@ -1,54 +0,0 @@
//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/ripple/rippled
Copyright (c) 2018 Ripple Labs Inc.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
#include <ripple/beast/unit_test.h>
#include <ripple/crypto/impl/openssl.h>
#include <type_traits>
namespace ripple {
struct Openssl_test : public beast::unit_test::suite
{
void
testBasicProperties()
{
using namespace openssl;
BEAST_EXPECT(std::is_default_constructible<bignum>{});
BEAST_EXPECT(!std::is_copy_constructible<bignum>{});
BEAST_EXPECT(!std::is_copy_assignable<bignum>{});
BEAST_EXPECT(std::is_nothrow_move_constructible<bignum>{});
BEAST_EXPECT(std::is_nothrow_move_assignable<bignum>{});
BEAST_EXPECT(!std::is_default_constructible<ec_point>{});
BEAST_EXPECT(!std::is_copy_constructible<ec_point>{});
BEAST_EXPECT(!std::is_copy_assignable<ec_point>{});
BEAST_EXPECT(std::is_nothrow_move_constructible<ec_point>{});
BEAST_EXPECT(!std::is_nothrow_move_assignable<ec_point>{});
}
void
run() override
{
testBasicProperties();
};
};
BEAST_DEFINE_TESTSUITE(Openssl, crypto, ripple);
} // namespace ripple

File diff suppressed because it is too large Load Diff