// schnorrPartialSign creates a partial Schnorr signature which may be combined // with other Schnorr signatures to create a valid signature for a group pubkey. func schnorrPartialSign(curve *secp256k1.KoblitzCurve, msg []byte, priv []byte, privNonce []byte, pubSum *secp256k1.PublicKey, hashFunc func([]byte) []byte) (*Signature, error) { // Sanity checks. if len(msg) != scalarSize { str := fmt.Sprintf("wrong size for message (got %v, want %v)", len(msg), scalarSize) return nil, schnorrError(ErrBadInputSize, str) } if len(priv) != scalarSize { str := fmt.Sprintf("wrong size for privkey (got %v, want %v)", len(priv), scalarSize) return nil, schnorrError(ErrBadInputSize, str) } if len(privNonce) != scalarSize { str := fmt.Sprintf("wrong size for privnonce (got %v, want %v)", len(privNonce), scalarSize) return nil, schnorrError(ErrBadInputSize, str) } if pubSum == nil { str := fmt.Sprintf("nil pubkey") return nil, schnorrError(ErrInputValue, str) } privBig := new(big.Int).SetBytes(priv) if privBig.Cmp(bigZero) == 0 { str := fmt.Sprintf("priv scalar is zero") return nil, schnorrError(ErrInputValue, str) } if privBig.Cmp(curve.N) >= 0 { str := fmt.Sprintf("priv scalar is out of bounds") return nil, schnorrError(ErrInputValue, str) } privBig.SetInt64(0) privNonceBig := new(big.Int).SetBytes(privNonce) if privNonceBig.Cmp(bigZero) == 0 { str := fmt.Sprintf("privNonce scalar is zero") return nil, schnorrError(ErrInputValue, str) } if privNonceBig.Cmp(curve.N) >= 0 { str := fmt.Sprintf("privNonce scalar is out of bounds") return nil, schnorrError(ErrInputValue, str) } privNonceBig.SetInt64(0) if !curve.IsOnCurve(pubSum.GetX(), pubSum.GetY()) { str := fmt.Sprintf("public key sum is off curve") return nil, schnorrError(ErrInputValue, str) } return schnorrSign(curve, msg, priv, privNonce, pubSum.GetX(), pubSum.GetY(), hashFunc) }
// schnorrVerify is the internal function for verification of a secp256k1 // Schnorr signature. A secure hash function may be passed for the calculation // of r. // This is identical to the Schnorr verification function found in libsecp256k1: // https://github.com/bitcoin/secp256k1/tree/master/src/modules/schnorr func schnorrVerify(curve *secp256k1.KoblitzCurve, sig []byte, pubkey *secp256k1.PublicKey, msg []byte, hashFunc func([]byte) []byte) (bool, error) { if len(msg) != scalarSize { str := fmt.Sprintf("wrong size for message (got %v, want %v)", len(msg), scalarSize) return false, schnorrError(ErrBadInputSize, str) } if len(sig) != SignatureSize { str := fmt.Sprintf("wrong size for signature (got %v, want %v)", len(sig), SignatureSize) return false, schnorrError(ErrBadInputSize, str) } if pubkey == nil { str := fmt.Sprintf("nil pubkey") return false, schnorrError(ErrInputValue, str) } if !curve.IsOnCurve(pubkey.GetX(), pubkey.GetY()) { str := fmt.Sprintf("pubkey point is not on curve") return false, schnorrError(ErrPointNotOnCurve, str) } sigR := sig[:32] sigS := sig[32:] sigRCopy := make([]byte, scalarSize, scalarSize) copy(sigRCopy, sigR) toHash := append(sigRCopy, msg...) h := hashFunc(toHash) hBig := new(big.Int).SetBytes(h) // If the hash ends up larger than the order of the curve, abort. // Same thing for hash == 0 (as unlikely as that is...). if hBig.Cmp(curve.N) >= 0 { str := fmt.Sprintf("hash of (R || m) too big") return false, schnorrError(ErrSchnorrHashValue, str) } if hBig.Cmp(bigZero) == 0 { str := fmt.Sprintf("hash of (R || m) is zero value") return false, schnorrError(ErrSchnorrHashValue, str) } // Convert s to big int. sBig := EncodedBytesToBigInt(copyBytes(sigS)) // We also can't have s greater than the order of the curve. if sBig.Cmp(curve.N) >= 0 { str := fmt.Sprintf("s value is too big") return false, schnorrError(ErrInputValue, str) } // r can't be larger than the curve prime. rBig := EncodedBytesToBigInt(copyBytes(sigR)) if rBig.Cmp(curve.P) == 1 { str := fmt.Sprintf("given R was greater than curve prime") return false, schnorrError(ErrBadSigRNotOnCurve, str) } // r' = hQ + sG lx, ly := curve.ScalarMult(pubkey.GetX(), pubkey.GetY(), h) rx, ry := curve.ScalarBaseMult(sigS) rlx, rly := curve.Add(lx, ly, rx, ry) if rly.Bit(0) == 1 { str := fmt.Sprintf("calculated R y-value was odd") return false, schnorrError(ErrBadSigRYValue, str) } if !curve.IsOnCurve(rlx, rly) { str := fmt.Sprintf("calculated R point was not on curve") return false, schnorrError(ErrBadSigRNotOnCurve, str) } rlxB := BigIntToEncodedBytes(rlx) // r == r' --> valid signature if !bytes.Equal(sigR, rlxB[:]) { str := fmt.Sprintf("calculated R point was not given R") return false, schnorrError(ErrUnequalRValues, str) } return true, nil }