package federation import ( "errors" "strings" "testing" "github.com/dariuszjaniak/warp/server/internal/matrix/signing" ) func TestParseXMatrix(t *testing.T) { cases := []struct { name string header string want XMatrix bad bool }{ { name: "quoted", header: `X-Matrix origin="a.example",destination="b.example",key="ed25519:k1",sig="c2ln"`, want: XMatrix{Origin: "a.example", Destination: "b.example", Key: "ed25519:k1", Sig: "c2ln"}, }, { name: "unquoted and spaced", header: `X-Matrix origin=a.example, key=ed25519:k1 ,sig=c2ln`, want: XMatrix{Origin: "a.example", Key: "ed25519:k1", Sig: "c2ln"}, }, { name: "escapes and an unknown parameter", header: `x-matrix origin="a\"b",future="x,y",key="ed25519:k",sig="s"`, want: XMatrix{Origin: `a"b`, Key: "ed25519:k", Sig: "s"}, }, {name: "wrong scheme", header: `Bearer abc`, bad: true}, {name: "no origin", header: `X-Matrix key="ed25519:k",sig="s"`, bad: true}, {name: "no sig", header: `X-Matrix origin="a",key="ed25519:k"`, bad: true}, {name: "key without an algorithm", header: `X-Matrix origin="a",key="k",sig="s"`, bad: true}, {name: "unterminated quote", header: `X-Matrix origin="a,key="ed25519:k",sig="s"`, bad: true}, {name: "duplicate origin", header: `X-Matrix origin="a",origin="b",key="ed25519:k",sig="s"`, bad: true}, {name: "too long", header: "X-Matrix origin=" + strings.Repeat("a", 5000) + `,key="ed25519:k",sig="s"`, bad: true}, } for _, tc := range cases { t.Run(tc.name, func(t *testing.T) { got, err := ParseXMatrix(tc.header) if tc.bad { if !errors.Is(err, ErrBadAuthorization) { t.Fatalf("ParseXMatrix(%q) = %+v, %v; want ErrBadAuthorization", tc.header, got, err) } return } if err != nil { t.Fatalf("ParseXMatrix(%q): %v", tc.header, err) } if got != tc.want { t.Errorf("ParseXMatrix(%q) = %+v, want %+v", tc.header, got, tc.want) } }) } } func TestXMatrixStringRoundTrips(t *testing.T) { x := XMatrix{Origin: `we"ird`, Destination: `b\c`, Key: "ed25519:k", Sig: "c2ln"} got, err := ParseXMatrix(x.String()) if err != nil { t.Fatalf("ParseXMatrix(%q): %v", x.String(), err) } if got != x { t.Errorf("round trip = %+v, want %+v", got, x) } } func TestSignedRequestVerifies(t *testing.T) { key, err := signing.Generate("k1") if err != nil { t.Fatal(err) } content := []byte(`{"b":2,"a":1}`) x, err := SignRequest(key, "a.example", "b.example", "PUT", "/_matrix/federation/v1/send/1?x=%21", content) if err != nil { t.Fatalf("SignRequest: %v", err) } verify := func(method, uri string, body []byte) error { signed, err := requestSignedJSON(x, "b.example", method, uri, body) if err != nil { return err } return signing.VerifyJSON("a.example", x.Key, key.Public, signed) } if err := verify("PUT", "/_matrix/federation/v1/send/1?x=%21", []byte(`{"a":1,"b":2}`)); err != nil { t.Errorf("the request as signed does not verify: %v", err) } // Each part of the request is covered. if err := verify("POST", "/_matrix/federation/v1/send/1?x=%21", content); err == nil { t.Error("a different method verified") } if err := verify("PUT", "/_matrix/federation/v1/send/1?x=!", content); err == nil { t.Error("a differently escaped URI verified") } if err := verify("PUT", "/_matrix/federation/v1/send/1?x=%21", []byte(`{"a":1}`)); err == nil { t.Error("a different body verified") } } func FuzzParseXMatrix(f *testing.F) { f.Add(`X-Matrix origin="a.example",destination="b.example",key="ed25519:k1",sig="c2ln"`) f.Add(`X-Matrix origin=a,key=ed25519:k,sig=s`) f.Add(`X-Matrix origin="\\",key="ed25519:k",sig="\"`) f.Fuzz(func(t *testing.T, header string) { x, err := ParseXMatrix(header) if err != nil { return } // Whatever parses must survive being written back out. again, err := ParseXMatrix(x.String()) if err != nil { t.Fatalf("re-parsing %q (from %q): %v", x.String(), header, err) } if again != x { t.Fatalf("round trip of %q changed %+v into %+v", header, x, again) } }) }