// Verifies that the 'message' is included in the signature and that it // is correct. // Message is your own hash, and reply contains the inclusion proof + signature // on the aggregated message func VerifySignature(suite abstract.Suite, reply *StampSignature, public abstract.Point, message []byte) bool { // Check if aggregate public key is correct if !public.Equal(reply.AggPublic) { dbg.Lvl1("Aggregate-public-key check: FAILED (maybe you have an outdated config file of the tree)") return false } // First check if the challenge is ok if err := VerifyChallenge(suite, reply); err != nil { dbg.Lvl1("Challenge-check: FAILED (", err, ")") return false } dbg.Lvl2("Challenge-check: OK") // Incorporate the timestamp in the message since the verification process // is done by reconstructing the challenge var b bytes.Buffer if err := binary.Write(&b, binary.LittleEndian, reply.Timestamp); err != nil { dbg.Lvl1("Error marshaling the timestamp for signature verification") return false } msg := append(b.Bytes(), []byte(reply.MerkleRoot)...) if err := VerifySchnorr(suite, msg, public, reply.Challenge, reply.Response); err != nil { dbg.Lvl1("Signature-check: FAILED (", err, ")") return false } dbg.Lvl2("Signature-check: OK") // finally check the proof if !proof.CheckProof(suite.Hash, reply.MerkleRoot, hashid.HashId(message), reply.Prf) { dbg.Lvl2("Inclusion-check: FAILED") return false } dbg.Lvl2("Inclusion-check: OK") return true }
// Pick a [pseudo-]random curve point with optional embedded data, // filling in the point's x,y coordinates // and returning any remaining data not embedded. func (c *curve) pickPoint(P point, data []byte, rand cipher.Stream) []byte { // How much data to embed? dl := c.pickLen() if dl > len(data) { dl = len(data) } // Retry until we find a valid point var x, y nist.Int var Q abstract.Point for { // Get random bits the size of a compressed Point encoding, // in which the topmost bit is reserved for the x-coord sign. l := c.PointLen() b := make([]byte, l) rand.XORKeyStream(b, b) // Interpret as little-endian if data != nil { b[0] = byte(dl) // Encode length in low 8 bits copy(b[1:1+dl], data) // Copy in data to embed } util.Reverse(b, b) // Convert to big-endian form xsign := b[0] >> 7 // save x-coordinate sign bit b[0] &^= 0xff << uint(c.P.BitLen()&7) // clear high bits y.M = &c.P // set y-coordinate y.SetBytes(b) if !c.solveForX(&x, &y) { // Corresponding x-coordinate? continue // none, retry } // Pick a random sign for the x-coordinate if c.coordSign(&x) != uint(xsign) { x.Neg(&x) } // Initialize the point P.initXY(&x.V, &y.V, c.self) if c.full { // If we're using the full group, // we just need any point on the curve, so we're done. return data[dl:] } // We're using the prime-order subgroup, // so we need to make sure the point is in that subgroup. // If we're not trying to embed data, // we can convert our point into one in the subgroup // simply by multiplying it by the cofactor. if data == nil { P.Mul(P, &c.cofact) // multiply by cofactor if P.Equal(c.null) { continue // unlucky; try again } return data[dl:] } // Since we need the point's y-coordinate to make sense, // we must simply check if the point is in the subgroup // and retry point generation until it is. if Q == nil { Q = c.self.Point() } Q.Mul(P, &c.order) if Q.Equal(c.null) { return data[dl:] } // Keep trying... } }
// Simple helper to verify Theta elements, // by checking whether A^a*B^-b = T. // P,Q,s are simply "scratch" abstract.Point/Scalars reused for efficiency. func thver(A, B, T, P, Q abstract.Point, a, b, s abstract.Scalar) bool { P.Mul(A, a) Q.Mul(B, s.Neg(b)) P.Add(P, Q) return P.Equal(T) }