diff --git a/src/key.cpp b/src/key.cpp index 97d7821e740..3945170515c 100644 --- a/src/key.cpp +++ b/src/key.cpp @@ -363,6 +363,11 @@ ECDHSecret CKey::ComputeBIP324ECDHSecret(const EllSwiftPubKey& their_ellswift, c return output; } +KeyPair CKey::ComputeKeyPair() const +{ + return KeyPair(*this); +} + CKey GenerateRandomKey(bool compressed) noexcept { CKey key; @@ -420,6 +425,16 @@ void CExtKey::Decode(const unsigned char code[BIP32_EXTKEY_SIZE]) { if ((nDepth == 0 && (nChild != 0 || ReadLE32(vchFingerprint) != 0)) || code[41] != 0) key = CKey(); } +KeyPair::KeyPair(const CKey& key) +{ + static_assert(std::tuple_size() == sizeof(secp256k1_keypair)); + MakeKeyPairData(); + auto keypair = reinterpret_cast(m_keypair->data()); + + bool success = secp256k1_keypair_create(secp256k1_context_sign, keypair, UCharCast(key.data())); + if (!success) ClearKeyPairData(); +} + bool ECC_InitSanityCheck() { CKey key = GenerateRandomKey(); CPubKey pubkey = key.GetPubKey(); diff --git a/src/key.h b/src/key.h index bbca2c4deef..f84117f98f0 100644 --- a/src/key.h +++ b/src/key.h @@ -28,6 +28,8 @@ constexpr static size_t ECDH_SECRET_SIZE = CSHA256::OUTPUT_SIZE; // Used to represent ECDH shared secret (ECDH_SECRET_SIZE bytes) using ECDHSecret = std::array; +class KeyPair; + /** An encapsulated private key. */ class CKey { @@ -202,6 +204,11 @@ public: ECDHSecret ComputeBIP324ECDHSecret(const EllSwiftPubKey& their_ellswift, const EllSwiftPubKey& our_ellswift, bool initiating) const; + /** Compute a KeyPair + * + * Wraps a `secp256k1_keypair` type. + */ + KeyPair ComputeKeyPair() const; }; CKey GenerateRandomKey(bool compressed = true) noexcept; @@ -235,6 +242,58 @@ struct CExtKey { void SetSeed(Span seed); }; +/** KeyPair + * + * Wraps a `secp256k1_keypair` type, an opaque data structure for holding a secret and public key. + * This is intended for BIP340 keys and allows us to easily determine if the secret key needs to + * be negated by checking the parity of the public key. This class primarily intended for passing + * secret keys to libsecp256k1 functions expecting a `secp256k1_keypair`. For all other cases, + * CKey should be preferred. + */ +class KeyPair +{ +public: + KeyPair() noexcept = default; + KeyPair(KeyPair&&) noexcept = default; + KeyPair& operator=(KeyPair&&) noexcept = default; + KeyPair& operator=(const KeyPair& other) + { + if (this != &other) { + if (other.m_keypair) { + MakeKeyPairData(); + *m_keypair = *other.m_keypair; + } else { + ClearKeyPairData(); + } + } + return *this; + } + + KeyPair(const KeyPair& other) { *this = other; } + + friend KeyPair CKey::ComputeKeyPair() const; + [[nodiscard]] bool SignSchnorr(const uint256& hash, Span sig, const uint256& aux) const; + + //! Check whether this keypair is valid. + bool IsValid() const { return !!m_keypair; } + +private: + KeyPair(const CKey& key); + + using KeyType = std::array; + secure_unique_ptr m_keypair; + + void MakeKeyPairData() + { + if (!m_keypair) m_keypair = make_secure_unique(); + } + + void ClearKeyPairData() + { + m_keypair.reset(); + } +}; + /** Check that required EC support is available at runtime. */ bool ECC_InitSanityCheck();