aboutsummaryrefslogtreecommitdiff
path: root/handler_test.go
diff options
context:
space:
mode:
Diffstat (limited to 'handler_test.go')
-rw-r--r--handler_test.go169
1 files changed, 169 insertions, 0 deletions
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(),
+ },
+ }
+}