Skip to content

Commit eff72a0

Browse files
sipadhruv
andcommitted
Add ElligatorSwift key creation and ECDH logic
Co-authored-by: Dhruv Mehta <[email protected]>
1 parent 42239f8 commit eff72a0

File tree

5 files changed

+102
-0
lines changed

5 files changed

+102
-0
lines changed

src/key.cpp

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
#include <random.h>
1212

1313
#include <secp256k1.h>
14+
#include <secp256k1_ellswift.h>
1415
#include <secp256k1_extrakeys.h>
1516
#include <secp256k1_recovery.h>
1617
#include <secp256k1_schnorrsig.h>
@@ -331,6 +332,42 @@ bool CKey::Derive(CKey& keyChild, ChainCode &ccChild, unsigned int nChild, const
331332
return ret;
332333
}
333334

335+
EllSwiftPubKey CKey::EllSwiftCreate(Span<const std::byte> ent32) const
336+
{
337+
assert(fValid);
338+
assert(ent32.size() == 32);
339+
std::array<std::byte, EllSwiftPubKey::size()> encoded_pubkey;
340+
341+
auto success = secp256k1_ellswift_create(secp256k1_context_sign,
342+
UCharCast(encoded_pubkey.data()),
343+
keydata.data(),
344+
UCharCast(ent32.data()));
345+
346+
// Should always succeed for valid keys (asserted above).
347+
assert(success);
348+
return {encoded_pubkey};
349+
}
350+
351+
ECDHSecret CKey::ComputeBIP324ECDHSecret(const EllSwiftPubKey& their_ellswift, const EllSwiftPubKey& our_ellswift, bool initiating) const
352+
{
353+
assert(fValid);
354+
355+
ECDHSecret output;
356+
// BIP324 uses the initiator as party A, and the responder as party B. Remap the inputs
357+
// accordingly:
358+
bool success = secp256k1_ellswift_xdh(secp256k1_context_sign,
359+
UCharCast(output.data()),
360+
UCharCast(initiating ? our_ellswift.data() : their_ellswift.data()),
361+
UCharCast(initiating ? their_ellswift.data() : our_ellswift.data()),
362+
keydata.data(),
363+
initiating ? 0 : 1,
364+
secp256k1_ellswift_xdh_hash_function_bip324,
365+
nullptr);
366+
// Should always succeed for valid keys (assert above).
367+
assert(success);
368+
return output;
369+
}
370+
334371
bool CExtKey::Derive(CExtKey &out, unsigned int _nChild) const {
335372
if (nDepth == std::numeric_limits<unsigned char>::max()) return false;
336373
out.nDepth = nDepth + 1;

src/key.h

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,12 @@
2222
*/
2323
typedef std::vector<unsigned char, secure_allocator<unsigned char> > CPrivKey;
2424

25+
/** Size of ECDH shared secrets. */
26+
constexpr static size_t ECDH_SECRET_SIZE = CSHA256::OUTPUT_SIZE;
27+
28+
// Used to represent ECDH shared secret (ECDH_SECRET_SIZE bytes)
29+
using ECDHSecret = std::array<std::byte, ECDH_SECRET_SIZE>;
30+
2531
/** An encapsulated private key. */
2632
class CKey
2733
{
@@ -156,6 +162,27 @@ class CKey
156162

157163
//! Load private key and check that public key matches.
158164
bool Load(const CPrivKey& privkey, const CPubKey& vchPubKey, bool fSkipCheck);
165+
166+
/** Create an ellswift-encoded public key for this key, with specified entropy.
167+
*
168+
* entropy must be a 32-byte span with additional entropy to use in the encoding. Every
169+
* public key has ~2^256 different encodings, and this function will deterministically pick
170+
* one of them, based on entropy. Note that even without truly random entropy, the
171+
* resulting encoding will be indistinguishable from uniform to any adversary who does not
172+
* know the private key (because the private key itself is always used as entropy as well).
173+
*/
174+
EllSwiftPubKey EllSwiftCreate(Span<const std::byte> entropy) const;
175+
176+
/** Compute a BIP324-style ECDH shared secret.
177+
*
178+
* - their_ellswift: EllSwiftPubKey that was received from the other side.
179+
* - our_ellswift: EllSwiftPubKey that was sent to the other side (must have been generated
180+
* from *this using EllSwiftCreate()).
181+
* - initiating: whether we are the initiating party (true) or responding party (false).
182+
*/
183+
ECDHSecret ComputeBIP324ECDHSecret(const EllSwiftPubKey& their_ellswift,
184+
const EllSwiftPubKey& our_ellswift,
185+
bool initiating) const;
159186
};
160187

161188
struct CExtKey {

src/pubkey.cpp

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
#include <hash.h>
99
#include <secp256k1.h>
10+
#include <secp256k1_ellswift.h>
1011
#include <secp256k1_extrakeys.h>
1112
#include <secp256k1_recovery.h>
1213
#include <secp256k1_schnorrsig.h>
@@ -335,6 +336,20 @@ bool CPubKey::Derive(CPubKey& pubkeyChild, ChainCode &ccChild, unsigned int nChi
335336
return true;
336337
}
337338

339+
CPubKey EllSwiftPubKey::Decode() const
340+
{
341+
secp256k1_pubkey pubkey;
342+
secp256k1_ellswift_decode(secp256k1_context_static, &pubkey, UCharCast(m_pubkey.data()));
343+
344+
size_t sz = CPubKey::COMPRESSED_SIZE;
345+
std::array<uint8_t, CPubKey::COMPRESSED_SIZE> vch_bytes;
346+
347+
secp256k1_ec_pubkey_serialize(secp256k1_context_static, vch_bytes.data(), &sz, &pubkey, SECP256K1_EC_COMPRESSED);
348+
assert(sz == vch_bytes.size());
349+
350+
return CPubKey{vch_bytes.begin(), vch_bytes.end()};
351+
}
352+
338353
void CExtPubKey::Encode(unsigned char code[BIP32_EXTKEY_SIZE]) const {
339354
code[0] = nDepth;
340355
memcpy(code+1, vchFingerprint, 4);

src/pubkey.h

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -291,6 +291,28 @@ class XOnlyPubKey
291291
SERIALIZE_METHODS(XOnlyPubKey, obj) { READWRITE(obj.m_keydata); }
292292
};
293293

294+
/** An ElligatorSwift-encoded public key. */
295+
struct EllSwiftPubKey
296+
{
297+
private:
298+
static constexpr size_t SIZE = 64;
299+
std::array<std::byte, SIZE> m_pubkey;
300+
301+
public:
302+
/** Construct a new ellswift public key from a given serialization. */
303+
EllSwiftPubKey(const std::array<std::byte, SIZE>& ellswift) :
304+
m_pubkey(ellswift) {}
305+
306+
/** Decode to normal compressed CPubKey (for debugging purposes). */
307+
CPubKey Decode() const;
308+
309+
// Read-only access for serialization.
310+
const std::byte* data() const { return m_pubkey.data(); }
311+
static constexpr size_t size() { return SIZE; }
312+
auto begin() const { return m_pubkey.cbegin(); }
313+
auto end() const { return m_pubkey.cend(); }
314+
};
315+
294316
struct CExtPubKey {
295317
unsigned char version[4];
296318
unsigned char nDepth;

src/span.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -274,6 +274,7 @@ Span<std::byte> MakeWritableByteSpan(V&& v) noexcept
274274
// Helper functions to safely cast to unsigned char pointers.
275275
inline unsigned char* UCharCast(char* c) { return (unsigned char*)c; }
276276
inline unsigned char* UCharCast(unsigned char* c) { return c; }
277+
inline unsigned char* UCharCast(std::byte* c) { return (unsigned char*)c; }
277278
inline const unsigned char* UCharCast(const char* c) { return (unsigned char*)c; }
278279
inline const unsigned char* UCharCast(const unsigned char* c) { return c; }
279280
inline const unsigned char* UCharCast(const std::byte* c) { return reinterpret_cast<const unsigned char*>(c); }

0 commit comments

Comments
 (0)