UltrafastSecp256k1 3.50.0
Ultra high-performance secp256k1 elliptic curve cryptography library
Loading...
Searching...
No Matches
schnorr.hpp
Go to the documentation of this file.
1#ifndef SECP256K1_SCHNORR_HPP
2#define SECP256K1_SCHNORR_HPP
3#pragma once
4
5// ============================================================================
6// Schnorr Signatures (BIP-340) for secp256k1
7// ============================================================================
8// Implements BIP-340 Schnorr signatures:
9// - X-only public keys (32 bytes)
10// - 64-byte signatures (R.x || s)
11// - Tagged hashing per BIP-340 spec
12//
13// Reference: https://github.com/bitcoin/bips/blob/master/bip-0340.mediawiki
14// ============================================================================
15
16#include <array>
17#include <cstdint>
18#include "secp256k1/scalar.hpp"
19#include "secp256k1/point.hpp"
20
21namespace secp256k1 {
22
23// -- Schnorr Signature --------------------------------------------------------
24
26 std::array<std::uint8_t, 32> r; // R.x (x-coordinate of nonce point)
27 fast::Scalar s; // scalar s
28
29 // 64-byte compact encoding: r [32 bytes] concatenated with s [32 bytes]
30 std::array<std::uint8_t, 64> to_bytes() const;
31 static SchnorrSignature from_bytes(const std::array<std::uint8_t, 64>& data);
32 static SchnorrSignature from_bytes(const std::uint8_t* data64);
33
34 // BIP-340 strict parsing: rejects if r >= p or s >= n or s == 0.
35 // Returns false for non-canonical encodings (BIP-340 compliance).
36 static bool parse_strict(const std::uint8_t* data64, SchnorrSignature& out) noexcept;
37 static bool parse_strict(const std::array<std::uint8_t, 64>& data, SchnorrSignature& out) noexcept;
38};
39
40// -- Pre-computed Schnorr Keypair ----------------------------------------------
41// Equivalent to libsecp256k1's secp256k1_keypair: pre-computes pubkey x-bytes
42// and adjusts private key for even-Y, saving 1 gen_mul + 1 inverse per sign.
43
45 fast::Scalar d; // signing key (negated for even Y)
46 std::array<std::uint8_t, 32> px; // x-coordinate bytes of pubkey
47};
48
49// Create a pre-computed keypair (call once, then reuse for multiple signs).
51
52// -- BIP-340 Operations -------------------------------------------------------
53
54// Sign using pre-computed keypair (fast: only 1 gen_mul per sign).
55//
56// aux_rand: MUST be 32 bytes of fresh cryptographic randomness (e.g. from
57// OS CSPRNG). Per BIP-340: aux_rand provides synthetic nonce hedging --
58// the signing nonce k is derived as H(d XOR H(aux_rand) || P || m).
59// All-zeros aux_rand makes the nonce fully deterministic (no entropy
60// hedging), which is safe against nonce reuse but not against fault
61// injection or HMAC-state compromise.
62// WARNING: Never reuse aux_rand across different messages with the same
63// key -- while BIP-340 nonces remain safe, unique randomness per sign
64// maximizes defense-in-depth.
66 const std::array<std::uint8_t, 32>& msg,
67 const std::array<std::uint8_t, 32>& aux_rand);
68
69// Sign + verify (FIPS 186-4 fault attack countermeasure).
70// Verifies the produced Schnorr signature before returning it.
72 const std::array<std::uint8_t, 32>& msg,
73 const std::array<std::uint8_t, 32>& aux_rand);
74
75// Sign from raw private key (convenience: creates keypair internally).
76// See above for aux_rand entropy requirements.
78 const std::array<std::uint8_t, 32>& msg,
79 const std::array<std::uint8_t, 32>& aux_rand);
80
81// Raw key sign + verify (fault attack countermeasure).
83 const std::array<std::uint8_t, 32>& msg,
84 const std::array<std::uint8_t, 32>& aux_rand);
85
86// Verify a BIP-340 Schnorr signature.
87// pubkey_x: 32-byte x-only public key
88// msg: 32-byte message
89// sig: 64-byte signature
90bool schnorr_verify(const std::uint8_t* pubkey_x32,
91 const std::uint8_t* msg32,
92 const SchnorrSignature& sig);
93
94// Array convenience wrappers
95bool schnorr_verify(const std::array<std::uint8_t, 32>& pubkey_x,
96 const std::array<std::uint8_t, 32>& msg,
97 const SchnorrSignature& sig);
98
99bool schnorr_verify(const std::array<std::uint8_t, 32>& pubkey_x,
100 const std::uint8_t* msg32,
101 const SchnorrSignature& sig);
102
103// -- Pre-cached X-only Public Key ---------------------------------------------
104// Caches the full Point (avoiding sqrt per verify), similar to libsecp's
105// secp256k1_xonly_pubkey which internally stores the cached (x,y) point.
106
109 std::array<std::uint8_t, 32> x_bytes;
110};
111
112// Parse an x-only pubkey (call once; lift_x + sqrt done here).
113// Returns false if the x-coordinate is not on the curve.
115 const std::uint8_t* pubkey_x32);
117 const std::array<std::uint8_t, 32>& pubkey_x);
118
119// Create from keypair (no sqrt needed -- point already known).
121
122// Verify using pre-cached pubkey (fast: skips lift_x sqrt).
124 const std::array<std::uint8_t, 32>& msg,
125 const SchnorrSignature& sig);
126
127// Raw-pointer msg overload for pre-cached pubkey.
129 const std::uint8_t* msg32,
130 const SchnorrSignature& sig);
131
132// -- Tagged Hashing (BIP-340) -------------------------------------------------
133
134// H_tag: SHA256 of (SHA256(tag) concatenated twice with msg)
135std::array<std::uint8_t, 32> tagged_hash(const char* tag,
136 const void* data, std::size_t len);
137
138// X-only public key from private key (BIP-340: negate if Y is odd)
139std::array<std::uint8_t, 32> schnorr_pubkey(const fast::Scalar& private_key);
140
141} // namespace secp256k1
142
143#endif // SECP256K1_SCHNORR_HPP
SchnorrSignature schnorr_sign_verified(const SchnorrKeypair &kp, const std::array< std::uint8_t, 32 > &msg, const std::array< std::uint8_t, 32 > &aux_rand)
SchnorrSignature schnorr_sign(const SchnorrKeypair &kp, const std::array< std::uint8_t, 32 > &msg, const std::array< std::uint8_t, 32 > &aux_rand)
std::array< std::uint8_t, 32 > tagged_hash(const char *tag, const void *data, std::size_t len)
bool schnorr_verify(const std::uint8_t *pubkey_x32, const std::uint8_t *msg32, const SchnorrSignature &sig)
bool schnorr_xonly_pubkey_parse(SchnorrXonlyPubkey &out, const std::uint8_t *pubkey_x32)
SchnorrXonlyPubkey schnorr_xonly_from_keypair(const SchnorrKeypair &kp)
SchnorrKeypair schnorr_keypair_create(const fast::Scalar &private_key)
std::array< std::uint8_t, 32 > schnorr_pubkey(const fast::Scalar &private_key)
std::array< std::uint8_t, 32 > px
Definition schnorr.hpp:46
std::array< std::uint8_t, 32 > r
Definition schnorr.hpp:26
std::array< std::uint8_t, 64 > to_bytes() const
static bool parse_strict(const std::array< std::uint8_t, 64 > &data, SchnorrSignature &out) noexcept
static bool parse_strict(const std::uint8_t *data64, SchnorrSignature &out) noexcept
static SchnorrSignature from_bytes(const std::uint8_t *data64)
static SchnorrSignature from_bytes(const std::array< std::uint8_t, 64 > &data)
std::array< std::uint8_t, 32 > x_bytes
Definition schnorr.hpp:109