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