Пример #1
0
// Use our actual hashing algorithm here.
func TestSignaturesAndRecovery(t *testing.T) {
	curve := secp256k1.S256()
	r := rand.New(rand.NewSource(54321))

	numSigs := 128
	sigList := randSigList(curve, numSigs)

	for _, tv := range sigList {
		pubkey := tv.pubkey
		sig := tv.sig

		// Make sure we can verify the original signature.
		_, err := schnorrVerify(curve, sig.Serialize(), pubkey, tv.msg,
			chainhash.HashFuncB)
		assert.NoError(t, err)

		ok := Verify(curve, pubkey, tv.msg, sig.R, sig.S)
		assert.Equal(t, true, ok)

		// See if we can recover the public keys OK.
		var pkRecover *secp256k1.PublicKey
		pkRecover, _, err = schnorrRecover(curve, sig.Serialize(), tv.msg,
			chainhash.HashFuncB)
		assert.NoError(t, err)
		if err == nil {
			assert.Equal(t, pubkey.Serialize(), pkRecover.Serialize())
		}

		// Screw up the signature at some random bits and make sure
		// that breaks it.
		numBadBits := r.Intn(2)
		sigBad := sig.Serialize()
		// (numBadBits*2)+1 --> always odd so at least one bit is different
		for i := 0; i < (numBadBits*2)+1; i++ {
			pos := r.Intn(63)
			bitPos := r.Intn(7)
			sigBad[pos] ^= 1 << uint8(bitPos)
		}
		_, err = schnorrVerify(curve, sigBad, pubkey, tv.msg,
			chainhash.HashFuncB)
		assert.Error(t, err)

		// Make sure it breaks pubkey recovery too.
		valid := false
		pkRecover, valid, err = schnorrRecover(curve, sigBad, tv.msg,
			testSchnorrHash)
		if valid {
			assert.NotEqual(t, pubkey.Serialize(), pkRecover.Serialize())
		} else {
			assert.Error(t, err)
		}
	}
}
Пример #2
0
func TestSchnorrSigning(t *testing.T) {
	tRand := rand.New(rand.NewSource(54321))
	curve := secp256k1.S256()
	tvs := GetSigningTestVectors()
	for _, tv := range tvs {
		_, pubkey := secp256k1.PrivKeyFromBytes(curve, tv.priv)

		sig, err :=
			schnorrSign(curve, tv.msg, tv.priv, tv.nonce, nil, nil,
				testSchnorrHash)

		assert.NoError(t, err)
		assert.Equal(t, sig.Serialize(), tv.sig)

		// Make sure they verify too while we're at it.
		_, err = schnorrVerify(curve, sig.Serialize(), pubkey, tv.msg,
			testSchnorrHash)
		assert.NoError(t, err)

		// See if we can recover the public keys OK.
		var pkRecover *secp256k1.PublicKey
		pkRecover, _, err = schnorrRecover(curve, sig.Serialize(), tv.msg,
			testSchnorrHash)
		assert.NoError(t, err)
		if err == nil {
			assert.Equal(t, pubkey.Serialize(), pkRecover.Serialize())
		}

		// Screw up the signature at a random bit and make sure that breaks it.
		sigBad := sig.Serialize()
		pos := tRand.Intn(63)
		bitPos := tRand.Intn(7)
		sigBad[pos] ^= 1 << uint8(bitPos)
		_, err = schnorrVerify(curve, sigBad, pubkey, tv.msg,
			testSchnorrHash)
		assert.Error(t, err)

		// Make sure it breaks pubkey recovery too.
		valid := false
		pkRecover, valid, err = schnorrRecover(curve, sigBad, tv.msg,
			testSchnorrHash)
		if valid {
			assert.NotEqual(t, pubkey.Serialize(), pkRecover.Serialize())
		} else {
			assert.Error(t, err)
		}
	}
}
Пример #3
0
// 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)
}
Пример #4
0
// 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
}