Skip to content

Commit c39fd39

Browse files
josibaketheuni
andcommitted
crypto: add KeyPair wrapper class
Add a `KeyPair` class which wraps the `secp256k1_keypair`. This keeps the secret data in secure memory and enables passing the `KeyPair` object directly to libsecp256k1 functions expecting a `secp256k1_keypair`. Motivation: when passing `CKeys` for taproot outputs to libsecp256k1 functions, the first step is to create a `secp256k1_keypair` data type and use that instead. This is so the libsecp256k1 function can determine if the key needs to be negated, e.g., when signing. This is a bit clunky in that it creates an extra step when using a `CKey` for a taproot output and also involves copying the secret data into a temporary object, which the caller must then take care to cleanse. In addition, the logic for applying the merkle_root tweak currently only exists in the `SignSchnorr` function. In a later commit, we will add the merkle_root tweaking logic to this function, which will make the merkle_root logic reusable outside of signing by using the `KeyPair` class directly. Co-authored-by: Cory Fields <[email protected]>
1 parent 5d507a0 commit c39fd39

File tree

2 files changed

+74
-0
lines changed

2 files changed

+74
-0
lines changed

src/key.cpp

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -363,6 +363,11 @@ ECDHSecret CKey::ComputeBIP324ECDHSecret(const EllSwiftPubKey& their_ellswift, c
363363
return output;
364364
}
365365

366+
KeyPair CKey::ComputeKeyPair() const
367+
{
368+
return KeyPair(*this);
369+
}
370+
366371
CKey GenerateRandomKey(bool compressed) noexcept
367372
{
368373
CKey key;
@@ -420,6 +425,16 @@ void CExtKey::Decode(const unsigned char code[BIP32_EXTKEY_SIZE]) {
420425
if ((nDepth == 0 && (nChild != 0 || ReadLE32(vchFingerprint) != 0)) || code[41] != 0) key = CKey();
421426
}
422427

428+
KeyPair::KeyPair(const CKey& key)
429+
{
430+
static_assert(std::tuple_size<KeyType>() == sizeof(secp256k1_keypair));
431+
MakeKeyPairData();
432+
auto keypair = reinterpret_cast<secp256k1_keypair*>(m_keypair->data());
433+
434+
bool success = secp256k1_keypair_create(secp256k1_context_sign, keypair, UCharCast(key.data()));
435+
if (!success) ClearKeyPairData();
436+
}
437+
423438
bool ECC_InitSanityCheck() {
424439
CKey key = GenerateRandomKey();
425440
CPubKey pubkey = key.GetPubKey();

src/key.h

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ constexpr static size_t ECDH_SECRET_SIZE = CSHA256::OUTPUT_SIZE;
2828
// Used to represent ECDH shared secret (ECDH_SECRET_SIZE bytes)
2929
using ECDHSecret = std::array<std::byte, ECDH_SECRET_SIZE>;
3030

31+
class KeyPair;
32+
3133
/** An encapsulated private key. */
3234
class CKey
3335
{
@@ -202,6 +204,11 @@ class CKey
202204
ECDHSecret ComputeBIP324ECDHSecret(const EllSwiftPubKey& their_ellswift,
203205
const EllSwiftPubKey& our_ellswift,
204206
bool initiating) const;
207+
/** Compute a KeyPair
208+
*
209+
* Wraps a `secp256k1_keypair` type.
210+
*/
211+
KeyPair ComputeKeyPair() const;
205212
};
206213

207214
CKey GenerateRandomKey(bool compressed = true) noexcept;
@@ -235,6 +242,58 @@ struct CExtKey {
235242
void SetSeed(Span<const std::byte> seed);
236243
};
237244

245+
/** KeyPair
246+
*
247+
* Wraps a `secp256k1_keypair` type, an opaque data structure for holding a secret and public key.
248+
* This is intended for BIP340 keys and allows us to easily determine if the secret key needs to
249+
* be negated by checking the parity of the public key. This class primarily intended for passing
250+
* secret keys to libsecp256k1 functions expecting a `secp256k1_keypair`. For all other cases,
251+
* CKey should be preferred.
252+
*/
253+
class KeyPair
254+
{
255+
public:
256+
KeyPair() noexcept = default;
257+
KeyPair(KeyPair&&) noexcept = default;
258+
KeyPair& operator=(KeyPair&&) noexcept = default;
259+
KeyPair& operator=(const KeyPair& other)
260+
{
261+
if (this != &other) {
262+
if (other.m_keypair) {
263+
MakeKeyPairData();
264+
*m_keypair = *other.m_keypair;
265+
} else {
266+
ClearKeyPairData();
267+
}
268+
}
269+
return *this;
270+
}
271+
272+
KeyPair(const KeyPair& other) { *this = other; }
273+
274+
friend KeyPair CKey::ComputeKeyPair() const;
275+
[[nodiscard]] bool SignSchnorr(const uint256& hash, Span<unsigned char> sig, const uint256& aux) const;
276+
277+
//! Check whether this keypair is valid.
278+
bool IsValid() const { return !!m_keypair; }
279+
280+
private:
281+
KeyPair(const CKey& key);
282+
283+
using KeyType = std::array<unsigned char, 96>;
284+
secure_unique_ptr<KeyType> m_keypair;
285+
286+
void MakeKeyPairData()
287+
{
288+
if (!m_keypair) m_keypair = make_secure_unique<KeyType>();
289+
}
290+
291+
void ClearKeyPairData()
292+
{
293+
m_keypair.reset();
294+
}
295+
};
296+
238297
/** Check that required EC support is available at runtime. */
239298
bool ECC_InitSanityCheck();
240299

0 commit comments

Comments
 (0)