package protocol_test import ( "errors" "testing" "git.n1ko.dev/Niko/niko_trust/pkg/protocol" "git.n1ko.dev/Niko/niko_trust/pkg/tce" ) // TestRejectVectors runs the frozen malformed-encoding vectors from // PROTOCOL.md section 12.3. Every one must be refused by the strict decoder; // a few also pin the exact reason. func TestRejectVectors(t *testing.T) { vf := loadVectors(t) exact := map[string]error{ "empty": tce.ErrTruncated, "magic_truncated": tce.ErrTruncated, "magic_wrong_version": tce.ErrMagic, "unknown_object_tag": tce.ErrObjectTag, "object_tag_zero": tce.ErrObjectTag, "non_minimal_uvarint": tce.ErrNonMinimal, "trailing_byte": tce.ErrTrailing, } checked := 0 for _, r := range vf.Rejects { if r.TCEHex == nil { // Constructed cases (unsorted/duplicate map keys, reserved value // tag) are exercised separately; they have no serialised bytes in // the file. continue } checked++ t.Run(r.Name, func(t *testing.T) { b := mustHex(t, *r.TCEHex) _, err := protocol.DecodeClaim(b) if err == nil { t.Fatalf("accepted a vector the reference implementation refuses") } if want, ok := exact[r.Name]; ok && !errors.Is(err, want) { t.Fatalf("err = %v, want %v", err, want) } }) } if checked == 0 { t.Fatal("no vector rejects with tce_hex found") } } // twoKeyClaimBytes builds a valid two-key claim, whose map occupies bytes // [91, 98): count(1) | len(1) key value | len(1) key value. func twoKeyClaimBytes(t *testing.T, fx *signerFixtures) []byte { t.Helper() c := &protocol.Claim{ Issuer: fx.alice.Public(), Subject: fx.bob.Public(), Claims: map[string]tce.Value{"a": tce.Bool(true), "b": tce.Bool(false)}, CreatedAt: 1_700_000_000, ExpiresAt: 1_700_086_400, Serial: 1, Nonce: nonce(1), } b, err := protocol.EncodeClaim(c) if err != nil { t.Fatal(err) } const wantLen = 91 + 7 + 5 + 5 + 1 + 1 + 16 if len(b) != wantLen { t.Fatalf("unexpected claim length %d, want %d; offset assumptions are stale", len(b), wantLen) } return b } func TestRejectUnsortedAndDuplicateMapKeys(t *testing.T) { fx := newSignerFixtures(t) base := twoKeyClaimBytes(t, fx) t.Run("unsorted map keys", func(t *testing.T) { bad := append([]byte{}, base...) copy(bad[91:98], []byte{0x02, 0x01, 'b', 0x01, 0x01, 'a', 0x02}) if _, err := protocol.DecodeClaim(bad); !errors.Is(err, tce.ErrKeyOrder) { t.Fatalf("err = %v, want ErrKeyOrder", err) } }) t.Run("duplicate map key", func(t *testing.T) { bad := append([]byte{}, base...) copy(bad[91:98], []byte{0x02, 0x01, 'a', 0x02, 0x01, 'a', 0x01}) if _, err := protocol.DecodeClaim(bad); !errors.Is(err, tce.ErrDuplicateKey) { t.Fatalf("err = %v, want ErrDuplicateKey", err) } }) } func TestRejectReservedValueTag(t *testing.T) { // The frozen claim/boolean vector has a single value tag at offset 105. vf := loadVectors(t) for _, v := range vf.Vectors { if v.Name != "claim/boolean" { continue } b := mustHex(t, v.TCEHex) for _, tag := range []byte{0x05, 0x06, 0x07, 0x08, 0xff} { bad := append([]byte{}, b...) bad[105] = tag if _, err := protocol.DecodeClaim(bad); !errors.Is(err, tce.ErrValueTag) { t.Fatalf("value tag 0x%02x: err = %v, want ErrValueTag", tag, err) } } return } t.Fatal("claim/boolean vector not found") }