You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
515 lines
16 KiB
515 lines
16 KiB
// Copyright (c) 2013-2022 The btcsuite developers |
|
|
|
package schnorr |
|
|
|
import ( |
|
"fmt" |
|
|
|
"crypto.orly/ec" |
|
"crypto.orly/ec/chainhash" |
|
"crypto.orly/ec/secp256k1" |
|
"lol.mleku.dev/chk" |
|
) |
|
|
|
const ( |
|
// SignatureSize is the size of an encoded Schnorr signature. |
|
SignatureSize = 64 |
|
// scalarSize is the size of an encoded big endian scalar. |
|
scalarSize = 32 |
|
) |
|
|
|
var ( |
|
// rfc6979ExtraDataV0 is the extra data to feed to RFC6979 when generating |
|
// the deterministic nonce for the BIP-340 scheme. This ensures the same |
|
// nonce is not generated for the same message and key as for other signing |
|
// algorithms such as ECDSA. |
|
// |
|
// It is equal to SHA-256(by("BIP-340")). |
|
rfc6979ExtraDataV0 = [32]uint8{ |
|
0xa3, 0xeb, 0x4c, 0x18, 0x2f, 0xae, 0x7e, 0xf4, |
|
0xe8, 0x10, 0xc6, 0xee, 0x13, 0xb0, 0xe9, 0x26, |
|
0x68, 0x6d, 0x71, 0xe8, 0x7f, 0x39, 0x4f, 0x79, |
|
0x9c, 0x00, 0xa5, 0x21, 0x03, 0xcb, 0x4e, 0x17, |
|
} |
|
) |
|
|
|
// Signature is a type representing a Schnorr signature. |
|
type Signature struct { |
|
r btcec.FieldVal |
|
s btcec.ModNScalar |
|
} |
|
|
|
// NewSignature instantiates a new signature given some r and s values. |
|
func NewSignature(r *btcec.FieldVal, s *btcec.ModNScalar) *Signature { |
|
var sig Signature |
|
sig.r.Set(r).Normalize() |
|
sig.s.Set(s) |
|
return &sig |
|
} |
|
|
|
// Serialize returns the Schnorr signature in a stricter format. |
|
// |
|
// The signatures are encoded as |
|
// |
|
// sig[0:32] |
|
// x coordinate of the point R, encoded as a big-endian uint256 |
|
// sig[32:64] |
|
// s, encoded also as big-endian uint256 |
|
func (sig Signature) Serialize() []byte { |
|
// Total length of returned signature is the length of r and s. |
|
var b [SignatureSize]byte |
|
sig.r.PutBytesUnchecked(b[0:32]) |
|
sig.s.PutBytesUnchecked(b[32:64]) |
|
return b[:] |
|
} |
|
|
|
// ParseSignature parses a signature according to the BIP-340 specification and |
|
// enforces the following additional restrictions specific to secp256k1: |
|
// |
|
// - The r component must be in the valid range for secp256k1 field elements |
|
// |
|
// - The s component must be in the valid range for secp256k1 scalars |
|
func ParseSignature(sig []byte) (*Signature, error) { |
|
// The signature must be the correct length. |
|
sigLen := len(sig) |
|
if sigLen < SignatureSize { |
|
str := fmt.Sprintf( |
|
"malformed signature: too short: %d < %d", sigLen, |
|
SignatureSize, |
|
) |
|
return nil, signatureError(ErrSigTooShort, str) |
|
} |
|
if sigLen > SignatureSize { |
|
str := fmt.Sprintf( |
|
"malformed signature: too long: %d > %d", sigLen, |
|
SignatureSize, |
|
) |
|
return nil, signatureError(ErrSigTooLong, str) |
|
} |
|
// The signature is validly encoded at this point, however, enforce |
|
// additional restrictions to ensure r is in the range [0, p-1], and s is in |
|
// the range [0, n-1] since valid Schnorr signatures are required to be in |
|
// that range per spec. |
|
var r btcec.FieldVal |
|
if overflow := r.SetByteSlice(sig[0:32]); overflow { |
|
str := "invalid signature: r >= field prime" |
|
return nil, signatureError(ErrSigRTooBig, str) |
|
} |
|
var s btcec.ModNScalar |
|
s.SetByteSlice(sig[32:64]) |
|
// Return the signature. |
|
return NewSignature(&r, &s), nil |
|
} |
|
|
|
// IsEqual compares this Signature instance to the one passed, returning true if |
|
// both Signatures are equivalent. A signature is equivalent to another if they |
|
// both have the same scalar value for R and S. |
|
func (sig Signature) IsEqual(otherSig *Signature) bool { |
|
return sig.r.Equals(&otherSig.r) && sig.s.Equals(&otherSig.s) |
|
} |
|
|
|
// schnorrVerify attempt to verify the signature for the provided hash and |
|
// secp256k1 public key and either returns nil if successful or a specific error |
|
// indicating why it failed if not successful. |
|
// |
|
// This differs from the exported Verify method in that it returns a specific |
|
// error to support better testing, while the exported method simply returns a |
|
// bool indicating success or failure. |
|
func schnorrVerify(sig *Signature, hash []byte, pubKeyBytes []byte) error { |
|
// The algorithm for producing a BIP-340 signature is described in |
|
// README.md and is reproduced here for reference: |
|
// |
|
// 1. Fail if m is not 32 bytes |
|
// 2. P = lift_x(int(pk)). |
|
// 3. r = int(sig[0:32]); fail is r >= p. |
|
// 4. s = int(sig[32:64]); fail if s >= n. |
|
// 5. e = int(tagged_hash("BIP0340/challenge", bytes(r) || bytes(P) || M)) mod n. |
|
// 6. R = s*G - e*P |
|
// 7. Fail if is_infinite(R) |
|
// 8. Fail if not hash_even_y(R) |
|
// 9. Fail is x(R) != r. |
|
// 10. Return success iff not failure occured before reachign this |
|
// point. |
|
|
|
// // Step 1. |
|
// // |
|
// // Fail if m is not 32 bytes |
|
// if len(hash) != scalarSize { |
|
// str := fmt.Sprintf("wrong size for message (got %v, want %v)", |
|
// len(hash), scalarSize) |
|
// return signatureError(schnorr.ErrInvalidHashLen, str) |
|
// } |
|
|
|
// Step 2. |
|
// |
|
// P = lift_x(int(pk)) |
|
// |
|
// Fail if P is not a point on the curve |
|
pubKey, err := ParsePubKey(pubKeyBytes) |
|
if chk.E(err) { |
|
return err |
|
} |
|
if !pubKey.IsOnCurve() { |
|
str := "pubkey point is not on curve" |
|
return signatureError(ErrPubKeyNotOnCurve, str) |
|
} |
|
// Step 3. |
|
// |
|
// Fail if r >= p |
|
// |
|
// Note this is already handled by the fact r is a field element. |
|
// |
|
// Step 4. |
|
// |
|
// Fail if s >= n |
|
// |
|
// Note this is already handled by the fact s is a mod n scalar. |
|
// |
|
// Step 5. |
|
// |
|
// e = int(tagged_hash("BIP0340/challenge", bytes(r) || bytes(P) || M)) mod n. |
|
var rBytes [32]byte |
|
sig.r.PutBytesUnchecked(rBytes[:]) |
|
pBytes := SerializePubKey(pubKey) |
|
commitment := chainhash.TaggedHash( |
|
chainhash.TagBIP0340Challenge, rBytes[:], pBytes, hash, |
|
) |
|
var e btcec.ModNScalar |
|
e.SetBytes((*[32]byte)(commitment)) |
|
// Negate e here so we can use AddNonConst below to subtract the s*G |
|
// point from e*P. |
|
e.Negate() |
|
// Step 6. |
|
// |
|
// R = s*G - e*P |
|
var P, R, sG, eP btcec.JacobianPoint |
|
pubKey.AsJacobian(&P) |
|
btcec.ScalarBaseMultNonConst(&sig.s, &sG) |
|
btcec.ScalarMultNonConst(&e, &P, &eP) |
|
btcec.AddNonConst(&sG, &eP, &R) |
|
// Step 7. |
|
// |
|
// Fail if R is the point at infinity |
|
if (R.X.IsZero() && R.Y.IsZero()) || R.Z.IsZero() { |
|
str := "calculated R point is the point at infinity" |
|
return signatureError(ErrSigRNotOnCurve, str) |
|
} |
|
// Step 8. |
|
// |
|
// Fail if R.y is odd |
|
// |
|
// Note that R must be in affine coordinates for this check. |
|
R.ToAffine() |
|
if R.Y.IsOdd() { |
|
str := "calculated R y-value is odd" |
|
return signatureError(ErrSigRYIsOdd, str) |
|
} |
|
// Step 9. |
|
// |
|
// Verified if R.x == r |
|
// |
|
// Note that R must be in affine coordinates for this check. |
|
if !sig.r.Equals(&R.X) { |
|
str := "calculated R point was not given R" |
|
return signatureError(ErrUnequalRValues, str) |
|
} |
|
// Step 10. |
|
// |
|
// Return success iff not failure occured before reachign this |
|
return nil |
|
} |
|
|
|
// Verify returns whether or not the signature is valid for the provided hash |
|
// and secp256k1 public key. |
|
func (sig *Signature) Verify(hash []byte, pubKey *btcec.PublicKey) bool { |
|
pubkeyBytes := SerializePubKey(pubKey) |
|
return schnorrVerify(sig, hash, pubkeyBytes) == nil |
|
} |
|
|
|
// zeroArray zeroes the memory of a scalar array. |
|
func zeroArray(a *[scalarSize]byte) { |
|
for i := 0; i < scalarSize; i++ { |
|
a[i] = 0x00 |
|
} |
|
} |
|
|
|
// schnorrSign generates an BIP-340 signature over the secp256k1 curve for the |
|
// provided hash (which should be the result of hashing a larger message) using |
|
// the given nonce and secret key. The produced signature is deterministic (the |
|
// same message, nonce, and key yield the same signature) and canonical. |
|
// |
|
// WARNING: The hash MUST be 32 bytes, and both the nonce and secret keys must |
|
// NOT be 0. Since this is an internal use function, these preconditions MUST be |
|
// satisified by the caller. |
|
func schnorrSign( |
|
privKey, nonce *btcec.ModNScalar, pubKey *btcec.PublicKey, |
|
hash []byte, opts *signOptions, |
|
) (*Signature, error) { |
|
|
|
// The algorithm for producing a BIP-340 signature is described in |
|
// README.md and is reproduced here for reference: |
|
// |
|
// G = curve generator |
|
// n = curve order |
|
// d = secret key |
|
// m = message |
|
// a = input randmoness |
|
// r, s = signature |
|
// |
|
// 1. d' = int(d) |
|
// 2. Fail if m is not 32 bytes |
|
// 3. Fail if d = 0 or d >= n |
|
// 4. P = d'*G |
|
// 5. Negate d if P.y is odd |
|
// 6. t = bytes(d) xor tagged_hash("BIP0340/aux", t || bytes(P) || m) |
|
// 7. rand = tagged_hash("BIP0340/nonce", a) |
|
// 8. k' = int(rand) mod n |
|
// 9. Fail if k' = 0 |
|
// 10. R = 'k*G |
|
// 11. Negate k if R.y id odd |
|
// 12. e = tagged_hash("BIP0340/challenge", bytes(R) || bytes(P) || m) mod n |
|
// 13. sig = bytes(R) || bytes((k + e*d)) mod n |
|
// 14. If Verify(bytes(P), m, sig) fails, abort. |
|
// 15. return sig. |
|
// |
|
// Note that the set of functional options passed in may modify the |
|
// above algorithm. Namely if CustomNonce is used, then steps 6-8 are |
|
// replaced with a process that generates the nonce using rfc6979. If |
|
// FastSign is passed, then we skip set 14. |
|
|
|
// NOTE: Steps 1-9 are performed by the caller. |
|
|
|
// |
|
// Step 10. |
|
// |
|
// R = kG |
|
var R btcec.JacobianPoint |
|
k := *nonce |
|
btcec.ScalarBaseMultNonConst(&k, &R) |
|
// Step 11. |
|
// |
|
// Negate nonce k if R.y is odd (R.y is the y coordinate of the point R) |
|
// |
|
// Note that R must be in affine coordinates for this check. |
|
R.ToAffine() |
|
if R.Y.IsOdd() { |
|
k.Negate() |
|
} |
|
// Step 12. |
|
// |
|
// e = tagged_hash("BIP0340/challenge", bytes(R) || bytes(P) || m) mod n |
|
var rBytes [32]byte |
|
r := &R.X |
|
r.PutBytesUnchecked(rBytes[:]) |
|
pBytes := SerializePubKey(pubKey) |
|
commitment := chainhash.TaggedHash( |
|
chainhash.TagBIP0340Challenge, rBytes[:], pBytes, hash, |
|
) |
|
var e btcec.ModNScalar |
|
if overflow := e.SetBytes((*[32]byte)(commitment)); overflow != 0 { |
|
k.Zero() |
|
str := "hash of (r || P || m) too big" |
|
return nil, signatureError(ErrSchnorrHashValue, str) |
|
} |
|
// Step 13. |
|
// |
|
// s = k + e*d mod n |
|
s := new(btcec.ModNScalar).Mul2(&e, privKey).Add(&k) |
|
k.Zero() |
|
sig := NewSignature(r, s) |
|
// Step 14. |
|
// |
|
// If Verify(bytes(P), m, sig) fails, abort. |
|
if !opts.fastSign { |
|
if err := schnorrVerify(sig, hash, pBytes); chk.T(err) { |
|
return nil, err |
|
} |
|
} |
|
// Step 15. |
|
// |
|
// Return (r, s) |
|
return sig, nil |
|
} |
|
|
|
// SignOption is a functional option argument that allows callers to modify the |
|
// way we generate BIP-340 schnorr signatures. |
|
type SignOption func(*signOptions) |
|
|
|
// signOptions houses the set of functional options that can be used to modify |
|
// the method used to generate the BIP-340 signature. |
|
type signOptions struct { |
|
// fastSign determines if we'll skip the check at the end of the routine |
|
// where we attempt to verify the produced signature. |
|
fastSign bool |
|
// authNonce allows the user to pass in their own nonce information, which |
|
// is useful for schemes like mu-sig. |
|
authNonce *[32]byte |
|
} |
|
|
|
// defaultSignOptions returns the default set of signing operations. |
|
func defaultSignOptions() *signOptions { return &signOptions{} } |
|
|
|
// FastSign forces signing to skip the extra verification step at the end. |
|
// Peformance sensitive applications may opt to use this option to speed up the |
|
// signing operation. |
|
func FastSign() SignOption { |
|
return func(o *signOptions) { o.fastSign = true } |
|
} |
|
|
|
// CustomNonce allows users to pass in a custom set of auxData that's used as |
|
// input randomness to generate the nonce used during signing. Users may want |
|
// to specify this custom value when using multi-signatures schemes such as |
|
// Mu-Sig2. If this option isn't set, then rfc6979 will be used to generate the |
|
// nonce material. |
|
func CustomNonce(auxData [32]byte) SignOption { |
|
return func(o *signOptions) { o.authNonce = &auxData } |
|
} |
|
|
|
// Sign generates an BIP-340 signature over the secp256k1 curve for the provided |
|
// hash (which should be the result of hashing a larger message) using the given |
|
// secret key. The produced signature is deterministic (the same message and the |
|
// same key yield the same signature) and canonical. |
|
// |
|
// Note that the current signing implementation has a few remaining variable |
|
// time aspects which make use of the secret key and the generated nonce, which |
|
// can expose the signer to constant time attacks. As a result, this function |
|
// should not be used in situations where there is the possibility of someone |
|
// having EM field/cache/etc access. |
|
func Sign( |
|
privKey *btcec.SecretKey, hash []byte, |
|
signOpts ...SignOption, |
|
) (*Signature, error) { |
|
// First, parse the set of optional signing options. |
|
opts := defaultSignOptions() |
|
for _, option := range signOpts { |
|
option(opts) |
|
} |
|
// The algorithm for producing a BIP-340 signature is described in README.md |
|
// and is reproduced here for reference: |
|
// |
|
// G = curve generator |
|
// n = curve order |
|
// d = secret key |
|
// m = message |
|
// a = input randmoness |
|
// r, s = signature |
|
// |
|
// 1. d' = int(d) |
|
// 2. Fail if m is not 32 bytes |
|
// 3. Fail if d = 0 or d >= n |
|
// 4. P = d'*G |
|
// 5. Negate d if P.y is odd |
|
// 6. t = bytes(d) xor tagged_hash("BIP0340/aux", t || bytes(P) || m) |
|
// 7. rand = tagged_hash("BIP0340/nonce", a) |
|
// 8. k' = int(rand) mod n |
|
// 9. Fail if k' = 0 |
|
// 10. R = 'k*G |
|
// 11. Negate k if R.y id odd |
|
// 12. e = tagged_hash("BIP0340/challenge", bytes(R) || bytes(P) || mod) mod n |
|
// 13. sig = bytes(R) || bytes((k + e*d)) mod n |
|
// 14. If Verify(bytes(P), m, sig) fails, abort. |
|
// 15. return sig. |
|
// |
|
// Note that the set of functional options passed in may modify the above |
|
// algorithm. Namely if CustomNonce is used, then steps 6-8 are replaced |
|
// with a process that generates the nonce using rfc6979. If FastSign is |
|
// passed, then we skip set 14. |
|
// |
|
// Step 1. |
|
// |
|
// d' = int(d) |
|
var privKeyScalar btcec.ModNScalar |
|
privKeyScalar.Set(&privKey.Key) |
|
|
|
// Step 2. |
|
// |
|
// Fail if m is not 32 bytes |
|
// if len(hash) != scalarSize { |
|
// str := fmt.Sprintf("wrong size for message hash (got %v, want %v)", |
|
// len(hash), scalarSize) |
|
// return nil, signatureError(schnorr.ErrInvalidHashLen, str) |
|
// } |
|
// |
|
// Step 3. |
|
// |
|
// Fail if d = 0 or d >= n |
|
if privKeyScalar.IsZero() { |
|
str := "secret key is zero" |
|
return nil, signatureError(ErrSecretKeyIsZero, str) |
|
} |
|
// Step 4. |
|
// |
|
// P = 'd*G |
|
pub := privKey.PubKey() |
|
// Step 5. |
|
// |
|
// Negate d if P.y is odd. |
|
pubKeyBytes := pub.SerializeCompressed() |
|
if pubKeyBytes[0] == secp256k1.PubKeyFormatCompressedOdd { |
|
privKeyScalar.Negate() |
|
} |
|
// At this point, we check to see if a CustomNonce has been passed in, and |
|
// if so, then we'll deviate from the main routine here by generating the |
|
// nonce value as specified by BIP-0340. |
|
if opts.authNonce != nil { |
|
// Step 6. |
|
// |
|
// t = bytes(d) xor tagged_hash("BIP0340/aux", a) |
|
privBytes := privKeyScalar.Bytes() |
|
t := chainhash.TaggedHash( |
|
chainhash.TagBIP0340Aux, (*opts.authNonce)[:], |
|
) |
|
for i := 0; i < len(t); i++ { |
|
t[i] ^= privBytes[i] |
|
} |
|
// Step 7. |
|
// |
|
// rand = tagged_hash("BIP0340/nonce", t || bytes(P) || m) |
|
// |
|
// We snip off the first byte of the serialized pubkey, as we only need |
|
// the x coordinate and not the market byte. |
|
rand := chainhash.TaggedHash( |
|
chainhash.TagBIP0340Nonce, t[:], pubKeyBytes[1:], hash, |
|
) |
|
// Step 8. |
|
// |
|
// k'= int(rand) mod n |
|
var kPrime btcec.ModNScalar |
|
kPrime.SetBytes((*[32]byte)(rand)) |
|
// Step 9. |
|
// |
|
// Fail if k' = 0 |
|
if kPrime.IsZero() { |
|
str := fmt.Sprintf("generated nonce is zero") |
|
return nil, signatureError(ErrSchnorrHashValue, str) |
|
} |
|
sig, err := schnorrSign(&privKeyScalar, &kPrime, pub, hash, opts) |
|
kPrime.Zero() |
|
if err != nil { |
|
return nil, err |
|
} |
|
return sig, nil |
|
} |
|
var privKeyBytes [scalarSize]byte |
|
privKeyScalar.PutBytes(&privKeyBytes) |
|
defer zeroArray(&privKeyBytes) |
|
for iteration := uint32(0); ; iteration++ { |
|
var k *secp256k1.ModNScalar |
|
// Step 6-9. |
|
// |
|
// Use RFC6979 to generate a deterministic nonce k in [1, n-1] |
|
// parameterized by the secret key, message being signed, extra data |
|
// that identifies the scheme, and an iteration count |
|
k = btcec.NonceRFC6979( |
|
privKeyBytes[:], hash, rfc6979ExtraDataV0[:], nil, iteration, |
|
) |
|
// Steps 10-15. |
|
sig, err := schnorrSign(&privKeyScalar, k, pub, hash, opts) |
|
k.Zero() |
|
if err != nil { |
|
// Try again with a new nonce. |
|
continue |
|
} |
|
return sig, nil |
|
} |
|
}
|
|
|