diff options
| author | Rasmus Dahlberg <rasmus.dahlberg@kau.se> | 2021-06-02 18:30:55 +0200 | 
|---|---|---|
| committer | Rasmus Dahlberg <rasmus.dahlberg@kau.se> | 2021-06-02 18:30:55 +0200 | 
| commit | 4c7b24401cfda82f61ae87320c72639fc499c32e (patch) | |
| tree | 547494caaccabb97cdb780fb73d92a8d34631249 /state | |
| parent | 5d945794190b0088b5a953d4931d8ede31866182 (diff) | |
added start on refactored STH source
Diffstat (limited to 'state')
| -rw-r--r-- | state/state_manager.go | 168 | ||||
| -rw-r--r-- | state/state_manager_test.go | 26 | 
2 files changed, 194 insertions, 0 deletions
| diff --git a/state/state_manager.go b/state/state_manager.go new file mode 100644 index 0000000..21c3eda --- /dev/null +++ b/state/state_manager.go @@ -0,0 +1,168 @@ +package stfe + +import ( +	"context" +	"crypto" +	"crypto/ed25519" +	"fmt" +	"reflect" +	"sync" +	"time" + +	"github.com/golang/glog" +	"github.com/google/certificate-transparency-go/schedule" +	"github.com/system-transparency/stfe/trillian" +	"github.com/system-transparency/stfe/types" +) + +// StateManager coordinates access to the 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.SignedTreeHead, error) +	AddCosignature(context.Context, ed25519.PublicKey, *[types.SignatureSize]byte) 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.SignedTreeHead + +	// 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 = *sth +	sm.tosign = *sth +	sm.cosignature = make(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) +		if err != nil { +			glog.Warningf("rotate failed: Latest: %v", err) +			return +		} + +		sm.Lock() +		defer sm.Unlock() +		sm.rotate(nextTreeHead) +	}) +} + +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) +	} +	sth, err := sign(sm.signer, th) +	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.SignedTreeHead, error) { +	sm.RLock() +	defer sm.RUnlock() +	return &sm.cosigned, nil +} + +func (sm *StateManagerSingle) AddCosignature(_ context.Context, vk ed25519.PublicKey, sig *[types.SignatureSize]byte) error { +	sm.Lock() +	defer sm.Unlock() + +	if msg := sm.tosign.TreeHead.Marshal(); !ed25519.Verify(vk, msg, sig[:]) { +		return fmt.Errorf("invalid signature for tree head with timestamp: %d", sm.tosign.Timestamp) +	} +	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.TreeHead, sm.tosign.TreeHead) { +		for _, sigident := range sm.cosigned.SigIdent[1:] { // skip log sigident +			if _, ok := sm.cosignature[*sigident.KeyHash]; !ok { +				sm.cosignature[*sigident.KeyHash] = sigident +			} +		} +	} +	// cosignatures will contain all cosignatures (even if repeated tree head) +	var cosignatures []*types.SigIdent +	for _, sigident := range sm.cosignature { +		cosignatures = append(cosignatures, sigident) +	} + +	// Update cosigned tree head +	sm.cosigned.TreeHead = sm.tosign.TreeHead +	sm.cosigned.SigIdent = append(sm.tosign.SigIdent, cosignatures...) + +	// Update to-sign tree head +	sm.tosign = *next +	sm.cosignature = make(map[[types.HashSize]byte]*types.SigIdent) +	glog.V(3).Infof("rotated sth") +} + +func sign(signer crypto.Signer, th *types.TreeHead) (*types.SignedTreeHead, error) { +	sig, err := signer.Sign(nil, th.Marshal(), crypto.Hash(0)) +	if err != nil { +		return nil, fmt.Errorf("Sign: %v", err) +	} + +	sigident := types.SigIdent{ +		KeyHash:   types.Hash(signer.Public().(ed25519.PublicKey)[:]), +		Signature: &[types.SignatureSize]byte{}, +	} +	copy(sigident.Signature[:], sig) +	return &types.SignedTreeHead{ +		TreeHead: *th, +		SigIdent: []*types.SigIdent{ +			&sigident, +		}, +	}, nil +} diff --git a/state/state_manager_test.go b/state/state_manager_test.go new file mode 100644 index 0000000..6db8592 --- /dev/null +++ b/state/state_manager_test.go @@ -0,0 +1,26 @@ +package stfe + +import ( +	"testing" +) + +func TestNewStateManagerSingle(t *testing.T) { +} + +func TestLatest(t *testing.T) { +} + +func TestToSign(t *testing.T) { +} + +func TestCosigned(t *testing.T) { +} + +func TestAddCosignature(t *testing.T) { +} + +func TestRotate(t *testing.T) { +} + +func TestSign(t *testing.T) { +} | 
