Skip to content

Commit 1c64be4

Browse files
committed
Refactor initiator message authorization
1 parent 2f4c19d commit 1c64be4

3 files changed

Lines changed: 822 additions & 433 deletions

File tree

pkg/identity/identity.go

Lines changed: 115 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -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
7981
type 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
8788
type 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
243284
func 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+
537637
func (s *fileStore) verifyEd25519(msg types.InitiatorMessage) error {
538638
msgBytes, err := msg.Raw()
539639
if err != nil {

0 commit comments

Comments
 (0)