aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--handler.go8
-rw-r--r--handler_test.go85
-rw-r--r--server/testdata/data.go46
-rw-r--r--server/testdata/helper.go47
-rw-r--r--server/testdata/type.go32
-rw-r--r--trillian.go16
-rw-r--r--trillian_test.go214
-rw-r--r--type_test.go6
8 files changed, 265 insertions, 189 deletions
diff --git a/handler.go b/handler.go
index 839a310..f06bd74 100644
--- a/handler.go
+++ b/handler.go
@@ -65,8 +65,8 @@ func addEntry(ctx context.Context, i *Instance, w http.ResponseWriter, r *http.R
ExtraData: appendix,
},
})
- if status, errInner := checkQueueLeaf(trsp, err); errInner != nil {
- return status, fmt.Errorf("bad QueueLeafResponse: %v", errInner)
+ if errInner := checkQueueLeaf(trsp, err); errInner != nil {
+ return http.StatusInternalServerError, fmt.Errorf("bad QueueLeafResponse: %v", errInner)
}
sdi, err := i.LogParameters.genV1Sdi(trsp.QueuedLeaf.Leaf.LeafValue)
@@ -182,8 +182,8 @@ func getSth(ctx context.Context, i *Instance, w http.ResponseWriter, _ *http.Req
LogId: i.LogParameters.TreeId,
})
var lr types.LogRootV1
- if status, errInner := checkGetLatestSignedLogRoot(i.LogParameters, trsp, err, &lr); errInner != nil {
- return status, fmt.Errorf("bad GetLatestSignedLogRootResponse: %v", errInner)
+ if errInner := checkGetLatestSignedLogRoot(i.LogParameters, trsp, err, &lr); errInner != nil {
+ return http.StatusInternalServerError, fmt.Errorf("bad GetLatestSignedLogRootResponse: %v", errInner)
}
sth, err := i.LogParameters.genV1Sth(NewTreeHeadV1(&lr))
diff --git a/handler_test.go b/handler_test.go
index 277c74f..4496b15 100644
--- a/handler_test.go
+++ b/handler_test.go
@@ -2,6 +2,7 @@ package stfe
import (
"bytes"
+ "context"
"crypto"
"fmt"
"strings"
@@ -20,7 +21,7 @@ import (
"github.com/google/certificate-transparency-go/trillian/mockclient"
cttestdata "github.com/google/certificate-transparency-go/trillian/testdata"
"github.com/google/trillian"
- "github.com/system-transparency/stfe/server/testdata"
+ "github.com/system-transparency/stfe/testdata"
"github.com/system-transparency/stfe/x509util"
)
@@ -197,7 +198,7 @@ func TestGetEntries(t *testing.T) {
Start: 0,
End: 1,
},
- trsp: makeTrillianGetLeavesByRangeResponse(t, 0, 1, []byte("foobar-1.2.3"), testdata.PemChain, testdata.PemChainKey, false),
+ trsp: makeTrillianGetLeavesByRangeResponse(t, 0, 1, []byte("foobar-1.2.3"), testdata.FirstPemChain, testdata.FirstPemChainKey, false),
wantCode: http.StatusInternalServerError,
wantErrText: http.StatusText(http.StatusInternalServerError) + "\n",
},
@@ -207,7 +208,7 @@ func TestGetEntries(t *testing.T) {
Start: 0,
End: 1,
},
- trsp: makeTrillianGetLeavesByRangeResponse(t, 0, 1, []byte("foobar-1.2.3"), testdata.PemChain, testdata.PemChainKey, true),
+ trsp: makeTrillianGetLeavesByRangeResponse(t, 0, 1, []byte("foobar-1.2.3"), testdata.FirstPemChain, testdata.FirstPemChainKey, true),
wantCode: http.StatusOK,
},
} {
@@ -226,7 +227,7 @@ func TestGetEntries(t *testing.T) {
req.URL.RawQuery = q.Encode()
if table.trsp != nil || table.terr != nil {
- th.client.EXPECT().GetLeavesByRange(testdata.NewDeadlineMatcher(), gomock.Any()).Return(table.trsp, table.terr)
+ th.client.EXPECT().GetLeavesByRange(newDeadlineMatcher(), gomock.Any()).Return(table.trsp, table.terr)
}
w := httptest.NewRecorder()
th.getHandler(t, "get-entries").ServeHTTP(w, req)
@@ -298,29 +299,29 @@ func TestAddEntry(t *testing.T) {
}{
{
description: "empty trillian response",
- breq: makeTestLeafBuffer(t, []byte("foobar-1.2.3"), testdata.PemChain, testdata.PemChainKey, true),
+ breq: makeTestLeafBuffer(t, []byte("foobar-1.2.3"), testdata.FirstPemChain, testdata.FirstPemChainKey, 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),
+ breq: makeTestLeafBuffer(t, []byte("foobar-1.2.3"), testdata.FirstPemChain, testdata.FirstPemChainKey, 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),
+ breq: makeTestLeafBuffer(t, []byte("foobar-1.2.3"), testdata.FirstPemChain, testdata.FirstPemChainKey, true),
+ trsp: makeTrillianQueueLeafResponse(t, []byte("foobar-1.2.3"), testdata.FirstPemChain, testdata.FirstPemChainKey, false),
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),
+ breq: makeTestLeafBuffer(t, []byte("foobar-1.2.3"), testdata.FirstPemChain, testdata.FirstPemChainKey, true),
+ trsp: makeTrillianQueueLeafResponse(t, []byte("foobar-1.2.3"), testdata.FirstPemChain, testdata.FirstPemChainKey, false),
wantCode: http.StatusOK,
signer: cttestdata.NewSignerWithFixedSig(nil, make([]byte, 32)),
},
@@ -338,7 +339,7 @@ func TestAddEntry(t *testing.T) {
if table.trsp != nil || table.terr != nil {
// TODO: replace gomock.Any with a check that leaf and appendix are OK, e.g., chain length should be 3
- th.client.EXPECT().QueueLeaf(testdata.NewDeadlineMatcher(), gomock.Any()).Return(table.trsp, table.terr)
+ th.client.EXPECT().QueueLeaf(newDeadlineMatcher(), gomock.Any()).Return(table.trsp, table.terr)
}
w := httptest.NewRecorder()
th.postHandler(t, "add-entry").ServeHTTP(w, req)
@@ -382,8 +383,9 @@ func TestAddEntry(t *testing.T) {
}
}
-// TestGetSth: docdoc and TODO: move quirky tests to trillian_tests.go?
func TestGetSth(t *testing.T) {
+ tr := makeLatestSignedLogRootResponse(t, 0, 0, make([]byte, 32))
+ tr.SignedLogRoot.LogRoot = tr.SignedLogRoot.LogRoot[1:]
for _, table := range []struct {
description string
trsp *trillian.GetLatestSignedLogRootResponse
@@ -399,45 +401,22 @@ func TestGetSth(t *testing.T) {
wantErrText: http.StatusText(http.StatusInternalServerError) + "\n",
},
{
- description: "incomplete trillian response: nil response",
- wantCode: http.StatusInternalServerError,
- wantErrText: http.StatusText(http.StatusInternalServerError) + "\n",
- },
- {
- description: "incomplete trillian response: no signed log root",
- trsp: &trillian.GetLatestSignedLogRootResponse{SignedLogRoot: nil},
- wantCode: http.StatusInternalServerError,
- wantErrText: http.StatusText(http.StatusInternalServerError) + "\n",
- },
- {
- description: "incomplete trillian response: truncated log root",
- trsp: testdata.TruncatedSignedLogRootResponse(t),
- wantCode: http.StatusInternalServerError,
- wantErrText: http.StatusText(http.StatusInternalServerError) + "\n",
- },
- {
- description: "incomplete trillian response: invalid root hash size",
- trsp: testdata.NewGetLatestSignedLogRootResponse(t, 0, 0, make([]byte, 31)),
- wantCode: http.StatusInternalServerError,
- wantErrText: http.StatusText(http.StatusInternalServerError) + "\n",
- },
- {
description: "marshal failure: no signature",
- trsp: testdata.NewGetLatestSignedLogRootResponse(t, 0, 0, make([]byte, 32)),
+ trsp: makeLatestSignedLogRootResponse(t, 0, 0, make([]byte, 32)),
wantCode: http.StatusInternalServerError,
wantErrText: http.StatusText(http.StatusInternalServerError) + "\n",
signer: cttestdata.NewSignerWithFixedSig(nil, make([]byte, 0)),
},
{
description: "signature failure",
- trsp: testdata.NewGetLatestSignedLogRootResponse(t, 0, 0, make([]byte, 32)),
+ trsp: makeLatestSignedLogRootResponse(t, 0, 0, make([]byte, 32)),
wantCode: http.StatusInternalServerError,
wantErrText: http.StatusText(http.StatusInternalServerError) + "\n",
signer: cttestdata.NewSignerWithErr(nil, fmt.Errorf("signing failed")),
},
{
description: "valid request and response",
- trsp: testdata.NewGetLatestSignedLogRootResponse(t, 0, 0, make([]byte, 32)),
+ trsp: makeLatestSignedLogRootResponse(t, 0, 0, make([]byte, 32)),
wantCode: http.StatusOK,
signer: cttestdata.NewSignerWithFixedSig(nil, make([]byte, 32)),
},
@@ -453,7 +432,7 @@ func TestGetSth(t *testing.T) {
}
w := httptest.NewRecorder()
- th.client.EXPECT().GetLatestSignedLogRoot(testdata.NewDeadlineMatcher(), gomock.Any()).Return(table.trsp, table.terr)
+ th.client.EXPECT().GetLatestSignedLogRoot(newDeadlineMatcher(), gomock.Any()).Return(table.trsp, table.terr)
th.getHandler(t, "get-sth").ServeHTTP(w, req)
if w.Code != table.wantCode {
t.Errorf("GET(%s)=%d, want http status code %d", url, w.Code, table.wantCode)
@@ -562,7 +541,7 @@ func TestGetConsistencyProof(t *testing.T) {
w := httptest.NewRecorder()
if table.trsp != nil || table.terr != nil {
- th.client.EXPECT().GetConsistencyProof(testdata.NewDeadlineMatcher(), gomock.Any()).Return(table.trsp, table.terr)
+ th.client.EXPECT().GetConsistencyProof(newDeadlineMatcher(), gomock.Any()).Return(table.trsp, table.terr)
}
th.getHandler(t, "get-consistency-proof").ServeHTTP(w, req)
if w.Code != table.wantCode {
@@ -671,7 +650,7 @@ func TestGetProofByHash(t *testing.T) {
w := httptest.NewRecorder()
if table.trsp != nil || table.terr != nil {
- th.client.EXPECT().GetInclusionProofByHash(testdata.NewDeadlineMatcher(), gomock.Any()).Return(table.trsp, table.terr)
+ th.client.EXPECT().GetInclusionProofByHash(newDeadlineMatcher(), gomock.Any()).Return(table.trsp, table.terr)
}
th.getHandler(t, "get-proof-by-hash").ServeHTTP(w, req)
if w.Code != table.wantCode {
@@ -773,3 +752,27 @@ func makeTestLeafBuffer(t *testing.T, name, pemChain, pemKey []byte, valid bool)
}
return bytes.NewBuffer(data)
}
+
+// deadlineMatcher implements gomock.Matcher, such that an error is raised if
+// there is no context.Context deadline set
+type deadlineMatcher struct{}
+
+// newDeadlineMatcher returns a new DeadlineMatcher
+func newDeadlineMatcher() gomock.Matcher {
+ return &deadlineMatcher{}
+}
+
+// Matches returns true if the passed interface is a context with a deadline
+func (dm *deadlineMatcher) Matches(i interface{}) bool {
+ ctx, ok := i.(context.Context)
+ if !ok {
+ return false
+ }
+ _, ok = ctx.Deadline()
+ return ok
+}
+
+// String is needed to implement gomock.Matcher
+func (dm *deadlineMatcher) String() string {
+ return fmt.Sprintf("deadlineMatcher{}")
+}
diff --git a/server/testdata/data.go b/server/testdata/data.go
deleted file mode 100644
index 3c814d9..0000000
--- a/server/testdata/data.go
+++ /dev/null
@@ -1,46 +0,0 @@
-package testdata
-
-var (
- // PemAnchors is a list of trusted root certificates
- PemAnchors = []byte(`-----BEGIN CERTIFICATE-----
-MIIB/TCCAa+gAwIBAgIUDYJzaC5VSkKwiLVAxO5MyphAkN8wBQYDK2VwMGwxCzAJ
-BgNVBAYTAk5BMQswCQYDVQQIDAJOQTELMAkGA1UEBwwCTkExCzAJBgNVBAoMAk5B
-MQswCQYDVQQLDAJOQTEWMBQGA1UEAwwNc3RmZSB0ZXN0ZGF0YTERMA8GCSqGSIb3
-DQEJARYCTkEwHhcNMjAxMTAzMTgzMTMxWhcNMzIwMTIxMTgzMTMxWjBsMQswCQYD
-VQQGEwJOQTELMAkGA1UECAwCTkExCzAJBgNVBAcMAk5BMQswCQYDVQQKDAJOQTEL
-MAkGA1UECwwCTkExFjAUBgNVBAMMDXN0ZmUgdGVzdGRhdGExETAPBgkqhkiG9w0B
-CQEWAk5BMCowBQYDK2VwAyEAJ1IiXCB4YHwdWka9MM0bc7LvKAtksmtIo8IhkuEB
-uzGjYzBhMB0GA1UdDgQWBBQBvsxROtKU6zmr/SxcfTMDsAQcMTAfBgNVHSMEGDAW
-gBQBvsxROtKU6zmr/SxcfTMDsAQcMTAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB
-/wQEAwIChDAFBgMrZXADQQCXh6kDnE5giTjcLET2S94qTwnHVAj57DJcR/rf9Jy8
-NMGbtzTL0/V0B8DHuJFA/islbZJbN7rSvqddEKL8N2gI
------END CERTIFICATE-----`)
- // PemChain is composed of an end-entity and intermediate certificate
- PemChain = []byte(`-----BEGIN CERTIFICATE-----
-MIIBbDCCAR4CFDfeuu6XURfn7AE4WShuwZBHEaLIMAUGAytlcDBsMQswCQYDVQQG
-EwJOQTELMAkGA1UECAwCTkExCzAJBgNVBAcMAk5BMQswCQYDVQQKDAJOQTELMAkG
-A1UECwwCTkExFjAUBgNVBAMMDXN0ZmUgdGVzdGRhdGExETAPBgkqhkiG9w0BCQEW
-Ak5BMB4XDTIwMTEwMzE4MzI0MFoXDTMyMDEyMTE4MzI0MFowRTELMAkGA1UEBhMC
-QVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGEludGVybmV0IFdpZGdp
-dHMgUHR5IEx0ZDAqMAUGAytlcAMhAJvk390ZvwULplBri03Od4LLz+Sf/OUHu+20
-wik+T9y5MAUGAytlcANBANekliXq4ttoClBJDZoktIQxyHHNcWyXFrj1HlOaT5bC
-I3GIqqZ60Ua3jKytnEsKsD2rLMPItDwmG6wYSecy2ws=
------END CERTIFICATE-----
------BEGIN CERTIFICATE-----
-MIIB7jCCAaCgAwIBAgICEAAwBQYDK2VwMGwxCzAJBgNVBAYTAk5BMQswCQYDVQQI
-DAJOQTELMAkGA1UEBwwCTkExCzAJBgNVBAoMAk5BMQswCQYDVQQLDAJOQTEWMBQG
-A1UEAwwNc3RmZSB0ZXN0ZGF0YTERMA8GCSqGSIb3DQEJARYCTkEwHhcNMjAxMTAz
-MTgzMjE4WhcNMzIwMTIxMTgzMjE4WjBsMQswCQYDVQQGEwJOQTELMAkGA1UECAwC
-TkExCzAJBgNVBAcMAk5BMQswCQYDVQQKDAJOQTELMAkGA1UECwwCTkExFjAUBgNV
-BAMMDXN0ZmUgdGVzdGRhdGExETAPBgkqhkiG9w0BCQEWAk5BMCowBQYDK2VwAyEA
-F1yPPpjHKDAKN73pBFGXzAvIjdkLLimydu2y1HLMOiKjZjBkMB0GA1UdDgQWBBQ6
-P7JQ7yXtrTh7YkVU0I78P9A+nDAfBgNVHSMEGDAWgBQBvsxROtKU6zmr/SxcfTMD
-sAQcMTASBgNVHRMBAf8ECDAGAQH/AgEAMA4GA1UdDwEB/wQEAwIChDAFBgMrZXAD
-QQBm1GMV0ADPnXRWnelCW9tcyTh0p9hKefuSy/MNx7/XLHKnM5fX+yHqD84QOxES
-Vc510vi4dM8I+e/vcoBsmMQP
------END CERTIFICATE-----`)
- // PemChainKey is the private key of the final certificate in PemChain
- PemChainKey = []byte(`-----BEGIN PRIVATE KEY-----
-MC4CAQAwBQYDK2VwBCIEIDme3WaCwW2/FX095yh02yIIsn0D3vbvN5NsJzcdUwq1
------END PRIVATE KEY-----`)
-)
diff --git a/server/testdata/helper.go b/server/testdata/helper.go
deleted file mode 100644
index 6874616..0000000
--- a/server/testdata/helper.go
+++ /dev/null
@@ -1,47 +0,0 @@
-package testdata
-
-import (
- "testing"
-
- "github.com/google/trillian"
- "github.com/google/trillian/types"
-)
-
-// NewGetLatestSignedLogRootResponse creates a new trillian STH. Revision,
-// Metadata, Proof, KeyHint, and LogRootSignature are unsset.
-func NewGetLatestSignedLogRootResponse(t *testing.T, timestamp, size uint64, hash []byte) *trillian.GetLatestSignedLogRootResponse {
- t.Helper()
- return &trillian.GetLatestSignedLogRootResponse{
- SignedLogRoot: marshalSignedLogRoot(t, &types.LogRootV1{
- TreeSize: size,
- RootHash: hash,
- TimestampNanos: timestamp,
- Revision: 0, // not used by stfe
- Metadata: nil, // not used by stfe
- }),
- Proof: nil, // not used by stfe
- }
-}
-
-// TruncatedSignedLogRootResponse creates a truncated signed log root response
-// that cannot be unmarshalled, i.e., SignedLogRoot.LogRoot is invalid.
-func TruncatedSignedLogRootResponse(t *testing.T) *trillian.GetLatestSignedLogRootResponse {
- t.Helper()
- slrr := NewGetLatestSignedLogRootResponse(t, 0, 0, make([]byte, 32))
- slrr.SignedLogRoot.LogRoot = slrr.SignedLogRoot.LogRoot[1:]
- return slrr
-}
-
-// marshalSignedLogRoot must marshal a signed log root
-func marshalSignedLogRoot(t *testing.T, lr *types.LogRootV1) *trillian.SignedLogRoot {
- t.Helper()
- rootBytes, err := lr.MarshalBinary()
- if err != nil {
- t.Fatalf("failed to marshal root in test: %v", err)
- }
- return &trillian.SignedLogRoot{
- KeyHint: nil, // not used by stfe
- LogRoot: rootBytes,
- LogRootSignature: nil, // not used by stfe
- }
-}
diff --git a/server/testdata/type.go b/server/testdata/type.go
deleted file mode 100644
index 93041c9..0000000
--- a/server/testdata/type.go
+++ /dev/null
@@ -1,32 +0,0 @@
-package testdata
-
-import (
- "context"
- "fmt"
-
- "github.com/golang/mock/gomock"
-)
-
-// DeadlineMatcher implements gomock.Matcher, such that an error is raised if
-// there is no context.Context deadline set
-type DeadlineMatcher struct{}
-
-// NewDeadlineMatcher returns a new DeadlineMatcher
-func NewDeadlineMatcher() gomock.Matcher {
- return &DeadlineMatcher{}
-}
-
-// Matches returns true if the passed interface is a context with a deadline
-func (dm *DeadlineMatcher) Matches(i interface{}) bool {
- ctx, ok := i.(context.Context)
- if !ok {
- return false
- }
- _, ok = ctx.Deadline()
- return ok
-}
-
-// String is needed to implement gomock.Matcher
-func (dm *DeadlineMatcher) String() string {
- return fmt.Sprintf("deadlineMatcher{}")
-}
diff --git a/trillian.go b/trillian.go
index 8ae96a1..5fab6f5 100644
--- a/trillian.go
+++ b/trillian.go
@@ -11,15 +11,15 @@ import (
"google.golang.org/grpc/codes"
)
-func checkQueueLeaf(rsp *trillian.QueueLeafResponse, err error) (int, error) {
+func checkQueueLeaf(rsp *trillian.QueueLeafResponse, err error) error {
if err != nil || rsp == nil || rsp.QueuedLeaf == nil {
- return http.StatusInternalServerError, fmt.Errorf("%v", err)
+ return fmt.Errorf("%v", err)
}
if codes.Code(rsp.QueuedLeaf.GetStatus().GetCode()) == codes.AlreadyExists {
// no need to report this as an invalid request, just (re)issue sdi
glog.V(3).Infof("queued leaf is a duplicate => %X", rsp.QueuedLeaf.Leaf.LeafValue)
}
- return 0, nil
+ return nil
}
func checkGetLeavesByRange(req *GetEntriesRequest, rsp *trillian.GetLeavesByRangeResponse, err error) (int, error) {
@@ -62,17 +62,17 @@ func checkGetConsistencyProof(lp *LogParameters, rsp *trillian.GetConsistencyPro
return checkHashPath(lp.HashType.Size(), rsp.Proof.Hashes)
}
-func checkGetLatestSignedLogRoot(lp *LogParameters, rsp *trillian.GetLatestSignedLogRootResponse, err error, out *types.LogRootV1) (int, error) {
+func checkGetLatestSignedLogRoot(lp *LogParameters, rsp *trillian.GetLatestSignedLogRootResponse, err error, out *types.LogRootV1) error {
if err != nil || rsp == nil || rsp.SignedLogRoot == nil || rsp.SignedLogRoot.LogRoot == nil {
- return http.StatusInternalServerError, fmt.Errorf("%v", err)
+ return fmt.Errorf("%v", err)
}
if err := out.UnmarshalBinary(rsp.SignedLogRoot.LogRoot); err != nil {
- return http.StatusInternalServerError, fmt.Errorf("cannot unmarshal log root: %v", err)
+ return fmt.Errorf("cannot unmarshal log root: %v", err)
}
if len(out.RootHash) != lp.HashType.Size() {
- return http.StatusInternalServerError, fmt.Errorf("invalid root hash: %v", out.RootHash)
+ return fmt.Errorf("invalid root hash: %v", out.RootHash)
}
- return 0, nil
+ return nil
}
func checkHashPath(hashSize int, path [][]byte) (int, error) {
diff --git a/trillian_test.go b/trillian_test.go
index 174fa13..6c9b7af 100644
--- a/trillian_test.go
+++ b/trillian_test.go
@@ -6,18 +6,139 @@ import (
"github.com/google/trillian"
"github.com/google/trillian/types"
- "github.com/system-transparency/stfe/server/testdata"
+ "github.com/system-transparency/stfe/testdata"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
)
-// TODO: TestCheckQueueLeaf
func TestCheckQueueLeaf(t *testing.T) {
+ for _, table := range []struct {
+ description string
+ rsp *trillian.QueueLeafResponse
+ err error
+ wantErr bool
+ }{
+ {
+ description: "trillian error",
+ err: fmt.Errorf("backend error"),
+ wantErr: true,
+ },
+ {
+ description: "empty trillian response",
+ wantErr: true,
+ },
+ {
+ description: "partial trillian response: empty QueuedLeaf field",
+ rsp: &trillian.QueueLeafResponse{},
+ wantErr: true,
+ },
+ {
+ description: "ok: duplicate leaf",
+ rsp: makeTrillianQueueLeafResponse(t, testPackage, testdata.FirstPemChain, testdata.FirstPemChainKey, true),
+ },
+ {
+ description: "ok: new leaf",
+ rsp: makeTrillianQueueLeafResponse(t, testPackage, testdata.FirstPemChain, testdata.FirstPemChainKey, false),
+ },
+ } {
+ if err := checkQueueLeaf(table.rsp, table.err); (err != nil) != table.wantErr {
+ t.Errorf("got error %v, but wanted error %v in test %q", err, table.wantErr, table.description)
+ }
+ }
}
-// TODO: TestCheckGetLeavesByRange
func TestCheckGetLeavesByRange(t *testing.T) {
+ // rsp without leaves
+ noLeaves := makeTrillianGetLeavesByRangeResponse(t, 0, 1, testPackage, testdata.FirstPemChain, testdata.FirstPemChainKey, true)
+ noLeaves.Leaves = nil
+
+ // rsp without signed log root
+ noSlr := makeTrillianGetLeavesByRangeResponse(t, 0, 1, testPackage, testdata.FirstPemChain, testdata.FirstPemChainKey, true)
+ noSlr.SignedLogRoot = nil
+
+ // rsp without log root
+ noLr := makeTrillianGetLeavesByRangeResponse(t, 0, 1, testPackage, testdata.FirstPemChain, testdata.FirstPemChainKey, true)
+ noLr.SignedLogRoot.LogRoot = nil
+
+ // rsp with root that cannot be unmarshalled
+ tr := makeTrillianGetLeavesByRangeResponse(t, 0, 1, testPackage, testdata.FirstPemChain, testdata.FirstPemChainKey, true)
+ tr.SignedLogRoot.LogRoot = tr.SignedLogRoot.LogRoot[1:]
+
+ // rsp with fixed tree size
+ fixedSize := makeTrillianGetLeavesByRangeResponse(t, int64(testTreeSize)-1, int64(testTreeSize)-1, testPackage, testdata.FirstPemChain, testdata.FirstPemChainKey, true)
+ fixedSize.SignedLogRoot = makeLatestSignedLogRootResponse(t, 0, testTreeSize, testNodeHash).SignedLogRoot
+
+ for _, table := range []struct {
+ description string
+ req *GetEntriesRequest
+ rsp *trillian.GetLeavesByRangeResponse
+ err error
+ wantErr bool
+ }{
+ {
+ description: "trillian error",
+ err: fmt.Errorf("backend error"),
+ wantErr: true,
+ },
+ {
+ description: "empty trillian response",
+ wantErr: true,
+ },
+ {
+ description: "partial trillian response: no leaves",
+ rsp: noLeaves,
+ wantErr: true,
+ },
+ {
+ description: "partial trillian response: no signed log root",
+ rsp: noSlr,
+ wantErr: true,
+ },
+ {
+ description: "partial trillian response: no log root",
+ rsp: noLr,
+ wantErr: true,
+ },
+ {
+ description: "bad response: too many leaves",
+ req: &GetEntriesRequest{Start: 0, End: 1},
+ rsp: makeTrillianGetLeavesByRangeResponse(t, 0, 2, testPackage, testdata.FirstPemChain, testdata.FirstPemChainKey, true),
+ wantErr: true,
+ },
+ {
+ description: "bad response: too many leaves",
+ req: &GetEntriesRequest{Start: 0, End: 1},
+ rsp: tr,
+ wantErr: true,
+ },
+ {
+ description: "bad response: start is not a valid index",
+ req: &GetEntriesRequest{Start: int64(testTreeSize), End: int64(testTreeSize)},
+ rsp: fixedSize,
+ wantErr: true,
+ },
+ {
+ description: "ok response: interval refers to the latest leaf",
+ req: &GetEntriesRequest{Start: int64(testTreeSize) - 1, End: int64(testTreeSize) - 1},
+ rsp: fixedSize,
+ },
+ {
+ description: "bad response: invalid leaf indices",
+ req: &GetEntriesRequest{Start: 10, End: 11},
+ rsp: makeTrillianGetLeavesByRangeResponse(t, 11, 12, testPackage, testdata.FirstPemChain, testdata.FirstPemChainKey, true),
+ wantErr: true,
+ },
+ {
+ description: "ok response: a bunch of leaves",
+ req: &GetEntriesRequest{Start: 10, End: 20},
+ rsp: makeTrillianGetLeavesByRangeResponse(t, 10, 20, testPackage, testdata.FirstPemChain, testdata.FirstPemChainKey, true),
+ },
+ } {
+ if _, err := checkGetLeavesByRange(table.req, table.rsp, table.err); (err != nil) != table.wantErr {
+ t.Errorf("got error %v, but wanted error %v in test %q", err, table.wantErr, table.description)
+ }
+ }
}
// TODO: TestCheckGetInclusionProofByHash
@@ -28,8 +149,61 @@ func TestCheckGetInclusionProofByHash(t *testing.T) {
func TestCheckGetConsistencyProof(t *testing.T) {
}
-// TODO: TestCheckGetLatestSignedLogRoot
func TestCheckGetLatestSignedLogRoot(t *testing.T) {
+ // response with no log root
+ noLr := makeLatestSignedLogRootResponse(t, 0, 0, testNodeHash)
+ noLr.SignedLogRoot.LogRoot = nil
+
+ // response with truncated log root
+ tr := makeLatestSignedLogRootResponse(t, 0, 0, testNodeHash)
+ tr.SignedLogRoot.LogRoot = tr.SignedLogRoot.LogRoot[1:]
+
+ lp := makeTestLogParameters(t, nil)
+ for _, table := range []struct {
+ description string
+ rsp *trillian.GetLatestSignedLogRootResponse
+ err error
+ wantErr bool
+ }{
+ {
+ description: "bad trillian response: error",
+ err: fmt.Errorf("backend failure"),
+ wantErr: true,
+ },
+ {
+ description: "bad trillian response: empty",
+ wantErr: true,
+ },
+ {
+ description: "bad trillian response: no signed log root",
+ rsp: &trillian.GetLatestSignedLogRootResponse{SignedLogRoot: nil},
+ wantErr: true,
+ },
+ {
+ description: "bad trillian response: no log root",
+ rsp: noLr,
+ wantErr: true,
+ },
+ {
+ description: "bad trillian response: truncated log root",
+ rsp: tr,
+ wantErr: true,
+ },
+ {
+ description: "bad trillian response: invalid root hash size",
+ rsp: makeLatestSignedLogRootResponse(t, 0, 0, make([]byte, 31)),
+ wantErr: true,
+ },
+ {
+ description: "ok response",
+ rsp: makeLatestSignedLogRootResponse(t, 0, 0, testNodeHash),
+ },
+ } {
+ var lr types.LogRootV1
+ if err := checkGetLatestSignedLogRoot(lp, table.rsp, table.err, &lr); (err != nil) != table.wantErr {
+ t.Errorf("got error %v, but wanted error %v in test %q", err, table.wantErr, table.description)
+ }
+ }
}
// makeTrillianQueueLeafResponse creates a valid trillian QueueLeafResponse
@@ -37,9 +211,13 @@ func TestCheckGetLatestSignedLogRoot(t *testing.T) {
// is a PEM-encoded ed25519 signing key, and pemChain its certificate chain.
//
// Note: MerkleLeafHash and LeafIdentityHash are unset (not used by stfe).
-func makeTrillianQueueLeafResponse(t *testing.T, name, pemChain, pemKey []byte) *trillian.QueueLeafResponse {
+func makeTrillianQueueLeafResponse(t *testing.T, name, pemChain, pemKey []byte, dupCode bool) *trillian.QueueLeafResponse {
t.Helper()
leaf, appendix := makeTestLeaf(t, name, pemChain, pemKey)
+ s := status.New(codes.OK, "ok").Proto()
+ if dupCode {
+ s = status.New(codes.AlreadyExists, "duplicate").Proto()
+ }
return &trillian.QueueLeafResponse{
QueuedLeaf: &trillian.QueuedLogLeaf{
Leaf: &trillian.LogLeaf{
@@ -49,7 +227,7 @@ func makeTrillianQueueLeafResponse(t *testing.T, name, pemChain, pemKey []byte)
LeafIndex: 0, // not applicable (log is not pre-ordered)
LeafIdentityHash: nil, // not used by stfe
},
- Status: status.New(codes.OK, "ok").Proto(),
+ Status: s,
},
}
}
@@ -94,7 +272,7 @@ func makeTrillianGetConsistencyProofResponse(t *testing.T, path [][]byte) *trill
// Note: MerkleLeafHash and LeafIdentityHash are unset (not used by stfe).
func makeTrillianGetLeavesByRangeResponse(t *testing.T, start, end int64, name, pemChain, pemKey []byte, valid bool) *trillian.GetLeavesByRangeResponse {
t.Helper()
- leaves := make([]*trillian.LogLeaf, 0, start-end+1)
+ leaves := make([]*trillian.LogLeaf, 0, end-start+1)
for i, n := start, end+1; i < n; i++ {
leaf, appendix := makeTestLeaf(t, append(name, []byte(fmt.Sprintf("_%d", i))...), pemChain, pemKey)
if !valid {
@@ -110,11 +288,13 @@ func makeTrillianGetLeavesByRangeResponse(t *testing.T, start, end int64, name,
}
return &trillian.GetLeavesByRangeResponse{
Leaves: leaves,
- SignedLogRoot: testdata.NewGetLatestSignedLogRootResponse(t, 0, uint64(end)+1, make([]byte, 32)).SignedLogRoot,
+ SignedLogRoot: makeLatestSignedLogRootResponse(t, 0, uint64(end)+1, make([]byte, 32)).SignedLogRoot,
}
}
+// makeTrillianLogRoot: docdoc
func makeTrillianLogRoot(t *testing.T, timestamp, size uint64, hash []byte) *types.LogRootV1 {
+ t.Helper()
return &types.LogRootV1{
TreeSize: size,
RootHash: hash,
@@ -123,3 +303,21 @@ func makeTrillianLogRoot(t *testing.T, timestamp, size uint64, hash []byte) *typ
Metadata: nil, // not used by stfe
}
}
+
+// makeLatestSignedLogRootResponse creates a new trillian STH. Revision,
+// Metadata, Proof, KeyHint, and LogRootSignature are unsset.
+func makeLatestSignedLogRootResponse(t *testing.T, timestamp, size uint64, hash []byte) *trillian.GetLatestSignedLogRootResponse {
+ t.Helper()
+ rootBytes, err := makeTrillianLogRoot(t, timestamp, size, hash).MarshalBinary()
+ if err != nil {
+ t.Fatalf("failed to marshal root in test: %v", err)
+ }
+ return &trillian.GetLatestSignedLogRootResponse{
+ SignedLogRoot: &trillian.SignedLogRoot{
+ KeyHint: nil, // not used by stfe
+ LogRoot: rootBytes,
+ LogRootSignature: nil, // not used by stfe
+ },
+ Proof: nil, // not used by stfe
+ }
+}
diff --git a/type_test.go b/type_test.go
index 6b292ed..e249fb6 100644
--- a/type_test.go
+++ b/type_test.go
@@ -5,7 +5,7 @@ import (
"crypto/tls"
- "github.com/system-transparency/stfe/server/testdata"
+ "github.com/system-transparency/stfe/testdata"
"github.com/system-transparency/stfe/x509util"
)
@@ -275,7 +275,7 @@ func TestEncDecStItem(t *testing.T) {
//
// TODO: max limits for certificate chains are not tested.
func TestEncDecAppendix(t *testing.T) {
- chain, err := x509util.NewCertificateList(testdata.PemChain)
+ chain, err := x509util.NewCertificateList(testdata.FirstPemChain)
if err != nil {
t.Fatalf("must decode certificate chain: %v", err)
}
@@ -398,7 +398,7 @@ func TestStItemUnmarshalFailure(t *testing.T) {
// TestAppendixUnmarshal tests that invalid appendices cannot be unmarshaled
func TestAppendixUnmarshalFailure(t *testing.T) {
- chain, err := x509util.NewCertificateList(testdata.PemChain)
+ chain, err := x509util.NewCertificateList(testdata.FirstPemChain)
if err != nil {
t.Fatalf("must decode certificate chain: %v", err)
}