epoch.go raw
1 package gnarlring
2
3 import (
4 "crypto/rand"
5 "errors"
6 "io"
7 )
8
9 // EpochState holds a coordinator's view of one group epoch, collecting
10 // child commitments and producing a root NTRU signature binding them.
11 // The coordinator holds an NTRU keypair; children hold individual keypairs.
12 type EpochState struct {
13 Counter uint64
14 Coordinator *NTRUPublicKey
15 Agg *AggregatedCommitment
16 RootSig *NTRUSignature
17 finalized bool
18 }
19
20 // StartEpoch initializes a new epoch.
21 func StartEpoch(counter uint64, coordinatorPK *NTRUPublicKey) *EpochState {
22 return &EpochState{
23 Counter: counter,
24 Coordinator: coordinatorPK,
25 Agg: NewAggregatedCommitment(),
26 }
27 }
28
29 // AddCommitment inserts a child commitment into the epoch.
30 func (es *EpochState) AddCommitment(cc *ChildCommitment) error {
31 if es.finalized {
32 return errors.New("gnarlring: epoch already finalized")
33 }
34 if !es.Agg.Add(cc) {
35 return errors.New("gnarlring: commitment slot occupied or index out of range")
36 }
37 return nil
38 }
39
40 // IsComplete reports whether all N slots are filled.
41 func (es *EpochState) IsComplete() bool {
42 return es.Agg.IsComplete()
43 }
44
45 // Finalize produces the root signature binding all commitments under
46 // the coordinator's NTRU key. Returns error if not all slots are filled.
47 func (es *EpochState) Finalize(coordinatorSK *NTRUPrivateKey, msg []byte) error {
48 return es.FinalizeFrom(coordinatorSK, msg, rand.Reader)
49 }
50
51 // FinalizeFrom signs with a given randomness source.
52 func (es *EpochState) FinalizeFrom(coordinatorSK *NTRUPrivateKey, msg []byte, rng io.Reader) error {
53 if !es.IsComplete() {
54 return errors.New("gnarlring: epoch not complete")
55 }
56 if es.finalized {
57 return errors.New("gnarlring: epoch already finalized")
58 }
59
60 target := es.Agg.Target(msg, es.Counter)
61 sig := NTRUSignTarget(coordinatorSK, target, rng)
62 es.RootSig = sig
63 es.finalized = true
64 return nil
65 }
66
67 // Verify checks the epoch state: reconstructs target from commitments,
68 // verifies root sig under coordinator's public key.
69 func (es *EpochState) Verify(coordinatorPK *NTRUPublicKey, msg []byte) bool {
70 if !es.finalized || es.RootSig == nil {
71 return false
72 }
73 target := es.Agg.Target(msg, es.Counter)
74 return NTRUVerifyTarget(coordinatorPK, target, es.RootSig)
75 }
76
77 // RotateMember replaces one child's commitment. Marks epoch as not finalized.
78 func (es *EpochState) RotateMember(index uint8, newCC *ChildCommitment) {
79 if index >= N {
80 return
81 }
82 es.Agg.Remove(index)
83 es.Agg.Add(newCC)
84 es.finalized = false
85 es.RootSig = nil
86 }
87
88 // IsFinalized reports whether RootSig has been produced.
89 func (es *EpochState) IsFinalized() bool { return es.finalized }
90