func ElGamalVerify(suite abstract.Suite, message []byte, publicKey abstract.Point, signatureBuffer []byte, g abstract.Point) error { // Decode the signature buf := bytes.NewBuffer(signatureBuffer) sig := basicSig{} if err := abstract.Read(buf, &sig, suite); err != nil { return err } r := sig.R c := sig.C // Compute base**(r + x*c) == T var P, T abstract.Point P = suite.Point() T = suite.Point() T.Add(T.Mul(g, r), P.Mul(publicKey, c)) // Verify that the hash based on the message and T // matches the challange c from the signature c = hashElGamal(suite, message, T) if !c.Equal(sig.C) { return errors.New("invalid signature") } return nil }
// Verify checks a signature generated by Sign. // // The caller provides the message, anonymity set, and linkage scope // with which the signature was purportedly produced. // If the signature is a valid linkable signature (linkScope != nil), // this function returns a linkage tag that uniquely corresponds // to the signer within the given linkScope. // If the signature is a valid unlinkable signature (linkScope == nil), // returns an empty but non-nil byte-slice instead of a linkage tag on success. // Returns a nil linkage tag and an error if the signature is invalid. func Verify(suite abstract.Suite, message []byte, anonymitySet Set, linkScope []byte, signatureBuffer []byte) ([]byte, error) { n := len(anonymitySet) // anonymity set size L := []abstract.Point(anonymitySet) // public keys in ring // Decode the signature buf := bytes.NewBuffer(signatureBuffer) var linkBase, linkTag abstract.Point sig := lSig{} sig.S = make([]abstract.Scalar, n) if linkScope != nil { // linkable ring signature if err := suite.Read(buf, &sig); err != nil { return nil, err } linkStream := suite.Cipher(linkScope) linkBase, _ = suite.Point().Pick(nil, linkStream) linkTag = sig.Tag } else { // unlinkable ring signature if err := suite.Read(buf, &sig.C0); err != nil { return nil, err } if err := suite.Read(buf, &sig.S); err != nil { return nil, err } } // Pre-hash the ring-position-invariant parameters to H1. H1pre := signH1pre(suite, linkScope, linkTag, message) // Verify the signature var P, PG, PH abstract.Point P = suite.Point() PG = suite.Point() if linkScope != nil { PH = suite.Point() } s := sig.S ci := sig.C0 for i := 0; i < n; i++ { PG.Add(PG.Mul(nil, s[i]), P.Mul(L[i], ci)) if linkScope != nil { PH.Add(PH.Mul(linkBase, s[i]), P.Mul(linkTag, ci)) } ci = signH1(suite, H1pre, PG, PH) } if !ci.Equal(sig.C0) { return nil, errors.New("invalid signature") } // Return the re-encoded linkage tag, for uniqueness checking if linkScope != nil { tag, _ := linkTag.MarshalBinary() return tag, nil } else { return []byte{}, nil } }
func ElGamalVerify(suite abstract.Suite, message []byte, publicKey abstract.Point, sig BasicSig) error { r := sig.R c := sig.C // Compute base**(r + x*c) == T var P, T abstract.Point P = suite.Point() T = suite.Point() T.Add(T.Mul(nil, r), P.Mul(publicKey, c)) // Verify that the hash based on the message and T // matches the challange c from the signature c = hashElGamal(suite, message, T) if !c.Equal(sig.C) { return errors.New("invalid signature") } return nil }
func SchnorrVerify(suite abstract.Suite, message []byte, signature net.BasicSignature) error { publicKey := signature.Pub r := signature.Resp c := signature.Chall // Compute base**(r + x*c) == T var P, T abstract.Point P = suite.Point() T = suite.Point() T.Add(T.Mul(nil, r), P.Mul(publicKey, c)) // Verify that the hash based on the message and T // matches the challange c from the signature c = hashSchnorr(suite, message, T) if !c.Equal(signature.Chall) { return errors.New("invalid signature") } return nil }
// 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... } }
// Sign creates an optionally anonymous, optionally linkable // signature on a given message. // // The caller supplies one or more public keys representing an anonymity set, // and the private key corresponding to one of those public keys. // The resulting signature proves to a verifier that the owner of // one of these public keys signed the message, // without revealing which key-holder signed the message, // offering anonymity among the members of this explicit anonymity set. // The other users whose keys are listed in the anonymity set need not consent // or even be aware that they have been included in an anonymity set: // anyone having a suitable public key may be "conscripted" into a set. // // If the provided anonymity set contains only one public key (the signer's), // then this function produces a traditional non-anonymous signature, // equivalent in both size and performance to a standard ElGamal signature. // // The caller may request either unlinkable or linkable anonymous signatures. // If linkScope is nil, this function generates an unlinkable signature, // which contains no information about which member signed the message. // The anonymity provided by unlinkable signatures is forward-secure, // in that a signature reveals nothing about which member generated it, // even if all members' private keys are later released. // For cryptographic background on unlinkable anonymity-set signatures - // also known as ring signatures or ad-hoc group signatures - // see Rivest, "How to Leak a Secret" at // http://people.csail.mit.edu/rivest/RivestShamirTauman-HowToLeakASecret.pdf. // // If the caller passes a non-nil linkScope, // the resulting anonymous signature will be linkable. // This means that given two signatures produced using the same linkScope, // a verifier will be able to tell whether // the same or different anonymity set members produced those signatures. // In particular, verifying a linkable signature yields a linkage tag. // This linkage tag has a 1-to-1 correspondence with the signer's public key // within a given linkScope, but is cryptographically unlinkable // to either the signer's public key or to linkage tags in other scopes. // The provided linkScope may be an arbitrary byte-string; // the only significance these scopes have is whether they are equal or unequal. // For details on the linkable signature algorithm this function implements, // see Liu/Wei/Wong, // "Linkable Spontaneous Anonymous Group Signature for Ad Hoc Groups" at // http://www.cs.cityu.edu.hk/~duncan/papers/04liuetal_lsag.pdf. // // Linkage tags may be used to protect against sock-puppetry or Sybil attacks // in situations where a verifier needs to know how many distinct members // of an anonymity set are present or signed messages in a given context. // It is cryptographically hard for one anonymity set member // to produce signatures with different linkage tags in the same scope. // An important and fundamental downside, however, is that // linkable signatures do NOT offer forward-secure anonymity. // If an anonymity set member's private key is later released, // it is trivial to check whether or not that member produced a given signature. // Also, anonymity set members who did NOT sign a message could // (voluntarily or under coercion) prove that they did not sign it, // e.g., simply by signing some other message in that linkage context // and noting that the resulting linkage tag comes out different. // Thus, linkable anonymous signatures are not appropriate to use // in situations where there may be significant risk // that members' private keys may later be compromised, // or that members may be persuaded or coerced into revealing whether or not // they produced a signature of interest. // func Sign(suite abstract.Suite, random cipher.Stream, message []byte, anonymitySet Set, linkScope []byte, mine int, privateKey abstract.Secret) []byte { // Note that Rivest's original ring construction directly supports // heterogeneous rings containing public keys of different types - // e.g., a mixture of RSA keys and DSA keys with varying parameters. // Our ring signature construction currently supports // only homogeneous rings containing compatible keys // drawn from the cipher suite (e.g., the same elliptic curve). // The upside to this constrint is greater flexibility: // e.g., we also easily obtain linkable ring signatures, // which are not readily feasible with the original ring construction. n := len(anonymitySet) // anonymity set size L := []abstract.Point(anonymitySet) // public keys in anonymity set pi := mine // If we want a linkable ring signature, produce correct linkage tag, // as a pseudorandom base point multiplied by our private key. // Liu's scheme specifies the linkScope as a hash of the ring; // this is one reasonable choice of linkage scope, // but there are others, so we parameterize this choice. var linkBase, linkTag abstract.Point if linkScope != nil { linkStream := suite.Cipher(linkScope) linkBase, _ = suite.Point().Pick(nil, linkStream) linkTag = suite.Point().Mul(linkBase, privateKey) } // First pre-hash the parameters to H1 // that are invariant for different ring positions, // so that we don't have to hash them many times. H1pre := signH1pre(suite, linkScope, linkTag, message) // Pick a random commit for my ring position u := suite.Secret().Pick(random) var UB, UL abstract.Point UB = suite.Point().Mul(nil, u) if linkScope != nil { UL = suite.Point().Mul(linkBase, u) } // Build the challenge ring s := make([]abstract.Secret, n) c := make([]abstract.Secret, n) c[(pi+1)%n] = signH1(suite, H1pre, UB, UL) var P, PG, PH abstract.Point P = suite.Point() PG = suite.Point() if linkScope != nil { PH = suite.Point() } for i := (pi + 1) % n; i != pi; i = (i + 1) % n { s[i] = suite.Secret().Pick(random) PG.Add(PG.Mul(nil, s[i]), P.Mul(L[i], c[i])) if linkScope != nil { PH.Add(PH.Mul(linkBase, s[i]), P.Mul(linkTag, c[i])) } c[(i+1)%n] = signH1(suite, H1pre, PG, PH) //fmt.Printf("s%d %s\n",i,s[i].String()) //fmt.Printf("c%d %s\n",(i+1)%n,c[(i+1)%n].String()) } s[pi] = suite.Secret() s[pi].Mul(privateKey, c[pi]).Sub(u, s[pi]) // s_pi = u - x_pi c_pi // Encode and return the signature buf := bytes.Buffer{} if linkScope != nil { // linkable ring signature sig := lSig{uSig{c[0], s}, linkTag} suite.Write(&buf, &sig) } else { // unlinkable ring signature sig := uSig{c[0], s} suite.Write(&buf, &sig) } return buf.Bytes() }
// 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) }