aboutsummaryrefslogtreecommitdiff
path: root/pkg
diff options
context:
space:
mode:
Diffstat (limited to 'pkg')
-rw-r--r--pkg/db/client.go17
-rw-r--r--pkg/db/mocks/client.go (renamed from pkg/mocks/sigsum_trillian_client.go)15
-rw-r--r--pkg/db/mocks/trillian.go (renamed from pkg/mocks/trillian_log_client.go)0
-rw-r--r--pkg/db/trillian.go (renamed from pkg/trillian/client.go)67
-rw-r--r--pkg/db/trillian_test.go (renamed from pkg/trillian/client_test.go)93
-rw-r--r--pkg/dns/dns.go11
-rw-r--r--pkg/dns/mocks/dns.go (renamed from pkg/mocks/sigsum_dns.go)3
-rw-r--r--pkg/instance/experimental.go (renamed from pkg/instance/experimental_endpoint.go)2
-rw-r--r--pkg/instance/handler.go (renamed from pkg/instance/endpoint.go)63
-rw-r--r--pkg/instance/handler_test.go (renamed from pkg/instance/endpoint_test.go)271
-rw-r--r--pkg/instance/instance.go104
-rw-r--r--pkg/instance/instance_test.go98
-rw-r--r--pkg/state/mocks/signer.go (renamed from pkg/mocks/crypto.go)4
-rw-r--r--pkg/state/mocks/state_manager.go (renamed from pkg/mocks/sigsum_state_manager.go)4
-rw-r--r--pkg/state/single.go156
-rw-r--r--pkg/state/single_test.go (renamed from pkg/state/state_manager_test.go)233
-rw-r--r--pkg/state/state_manager.go148
-rw-r--r--pkg/trillian/util.go33
-rw-r--r--pkg/types/ascii.go399
-rw-r--r--pkg/types/ascii_test.go438
-rw-r--r--pkg/types/trunnel.go63
-rw-r--r--pkg/types/trunnel_test.go116
-rw-r--r--pkg/types/types.go138
-rw-r--r--pkg/types/types_test.go58
-rw-r--r--pkg/types/util.go21
25 files changed, 661 insertions, 1894 deletions
diff --git a/pkg/db/client.go b/pkg/db/client.go
new file mode 100644
index 0000000..090dfff
--- /dev/null
+++ b/pkg/db/client.go
@@ -0,0 +1,17 @@
+package db
+
+import (
+ "context"
+
+ "git.sigsum.org/sigsum-lib-go/pkg/requests"
+ "git.sigsum.org/sigsum-lib-go/pkg/types"
+)
+
+// Client is an interface that interacts with a log's database backend
+type Client interface {
+ AddLeaf(context.Context, *requests.Leaf) error
+ GetTreeHead(context.Context) (*types.TreeHead, error)
+ GetConsistencyProof(context.Context, *requests.ConsistencyProof) (*types.ConsistencyProof, error)
+ GetInclusionProof(context.Context, *requests.InclusionProof) (*types.InclusionProof, error)
+ GetLeaves(context.Context, *requests.Leaves) (*types.Leaves, error)
+}
diff --git a/pkg/mocks/sigsum_trillian_client.go b/pkg/db/mocks/client.go
index 3397237..182869f 100644
--- a/pkg/mocks/sigsum_trillian_client.go
+++ b/pkg/db/mocks/client.go
@@ -1,5 +1,5 @@
// Code generated by MockGen. DO NOT EDIT.
-// Source: git.sigsum.org/sigsum-log-go/pkg/trillian (interfaces: Client)
+// Source: git.sigsum.org/sigsum-log-go/pkg/db (interfaces: Client)
// Package mocks is a generated GoMock package.
package mocks
@@ -8,8 +8,9 @@ import (
context "context"
reflect "reflect"
+ requests "git.sigsum.org/sigsum-lib-go/pkg/requests"
+ types "git.sigsum.org/sigsum-lib-go/pkg/types"
gomock "github.com/golang/mock/gomock"
- types "git.sigsum.org/sigsum-log-go/pkg/types"
)
// MockClient is a mock of Client interface.
@@ -36,7 +37,7 @@ func (m *MockClient) EXPECT() *MockClientMockRecorder {
}
// AddLeaf mocks base method.
-func (m *MockClient) AddLeaf(arg0 context.Context, arg1 *types.LeafRequest) error {
+func (m *MockClient) AddLeaf(arg0 context.Context, arg1 *requests.Leaf) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "AddLeaf", arg0, arg1)
ret0, _ := ret[0].(error)
@@ -50,7 +51,7 @@ func (mr *MockClientMockRecorder) AddLeaf(arg0, arg1 interface{}) *gomock.Call {
}
// GetConsistencyProof mocks base method.
-func (m *MockClient) GetConsistencyProof(arg0 context.Context, arg1 *types.ConsistencyProofRequest) (*types.ConsistencyProof, error) {
+func (m *MockClient) GetConsistencyProof(arg0 context.Context, arg1 *requests.ConsistencyProof) (*types.ConsistencyProof, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "GetConsistencyProof", arg0, arg1)
ret0, _ := ret[0].(*types.ConsistencyProof)
@@ -65,7 +66,7 @@ func (mr *MockClientMockRecorder) GetConsistencyProof(arg0, arg1 interface{}) *g
}
// GetInclusionProof mocks base method.
-func (m *MockClient) GetInclusionProof(arg0 context.Context, arg1 *types.InclusionProofRequest) (*types.InclusionProof, error) {
+func (m *MockClient) GetInclusionProof(arg0 context.Context, arg1 *requests.InclusionProof) (*types.InclusionProof, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "GetInclusionProof", arg0, arg1)
ret0, _ := ret[0].(*types.InclusionProof)
@@ -80,10 +81,10 @@ func (mr *MockClientMockRecorder) GetInclusionProof(arg0, arg1 interface{}) *gom
}
// GetLeaves mocks base method.
-func (m *MockClient) GetLeaves(arg0 context.Context, arg1 *types.LeavesRequest) (*types.LeafList, error) {
+func (m *MockClient) GetLeaves(arg0 context.Context, arg1 *requests.Leaves) (*types.Leaves, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "GetLeaves", arg0, arg1)
- ret0, _ := ret[0].(*types.LeafList)
+ ret0, _ := ret[0].(*types.Leaves)
ret1, _ := ret[1].(error)
return ret0, ret1
}
diff --git a/pkg/mocks/trillian_log_client.go b/pkg/db/mocks/trillian.go
index 8aa3a58..8aa3a58 100644
--- a/pkg/mocks/trillian_log_client.go
+++ b/pkg/db/mocks/trillian.go
diff --git a/pkg/trillian/client.go b/pkg/db/trillian.go
index 6fe8ce4..ab57db6 100644
--- a/pkg/trillian/client.go
+++ b/pkg/db/trillian.go
@@ -1,25 +1,18 @@
-package trillian
+package db
import (
"context"
"fmt"
+ "git.sigsum.org/sigsum-lib-go/pkg/requests"
+ "git.sigsum.org/sigsum-lib-go/pkg/types"
"github.com/golang/glog"
"github.com/google/trillian"
- ttypes "github.com/google/trillian/types"
- "git.sigsum.org/sigsum-log-go/pkg/types"
+ trillianTypes "github.com/google/trillian/types"
"google.golang.org/grpc/codes"
)
-type Client interface {
- AddLeaf(context.Context, *types.LeafRequest) error
- GetConsistencyProof(context.Context, *types.ConsistencyProofRequest) (*types.ConsistencyProof, error)
- GetTreeHead(context.Context) (*types.TreeHead, error)
- GetInclusionProof(context.Context, *types.InclusionProofRequest) (*types.InclusionProof, error)
- GetLeaves(context.Context, *types.LeavesRequest) (*types.LeafList, error)
-}
-
-// TrillianClient is a wrapper around the Trillian gRPC client.
+// TrillianClient implements the Client interface for Trillian's gRPC backend
type TrillianClient struct {
// TreeID is a Merkle tree identifier that Trillian uses
TreeID int64
@@ -28,17 +21,18 @@ type TrillianClient struct {
GRPC trillian.TrillianLogClient
}
-func (c *TrillianClient) AddLeaf(ctx context.Context, req *types.LeafRequest) error {
+func (c *TrillianClient) AddLeaf(ctx context.Context, req *requests.Leaf) error {
leaf := types.Leaf{
- Message: req.Message,
- SigIdent: types.SigIdent{
- Signature: req.Signature,
- KeyHash: types.Hash(req.VerificationKey[:]),
+ Statement: types.Statement{
+ ShardHint: req.ShardHint,
+ Checksum: req.Checksum,
},
+ Signature: req.Signature,
+ KeyHash: *types.HashFn(req.VerificationKey[:]),
}
- serialized := leaf.Marshal()
+ serialized := leaf.ToBinary()
- glog.V(3).Infof("queueing leaf request: %x", types.HashLeaf(serialized))
+ glog.V(3).Infof("queueing leaf request: %x", types.LeafHash(serialized))
rsp, err := c.GRPC.QueueLeaf(ctx, &trillian.QueueLeafRequest{
LogId: c.TreeID,
Leaf: &trillian.LogLeaf{
@@ -76,7 +70,7 @@ func (c *TrillianClient) GetTreeHead(ctx context.Context) (*types.TreeHead, erro
if rsp.SignedLogRoot.LogRoot == nil {
return nil, fmt.Errorf("no log root")
}
- var r ttypes.LogRootV1
+ var r trillianTypes.LogRootV1
if err := r.UnmarshalBinary(rsp.SignedLogRoot.LogRoot); err != nil {
return nil, fmt.Errorf("no log root: unmarshal failed: %v", err)
}
@@ -86,7 +80,7 @@ func (c *TrillianClient) GetTreeHead(ctx context.Context) (*types.TreeHead, erro
return treeHeadFromLogRoot(&r), nil
}
-func (c *TrillianClient) GetConsistencyProof(ctx context.Context, req *types.ConsistencyProofRequest) (*types.ConsistencyProof, error) {
+func (c *TrillianClient) GetConsistencyProof(ctx context.Context, req *requests.ConsistencyProof) (*types.ConsistencyProof, error) {
rsp, err := c.GRPC.GetConsistencyProof(ctx, &trillian.GetConsistencyProofRequest{
LogId: c.TreeID,
FirstTreeSize: int64(req.OldSize),
@@ -115,7 +109,7 @@ func (c *TrillianClient) GetConsistencyProof(ctx context.Context, req *types.Con
}, nil
}
-func (c *TrillianClient) GetInclusionProof(ctx context.Context, req *types.InclusionProofRequest) (*types.InclusionProof, error) {
+func (c *TrillianClient) GetInclusionProof(ctx context.Context, req *requests.InclusionProof) (*types.InclusionProof, error) {
rsp, err := c.GRPC.GetInclusionProofByHash(ctx, &trillian.GetInclusionProofByHashRequest{
LogId: c.TreeID,
LeafHash: req.LeafHash[:],
@@ -146,7 +140,7 @@ func (c *TrillianClient) GetInclusionProof(ctx context.Context, req *types.Inclu
}, nil
}
-func (c *TrillianClient) GetLeaves(ctx context.Context, req *types.LeavesRequest) (*types.LeafList, error) {
+func (c *TrillianClient) GetLeaves(ctx context.Context, req *requests.Leaves) (*types.Leaves, error) {
rsp, err := c.GRPC.GetLeavesByRange(ctx, &trillian.GetLeavesByRangeRequest{
LogId: c.TreeID,
StartIndex: int64(req.StartSize),
@@ -161,7 +155,7 @@ func (c *TrillianClient) GetLeaves(ctx context.Context, req *types.LeavesRequest
if got, want := len(rsp.Leaves), int(req.EndSize-req.StartSize+1); got != want {
return nil, fmt.Errorf("unexpected number of leaves: %d", got)
}
- var list types.LeafList
+ var list types.Leaves = make([]types.Leaf, 0, len(rsp.Leaves))
for i, leaf := range rsp.Leaves {
leafIndex := int64(req.StartSize + uint64(i))
if leafIndex != leaf.LeafIndex {
@@ -169,10 +163,31 @@ func (c *TrillianClient) GetLeaves(ctx context.Context, req *types.LeavesRequest
}
var l types.Leaf
- if err := l.Unmarshal(leaf.LeafValue); err != nil {
+ if err := l.FromBinary(leaf.LeafValue); err != nil {
return nil, fmt.Errorf("unexpected leaf(%d): %v", leafIndex, err)
}
- list = append(list[:], &l)
+ list = append(list[:], l)
}
return &list, nil
}
+
+func treeHeadFromLogRoot(lr *trillianTypes.LogRootV1) *types.TreeHead {
+ th := types.TreeHead{
+ Timestamp: uint64(lr.TimestampNanos / 1000 / 1000 / 1000),
+ TreeSize: uint64(lr.TreeSize),
+ }
+ copy(th.RootHash[:], lr.RootHash)
+ return &th
+}
+
+func nodePathFromHashes(hashes [][]byte) ([]types.Hash, error) {
+ path := make([]types.Hash, len(hashes))
+ for i := 0; i < len(hashes); i++ {
+ if len(hashes[i]) != types.HashSize {
+ return nil, fmt.Errorf("unexpected hash length: %v", len(hashes[i]))
+ }
+
+ copy(path[i][:], hashes[i])
+ }
+ return path, nil
+}
diff --git a/pkg/trillian/client_test.go b/pkg/db/trillian_test.go
index 143c411..a33458f 100644
--- a/pkg/trillian/client_test.go
+++ b/pkg/db/trillian_test.go
@@ -1,4 +1,4 @@
-package trillian
+package db
import (
"context"
@@ -6,28 +6,29 @@ import (
"reflect"
"testing"
+ "git.sigsum.org/sigsum-lib-go/pkg/requests"
+ "git.sigsum.org/sigsum-lib-go/pkg/types"
+ "git.sigsum.org/sigsum-log-go/pkg/db/mocks"
"github.com/golang/mock/gomock"
"github.com/google/trillian"
ttypes "github.com/google/trillian/types"
- "git.sigsum.org/sigsum-log-go/pkg/mocks"
- "git.sigsum.org/sigsum-log-go/pkg/types"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
)
func TestAddLeaf(t *testing.T) {
- req := &types.LeafRequest{
- Message: types.Message{
+ req := &requests.Leaf{
+ Statement: types.Statement{
ShardHint: 0,
- Checksum: &[types.HashSize]byte{},
+ Checksum: types.Hash{},
},
- Signature: &[types.SignatureSize]byte{},
- VerificationKey: &[types.VerificationKeySize]byte{},
+ Signature: types.Signature{},
+ VerificationKey: types.PublicKey{},
DomainHint: "example.com",
}
for _, table := range []struct {
description string
- req *types.LeafRequest
+ req *requests.Leaf
rsp *trillian.QueueLeafResponse
err error
wantErr bool
@@ -55,7 +56,7 @@ func TestAddLeaf(t *testing.T) {
rsp: &trillian.QueueLeafResponse{
QueuedLeaf: &trillian.QueuedLogLeaf{
Leaf: &trillian.LogLeaf{
- LeafValue: req.Message.Marshal(),
+ LeafValue: []byte{0}, // does not matter for test
},
Status: status.New(codes.AlreadyExists, "duplicate").Proto(),
},
@@ -68,7 +69,7 @@ func TestAddLeaf(t *testing.T) {
rsp: &trillian.QueueLeafResponse{
QueuedLeaf: &trillian.QueuedLogLeaf{
Leaf: &trillian.LogLeaf{
- LeafValue: req.Message.Marshal(),
+ LeafValue: []byte{0}, // does not matter for test
},
Status: status.New(codes.OK, "ok").Proto(),
},
@@ -165,7 +166,7 @@ func TestGetTreeHead(t *testing.T) {
wantTh: &types.TreeHead{
Timestamp: 1622585623,
TreeSize: 0,
- RootHash: &[types.HashSize]byte{},
+ RootHash: types.Hash{},
},
},
} {
@@ -192,13 +193,13 @@ func TestGetTreeHead(t *testing.T) {
}
func TestGetConsistencyProof(t *testing.T) {
- req := &types.ConsistencyProofRequest{
+ req := &requests.ConsistencyProof{
OldSize: 1,
NewSize: 3,
}
for _, table := range []struct {
description string
- req *types.ConsistencyProofRequest
+ req *requests.ConsistencyProof
rsp *trillian.GetConsistencyProofResponse
err error
wantErr bool
@@ -258,9 +259,9 @@ func TestGetConsistencyProof(t *testing.T) {
wantProof: &types.ConsistencyProof{
OldSize: 1,
NewSize: 3,
- Path: []*[types.HashSize]byte{
- &[types.HashSize]byte{},
- &[types.HashSize]byte{},
+ Path: []types.Hash{
+ types.Hash{},
+ types.Hash{},
},
},
},
@@ -288,13 +289,13 @@ func TestGetConsistencyProof(t *testing.T) {
}
func TestGetInclusionProof(t *testing.T) {
- req := &types.InclusionProofRequest{
+ req := &requests.InclusionProof{
TreeSize: 4,
- LeafHash: &[types.HashSize]byte{},
+ LeafHash: types.Hash{},
}
for _, table := range []struct {
description string
- req *types.InclusionProofRequest
+ req *requests.InclusionProof
rsp *trillian.GetInclusionProofByHashResponse
err error
wantErr bool
@@ -368,9 +369,9 @@ func TestGetInclusionProof(t *testing.T) {
wantProof: &types.InclusionProof{
TreeSize: 4,
LeafIndex: 1,
- Path: []*[types.HashSize]byte{
- &[types.HashSize]byte{},
- &[types.HashSize]byte{},
+ Path: []types.Hash{
+ types.Hash{},
+ types.Hash{},
},
},
},
@@ -398,38 +399,34 @@ func TestGetInclusionProof(t *testing.T) {
}
func TestGetLeaves(t *testing.T) {
- req := &types.LeavesRequest{
+ req := &requests.Leaves{
StartSize: 1,
EndSize: 2,
}
firstLeaf := &types.Leaf{
- Message: types.Message{
+ Statement: types.Statement{
ShardHint: 0,
- Checksum: &[types.HashSize]byte{},
- },
- SigIdent: types.SigIdent{
- Signature: &[types.SignatureSize]byte{},
- KeyHash: &[types.HashSize]byte{},
+ Checksum: types.Hash{},
},
+ Signature: types.Signature{},
+ KeyHash: types.Hash{},
}
secondLeaf := &types.Leaf{
- Message: types.Message{
+ Statement: types.Statement{
ShardHint: 0,
- Checksum: &[types.HashSize]byte{},
- },
- SigIdent: types.SigIdent{
- Signature: &[types.SignatureSize]byte{},
- KeyHash: &[types.HashSize]byte{},
+ Checksum: types.Hash{},
},
+ Signature: types.Signature{},
+ KeyHash: types.Hash{},
}
for _, table := range []struct {
description string
- req *types.LeavesRequest
+ req *requests.Leaves
rsp *trillian.GetLeavesByRangeResponse
err error
wantErr bool
- wantLeaves *types.LeafList
+ wantLeaves *types.Leaves
}{
{
description: "invalid: backend failure",
@@ -448,7 +445,7 @@ func TestGetLeaves(t *testing.T) {
rsp: &trillian.GetLeavesByRangeResponse{
Leaves: []*trillian.LogLeaf{
&trillian.LogLeaf{
- LeafValue: firstLeaf.Marshal(),
+ LeafValue: firstLeaf.ToBinary(),
LeafIndex: 1,
},
},
@@ -461,11 +458,11 @@ func TestGetLeaves(t *testing.T) {
rsp: &trillian.GetLeavesByRangeResponse{
Leaves: []*trillian.LogLeaf{
&trillian.LogLeaf{
- LeafValue: firstLeaf.Marshal(),
+ LeafValue: firstLeaf.ToBinary(),
LeafIndex: 1,
},
&trillian.LogLeaf{
- LeafValue: secondLeaf.Marshal(),
+ LeafValue: secondLeaf.ToBinary(),
LeafIndex: 3,
},
},
@@ -478,11 +475,11 @@ func TestGetLeaves(t *testing.T) {
rsp: &trillian.GetLeavesByRangeResponse{
Leaves: []*trillian.LogLeaf{
&trillian.LogLeaf{
- LeafValue: firstLeaf.Marshal(),
+ LeafValue: firstLeaf.ToBinary(),
LeafIndex: 1,
},
&trillian.LogLeaf{
- LeafValue: secondLeaf.Marshal()[1:],
+ LeafValue: secondLeaf.ToBinary()[1:],
LeafIndex: 2,
},
},
@@ -495,18 +492,18 @@ func TestGetLeaves(t *testing.T) {
rsp: &trillian.GetLeavesByRangeResponse{
Leaves: []*trillian.LogLeaf{
&trillian.LogLeaf{
- LeafValue: firstLeaf.Marshal(),
+ LeafValue: firstLeaf.ToBinary(),
LeafIndex: 1,
},
&trillian.LogLeaf{
- LeafValue: secondLeaf.Marshal(),
+ LeafValue: secondLeaf.ToBinary(),
LeafIndex: 2,
},
},
},
- wantLeaves: &types.LeafList{
- firstLeaf,
- secondLeaf,
+ wantLeaves: &types.Leaves{
+ *firstLeaf,
+ *secondLeaf,
},
},
} {
diff --git a/pkg/dns/dns.go b/pkg/dns/dns.go
index 7979119..94cbdeb 100644
--- a/pkg/dns/dns.go
+++ b/pkg/dns/dns.go
@@ -5,14 +5,13 @@ import (
"fmt"
"net"
- "encoding/hex"
-
- "git.sigsum.org/sigsum-log-go/pkg/types"
+ "git.sigsum.org/sigsum-lib-go/pkg/hex"
+ "git.sigsum.org/sigsum-lib-go/pkg/types"
)
// Verifier can verify that a domain name is aware of a public key
type Verifier interface {
- Verify(ctx context.Context, name string, key *[types.VerificationKeySize]byte) error
+ Verify(ctx context.Context, name string, key *types.PublicKey) error
}
// DefaultResolver implements the Verifier interface with Go's default resolver
@@ -24,13 +23,13 @@ func NewDefaultResolver() Verifier {
return &DefaultResolver{}
}
-func (dr *DefaultResolver) Verify(ctx context.Context, name string, key *[types.VerificationKeySize]byte) error {
+func (dr *DefaultResolver) Verify(ctx context.Context, name string, key *types.PublicKey) error {
rsp, err := dr.resolver.LookupTXT(ctx, name)
if err != nil {
return fmt.Errorf("domain name look-up failed: %v", err)
}
- want := hex.EncodeToString(types.Hash(key[:])[:])
+ want := hex.Serialize(types.HashFn(key[:])[:])
for _, got := range rsp {
if got == want {
return nil
diff --git a/pkg/mocks/sigsum_dns.go b/pkg/dns/mocks/dns.go
index ede237e..c60082b 100644
--- a/pkg/mocks/sigsum_dns.go
+++ b/pkg/dns/mocks/dns.go
@@ -8,6 +8,7 @@ import (
context "context"
reflect "reflect"
+ types "git.sigsum.org/sigsum-lib-go/pkg/types"
gomock "github.com/golang/mock/gomock"
)
@@ -35,7 +36,7 @@ func (m *MockVerifier) EXPECT() *MockVerifierMockRecorder {
}
// Verify mocks base method.
-func (m *MockVerifier) Verify(arg0 context.Context, arg1 string, arg2 *[32]byte) error {
+func (m *MockVerifier) Verify(arg0 context.Context, arg1 string, arg2 *types.PublicKey) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "Verify", arg0, arg1, arg2)
ret0, _ := ret[0].(error)
diff --git a/pkg/instance/experimental_endpoint.go b/pkg/instance/experimental.go
index 2986a27..ab81ada 100644
--- a/pkg/instance/experimental_endpoint.go
+++ b/pkg/instance/experimental.go
@@ -11,7 +11,7 @@ import (
"fmt"
"net/http"
- "git.sigsum.org/sigsum-log-go/pkg/types"
+ "git.sigsum.org/sigsum-lib-go/pkg/types"
"github.com/golang/glog"
)
diff --git a/pkg/instance/endpoint.go b/pkg/instance/handler.go
index a6d424d..66a20a5 100644
--- a/pkg/instance/endpoint.go
+++ b/pkg/instance/handler.go
@@ -2,11 +2,58 @@ package instance
import (
"context"
+ "fmt"
"net/http"
+ "time"
+ "git.sigsum.org/sigsum-lib-go/pkg/types"
"github.com/golang/glog"
)
+// Handler implements the http.Handler interface, and contains a reference
+// to a sigsum server instance as well as a function that uses it.
+type Handler struct {
+ Instance *Instance
+ Endpoint types.Endpoint
+ Method string
+ Handler func(context.Context, *Instance, http.ResponseWriter, *http.Request) (int, error)
+}
+
+// Path returns a path that should be configured for this handler
+func (h Handler) Path() string {
+ if len(h.Instance.Prefix) == 0 {
+ return h.Endpoint.Path("", "sigsum", "v0")
+ }
+ return h.Endpoint.Path("", h.Instance.Prefix, "sigsum", "v0")
+}
+
+// ServeHTTP is part of the http.Handler interface
+func (a Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
+ // export prometheus metrics
+ var now time.Time = time.Now()
+ var statusCode int
+ defer func() {
+ rspcnt.Inc(a.Instance.LogID, string(a.Endpoint), fmt.Sprintf("%d", statusCode))
+ latency.Observe(time.Now().Sub(now).Seconds(), a.Instance.LogID, string(a.Endpoint), fmt.Sprintf("%d", statusCode))
+ }()
+ reqcnt.Inc(a.Instance.LogID, string(a.Endpoint))
+
+ ctx, cancel := context.WithDeadline(r.Context(), now.Add(a.Instance.Deadline))
+ defer cancel()
+
+ if r.Method != a.Method {
+ glog.Warningf("%s/%s: got HTTP %s, wanted HTTP %s", a.Instance.Prefix, string(a.Endpoint), r.Method, a.Method)
+ http.Error(w, "", http.StatusMethodNotAllowed)
+ return
+ }
+
+ statusCode, err := a.Handler(ctx, a.Instance, w, r)
+ if err != nil {
+ glog.Warningf("handler error %s/%s: %v", a.Instance.Prefix, a.Endpoint, err)
+ http.Error(w, fmt.Sprintf("Error=%s\n", err.Error()), statusCode)
+ }
+}
+
func addLeaf(ctx context.Context, i *Instance, w http.ResponseWriter, r *http.Request) (int, error) {
glog.V(3).Info("handling add-entry request")
req, err := i.leafRequestFromHTTP(ctx, r)
@@ -25,8 +72,8 @@ func addCosignature(ctx context.Context, i *Instance, w http.ResponseWriter, r *
if err != nil {
return http.StatusBadRequest, err
}
- vk := i.Witnesses[*req.KeyHash]
- if err := i.Stateman.AddCosignature(ctx, &vk, req.Signature); err != nil {
+ vk := i.Witnesses[req.KeyHash]
+ if err := i.Stateman.AddCosignature(ctx, &vk, &req.Cosignature); err != nil {
return http.StatusBadRequest, err
}
return http.StatusOK, nil
@@ -38,7 +85,7 @@ func getTreeHeadLatest(ctx context.Context, i *Instance, w http.ResponseWriter,
if err != nil {
return http.StatusInternalServerError, err
}
- if err := sth.MarshalASCII(w); err != nil {
+ if err := sth.ToASCII(w); err != nil {
return http.StatusInternalServerError, err
}
return http.StatusOK, nil
@@ -50,7 +97,7 @@ func getTreeHeadToSign(ctx context.Context, i *Instance, w http.ResponseWriter,
if err != nil {
return http.StatusInternalServerError, err
}
- if err := sth.MarshalASCII(w); err != nil {
+ if err := sth.ToASCII(w); err != nil {
return http.StatusInternalServerError, err
}
return http.StatusOK, nil
@@ -62,7 +109,7 @@ func getTreeHeadCosigned(ctx context.Context, i *Instance, w http.ResponseWriter
if err != nil {
return http.StatusInternalServerError, err
}
- if err := cth.MarshalASCII(w); err != nil {
+ if err := cth.ToASCII(w); err != nil {
return http.StatusInternalServerError, err
}
return http.StatusOK, nil
@@ -79,7 +126,7 @@ func getConsistencyProof(ctx context.Context, i *Instance, w http.ResponseWriter
if err != nil {
return http.StatusInternalServerError, err
}
- if err := proof.MarshalASCII(w); err != nil {
+ if err := proof.ToASCII(w); err != nil {
return http.StatusInternalServerError, err
}
return http.StatusOK, nil
@@ -96,7 +143,7 @@ func getInclusionProof(ctx context.Context, i *Instance, w http.ResponseWriter,
if err != nil {
return http.StatusInternalServerError, err
}
- if err := proof.MarshalASCII(w); err != nil {
+ if err := proof.ToASCII(w); err != nil {
return http.StatusInternalServerError, err
}
return http.StatusOK, nil
@@ -114,7 +161,7 @@ func getLeaves(ctx context.Context, i *Instance, w http.ResponseWriter, r *http.
return http.StatusInternalServerError, err
}
for _, leaf := range *leaves {
- if err := leaf.MarshalASCII(w); err != nil {
+ if err := leaf.ToASCII(w); err != nil {
return http.StatusInternalServerError, err
}
}
diff --git a/pkg/instance/endpoint_test.go b/pkg/instance/handler_test.go
index 18a6c27..ba5b60c 100644
--- a/pkg/instance/endpoint_test.go
+++ b/pkg/instance/handler_test.go
@@ -4,60 +4,137 @@ import (
"bytes"
"crypto/ed25519"
"crypto/rand"
- "encoding/hex"
"fmt"
"io"
"net/http"
"net/http/httptest"
+ "reflect"
"testing"
"time"
- "git.sigsum.org/sigsum-log-go/pkg/mocks"
- "git.sigsum.org/sigsum-log-go/pkg/types"
+ "git.sigsum.org/sigsum-lib-go/pkg/types"
+ mocksDB "git.sigsum.org/sigsum-log-go/pkg/db/mocks"
+ mocksDNS "git.sigsum.org/sigsum-log-go/pkg/dns/mocks"
+ mocksState "git.sigsum.org/sigsum-log-go/pkg/state/mocks"
"github.com/golang/mock/gomock"
)
var (
- testWitVK = [types.VerificationKeySize]byte{}
+ testWitVK = types.PublicKey{}
testConfig = Config{
- LogID: hex.EncodeToString(types.Hash([]byte("logid"))[:]),
+ LogID: fmt.Sprintf("%x", types.HashFn([]byte("logid"))[:]),
TreeID: 0,
Prefix: "testonly",
MaxRange: 3,
Deadline: 10,
Interval: 10,
ShardStart: 10,
- Witnesses: map[[types.HashSize]byte][types.VerificationKeySize]byte{
- *types.Hash(testWitVK[:]): testWitVK,
+ Witnesses: map[types.Hash]types.PublicKey{
+ *types.HashFn(testWitVK[:]): testWitVK,
},
}
testSTH = &types.SignedTreeHead{
TreeHead: types.TreeHead{
Timestamp: 0,
TreeSize: 0,
- RootHash: types.Hash(nil),
+ RootHash: *types.HashFn([]byte("root hash")),
},
- Signature: &[types.SignatureSize]byte{},
+ Signature: types.Signature{},
}
testCTH = &types.CosignedTreeHead{
SignedTreeHead: *testSTH,
- SigIdent: []*types.SigIdent{
- &types.SigIdent{
- KeyHash: &[types.HashSize]byte{},
- Signature: &[types.SignatureSize]byte{},
- },
- },
+ Cosignature: []types.Signature{types.Signature{}},
+ KeyHash: []types.Hash{types.Hash{}},
}
)
-func mustHandle(t *testing.T, i Instance, e types.Endpoint) Handler {
+// TestHandlers check that the expected handlers are configured
+func TestHandlers(t *testing.T) {
+ endpoints := map[types.Endpoint]bool{
+ types.EndpointAddLeaf: false,
+ types.EndpointAddCosignature: false,
+ types.EndpointGetTreeHeadLatest: false,
+ types.EndpointGetTreeHeadToSign: false,
+ types.EndpointGetTreeHeadCosigned: false,
+ types.EndpointGetConsistencyProof: false,
+ types.EndpointGetInclusionProof: false,
+ types.EndpointGetLeaves: false,
+ types.Endpoint("get-checkpoint"): false,
+ }
+ i := &Instance{
+ Config: testConfig,
+ }
for _, handler := range i.Handlers() {
- if handler.Endpoint == e {
- return handler
+ if _, ok := endpoints[handler.Endpoint]; !ok {
+ t.Errorf("got unexpected endpoint: %s", handler.Endpoint)
+ }
+ endpoints[handler.Endpoint] = true
+ }
+ for endpoint, ok := range endpoints {
+ if !ok {
+ t.Errorf("endpoint %s is not configured", endpoint)
+ }
+ }
+}
+
+// TestServeHTTP checks that invalid HTTP methods are rejected
+func TestServeHTTP(t *testing.T) {
+ i := &Instance{
+ Config: testConfig,
+ }
+ for _, handler := range i.Handlers() {
+ // Prepare invalid HTTP request
+ method := http.MethodPost
+ if method == handler.Method {
+ method = http.MethodGet
+ }
+ url := handler.Endpoint.Path("http://example.com", i.Prefix)
+ req, err := http.NewRequest(method, url, nil)
+ if err != nil {
+ t.Fatalf("must create HTTP request: %v", err)
+ }
+ w := httptest.NewRecorder()
+
+ // Check that it is rejected
+ handler.ServeHTTP(w, req)
+ if got, want := w.Code, http.StatusMethodNotAllowed; got != want {
+ t.Errorf("got HTTP code %v but wanted %v for endpoint %q", got, want, handler.Endpoint)
+ }
+ }
+}
+
+// TestPath checks that Path works for an endpoint (add-leaf)
+func TestPath(t *testing.T) {
+ for _, table := range []struct {
+ description string
+ prefix string
+ want string
+ }{
+ {
+ description: "no prefix",
+ want: "/sigsum/v0/add-leaf",
+ },
+ {
+ description: "a prefix",
+ prefix: "test-prefix",
+ want: "/test-prefix/sigsum/v0/add-leaf",
+ },
+ } {
+ instance := &Instance{
+ Config: Config{
+ Prefix: table.prefix,
+ },
+ }
+ handler := Handler{
+ Instance: instance,
+ Handler: addLeaf,
+ Endpoint: types.EndpointAddLeaf,
+ Method: http.MethodPost,
+ }
+ if got, want := handler.Path(), table.want; got != want {
+ t.Errorf("got path %v but wanted %v", got, want)
}
}
- t.Fatalf("must handle endpoint: %v", e)
- return Handler{}
}
func TestAddLeaf(t *testing.T) {
@@ -77,29 +154,29 @@ func TestAddLeaf(t *testing.T) {
},
{
description: "invalid: bad request (signature error)",
- ascii: mustLeafBuffer(t, 10, &[types.HashSize]byte{}, false),
+ ascii: mustLeafBuffer(t, 10, types.Hash{}, false),
wantCode: http.StatusBadRequest,
},
{
description: "invalid: bad request (shard hint is before shard start)",
- ascii: mustLeafBuffer(t, 9, &[types.HashSize]byte{}, true),
+ ascii: mustLeafBuffer(t, 9, types.Hash{}, true),
wantCode: http.StatusBadRequest,
},
{
description: "invalid: bad request (shard hint is after shard end)",
- ascii: mustLeafBuffer(t, uint64(time.Now().Unix())+1024, &[types.HashSize]byte{}, true),
+ ascii: mustLeafBuffer(t, uint64(time.Now().Unix())+1024, types.Hash{}, true),
wantCode: http.StatusBadRequest,
},
{
description: "invalid: failed verifying domain hint",
- ascii: mustLeafBuffer(t, 10, &[types.HashSize]byte{}, true),
+ ascii: mustLeafBuffer(t, 10, types.Hash{}, true),
expectDNS: true,
errDNS: fmt.Errorf("something went wrong"),
wantCode: http.StatusBadRequest,
},
{
description: "invalid: backend failure",
- ascii: mustLeafBuffer(t, 10, &[types.HashSize]byte{}, true),
+ ascii: mustLeafBuffer(t, 10, types.Hash{}, true),
expectDNS: true,
expectTrillian: true,
errTrillian: fmt.Errorf("something went wrong"),
@@ -107,7 +184,7 @@ func TestAddLeaf(t *testing.T) {
},
{
description: "valid",
- ascii: mustLeafBuffer(t, 10, &[types.HashSize]byte{}, true),
+ ascii: mustLeafBuffer(t, 10, types.Hash{}, true),
expectDNS: true,
expectTrillian: true,
wantCode: http.StatusOK,
@@ -117,11 +194,11 @@ func TestAddLeaf(t *testing.T) {
func() {
ctrl := gomock.NewController(t)
defer ctrl.Finish()
- dns := mocks.NewMockVerifier(ctrl)
+ dns := mocksDNS.NewMockVerifier(ctrl)
if table.expectDNS {
dns.EXPECT().Verify(gomock.Any(), gomock.Any(), gomock.Any()).Return(table.errDNS)
}
- client := mocks.NewMockClient(ctrl)
+ client := mocksDB.NewMockClient(ctrl)
if table.expectTrillian {
client.EXPECT().AddLeaf(gomock.Any(), gomock.Any()).Return(table.errTrillian)
}
@@ -150,10 +227,9 @@ func TestAddLeaf(t *testing.T) {
func TestAddCosignature(t *testing.T) {
buf := func() io.Reader {
- return bytes.NewBufferString(fmt.Sprintf(
- "%s%s%x%s"+"%s%s%x%s",
- types.Cosignature, types.Delim, make([]byte, types.SignatureSize), types.EOL,
- types.KeyHash, types.Delim, *types.Hash(testWitVK[:]), types.EOL,
+ return bytes.NewBufferString(fmt.Sprintf("%s=%x\n%s=%x\n",
+ "cosignature", types.Signature{},
+ "key_hash", *types.HashFn(testWitVK[:]),
))
}
for _, table := range []struct {
@@ -170,10 +246,9 @@ func TestAddCosignature(t *testing.T) {
},
{
description: "invalid: bad request (unknown witness)",
- ascii: bytes.NewBufferString(fmt.Sprintf(
- "%s%s%x%s"+"%s%s%x%s",
- types.Signature, types.Delim, make([]byte, types.SignatureSize), types.EOL,
- types.KeyHash, types.Delim, *types.Hash(testWitVK[1:]), types.EOL,
+ ascii: bytes.NewBufferString(fmt.Sprintf("%s=%x\n%s=%x\n",
+ "cosignature", types.Signature{},
+ "key_hash", *types.HashFn(testWitVK[1:]),
)),
wantCode: http.StatusBadRequest,
},
@@ -195,7 +270,7 @@ func TestAddCosignature(t *testing.T) {
func() {
ctrl := gomock.NewController(t)
defer ctrl.Finish()
- stateman := mocks.NewMockStateManager(ctrl)
+ stateman := mocksState.NewMockStateManager(ctrl)
if table.expect {
stateman.EXPECT().AddCosignature(gomock.Any(), gomock.Any(), gomock.Any()).Return(table.err)
}
@@ -246,7 +321,7 @@ func TestGetTreeHeadLatest(t *testing.T) {
func() {
ctrl := gomock.NewController(t)
defer ctrl.Finish()
- stateman := mocks.NewMockStateManager(ctrl)
+ stateman := mocksState.NewMockStateManager(ctrl)
if table.expect {
stateman.EXPECT().Latest(gomock.Any()).Return(table.rsp, table.err)
}
@@ -297,7 +372,7 @@ func TestGetTreeToSign(t *testing.T) {
func() {
ctrl := gomock.NewController(t)
defer ctrl.Finish()
- stateman := mocks.NewMockStateManager(ctrl)
+ stateman := mocksState.NewMockStateManager(ctrl)
if table.expect {
stateman.EXPECT().ToSign(gomock.Any()).Return(table.rsp, table.err)
}
@@ -348,7 +423,7 @@ func TestGetTreeCosigned(t *testing.T) {
func() {
ctrl := gomock.NewController(t)
defer ctrl.Finish()
- stateman := mocks.NewMockStateManager(ctrl)
+ stateman := mocksState.NewMockStateManager(ctrl)
if table.expect {
stateman.EXPECT().Cosigned(gomock.Any()).Return(table.rsp, table.err)
}
@@ -376,10 +451,9 @@ func TestGetTreeCosigned(t *testing.T) {
func TestGetConsistencyProof(t *testing.T) {
buf := func(oldSize, newSize int) io.Reader {
- return bytes.NewBufferString(fmt.Sprintf(
- "%s%s%d%s"+"%s%s%d%s",
- types.OldSize, types.Delim, oldSize, types.EOL,
- types.NewSize, types.Delim, newSize, types.EOL,
+ return bytes.NewBufferString(fmt.Sprintf("%s=%d\n%s=%d\n",
+ "old_size", oldSize,
+ "new_size", newSize,
))
}
for _, table := range []struct {
@@ -419,8 +493,8 @@ func TestGetConsistencyProof(t *testing.T) {
rsp: &types.ConsistencyProof{
OldSize: 1,
NewSize: 2,
- Path: []*[types.HashSize]byte{
- types.Hash(nil),
+ Path: []types.Hash{
+ *types.HashFn([]byte{}),
},
},
wantCode: http.StatusOK,
@@ -430,7 +504,7 @@ func TestGetConsistencyProof(t *testing.T) {
func() {
ctrl := gomock.NewController(t)
defer ctrl.Finish()
- client := mocks.NewMockClient(ctrl)
+ client := mocksDB.NewMockClient(ctrl)
if table.expect {
client.EXPECT().GetConsistencyProof(gomock.Any(), gomock.Any()).Return(table.rsp, table.err)
}
@@ -457,11 +531,10 @@ func TestGetConsistencyProof(t *testing.T) {
}
func TestGetInclusionProof(t *testing.T) {
- buf := func(hash *[types.HashSize]byte, treeSize int) io.Reader {
- return bytes.NewBufferString(fmt.Sprintf(
- "%s%s%x%s"+"%s%s%d%s",
- types.LeafHash, types.Delim, hash[:], types.EOL,
- types.TreeSize, types.Delim, treeSize, types.EOL,
+ buf := func(hash *types.Hash, treeSize int) io.Reader {
+ return bytes.NewBufferString(fmt.Sprintf("%s=%x\n%s=%d\n",
+ "leaf_hash", hash[:],
+ "tree_size", treeSize,
))
}
for _, table := range []struct {
@@ -479,25 +552,25 @@ func TestGetInclusionProof(t *testing.T) {
},
{
description: "invalid: bad request (no proof for tree size)",
- ascii: buf(types.Hash(nil), 1),
+ ascii: buf(types.HashFn([]byte{}), 1),
wantCode: http.StatusBadRequest,
},
{
description: "invalid: backend failure",
- ascii: buf(types.Hash(nil), 2),
+ ascii: buf(types.HashFn([]byte{}), 2),
expect: true,
err: fmt.Errorf("something went wrong"),
wantCode: http.StatusInternalServerError,
},
{
description: "valid",
- ascii: buf(types.Hash(nil), 2),
+ ascii: buf(types.HashFn([]byte{}), 2),
expect: true,
rsp: &types.InclusionProof{
TreeSize: 2,
LeafIndex: 0,
- Path: []*[types.HashSize]byte{
- types.Hash(nil),
+ Path: []types.Hash{
+ *types.HashFn([]byte{}),
},
},
wantCode: http.StatusOK,
@@ -507,7 +580,7 @@ func TestGetInclusionProof(t *testing.T) {
func() {
ctrl := gomock.NewController(t)
defer ctrl.Finish()
- client := mocks.NewMockClient(ctrl)
+ client := mocksDB.NewMockClient(ctrl)
if table.expect {
client.EXPECT().GetInclusionProof(gomock.Any(), gomock.Any()).Return(table.rsp, table.err)
}
@@ -535,19 +608,18 @@ func TestGetInclusionProof(t *testing.T) {
func TestGetLeaves(t *testing.T) {
buf := func(startSize, endSize int64) io.Reader {
- return bytes.NewBufferString(fmt.Sprintf(
- "%s%s%d%s"+"%s%s%d%s",
- types.StartSize, types.Delim, startSize, types.EOL,
- types.EndSize, types.Delim, endSize, types.EOL,
+ return bytes.NewBufferString(fmt.Sprintf("%s=%d\n%s=%d\n",
+ "start_size", startSize,
+ "end_size", endSize,
))
}
for _, table := range []struct {
description string
- ascii io.Reader // buffer used to populate HTTP request
- expect bool // set if a mock answer is expected
- rsp *types.LeafList // list of leaves from Trillian client
- err error // error from Trillian client
- wantCode int // HTTP status ok
+ ascii io.Reader // buffer used to populate HTTP request
+ expect bool // set if a mock answer is expected
+ rsp *types.Leaves // list of leaves from Trillian client
+ err error // error from Trillian client
+ wantCode int // HTTP status ok
}{
{
description: "invalid: bad request (parser error)",
@@ -570,18 +642,16 @@ func TestGetLeaves(t *testing.T) {
description: "valid: one more entry than the configured MaxRange",
ascii: buf(0, testConfig.MaxRange), // query will be pruned
expect: true,
- rsp: func() *types.LeafList {
- var list types.LeafList
+ rsp: func() *types.Leaves {
+ var list types.Leaves
for i := int64(0); i < testConfig.MaxRange; i++ {
- list = append(list[:], &types.Leaf{
- Message: types.Message{
+ list = append(list[:], types.Leaf{
+ Statement: types.Statement{
ShardHint: 0,
- Checksum: types.Hash(nil),
- },
- SigIdent: types.SigIdent{
- Signature: &[types.SignatureSize]byte{},
- KeyHash: types.Hash(nil),
+ Checksum: types.Hash{},
},
+ Signature: types.Signature{},
+ KeyHash: types.Hash{},
})
}
return &list
@@ -593,7 +663,7 @@ func TestGetLeaves(t *testing.T) {
func() {
ctrl := gomock.NewController(t)
defer ctrl.Finish()
- client := mocks.NewMockClient(ctrl)
+ client := mocksDB.NewMockClient(ctrl)
if table.expect {
client.EXPECT().GetLeaves(gomock.Any(), gomock.Any()).Return(table.rsp, table.err)
}
@@ -619,43 +689,48 @@ func TestGetLeaves(t *testing.T) {
return
}
- // TODO: check that we got the right leaves back. It is especially
- // important that we check that we got the right number of leaves.
- //
- // Pseuducode for when we have types.LeafList.UnmarshalASCII()
- //
- //list := &types.LeafList{}
- //if err := list.UnmarshalASCII(w.Body); err != nil {
- // t.Fatalf("must unmarshal leaf list: %v", err)
- //}
- //if got, want := list, table.rsp; !reflect.DeepEqual(got, want) {
- // t.Errorf("got leaf list\n\t%v\nbut wanted\n\t%v\nin test %q", got, want, table.description)
- //}
+ list := types.Leaves{}
+ if err := list.FromASCII(w.Body); err != nil {
+ t.Fatalf("must unmarshal leaf list: %v", err)
+ }
+ if got, want := &list, table.rsp; !reflect.DeepEqual(got, want) {
+ t.Errorf("got leaf list\n\t%v\nbut wanted\n\t%v\nin test %q", got, want, table.description)
+ }
}()
}
}
-func mustLeafBuffer(t *testing.T, shardHint uint64, checksum *[types.HashSize]byte, wantSig bool) io.Reader {
+func mustHandle(t *testing.T, i Instance, e types.Endpoint) Handler {
+ for _, handler := range i.Handlers() {
+ if handler.Endpoint == e {
+ return handler
+ }
+ }
+ t.Fatalf("must handle endpoint: %v", e)
+ return Handler{}
+}
+
+func mustLeafBuffer(t *testing.T, shardHint uint64, checksum types.Hash, wantSig bool) io.Reader {
t.Helper()
vk, sk, err := ed25519.GenerateKey(rand.Reader)
if err != nil {
t.Fatalf("must generate ed25519 keys: %v", err)
}
- msg := types.Message{
+ msg := types.Statement{
ShardHint: shardHint,
Checksum: checksum,
}
- sig := ed25519.Sign(sk, msg.Marshal())
+ sig := ed25519.Sign(sk, msg.ToBinary())
if !wantSig {
sig[0] += 1
}
return bytes.NewBufferString(fmt.Sprintf(
- "%s%s%d%s"+"%s%s%x%s"+"%s%s%x%s"+"%s%s%x%s"+"%s%s%s%s",
- types.ShardHint, types.Delim, shardHint, types.EOL,
- types.Checksum, types.Delim, checksum[:], types.EOL,
- types.Signature, types.Delim, sig, types.EOL,
- types.VerificationKey, types.Delim, vk, types.EOL,
- types.DomainHint, types.Delim, "example.com", types.EOL,
+ "%s=%d\n"+"%s=%x\n"+"%s=%x\n"+"%s=%x\n"+"%s=%s\n",
+ "shard_hint", shardHint,
+ "checksum", checksum[:],
+ "signature", sig,
+ "verification_key", vk,
+ "domain_hint", "example.com",
))
}
diff --git a/pkg/instance/instance.go b/pkg/instance/instance.go
index 4dff31a..9f71aa3 100644
--- a/pkg/instance/instance.go
+++ b/pkg/instance/instance.go
@@ -3,16 +3,15 @@ package instance
import (
"context"
"crypto"
- "crypto/ed25519"
"fmt"
"net/http"
"time"
+ "git.sigsum.org/sigsum-lib-go/pkg/requests"
+ "git.sigsum.org/sigsum-lib-go/pkg/types"
+ "git.sigsum.org/sigsum-log-go/pkg/db"
"git.sigsum.org/sigsum-log-go/pkg/dns"
"git.sigsum.org/sigsum-log-go/pkg/state"
- "git.sigsum.org/sigsum-log-go/pkg/trillian"
- "git.sigsum.org/sigsum-log-go/pkg/types"
- "github.com/golang/glog"
)
// Config is a collection of log parameters
@@ -26,27 +25,18 @@ type Config struct {
ShardStart uint64 // Shard interval start (num seconds since UNIX epoch)
// Witnesses map trusted witness identifiers to public verification keys
- Witnesses map[[types.HashSize]byte][types.VerificationKeySize]byte
+ Witnesses map[types.Hash]types.PublicKey
}
// Instance is an instance of the log's front-end
type Instance struct {
Config // configuration parameters
- Client trillian.Client // provides access to the Trillian backend
+ Client db.Client // provides access to the Trillian backend
Signer crypto.Signer // provides access to Ed25519 private key
Stateman state.StateManager // coordinates access to (co)signed tree heads
DNS dns.Verifier // checks if domain name knows a public key
}
-// Handler implements the http.Handler interface, and contains a reference
-// to a sigsum server instance as well as a function that uses it.
-type Handler struct {
- Instance *Instance
- Endpoint types.Endpoint
- Method string
- Handler func(context.Context, *Instance, http.ResponseWriter, *http.Request) (int, error)
-}
-
// Handlers returns a list of sigsum handlers
func (i *Instance) Handlers() []Handler {
return []Handler{
@@ -62,51 +52,13 @@ func (i *Instance) Handlers() []Handler {
}
}
-// Path returns a path that should be configured for this handler
-func (h Handler) Path() string {
- if len(h.Instance.Prefix) == 0 {
- return h.Endpoint.Path("", "sigsum", "v0")
- }
- return h.Endpoint.Path("", h.Instance.Prefix, "sigsum", "v0")
-}
-
-// ServeHTTP is part of the http.Handler interface
-func (a Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
- // export prometheus metrics
- var now time.Time = time.Now()
- var statusCode int
- defer func() {
- rspcnt.Inc(a.Instance.LogID, string(a.Endpoint), fmt.Sprintf("%d", statusCode))
- latency.Observe(time.Now().Sub(now).Seconds(), a.Instance.LogID, string(a.Endpoint), fmt.Sprintf("%d", statusCode))
- }()
- reqcnt.Inc(a.Instance.LogID, string(a.Endpoint))
-
- ctx, cancel := context.WithDeadline(r.Context(), now.Add(a.Instance.Deadline))
- defer cancel()
-
- if r.Method != a.Method {
- glog.Warningf("%s/%s: got HTTP %s, wanted HTTP %s", a.Instance.Prefix, string(a.Endpoint), r.Method, a.Method)
- http.Error(w, "", http.StatusMethodNotAllowed)
- return
- }
-
- statusCode, err := a.Handler(ctx, a.Instance, w, r)
- if err != nil {
- glog.Warningf("handler error %s/%s: %v", a.Instance.Prefix, a.Endpoint, err)
- http.Error(w, fmt.Sprintf("%s%s%s%s", "Error", types.Delim, err.Error(), types.EOL), statusCode)
- }
-}
-
-func (i *Instance) leafRequestFromHTTP(ctx context.Context, r *http.Request) (*types.LeafRequest, error) {
- var req types.LeafRequest
- if err := req.UnmarshalASCII(r.Body); err != nil {
- return nil, fmt.Errorf("UnmarshalASCII: %v", err)
+func (i *Instance) leafRequestFromHTTP(ctx context.Context, r *http.Request) (*requests.Leaf, error) {
+ var req requests.Leaf
+ if err := req.FromASCII(r.Body); err != nil {
+ return nil, fmt.Errorf("FromASCII: %v", err)
}
- vk := ed25519.PublicKey(req.VerificationKey[:])
- msg := req.Message.Marshal()
- sig := req.Signature[:]
- if !ed25519.Verify(vk, msg, sig) {
+ if !req.Statement.Verify(&req.VerificationKey, &req.Signature) {
return nil, fmt.Errorf("invalid signature")
}
shardEnd := uint64(time.Now().Unix())
@@ -116,27 +68,27 @@ func (i *Instance) leafRequestFromHTTP(ctx context.Context, r *http.Request) (*t
if req.ShardHint > shardEnd {
return nil, fmt.Errorf("invalid shard hint: %d not in [%d, %d]", req.ShardHint, i.ShardStart, shardEnd)
}
- if err := i.DNS.Verify(ctx, req.DomainHint, req.VerificationKey); err != nil {
+ if err := i.DNS.Verify(ctx, req.DomainHint, &req.VerificationKey); err != nil {
return nil, fmt.Errorf("invalid domain hint: %v", err)
}
return &req, nil
}
-func (i *Instance) cosignatureRequestFromHTTP(r *http.Request) (*types.CosignatureRequest, error) {
- var req types.CosignatureRequest
- if err := req.UnmarshalASCII(r.Body); err != nil {
- return nil, fmt.Errorf("UnmarshalASCII: %v", err)
+func (i *Instance) cosignatureRequestFromHTTP(r *http.Request) (*requests.Cosignature, error) {
+ var req requests.Cosignature
+ if err := req.FromASCII(r.Body); err != nil {
+ return nil, fmt.Errorf("FromASCII: %v", err)
}
- if _, ok := i.Witnesses[*req.KeyHash]; !ok {
+ if _, ok := i.Witnesses[req.KeyHash]; !ok {
return nil, fmt.Errorf("Unknown witness: %x", req.KeyHash)
}
return &req, nil
}
-func (i *Instance) consistencyProofRequestFromHTTP(r *http.Request) (*types.ConsistencyProofRequest, error) {
- var req types.ConsistencyProofRequest
- if err := req.UnmarshalASCII(r.Body); err != nil {
- return nil, fmt.Errorf("UnmarshalASCII: %v", err)
+func (i *Instance) consistencyProofRequestFromHTTP(r *http.Request) (*requests.ConsistencyProof, error) {
+ var req requests.ConsistencyProof
+ if err := req.FromASCII(r.Body); err != nil {
+ return nil, fmt.Errorf("FromASCII: %v", err)
}
if req.OldSize < 1 {
return nil, fmt.Errorf("OldSize(%d) must be larger than zero", req.OldSize)
@@ -147,10 +99,10 @@ func (i *Instance) consistencyProofRequestFromHTTP(r *http.Request) (*types.Cons
return &req, nil
}
-func (i *Instance) inclusionProofRequestFromHTTP(r *http.Request) (*types.InclusionProofRequest, error) {
- var req types.InclusionProofRequest
- if err := req.UnmarshalASCII(r.Body); err != nil {
- return nil, fmt.Errorf("UnmarshalASCII: %v", err)
+func (i *Instance) inclusionProofRequestFromHTTP(r *http.Request) (*requests.InclusionProof, error) {
+ var req requests.InclusionProof
+ if err := req.FromASCII(r.Body); err != nil {
+ return nil, fmt.Errorf("FromASCII: %v", err)
}
if req.TreeSize < 2 {
// TreeSize:0 => not possible to prove inclusion of anything
@@ -160,10 +112,10 @@ func (i *Instance) inclusionProofRequestFromHTTP(r *http.Request) (*types.Inclus
return &req, nil
}
-func (i *Instance) leavesRequestFromHTTP(r *http.Request) (*types.LeavesRequest, error) {
- var req types.LeavesRequest
- if err := req.UnmarshalASCII(r.Body); err != nil {
- return nil, fmt.Errorf("UnmarshalASCII: %v", err)
+func (i *Instance) leavesRequestFromHTTP(r *http.Request) (*requests.Leaves, error) {
+ var req requests.Leaves
+ if err := req.FromASCII(r.Body); err != nil {
+ return nil, fmt.Errorf("FromASCII: %v", err)
}
if req.StartSize > req.EndSize {
diff --git a/pkg/instance/instance_test.go b/pkg/instance/instance_test.go
deleted file mode 100644
index 6ca7baf..0000000
--- a/pkg/instance/instance_test.go
+++ /dev/null
@@ -1,98 +0,0 @@
-package instance
-
-import (
- "net/http"
- "net/http/httptest"
- "testing"
-
- "git.sigsum.org/sigsum-log-go/pkg/types"
-)
-
-// TestHandlers check that the expected handlers are configured
-func TestHandlers(t *testing.T) {
- endpoints := map[types.Endpoint]bool{
- types.EndpointAddLeaf: false,
- types.EndpointAddCosignature: false,
- types.EndpointGetTreeHeadLatest: false,
- types.EndpointGetTreeHeadToSign: false,
- types.EndpointGetTreeHeadCosigned: false,
- types.EndpointGetConsistencyProof: false,
- types.EndpointGetInclusionProof: false,
- types.EndpointGetLeaves: false,
- types.Endpoint("get-checkpoint"): false,
- }
- i := &Instance{
- Config: testConfig,
- }
- for _, handler := range i.Handlers() {
- if _, ok := endpoints[handler.Endpoint]; !ok {
- t.Errorf("got unexpected endpoint: %s", handler.Endpoint)
- }
- endpoints[handler.Endpoint] = true
- }
- for endpoint, ok := range endpoints {
- if !ok {
- t.Errorf("endpoint %s is not configured", endpoint)
- }
- }
-}
-
-// TestServeHTTP checks that invalid HTTP methods are rejected
-func TestServeHTTP(t *testing.T) {
- i := &Instance{
- Config: testConfig,
- }
- for _, handler := range i.Handlers() {
- // Prepare invalid HTTP request
- method := http.MethodPost
- if method == handler.Method {
- method = http.MethodGet
- }
- url := handler.Endpoint.Path("http://example.com", i.Prefix)
- req, err := http.NewRequest(method, url, nil)
- if err != nil {
- t.Fatalf("must create HTTP request: %v", err)
- }
- w := httptest.NewRecorder()
-
- // Check that it is rejected
- handler.ServeHTTP(w, req)
- if got, want := w.Code, http.StatusMethodNotAllowed; got != want {
- t.Errorf("got HTTP code %v but wanted %v for endpoint %q", got, want, handler.Endpoint)
- }
- }
-}
-
-// TestPath checks that Path works for an endpoint (add-leaf)
-func TestPath(t *testing.T) {
- for _, table := range []struct {
- description string
- prefix string
- want string
- }{
- {
- description: "no prefix",
- want: "/sigsum/v0/add-leaf",
- },
- {
- description: "a prefix",
- prefix: "test-prefix",
- want: "/test-prefix/sigsum/v0/add-leaf",
- },
- } {
- instance := &Instance{
- Config: Config{
- Prefix: table.prefix,
- },
- }
- handler := Handler{
- Instance: instance,
- Handler: addLeaf,
- Endpoint: types.EndpointAddLeaf,
- Method: http.MethodPost,
- }
- if got, want := handler.Path(), table.want; got != want {
- t.Errorf("got path %v but wanted %v", got, want)
- }
- }
-}
diff --git a/pkg/mocks/crypto.go b/pkg/state/mocks/signer.go
index 87c883a..7c699dd 100644
--- a/pkg/mocks/crypto.go
+++ b/pkg/state/mocks/signer.go
@@ -9,8 +9,8 @@ import (
// TestSign implements the signer interface. It can be used to mock an Ed25519
// signer that always return the same public key, signature, and error.
type TestSigner struct {
- PublicKey *[ed25519.PublicKeySize]byte
- Signature *[ed25519.SignatureSize]byte
+ PublicKey [ed25519.PublicKeySize]byte
+ Signature [ed25519.SignatureSize]byte
Error error
}
diff --git a/pkg/mocks/sigsum_state_manager.go b/pkg/state/mocks/state_manager.go
index 3b74ac4..009e20f 100644
--- a/pkg/mocks/sigsum_state_manager.go
+++ b/pkg/state/mocks/state_manager.go
@@ -8,8 +8,8 @@ import (
context "context"
reflect "reflect"
+ types "git.sigsum.org/sigsum-lib-go/pkg/types"
gomock "github.com/golang/mock/gomock"
- types "git.sigsum.org/sigsum-log-go/pkg/types"
)
// MockStateManager is a mock of StateManager interface.
@@ -36,7 +36,7 @@ func (m *MockStateManager) EXPECT() *MockStateManagerMockRecorder {
}
// AddCosignature mocks base method.
-func (m *MockStateManager) AddCosignature(arg0 context.Context, arg1 *[32]byte, arg2 *[64]byte) error {
+func (m *MockStateManager) AddCosignature(arg0 context.Context, arg1 *types.PublicKey, arg2 *types.Signature) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "AddCosignature", arg0, arg1, arg2)
ret0, _ := ret[0].(error)
diff --git a/pkg/state/single.go b/pkg/state/single.go
new file mode 100644
index 0000000..d02b86f
--- /dev/null
+++ b/pkg/state/single.go
@@ -0,0 +1,156 @@
+package state
+
+import (
+ "context"
+ "crypto"
+ "crypto/ed25519"
+ "fmt"
+ "reflect"
+ "sync"
+ "time"
+
+ "git.sigsum.org/sigsum-lib-go/pkg/types"
+ "git.sigsum.org/sigsum-log-go/pkg/db"
+ "github.com/golang/glog"
+ "github.com/google/certificate-transparency-go/schedule"
+)
+
+// StateManagerSingle implements a single-instance StateManager. In other
+// words, there's no other state that needs to be synced on any remote machine.
+type StateManagerSingle struct {
+ client db.Client
+ signer crypto.Signer
+ interval time.Duration
+ deadline time.Duration
+ sync.RWMutex
+
+ // cosigned is the current cosigned tree head that is being served
+ cosigned types.CosignedTreeHead
+
+ // toSign is the current tree head that is being cosigned by witnesses
+ toSign types.SignedTreeHead
+
+ // cosignatures keep track of all cosignatures for the toSign tree head
+ cosignatures map[types.Hash]*types.Signature
+}
+
+func NewStateManagerSingle(client db.Client, signer crypto.Signer, interval, deadline time.Duration) (*StateManagerSingle, error) {
+ sm := &StateManagerSingle{
+ client: client,
+ signer: signer,
+ interval: interval,
+ deadline: deadline,
+ }
+ ctx, cancel := context.WithTimeout(context.Background(), sm.deadline)
+ defer cancel()
+
+ sth, err := sm.Latest(ctx)
+ if err != nil {
+ return nil, fmt.Errorf("Latest: %v", err)
+ }
+ sm.toSign = *sth
+ sm.cosignatures = make(map[types.Hash]*types.Signature)
+ sm.cosigned = types.CosignedTreeHead{
+ SignedTreeHead: *sth,
+ Cosignature: make([]types.Signature, 0),
+ KeyHash: make([]types.Hash, 0),
+ }
+ return sm, nil
+}
+
+func (sm *StateManagerSingle) Run(ctx context.Context) {
+ schedule.Every(ctx, sm.interval, func(ctx context.Context) {
+ ictx, cancel := context.WithTimeout(ctx, sm.deadline)
+ defer cancel()
+
+ nextSTH, err := sm.Latest(ictx)
+ if err != nil {
+ glog.Warningf("rotate failed: Latest: %v", err)
+ return
+ }
+
+ sm.Lock()
+ defer sm.Unlock()
+ sm.rotate(nextSTH)
+ })
+}
+
+func (sm *StateManagerSingle) Latest(ctx context.Context) (*types.SignedTreeHead, error) {
+ th, err := sm.client.GetTreeHead(ctx)
+ if err != nil {
+ return nil, fmt.Errorf("LatestTreeHead: %v", err)
+ }
+
+ namespace := types.HashFn(sm.signer.Public().(ed25519.PublicKey))
+ return th.Sign(sm.signer, namespace)
+}
+
+func (sm *StateManagerSingle) ToSign(_ context.Context) (*types.SignedTreeHead, error) {
+ sm.RLock()
+ defer sm.RUnlock()
+ return &sm.toSign, nil
+}
+
+func (sm *StateManagerSingle) Cosigned(_ context.Context) (*types.CosignedTreeHead, error) {
+ sm.RLock()
+ defer sm.RUnlock()
+ if len(sm.cosigned.Cosignature) == 0 {
+ return nil, fmt.Errorf("no witness cosignatures available")
+ }
+ return &sm.cosigned, nil
+}
+
+func (sm *StateManagerSingle) AddCosignature(_ context.Context, vk *types.PublicKey, sig *types.Signature) error {
+ sm.Lock()
+ defer sm.Unlock()
+
+ namespace := types.HashFn(sm.signer.Public().(ed25519.PublicKey))
+ msg := sm.toSign.TreeHead.ToBinary(namespace)
+ if !ed25519.Verify(ed25519.PublicKey(vk[:]), msg, sig[:]) {
+ return fmt.Errorf("invalid tree head signature")
+ }
+
+ witness := types.HashFn(vk[:])
+ if _, ok := sm.cosignatures[*witness]; ok {
+ return fmt.Errorf("signature-signer pair is a duplicate") // TODO: maybe not an error
+ }
+ sm.cosignatures[*witness] = sig
+
+ glog.V(3).Infof("accepted new cosignature from witness: %x", *witness)
+ return nil
+}
+
+// rotate rotates the log's cosigned and stable STH. The caller must aquire the
+// source's read-write lock if there are concurrent reads and/or writes.
+func (sm *StateManagerSingle) rotate(next *types.SignedTreeHead) {
+ if reflect.DeepEqual(sm.cosigned.SignedTreeHead, sm.toSign) {
+ // cosigned and toSign are the same. So, we need to merge all
+ // cosignatures that we already had with the new collected ones.
+ for i := 0; i < len(sm.cosigned.Cosignature); i++ {
+ kh := sm.cosigned.KeyHash[i]
+ sig := sm.cosigned.Cosignature[i]
+
+ if _, ok := sm.cosignatures[kh]; !ok {
+ sm.cosignatures[kh] = &sig
+ }
+ }
+ glog.V(3).Infof("cosigned tree head repeated, merged signatures")
+ }
+ var cosignatures []types.Signature
+ var keyHashes []types.Hash
+
+ for keyHash, cosignature := range sm.cosignatures {
+ cosignatures = append(cosignatures, *cosignature)
+ keyHashes = append(keyHashes, keyHash)
+ }
+
+ // Update cosigned tree head
+ sm.cosigned.SignedTreeHead = sm.toSign
+ sm.cosigned.Cosignature = cosignatures
+ sm.cosigned.KeyHash = keyHashes
+
+ // Update to-sign tree head
+ sm.toSign = *next
+ sm.cosignatures = make(map[types.Hash]*types.Signature, 0) // TODO: on repeat we might want to not zero this
+ glog.V(3).Infof("rotated tree heads")
+}
diff --git a/pkg/state/state_manager_test.go b/pkg/state/single_test.go
index f11ba28..b315b3e 100644
--- a/pkg/state/state_manager_test.go
+++ b/pkg/state/single_test.go
@@ -11,36 +11,34 @@ import (
"testing"
"time"
+ "git.sigsum.org/sigsum-lib-go/pkg/types"
+ mocksTrillian "git.sigsum.org/sigsum-log-go/pkg/db/mocks"
+ mocksSigner "git.sigsum.org/sigsum-log-go/pkg/state/mocks"
"github.com/golang/mock/gomock"
- "git.sigsum.org/sigsum-log-go/pkg/mocks"
- "git.sigsum.org/sigsum-log-go/pkg/types"
)
var (
- testSig = &[types.SignatureSize]byte{}
- testPub = &[types.VerificationKeySize]byte{}
- testTH = &types.TreeHead{
+ testTH = types.TreeHead{
Timestamp: 0,
TreeSize: 0,
- RootHash: types.Hash(nil),
- KeyHash: types.Hash(testPub[:]),
+ RootHash: types.Hash{},
}
- testSigIdent = &types.SigIdent{
- Signature: testSig,
- KeyHash: types.Hash(testPub[:]),
+ testSTH = types.SignedTreeHead{
+ TreeHead: testTH,
+ Signature: types.Signature{},
}
- testSTH = &types.SignedTreeHead{
- TreeHead: *testTH,
- Signature: testSig,
- }
- testCTH = &types.CosignedTreeHead{
- SignedTreeHead: *testSTH,
- SigIdent: []*types.SigIdent{
- testSigIdent,
+ testCTH = types.CosignedTreeHead{
+ SignedTreeHead: testSTH,
+ Cosignature: []types.Signature{
+ types.Signature{},
+ },
+ KeyHash: []types.Hash{
+ types.Hash{},
},
}
- testSignerOK = &mocks.TestSigner{testPub, testSig, nil}
- testSignerErr = &mocks.TestSigner{testPub, testSig, fmt.Errorf("something went wrong")}
+
+ testSignerOK = &mocksSigner.TestSigner{types.PublicKey{}, types.Signature{}, nil}
+ testSignerErr = &mocksSigner.TestSigner{types.PublicKey{}, types.Signature{}, fmt.Errorf("something went wrong")}
)
func TestNewStateManagerSingle(t *testing.T) {
@@ -61,15 +59,15 @@ func TestNewStateManagerSingle(t *testing.T) {
{
description: "valid",
signer: testSignerOK,
- rsp: testTH,
- wantSth: testSTH,
+ rsp: &testTH,
+ wantSth: &testSTH,
},
} {
// Run deferred functions at the end of each iteration
func() {
ctrl := gomock.NewController(t)
defer ctrl.Finish()
- client := mocks.NewMockClient(ctrl)
+ client := mocksTrillian.NewMockClient(ctrl)
client.EXPECT().GetTreeHead(gomock.Any()).Return(table.rsp, table.err)
sm, err := NewStateManagerSingle(client, table.signer, time.Duration(0), time.Duration(0))
@@ -82,11 +80,11 @@ func TestNewStateManagerSingle(t *testing.T) {
if got, want := &sm.cosigned.SignedTreeHead, table.wantSth; !reflect.DeepEqual(got, want) {
t.Errorf("got cosigned tree head\n\t%v\nbut wanted\n\t%v\nin test %q", got, want, table.description)
}
- if got, want := &sm.tosign, table.wantSth; !reflect.DeepEqual(got, want) {
- t.Errorf("got tosign tree head\n\t%v\nbut wanted\n\t%v\nin test %q", got, want, table.description)
+ if got, want := &sm.toSign, table.wantSth; !reflect.DeepEqual(got, want) {
+ t.Errorf("got toSign tree head\n\t%v\nbut wanted\n\t%v\nin test %q", got, want, table.description)
}
// we only have log signature on startup
- if got, want := len(sm.cosignature), 0; got != want {
+ if got, want := len(sm.cosignatures), 0; got != want {
t.Errorf("got %d cosignatures but wanted %d in test %q", got, want, table.description)
}
}()
@@ -110,22 +108,22 @@ func TestLatest(t *testing.T) {
},
{
description: "invalid: signature failure",
- rsp: testTH,
+ rsp: &testTH,
signer: testSignerErr,
wantErr: true,
},
{
description: "valid",
signer: testSignerOK,
- rsp: testTH,
- wantSth: testSTH,
+ rsp: &testTH,
+ wantSth: &testSTH,
},
} {
// Run deferred functions at the end of each iteration
func() {
ctrl := gomock.NewController(t)
defer ctrl.Finish()
- client := mocks.NewMockClient(ctrl)
+ client := mocksTrillian.NewMockClient(ctrl)
client.EXPECT().GetTreeHead(gomock.Any()).Return(table.rsp, table.err)
sm := StateManagerSingle{
client: client,
@@ -149,14 +147,14 @@ func TestLatest(t *testing.T) {
func TestToSign(t *testing.T) {
description := "valid"
sm := StateManagerSingle{
- tosign: *testSTH,
+ toSign: testSTH,
}
sth, err := sm.ToSign(context.Background())
if err != nil {
t.Errorf("ToSign should not fail with error: %v", err)
return
}
- if got, want := sth, testSTH; !reflect.DeepEqual(got, want) {
+ if got, want := sth, &testSTH; !reflect.DeepEqual(got, want) {
t.Errorf("got signed tree head\n\t%v\nbut wanted\n\t%v\nin test %q", got, want, description)
}
}
@@ -164,18 +162,19 @@ func TestToSign(t *testing.T) {
func TestCosigned(t *testing.T) {
description := "valid"
sm := StateManagerSingle{
- cosigned: *testCTH,
+ cosigned: testCTH,
}
cth, err := sm.Cosigned(context.Background())
if err != nil {
t.Errorf("Cosigned should not fail with error: %v", err)
return
}
- if got, want := cth, testCTH; !reflect.DeepEqual(got, want) {
+ if got, want := cth, &testCTH; !reflect.DeepEqual(got, want) {
t.Errorf("got signed tree head\n\t%v\nbut wanted\n\t%v\nin test %q", got, want, description)
}
- sm.cosigned.SigIdent = make([]*types.SigIdent, 0)
+ sm.cosigned.Cosignature = make([]types.Signature, 0)
+ sm.cosigned.KeyHash = make([]types.Hash, 0)
cth, err = sm.Cosigned(context.Background())
if err == nil {
t.Errorf("Cosigned should fail without witness cosignatures")
@@ -188,57 +187,55 @@ func TestAddCosignature(t *testing.T) {
if err != nil {
t.Fatalf("GenerateKey: %v", err)
}
- if bytes.Equal(vk[:], testPub[:]) {
+ if bytes.Equal(vk[:], new(types.PublicKey)[:]) {
t.Fatalf("Sampled same key as testPub, aborting...")
}
- var vkArray [types.VerificationKeySize]byte
+ var vkArray types.PublicKey
copy(vkArray[:], vk[:])
for _, table := range []struct {
description string
signer crypto.Signer
- vk *[types.VerificationKeySize]byte
- th *types.TreeHead
+ vk types.PublicKey
+ th types.TreeHead
wantErr bool
}{
{
description: "invalid: signature error",
signer: sk,
- vk: testPub, // wrong key for message
+ vk: types.PublicKey{}, // wrong key for message
th: testTH,
wantErr: true,
},
{
description: "valid",
signer: sk,
- vk: &vkArray,
+ vk: vkArray,
th: testTH,
},
} {
- sth, _ := table.th.Sign(testSignerOK)
+ kh := types.HashFn(testSignerOK.Public().(ed25519.PublicKey))
+ sth := mustSign(t, testSignerOK, &table.th, kh)
cth := &types.CosignedTreeHead{
SignedTreeHead: *sth,
- SigIdent: []*types.SigIdent{},
+ Cosignature: make([]types.Signature, 0),
+ KeyHash: make([]types.Hash, 0),
}
sm := &StateManagerSingle{
- signer: testSignerOK,
- cosigned: *cth,
- tosign: *sth,
- cosignature: map[[types.HashSize]byte]*types.SigIdent{},
+ signer: testSignerOK,
+ cosigned: *cth,
+ toSign: *sth,
+ cosignatures: make(map[types.Hash]*types.Signature, 0),
}
// Prepare witness signature
- sth, err := table.th.Sign(table.signer)
- if err != nil {
- t.Fatalf("Sign: %v", err)
- }
- si := &types.SigIdent{
- KeyHash: types.Hash(table.signer.Public().(ed25519.PublicKey)[:]),
- Signature: sth.Signature,
- }
+ var vk types.PublicKey
+ copy(vk[:], table.vk[:]) //table.signer.Public().(ed25519.PublicKey))
+ sth = mustSign(t, table.signer, &table.th, kh)
+ kh = types.HashFn(vk[:])
// Add witness signature
- err = sm.AddCosignature(context.Background(), table.vk, si.Signature)
+ err = sm.AddCosignature(context.Background(), &vk, &sth.Signature)
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)
}
@@ -247,48 +244,47 @@ func TestAddCosignature(t *testing.T) {
}
// We should have one witness signature
- if got, want := len(sm.cosignature), 1; got != want {
+ if got, want := len(sm.cosignatures), 1; got != want {
t.Errorf("got %d cosignatures but wanted %v in test %q", got, want, table.description)
continue
}
// check that witness signature is there
- sigident, ok := sm.cosignature[*si.KeyHash]
+ sig, ok := sm.cosignatures[*kh]
if !ok {
t.Errorf("witness signature is missing")
continue
}
- if got, want := si, sigident; !reflect.DeepEqual(got, want) {
+ if got, want := sig[:], sth.Signature[:]; !bytes.Equal(got, want) {
t.Errorf("got witness sigident\n\t%v\nbut wanted\n\t%v\nin test %q", got, want, table.description)
continue
}
// Adding a duplicate signature should give an error
- if err := sm.AddCosignature(context.Background(), table.vk, si.Signature); err == nil {
+ if err := sm.AddCosignature(context.Background(), &vk, &sth.Signature); err == nil {
t.Errorf("duplicate witness signature accepted as valid")
}
}
}
func TestRotate(t *testing.T) {
- log := testSigIdent
- wit1 := &types.SigIdent{
- Signature: testSig,
- KeyHash: types.Hash([]byte("wit1 key")),
- }
- wit2 := &types.SigIdent{
- Signature: testSig,
- KeyHash: types.Hash([]byte("wit2 key")),
- }
- th0 := testTH
+ logSig := types.Signature{}
+ wit1Sig := types.Signature{}
+ wit2Sig := types.Signature{}
+
+ //logKH := &types.Hash{}
+ wit1KH := types.HashFn([]byte("wit1 key"))
+ wit2KH := types.HashFn([]byte("wit2 key"))
+
+ th0 := &testTH
th1 := &types.TreeHead{
Timestamp: 1,
TreeSize: 1,
- RootHash: types.Hash([]byte("1")),
+ RootHash: *types.HashFn([]byte("1")),
}
th2 := &types.TreeHead{
Timestamp: 2,
TreeSize: 2,
- RootHash: types.Hash([]byte("2")),
+ RootHash: *types.HashFn([]byte("2")),
}
for _, table := range []struct {
@@ -297,77 +293,81 @@ func TestRotate(t *testing.T) {
next *types.SignedTreeHead
}{
{
- description: "tosign tree head repated, but got one new witnes signature",
+ description: "toSign tree head repated, but got one new witnes signature",
before: &StateManagerSingle{
cosigned: types.CosignedTreeHead{
SignedTreeHead: types.SignedTreeHead{
TreeHead: *th0,
- Signature: log.Signature,
+ Signature: logSig,
},
- SigIdent: []*types.SigIdent{wit1},
+ Cosignature: []types.Signature{wit1Sig},
+ KeyHash: []types.Hash{*wit1KH},
},
- tosign: types.SignedTreeHead{
+ toSign: types.SignedTreeHead{
TreeHead: *th0,
- Signature: log.Signature,
+ Signature: logSig,
},
- cosignature: map[[types.HashSize]byte]*types.SigIdent{
- *wit2.KeyHash: wit2, // the new witness signature
+ cosignatures: map[types.Hash]*types.Signature{
+ *wit2KH: &wit2Sig, // the new witness signature
},
},
next: &types.SignedTreeHead{
TreeHead: *th1,
- Signature: log.Signature,
+ Signature: logSig,
},
after: &StateManagerSingle{
cosigned: types.CosignedTreeHead{
SignedTreeHead: types.SignedTreeHead{
TreeHead: *th0,
- Signature: log.Signature,
+ Signature: logSig,
},
- SigIdent: []*types.SigIdent{wit1, wit2},
+ Cosignature: []types.Signature{wit1Sig, wit2Sig},
+ KeyHash: []types.Hash{*wit1KH, *wit2KH},
},
- tosign: types.SignedTreeHead{
+ toSign: types.SignedTreeHead{
TreeHead: *th1,
- Signature: log.Signature,
+ Signature: logSig,
},
- cosignature: map[[types.HashSize]byte]*types.SigIdent{},
+ cosignatures: map[types.Hash]*types.Signature{},
},
},
{
- description: "tosign tree head did not repeat, it got one witness signature",
+ description: "toSign tree head did not repeat, it got one witness signature",
before: &StateManagerSingle{
cosigned: types.CosignedTreeHead{
SignedTreeHead: types.SignedTreeHead{
TreeHead: *th0,
- Signature: log.Signature,
+ Signature: logSig,
},
- SigIdent: []*types.SigIdent{wit1},
+ Cosignature: []types.Signature{wit1Sig},
+ KeyHash: []types.Hash{*wit1KH},
},
- tosign: types.SignedTreeHead{
+ toSign: types.SignedTreeHead{
TreeHead: *th1,
- Signature: log.Signature,
+ Signature: logSig,
},
- cosignature: map[[types.HashSize]byte]*types.SigIdent{
- *log.KeyHash: wit2,
+ cosignatures: map[types.Hash]*types.Signature{
+ *wit2KH: &wit2Sig,
},
},
next: &types.SignedTreeHead{
TreeHead: *th2,
- Signature: log.Signature,
+ Signature: logSig,
},
after: &StateManagerSingle{
cosigned: types.CosignedTreeHead{
SignedTreeHead: types.SignedTreeHead{
TreeHead: *th1,
- Signature: log.Signature,
+ Signature: logSig,
},
- SigIdent: []*types.SigIdent{wit2},
+ Cosignature: []types.Signature{wit2Sig},
+ KeyHash: []types.Hash{*wit2KH},
},
- tosign: types.SignedTreeHead{
+ toSign: types.SignedTreeHead{
TreeHead: *th2,
- Signature: log.Signature,
+ Signature: logSig,
},
- cosignature: map[[types.HashSize]byte]*types.SigIdent{},
+ cosignatures: map[types.Hash]*types.Signature{},
},
},
} {
@@ -375,31 +375,44 @@ func TestRotate(t *testing.T) {
if got, want := table.before.cosigned.SignedTreeHead, table.after.cosigned.SignedTreeHead; !reflect.DeepEqual(got, want) {
t.Errorf("got cosigned tree head\n\t%v\nbut wanted\n\t%v\nin test %q", got, want, table.description)
}
- checkWitnessList(t, table.description, table.before.cosigned.SigIdent, table.after.cosigned.SigIdent)
- if got, want := table.before.tosign, table.after.tosign; !reflect.DeepEqual(got, want) {
- t.Errorf("got tosign tree head\n\t%v\nbut wanted\n\t%v\nin test %q", got, want, table.description)
+ checkWitnessList(t, table.description, table.before.cosigned, table.after.cosigned)
+ if got, want := table.before.toSign, table.after.toSign; !reflect.DeepEqual(got, want) {
+ t.Errorf("got toSign tree head\n\t%v\nbut wanted\n\t%v\nin test %q", got, want, table.description)
}
- if got, want := table.before.cosignature, table.after.cosignature; !reflect.DeepEqual(got, want) {
- t.Errorf("got cosignature map\n\t%v\nbut wanted\n\t%v\nin test %q", got, want, table.description)
+ if got, want := table.before.cosignatures, table.after.cosignatures; !reflect.DeepEqual(got, want) {
+ t.Errorf("got cosignatures map\n\t%v\nbut wanted\n\t%v\nin test %q", got, want, table.description)
}
}
}
-func checkWitnessList(t *testing.T, description string, got, want []*types.SigIdent) {
+func checkWitnessList(t *testing.T, description string, got, want types.CosignedTreeHead) {
t.Helper()
- for _, si := range got {
+ if got, want := len(got.Cosignature), len(want.Cosignature); got != want {
+ t.Errorf("got %d cosignatures but wanted %d in test %q", got, want, description)
+ return
+ }
+ if got, want := len(got.KeyHash), len(want.KeyHash); got != want {
+ t.Errorf("got %d key hashes but wanted %d in test %q", got, want, description)
+ return
+ }
+ for i := 0; i < len(got.Cosignature); i++ {
found := false
- for _, sj := range want {
- if reflect.DeepEqual(si, sj) {
+ for j := 0; j < len(want.Cosignature); j++ {
+ if bytes.Equal(got.Cosignature[i][:], want.Cosignature[j][:]) && bytes.Equal(got.KeyHash[i][:], want.KeyHash[j][:]) {
found = true
break
}
}
if !found {
- t.Errorf("got unexpected signature-signer pair with key hash in test %q: %x", description, si.KeyHash[:])
+ t.Errorf("got unexpected signature-signer pair with key hash in test %q: %x", description, got.KeyHash[i][:])
}
}
- if len(got) != len(want) {
- t.Errorf("got %d signature-signer pairs but wanted %d in test %q", len(got), len(want), description)
+}
+
+func mustSign(t *testing.T, s crypto.Signer, th *types.TreeHead, kh *types.Hash) *types.SignedTreeHead {
+ sth, err := th.Sign(s, kh)
+ if err != nil {
+ t.Fatal(err)
}
+ return sth
}
diff --git a/pkg/state/state_manager.go b/pkg/state/state_manager.go
index 84431be..5aa7609 100644
--- a/pkg/state/state_manager.go
+++ b/pkg/state/state_manager.go
@@ -2,157 +2,15 @@ package state
import (
"context"
- "crypto"
- "crypto/ed25519"
- "fmt"
- "reflect"
- "sync"
- "time"
- "github.com/golang/glog"
- "github.com/google/certificate-transparency-go/schedule"
- "git.sigsum.org/sigsum-log-go/pkg/trillian"
- "git.sigsum.org/sigsum-log-go/pkg/types"
+ "git.sigsum.org/sigsum-lib-go/pkg/types"
)
-// StateManager coordinates access to the log's tree heads and (co)signatures
+// StateManager coordinates access to a log's tree heads and (co)signatures
type StateManager interface {
Latest(context.Context) (*types.SignedTreeHead, error)
ToSign(context.Context) (*types.SignedTreeHead, error)
Cosigned(context.Context) (*types.CosignedTreeHead, error)
- AddCosignature(context.Context, *[types.VerificationKeySize]byte, *[types.SignatureSize]byte) error
+ AddCosignature(context.Context, *types.PublicKey, *types.Signature) error
Run(context.Context)
}
-
-// StateManagerSingle implements the StateManager interface. It is assumed that
-// the log server is running on a single-instance machine. So, no coordination.
-type StateManagerSingle struct {
- client trillian.Client
- signer crypto.Signer
- interval time.Duration
- deadline time.Duration
- sync.RWMutex
-
- // cosigned is the current cosigned tree head that is being served
- cosigned types.CosignedTreeHead
-
- // tosign is the current tree head that is being cosigned by witnesses
- tosign types.SignedTreeHead
-
- // cosignature keeps track of all cosignatures for the tosign tree head
- cosignature map[[types.HashSize]byte]*types.SigIdent
-}
-
-func NewStateManagerSingle(client trillian.Client, signer crypto.Signer, interval, deadline time.Duration) (*StateManagerSingle, error) {
- sm := &StateManagerSingle{
- client: client,
- signer: signer,
- interval: interval,
- deadline: deadline,
- }
-
- ctx, _ := context.WithTimeout(context.Background(), sm.deadline)
- sth, err := sm.Latest(ctx)
- if err != nil {
- return nil, fmt.Errorf("Latest: %v", err)
- }
-
- sm.cosigned = types.CosignedTreeHead{
- SignedTreeHead: *sth,
- SigIdent: []*types.SigIdent{},
- }
- sm.tosign = *sth
- sm.cosignature = map[[types.HashSize]byte]*types.SigIdent{}
- return sm, nil
-}
-
-func (sm *StateManagerSingle) Run(ctx context.Context) {
- schedule.Every(ctx, sm.interval, func(ctx context.Context) {
- ictx, _ := context.WithTimeout(ctx, sm.deadline)
- nextSTH, err := sm.Latest(ictx)
- if err != nil {
- glog.Warningf("rotate failed: Latest: %v", err)
- return
- }
-
- sm.Lock()
- defer sm.Unlock()
- sm.rotate(nextSTH)
- })
-}
-
-func (sm *StateManagerSingle) Latest(ctx context.Context) (*types.SignedTreeHead, error) {
- th, err := sm.client.GetTreeHead(ctx)
- if err != nil {
- return nil, fmt.Errorf("LatestTreeHead: %v", err)
- }
- th.KeyHash = types.Hash(sm.signer.Public().(ed25519.PublicKey)[:])
- sth, err := th.Sign(sm.signer)
- if err != nil {
- return nil, fmt.Errorf("sign: %v", err)
- }
- return sth, nil
-}
-
-func (sm *StateManagerSingle) ToSign(_ context.Context) (*types.SignedTreeHead, error) {
- sm.RLock()
- defer sm.RUnlock()
- return &sm.tosign, nil
-}
-
-func (sm *StateManagerSingle) Cosigned(_ context.Context) (*types.CosignedTreeHead, error) {
- sm.RLock()
- defer sm.RUnlock()
- if len(sm.cosigned.SigIdent) == 0 {
- return nil, fmt.Errorf("no witness cosignatures available")
- }
- return &sm.cosigned, nil
-}
-
-func (sm *StateManagerSingle) AddCosignature(_ context.Context, vk *[types.VerificationKeySize]byte, sig *[types.SignatureSize]byte) error {
- sm.Lock()
- defer sm.Unlock()
-
- if err := sm.tosign.TreeHead.Verify(vk, sig); err != nil {
- return fmt.Errorf("Verify: %v", err)
- }
- witness := types.Hash(vk[:])
- if _, ok := sm.cosignature[*witness]; ok {
- return fmt.Errorf("signature-signer pair is a duplicate")
- }
- sm.cosignature[*witness] = &types.SigIdent{
- Signature: sig,
- KeyHash: witness,
- }
-
- glog.V(3).Infof("accepted new cosignature from witness: %x", *witness)
- return nil
-}
-
-// rotate rotates the log's cosigned and stable STH. The caller must aquire the
-// source's read-write lock if there are concurrent reads and/or writes.
-func (sm *StateManagerSingle) rotate(next *types.SignedTreeHead) {
- if reflect.DeepEqual(sm.cosigned.SignedTreeHead, sm.tosign) {
- // cosigned and tosign are the same. So, we need to merge all
- // cosignatures that we already had with the new collected ones.
- for _, sigident := range sm.cosigned.SigIdent {
- if _, ok := sm.cosignature[*sigident.KeyHash]; !ok {
- sm.cosignature[*sigident.KeyHash] = sigident
- }
- }
- glog.V(3).Infof("cosigned tree head repeated, merged signatures")
- }
- var cosignatures []*types.SigIdent
- for _, sigident := range sm.cosignature {
- cosignatures = append(cosignatures, sigident)
- }
-
- // Update cosigned tree head
- sm.cosigned.SignedTreeHead = sm.tosign
- sm.cosigned.SigIdent = cosignatures
-
- // Update to-sign tree head
- sm.tosign = *next
- sm.cosignature = map[[types.HashSize]byte]*types.SigIdent{} // TODO: on repeat we might want to not zero this
- glog.V(3).Infof("rotated tree heads")
-}
diff --git a/pkg/trillian/util.go b/pkg/trillian/util.go
deleted file mode 100644
index fbd0aea..0000000
--- a/pkg/trillian/util.go
+++ /dev/null
@@ -1,33 +0,0 @@
-package trillian
-
-import (
- "fmt"
-
- trillian "github.com/google/trillian/types"
- sigsum "git.sigsum.org/sigsum-log-go/pkg/types"
-)
-
-func treeHeadFromLogRoot(lr *trillian.LogRootV1) *sigsum.TreeHead {
- var hash [sigsum.HashSize]byte
- th := sigsum.TreeHead{
- Timestamp: uint64(lr.TimestampNanos / 1000 / 1000 / 1000),
- TreeSize: uint64(lr.TreeSize),
- RootHash: &hash,
- }
- copy(th.RootHash[:], lr.RootHash)
- return &th
-}
-
-func nodePathFromHashes(hashes [][]byte) ([]*[sigsum.HashSize]byte, error) {
- var path []*[sigsum.HashSize]byte
- for _, hash := range hashes {
- if len(hash) != sigsum.HashSize {
- return nil, fmt.Errorf("unexpected hash length: %v", len(hash))
- }
-
- var h [sigsum.HashSize]byte
- copy(h[:], hash)
- path = append(path, &h)
- }
- return path, nil
-}
diff --git a/pkg/types/ascii.go b/pkg/types/ascii.go
deleted file mode 100644
index 72abfcb..0000000
--- a/pkg/types/ascii.go
+++ /dev/null
@@ -1,399 +0,0 @@
-package types
-
-import (
- "bytes"
- "encoding/hex"
- "fmt"
- "io"
- "io/ioutil"
- "strconv"
-)
-
-const (
- // Delim is a key-value separator
- Delim = "="
-
- // EOL is a line sepator
- EOL = "\n"
-
- // NumField* is the number of unique keys in an incoming ASCII message
- NumFieldLeaf = 4
- NumFieldSignedTreeHead = 4
- NumFieldConsistencyProof = 3
- NumFieldInclusionProof = 3
- NumFieldLeavesRequest = 2
- NumFieldInclusionProofRequest = 2
- NumFieldConsistencyProofRequest = 2
- NumFieldLeafRequest = 5
- NumFieldCosignatureRequest = 2
-
- // New leaf keys
- ShardHint = "shard_hint"
- Checksum = "checksum"
- Signature = "signature"
- VerificationKey = "verification_key"
- DomainHint = "domain_hint"
-
- // Inclusion proof keys
- LeafHash = "leaf_hash"
- LeafIndex = "leaf_index"
- InclusionPath = "inclusion_path"
-
- // Consistency proof keys
- NewSize = "new_size"
- OldSize = "old_size"
- ConsistencyPath = "consistency_path"
-
- // Range of leaves keys
- StartSize = "start_size"
- EndSize = "end_size"
-
- // Tree head keys
- Timestamp = "timestamp"
- TreeSize = "tree_size"
- RootHash = "root_hash"
-
- // Witness signature-identity keys
- KeyHash = "key_hash"
- Cosignature = "cosignature"
-)
-
-// MessageASCI is a wrapper that manages ASCII key-value pairs
-type MessageASCII struct {
- m map[string][]string
-}
-
-// NewMessageASCII unpacks an incoming ASCII message
-func NewMessageASCII(r io.Reader, numFieldExpected int) (*MessageASCII, error) {
- buf, err := ioutil.ReadAll(r)
- if err != nil {
- return nil, fmt.Errorf("ReadAll: %v", err)
- }
- lines := bytes.Split(buf, []byte(EOL))
- if len(lines) <= 1 {
- return nil, fmt.Errorf("Not enough lines: empty")
- }
- lines = lines[:len(lines)-1] // valid message => split gives empty last line
-
- msg := MessageASCII{make(map[string][]string)}
- for _, line := range lines {
- split := bytes.Index(line, []byte(Delim))
- if split == -1 {
- return nil, fmt.Errorf("invalid line: %v", string(line))
- }
-
- key := string(line[:split])
- value := string(line[split+len(Delim):])
- values, ok := msg.m[key]
- if !ok {
- values = nil
- msg.m[key] = values
- }
- msg.m[key] = append(values, value)
- }
-
- if msg.NumField() != numFieldExpected {
- return nil, fmt.Errorf("Unexpected number of keys: %v", msg.NumField())
- }
- return &msg, nil
-}
-
-// NumField returns the number of unique keys
-func (msg *MessageASCII) NumField() int {
- return len(msg.m)
-}
-
-// GetStrings returns a list of strings
-func (msg *MessageASCII) GetStrings(key string) []string {
- strs, ok := msg.m[key]
- if !ok {
- return nil
- }
- return strs
-}
-
-// GetString unpacks a string
-func (msg *MessageASCII) GetString(key string) (string, error) {
- strs := msg.GetStrings(key)
- if len(strs) != 1 {
- return "", fmt.Errorf("expected one string: %v", strs)
- }
- return strs[0], nil
-}
-
-// GetUint64 unpacks an uint64
-func (msg *MessageASCII) GetUint64(key string) (uint64, error) {
- str, err := msg.GetString(key)
- if err != nil {
- return 0, fmt.Errorf("GetString: %v", err)
- }
- num, err := strconv.ParseUint(str, 10, 64)
- if err != nil {
- return 0, fmt.Errorf("ParseUint: %v", err)
- }
- return num, nil
-}
-
-// GetHash unpacks a hash
-func (msg *MessageASCII) GetHash(key string) (*[HashSize]byte, error) {
- str, err := msg.GetString(key)
- if err != nil {
- return nil, fmt.Errorf("GetString: %v", err)
- }
-
- var hash [HashSize]byte
- if err := decodeHex(str, hash[:]); err != nil {
- return nil, fmt.Errorf("decodeHex: %v", err)
- }
- return &hash, nil
-}
-
-// GetSignature unpacks a signature
-func (msg *MessageASCII) GetSignature(key string) (*[SignatureSize]byte, error) {
- str, err := msg.GetString(key)
- if err != nil {
- return nil, fmt.Errorf("GetString: %v", err)
- }
-
- var signature [SignatureSize]byte
- if err := decodeHex(str, signature[:]); err != nil {
- return nil, fmt.Errorf("decodeHex: %v", err)
- }
- return &signature, nil
-}
-
-// GetVerificationKey unpacks a verification key
-func (msg *MessageASCII) GetVerificationKey(key string) (*[VerificationKeySize]byte, error) {
- str, err := msg.GetString(key)
- if err != nil {
- return nil, fmt.Errorf("GetString: %v", err)
- }
-
- var vk [VerificationKeySize]byte
- if err := decodeHex(str, vk[:]); err != nil {
- return nil, fmt.Errorf("decodeHex: %v", err)
- }
- return &vk, nil
-}
-
-// decodeHex decodes a hex-encoded string into an already-sized byte slice
-func decodeHex(str string, out []byte) error {
- buf, err := hex.DecodeString(str)
- if err != nil {
- return fmt.Errorf("DecodeString: %v", err)
- }
- if len(buf) != len(out) {
- return fmt.Errorf("invalid length: %v", len(buf))
- }
- copy(out, buf)
- return nil
-}
-
-/*
- *
- * MarshalASCII wrappers for types that the log server outputs
- *
- */
-func (l *Leaf) MarshalASCII(w io.Writer) error {
- if err := writeASCII(w, ShardHint, strconv.FormatUint(l.ShardHint, 10)); err != nil {
- return fmt.Errorf("writeASCII: %v", err)
- }
- if err := writeASCII(w, Checksum, hex.EncodeToString(l.Checksum[:])); err != nil {
- return fmt.Errorf("writeASCII: %v", err)
- }
- if err := writeASCII(w, Signature, hex.EncodeToString(l.Signature[:])); err != nil {
- return fmt.Errorf("writeASCII: %v", err)
- }
- if err := writeASCII(w, KeyHash, hex.EncodeToString(l.KeyHash[:])); err != nil {
- return fmt.Errorf("writeASCII: %v", err)
- }
- return nil
-}
-
-func (sth *SignedTreeHead) MarshalASCII(w io.Writer) error {
- if err := writeASCII(w, Timestamp, strconv.FormatUint(sth.Timestamp, 10)); err != nil {
- return fmt.Errorf("writeASCII: %v", err)
- }
- if err := writeASCII(w, TreeSize, strconv.FormatUint(sth.TreeSize, 10)); err != nil {
- return fmt.Errorf("writeASCII: %v", err)
- }
- if err := writeASCII(w, RootHash, hex.EncodeToString(sth.RootHash[:])); err != nil {
- return fmt.Errorf("writeASCII: %v", err)
- }
- if err := writeASCII(w, Signature, hex.EncodeToString(sth.Signature[:])); err != nil {
- return fmt.Errorf("writeASCII: %v", err)
- }
- return nil
-}
-
-func (cth *CosignedTreeHead) MarshalASCII(w io.Writer) error {
- if err := cth.SignedTreeHead.MarshalASCII(w); err != nil {
- return fmt.Errorf("writeASCII: %v", err)
- }
- for _, si := range cth.SigIdent {
- if err := si.MarshalASCII(w); err != nil {
- return fmt.Errorf("writeASCII: %v", err)
- }
- }
- return nil
-}
-
-func (si *SigIdent) MarshalASCII(w io.Writer) error {
- if err := writeASCII(w, KeyHash, hex.EncodeToString(si.KeyHash[:])); err != nil {
- return fmt.Errorf("writeASCII: %v", err)
- }
- if err := writeASCII(w, Cosignature, hex.EncodeToString(si.Signature[:])); err != nil {
- return fmt.Errorf("writeASCII: %v", err)
- }
- return nil
-}
-
-func (p *ConsistencyProof) MarshalASCII(w io.Writer) error {
- for _, hash := range p.Path {
- if err := writeASCII(w, ConsistencyPath, hex.EncodeToString(hash[:])); err != nil {
- return fmt.Errorf("writeASCII: %v", err)
- }
- }
- return nil
-}
-
-func (p *InclusionProof) MarshalASCII(w io.Writer) error {
- if err := writeASCII(w, LeafIndex, strconv.FormatUint(p.LeafIndex, 10)); err != nil {
- return fmt.Errorf("writeASCII: %v", err)
- }
- for _, hash := range p.Path {
- if err := writeASCII(w, InclusionPath, hex.EncodeToString(hash[:])); err != nil {
- return fmt.Errorf("writeASCII: %v", err)
- }
- }
- return nil
-}
-
-func writeASCII(w io.Writer, key, value string) error {
- if _, err := fmt.Fprintf(w, "%s%s%s%s", key, Delim, value, EOL); err != nil {
- return fmt.Errorf("Fprintf: %v", err)
- }
- return nil
-}
-
-/*
- *
- * Unmarshal ASCII wrappers that the log server and/or log clients receive.
- *
- */
-func (ll *LeafList) UnmarshalASCII(r io.Reader) error {
- return nil
-}
-
-func (sth *SignedTreeHead) UnmarshalASCII(r io.Reader) error {
- msg, err := NewMessageASCII(r, NumFieldSignedTreeHead)
- if err != nil {
- return fmt.Errorf("NewMessageASCII: %v", err)
- }
-
- if sth.Timestamp, err = msg.GetUint64(Timestamp); err != nil {
- return fmt.Errorf("GetUint64(Timestamp): %v", err)
- }
- if sth.TreeSize, err = msg.GetUint64(TreeSize); err != nil {
- return fmt.Errorf("GetUint64(TreeSize): %v", err)
- }
- if sth.RootHash, err = msg.GetHash(RootHash); err != nil {
- return fmt.Errorf("GetHash(RootHash): %v", err)
- }
- if sth.Signature, err = msg.GetSignature(Signature); err != nil {
- return fmt.Errorf("GetHash(RootHash): %v", err)
- }
- return nil
-}
-
-func (p *InclusionProof) UnmarshalASCII(r io.Reader) error {
- return nil
-}
-
-func (p *ConsistencyProof) UnmarshalASCII(r io.Reader) error {
- return nil
-}
-
-func (req *InclusionProofRequest) UnmarshalASCII(r io.Reader) error {
- msg, err := NewMessageASCII(r, NumFieldInclusionProofRequest)
- if err != nil {
- return fmt.Errorf("NewMessageASCII: %v", err)
- }
-
- if req.LeafHash, err = msg.GetHash(LeafHash); err != nil {
- return fmt.Errorf("GetHash(LeafHash): %v", err)
- }
- if req.TreeSize, err = msg.GetUint64(TreeSize); err != nil {
- return fmt.Errorf("GetUint64(TreeSize): %v", err)
- }
- return nil
-}
-
-func (req *ConsistencyProofRequest) UnmarshalASCII(r io.Reader) error {
- msg, err := NewMessageASCII(r, NumFieldConsistencyProofRequest)
- if err != nil {
- return fmt.Errorf("NewMessageASCII: %v", err)
- }
-
- if req.NewSize, err = msg.GetUint64(NewSize); err != nil {
- return fmt.Errorf("GetUint64(NewSize): %v", err)
- }
- if req.OldSize, err = msg.GetUint64(OldSize); err != nil {
- return fmt.Errorf("GetUint64(OldSize): %v", err)
- }
- return nil
-}
-
-func (req *LeavesRequest) UnmarshalASCII(r io.Reader) error {
- msg, err := NewMessageASCII(r, NumFieldLeavesRequest)
- if err != nil {
- return fmt.Errorf("NewMessageASCII: %v", err)
- }
-
- if req.StartSize, err = msg.GetUint64(StartSize); err != nil {
- return fmt.Errorf("GetUint64(StartSize): %v", err)
- }
- if req.EndSize, err = msg.GetUint64(EndSize); err != nil {
- return fmt.Errorf("GetUint64(EndSize): %v", err)
- }
- return nil
-}
-
-func (req *LeafRequest) UnmarshalASCII(r io.Reader) error {
- msg, err := NewMessageASCII(r, NumFieldLeafRequest)
- if err != nil {
- return fmt.Errorf("NewMessageASCII: %v", err)
- }
-
- if req.ShardHint, err = msg.GetUint64(ShardHint); err != nil {
- return fmt.Errorf("GetUint64(ShardHint): %v", err)
- }
- if req.Checksum, err = msg.GetHash(Checksum); err != nil {
- return fmt.Errorf("GetHash(Checksum): %v", err)
- }
- if req.Signature, err = msg.GetSignature(Signature); err != nil {
- return fmt.Errorf("GetSignature: %v", err)
- }
- if req.VerificationKey, err = msg.GetVerificationKey(VerificationKey); err != nil {
- return fmt.Errorf("GetVerificationKey: %v", err)
- }
- if req.DomainHint, err = msg.GetString(DomainHint); err != nil {
- return fmt.Errorf("GetString(DomainHint): %v", err)
- }
- return nil
-}
-
-func (req *CosignatureRequest) UnmarshalASCII(r io.Reader) error {
- msg, err := NewMessageASCII(r, NumFieldCosignatureRequest)
- if err != nil {
- return fmt.Errorf("NewMessageASCII: %v", err)
- }
-
- if req.Signature, err = msg.GetSignature(Cosignature); err != nil {
- return fmt.Errorf("GetSignature: %v", err)
- }
- if req.KeyHash, err = msg.GetHash(KeyHash); err != nil {
- return fmt.Errorf("GetHash(KeyHash): %v", err)
- }
- return nil
-}
diff --git a/pkg/types/ascii_test.go b/pkg/types/ascii_test.go
deleted file mode 100644
index fc3f486..0000000
--- a/pkg/types/ascii_test.go
+++ /dev/null
@@ -1,438 +0,0 @@
-package types
-
-import (
- "bytes"
- "fmt"
- "io"
- "reflect"
- "testing"
-)
-
-/*
- *
- * MessageASCII methods and helpers
- *
- */
-func TestNewMessageASCII(t *testing.T) {
- for _, table := range []struct {
- description string
- input io.Reader
- wantErr bool
- wantMap map[string][]string
- }{
- {
- description: "invalid: not enough lines",
- input: bytes.NewBufferString(""),
- wantErr: true,
- },
- {
- description: "invalid: lines must end with new line",
- input: bytes.NewBufferString("k1=v1\nk2=v2"),
- wantErr: true,
- },
- {
- description: "invalid: lines must not be empty",
- input: bytes.NewBufferString("k1=v1\n\nk2=v2\n"),
- wantErr: true,
- },
- {
- description: "invalid: wrong number of fields",
- input: bytes.NewBufferString("k1=v1\n"),
- wantErr: true,
- },
- {
- description: "valid",
- input: bytes.NewBufferString("k1=v1\nk2=v2\nk2=v3=4\n"),
- wantMap: map[string][]string{
- "k1": []string{"v1"},
- "k2": []string{"v2", "v3=4"},
- },
- },
- } {
- msg, err := NewMessageASCII(table.input, len(table.wantMap))
- 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
- }
- if got, want := msg.m, table.wantMap; !reflect.DeepEqual(got, want) {
- t.Errorf("got\n\t%v\nbut wanted\n\t%v\nin test %q", got, want, table.description)
- }
- }
-}
-
-func TestNumField(t *testing.T) {}
-func TestGetStrings(t *testing.T) {}
-func TestGetString(t *testing.T) {}
-func TestGetUint64(t *testing.T) {}
-func TestGetHash(t *testing.T) {}
-func TestGetSignature(t *testing.T) {}
-func TestGetVerificationKey(t *testing.T) {}
-func TestDecodeHex(t *testing.T) {}
-
-/*
- *
- * MarshalASCII methods and helpers
- *
- */
-func TestLeafMarshalASCII(t *testing.T) {
- description := "valid: two leaves"
- leafList := []*Leaf{
- &Leaf{
- Message: Message{
- ShardHint: 123,
- Checksum: testBuffer32,
- },
- SigIdent: SigIdent{
- Signature: testBuffer64,
- KeyHash: testBuffer32,
- },
- },
- &Leaf{
- Message: Message{
- ShardHint: 456,
- Checksum: testBuffer32,
- },
- SigIdent: SigIdent{
- Signature: testBuffer64,
- KeyHash: testBuffer32,
- },
- },
- }
- wantBuf := bytes.NewBufferString(fmt.Sprintf(
- "%s%s%d%s"+"%s%s%x%s"+"%s%s%x%s"+"%s%s%x%s"+
- "%s%s%d%s"+"%s%s%x%s"+"%s%s%x%s"+"%s%s%x%s",
- // Leaf 1
- ShardHint, Delim, 123, EOL,
- Checksum, Delim, testBuffer32[:], EOL,
- Signature, Delim, testBuffer64[:], EOL,
- KeyHash, Delim, testBuffer32[:], EOL,
- // Leaf 2
- ShardHint, Delim, 456, EOL,
- Checksum, Delim, testBuffer32[:], EOL,
- Signature, Delim, testBuffer64[:], EOL,
- KeyHash, Delim, testBuffer32[:], EOL,
- ))
- buf := bytes.NewBuffer(nil)
- for _, leaf := range leafList {
- if err := leaf.MarshalASCII(buf); err != nil {
- t.Errorf("expected error %v but got %v in test %q: %v", false, true, description, err)
- return
- }
- }
- if got, want := buf.Bytes(), wantBuf.Bytes(); !bytes.Equal(got, want) {
- t.Errorf("got\n\t%v\nbut wanted\n\t%v\nin test %q", string(got), string(want), description)
- }
-}
-
-func TestSignedTreeHeadMarshalASCII(t *testing.T) {
- description := "valid"
- sth := &SignedTreeHead{
- TreeHead: TreeHead{
- Timestamp: 123,
- TreeSize: 456,
- RootHash: testBuffer32,
- },
- Signature: testBuffer64,
- }
- wantBuf := bytes.NewBufferString(fmt.Sprintf(
- "%s%s%d%s"+"%s%s%d%s"+"%s%s%x%s"+"%s%s%x%s",
- Timestamp, Delim, 123, EOL,
- TreeSize, Delim, 456, EOL,
- RootHash, Delim, testBuffer32[:], EOL,
- Signature, Delim, testBuffer64[:], EOL,
- ))
- buf := bytes.NewBuffer(nil)
- if err := sth.MarshalASCII(buf); err != nil {
- t.Errorf("expected error %v but got %v in test %q", false, true, description)
- return
- }
- if got, want := buf.Bytes(), wantBuf.Bytes(); !bytes.Equal(got, want) {
- t.Errorf("got\n\t%v\nbut wanted\n\t%v\nin test %q", string(got), string(want), description)
- }
-}
-
-func TestInclusionProofMarshalASCII(t *testing.T) {
- description := "valid"
- proof := InclusionProof{
- TreeSize: 321,
- LeafIndex: 123,
- Path: []*[HashSize]byte{
- testBuffer32,
- testBuffer32,
- },
- }
- wantBuf := bytes.NewBufferString(fmt.Sprintf(
- "%s%s%d%s"+"%s%s%x%s"+"%s%s%x%s",
- LeafIndex, Delim, 123, EOL,
- InclusionPath, Delim, testBuffer32[:], EOL,
- InclusionPath, Delim, testBuffer32[:], EOL,
- ))
- buf := bytes.NewBuffer(nil)
- if err := proof.MarshalASCII(buf); err != nil {
- t.Errorf("expected error %v but got %v in test %q", false, true, description)
- return
- }
- if got, want := buf.Bytes(), wantBuf.Bytes(); !bytes.Equal(got, want) {
- t.Errorf("got\n\t%v\nbut wanted\n\t%v\nin test %q", string(got), string(want), description)
- }
-}
-
-func TestConsistencyProofMarshalASCII(t *testing.T) {
- description := "valid"
- proof := ConsistencyProof{
- NewSize: 321,
- OldSize: 123,
- Path: []*[HashSize]byte{
- testBuffer32,
- testBuffer32,
- },
- }
- wantBuf := bytes.NewBufferString(fmt.Sprintf(
- "%s%s%x%s"+"%s%s%x%s",
- ConsistencyPath, Delim, testBuffer32[:], EOL,
- ConsistencyPath, Delim, testBuffer32[:], EOL,
- ))
- buf := bytes.NewBuffer(nil)
- if err := proof.MarshalASCII(buf); err != nil {
- t.Errorf("expected error %v but got %v in test %q", false, true, description)
- return
- }
- if got, want := buf.Bytes(), wantBuf.Bytes(); !bytes.Equal(got, want) {
- t.Errorf("got\n\t%v\nbut wanted\n\t%v\nin test %q", string(got), string(want), description)
- }
-}
-
-func TestWriteASCII(t *testing.T) {
-}
-
-/*
- *
- * UnmarshalASCII methods and helpers
- *
- */
-func TestLeafListUnmarshalASCII(t *testing.T) {}
-
-func TestSignedTreeHeadUnmarshalASCII(t *testing.T) {
- for _, table := range []struct {
- description string
- buf io.Reader
- wantErr bool
- wantSth *SignedTreeHead
- }{
- {
- description: "valid",
- buf: bytes.NewBufferString(fmt.Sprintf(
- "%s%s%d%s"+"%s%s%d%s"+"%s%s%x%s"+"%s%s%x%s",
- Timestamp, Delim, 123, EOL,
- TreeSize, Delim, 456, EOL,
- RootHash, Delim, testBuffer32[:], EOL,
- Signature, Delim, testBuffer64[:], EOL,
- )),
- wantSth: &SignedTreeHead{
- TreeHead: TreeHead{
- Timestamp: 123,
- TreeSize: 456,
- RootHash: testBuffer32,
- },
- Signature: testBuffer64,
- },
- },
- } {
- var sth SignedTreeHead
- err := sth.UnmarshalASCII(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
- }
- if got, want := &sth, table.wantSth; !reflect.DeepEqual(got, want) {
- t.Errorf("got\n\t%v\nbut wanted\n\t%v\nin test %q", got, want, table.description)
- }
- }
-}
-
-func TestInclusionProofUnmarshalASCII(t *testing.T) {}
-func TestConsistencyProofUnmarshalASCII(t *testing.T) {}
-
-func TestInclusionProofRequestUnmarshalASCII(t *testing.T) {
- for _, table := range []struct {
- description string
- buf io.Reader
- wantErr bool
- wantReq *InclusionProofRequest
- }{
- {
- description: "valid",
- buf: bytes.NewBufferString(fmt.Sprintf(
- "%s%s%x%s"+"%s%s%d%s",
- LeafHash, Delim, testBuffer32[:], EOL,
- TreeSize, Delim, 123, EOL,
- )),
- wantReq: &InclusionProofRequest{
- LeafHash: testBuffer32,
- TreeSize: 123,
- },
- },
- } {
- var req InclusionProofRequest
- err := req.UnmarshalASCII(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
- }
- if got, want := &req, table.wantReq; !reflect.DeepEqual(got, want) {
- t.Errorf("got\n\t%v\nbut wanted\n\t%v\nin test %q", got, want, table.description)
- }
- }
-}
-
-func TestConsistencyProofRequestUnmarshalASCII(t *testing.T) {
- for _, table := range []struct {
- description string
- buf io.Reader
- wantErr bool
- wantReq *ConsistencyProofRequest
- }{
- {
- description: "valid",
- buf: bytes.NewBufferString(fmt.Sprintf(
- "%s%s%d%s"+"%s%s%d%s",
- NewSize, Delim, 321, EOL,
- OldSize, Delim, 123, EOL,
- )),
- wantReq: &ConsistencyProofRequest{
- NewSize: 321,
- OldSize: 123,
- },
- },
- } {
- var req ConsistencyProofRequest
- err := req.UnmarshalASCII(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
- }
- if got, want := &req, table.wantReq; !reflect.DeepEqual(got, want) {
- t.Errorf("got\n\t%v\nbut wanted\n\t%v\nin test %q", got, want, table.description)
- }
- }
-}
-
-func TestLeavesRequestUnmarshalASCII(t *testing.T) {
- for _, table := range []struct {
- description string
- buf io.Reader
- wantErr bool
- wantReq *LeavesRequest
- }{
- {
- description: "valid",
- buf: bytes.NewBufferString(fmt.Sprintf(
- "%s%s%d%s"+"%s%s%d%s",
- StartSize, Delim, 123, EOL,
- EndSize, Delim, 456, EOL,
- )),
- wantReq: &LeavesRequest{
- StartSize: 123,
- EndSize: 456,
- },
- },
- } {
- var req LeavesRequest
- err := req.UnmarshalASCII(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
- }
- if got, want := &req, table.wantReq; !reflect.DeepEqual(got, want) {
- t.Errorf("got\n\t%v\nbut wanted\n\t%v\nin test %q", got, want, table.description)
- }
- }
-}
-
-func TestLeafRequestUnmarshalASCII(t *testing.T) {
- for _, table := range []struct {
- description string
- buf io.Reader
- wantErr bool
- wantReq *LeafRequest
- }{
- {
- description: "valid",
- buf: bytes.NewBufferString(fmt.Sprintf(
- "%s%s%d%s"+"%s%s%x%s"+"%s%s%x%s"+"%s%s%x%s"+"%s%s%s%s",
- ShardHint, Delim, 123, EOL,
- Checksum, Delim, testBuffer32[:], EOL,
- Signature, Delim, testBuffer64[:], EOL,
- VerificationKey, Delim, testBuffer32[:], EOL,
- DomainHint, Delim, "example.com", EOL,
- )),
- wantReq: &LeafRequest{
- Message: Message{
- ShardHint: 123,
- Checksum: testBuffer32,
- },
- Signature: testBuffer64,
- VerificationKey: testBuffer32,
- DomainHint: "example.com",
- },
- },
- } {
- var req LeafRequest
- err := req.UnmarshalASCII(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
- }
- if got, want := &req, table.wantReq; !reflect.DeepEqual(got, want) {
- t.Errorf("got\n\t%v\nbut wanted\n\t%v\nin test %q", got, want, table.description)
- }
- }
-}
-
-func TestCosignatureRequestUnmarshalASCII(t *testing.T) {
- for _, table := range []struct {
- description string
- buf io.Reader
- wantErr bool
- wantReq *CosignatureRequest
- }{
- {
- description: "valid",
- buf: bytes.NewBufferString(fmt.Sprintf(
- "%s%s%x%s"+"%s%s%x%s",
- Cosignature, Delim, testBuffer64[:], EOL,
- KeyHash, Delim, testBuffer32[:], EOL,
- )),
- wantReq: &CosignatureRequest{
- SigIdent: SigIdent{
- Signature: testBuffer64,
- KeyHash: testBuffer32,
- },
- },
- },
- } {
- var req CosignatureRequest
- err := req.UnmarshalASCII(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
- }
- if got, want := &req, table.wantReq; !reflect.DeepEqual(got, want) {
- t.Errorf("got\n\t%v\nbut wanted\n\t%v\nin test %q", got, want, table.description)
- }
- }
-}
diff --git a/pkg/types/trunnel.go b/pkg/types/trunnel.go
deleted file mode 100644
index 5350c5b..0000000
--- a/pkg/types/trunnel.go
+++ /dev/null
@@ -1,63 +0,0 @@
-package types
-
-import (
- "encoding/binary"
- "fmt"
-)
-
-const (
- // MessageSize is the number of bytes in a Trunnel-encoded leaf message
- MessageSize = 8 + HashSize
- // LeafSize is the number of bytes in a Trunnel-encoded leaf
- LeafSize = MessageSize + SignatureSize + HashSize
- // TreeHeadSize is the number of bytes in a Trunnel-encoded tree head
- TreeHeadSize = 8 + 8 + HashSize + HashSize
-)
-
-// Marshal returns a Trunnel-encoded message
-func (m *Message) Marshal() []byte {
- buf := make([]byte, MessageSize)
- binary.BigEndian.PutUint64(buf, m.ShardHint)
- copy(buf[8:], m.Checksum[:])
- return buf
-}
-
-// Marshal returns a Trunnel-encoded leaf
-func (l *Leaf) Marshal() []byte {
- buf := l.Message.Marshal()
- buf = append(buf, l.SigIdent.Signature[:]...)
- buf = append(buf, l.SigIdent.KeyHash[:]...)
- return buf
-}
-
-// Marshal returns a Trunnel-encoded tree head
-func (th *TreeHead) Marshal() []byte {
- buf := make([]byte, TreeHeadSize)
- binary.BigEndian.PutUint64(buf[0:8], th.Timestamp)
- binary.BigEndian.PutUint64(buf[8:16], th.TreeSize)
- copy(buf[16:16+HashSize], th.RootHash[:])
- copy(buf[16+HashSize:], th.KeyHash[:])
- return buf
-}
-
-// Unmarshal parses the Trunnel-encoded buffer as a leaf
-func (l *Leaf) Unmarshal(buf []byte) error {
- if len(buf) != LeafSize {
- return fmt.Errorf("invalid leaf size: %v", len(buf))
- }
- // Shard hint
- l.ShardHint = binary.BigEndian.Uint64(buf)
- offset := 8
- // Checksum
- l.Checksum = &[HashSize]byte{}
- copy(l.Checksum[:], buf[offset:offset+HashSize])
- offset += HashSize
- // Signature
- l.Signature = &[SignatureSize]byte{}
- copy(l.Signature[:], buf[offset:offset+SignatureSize])
- offset += SignatureSize
- // KeyHash
- l.KeyHash = &[HashSize]byte{}
- copy(l.KeyHash[:], buf[offset:])
- return nil
-}
diff --git a/pkg/types/trunnel_test.go b/pkg/types/trunnel_test.go
deleted file mode 100644
index a3ae1ba..0000000
--- a/pkg/types/trunnel_test.go
+++ /dev/null
@@ -1,116 +0,0 @@
-package types
-
-import (
- "bytes"
- "reflect"
- "testing"
-)
-
-var (
- testBuffer32 = &[32]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31}
- testBuffer64 = &[64]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63}
-)
-
-func TestMarshalMessage(t *testing.T) {
- description := "valid: shard hint 72623859790382856, checksum 0x00,0x01,..."
- message := &Message{
- ShardHint: 72623859790382856,
- Checksum: testBuffer32,
- }
- want := bytes.Join([][]byte{
- []byte{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08},
- testBuffer32[:],
- }, nil)
- if got := message.Marshal(); !bytes.Equal(got, want) {
- t.Errorf("got message\n\t%v\nbut wanted\n\t%v\nin test %q\n", got, want, description)
- }
-}
-
-func TestMarshalLeaf(t *testing.T) {
- description := "valid: shard hint 72623859790382856, buffers 0x00,0x01,..."
- leaf := &Leaf{
- Message: Message{
- ShardHint: 72623859790382856,
- Checksum: testBuffer32,
- },
- SigIdent: SigIdent{
- Signature: testBuffer64,
- KeyHash: testBuffer32,
- },
- }
- want := bytes.Join([][]byte{
- []byte{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08},
- testBuffer32[:], testBuffer64[:], testBuffer32[:],
- }, nil)
- if got := leaf.Marshal(); !bytes.Equal(got, want) {
- t.Errorf("got leaf\n\t%v\nbut wanted\n\t%v\nin test %q\n", got, want, description)
- }
-}
-
-func TestMarshalTreeHead(t *testing.T) {
- description := "valid: timestamp 16909060, tree size 72623859790382856, root hash & key hash 0x00,0x01,..."
- th := &TreeHead{
- Timestamp: 16909060,
- TreeSize: 72623859790382856,
- RootHash: testBuffer32,
- KeyHash: testBuffer32,
- }
- want := bytes.Join([][]byte{
- []byte{0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04},
- []byte{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08},
- testBuffer32[:],
- testBuffer32[:],
- }, nil)
- if got := th.Marshal(); !bytes.Equal(got, want) {
- t.Errorf("got tree head\n\t%v\nbut wanted\n\t%v\nin test %q\n", got, want, description)
- }
-}
-
-func TestUnmarshalLeaf(t *testing.T) {
- for _, table := range []struct {
- description string
- serialized []byte
- wantErr bool
- want *Leaf
- }{
- {
- description: "invalid: not enough bytes",
- serialized: make([]byte, LeafSize-1),
- wantErr: true,
- },
- {
- description: "invalid: too many bytes",
- serialized: make([]byte, LeafSize+1),
- wantErr: true,
- },
- {
- description: "valid: shard hint 72623859790382856, buffers 0x00,0x01,...",
- serialized: bytes.Join([][]byte{
- []byte{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08},
- testBuffer32[:], testBuffer64[:], testBuffer32[:],
- }, nil),
- want: &Leaf{
- Message: Message{
- ShardHint: 72623859790382856,
- Checksum: testBuffer32,
- },
- SigIdent: SigIdent{
- Signature: testBuffer64,
- KeyHash: testBuffer32,
- },
- },
- },
- } {
- var leaf Leaf
- err := leaf.Unmarshal(table.serialized)
- 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
- }
- if got, want := &leaf, table.want; !reflect.DeepEqual(got, want) {
- t.Errorf("got leaf\n\t%v\nbut wanted\n\t%v\nin test %q\n", got, want, table.description)
- }
- }
-}
diff --git a/pkg/types/types.go b/pkg/types/types.go
deleted file mode 100644
index bc58c98..0000000
--- a/pkg/types/types.go
+++ /dev/null
@@ -1,138 +0,0 @@
-package types
-
-import (
- "crypto"
- "crypto/ed25519"
- "crypto/sha256"
- "fmt"
- "strings"
-)
-
-const (
- HashSize = sha256.Size
- SignatureSize = ed25519.SignatureSize
- VerificationKeySize = ed25519.PublicKeySize
-
- EndpointAddLeaf = Endpoint("add-leaf")
- EndpointAddCosignature = Endpoint("add-cosignature")
- EndpointGetTreeHeadLatest = Endpoint("get-tree-head-latest")
- EndpointGetTreeHeadToSign = Endpoint("get-tree-head-to-sign")
- EndpointGetTreeHeadCosigned = Endpoint("get-tree-head-cosigned")
- EndpointGetInclusionProof = Endpoint("get-inclusion-proof")
- EndpointGetConsistencyProof = Endpoint("get-consistency-proof")
- EndpointGetLeaves = Endpoint("get-leaves")
-)
-
-// Endpoint is a named HTTP API endpoint
-type Endpoint string
-
-// Path joins a number of components to form a full endpoint path. For example,
-// EndpointAddLeaf.Path("example.com", "st/v0") -> example.com/st/v0/add-leaf.
-func (e Endpoint) Path(components ...string) string {
- return strings.Join(append(components, string(e)), "/")
-}
-
-type Leaf struct {
- Message
- SigIdent
-}
-
-type Message struct {
- ShardHint uint64
- Checksum *[HashSize]byte
-}
-
-type SigIdent struct {
- Signature *[SignatureSize]byte
- KeyHash *[HashSize]byte
-}
-
-type SignedTreeHead struct {
- TreeHead
- Signature *[SignatureSize]byte
-}
-
-type CosignedTreeHead struct {
- SignedTreeHead
- SigIdent []*SigIdent
-}
-
-type TreeHead struct {
- Timestamp uint64
- TreeSize uint64
- RootHash *[HashSize]byte
- KeyHash *[HashSize]byte
-}
-
-type ConsistencyProof struct {
- NewSize uint64
- OldSize uint64
- Path []*[HashSize]byte
-}
-
-type InclusionProof struct {
- TreeSize uint64
- LeafIndex uint64
- Path []*[HashSize]byte
-}
-
-type LeafList []*Leaf
-
-type ConsistencyProofRequest struct {
- NewSize uint64
- OldSize uint64
-}
-
-type InclusionProofRequest struct {
- LeafHash *[HashSize]byte
- TreeSize uint64
-}
-
-type LeavesRequest struct {
- StartSize uint64
- EndSize uint64
-}
-
-type LeafRequest struct {
- Message
- Signature *[SignatureSize]byte
- VerificationKey *[VerificationKeySize]byte
- DomainHint string
-}
-
-type CosignatureRequest struct {
- SigIdent
-}
-
-// Sign signs the tree head using the log's signature scheme
-func (th *TreeHead) Sign(signer crypto.Signer) (*SignedTreeHead, error) {
- sig, err := signer.Sign(nil, th.Marshal(), crypto.Hash(0))
- if err != nil {
- return nil, fmt.Errorf("Sign: %v", err)
- }
-
- sth := &SignedTreeHead{
- TreeHead: *th,
- Signature: &[SignatureSize]byte{},
- }
- copy(sth.Signature[:], sig)
- return sth, nil
-}
-
-// Verify verifies the tree head signature using the log's signature scheme
-func (th *TreeHead) Verify(vk *[VerificationKeySize]byte, sig *[SignatureSize]byte) error {
- if !ed25519.Verify(ed25519.PublicKey(vk[:]), th.Marshal(), sig[:]) {
- return fmt.Errorf("invalid tree head signature")
- }
- return nil
-}
-
-// Verify checks if a leaf is included in the log
-func (p *InclusionProof) Verify(leaf *Leaf, th *TreeHead) error { // TODO
- return nil
-}
-
-// Verify checks if two tree heads are consistent
-func (p *ConsistencyProof) Verify(oldTH, newTH *TreeHead) error { // TODO
- return nil
-}
diff --git a/pkg/types/types_test.go b/pkg/types/types_test.go
deleted file mode 100644
index d823ea2..0000000
--- a/pkg/types/types_test.go
+++ /dev/null
@@ -1,58 +0,0 @@
-package types
-
-import (
- "testing"
-)
-
-func TestEndpointPath(t *testing.T) {
- base, prefix, proto := "example.com", "log", "sigsum/v0"
- for _, table := range []struct {
- endpoint Endpoint
- want string
- }{
- {
- endpoint: EndpointAddLeaf,
- want: "example.com/log/sigsum/v0/add-leaf",
- },
- {
- endpoint: EndpointAddCosignature,
- want: "example.com/log/sigsum/v0/add-cosignature",
- },
- {
- endpoint: EndpointGetTreeHeadLatest,
- want: "example.com/log/sigsum/v0/get-tree-head-latest",
- },
- {
- endpoint: EndpointGetTreeHeadToSign,
- want: "example.com/log/sigsum/v0/get-tree-head-to-sign",
- },
- {
- endpoint: EndpointGetTreeHeadCosigned,
- want: "example.com/log/sigsum/v0/get-tree-head-cosigned",
- },
- {
- endpoint: EndpointGetConsistencyProof,
- want: "example.com/log/sigsum/v0/get-consistency-proof",
- },
- {
- endpoint: EndpointGetInclusionProof,
- want: "example.com/log/sigsum/v0/get-inclusion-proof",
- },
- {
- endpoint: EndpointGetLeaves,
- want: "example.com/log/sigsum/v0/get-leaves",
- },
- } {
- if got, want := table.endpoint.Path(base+"/"+prefix+"/"+proto), table.want; got != want {
- t.Errorf("got endpoint\n%s\n\tbut wanted\n%s\n\twith one component", got, want)
- }
- if got, want := table.endpoint.Path(base, prefix, proto), table.want; got != want {
- t.Errorf("got endpoint\n%s\n\tbut wanted\n%s\n\tmultiple components", got, want)
- }
- }
-}
-
-func TestTreeHeadSign(t *testing.T) {}
-func TestTreeHeadVerify(t *testing.T) {}
-func TestInclusionProofVerify(t *testing.T) {}
-func TestConsistencyProofVerify(t *testing.T) {}
diff --git a/pkg/types/util.go b/pkg/types/util.go
deleted file mode 100644
index 3cd7dfa..0000000
--- a/pkg/types/util.go
+++ /dev/null
@@ -1,21 +0,0 @@
-package types
-
-import (
- "crypto/sha256"
-)
-
-const (
- LeafHashPrefix = 0x00
-)
-
-func Hash(buf []byte) *[HashSize]byte {
- var ret [HashSize]byte
- hash := sha256.New()
- hash.Write(buf)
- copy(ret[:], hash.Sum(nil))
- return &ret
-}
-
-func HashLeaf(buf []byte) *[HashSize]byte {
- return Hash(append([]byte{LeafHashPrefix}, buf...))
-}