aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRasmus Dahlberg <rasmus.dahlberg@kau.se>2021-09-13 19:53:17 +0200
committerRasmus Dahlberg <rasmus.dahlberg@kau.se>2021-09-13 19:53:17 +0200
commit26b786d9857db21fdf110eaf9cb6d1d6e4e68ef9 (patch)
tree92606f47a91ebcc9e3cb4258af7a9472fc6f2e07
parentf34351da0731a11316e2266d2aadd62554a5b867 (diff)
updated (co)signed tree head structuresv0.2.0
- Added key_hash in tree head, see motivation in api.md - Added separate types for (co)signed tree heads - Refactored tree head HTTP APIs to be current, see api.md
-rw-r--r--cmd/tmp/cosign/main.go13
-rw-r--r--pkg/instance/endpoint.go4
-rw-r--r--pkg/instance/endpoint_test.go18
-rw-r--r--pkg/mocks/sigsum_state_manager.go4
-rw-r--r--pkg/state/state_manager.go32
-rw-r--r--pkg/state/state_manager_test.go148
-rw-r--r--pkg/types/ascii.go66
-rw-r--r--pkg/types/ascii_test.go34
-rw-r--r--pkg/types/trunnel.go7
-rw-r--r--pkg/types/trunnel_test.go4
-rw-r--r--pkg/types/types.go37
11 files changed, 173 insertions, 194 deletions
diff --git a/cmd/tmp/cosign/main.go b/cmd/tmp/cosign/main.go
index 629e7ac..42162e4 100644
--- a/cmd/tmp/cosign/main.go
+++ b/cmd/tmp/cosign/main.go
@@ -13,11 +13,17 @@ import (
)
var (
- url = flag.String("url", "http://localhost:6965/sigsum/v0", "base url")
- sk = flag.String("sk", "e1d7c494dacb0ddf809a17e4528b01f584af22e3766fa740ec52a1711c59500d711090dd2286040b50961b0fe09f58aa665ccee5cb7ee042d819f18f6ab5046b", "hex key")
+ url = flag.String("url", "http://localhost:6965/sigsum/v0", "base url")
+ sk = flag.String("sk", "e1d7c494dacb0ddf809a17e4528b01f584af22e3766fa740ec52a1711c59500d711090dd2286040b50961b0fe09f58aa665ccee5cb7ee042d819f18f6ab5046b", "witness secret key (hex)")
+ log_vk = flag.String("log_vk", "cc0e7294a9d002c33aaa828efba6622ab1ce8ebdb8a795902555c2813133cfe8", "log public key (hex)")
)
func main() {
+ log_vk, err := hex.DecodeString(*log_vk)
+ if err != nil {
+ log.Fatalf("DecodeString: %v", err)
+ }
+
priv, err := hex.DecodeString(*sk)
if err != nil {
log.Fatalf("DecodeString: %v", err)
@@ -34,7 +40,8 @@ func main() {
if err := sth.UnmarshalASCII(rsp.Body); err != nil {
log.Fatalf("UnmarshalASCII: %v", err)
}
- fmt.Printf("%+v\n", sth)
+ sth.TreeHead.KeyHash = types.Hash(log_vk)
+ fmt.Printf("%+v\n\n", sth)
msg := sth.TreeHead.Marshal()
sig := ed25519.Sign(sk, msg)
diff --git a/pkg/instance/endpoint.go b/pkg/instance/endpoint.go
index ec87303..2387263 100644
--- a/pkg/instance/endpoint.go
+++ b/pkg/instance/endpoint.go
@@ -58,11 +58,11 @@ func getTreeHeadToSign(ctx context.Context, i *Instance, w http.ResponseWriter,
func getTreeHeadCosigned(ctx context.Context, i *Instance, w http.ResponseWriter, _ *http.Request) (int, error) {
glog.V(3).Info("handling get-tree-head-cosigned request")
- sth, err := i.Stateman.Cosigned(ctx)
+ cth, err := i.Stateman.Cosigned(ctx)
if err != nil {
return http.StatusInternalServerError, err
}
- if err := sth.MarshalASCII(w); err != nil {
+ if err := cth.MarshalASCII(w); err != nil {
return http.StatusInternalServerError, err
}
return http.StatusOK, nil
diff --git a/pkg/instance/endpoint_test.go b/pkg/instance/endpoint_test.go
index 51ec0f4..95b71d3 100644
--- a/pkg/instance/endpoint_test.go
+++ b/pkg/instance/endpoint_test.go
@@ -34,10 +34,14 @@ var (
TreeSize: 0,
RootHash: types.Hash(nil),
},
+ Signature: &[types.SignatureSize]byte{},
+ }
+ testCTH = &types.CosignedTreeHead{
+ SignedTreeHead: *testSTH,
SigIdent: []*types.SigIdent{
&types.SigIdent{
- Signature: &[types.SignatureSize]byte{},
KeyHash: &[types.HashSize]byte{},
+ Signature: &[types.SignatureSize]byte{},
},
},
}
@@ -137,7 +141,7 @@ func TestAddCosignature(t *testing.T) {
buf := func() io.Reader {
return bytes.NewBufferString(fmt.Sprintf(
"%s%s%x%s"+"%s%s%x%s",
- types.Signature, types.Delim, make([]byte, types.SignatureSize), types.EOL,
+ types.Cosignature, types.Delim, make([]byte, types.SignatureSize), types.EOL,
types.KeyHash, types.Delim, *types.Hash(testWitVK[:]), types.EOL,
))
}
@@ -311,10 +315,10 @@ func TestGetTreeToSign(t *testing.T) {
func TestGetTreeCosigned(t *testing.T) {
for _, table := range []struct {
description string
- expect bool // set if a mock answer is expected
- rsp *types.SignedTreeHead // signed tree head from Trillian client
- err error // error from Trillian client
- wantCode int // HTTP status ok
+ expect bool // set if a mock answer is expected
+ rsp *types.CosignedTreeHead // cosigned tree head from Trillian client
+ err error // error from Trillian client
+ wantCode int // HTTP status ok
}{
{
description: "invalid: backend failure",
@@ -325,7 +329,7 @@ func TestGetTreeCosigned(t *testing.T) {
{
description: "valid",
expect: true,
- rsp: testSTH,
+ rsp: testCTH,
wantCode: http.StatusOK,
},
} {
diff --git a/pkg/mocks/sigsum_state_manager.go b/pkg/mocks/sigsum_state_manager.go
index b999677..594d0a1 100644
--- a/pkg/mocks/sigsum_state_manager.go
+++ b/pkg/mocks/sigsum_state_manager.go
@@ -50,10 +50,10 @@ func (mr *MockStateManagerMockRecorder) AddCosignature(arg0, arg1, arg2 interfac
}
// Cosigned mocks base method.
-func (m *MockStateManager) Cosigned(arg0 context.Context) (*types.SignedTreeHead, error) {
+func (m *MockStateManager) Cosigned(arg0 context.Context) (*types.CosignedTreeHead, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "Cosigned", arg0)
- ret0, _ := ret[0].(*types.SignedTreeHead)
+ ret0, _ := ret[0].(*types.CosignedTreeHead)
ret1, _ := ret[1].(error)
return ret0, ret1
}
diff --git a/pkg/state/state_manager.go b/pkg/state/state_manager.go
index 7ddf986..a08fd84 100644
--- a/pkg/state/state_manager.go
+++ b/pkg/state/state_manager.go
@@ -3,6 +3,7 @@ package state
import (
"context"
"crypto"
+ "crypto/ed25519"
"fmt"
"reflect"
"sync"
@@ -18,7 +19,7 @@ import (
type StateManager interface {
Latest(context.Context) (*types.SignedTreeHead, error)
ToSign(context.Context) (*types.SignedTreeHead, error)
- Cosigned(context.Context) (*types.SignedTreeHead, error)
+ Cosigned(context.Context) (*types.CosignedTreeHead, error)
AddCosignature(context.Context, *[types.VerificationKeySize]byte, *[types.SignatureSize]byte) error
Run(context.Context)
}
@@ -33,7 +34,7 @@ type StateManagerSingle struct {
sync.RWMutex
// cosigned is the current cosigned tree head that is being served
- cosigned types.SignedTreeHead
+ cosigned types.CosignedTreeHead
// tosign is the current tree head that is being cosigned by witnesses
tosign types.SignedTreeHead
@@ -56,18 +57,19 @@ func NewStateManagerSingle(client trillian.Client, signer crypto.Signer, interva
return nil, fmt.Errorf("Latest: %v", err)
}
- sm.cosigned = *sth
- sm.tosign = *sth
- sm.cosignature = map[[types.HashSize]byte]*types.SigIdent{
- *sth.SigIdent[0].KeyHash: sth.SigIdent[0], // log signature
+ 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)
- nextTreeHead, err := sm.Latest(ictx)
+ nextSTH, err := sm.Latest(ictx)
if err != nil {
glog.Warningf("rotate failed: Latest: %v", err)
return
@@ -75,7 +77,7 @@ func (sm *StateManagerSingle) Run(ctx context.Context) {
sm.Lock()
defer sm.Unlock()
- sm.rotate(nextTreeHead)
+ sm.rotate(nextSTH)
})
}
@@ -84,6 +86,7 @@ func (sm *StateManagerSingle) Latest(ctx context.Context) (*types.SignedTreeHead
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)
@@ -97,9 +100,12 @@ func (sm *StateManagerSingle) ToSign(_ context.Context) (*types.SignedTreeHead,
return &sm.tosign, nil
}
-func (sm *StateManagerSingle) Cosigned(_ context.Context) (*types.SignedTreeHead, error) {
+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
}
@@ -126,7 +132,7 @@ func (sm *StateManagerSingle) AddCosignature(_ context.Context, vk *[types.Verif
// 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.TreeHead, sm.tosign.TreeHead) {
+ 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 {
@@ -142,13 +148,11 @@ func (sm *StateManagerSingle) rotate(next *types.SignedTreeHead) {
}
// Update cosigned tree head
- sm.cosigned.TreeHead = sm.tosign.TreeHead
+ 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{
- *next.SigIdent[0].KeyHash: next.SigIdent[0], // log signature
- }
+ 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/state/state_manager_test.go b/pkg/state/state_manager_test.go
index acc2319..6650544 100644
--- a/pkg/state/state_manager_test.go
+++ b/pkg/state/state_manager_test.go
@@ -23,14 +23,21 @@ var (
Timestamp: 0,
TreeSize: 0,
RootHash: types.Hash(nil),
+ KeyHash: types.Hash(testPub[:]),
}
testSigIdent = &types.SigIdent{
Signature: testSig,
KeyHash: types.Hash(testPub[:]),
}
testSTH = &types.SignedTreeHead{
- TreeHead: *testTH,
- SigIdent: []*types.SigIdent{testSigIdent},
+ TreeHead: *testTH,
+ Signature: testSig,
+ }
+ testCTH = &types.CosignedTreeHead{
+ SignedTreeHead: *testSTH,
+ SigIdent: []*types.SigIdent{
+ testSigIdent,
+ },
}
testSignerOK = &mocks.TestSigner{testPub, testSig, nil}
testSignerErr = &mocks.TestSigner{testPub, testSig, fmt.Errorf("something went wrong")}
@@ -72,14 +79,14 @@ func TestNewStateManagerSingle(t *testing.T) {
if err != nil {
return
}
- if got, want := &sm.cosigned, table.wantSth; !reflect.DeepEqual(got, want) {
+ 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.cosignature), 1; got != want {
+ if got, want := len(sm.cosignature), 0; got != want {
t.Errorf("got %d cosignatures but wanted %d in test %q", got, want, table.description)
}
}()
@@ -157,16 +164,23 @@ func TestToSign(t *testing.T) {
func TestCosigned(t *testing.T) {
description := "valid"
sm := StateManagerSingle{
- cosigned: *testSTH,
+ cosigned: *testCTH,
}
- sth, err := sm.Cosigned(context.Background())
+ cth, err := sm.Cosigned(context.Background())
if err != nil {
t.Errorf("Cosigned should not fail with error: %v", err)
return
}
- if got, want := sth, testSTH; !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)
+ cth, err = sm.Cosigned(context.Background())
+ if err == nil {
+ t.Errorf("Cosigned should fail without witness cosignatures")
+ return
+ }
}
func TestAddCosignature(t *testing.T) {
@@ -202,15 +216,15 @@ func TestAddCosignature(t *testing.T) {
},
} {
sth, _ := table.th.Sign(testSignerOK)
- logKeyHash := sth.SigIdent[0].KeyHash
- logSigIdent := sth.SigIdent[0]
+ cth := &types.CosignedTreeHead{
+ SignedTreeHead: *sth,
+ SigIdent: []*types.SigIdent{},
+ }
sm := &StateManagerSingle{
- signer: testSignerOK,
- cosigned: *sth,
- tosign: *sth,
- cosignature: map[[types.HashSize]byte]*types.SigIdent{
- *logKeyHash: logSigIdent,
- },
+ signer: testSignerOK,
+ cosigned: *cth,
+ tosign: *sth,
+ cosignature: map[[types.HashSize]byte]*types.SigIdent{},
}
// Prepare witness signature
@@ -218,11 +232,13 @@ func TestAddCosignature(t *testing.T) {
if err != nil {
t.Fatalf("Sign: %v", err)
}
- witnessKeyHash := sth.SigIdent[0].KeyHash
- witnessSigIdent := sth.SigIdent[0]
+ si := &types.SigIdent{
+ KeyHash: types.Hash(table.signer.Public().(ed25519.PublicKey)[:]),
+ Signature: sth.Signature,
+ }
// Add witness signature
- err = sm.AddCosignature(context.Background(), table.vk, witnessSigIdent.Signature)
+ err = sm.AddCosignature(context.Background(), table.vk, si.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)
}
@@ -230,33 +246,24 @@ func TestAddCosignature(t *testing.T) {
continue
}
- // We should have two signatures (log + witness)
- if got, want := len(sm.cosignature), 2; got != want {
+ // We should have one witness signature
+ if got, want := len(sm.cosignature), 1; got != want {
t.Errorf("got %d cosignatures but wanted %v in test %q", got, want, table.description)
continue
}
- // check that log signature is there
- sigident, ok := sm.cosignature[*logKeyHash]
- if !ok {
- t.Errorf("log signature is missing")
- continue
- }
- if got, want := sigident, logSigIdent; !reflect.DeepEqual(got, want) {
- t.Errorf("got log sigident\n\t%v\nbut wanted\n\t%v\nin test %q", got, want, table.description)
- }
// check that witness signature is there
- sigident, ok = sm.cosignature[*witnessKeyHash]
+ sigident, ok := sm.cosignature[*si.KeyHash]
if !ok {
t.Errorf("witness signature is missing")
continue
}
- if got, want := sigident, witnessSigIdent; !reflect.DeepEqual(got, want) {
+ if got, want := si, sigident; !reflect.DeepEqual(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, witnessSigIdent.Signature); err == nil {
+ if err := sm.AddCosignature(context.Background(), table.vk, si.Signature); err == nil {
t.Errorf("duplicate witness signature accepted as valid")
}
}
@@ -292,81 +299,86 @@ func TestRotate(t *testing.T) {
{
description: "tosign tree head repated, but got one new witnes signature",
before: &StateManagerSingle{
- cosigned: types.SignedTreeHead{
- TreeHead: *th0,
- SigIdent: []*types.SigIdent{log, wit1},
+ cosigned: types.CosignedTreeHead{
+ SignedTreeHead: types.SignedTreeHead{
+ TreeHead: *th0,
+ Signature: log.Signature,
+ },
+ SigIdent: []*types.SigIdent{wit1},
},
tosign: types.SignedTreeHead{
- TreeHead: *th0,
- SigIdent: []*types.SigIdent{log},
+ TreeHead: *th0,
+ Signature: log.Signature,
},
cosignature: map[[types.HashSize]byte]*types.SigIdent{
- *log.KeyHash: log,
*wit2.KeyHash: wit2, // the new witness signature
},
},
next: &types.SignedTreeHead{
- TreeHead: *th1,
- SigIdent: []*types.SigIdent{log},
+ TreeHead: *th1,
+ Signature: log.Signature,
},
after: &StateManagerSingle{
- cosigned: types.SignedTreeHead{
- TreeHead: *th0,
- SigIdent: []*types.SigIdent{log, wit1, wit2},
+ cosigned: types.CosignedTreeHead{
+ SignedTreeHead: types.SignedTreeHead{
+ TreeHead: *th0,
+ Signature: log.Signature,
+ },
+ SigIdent: []*types.SigIdent{wit1, wit2},
},
tosign: types.SignedTreeHead{
- TreeHead: *th1,
- SigIdent: []*types.SigIdent{log},
- },
- cosignature: map[[types.HashSize]byte]*types.SigIdent{
- *log.KeyHash: log, // after rotate we always have log sig
+ TreeHead: *th1,
+ Signature: log.Signature,
},
+ cosignature: map[[types.HashSize]byte]*types.SigIdent{},
},
},
{
description: "tosign tree head did not repeat, it got one witness signature",
before: &StateManagerSingle{
- cosigned: types.SignedTreeHead{
- TreeHead: *th0,
- SigIdent: []*types.SigIdent{log, wit1},
+ cosigned: types.CosignedTreeHead{
+ SignedTreeHead: types.SignedTreeHead{
+ TreeHead: *th0,
+ Signature: log.Signature,
+ },
+ SigIdent: []*types.SigIdent{wit1},
},
tosign: types.SignedTreeHead{
- TreeHead: *th1,
- SigIdent: []*types.SigIdent{log},
+ TreeHead: *th1,
+ Signature: log.Signature,
},
cosignature: map[[types.HashSize]byte]*types.SigIdent{
- *log.KeyHash: log,
- *wit2.KeyHash: wit2, // the only witness that signed tosign
+ *log.KeyHash: wit2,
},
},
next: &types.SignedTreeHead{
- TreeHead: *th2,
- SigIdent: []*types.SigIdent{log},
+ TreeHead: *th2,
+ Signature: log.Signature,
},
after: &StateManagerSingle{
- cosigned: types.SignedTreeHead{
- TreeHead: *th1,
- SigIdent: []*types.SigIdent{log, wit2},
+ cosigned: types.CosignedTreeHead{
+ SignedTreeHead: types.SignedTreeHead{
+ TreeHead: *th1,
+ Signature: log.Signature,
+ },
+ SigIdent: []*types.SigIdent{wit2},
},
tosign: types.SignedTreeHead{
- TreeHead: *th2,
- SigIdent: []*types.SigIdent{log},
- },
- cosignature: map[[types.HashSize]byte]*types.SigIdent{
- *log.KeyHash: log, // after rotate we always have log sig
+ TreeHead: *th2,
+ Signature: log.Signature,
},
+ cosignature: map[[types.HashSize]byte]*types.SigIdent{},
},
},
} {
table.before.rotate(table.next)
- if got, want := table.before.cosigned.TreeHead, table.after.cosigned.TreeHead; !reflect.DeepEqual(got, want) {
+ 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.TreeHead, table.after.tosign.TreeHead; !reflect.DeepEqual(got, want) {
+ 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.tosign.SigIdent, table.after.tosign.SigIdent)
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)
}
diff --git a/pkg/types/ascii.go b/pkg/types/ascii.go
index f0a96ad..72abfcb 100644
--- a/pkg/types/ascii.go
+++ b/pkg/types/ascii.go
@@ -18,7 +18,7 @@ const (
// NumField* is the number of unique keys in an incoming ASCII message
NumFieldLeaf = 4
- NumFieldSignedTreeHead = 5
+ NumFieldSignedTreeHead = 4
NumFieldConsistencyProof = 3
NumFieldInclusionProof = 3
NumFieldLeavesRequest = 2
@@ -28,11 +28,11 @@ const (
NumFieldCosignatureRequest = 2
// New leaf keys
- ShardHint = "shard_hint"
- Checksum = "checksum"
- Signature = "signature"
- VerificationKey = "verification_key"
- DomainHint = "domain_hint"
+ ShardHint = "shard_hint"
+ Checksum = "checksum"
+ Signature = "signature"
+ VerificationKey = "verification_key"
+ DomainHint = "domain_hint"
// Inclusion proof keys
LeafHash = "leaf_hash"
@@ -53,8 +53,9 @@ const (
TreeSize = "tree_size"
RootHash = "root_hash"
- // Signature and signer-identity keys
- KeyHash = "key_hash"
+ // Witness signature-identity keys
+ KeyHash = "key_hash"
+ Cosignature = "cosignature"
)
// MessageASCI is a wrapper that manages ASCII key-value pairs
@@ -219,19 +220,29 @@ func (sth *SignedTreeHead) MarshalASCII(w io.Writer) error {
if err := writeASCII(w, RootHash, hex.EncodeToString(sth.RootHash[:])); err != nil {
return fmt.Errorf("writeASCII: %v", err)
}
- for _, sigident := range sth.SigIdent {
- if err := sigident.MarshalASCII(w); err != nil {
- return fmt.Errorf("MarshalASCII: %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, Signature, hex.EncodeToString(si.Signature[:])); err != nil {
+ if err := writeASCII(w, KeyHash, hex.EncodeToString(si.KeyHash[:])); err != nil {
return fmt.Errorf("writeASCII: %v", err)
}
- if err := writeASCII(w, KeyHash, hex.EncodeToString(si.KeyHash[:])); err != nil {
+ if err := writeASCII(w, Cosignature, hex.EncodeToString(si.Signature[:])); err != nil {
return fmt.Errorf("writeASCII: %v", err)
}
return nil
@@ -280,7 +291,6 @@ func (sth *SignedTreeHead) UnmarshalASCII(r io.Reader) error {
return fmt.Errorf("NewMessageASCII: %v", err)
}
- // TreeHead
if sth.Timestamp, err = msg.GetUint64(Timestamp); err != nil {
return fmt.Errorf("GetUint64(Timestamp): %v", err)
}
@@ -290,30 +300,8 @@ func (sth *SignedTreeHead) UnmarshalASCII(r io.Reader) error {
if sth.RootHash, err = msg.GetHash(RootHash); err != nil {
return fmt.Errorf("GetHash(RootHash): %v", err)
}
-
- // SigIdent
- signatures := msg.GetStrings(Signature)
- if len(signatures) == 0 {
- return fmt.Errorf("no signer")
- }
- keyHashes := msg.GetStrings(KeyHash)
- if len(signatures) != len(keyHashes) {
- return fmt.Errorf("mismatched signature-signer count")
- }
- sth.SigIdent = make([]*SigIdent, 0, len(signatures))
- for i, n := 0, len(signatures); i < n; i++ {
- var signature [SignatureSize]byte
- if err := decodeHex(signatures[i], signature[:]); err != nil {
- return fmt.Errorf("decodeHex: %v", err)
- }
- var hash [HashSize]byte
- if err := decodeHex(keyHashes[i], hash[:]); err != nil {
- return fmt.Errorf("decodeHex: %v", err)
- }
- sth.SigIdent = append(sth.SigIdent, &SigIdent{
- Signature: &signature,
- KeyHash: &hash,
- })
+ if sth.Signature, err = msg.GetSignature(Signature); err != nil {
+ return fmt.Errorf("GetHash(RootHash): %v", err)
}
return nil
}
@@ -401,7 +389,7 @@ func (req *CosignatureRequest) UnmarshalASCII(r io.Reader) error {
return fmt.Errorf("NewMessageASCII: %v", err)
}
- if req.Signature, err = msg.GetSignature(Signature); err != nil {
+ if req.Signature, err = msg.GetSignature(Cosignature); err != nil {
return fmt.Errorf("GetSignature: %v", err)
}
if req.KeyHash, err = msg.GetHash(KeyHash); err != nil {
diff --git a/pkg/types/ascii_test.go b/pkg/types/ascii_test.go
index c3f9e98..fc3f486 100644
--- a/pkg/types/ascii_test.go
+++ b/pkg/types/ascii_test.go
@@ -134,26 +134,14 @@ func TestSignedTreeHeadMarshalASCII(t *testing.T) {
TreeSize: 456,
RootHash: testBuffer32,
},
- SigIdent: []*SigIdent{
- &SigIdent{
- Signature: testBuffer64,
- KeyHash: testBuffer32,
- },
- &SigIdent{
- Signature: testBuffer64,
- KeyHash: testBuffer32,
- },
- },
+ Signature: testBuffer64,
}
wantBuf := bytes.NewBufferString(fmt.Sprintf(
- "%s%s%d%s"+"%s%s%d%s"+"%s%s%x%s"+"%s%s%x%s"+"%s%s%x%s"+"%s%s%x%s"+"%s%s%x%s",
+ "%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,
- KeyHash, Delim, testBuffer32[:], EOL,
- Signature, Delim, testBuffer64[:], EOL,
- KeyHash, Delim, testBuffer32[:], EOL,
))
buf := bytes.NewBuffer(nil)
if err := sth.MarshalASCII(buf); err != nil {
@@ -236,14 +224,11 @@ func TestSignedTreeHeadUnmarshalASCII(t *testing.T) {
{
description: "valid",
buf: bytes.NewBufferString(fmt.Sprintf(
- "%s%s%d%s"+"%s%s%d%s"+"%s%s%x%s"+"%s%s%x%s"+"%s%s%x%s"+"%s%s%x%s"+"%s%s%x%s",
+ "%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,
- KeyHash, Delim, testBuffer32[:], EOL,
- Signature, Delim, testBuffer64[:], EOL,
- KeyHash, Delim, testBuffer32[:], EOL,
)),
wantSth: &SignedTreeHead{
TreeHead: TreeHead{
@@ -251,16 +236,7 @@ func TestSignedTreeHeadUnmarshalASCII(t *testing.T) {
TreeSize: 456,
RootHash: testBuffer32,
},
- SigIdent: []*SigIdent{
- &SigIdent{
- Signature: testBuffer64,
- KeyHash: testBuffer32,
- },
- &SigIdent{
- Signature: testBuffer64,
- KeyHash: testBuffer32,
- },
- },
+ Signature: testBuffer64,
},
},
} {
@@ -436,7 +412,7 @@ func TestCosignatureRequestUnmarshalASCII(t *testing.T) {
description: "valid",
buf: bytes.NewBufferString(fmt.Sprintf(
"%s%s%x%s"+"%s%s%x%s",
- Signature, Delim, testBuffer64[:], EOL,
+ Cosignature, Delim, testBuffer64[:], EOL,
KeyHash, Delim, testBuffer32[:], EOL,
)),
wantReq: &CosignatureRequest{
diff --git a/pkg/types/trunnel.go b/pkg/types/trunnel.go
index 268f6f7..5350c5b 100644
--- a/pkg/types/trunnel.go
+++ b/pkg/types/trunnel.go
@@ -10,6 +10,8 @@ const (
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
@@ -30,10 +32,11 @@ func (l *Leaf) Marshal() []byte {
// Marshal returns a Trunnel-encoded tree head
func (th *TreeHead) Marshal() []byte {
- buf := make([]byte, 8+8+HashSize)
+ buf := make([]byte, TreeHeadSize)
binary.BigEndian.PutUint64(buf[0:8], th.Timestamp)
binary.BigEndian.PutUint64(buf[8:16], th.TreeSize)
- copy(buf[16:], th.RootHash[:])
+ copy(buf[16:16+HashSize], th.RootHash[:])
+ copy(buf[16+HashSize:], th.KeyHash[:])
return buf
}
diff --git a/pkg/types/trunnel_test.go b/pkg/types/trunnel_test.go
index 297578c..a3ae1ba 100644
--- a/pkg/types/trunnel_test.go
+++ b/pkg/types/trunnel_test.go
@@ -48,16 +48,18 @@ func TestMarshalLeaf(t *testing.T) {
}
func TestMarshalTreeHead(t *testing.T) {
- description := "valid: timestamp 16909060, tree size 72623859790382856, root hash 0x00,0x01,..."
+ 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)
diff --git a/pkg/types/types.go b/pkg/types/types.go
index 96e2b18..bc58c98 100644
--- a/pkg/types/types.go
+++ b/pkg/types/types.go
@@ -32,78 +32,67 @@ func (e Endpoint) Path(components ...string) string {
return strings.Join(append(components, string(e)), "/")
}
-// Leaf is the log's Merkle tree leaf.
type Leaf struct {
Message
SigIdent
}
-// Message is composed of a shard hint and a checksum. The submitter selects
-// these values to fit the log's shard interval and the opaque data in question.
type Message struct {
ShardHint uint64
Checksum *[HashSize]byte
}
-// SigIdent is composed of a signature-signer pair. The signature is computed
-// over the Trunnel-serialized leaf message. KeyHash identifies the signer.
type SigIdent struct {
Signature *[SignatureSize]byte
KeyHash *[HashSize]byte
}
-// SignedTreeHead is composed of a tree head and a list of signature-signer
-// pairs. Each signature is computed over the Trunnel-serialized tree head.
type SignedTreeHead struct {
TreeHead
+ Signature *[SignatureSize]byte
+}
+
+type CosignedTreeHead struct {
+ SignedTreeHead
SigIdent []*SigIdent
}
-// TreeHead is the log's tree head.
type TreeHead struct {
Timestamp uint64
TreeSize uint64
RootHash *[HashSize]byte
+ KeyHash *[HashSize]byte
}
-// ConsistencyProof is a consistency proof that proves the log's append-only
-// property.
type ConsistencyProof struct {
NewSize uint64
OldSize uint64
Path []*[HashSize]byte
}
-// InclusionProof is an inclusion proof that proves a leaf is included in the
-// log.
type InclusionProof struct {
TreeSize uint64
LeafIndex uint64
Path []*[HashSize]byte
}
-// LeafList is a list of leaves
type LeafList []*Leaf
-// ConsistencyProofRequest is a get-consistency-proof request
type ConsistencyProofRequest struct {
NewSize uint64
OldSize uint64
}
-// InclusionProofRequest is a get-proof-by-hash request
type InclusionProofRequest struct {
LeafHash *[HashSize]byte
TreeSize uint64
}
-// LeavesRequest is a get-leaves request
type LeavesRequest struct {
StartSize uint64
EndSize uint64
}
-// LeafRequest is an add-leaf request
type LeafRequest struct {
Message
Signature *[SignatureSize]byte
@@ -111,7 +100,6 @@ type LeafRequest struct {
DomainHint string
}
-// CosignatureRequest is an add-cosignature request
type CosignatureRequest struct {
SigIdent
}
@@ -123,17 +111,12 @@ func (th *TreeHead) Sign(signer crypto.Signer) (*SignedTreeHead, error) {
return nil, fmt.Errorf("Sign: %v", err)
}
- sigident := SigIdent{
- KeyHash: Hash(signer.Public().(ed25519.PublicKey)[:]),
+ sth := &SignedTreeHead{
+ TreeHead: *th,
Signature: &[SignatureSize]byte{},
}
- copy(sigident.Signature[:], sig)
- return &SignedTreeHead{
- TreeHead: *th,
- SigIdent: []*SigIdent{
- &sigident,
- },
- }, nil
+ copy(sth.Signature[:], sig)
+ return sth, nil
}
// Verify verifies the tree head signature using the log's signature scheme