diff --git a/bbs/bbs.go b/bbs/bbs.go index 298cf1021..82d57c4cb 100644 --- a/bbs/bbs.go +++ b/bbs/bbs.go @@ -46,6 +46,45 @@ func (s Signature) Encode() []byte { return append(AEnc, eEnc...) } +// XXX(caw): rename this function +func octetsToSignature(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 + if len(data) != expectedLen { + return Signature{}, fmt.Errorf("bbs: malformed signature") + } + + // 3. A_octets = signature_octets[0..(octet_point_length - 1)] + // 4. A = octets_to_point_g1(A_octets) + AOctets := data[0:octetPointLen] + A := &pairing.G1{} + A.SetBytes(AOctets) + + // 5. if A is INVALID, return INVALID + if !A.IsOnG1() { + return Signature{}, fmt.Errorf("bbs: invalid A signature component (not in G1)") + } + // 6. if A == Identity_G1, return INVALID + if A.IsIdentity() { + return Signature{}, fmt.Errorf("bbs: invalid A signature component (identity element)") + } + + // 7. index = octet_point_length + // 8. end_index = index + octet_scalar_length - 1 + // 9. e = OS2IP(signature_octets[index..end_index]) + e := &pairing.Scalar{} + e.SetBytes(data[octetPointLen:]) + + // 10. if e = 0 OR e >= r, return INVALID + // 11. return (A, e) + + return Signature{ + A: A, + e: e, + }, nil +} + // (Abar, Bbar, r2^, r3^, (m^_j1, ..., m^_jU), c) type Proof struct { Abar *pairing.G1 @@ -92,10 +131,6 @@ func publicKey(sk *pairing.Scalar) []byte { return W.BytesCompressed() } -// Serialize -// https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-bbs-signatures-03#name-serialize -// XXX(caw): this function feels too complicated and should instead be replaced separate serializeFunctions invoked directly - // Domain calculation // https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-bbs-signatures-03#name-domain-calculation // XXX(caw): this function needs test vectors for the draft @@ -305,33 +340,9 @@ func sign(sk *pairing.Scalar, pk []byte, header []byte, messages [][]byte) ([]by return sig.Encode(), nil } -func octetsToSignature(data []byte) (Signature, error) { - // 1. expected_len = octet_point_length + octet_scalar_length - // 2. if length(signature_octets) != expected_len, return INVALID - // 3. A_octets = signature_octets[0..(octet_point_length - 1)] - // 4. A = octets_to_point_g1(A_octets) - // 5. if A is INVALID, return INVALID - // 6. if A == Identity_G1, return INVALID - // 7. index = octet_point_length - // 8. end_index = index + octet_scalar_length - 1 - // 9. e = OS2IP(signature_octets[index..end_index]) - // 10. if e = 0 OR e >= r, return INVALID - // 11. return (A, e) - - // XXX(caw): writeme - - return Signature{}, nil -} - // Signature verification // https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-bbs-signatures-03#name-signature-verification-veri func rawVerify(pk []byte, signature Signature, header []byte, messages [][]byte) error { - // 1. (Q_1, H_1, ..., H_L) = create_generators(L+1, PK) - // 2. domain = calculate_domain(PK, Q_1, (H_1, ..., H_L), header) - // 3. B = P1 + Q_1 * domain + H_1 * msg_1 + ... + H_L * msg_L - // 4. if e(A, W + BP2 * e) * e(B, -BP2) != Identity_GT, return INVALID - // 5. return VALID - // 1. L = length(messages) L := len(messages) diff --git a/bbs/bbs_test.go b/bbs/bbs_test.go index 301445c27..878f7d4e9 100644 --- a/bbs/bbs_test.go +++ b/bbs/bbs_test.go @@ -168,11 +168,30 @@ func TestSingleMessageSignature(t *testing.T) { t.Fatal(err) } - // sigEEnc, err := sig.e.MarshalBinary() - // if err != nil { - // t.Fatal(err) - // } - // fmt.Println(hex.EncodeToString(sigEEnc)) + sigEnc := sig.Encode() + if !bytes.Equal(sigEnc, expectedSigEnc) { + t.Fatalf("incorrect signature, got %s, wanted %s", hex.EncodeToString(sigEnc), hex.EncodeToString(expectedSigEnc)) + } +} + +// https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-bbs-signatures-03#name-valid-multi-message-signatur +func TestMultiMessageSignature(t *testing.T) { + header := mustDecodeHex("11223344556677889900aabbccddeeff") + messages := testMessages() + expectedSigEnc := mustDecodeHex("895cd9c0ccb9aca4de913218655346d718711472f2bf1f3e68916de106a0d93cf2f47200819b45920bbda541db2d91480665df253fedab2843055bdc02535d83baddbbb2803ec3808e074f71f199751e") + + ikm := mustDecodeHex("746869732d49532d6a7573742d616e2d546573742d494b4d2d746f2d67656e65726174652d246528724074232d6b6579") + keyInfo := mustDecodeHex("746869732d49532d736f6d652d6b65792d6d657461646174612d746f2d62652d757365642d696e2d746573742d6b65792d67656e") + + sk, err := keyGen(ikm, keyInfo, nil) + if err != nil { + t.Fatal(err) + } + + sig, err := rawSign(sk, publicKey(sk), header, messages) + if err != nil { + t.Fatal(err) + } sigEnc := sig.Encode() if !bytes.Equal(sigEnc, expectedSigEnc) {