aboutsummaryrefslogtreecommitdiff
path: root/pkg/state/single_test.go
diff options
context:
space:
mode:
authorRasmus Dahlberg <rasmus@mullvad.net>2021-12-20 19:53:54 +0100
committerRasmus Dahlberg <rasmus@mullvad.net>2021-12-20 19:53:54 +0100
commitdda238b9fc105219f220f0ec3b341b0c81b71301 (patch)
treeedbbb787ccd1c1816edfa44caf749c8be68b7bf9 /pkg/state/single_test.go
parent5ba4a77233549819440cc41a02503f3a85213e24 (diff)
types: Start using sigsum-lib-go
This commit does not change the way in which the log behaves externally. In other words, all changes are internal and involves renaming and code restructuring. Most notably picking up the refactored sigsum-lib-go.
Diffstat (limited to 'pkg/state/single_test.go')
-rw-r--r--pkg/state/single_test.go418
1 files changed, 418 insertions, 0 deletions
diff --git a/pkg/state/single_test.go b/pkg/state/single_test.go
new file mode 100644
index 0000000..b315b3e
--- /dev/null
+++ b/pkg/state/single_test.go
@@ -0,0 +1,418 @@
+package state
+
+import (
+ "bytes"
+ "context"
+ "crypto"
+ "crypto/ed25519"
+ "crypto/rand"
+ "fmt"
+ "reflect"
+ "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"
+)
+
+var (
+ testTH = types.TreeHead{
+ Timestamp: 0,
+ TreeSize: 0,
+ RootHash: types.Hash{},
+ }
+ testSTH = types.SignedTreeHead{
+ TreeHead: testTH,
+ Signature: types.Signature{},
+ }
+ testCTH = types.CosignedTreeHead{
+ SignedTreeHead: testSTH,
+ Cosignature: []types.Signature{
+ types.Signature{},
+ },
+ KeyHash: []types.Hash{
+ types.Hash{},
+ },
+ }
+
+ 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) {
+ for _, table := range []struct {
+ description string
+ signer crypto.Signer
+ rsp *types.TreeHead
+ err error
+ wantErr bool
+ wantSth *types.SignedTreeHead
+ }{
+ {
+ description: "invalid: backend failure",
+ signer: testSignerOK,
+ err: fmt.Errorf("something went wrong"),
+ wantErr: true,
+ },
+ {
+ description: "valid",
+ signer: testSignerOK,
+ rsp: &testTH,
+ wantSth: &testSTH,
+ },
+ } {
+ // Run deferred functions at the end of each iteration
+ func() {
+ ctrl := gomock.NewController(t)
+ defer ctrl.Finish()
+ 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))
+ 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 {
+ return
+ }
+ 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)
+ }
+ // we only have log signature on startup
+ if got, want := len(sm.cosignatures), 0; got != want {
+ t.Errorf("got %d cosignatures but wanted %d in test %q", got, want, table.description)
+ }
+ }()
+ }
+}
+
+func TestLatest(t *testing.T) {
+ for _, table := range []struct {
+ description string
+ signer crypto.Signer
+ rsp *types.TreeHead
+ err error
+ wantErr bool
+ wantSth *types.SignedTreeHead
+ }{
+ {
+ description: "invalid: backend failure",
+ signer: testSignerOK,
+ err: fmt.Errorf("something went wrong"),
+ wantErr: true,
+ },
+ {
+ description: "invalid: signature failure",
+ rsp: &testTH,
+ signer: testSignerErr,
+ wantErr: true,
+ },
+ {
+ description: "valid",
+ signer: testSignerOK,
+ rsp: &testTH,
+ wantSth: &testSTH,
+ },
+ } {
+ // Run deferred functions at the end of each iteration
+ func() {
+ ctrl := gomock.NewController(t)
+ defer ctrl.Finish()
+ client := mocksTrillian.NewMockClient(ctrl)
+ client.EXPECT().GetTreeHead(gomock.Any()).Return(table.rsp, table.err)
+ sm := StateManagerSingle{
+ client: client,
+ signer: table.signer,
+ }
+
+ sth, err := sm.Latest(context.Background())
+ 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 {
+ return
+ }
+ if got, want := sth, table.wantSth; !reflect.DeepEqual(got, want) {
+ t.Errorf("got signed tree head\n\t%v\nbut wanted\n\t%v\nin test %q", got, want, table.description)
+ }
+ }()
+ }
+}
+
+func TestToSign(t *testing.T) {
+ description := "valid"
+ sm := StateManagerSingle{
+ 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) {
+ t.Errorf("got signed tree head\n\t%v\nbut wanted\n\t%v\nin test %q", got, want, description)
+ }
+}
+
+func TestCosigned(t *testing.T) {
+ description := "valid"
+ sm := StateManagerSingle{
+ 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) {
+ t.Errorf("got signed tree head\n\t%v\nbut wanted\n\t%v\nin test %q", got, want, description)
+ }
+
+ 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")
+ return
+ }
+}
+
+func TestAddCosignature(t *testing.T) {
+ vk, sk, err := ed25519.GenerateKey(rand.Reader)
+ if err != nil {
+ t.Fatalf("GenerateKey: %v", err)
+ }
+ if bytes.Equal(vk[:], new(types.PublicKey)[:]) {
+ t.Fatalf("Sampled same key as testPub, aborting...")
+ }
+ var vkArray types.PublicKey
+ copy(vkArray[:], vk[:])
+
+ for _, table := range []struct {
+ description string
+ signer crypto.Signer
+ vk types.PublicKey
+ th types.TreeHead
+ wantErr bool
+ }{
+ {
+ description: "invalid: signature error",
+ signer: sk,
+ vk: types.PublicKey{}, // wrong key for message
+ th: testTH,
+ wantErr: true,
+ },
+ {
+ description: "valid",
+ signer: sk,
+ vk: vkArray,
+ th: testTH,
+ },
+ } {
+ kh := types.HashFn(testSignerOK.Public().(ed25519.PublicKey))
+ sth := mustSign(t, testSignerOK, &table.th, kh)
+ cth := &types.CosignedTreeHead{
+ SignedTreeHead: *sth,
+ Cosignature: make([]types.Signature, 0),
+ KeyHash: make([]types.Hash, 0),
+ }
+ sm := &StateManagerSingle{
+ signer: testSignerOK,
+ cosigned: *cth,
+ toSign: *sth,
+ cosignatures: make(map[types.Hash]*types.Signature, 0),
+ }
+
+ // Prepare witness 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(), &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)
+ }
+ if err != nil {
+ continue
+ }
+
+ // We should have one witness signature
+ 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
+ sig, ok := sm.cosignatures[*kh]
+ if !ok {
+ t.Errorf("witness signature is missing")
+ continue
+ }
+ 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(), &vk, &sth.Signature); err == nil {
+ t.Errorf("duplicate witness signature accepted as valid")
+ }
+ }
+}
+
+func TestRotate(t *testing.T) {
+ 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.HashFn([]byte("1")),
+ }
+ th2 := &types.TreeHead{
+ Timestamp: 2,
+ TreeSize: 2,
+ RootHash: *types.HashFn([]byte("2")),
+ }
+
+ for _, table := range []struct {
+ description string
+ before, after *StateManagerSingle
+ next *types.SignedTreeHead
+ }{
+ {
+ description: "toSign tree head repated, but got one new witnes signature",
+ before: &StateManagerSingle{
+ cosigned: types.CosignedTreeHead{
+ SignedTreeHead: types.SignedTreeHead{
+ TreeHead: *th0,
+ Signature: logSig,
+ },
+ Cosignature: []types.Signature{wit1Sig},
+ KeyHash: []types.Hash{*wit1KH},
+ },
+ toSign: types.SignedTreeHead{
+ TreeHead: *th0,
+ Signature: logSig,
+ },
+ cosignatures: map[types.Hash]*types.Signature{
+ *wit2KH: &wit2Sig, // the new witness signature
+ },
+ },
+ next: &types.SignedTreeHead{
+ TreeHead: *th1,
+ Signature: logSig,
+ },
+ after: &StateManagerSingle{
+ cosigned: types.CosignedTreeHead{
+ SignedTreeHead: types.SignedTreeHead{
+ TreeHead: *th0,
+ Signature: logSig,
+ },
+ Cosignature: []types.Signature{wit1Sig, wit2Sig},
+ KeyHash: []types.Hash{*wit1KH, *wit2KH},
+ },
+ toSign: types.SignedTreeHead{
+ TreeHead: *th1,
+ Signature: logSig,
+ },
+ cosignatures: map[types.Hash]*types.Signature{},
+ },
+ },
+ {
+ description: "toSign tree head did not repeat, it got one witness signature",
+ before: &StateManagerSingle{
+ cosigned: types.CosignedTreeHead{
+ SignedTreeHead: types.SignedTreeHead{
+ TreeHead: *th0,
+ Signature: logSig,
+ },
+ Cosignature: []types.Signature{wit1Sig},
+ KeyHash: []types.Hash{*wit1KH},
+ },
+ toSign: types.SignedTreeHead{
+ TreeHead: *th1,
+ Signature: logSig,
+ },
+ cosignatures: map[types.Hash]*types.Signature{
+ *wit2KH: &wit2Sig,
+ },
+ },
+ next: &types.SignedTreeHead{
+ TreeHead: *th2,
+ Signature: logSig,
+ },
+ after: &StateManagerSingle{
+ cosigned: types.CosignedTreeHead{
+ SignedTreeHead: types.SignedTreeHead{
+ TreeHead: *th1,
+ Signature: logSig,
+ },
+ Cosignature: []types.Signature{wit2Sig},
+ KeyHash: []types.Hash{*wit2KH},
+ },
+ toSign: types.SignedTreeHead{
+ TreeHead: *th2,
+ Signature: logSig,
+ },
+ cosignatures: map[types.Hash]*types.Signature{},
+ },
+ },
+ } {
+ table.before.rotate(table.next)
+ 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, 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.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.CosignedTreeHead) {
+ t.Helper()
+ 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 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, got.KeyHash[i][:])
+ }
+ }
+}
+
+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
+}