@@ -39,6 +39,7 @@ type Store interface {
3939 // GetPublicKey retrieves a node's public key by its ID
4040 GetPublicKey (nodeID string ) ([]byte , error )
4141 VerifyInitiatorMessage (msg types.InitiatorMessage ) error
42+ AuthorizeInitiatorMessage (msg types.InitiatorMessage ) error
4243 SignMessage (msg * types.TssMessage ) ([]byte , error )
4344 VerifyMessage (msg * types.TssMessage ) error
4445
@@ -69,25 +70,24 @@ const (
6970 AlgorithmP256 SignatureAlgorithm = "p256"
7071)
7172
72- // AuthorizerInfo represents a single authorizer with their public key and algorithm
73- type AuthorizerInfo struct {
74- PublicKey string `json:"public_key"`
75- Algorithm SignatureAlgorithm `json:"algorithm"`
73+ type AuthorizerID string
74+
75+ // AuthorizerPublicKey represents a single authorizer with their public key and algorithm
76+ type AuthorizerPublicKey struct {
77+ PublicKey string `json:"public_key" mapstructure:"public_key"`
78+ Algorithm SignatureAlgorithm `json:"algorithm" mapstructure:"algorithm"`
7679}
7780
78- // AuthorizationConfig holds the cached authorization configuration
7981type AuthorizationConfig struct {
80- Enabled bool `mapstructure:"enabled"`
81- RequiredAuthorizers int `mapstructure:"required_authorizers"`
82- AuthorizerPublicKeys map [string ]AuthorizerInfo `mapstructure:"authorizer_public_keys"`
83- Authorizers map [string ]AuthorizerInfo `mapstructure:"authorizers"` // backward compatibility
82+ Enabled bool `mapstructure:"enabled"`
83+ RequiredAuthorizers []AuthorizerID `mapstructure:"required_authorizers"`
84+ AuthorizerPublicKeys map [AuthorizerID ]AuthorizerPublicKey `mapstructure:"authorizer_public_keys"`
8485}
8586
8687// AuthorizerConfigEntry represents the raw configuration for an authorizer
8788type AuthorizerConfigEntry struct {
8889 PublicKey string `mapstructure:"public_key"`
8990 Algorithm string `mapstructure:"algorithm"`
90- Pubkey string `mapstructure:"pubkey"` // backward compatibility
9191}
9292
9393// fileStore implements the Store interface using the filesystem
@@ -99,10 +99,11 @@ type fileStore struct {
9999 publicKeys map [string ][]byte
100100 mu sync.RWMutex
101101
102- privateKey []byte
103- initiatorKey * InitiatorKey
104- symmetricKeys map [string ][]byte
105- authConfig AuthorizationConfig
102+ privateKey []byte
103+ initiatorKey * InitiatorKey
104+ symmetricKeys map [string ][]byte
105+ authConfig AuthorizationConfig
106+ cachedAuthorizerKeys map [AuthorizerID ]any // ed25519.PublicKey or *ecdsa.PublicKey
106107}
107108
108109// NewFileStore creates a new identity store
@@ -143,7 +144,6 @@ func NewFileStore(identityDir, nodeName string, decrypt bool, agePasswordFile st
143144 publicKeys : make (map [string ][]byte ),
144145 privateKey : privateKey ,
145146 initiatorKey : initiatorKey ,
146- authorizerInfo : make (map [string ]AuthorizerInfo ),
147147 symmetricKeys : make (map [string ][]byte ),
148148 }
149149
@@ -188,6 +188,11 @@ func NewFileStore(identityDir, nodeName string, decrypt bool, agePasswordFile st
188188 store .publicKeys [identity .NodeID ] = key
189189 }
190190
191+ err = store .loadAuthorizationConfig ()
192+ if err != nil {
193+ return nil , err
194+ }
195+
191196 return store , nil
192197}
193198
@@ -239,6 +244,42 @@ func loadInitiatorKeys() (*InitiatorKey, error) {
239244 return initiatorKey , nil
240245}
241246
247+ // loadAuthorizationConfig loads and caches the authorization configuration
248+ func (s * fileStore ) loadAuthorizationConfig () error {
249+ var authConfig AuthorizationConfig
250+ if err := viper .UnmarshalKey ("authorization" , & authConfig ); err != nil {
251+ return fmt .Errorf ("failed to unmarshal authorization config: %w" , err )
252+ }
253+ s .authConfig = authConfig
254+ if ! authConfig .Enabled {
255+ return nil
256+ }
257+
258+ for id , key := range authConfig .AuthorizerPublicKeys {
259+ switch key .Algorithm {
260+ case AlgorithmEd25519 :
261+ pubKeyBytes , err := encryption .ParseEd25519PublicKeyFromHex (key .PublicKey )
262+ if err != nil {
263+ logger .Fatal ("Invalid authorization config" , fmt .Errorf ("invalid ed25519 public key for authorizer %s: %w" , id , err ))
264+ }
265+ s .cachedAuthorizerKeys [id ] = ed25519 .PublicKey (pubKeyBytes )
266+
267+ case AlgorithmP256 :
268+ pubKey , err := encryption .ParseP256PublicKeyFromHex (key .PublicKey )
269+ if err != nil {
270+ logger .Fatal ("Invalid authorization config" , fmt .Errorf ("invalid P256 public key for authorizer %s: %w" , id , err ))
271+ }
272+ s .cachedAuthorizerKeys [id ] = pubKey
273+
274+ default :
275+ logger .Fatal ("Invalid authorization config" , fmt .Errorf ("unknown algorithm %s for authorizer %s" , key .Algorithm , id ))
276+ }
277+ }
278+
279+ logger .Info ("Loaded authorization config" , "authConfig" , authConfig )
280+ return nil
281+ }
282+
242283// loadEd25519InitiatorKey loads Ed25519 initiator public key
243284func loadEd25519InitiatorKey () ([]byte , error ) {
244285 pubKeyHex := viper .GetString ("event_initiator_pubkey" )
@@ -534,6 +575,65 @@ func (s *fileStore) VerifyInitiatorMessage(msg types.InitiatorMessage) error {
534575 return fmt .Errorf ("unsupported algorithm: %s" , algo )
535576}
536577
578+ func (s * fileStore ) AuthorizeInitiatorMessage (msg types.InitiatorMessage ) error {
579+ if ! s .authConfig .Enabled {
580+ return nil
581+ }
582+ sigs := msg .GetAuthorizerSignatures ()
583+ if len (sigs ) == 0 {
584+ return nil // skip as no signatures
585+ }
586+
587+ authorizerRaw , err := types .ComposeAuthorizerRaw (msg )
588+ if err != nil {
589+ return fmt .Errorf ("failed to compose authorizer raw: %w" , err )
590+ }
591+
592+ // Verify each authorizer signature
593+ for _ , sig := range sigs {
594+ if err := s .verifyAuthorizerSignature (authorizerRaw , sig ); err != nil {
595+ return fmt .Errorf ("authorizer %s verification failed: %w" , sig .AuthorizerID , err )
596+ }
597+ }
598+
599+ return nil
600+ }
601+
602+ func (s * fileStore ) verifyAuthorizerSignature (raw []byte , sig types.AuthorizerSignature ) error {
603+ authPub , ok := s .cachedAuthorizerKeys [AuthorizerID (sig .AuthorizerID )]
604+ if ! ok {
605+ return fmt .Errorf ("authorizer %s not found in cache" , sig .AuthorizerID )
606+ }
607+
608+ keyMeta := s .authConfig .AuthorizerPublicKeys [AuthorizerID (sig .AuthorizerID )]
609+ switch keyMeta .Algorithm {
610+ case AlgorithmEd25519 :
611+ pub := authPub .(ed25519.PublicKey )
612+ if ! ed25519 .Verify (pub , raw , sig .Signature ) {
613+ return fmt .Errorf ("ed25519 verification failed for %s" , sig .AuthorizerID )
614+ }
615+
616+ case AlgorithmP256 :
617+ pub := authPub .(* ecdsa.PublicKey )
618+ if err := encryption .VerifyP256Signature (pub , raw , sig .Signature ); err != nil {
619+ return fmt .Errorf ("p256 verification failed for %s: %w" , sig .AuthorizerID , err )
620+ }
621+
622+ default :
623+ return fmt .Errorf ("unsupported algorithm %q for authorizer %s" , keyMeta .Algorithm , sig .AuthorizerID )
624+ }
625+
626+ return nil
627+ }
628+
629+ func (s * fileStore ) getAuthorizerPublicKey (authorizerID string ) (* AuthorizerPublicKey , error ) {
630+ publicKey , ok := s .authConfig .AuthorizerPublicKeys [AuthorizerID (authorizerID )]
631+ if ! ok {
632+ return nil , fmt .Errorf ("unknown authorizer ID: %s" , authorizerID )
633+ }
634+ return & publicKey , nil
635+ }
636+
537637func (s * fileStore ) verifyEd25519 (msg types.InitiatorMessage ) error {
538638 msgBytes , err := msg .Raw ()
539639 if err != nil {
0 commit comments