From 2b2fc76121699e20c60dabb40e4507128731c0d5 Mon Sep 17 00:00:00 2001 From: Rasmus Dahlberg Date: Mon, 17 May 2021 22:04:33 +0200 Subject: started to refactor types and parsers Work in progress. --- types/http_test.go | 322 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 322 insertions(+) create mode 100644 types/http_test.go (limited to 'types/http_test.go') diff --git a/types/http_test.go b/types/http_test.go new file mode 100644 index 0000000..8165edf --- /dev/null +++ b/types/http_test.go @@ -0,0 +1,322 @@ +package types + +import ( + "bytes" + "encoding/hex" + "net/http" + "reflect" + "strings" + "testing" +) + +var ( + testZeroBuffer32 = [32]byte{} + testZeroBuffer64 = [64]byte{} +) + +func TestSignedTreeHeadToHTTP(t *testing.T) { + description := "valid: cosigned tree head with two signatures" + sth := &SignedTreeHead{ + TreeHead: TreeHead{ + Timestamp: 0, + TreeSize: 0, + RootHash: testBuffer32, + }, + SigIdent: []SigIdent{ + SigIdent{ + Signature: testZeroBuffer64, + KeyHash: testZeroBuffer32, + }, + SigIdent{ + Signature: testBuffer64, + KeyHash: testBuffer32, + }, + }, + } + want := map[string][]string{ + HeaderTimestamp: []string{"0"}, + HeaderTreeSize: []string{"0"}, + HeaderRootHash: []string{hex.EncodeToString(testBuffer32[:])}, + HeaderSignature: []string{ + hex.EncodeToString(testZeroBuffer64[:]), + hex.EncodeToString(testBuffer64[:]), + }, + HeaderKeyHash: []string{ + hex.EncodeToString(testZeroBuffer32[:]), + hex.EncodeToString(testBuffer32[:]), + }, + } + buf, err := sth.ToHTTP() + if err != nil { + t.Fatalf("sth.ToHTTP: %v", err) + } + hdr, err := headerFromBuf(buf) + if err != nil { + t.Fatalf("headerFromBuf: %v", err) + } + compareHeaderWithMap(t, description, hdr, want) +} + +func TestSignedTreeHeadFromHTTP(t *testing.T) { + for _, table := range []struct { + description string + buf []byte + wantErr bool + wantSth *SignedTreeHead + }{ + { + description: "invalid: not ST log HTTP header", + buf: newHeaderBuf(t, map[string][]string{ + "user-agent": []string{"secret"}, + }), + wantErr: true, + }, + { + description: "invalid: timestamp", + buf: newHeaderBuf(t, map[string][]string{ + HeaderTreeSize: []string{"0"}, + HeaderRootHash: []string{hex.EncodeToString(testBuffer32[:])}, + HeaderSignature: []string{hex.EncodeToString(testBuffer64[:])}, + HeaderKeyHash: []string{hex.EncodeToString(testBuffer32[:])}, + }), + wantErr: true, + }, + { + description: "invalid: tree size", + buf: newHeaderBuf(t, map[string][]string{ + HeaderTimestamp: []string{"0"}, + HeaderRootHash: []string{hex.EncodeToString(testBuffer32[:])}, + HeaderSignature: []string{hex.EncodeToString(testBuffer64[:])}, + HeaderKeyHash: []string{hex.EncodeToString(testBuffer32[:])}, + }), + wantErr: true, + }, + { + description: "invalid: root hash", + buf: newHeaderBuf(t, map[string][]string{ + HeaderTimestamp: []string{"0"}, + HeaderTreeSize: []string{"0"}, + HeaderSignature: []string{hex.EncodeToString(testBuffer64[:])}, + HeaderKeyHash: []string{hex.EncodeToString(testBuffer32[:])}, + }), + wantErr: true, + }, + { + description: "invalid: signature", + buf: newHeaderBuf(t, map[string][]string{ + HeaderTimestamp: []string{"0"}, + HeaderTreeSize: []string{"0"}, + HeaderRootHash: []string{hex.EncodeToString(testBuffer32[:])}, + HeaderSignature: []string{hex.EncodeToString(testBuffer32[:])}, + HeaderKeyHash: []string{hex.EncodeToString(testBuffer32[:])}, + }), + wantErr: true, + }, + { + description: "invalid: key hash", + buf: newHeaderBuf(t, map[string][]string{ + HeaderTimestamp: []string{"0"}, + HeaderTreeSize: []string{"0"}, + HeaderRootHash: []string{hex.EncodeToString(testBuffer32[:])}, + HeaderSignature: []string{hex.EncodeToString(testBuffer64[:])}, + HeaderKeyHash: []string{hex.EncodeToString(testBuffer64[:])}, + }), + wantErr: true, + }, + { + description: "invalid: sigident count", + buf: newHeaderBuf(t, map[string][]string{ + HeaderTimestamp: []string{"0"}, + HeaderTreeSize: []string{"0"}, + HeaderRootHash: []string{hex.EncodeToString(testBuffer32[:])}, + HeaderSignature: []string{hex.EncodeToString(testBuffer64[:])}, + HeaderKeyHash: []string{ + hex.EncodeToString(testZeroBuffer32[:]), + hex.EncodeToString(testBuffer32[:]), + }, + }), + wantErr: true, + }, + { + description: "invalid: no signer", + buf: newHeaderBuf(t, map[string][]string{ + HeaderTimestamp: []string{"0"}, + HeaderTreeSize: []string{"0"}, + HeaderRootHash: []string{hex.EncodeToString(testBuffer32[:])}, + }), + wantErr: true, + }, + { + description: "valid: cosigned tree head with two signatures", + buf: newHeaderBuf(t, map[string][]string{ + HeaderTimestamp: []string{"0"}, + HeaderTreeSize: []string{"0"}, + HeaderRootHash: []string{hex.EncodeToString(testBuffer32[:])}, + HeaderSignature: []string{ + hex.EncodeToString(testZeroBuffer64[:]), + hex.EncodeToString(testBuffer64[:]), + }, + HeaderKeyHash: []string{ + hex.EncodeToString(testZeroBuffer32[:]), + hex.EncodeToString(testBuffer32[:]), + }, + }), + wantSth: &SignedTreeHead{ + TreeHead: TreeHead{ + Timestamp: 0, + TreeSize: 0, + RootHash: testBuffer32, + }, + SigIdent: []SigIdent{ + SigIdent{ + Signature: testZeroBuffer64, + KeyHash: testZeroBuffer32, + }, + SigIdent{ + Signature: testBuffer64, + KeyHash: testBuffer32, + }, + }, + }, + }, + } { + sth, err := SignedTreeHeadFromHTTP(table.buf) + if got, want := err != nil, table.wantErr; got != want { + t.Errorf("got error %v but wanted %v in test %q: %v", got, want, table.description, err) + } + if err != nil { + continue // nothing more to check on error + } + if got, want := sth, table.wantSth; !reflect.DeepEqual(got, want) { + t.Errorf("got signed tree head\n\t%v\nbut wanted\n\t%v\nin test %q", got, want, table.description) + } + } +} + +func TestHeaderFromBuf(t *testing.T) { + for _, table := range []struct { + description string + buf []byte + wantErr bool + wantMap map[string][]string + }{ + { + description: "invalid: split", + buf: []byte(HeaderPrefix + "k1: v1:v2\r\n"), + wantErr: true, + }, + { + description: "invalid: prefix", + buf: []byte("user-agent: secret\r\n"), + wantErr: true, + }, + { + description: "valid: one key with funky case", + buf: []byte(funkyCase(t, HeaderPrefix) + "k1: v1\r\n"), + wantMap: map[string][]string{ + HeaderPrefix + "k1": []string{"v1"}, + }, + }, + { + description: "valid: two keys where one has multiple values", + buf: []byte( + HeaderPrefix + "k1: v1 \r\n" + + HeaderPrefix + "k2: v2\r\n" + + HeaderPrefix + "k2: v3\r\n", + ), + wantMap: map[string][]string{ + HeaderPrefix + "k1": []string{"v1"}, + HeaderPrefix + "k2": []string{"v2", "v3"}, + }, + }, + } { + hdr, err := headerFromBuf(table.buf) + if got, want := err != nil, table.wantErr; got != want { + t.Errorf("got error %v but wanted %v in test %q: %v", got, want, table.description, err) + } + if err != nil { + continue // nothing more to check on error + } + compareHeaderWithMap(t, table.description, hdr, table.wantMap) + } +} + +func TestDecodeHex(t *testing.T) { + for _, table := range []struct { + description string + hex string + wantErr bool + wantBuf [4]byte + }{ + { + description: "invalid: too short input", + hex: "000102", + wantErr: true, + }, + { + description: "invalid: too large input", + hex: "0001020304", + wantErr: true, + }, + { + description: "invalid: not hex (1/2)", + hex: "000102030", + wantErr: true, + }, + { + description: "invalid: not hex (2/2)", + hex: "0001020q", + wantErr: true, + }, + { + description: "valid", + hex: "00010203", + wantBuf: [4]byte{0, 1, 2, 3}, + }, + } { + var buf [4]byte + err := decodeHex(table.hex, buf[:]) + if got, want := err != nil, table.wantErr; got != want { + t.Errorf("got error %v but wanted %v in test %q: %v", got, want, table.description, err) + } + if err != nil { + continue // nothing more to check on error + } + if got, want := buf[:], table.wantBuf[:]; !bytes.Equal(got, want) { + t.Errorf("got buf %v but wanted %v in test %q", got, want, table.description) + } + } +} + +func newHeaderBuf(t *testing.T, kv map[string][]string) []byte { + t.Helper() + hdr := http.Header{} + for key, values := range kv { + for _, value := range values { + hdr.Add(key, value) + } + } + buf := bytes.NewBuffer(nil) + if err := hdr.Write(buf); err != nil { + t.Fatalf("hdr.Write(): %v", err) + } + return buf.Bytes() +} + +func compareHeaderWithMap(t *testing.T, description string, hdr http.Header, wantMap map[string][]string) { + t.Helper() + if got, want := len(hdr), len(wantMap); got != want { + t.Errorf("got %d keys but wanted %d in test %q", got, want, description) + } + for key, value := range wantMap { + if got, want := hdr.Values(key), value; !reflect.DeepEqual(got, want) { + t.Errorf("got value %v but wanted %v for key %v in test %q", got, want, key, description) + } + } +} + +func funkyCase(t *testing.T, str string) string { + t.Helper() + splitIndex := len(str) / 2 + return strings.ToLower(str[:splitIndex]) + strings.ToUpper(str[splitIndex:]) +} -- cgit v1.2.3