From 5007a769fa44616133b0316d86b0c8435129c6be Mon Sep 17 00:00:00 2001 From: Christopher Wood Date: Fri, 13 Oct 2023 16:20:47 -0700 Subject: [PATCH] Start filling out high-level API --- bbs/bbs.go | 109 +++++++++++++++++++++++++++++++++--------------- bbs/bbs_test.go | 75 ++++++++++++++++++++++++++------- 2 files changed, 134 insertions(+), 50 deletions(-) diff --git a/bbs/bbs.go b/bbs/bbs.go index 82d57c4cb..44c849762 100644 --- a/bbs/bbs.go +++ b/bbs/bbs.go @@ -46,8 +46,7 @@ func (s Signature) Encode() []byte { return append(AEnc, eEnc...) } -// XXX(caw): rename this function -func octetsToSignature(data []byte) (Signature, error) { +func UnmarshalSignature(data []byte) (Signature, error) { // 1. expected_len = octet_point_length + octet_scalar_length expectedLen := octetPointLen + octetScalarLen // 2. if length(signature_octets) != expected_len, return INVALID @@ -85,7 +84,6 @@ func octetsToSignature(data []byte) (Signature, error) { }, nil } -// (Abar, Bbar, r2^, r3^, (m^_j1, ..., m^_jU), c) type Proof struct { Abar *pairing.G1 Bbar *pairing.G1 @@ -95,14 +93,66 @@ type Proof struct { c *pairing.Scalar } +func (p Proof) Encode() []byte { + ABarEnc := p.Abar.BytesCompressed() + BBarEnc := p.Bbar.BytesCompressed() + r2hEnc, err := p.r2h.MarshalBinary() + if err != nil { + panic(err) + } + r3hEnc, err := p.r3h.MarshalBinary() + if err != nil { + panic(err) + } + cEnc, err := p.c.MarshalBinary() + if err != nil { + panic(err) + } + + result := append(ABarEnc, BBarEnc...) + result = append(result, r2hEnc...) + result = append(result, r3hEnc...) + for i := 0; i < len(p.commitments); i++ { + commitmentEnc, err := p.commitments[i].MarshalBinary() + if err != nil { + panic(err) + } + result = append(result, commitmentEnc...) + } + result = append(result, cEnc...) + return result +} + +type SecretKey struct { + sk *pairing.Scalar +} + +type PublicKey []byte + +func (s SecretKey) Public() PublicKey { + W := pairing.G2Generator() + W.ScalarMult(s.sk, W) + + // https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-bbs-signatures-03#section-6.2.2-3 + return W.BytesCompressed() +} + +func (s SecretKey) Encode() []byte { + enc, err := s.sk.MarshalBinary() + if err != nil { + panic(err) + } + return enc +} + // Key generation // https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-bbs-signatures-03#name-key-generation-operations -func keyGen(ikm []byte, keyInfo, keyDst []byte) (*pairing.Scalar, error) { +func KeyGen(ikm []byte, keyInfo, keyDst []byte) (SecretKey, error) { if len(ikm) < 32 { - return nil, fmt.Errorf("bbs: invalid keyGen ikm") + return SecretKey{}, fmt.Errorf("bbs: invalid keyGen ikm") } if len(keyInfo) > 65535 { - return nil, fmt.Errorf("bbs: invalid keyGen keyInfo") + return SecretKey{}, fmt.Errorf("bbs: invalid keyGen keyInfo") } if keyDst == nil { // keyDst = ciphersuite_id || "KEYGEN_DST_" @@ -120,15 +170,9 @@ func keyGen(ikm []byte, keyInfo, keyDst []byte) (*pairing.Scalar, error) { // if SK is INVALID, return INVALID // XXX(caw): what does it mean for SK to be invalid if hash_to_scalar never returns an invalid scalar? - return sk, nil -} - -func publicKey(sk *pairing.Scalar) []byte { - W := pairing.G2Generator() - W.ScalarMult(sk, W) - - // https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-bbs-signatures-03#section-6.2.2-3 - return W.BytesCompressed() + return SecretKey{ + sk: sk, + }, nil } // Domain calculation @@ -168,7 +212,6 @@ func encodeInt(x int) []byte { } func concat(x, y []byte) []byte { - // fmt.Println(hex.EncodeToString(y)) return append(x, y...) } @@ -214,8 +257,8 @@ func calculateChallenge(Abar, Bbar, C *pairing.G1, indexArray []int, msgArray [] challengeInput = concat(challengeInput, encodeInt(len(ph))) challengeInput = concat(challengeInput, ph) - dst := []byte("TODO") - return hashToScalar(challengeInput, dst), nil + // XXX(caw): this should have an explicit DST + return hashToScalar(challengeInput, nil), nil } // Generators calculation @@ -230,6 +273,9 @@ func hashToGenerators(count int, generatorSeed []byte) []*pairing.G1 { // ABORT if: // 1. count > 2^64 - 1 + if uint64(count) > ^uint64(0) { + panic("invalid invocation") + } // Procedure: @@ -267,7 +313,7 @@ func hashToCurveG1(seed []byte, dst []byte) *pairing.G1 { // Signature generation // https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-bbs-signatures-03#name-signature-generation-sign -func rawSign(sk *pairing.Scalar, pk []byte, header []byte, messages [][]byte) (Signature, error) { +func rawSign(sk SecretKey, pk []byte, header []byte, messages [][]byte) (Signature, error) { // Deserialization: // 1. L = length(messages) @@ -285,7 +331,7 @@ func rawSign(sk *pairing.Scalar, pk []byte, header []byte, messages [][]byte) (S domain := calculateDomain(pk, generators[0], generators[1:], header) // e_input = serialize((SK, domain, msg_1, ..., msg_L)) - skEnc, err := sk.MarshalBinary() + skEnc, err := sk.sk.MarshalBinary() if err != nil { return Signature{}, err } @@ -319,7 +365,7 @@ func rawSign(sk *pairing.Scalar, pk []byte, header []byte, messages [][]byte) (S // 5. A = B * (1 / (SK + e)) skE := &pairing.Scalar{} - skE.Add(sk, e) + skE.Add(sk.sk, e) skEInv := &pairing.Scalar{} skEInv.Inv(skE) A := &pairing.G1{} @@ -332,7 +378,7 @@ func rawSign(sk *pairing.Scalar, pk []byte, header []byte, messages [][]byte) (S }, nil } -func sign(sk *pairing.Scalar, pk []byte, header []byte, messages [][]byte) ([]byte, error) { +func Sign(sk SecretKey, pk []byte, header []byte, messages [][]byte) ([]byte, error) { sig, err := rawSign(sk, pk, header, messages) if err != nil { return nil, err @@ -392,19 +438,14 @@ func rawVerify(pk []byte, signature Signature, header []byte, messages [][]byte) } -func verify(pk, signature, header []byte, messages [][]byte) error { - // Deserialization: - +func Verify(pk PublicKey, signature, header []byte, messages [][]byte) error { // 1. signature_result = octets_to_signature(signature) - // 2. if signature_result is INVALID, return INVALID - // 3. (A, e) = signature_result - // 4. W = octets_to_pubkey(PK) - // 5. if W is INVALID, return INVALID - // 6. L = length(messages) - // 7. (msg_1, ..., msg_L) = messages_to_scalars(messages) - - // XXX(caw): invoke rawVerify with the deserialized values - return nil + sig, err := UnmarshalSignature(signature) + if err != nil { + return err + } + + return rawVerify(pk, sig, header, messages) } // Random scalars diff --git a/bbs/bbs_test.go b/bbs/bbs_test.go index 878f7d4e9..a9ce3c5eb 100644 --- a/bbs/bbs_test.go +++ b/bbs/bbs_test.go @@ -65,19 +65,16 @@ func TestKeyGen(t *testing.T) { expectedSkEnc := mustDecodeHex("60e55110f76883a13d030b2f6bd11883422d5abde717569fc0731f51237169fc") expectedPkEnc := mustDecodeHex("a820f230f6ae38503b86c70dc50b61c58a77e45c39ab25c0652bbaa8fa136f2851bd4781c9dcde39fc9d1d52c9e60268061e7d7632171d91aa8d460acee0e96f1e7c4cfb12d3ff9ab5d5dc91c277db75c845d649ef3c4f63aebc364cd55ded0c") - sk, err := keyGen(ikm, keyInfo, nil) - if err != nil { - t.Fatal(err) - } - skEnc, err := sk.MarshalBinary() + sk, err := KeyGen(ikm, keyInfo, nil) if err != nil { t.Fatal(err) } + skEnc := sk.Encode() if !bytes.Equal(skEnc, expectedSkEnc) { t.Fatalf("derived secret key mismatch, got %s, wanted %s", hex.EncodeToString(skEnc), hex.EncodeToString(expectedSkEnc)) } - pkEnc := publicKey(sk) + pkEnc := sk.Public() if !bytes.Equal(pkEnc, expectedPkEnc) { t.Fatalf("derived public key mismatch, got %s, wanted %s", hex.EncodeToString(pkEnc), hex.EncodeToString(expectedPkEnc)) } @@ -158,12 +155,12 @@ func TestSingleMessageSignature(t *testing.T) { ikm := mustDecodeHex("746869732d49532d6a7573742d616e2d546573742d494b4d2d746f2d67656e65726174652d246528724074232d6b6579") keyInfo := mustDecodeHex("746869732d49532d736f6d652d6b65792d6d657461646174612d746f2d62652d757365642d696e2d746573742d6b65792d67656e") - sk, err := keyGen(ikm, keyInfo, nil) + sk, err := KeyGen(ikm, keyInfo, nil) if err != nil { t.Fatal(err) } - sig, err := rawSign(sk, publicKey(sk), header, messages) + sig, err := rawSign(sk, sk.Public(), header, messages) if err != nil { t.Fatal(err) } @@ -183,12 +180,12 @@ func TestMultiMessageSignature(t *testing.T) { ikm := mustDecodeHex("746869732d49532d6a7573742d616e2d546573742d494b4d2d746f2d67656e65726174652d246528724074232d6b6579") keyInfo := mustDecodeHex("746869732d49532d736f6d652d6b65792d6d657461646174612d746f2d62652d757365642d696e2d746573742d6b65792d67656e") - sk, err := keyGen(ikm, keyInfo, nil) + sk, err := KeyGen(ikm, keyInfo, nil) if err != nil { t.Fatal(err) } - sig, err := rawSign(sk, publicKey(sk), header, messages) + sig, err := rawSign(sk, sk.Public(), header, messages) if err != nil { t.Fatal(err) } @@ -199,12 +196,12 @@ func TestMultiMessageSignature(t *testing.T) { } } -func TestRoundTrip(t *testing.T) { +func TestRawRoundTrip(t *testing.T) { ikm := make([]byte, 32) keyInfo := []byte{} keyDst := []byte{} - sk, err := keyGen(ikm, keyInfo, keyDst) + sk, err := KeyGen(ikm, keyInfo, keyDst) if err != nil { t.Fatal(err) } @@ -219,12 +216,12 @@ func TestRoundTrip(t *testing.T) { messages[1] = []byte("bar") messages[1] = []byte("baz") - sig, err := rawSign(sk, publicKey(sk), header, messages) + sig, err := rawSign(sk, sk.Public(), header, messages) if err != nil { t.Fatal(err) } - err = rawVerify(publicKey(sk), sig, header, messages) + err = rawVerify(sk.Public(), sig, header, messages) if err != nil { t.Fatal(err) } @@ -234,13 +231,59 @@ func TestRoundTrip(t *testing.T) { for i := 0; i < len(disclosedIndexes); i++ { disclosedMessages[i] = messages[disclosedIndexes[i]] } - proof, err := rawProofGen(publicKey(sk), sig, header, ph, messages, disclosedIndexes) + proof, err := rawProofGen(sk.Public(), sig, header, ph, messages, disclosedIndexes) if err != nil { t.Fatal(err) } - err = rawProofVerify(publicKey(sk), proof, header, ph, disclosedMessages, disclosedIndexes) + err = rawProofVerify(sk.Public(), proof, header, ph, disclosedMessages, disclosedIndexes) if err != nil { t.Fatal(err) } } + +func TestRoundTrip(t *testing.T) { + ikm := make([]byte, 32) + keyInfo := []byte{} + keyDst := []byte{} + + sk, err := KeyGen(ikm, keyInfo, keyDst) + if err != nil { + t.Fatal(err) + } + + header := []byte("test header") + + messages := make([][]byte, 5) + messages[0] = []byte("hello") + messages[1] = []byte("world") + messages[1] = []byte("foo") + messages[1] = []byte("bar") + messages[1] = []byte("baz") + + sig, err := Sign(sk, sk.Public(), header, messages) + if err != nil { + t.Fatal(err) + } + + err = Verify(sk.Public(), sig, header, messages) + if err != nil { + t.Fatal(err) + } + + // ph := []byte("presentation header") + // disclosedIndexes := []int{0, 1} + // disclosedMessages := make([][]byte, len(disclosedIndexes)) + // for i := 0; i < len(disclosedIndexes); i++ { + // disclosedMessages[i] = messages[disclosedIndexes[i]] + // } + // proof, err := rawProofGen(sk.Public(), sig, header, ph, messages, disclosedIndexes) + // if err != nil { + // t.Fatal(err) + // } + + // err = rawProofVerify(sk.Public(), proof, header, ph, disclosedMessages, disclosedIndexes) + // if err != nil { + // t.Fatal(err) + // } +}