From 38deaf4c6b4923eb4b0e77f151c0169ab439a268 Mon Sep 17 00:00:00 2001 From: Rasmus Dahlberg Date: Mon, 16 Nov 2020 14:58:09 +0100 Subject: added add-entry handler tests Ensures that the respective error handling functions are invoked. --- handler_test.go | 169 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 169 insertions(+) diff --git a/handler_test.go b/handler_test.go index 5fd4818..c0a6e68 100644 --- a/handler_test.go +++ b/handler_test.go @@ -8,6 +8,8 @@ import ( "testing" "time" + "crypto/ed25519" + "crypto/tls" "crypto/x509" "encoding/json" "net/http" @@ -19,6 +21,9 @@ import ( "github.com/google/trillian" "github.com/system-transparency/stfe/server/testdata" "github.com/system-transparency/stfe/x509util" + + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" ) type testHandler struct { @@ -159,6 +164,100 @@ func TestGetAnchors(t *testing.T) { } } +func TestAddEntry(t *testing.T) { + for _, table := range []struct { + description string + breq *bytes.Buffer + trsp *trillian.QueueLeafResponse + terr error + wantCode int + wantErrText string + signer crypto.Signer + }{ + { + description: "empty trillian response", + breq: makeTestLeafBuffer(t, []byte("foobar-1.2.3"), testdata.PemChain, testdata.PemChainKey, true), + terr: fmt.Errorf("back-end failure"), + wantCode: http.StatusInternalServerError, + wantErrText: http.StatusText(http.StatusInternalServerError) + "\n", + }, + { + description: "bad request parameters", + breq: makeTestLeafBuffer(t, []byte("foobar-1.2.3"), testdata.PemChain, testdata.PemChainKey, false), + wantCode: http.StatusBadRequest, + wantErrText: http.StatusText(http.StatusBadRequest) + "\n", + }, + { + description: "log signature failure", + breq: makeTestLeafBuffer(t, []byte("foobar-1.2.3"), testdata.PemChain, testdata.PemChainKey, true), + trsp: makeTrillianQueueLeafResponse(t, []byte("foobar-1.2.3"), testdata.PemChain, testdata.PemChainKey), + wantCode: http.StatusInternalServerError, + wantErrText: http.StatusText(http.StatusInternalServerError) + "\n", + signer: cttestdata.NewSignerWithErr(nil, fmt.Errorf("signing failed")), + }, + { + description: "valid add-entry request-response", + breq: makeTestLeafBuffer(t, []byte("foobar-1.2.3"), testdata.PemChain, testdata.PemChainKey, true), + trsp: makeTrillianQueueLeafResponse(t, []byte("foobar-1.2.3"), testdata.PemChain, testdata.PemChainKey), + wantCode: http.StatusOK, + signer: cttestdata.NewSignerWithFixedSig(nil, make([]byte, 32)), + }, + } { + func() { // run deferred functions at the end of each iteration + th := newTestHandler(t, table.signer) + defer th.mockCtrl.Finish() + + url := "http://example.com" + th.instance.LogParameters.Prefix + "/add-entry" + req, err := http.NewRequest("POST", "application/json", table.breq) + if err != nil { + t.Fatalf("failed creating http request: %v", err) + } + + if table.trsp != nil || table.terr != nil { + th.client.EXPECT().QueueLeaf(testdata.NewDeadlineMatcher(), gomock.Any()).Return(table.trsp, table.terr) + } + w := httptest.NewRecorder() + th.postHandler(t, "add-entry").ServeHTTP(w, req) + if w.Code != table.wantCode { + t.Errorf("GET(%s)=%d, want http status code %d", url, w.Code, table.wantCode) + } + + body := w.Body.String() + if w.Code != http.StatusOK { + if body != table.wantErrText { + t.Errorf("GET(%s)=%q, want text %q", url, body, table.wantErrText) + } + return + } + + // status code is http.StatusOK, check response + var data []byte + if err := json.Unmarshal([]byte(body), &data); err != nil { + t.Errorf("failed unmarshaling json: %v, wanted ok", err) + return + } + var item StItem + if err := item.Unmarshal(data); err != nil { + t.Errorf("failed unmarshaling StItem: %v, wanted ok", err) + return + } + if item.Format != StFormatSignedDebugInfoV1 { + t.Errorf("invalid StFormat: got %v, want %v", item.Format, StFormatSignedDebugInfoV1) + } + sdi := item.SignedDebugInfoV1 + if !bytes.Equal(sdi.LogId, th.instance.LogParameters.LogId) { + t.Errorf("want log id %X, got %X", sdi.LogId, th.instance.LogParameters.LogId) + } + if len(sdi.Message) == 0 { + t.Errorf("expected message, got none") + } + if !bytes.Equal(sdi.Signature, make([]byte, 32)) { + t.Errorf("want signature %X, got %X", sdi.Signature, make([]byte, 32)) + } + }() + } +} + // TestGetSth: docdoc and TODO: move quirky tests to trillian_tests.go? func TestGetSth(t *testing.T) { for _, table := range []struct { @@ -280,3 +379,73 @@ func TestGetSth(t *testing.T) { }() } } + +// makeTestLeaf creates add-entry test data +func makeTestLeaf(t *testing.T, name, pemChain, pemKey []byte) ([]byte, []byte) { + t.Helper() + key, err := x509util.NewEd25519PrivateKey(pemKey) + if err != nil { + t.Fatalf("failed creating ed25519 signing key: %v", err) + } + chain, err := x509util.NewCertificateList(pemChain) + if err != nil { + t.Fatalf("failed parsing x509 chain: %v", err) + } + leaf, err := NewChecksumV1(name, make([]byte, 32)).Marshal() + if err != nil { + t.Fatalf("failed creating serialized checksum_v1: %v", err) + } + appendix, err := NewAppendix(chain, ed25519.Sign(key, leaf), uint16(tls.Ed25519)).Marshal() + if err != nil { + t.Fatalf("failed creating serialized appendix: %v", err) + } + return leaf, appendix +} + +// makeTestLeafBuffer creates an add-entry data buffer that can be posted. If +// valid is set to false an invalid signature will be used. +func makeTestLeafBuffer(t *testing.T, name, pemChain, pemKey []byte, valid bool) *bytes.Buffer { + t.Helper() + leaf, appendix := makeTestLeaf(t, name, pemChain, pemKey) + + var a Appendix + if err := a.Unmarshal(appendix); err != nil { + t.Fatalf("failed unmarshaling Appendix: %v", err) + } + chain := make([][]byte, 0, len(a.Chain)) + for _, certificate := range a.Chain { + chain = append(chain, certificate.Data) + } + req := AddEntryRequest{ + Item: leaf, + Signature: a.Signature, + SignatureScheme: a.SignatureScheme, + Chain: chain, + } + if !valid { + req.Signature = []byte{0, 1, 2, 3} + } + data, err := json.Marshal(req) + if err != nil { + t.Fatalf("failed marshaling add-entry parameters: %v", err) + } + return bytes.NewBuffer(data) +} + +// makeTrillianQueueLeafResponse creates a valid trillian QueueLeafResponse +func makeTrillianQueueLeafResponse(t *testing.T, name, pemChain, pemKey []byte) *trillian.QueueLeafResponse { + t.Helper() + leaf, appendix := makeTestLeaf(t, name, pemChain, pemKey) + return &trillian.QueueLeafResponse{ + QueuedLeaf: &trillian.QueuedLogLeaf{ + Leaf: &trillian.LogLeaf{ + MerkleLeafHash: nil, // not used by stfe + LeafValue: leaf, + ExtraData: appendix, + LeafIndex: 0, // not applicable (log is not pre-ordered) + LeafIdentityHash: nil, // not used by stfe + }, + Status: status.New(codes.OK, "ok").Proto(), + }, + } +} -- cgit v1.2.3