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/state_manager.go | |
parent | 5d945794190b0088b5a953d4931d8ede31866182 (diff) |
added start on refactored STH source
Diffstat (limited to 'state/state_manager.go')
-rw-r--r-- | state/state_manager.go | 168 |
1 files changed, 168 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 +} |