mirror of
https://github.com/bitcoin/bitcoin.git
synced 2025-04-29 23:09:44 -04:00
Add ElligatorSwift key creation and ECDH logic
Co-authored-by: Dhruv Mehta <856960+dhruv@users.noreply.github.com>
This commit is contained in:
parent
42239f8390
commit
eff72a0dff
5 changed files with 102 additions and 0 deletions
37
src/key.cpp
37
src/key.cpp
|
@ -11,6 +11,7 @@
|
||||||
#include <random.h>
|
#include <random.h>
|
||||||
|
|
||||||
#include <secp256k1.h>
|
#include <secp256k1.h>
|
||||||
|
#include <secp256k1_ellswift.h>
|
||||||
#include <secp256k1_extrakeys.h>
|
#include <secp256k1_extrakeys.h>
|
||||||
#include <secp256k1_recovery.h>
|
#include <secp256k1_recovery.h>
|
||||||
#include <secp256k1_schnorrsig.h>
|
#include <secp256k1_schnorrsig.h>
|
||||||
|
@ -331,6 +332,42 @@ bool CKey::Derive(CKey& keyChild, ChainCode &ccChild, unsigned int nChild, const
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
EllSwiftPubKey CKey::EllSwiftCreate(Span<const std::byte> ent32) const
|
||||||
|
{
|
||||||
|
assert(fValid);
|
||||||
|
assert(ent32.size() == 32);
|
||||||
|
std::array<std::byte, EllSwiftPubKey::size()> encoded_pubkey;
|
||||||
|
|
||||||
|
auto success = secp256k1_ellswift_create(secp256k1_context_sign,
|
||||||
|
UCharCast(encoded_pubkey.data()),
|
||||||
|
keydata.data(),
|
||||||
|
UCharCast(ent32.data()));
|
||||||
|
|
||||||
|
// Should always succeed for valid keys (asserted above).
|
||||||
|
assert(success);
|
||||||
|
return {encoded_pubkey};
|
||||||
|
}
|
||||||
|
|
||||||
|
ECDHSecret CKey::ComputeBIP324ECDHSecret(const EllSwiftPubKey& their_ellswift, const EllSwiftPubKey& our_ellswift, bool initiating) const
|
||||||
|
{
|
||||||
|
assert(fValid);
|
||||||
|
|
||||||
|
ECDHSecret output;
|
||||||
|
// BIP324 uses the initiator as party A, and the responder as party B. Remap the inputs
|
||||||
|
// accordingly:
|
||||||
|
bool success = secp256k1_ellswift_xdh(secp256k1_context_sign,
|
||||||
|
UCharCast(output.data()),
|
||||||
|
UCharCast(initiating ? our_ellswift.data() : their_ellswift.data()),
|
||||||
|
UCharCast(initiating ? their_ellswift.data() : our_ellswift.data()),
|
||||||
|
keydata.data(),
|
||||||
|
initiating ? 0 : 1,
|
||||||
|
secp256k1_ellswift_xdh_hash_function_bip324,
|
||||||
|
nullptr);
|
||||||
|
// Should always succeed for valid keys (assert above).
|
||||||
|
assert(success);
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
bool CExtKey::Derive(CExtKey &out, unsigned int _nChild) const {
|
bool CExtKey::Derive(CExtKey &out, unsigned int _nChild) const {
|
||||||
if (nDepth == std::numeric_limits<unsigned char>::max()) return false;
|
if (nDepth == std::numeric_limits<unsigned char>::max()) return false;
|
||||||
out.nDepth = nDepth + 1;
|
out.nDepth = nDepth + 1;
|
||||||
|
|
27
src/key.h
27
src/key.h
|
@ -22,6 +22,12 @@
|
||||||
*/
|
*/
|
||||||
typedef std::vector<unsigned char, secure_allocator<unsigned char> > CPrivKey;
|
typedef std::vector<unsigned char, secure_allocator<unsigned char> > CPrivKey;
|
||||||
|
|
||||||
|
/** Size of ECDH shared secrets. */
|
||||||
|
constexpr static size_t ECDH_SECRET_SIZE = CSHA256::OUTPUT_SIZE;
|
||||||
|
|
||||||
|
// Used to represent ECDH shared secret (ECDH_SECRET_SIZE bytes)
|
||||||
|
using ECDHSecret = std::array<std::byte, ECDH_SECRET_SIZE>;
|
||||||
|
|
||||||
/** An encapsulated private key. */
|
/** An encapsulated private key. */
|
||||||
class CKey
|
class CKey
|
||||||
{
|
{
|
||||||
|
@ -156,6 +162,27 @@ public:
|
||||||
|
|
||||||
//! Load private key and check that public key matches.
|
//! Load private key and check that public key matches.
|
||||||
bool Load(const CPrivKey& privkey, const CPubKey& vchPubKey, bool fSkipCheck);
|
bool Load(const CPrivKey& privkey, const CPubKey& vchPubKey, bool fSkipCheck);
|
||||||
|
|
||||||
|
/** Create an ellswift-encoded public key for this key, with specified entropy.
|
||||||
|
*
|
||||||
|
* entropy must be a 32-byte span with additional entropy to use in the encoding. Every
|
||||||
|
* public key has ~2^256 different encodings, and this function will deterministically pick
|
||||||
|
* one of them, based on entropy. Note that even without truly random entropy, the
|
||||||
|
* resulting encoding will be indistinguishable from uniform to any adversary who does not
|
||||||
|
* know the private key (because the private key itself is always used as entropy as well).
|
||||||
|
*/
|
||||||
|
EllSwiftPubKey EllSwiftCreate(Span<const std::byte> entropy) const;
|
||||||
|
|
||||||
|
/** Compute a BIP324-style ECDH shared secret.
|
||||||
|
*
|
||||||
|
* - their_ellswift: EllSwiftPubKey that was received from the other side.
|
||||||
|
* - our_ellswift: EllSwiftPubKey that was sent to the other side (must have been generated
|
||||||
|
* from *this using EllSwiftCreate()).
|
||||||
|
* - initiating: whether we are the initiating party (true) or responding party (false).
|
||||||
|
*/
|
||||||
|
ECDHSecret ComputeBIP324ECDHSecret(const EllSwiftPubKey& their_ellswift,
|
||||||
|
const EllSwiftPubKey& our_ellswift,
|
||||||
|
bool initiating) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct CExtKey {
|
struct CExtKey {
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
|
|
||||||
#include <hash.h>
|
#include <hash.h>
|
||||||
#include <secp256k1.h>
|
#include <secp256k1.h>
|
||||||
|
#include <secp256k1_ellswift.h>
|
||||||
#include <secp256k1_extrakeys.h>
|
#include <secp256k1_extrakeys.h>
|
||||||
#include <secp256k1_recovery.h>
|
#include <secp256k1_recovery.h>
|
||||||
#include <secp256k1_schnorrsig.h>
|
#include <secp256k1_schnorrsig.h>
|
||||||
|
@ -335,6 +336,20 @@ bool CPubKey::Derive(CPubKey& pubkeyChild, ChainCode &ccChild, unsigned int nChi
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CPubKey EllSwiftPubKey::Decode() const
|
||||||
|
{
|
||||||
|
secp256k1_pubkey pubkey;
|
||||||
|
secp256k1_ellswift_decode(secp256k1_context_static, &pubkey, UCharCast(m_pubkey.data()));
|
||||||
|
|
||||||
|
size_t sz = CPubKey::COMPRESSED_SIZE;
|
||||||
|
std::array<uint8_t, CPubKey::COMPRESSED_SIZE> vch_bytes;
|
||||||
|
|
||||||
|
secp256k1_ec_pubkey_serialize(secp256k1_context_static, vch_bytes.data(), &sz, &pubkey, SECP256K1_EC_COMPRESSED);
|
||||||
|
assert(sz == vch_bytes.size());
|
||||||
|
|
||||||
|
return CPubKey{vch_bytes.begin(), vch_bytes.end()};
|
||||||
|
}
|
||||||
|
|
||||||
void CExtPubKey::Encode(unsigned char code[BIP32_EXTKEY_SIZE]) const {
|
void CExtPubKey::Encode(unsigned char code[BIP32_EXTKEY_SIZE]) const {
|
||||||
code[0] = nDepth;
|
code[0] = nDepth;
|
||||||
memcpy(code+1, vchFingerprint, 4);
|
memcpy(code+1, vchFingerprint, 4);
|
||||||
|
|
22
src/pubkey.h
22
src/pubkey.h
|
@ -291,6 +291,28 @@ public:
|
||||||
SERIALIZE_METHODS(XOnlyPubKey, obj) { READWRITE(obj.m_keydata); }
|
SERIALIZE_METHODS(XOnlyPubKey, obj) { READWRITE(obj.m_keydata); }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/** An ElligatorSwift-encoded public key. */
|
||||||
|
struct EllSwiftPubKey
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
static constexpr size_t SIZE = 64;
|
||||||
|
std::array<std::byte, SIZE> m_pubkey;
|
||||||
|
|
||||||
|
public:
|
||||||
|
/** Construct a new ellswift public key from a given serialization. */
|
||||||
|
EllSwiftPubKey(const std::array<std::byte, SIZE>& ellswift) :
|
||||||
|
m_pubkey(ellswift) {}
|
||||||
|
|
||||||
|
/** Decode to normal compressed CPubKey (for debugging purposes). */
|
||||||
|
CPubKey Decode() const;
|
||||||
|
|
||||||
|
// Read-only access for serialization.
|
||||||
|
const std::byte* data() const { return m_pubkey.data(); }
|
||||||
|
static constexpr size_t size() { return SIZE; }
|
||||||
|
auto begin() const { return m_pubkey.cbegin(); }
|
||||||
|
auto end() const { return m_pubkey.cend(); }
|
||||||
|
};
|
||||||
|
|
||||||
struct CExtPubKey {
|
struct CExtPubKey {
|
||||||
unsigned char version[4];
|
unsigned char version[4];
|
||||||
unsigned char nDepth;
|
unsigned char nDepth;
|
||||||
|
|
|
@ -274,6 +274,7 @@ Span<std::byte> MakeWritableByteSpan(V&& v) noexcept
|
||||||
// Helper functions to safely cast to unsigned char pointers.
|
// Helper functions to safely cast to unsigned char pointers.
|
||||||
inline unsigned char* UCharCast(char* c) { return (unsigned char*)c; }
|
inline unsigned char* UCharCast(char* c) { return (unsigned char*)c; }
|
||||||
inline unsigned char* UCharCast(unsigned char* c) { return c; }
|
inline unsigned char* UCharCast(unsigned char* c) { return c; }
|
||||||
|
inline unsigned char* UCharCast(std::byte* c) { return (unsigned char*)c; }
|
||||||
inline const unsigned char* UCharCast(const char* c) { return (unsigned char*)c; }
|
inline const unsigned char* UCharCast(const char* c) { return (unsigned char*)c; }
|
||||||
inline const unsigned char* UCharCast(const unsigned char* c) { return c; }
|
inline const unsigned char* UCharCast(const unsigned char* c) { return c; }
|
||||||
inline const unsigned char* UCharCast(const std::byte* c) { return reinterpret_cast<const unsigned char*>(c); }
|
inline const unsigned char* UCharCast(const std::byte* c) { return reinterpret_cast<const unsigned char*>(c); }
|
||||||
|
|
Loading…
Add table
Reference in a new issue