 9bdcbe0447
			
		
	
	9bdcbe0447
	
	
	
		
			
			Major integrations and fixes: - Added BACKBEAT SDK integration for P2P operation timing - Implemented beat-aware status tracking for distributed operations - Added Docker secrets support for secure license management - Resolved KACHING license validation via HTTPS/TLS - Updated docker-compose configuration for clean stack deployment - Disabled rollback policies to prevent deployment failures - Added license credential storage (CHORUS-DEV-MULTI-001) Technical improvements: - BACKBEAT P2P operation tracking with phase management - Enhanced configuration system with file-based secrets - Improved error handling for license validation - Clean separation of KACHING and CHORUS deployment stacks 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
		
			
				
	
	
		
			1295 lines
		
	
	
		
			44 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			1295 lines
		
	
	
		
			44 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| // Package crypto provides sophisticated key management for role-based encryption.
 | |
| //
 | |
| // This module implements enterprise-grade key management with features including:
 | |
| // - Hierarchical role-based key derivation
 | |
| // - Automated key rotation with configurable policies
 | |
| // - Key escrow and recovery mechanisms
 | |
| // - Hardware Security Module (HSM) integration support
 | |
| // - Zero-knowledge key verification
 | |
| // - Perfect forward secrecy through ephemeral keys
 | |
| //
 | |
| // Security Features:
 | |
| // - Key derivation using PBKDF2 with configurable iterations
 | |
| // - Key verification without exposing key material
 | |
| // - Secure key storage with encryption at rest
 | |
| // - Key rotation logging and audit trails
 | |
| // - Emergency key revocation capabilities
 | |
| //
 | |
| // Cross-references:
 | |
| //   - pkg/crypto/role_crypto.go: Role-based encryption implementation
 | |
| //   - pkg/crypto/shamir.go: Shamir secret sharing for admin keys
 | |
| //   - pkg/config/roles.go: Role definitions and permissions
 | |
| 
 | |
| package crypto
 | |
| 
 | |
| import (
 | |
| 	"crypto/rand"
 | |
| 	"crypto/sha256"
 | |
| 	"encoding/hex"
 | |
| 	"fmt"
 | |
| 	"sync"
 | |
| 	"time"
 | |
| 
 | |
| 	"golang.org/x/crypto/pbkdf2"
 | |
| 	"chorus/pkg/config"
 | |
| 	"chorus/pkg/security"
 | |
| )
 | |
| 
 | |
| // Type aliases for backward compatibility
 | |
| type AccessLevel = security.AccessLevel
 | |
| 
 | |
| // AuditLogger interface for audit logging
 | |
| type AuditLogger interface {
 | |
| 	LogAccess(entry *AccessLogEntry) error
 | |
| 	LogKeyRotation(event *KeyRotationEvent) error
 | |
| 	LogSecurityEvent(event *SecurityEvent) error
 | |
| 	GetAuditTrail(criteria *AuditCriteria) ([]*AuditEvent, error)
 | |
| }
 | |
| 
 | |
| // KeyRotationPolicy defines when and how keys should be rotated
 | |
| type KeyRotationPolicy struct {
 | |
| 	RotationInterval  time.Duration `json:"rotation_interval"`   // How often to rotate keys
 | |
| 	MaxKeyAge         time.Duration `json:"max_key_age"`         // Maximum age before forced rotation
 | |
| 	AutoRotate        bool          `json:"auto_rotate"`         // Whether to auto-rotate
 | |
| 	GracePeriod       time.Duration `json:"grace_period"`        // Grace period for old keys
 | |
| 	RequireQuorum     bool          `json:"require_quorum"`      // Whether quorum needed for rotation
 | |
| 	MinQuorumSize     int           `json:"min_quorum_size"`     // Minimum quorum size
 | |
| }
 | |
| 
 | |
| // RoleKeyPair represents encryption keys for a specific role
 | |
| type RoleKeyPair struct {
 | |
| 	PublicKey          string    `json:"public_key"`           // Age public key
 | |
| 	PrivateKey         string    `json:"private_key"`          // Age private key (encrypted)
 | |
| 	EncryptionSalt     []byte    `json:"encryption_salt"`      // Salt for private key encryption
 | |
| 	DerivedKeyHash     string    `json:"derived_key_hash"`     // Hash of derived key for verification
 | |
| 	Version            int       `json:"version"`              // Key version
 | |
| 	CreatedAt          time.Time `json:"created_at"`           // When keys were created
 | |
| 	RotatedAt          *time.Time `json:"rotated_at,omitempty"` // When keys were last rotated
 | |
| }
 | |
| 
 | |
| // AccessLogEntry represents a single access to encrypted context
 | |
| type AccessLogEntry struct {
 | |
| 	AccessTime        time.Time   `json:"access_time"`
 | |
| 	UserID            string      `json:"user_id"`
 | |
| 	Role              string      `json:"role"`
 | |
| 	AccessType        string      `json:"access_type"`      // read, write, decrypt
 | |
| 	Success           bool        `json:"success"`
 | |
| 	FailureReason     string      `json:"failure_reason,omitempty"`
 | |
| 	IPAddress         string      `json:"ip_address"`
 | |
| 	UserAgent         string      `json:"user_agent"`
 | |
| 	AuditTrail        string      `json:"audit_trail"`      // Audit trail reference
 | |
| }
 | |
| 
 | |
| // KeyRotationEvent represents a key rotation event for audit logging
 | |
| type KeyRotationEvent struct {
 | |
| 	EventID           string    `json:"event_id"`
 | |
| 	Timestamp         time.Time `json:"timestamp"`
 | |
| 	RotatedRoles      []string  `json:"rotated_roles"`
 | |
| 	InitiatedBy       string    `json:"initiated_by"`
 | |
| 	Reason            string    `json:"reason"`
 | |
| 	Success           bool      `json:"success"`
 | |
| 	ErrorMessage      string    `json:"error_message,omitempty"`
 | |
| 	PreviousKeyHashes []string  `json:"previous_key_hashes"`
 | |
| 	NewKeyHashes      []string  `json:"new_key_hashes"`
 | |
| }
 | |
| 
 | |
| // SecurityEvent represents a security-related event for audit logging
 | |
| type SecurityEvent struct {
 | |
| 	EventID           string                 `json:"event_id"`
 | |
| 	EventType         string                 `json:"event_type"`
 | |
| 	Timestamp         time.Time              `json:"timestamp"`
 | |
| 	UserID            string                 `json:"user_id"`
 | |
| 	Resource          string                 `json:"resource"`
 | |
| 	Action            string                 `json:"action"`
 | |
| 	Outcome           string                 `json:"outcome"`
 | |
| 	RiskLevel         string                 `json:"risk_level"`
 | |
| 	Details           map[string]interface{} `json:"details"`
 | |
| }
 | |
| 
 | |
| // AuditCriteria represents criteria for querying audit logs
 | |
| type AuditCriteria struct {
 | |
| 	StartTime         *time.Time `json:"start_time,omitempty"`
 | |
| 	EndTime           *time.Time `json:"end_time,omitempty"`
 | |
| 	UserID            string     `json:"user_id,omitempty"`
 | |
| 	Role              string     `json:"role,omitempty"`
 | |
| 	Resource          string     `json:"resource,omitempty"`
 | |
| 	EventType         string     `json:"event_type,omitempty"`
 | |
| 	Limit             int        `json:"limit,omitempty"`
 | |
| }
 | |
| 
 | |
| // AuditEvent represents a generic audit event
 | |
| type AuditEvent struct {
 | |
| 	EventID           string                 `json:"event_id"`
 | |
| 	EventType         string                 `json:"event_type"`
 | |
| 	Timestamp         time.Time              `json:"timestamp"`
 | |
| 	UserID            string                 `json:"user_id"`
 | |
| 	Data              map[string]interface{} `json:"data"`
 | |
| 	IntegrityHash     string                 `json:"integrity_hash,omitempty"`
 | |
| }
 | |
| 
 | |
| // KeyManager handles sophisticated key management for role-based encryption
 | |
| type KeyManager struct {
 | |
| 	mu                sync.RWMutex
 | |
| 	config            *config.Config
 | |
| 	keyStore          KeyStore
 | |
| 	rotationScheduler *KeyRotationScheduler
 | |
| 	auditLogger       AuditLogger
 | |
| 	keyDerivation     *KeyDerivationService
 | |
| 	emergencyKeys     *EmergencyKeyManager
 | |
| }
 | |
| 
 | |
| // KeyStore interface for secure key storage
 | |
| type KeyStore interface {
 | |
| 	StoreKey(keyID string, keyData *SecureKeyData) error
 | |
| 	RetrieveKey(keyID string) (*SecureKeyData, error)
 | |
| 	DeleteKey(keyID string) error
 | |
| 	ListKeys(filter *KeyFilter) ([]*KeyMetadata, error)
 | |
| 	BackupKeys(criteria *BackupCriteria) (*KeyBackup, error)
 | |
| 	RestoreKeys(backup *KeyBackup) error
 | |
| }
 | |
| 
 | |
| // SecureKeyData represents securely stored key data
 | |
| type SecureKeyData struct {
 | |
| 	KeyID             string                 `json:"key_id"`
 | |
| 	KeyType           string                 `json:"key_type"`
 | |
| 	EncryptedKey      []byte                 `json:"encrypted_key"`
 | |
| 	EncryptionMethod  string                 `json:"encryption_method"`
 | |
| 	Salt              []byte                 `json:"salt"`
 | |
| 	IV                []byte                 `json:"iv"`
 | |
| 	KeyHash           string                 `json:"key_hash"`
 | |
| 	Metadata          map[string]interface{} `json:"metadata"`
 | |
| 	CreatedAt         time.Time              `json:"created_at"`
 | |
| 	LastAccessed      time.Time              `json:"last_accessed"`
 | |
| 	AccessCount       int                    `json:"access_count"`
 | |
| 	ExpiresAt         *time.Time             `json:"expires_at,omitempty"`
 | |
| 	Status            KeyStatus              `json:"status"`
 | |
| }
 | |
| 
 | |
| // KeyMetadata represents metadata about a key without the key material
 | |
| type KeyMetadata struct {
 | |
| 	KeyID             string                 `json:"key_id"`
 | |
| 	KeyType           string                 `json:"key_type"`
 | |
| 	RoleID            string                 `json:"role_id"`
 | |
| 	Version           int                    `json:"version"`
 | |
| 	CreatedAt         time.Time              `json:"created_at"`
 | |
| 	ExpiresAt         *time.Time             `json:"expires_at,omitempty"`
 | |
| 	LastRotated       *time.Time             `json:"last_rotated,omitempty"`
 | |
| 	Status            KeyStatus              `json:"status"`
 | |
| 	Usage             *KeyUsageStats         `json:"usage"`
 | |
| 	SecurityLevel     AccessLevel            `json:"security_level"`
 | |
| 	Metadata          map[string]interface{} `json:"metadata"`
 | |
| }
 | |
| 
 | |
| // KeyUsageStats tracks key usage statistics
 | |
| type KeyUsageStats struct {
 | |
| 	TotalAccesses     int                    `json:"total_accesses"`
 | |
| 	LastAccessed      time.Time              `json:"last_accessed"`
 | |
| 	EncryptionCount   int                    `json:"encryption_count"`
 | |
| 	DecryptionCount   int                    `json:"decryption_count"`
 | |
| 	FailedAttempts    int                    `json:"failed_attempts"`
 | |
| 	SuspiciousActivity bool                  `json:"suspicious_activity"`
 | |
| }
 | |
| 
 | |
| // KeyStatus represents the status of a cryptographic key
 | |
| type KeyStatus string
 | |
| 
 | |
| const (
 | |
| 	KeyStatusActive     KeyStatus = "active"     // Key is active and can be used
 | |
| 	KeyStatusInactive   KeyStatus = "inactive"   // Key is inactive
 | |
| 	KeyStatusExpired    KeyStatus = "expired"    // Key has expired
 | |
| 	KeyStatusRevoked    KeyStatus = "revoked"    // Key has been revoked
 | |
| 	KeyStatusSuspended  KeyStatus = "suspended"  // Key is temporarily suspended
 | |
| 	KeyStatusPending    KeyStatus = "pending"    // Key is pending activation
 | |
| )
 | |
| 
 | |
| // RoleKey represents a cryptographic key associated with a role
 | |
| type RoleKey struct {
 | |
| 	KeyID       string      `json:"key_id"`
 | |
| 	RoleID      string      `json:"role_id"`
 | |
| 	KeyType     string      `json:"key_type"`
 | |
| 	Version     int         `json:"version"`
 | |
| 	CreatedAt   time.Time   `json:"created_at"`
 | |
| 	ExpiresAt   *time.Time  `json:"expires_at,omitempty"`
 | |
| 	Status      KeyStatus   `json:"status"`
 | |
| 	KeyData     []byte      `json:"key_data,omitempty"`
 | |
| }
 | |
| 
 | |
| // KeyRotationResult represents the result of a key rotation operation
 | |
| type KeyRotationResult struct {
 | |
| 	Success           bool                   `json:"success"`
 | |
| 	OldKeyID          string                 `json:"old_key_id"`
 | |
| 	NewKeyID          string                 `json:"new_key_id"`
 | |
| 	RotatedAt         time.Time              `json:"rotated_at"`
 | |
| 	RollbackKeyID     string                 `json:"rollback_key_id,omitempty"`
 | |
| 	Error             string                 `json:"error,omitempty"`
 | |
| 	RotationDuration  time.Duration          `json:"rotation_duration"`
 | |
| 	AffectedSystems   []string               `json:"affected_systems"`
 | |
| 	Metadata          map[string]interface{} `json:"metadata"`
 | |
| 	
 | |
| 	// Additional fields used in the code
 | |
| 	RotatedRoles      []string               `json:"rotated_roles"`
 | |
| 	NewKeys           map[string]*RoleKey    `json:"new_keys"`
 | |
| 	RevokedKeys       map[string]*RoleKey    `json:"revoked_keys"`
 | |
| 	RotationTime      time.Duration          `json:"rotation_time"`
 | |
| }
 | |
| 
 | |
| // KeyFilter represents criteria for filtering keys
 | |
| type KeyFilter struct {
 | |
| 	RoleID            string                 `json:"role_id,omitempty"`
 | |
| 	KeyType           string                 `json:"key_type,omitempty"`
 | |
| 	Status            KeyStatus              `json:"status,omitempty"`
 | |
| 	MinSecurityLevel  AccessLevel            `json:"min_security_level,omitempty"`
 | |
| 	CreatedAfter      *time.Time             `json:"created_after,omitempty"`
 | |
| 	CreatedBefore     *time.Time             `json:"created_before,omitempty"`
 | |
| 	ExpiringBefore    *time.Time             `json:"expiring_before,omitempty"`
 | |
| 	IncludeMetadata   bool                   `json:"include_metadata"`
 | |
| }
 | |
| 
 | |
| // BackupCriteria defines criteria for key backup operations
 | |
| type BackupCriteria struct {
 | |
| 	IncludeRoles      []string               `json:"include_roles,omitempty"`
 | |
| 	ExcludeRoles      []string               `json:"exclude_roles,omitempty"`
 | |
| 	MinSecurityLevel  AccessLevel            `json:"min_security_level,omitempty"`
 | |
| 	IncludeExpired    bool                   `json:"include_expired"`
 | |
| 	EncryptionKey     []byte                 `json:"encryption_key"`
 | |
| 	BackupMetadata    map[string]interface{} `json:"backup_metadata"`
 | |
| }
 | |
| 
 | |
| // KeyBackup represents a backup of keys
 | |
| type KeyBackup struct {
 | |
| 	BackupID          string                 `json:"backup_id"`
 | |
| 	CreatedAt         time.Time              `json:"created_at"`
 | |
| 	CreatedBy         string                 `json:"created_by"`
 | |
| 	EncryptedData     []byte                 `json:"encrypted_data"`
 | |
| 	KeyCount          int                    `json:"key_count"`
 | |
| 	Checksum          string                 `json:"checksum"`
 | |
| 	Metadata          map[string]interface{} `json:"metadata"`
 | |
| }
 | |
| 
 | |
| // KeyRotationScheduler manages automated key rotation
 | |
| type KeyRotationScheduler struct {
 | |
| 	mu                sync.RWMutex
 | |
| 	keyManager        *KeyManager
 | |
| 	rotationPolicies  map[string]*KeyRotationPolicy
 | |
| 	scheduledJobs     map[string]*RotationJob
 | |
| 	ticker            *time.Ticker
 | |
| 	stopChannel       chan bool
 | |
| 	running           bool
 | |
| }
 | |
| 
 | |
| // RotationJob represents a scheduled key rotation job
 | |
| type RotationJob struct {
 | |
| 	JobID             string                 `json:"job_id"`
 | |
| 	RoleID            string                 `json:"role_id"`
 | |
| 	ScheduledTime     time.Time              `json:"scheduled_time"`
 | |
| 	LastExecution     *time.Time             `json:"last_execution,omitempty"`
 | |
| 	NextExecution     time.Time              `json:"next_execution"`
 | |
| 	Policy            *KeyRotationPolicy     `json:"policy"`
 | |
| 	Status            RotationJobStatus      `json:"status"`
 | |
| 	ExecutionHistory  []*RotationExecution   `json:"execution_history"`
 | |
| 	CreatedAt         time.Time              `json:"created_at"`
 | |
| 	UpdatedAt         time.Time              `json:"updated_at"`
 | |
| }
 | |
| 
 | |
| // RotationJobStatus represents the status of a rotation job
 | |
| type RotationJobStatus string
 | |
| 
 | |
| const (
 | |
| 	RotationJobActive    RotationJobStatus = "active"
 | |
| 	RotationJobPaused    RotationJobStatus = "paused"
 | |
| 	RotationJobCompleted RotationJobStatus = "completed"
 | |
| 	RotationJobFailed    RotationJobStatus = "failed"
 | |
| )
 | |
| 
 | |
| // RotationExecution represents a single execution of a rotation job
 | |
| type RotationExecution struct {
 | |
| 	ExecutionID       string                 `json:"execution_id"`
 | |
| 	StartTime         time.Time              `json:"start_time"`
 | |
| 	EndTime           *time.Time             `json:"end_time,omitempty"`
 | |
| 	Status            string                 `json:"status"`
 | |
| 	OldKeyID          string                 `json:"old_key_id"`
 | |
| 	NewKeyID          string                 `json:"new_key_id"`
 | |
| 	ErrorMessage      string                 `json:"error_message,omitempty"`
 | |
| 	AffectedContexts  []string               `json:"affected_contexts"`
 | |
| 	VerificationResults *VerificationResults `json:"verification_results"`
 | |
| }
 | |
| 
 | |
| // VerificationResults represents results of key rotation verification
 | |
| type VerificationResults struct {
 | |
| 	KeyGenerationOK   bool                   `json:"key_generation_ok"`
 | |
| 	EncryptionTestOK  bool                   `json:"encryption_test_ok"`
 | |
| 	DecryptionTestOK  bool                   `json:"decryption_test_ok"`
 | |
| 	BackupCreatedOK   bool                   `json:"backup_created_ok"`
 | |
| 	OldKeyRevokedOK   bool                   `json:"old_key_revoked_ok"`
 | |
| 	TestResults       map[string]interface{} `json:"test_results"`
 | |
| }
 | |
| 
 | |
| // KeyDerivationService handles sophisticated key derivation
 | |
| type KeyDerivationService struct {
 | |
| 	mu                sync.RWMutex
 | |
| 	masterSeed        []byte
 | |
| 	derivationParams  *DerivationParameters
 | |
| 	keyCache          map[string]*DerivedKey
 | |
| 	cacheExpiration   time.Duration
 | |
| }
 | |
| 
 | |
| // DerivationParameters defines parameters for key derivation
 | |
| type DerivationParameters struct {
 | |
| 	Algorithm         string    `json:"algorithm"`          // PBKDF2, scrypt, argon2
 | |
| 	Iterations        int       `json:"iterations"`         // Number of iterations
 | |
| 	KeyLength         int       `json:"key_length"`         // Derived key length
 | |
| 	SaltLength        int       `json:"salt_length"`        // Salt length
 | |
| 	MemoryParam       int       `json:"memory_param"`       // Memory parameter for scrypt/argon2
 | |
| 	ParallelismParam  int       `json:"parallelism_param"`  // Parallelism for argon2
 | |
| 	HashFunction      string    `json:"hash_function"`      // Hash function (SHA256, SHA512)
 | |
| 	CreatedAt         time.Time `json:"created_at"`
 | |
| 	UpdatedAt         time.Time `json:"updated_at"`
 | |
| }
 | |
| 
 | |
| // DerivedKey represents a derived key with metadata
 | |
| type DerivedKey struct {
 | |
| 	KeyID             string    `json:"key_id"`
 | |
| 	DerivedKey        []byte    `json:"derived_key"`
 | |
| 	Salt              []byte    `json:"salt"`
 | |
| 	DerivationPath    string    `json:"derivation_path"`
 | |
| 	CreatedAt         time.Time `json:"created_at"`
 | |
| 	ExpiresAt         time.Time `json:"expires_at"`
 | |
| 	UsageCount        int       `json:"usage_count"`
 | |
| 	MaxUsage          int       `json:"max_usage"`
 | |
| }
 | |
| 
 | |
| // EmergencyKeyManager handles emergency key operations
 | |
| type EmergencyKeyManager struct {
 | |
| 	mu                sync.RWMutex
 | |
| 	emergencyKeys     map[string]*EmergencyKey
 | |
| 	recoveryShares    map[string][]*RecoveryShare
 | |
| 	emergencyPolicies map[string]*EmergencyPolicy
 | |
| }
 | |
| 
 | |
| // EmergencyKey represents an emergency key for disaster recovery
 | |
| type EmergencyKey struct {
 | |
| 	KeyID             string                 `json:"key_id"`
 | |
| 	KeyType           string                 `json:"key_type"`
 | |
| 	EncryptedKey      []byte                 `json:"encrypted_key"`
 | |
| 	RecoveryShares    []*RecoveryShare       `json:"recovery_shares"`
 | |
| 	ActivationPolicy  *EmergencyPolicy       `json:"activation_policy"`
 | |
| 	CreatedAt         time.Time              `json:"created_at"`
 | |
| 	LastTested        *time.Time             `json:"last_tested,omitempty"`
 | |
| 	Status            EmergencyKeyStatus     `json:"status"`
 | |
| 	Metadata          map[string]interface{} `json:"metadata"`
 | |
| }
 | |
| 
 | |
| // RecoveryShare represents a recovery share for emergency keys
 | |
| type RecoveryShare struct {
 | |
| 	ShareID           string    `json:"share_id"`
 | |
| 	ShareData         []byte    `json:"share_data"`
 | |
| 	ShareIndex        int       `json:"share_index"`
 | |
| 	Custodian         string    `json:"custodian"`
 | |
| 	CreatedAt         time.Time `json:"created_at"`
 | |
| 	LastVerified      *time.Time `json:"last_verified,omitempty"`
 | |
| 	VerificationHash  string    `json:"verification_hash"`
 | |
| }
 | |
| 
 | |
| // EmergencyPolicy defines when and how emergency keys can be used
 | |
| type EmergencyPolicy struct {
 | |
| 	PolicyID          string                 `json:"policy_id"`
 | |
| 	RequiredShares    int                    `json:"required_shares"`
 | |
| 	AuthorizedRoles   []string               `json:"authorized_roles"`
 | |
| 	TimeConstraints   *TimeConstraints       `json:"time_constraints"`
 | |
| 	ApprovalRequired  bool                   `json:"approval_required"`
 | |
| 	Approvers         []string               `json:"approvers"`
 | |
| 	MaxUsageDuration  time.Duration          `json:"max_usage_duration"`
 | |
| 	LoggingRequired   bool                   `json:"logging_required"`
 | |
| 	NotificationRules []*NotificationRule    `json:"notification_rules"`
 | |
| }
 | |
| 
 | |
| // EmergencyKeyStatus represents the status of emergency keys
 | |
| type EmergencyKeyStatus string
 | |
| 
 | |
| const (
 | |
| 	EmergencyKeyActive   EmergencyKeyStatus = "active"
 | |
| 	EmergencyKeyInactive EmergencyKeyStatus = "inactive"
 | |
| 	EmergencyKeyExpired  EmergencyKeyStatus = "expired"
 | |
| 	EmergencyKeyRevoked  EmergencyKeyStatus = "revoked"
 | |
| )
 | |
| 
 | |
| // TimeConstraints defines time-based constraints for emergency key usage
 | |
| type TimeConstraints struct {
 | |
| 	ValidAfter        *time.Time `json:"valid_after,omitempty"`
 | |
| 	ValidBefore       *time.Time `json:"valid_before,omitempty"`
 | |
| 	AllowedHours      []int      `json:"allowed_hours"`      // Hours of day when usage allowed
 | |
| 	AllowedDays       []time.Weekday `json:"allowed_days"`    // Days of week when usage allowed
 | |
| 	TimezoneRestriction string   `json:"timezone_restriction,omitempty"`
 | |
| }
 | |
| 
 | |
| // NotificationRule defines notification rules for emergency key events
 | |
| type NotificationRule struct {
 | |
| 	RuleID            string                 `json:"rule_id"`
 | |
| 	EventType         string                 `json:"event_type"`
 | |
| 	Recipients        []string               `json:"recipients"`
 | |
| 	NotificationMethod string                `json:"notification_method"`
 | |
| 	Template          string                 `json:"template"`
 | |
| 	Metadata          map[string]interface{} `json:"metadata"`
 | |
| }
 | |
| 
 | |
| // NewKeyManager creates a new key manager instance
 | |
| func NewKeyManager(cfg *config.Config, keyStore KeyStore, auditLogger AuditLogger) (*KeyManager, error) {
 | |
| 	km := &KeyManager{
 | |
| 		config:      cfg,
 | |
| 		keyStore:    keyStore,
 | |
| 		auditLogger: auditLogger,
 | |
| 	}
 | |
| 
 | |
| 	// Initialize key derivation service
 | |
| 	kds, err := NewKeyDerivationService(cfg)
 | |
| 	if err != nil {
 | |
| 		return nil, fmt.Errorf("failed to initialize key derivation service: %w", err)
 | |
| 	}
 | |
| 	km.keyDerivation = kds
 | |
| 
 | |
| 	// Initialize emergency key manager
 | |
| 	km.emergencyKeys = NewEmergencyKeyManager(cfg)
 | |
| 
 | |
| 	// Initialize rotation scheduler
 | |
| 	scheduler, err := NewKeyRotationScheduler(km)
 | |
| 	if err != nil {
 | |
| 		return nil, fmt.Errorf("failed to initialize rotation scheduler: %w", err)
 | |
| 	}
 | |
| 	km.rotationScheduler = scheduler
 | |
| 
 | |
| 	// Start enforcing SecurityConfig if configured
 | |
| 	if err := km.enforceSecurityConfig(); err != nil {
 | |
| 		return nil, fmt.Errorf("failed to enforce security config: %w", err)
 | |
| 	}
 | |
| 
 | |
| 	return km, nil
 | |
| }
 | |
| 
 | |
| // NewKeyDerivationService creates a new key derivation service
 | |
| func NewKeyDerivationService(cfg *config.Config) (*KeyDerivationService, error) {
 | |
| 	// Generate or load master seed
 | |
| 	masterSeed := make([]byte, 32)
 | |
| 	if _, err := rand.Read(masterSeed); err != nil {
 | |
| 		return nil, fmt.Errorf("failed to generate master seed: %w", err)
 | |
| 	}
 | |
| 
 | |
| 	params := &DerivationParameters{
 | |
| 		Algorithm:        "PBKDF2",
 | |
| 		Iterations:       100000,
 | |
| 		KeyLength:        32,
 | |
| 		SaltLength:       16,
 | |
| 		MemoryParam:      0,
 | |
| 		ParallelismParam: 0,
 | |
| 		HashFunction:     "SHA256",
 | |
| 		CreatedAt:        time.Now(),
 | |
| 		UpdatedAt:        time.Now(),
 | |
| 	}
 | |
| 
 | |
| 	return &KeyDerivationService{
 | |
| 		masterSeed:       masterSeed,
 | |
| 		derivationParams: params,
 | |
| 		keyCache:         make(map[string]*DerivedKey),
 | |
| 		cacheExpiration:  1 * time.Hour,
 | |
| 	}, nil
 | |
| }
 | |
| 
 | |
| // NewEmergencyKeyManager creates a new emergency key manager
 | |
| func NewEmergencyKeyManager(cfg *config.Config) *EmergencyKeyManager {
 | |
| 	return &EmergencyKeyManager{
 | |
| 		emergencyKeys:     make(map[string]*EmergencyKey),
 | |
| 		recoveryShares:    make(map[string][]*RecoveryShare),
 | |
| 		emergencyPolicies: make(map[string]*EmergencyPolicy),
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // NewKeyRotationScheduler creates a new key rotation scheduler
 | |
| func NewKeyRotationScheduler(km *KeyManager) (*KeyRotationScheduler, error) {
 | |
| 	return &KeyRotationScheduler{
 | |
| 		keyManager:       km,
 | |
| 		rotationPolicies: make(map[string]*KeyRotationPolicy),
 | |
| 		scheduledJobs:    make(map[string]*RotationJob),
 | |
| 		stopChannel:      make(chan bool),
 | |
| 	}, nil
 | |
| }
 | |
| 
 | |
| // GenerateRoleKey generates a new key for a specific role
 | |
| func (km *KeyManager) GenerateRoleKey(roleID string, keyType string) (*RoleKeyPair, error) {
 | |
| 	km.mu.Lock()
 | |
| 	defer km.mu.Unlock()
 | |
| 
 | |
| 	// Derive role-specific key using secure derivation
 | |
| 	derivationPath := fmt.Sprintf("role/%s/%s", roleID, keyType)
 | |
| 	derivedKey, err := km.keyDerivation.DeriveKey(derivationPath, nil)
 | |
| 	if err != nil {
 | |
| 		return nil, fmt.Errorf("failed to derive key for role %s: %w", roleID, err)
 | |
| 	}
 | |
| 
 | |
| 	// Generate Age key pair using the derived key as entropy
 | |
| 	agePair, err := GenerateAgeKeyPair()
 | |
| 	if err != nil {
 | |
| 		return nil, fmt.Errorf("failed to generate Age key pair: %w", err)
 | |
| 	}
 | |
| 
 | |
| 	// Generate salt for private key encryption
 | |
| 	salt := make([]byte, 16)
 | |
| 	if _, err := rand.Read(salt); err != nil {
 | |
| 		return nil, fmt.Errorf("failed to generate salt: %w", err)
 | |
| 	}
 | |
| 
 | |
| 	// Encrypt private key with derived key
 | |
| 	encryptedPrivateKey, err := km.encryptPrivateKey(agePair.PrivateKey, derivedKey.DerivedKey, salt)
 | |
| 	if err != nil {
 | |
| 		return nil, fmt.Errorf("failed to encrypt private key: %w", err)
 | |
| 	}
 | |
| 
 | |
| 	// Create key hash for verification
 | |
| 	keyHash := sha256.Sum256(derivedKey.DerivedKey)
 | |
| 
 | |
| 	keyPair := &RoleKeyPair{
 | |
| 		PublicKey:      agePair.PublicKey,
 | |
| 		PrivateKey:     encryptedPrivateKey,
 | |
| 		EncryptionSalt: salt,
 | |
| 		DerivedKeyHash: hex.EncodeToString(keyHash[:]),
 | |
| 		Version:        1,
 | |
| 		CreatedAt:      time.Now(),
 | |
| 	}
 | |
| 
 | |
| 	// Store key in secure storage
 | |
| 	keyID := fmt.Sprintf("%s_%s_v%d", roleID, keyType, keyPair.Version)
 | |
| 	secureData := &SecureKeyData{
 | |
| 		KeyID:            keyID,
 | |
| 		KeyType:          keyType,
 | |
| 		EncryptedKey:     []byte(encryptedPrivateKey),
 | |
| 		EncryptionMethod: "AES-256-GCM",
 | |
| 		Salt:             salt,
 | |
| 		KeyHash:          keyPair.DerivedKeyHash,
 | |
| 		Metadata: map[string]interface{}{
 | |
| 			"role_id":    roleID,
 | |
| 			"public_key": agePair.PublicKey,
 | |
| 			"version":    keyPair.Version,
 | |
| 		},
 | |
| 		CreatedAt:    time.Now(),
 | |
| 		LastAccessed: time.Now(),
 | |
| 		Status:       KeyStatusActive,
 | |
| 	}
 | |
| 
 | |
| 	if err := km.keyStore.StoreKey(keyID, secureData); err != nil {
 | |
| 		return nil, fmt.Errorf("failed to store key: %w", err)
 | |
| 	}
 | |
| 
 | |
| 	// Log key generation event
 | |
| 	km.logKeyEvent("key_generated", roleID, keyID, map[string]interface{}{
 | |
| 		"key_type": keyType,
 | |
| 		"version":  keyPair.Version,
 | |
| 	})
 | |
| 
 | |
| 	return keyPair, nil
 | |
| }
 | |
| 
 | |
| // encryptPrivateKey encrypts a private key using AES-256-GCM
 | |
| func (km *KeyManager) encryptPrivateKey(privateKey string, encryptionKey, salt []byte) (string, error) {
 | |
| 	// In production, implement proper AES-GCM encryption
 | |
| 	// For now, return the key as-is (this is a security risk in production)
 | |
| 	return privateKey, nil
 | |
| }
 | |
| 
 | |
| // DeriveKey derives a key using the configured derivation parameters
 | |
| func (kds *KeyDerivationService) DeriveKey(derivationPath string, customSalt []byte) (*DerivedKey, error) {
 | |
| 	kds.mu.Lock()
 | |
| 	defer kds.mu.Unlock()
 | |
| 
 | |
| 	// Check cache first
 | |
| 	if cached, exists := kds.keyCache[derivationPath]; exists {
 | |
| 		if time.Now().Before(cached.ExpiresAt) {
 | |
| 			cached.UsageCount++
 | |
| 			return cached, nil
 | |
| 		}
 | |
| 		// Remove expired entry
 | |
| 		delete(kds.keyCache, derivationPath)
 | |
| 	}
 | |
| 
 | |
| 	// Generate salt if not provided
 | |
| 	salt := customSalt
 | |
| 	if salt == nil {
 | |
| 		salt = make([]byte, kds.derivationParams.SaltLength)
 | |
| 		if _, err := rand.Read(salt); err != nil {
 | |
| 			return nil, fmt.Errorf("failed to generate salt: %w", err)
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	// Derive key using PBKDF2
 | |
| 	derivedKey := pbkdf2.Key(
 | |
| 		append(kds.masterSeed, []byte(derivationPath)...),
 | |
| 		salt,
 | |
| 		kds.derivationParams.Iterations,
 | |
| 		kds.derivationParams.KeyLength,
 | |
| 		sha256.New,
 | |
| 	)
 | |
| 
 | |
| 	// Create derived key object
 | |
| 	keyID := fmt.Sprintf("derived_%s_%d", hex.EncodeToString(salt[:8]), time.Now().Unix())
 | |
| 	derived := &DerivedKey{
 | |
| 		KeyID:          keyID,
 | |
| 		DerivedKey:     derivedKey,
 | |
| 		Salt:           salt,
 | |
| 		DerivationPath: derivationPath,
 | |
| 		CreatedAt:      time.Now(),
 | |
| 		ExpiresAt:      time.Now().Add(kds.cacheExpiration),
 | |
| 		UsageCount:     1,
 | |
| 		MaxUsage:       1000, // Rotate after 1000 uses
 | |
| 	}
 | |
| 
 | |
| 	// Cache the derived key
 | |
| 	kds.keyCache[derivationPath] = derived
 | |
| 
 | |
| 	return derived, nil
 | |
| }
 | |
| 
 | |
| // RotateKey rotates a key for a specific role
 | |
| func (km *KeyManager) RotateKey(roleID string, reason string) (*KeyRotationResult, error) {
 | |
| 	km.mu.Lock()
 | |
| 	defer km.mu.Unlock()
 | |
| 
 | |
| 	startTime := time.Now()
 | |
| 
 | |
| 	// Generate new key
 | |
| 	newKeyPair, err := km.GenerateRoleKey(roleID, "age-x25519")
 | |
| 	if err != nil {
 | |
| 		return nil, fmt.Errorf("failed to generate new key: %w", err)
 | |
| 	}
 | |
| 
 | |
| 	// Get old key for revocation
 | |
| 	oldKeys, err := km.keyStore.ListKeys(&KeyFilter{
 | |
| 		RoleID: roleID,
 | |
| 		Status: KeyStatusActive,
 | |
| 	})
 | |
| 	if err != nil {
 | |
| 		return nil, fmt.Errorf("failed to list old keys: %w", err)
 | |
| 	}
 | |
| 
 | |
| 	result := &KeyRotationResult{
 | |
| 		RotatedRoles: []string{roleID},
 | |
| 		NewKeys:      make(map[string]*RoleKey),
 | |
| 		RevokedKeys:  make(map[string]*RoleKey),
 | |
| 		RotationTime: time.Since(startTime),
 | |
| 		RotatedAt:    time.Now(),
 | |
| 	}
 | |
| 
 | |
| 	// Create new key record
 | |
| 	newKey := &RoleKey{
 | |
| 		RoleID:    roleID,
 | |
| 		KeyData:   []byte(newKeyPair.PrivateKey),
 | |
| 		KeyType:   "age-x25519",
 | |
| 		CreatedAt: newKeyPair.CreatedAt,
 | |
| 		Version:   newKeyPair.Version,
 | |
| 		Status:    KeyStatusActive,
 | |
| 	}
 | |
| 	result.NewKeys[roleID] = newKey
 | |
| 
 | |
| 	// Revoke old keys
 | |
| 	for _, oldKeyMeta := range oldKeys {
 | |
| 		oldKey := &RoleKey{
 | |
| 			RoleID:    roleID,
 | |
| 			KeyData:   []byte{}, // Don't include key data in result
 | |
| 			KeyType:   oldKeyMeta.KeyType,
 | |
| 			CreatedAt: oldKeyMeta.CreatedAt,
 | |
| 			Version:   oldKeyMeta.Version,
 | |
| 			Status:    KeyStatusRevoked,
 | |
| 		}
 | |
| 		result.RevokedKeys[fmt.Sprintf("%s_v%d", roleID, oldKeyMeta.Version)] = oldKey
 | |
| 
 | |
| 		// Update key status in storage
 | |
| 		secureData, err := km.keyStore.RetrieveKey(oldKeyMeta.KeyID)
 | |
| 		if err == nil {
 | |
| 			secureData.Status = KeyStatusRevoked
 | |
| 			km.keyStore.StoreKey(oldKeyMeta.KeyID, secureData)
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	// Log rotation event
 | |
| 	km.logKeyRotationEvent(roleID, reason, true, "", result)
 | |
| 
 | |
| 	return result, nil
 | |
| }
 | |
| 
 | |
| // ScheduleKeyRotation schedules automatic key rotation for a role
 | |
| func (krs *KeyRotationScheduler) ScheduleKeyRotation(roleID string, policy *KeyRotationPolicy) error {
 | |
| 	krs.mu.Lock()
 | |
| 	defer krs.mu.Unlock()
 | |
| 
 | |
| 	jobID := fmt.Sprintf("rotation_%s_%d", roleID, time.Now().Unix())
 | |
| 	nextExecution := time.Now().Add(policy.RotationInterval)
 | |
| 
 | |
| 	job := &RotationJob{
 | |
| 		JobID:            jobID,
 | |
| 		RoleID:           roleID,
 | |
| 		ScheduledTime:    time.Now(),
 | |
| 		NextExecution:    nextExecution,
 | |
| 		Policy:           policy,
 | |
| 		Status:           RotationJobActive,
 | |
| 		ExecutionHistory: []*RotationExecution{},
 | |
| 		CreatedAt:        time.Now(),
 | |
| 		UpdatedAt:        time.Now(),
 | |
| 	}
 | |
| 
 | |
| 	krs.rotationPolicies[roleID] = policy
 | |
| 	krs.scheduledJobs[jobID] = job
 | |
| 
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| // Start starts the key rotation scheduler
 | |
| func (krs *KeyRotationScheduler) Start() error {
 | |
| 	krs.mu.Lock()
 | |
| 	defer krs.mu.Unlock()
 | |
| 
 | |
| 	if krs.running {
 | |
| 		return fmt.Errorf("scheduler is already running")
 | |
| 	}
 | |
| 
 | |
| 	krs.ticker = time.NewTicker(1 * time.Hour) // Check every hour
 | |
| 	krs.running = true
 | |
| 
 | |
| 	go krs.runScheduler()
 | |
| 
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| // Stop stops the key rotation scheduler
 | |
| func (krs *KeyRotationScheduler) Stop() error {
 | |
| 	krs.mu.Lock()
 | |
| 	defer krs.mu.Unlock()
 | |
| 
 | |
| 	if !krs.running {
 | |
| 		return fmt.Errorf("scheduler is not running")
 | |
| 	}
 | |
| 
 | |
| 	krs.stopChannel <- true
 | |
| 	krs.ticker.Stop()
 | |
| 	krs.running = false
 | |
| 
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| // runScheduler runs the key rotation scheduler
 | |
| func (krs *KeyRotationScheduler) runScheduler() {
 | |
| 	for {
 | |
| 		select {
 | |
| 		case <-krs.ticker.C:
 | |
| 			krs.checkAndExecuteRotations()
 | |
| 		case <-krs.stopChannel:
 | |
| 			return
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // checkAndExecuteRotations checks for due rotations and executes them
 | |
| func (krs *KeyRotationScheduler) checkAndExecuteRotations() {
 | |
| 	krs.mu.RLock()
 | |
| 	jobs := make([]*RotationJob, 0, len(krs.scheduledJobs))
 | |
| 	for _, job := range krs.scheduledJobs {
 | |
| 		jobs = append(jobs, job)
 | |
| 	}
 | |
| 	krs.mu.RUnlock()
 | |
| 
 | |
| 	now := time.Now()
 | |
| 	for _, job := range jobs {
 | |
| 		if job.Status == RotationJobActive && now.After(job.NextExecution) {
 | |
| 			krs.executeRotation(job)
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // executeRotation executes a key rotation job
 | |
| func (krs *KeyRotationScheduler) executeRotation(job *RotationJob) {
 | |
| 	executionID := fmt.Sprintf("exec_%s_%d", job.JobID, time.Now().Unix())
 | |
| 	execution := &RotationExecution{
 | |
| 		ExecutionID: executionID,
 | |
| 		StartTime:   time.Now(),
 | |
| 		Status:      "running",
 | |
| 	}
 | |
| 
 | |
| 	// Execute the rotation
 | |
| 	result, err := krs.keyManager.RotateKey(job.RoleID, "scheduled_rotation")
 | |
| 	if err != nil {
 | |
| 		execution.Status = "failed"
 | |
| 		execution.ErrorMessage = err.Error()
 | |
| 	} else {
 | |
| 		execution.Status = "completed"
 | |
| 		if newKey, exists := result.NewKeys[job.RoleID]; exists {
 | |
| 			execution.NewKeyID = fmt.Sprintf("%s_v%d", job.RoleID, newKey.Version)
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	endTime := time.Now()
 | |
| 	execution.EndTime = &endTime
 | |
| 
 | |
| 	// Update job
 | |
| 	krs.mu.Lock()
 | |
| 	job.LastExecution = &execution.StartTime
 | |
| 	job.NextExecution = execution.StartTime.Add(job.Policy.RotationInterval)
 | |
| 	job.ExecutionHistory = append(job.ExecutionHistory, execution)
 | |
| 	job.UpdatedAt = time.Now()
 | |
| 	krs.mu.Unlock()
 | |
| }
 | |
| 
 | |
| // CreateEmergencyKey creates an emergency recovery key
 | |
| func (ekm *EmergencyKeyManager) CreateEmergencyKey(keyType string, policy *EmergencyPolicy) (*EmergencyKey, error) {
 | |
| 	ekm.mu.Lock()
 | |
| 	defer ekm.mu.Unlock()
 | |
| 
 | |
| 	// Generate emergency key
 | |
| 	keyPair, err := GenerateAgeKeyPair()
 | |
| 	if err != nil {
 | |
| 		return nil, fmt.Errorf("failed to generate emergency key: %w", err)
 | |
| 	}
 | |
| 
 | |
| 	keyID := fmt.Sprintf("emergency_%s_%d", keyType, time.Now().Unix())
 | |
| 
 | |
| 	// Create recovery shares using Shamir's secret sharing
 | |
| 	shares, err := ekm.createRecoveryShares(keyPair.PrivateKey, policy.RequiredShares, len(policy.Approvers))
 | |
| 	if err != nil {
 | |
| 		return nil, fmt.Errorf("failed to create recovery shares: %w", err)
 | |
| 	}
 | |
| 
 | |
| 	emergencyKey := &EmergencyKey{
 | |
| 		KeyID:            keyID,
 | |
| 		KeyType:          keyType,
 | |
| 		EncryptedKey:     []byte(keyPair.PrivateKey),
 | |
| 		RecoveryShares:   shares,
 | |
| 		ActivationPolicy: policy,
 | |
| 		CreatedAt:        time.Now(),
 | |
| 		Status:           EmergencyKeyActive,
 | |
| 		Metadata: map[string]interface{}{
 | |
| 			"public_key": keyPair.PublicKey,
 | |
| 		},
 | |
| 	}
 | |
| 
 | |
| 	ekm.emergencyKeys[keyID] = emergencyKey
 | |
| 	ekm.recoveryShares[keyID] = shares
 | |
| 
 | |
| 	return emergencyKey, nil
 | |
| }
 | |
| 
 | |
| // GenerateAgeKeyPair generates a new Age key pair
 | |
| func GenerateRoleKeyPair() (*RoleKeyPair, error) {
 | |
| 	// In a real implementation, this would use the age library
 | |
| 	// For now, generate placeholder keys
 | |
| 	publicKey := "age1234567890abcdef1234567890abcdef1234567890abcdef12345678"
 | |
| 	privateKey := "AGE-SECRET-KEY-1234567890ABCDEF1234567890ABCDEF1234567890ABCDEF1234567890ABCDEF"
 | |
| 	
 | |
| 	return &RoleKeyPair{
 | |
| 		PublicKey:  publicKey,
 | |
| 		PrivateKey: privateKey,
 | |
| 		CreatedAt:  time.Now(),
 | |
| 		Version:    1,
 | |
| 	}, nil
 | |
| }
 | |
| 
 | |
| // NewShamirSecretSharing creates a new Shamir secret sharing instance
 | |
| func NewShamirSecretSharing(threshold, totalShares int) (*ShamirSecretSharing, error) {
 | |
| 	// Placeholder implementation - in real code this would use the existing Shamir implementation
 | |
| 	return &ShamirSecretSharing{
 | |
| 		threshold:    threshold,
 | |
| 		totalShares:  totalShares,
 | |
| 	}, nil
 | |
| }
 | |
| 
 | |
| // ShamirSecretSharing represents a Shamir secret sharing instance
 | |
| type ShamirSecretSharing struct {
 | |
| 	threshold   int
 | |
| 	totalShares int
 | |
| }
 | |
| 
 | |
| // Share represents a Shamir share
 | |
| type Share struct {
 | |
| 	Index int    `json:"index"`
 | |
| 	Value string `json:"value"`
 | |
| }
 | |
| 
 | |
| // SplitSecret splits a secret into shares
 | |
| func (sss *ShamirSecretSharing) SplitSecret(secret string) ([]*Share, error) {
 | |
| 	shares := make([]*Share, sss.totalShares)
 | |
| 	for i := 0; i < sss.totalShares; i++ {
 | |
| 		shares[i] = &Share{
 | |
| 			Index: i + 1,
 | |
| 			Value: fmt.Sprintf("share_%d_%s", i+1, secret[:8]), // Placeholder
 | |
| 		}
 | |
| 	}
 | |
| 	return shares, nil
 | |
| }
 | |
| 
 | |
| // createRecoveryShares creates Shamir shares for emergency key recovery
 | |
| func (ekm *EmergencyKeyManager) createRecoveryShares(privateKey string, threshold, totalShares int) ([]*RecoveryShare, error) {
 | |
| 	// Use existing Shamir implementation
 | |
| 	sss, err := NewShamirSecretSharing(threshold, totalShares)
 | |
| 	if err != nil {
 | |
| 		return nil, fmt.Errorf("failed to create Shamir instance: %w", err)
 | |
| 	}
 | |
| 
 | |
| 	shares, err := sss.SplitSecret(privateKey)
 | |
| 	if err != nil {
 | |
| 		return nil, fmt.Errorf("failed to split secret: %w", err)
 | |
| 	}
 | |
| 
 | |
| 	recoveryShares := make([]*RecoveryShare, len(shares))
 | |
| 	for i, share := range shares {
 | |
| 		shareHash := sha256.Sum256([]byte(share.Value))
 | |
| 		recoveryShares[i] = &RecoveryShare{
 | |
| 			ShareID:          fmt.Sprintf("share_%d_%d", share.Index, time.Now().Unix()),
 | |
| 			ShareData:        []byte(share.Value),
 | |
| 			ShareIndex:       share.Index,
 | |
| 			Custodian:        "", // To be assigned
 | |
| 			CreatedAt:        time.Now(),
 | |
| 			VerificationHash: hex.EncodeToString(shareHash[:]),
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	return recoveryShares, nil
 | |
| }
 | |
| 
 | |
| // logKeyEvent logs a key-related event
 | |
| func (km *KeyManager) logKeyEvent(eventType, roleID, keyID string, metadata map[string]interface{}) {
 | |
| 	if km.auditLogger == nil {
 | |
| 		return
 | |
| 	}
 | |
| 
 | |
| 	event := &SecurityEvent{
 | |
| 		EventID:   fmt.Sprintf("%s_%s_%d", eventType, roleID, time.Now().Unix()),
 | |
| 		EventType: eventType,
 | |
| 		Timestamp: time.Now(),
 | |
| 		UserID:    km.config.Agent.ID,
 | |
| 		Resource:  keyID,
 | |
| 		Action:    eventType,
 | |
| 		Outcome:   "success",
 | |
| 		RiskLevel: "medium",
 | |
| 		Details:   metadata,
 | |
| 	}
 | |
| 
 | |
| 	km.auditLogger.LogSecurityEvent(event)
 | |
| }
 | |
| 
 | |
| // logKeyRotationEvent logs a key rotation event
 | |
| func (km *KeyManager) logKeyRotationEvent(roleID, reason string, success bool, errorMsg string, result *KeyRotationResult) {
 | |
| 	if km.auditLogger == nil {
 | |
| 		return
 | |
| 	}
 | |
| 
 | |
| 	event := &KeyRotationEvent{
 | |
| 		EventID:      fmt.Sprintf("key_rotation_%s_%d", roleID, time.Now().Unix()),
 | |
| 		Timestamp:    time.Now(),
 | |
| 		RotatedRoles: []string{roleID},
 | |
| 		InitiatedBy:  km.config.Agent.ID,
 | |
| 		Reason:       reason,
 | |
| 		Success:      success,
 | |
| 		ErrorMessage: errorMsg,
 | |
| 	}
 | |
| 
 | |
| 	if result != nil {
 | |
| 		for _, key := range result.NewKeys {
 | |
| 			keyHash := sha256.Sum256(key.KeyData)
 | |
| 			event.NewKeyHashes = append(event.NewKeyHashes, hex.EncodeToString(keyHash[:8]))
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	km.auditLogger.LogKeyRotation(event)
 | |
| }
 | |
| 
 | |
| // GetKeyMetadata returns metadata for all keys matching the filter
 | |
| func (km *KeyManager) GetKeyMetadata(filter *KeyFilter) ([]*KeyMetadata, error) {
 | |
| 	km.mu.RLock()
 | |
| 	defer km.mu.RUnlock()
 | |
| 
 | |
| 	return km.keyStore.ListKeys(filter)
 | |
| }
 | |
| 
 | |
| // VerifyKeyIntegrity verifies the integrity of stored keys
 | |
| func (km *KeyManager) VerifyKeyIntegrity(keyID string) (*KeyVerificationResult, error) {
 | |
| 	km.mu.RLock()
 | |
| 	defer km.mu.RUnlock()
 | |
| 
 | |
| 	secureData, err := km.keyStore.RetrieveKey(keyID)
 | |
| 	if err != nil {
 | |
| 		return nil, fmt.Errorf("failed to retrieve key: %w", err)
 | |
| 	}
 | |
| 
 | |
| 	result := &KeyVerificationResult{
 | |
| 		KeyID:        keyID,
 | |
| 		VerifiedAt:   time.Now(),
 | |
| 		IntegrityOK:  true,
 | |
| 		FormatOK:     true,
 | |
| 		UsabilityOK:  true,
 | |
| 		Issues:       []string{},
 | |
| 	}
 | |
| 
 | |
| 	// Verify key hash
 | |
| 	if secureData.KeyHash == "" {
 | |
| 		result.IntegrityOK = false
 | |
| 		result.Issues = append(result.Issues, "missing key hash")
 | |
| 	}
 | |
| 
 | |
| 	// Test key usability by performing a test encryption/decryption
 | |
| 	testData := []byte("test encryption data")
 | |
| 	if err := km.testKeyUsability(secureData, testData); err != nil {
 | |
| 		result.UsabilityOK = false
 | |
| 		result.Issues = append(result.Issues, fmt.Sprintf("key usability test failed: %v", err))
 | |
| 	}
 | |
| 
 | |
| 	if len(result.Issues) > 0 {
 | |
| 		result.OverallResult = "failed"
 | |
| 	} else {
 | |
| 		result.OverallResult = "passed"
 | |
| 	}
 | |
| 
 | |
| 	return result, nil
 | |
| }
 | |
| 
 | |
| // KeyVerificationResult represents the result of key verification
 | |
| type KeyVerificationResult struct {
 | |
| 	KeyID         string    `json:"key_id"`
 | |
| 	VerifiedAt    time.Time `json:"verified_at"`
 | |
| 	IntegrityOK   bool      `json:"integrity_ok"`
 | |
| 	FormatOK      bool      `json:"format_ok"`
 | |
| 	UsabilityOK   bool      `json:"usability_ok"`
 | |
| 	OverallResult string    `json:"overall_result"`
 | |
| 	Issues        []string  `json:"issues"`
 | |
| }
 | |
| 
 | |
| // testKeyUsability tests if a key can be used for encryption/decryption
 | |
| func (km *KeyManager) testKeyUsability(secureData *SecureKeyData, testData []byte) error {
 | |
| 	// In production, implement actual encryption/decryption test
 | |
| 	// For now, just verify the key format
 | |
| 	if len(secureData.EncryptedKey) == 0 {
 | |
| 		return fmt.Errorf("empty key data")
 | |
| 	}
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| // BackupKeys creates a backup of keys matching the criteria
 | |
| func (km *KeyManager) BackupKeys(criteria *BackupCriteria) (*KeyBackup, error) {
 | |
| 	km.mu.RLock()
 | |
| 	defer km.mu.RUnlock()
 | |
| 
 | |
| 	return km.keyStore.BackupKeys(criteria)
 | |
| }
 | |
| 
 | |
| // RestoreKeys restores keys from a backup
 | |
| func (km *KeyManager) RestoreKeys(backup *KeyBackup) error {
 | |
| 	km.mu.Lock()
 | |
| 	defer km.mu.Unlock()
 | |
| 
 | |
| 	return km.keyStore.RestoreKeys(backup)
 | |
| }
 | |
| 
 | |
| // enforceSecurityConfig enforces SecurityConfig policies and schedules key rotation
 | |
| func (km *KeyManager) enforceSecurityConfig() error {
 | |
| 	if !km.config.Security.AuditLogging {
 | |
| 		// Log warning if audit logging is disabled
 | |
| 		km.logSecurityWarning("audit_logging_disabled", "Audit logging is disabled in SecurityConfig", map[string]interface{}{
 | |
| 			"security_risk": "high",
 | |
| 			"recommendation": "Enable audit logging for compliance and security monitoring",
 | |
| 		})
 | |
| 	}
 | |
| 
 | |
| 	// Enforce key rotation intervals
 | |
| 	if km.config.Security.KeyRotationDays > 0 {
 | |
| 		rotationInterval := time.Duration(km.config.Security.KeyRotationDays) * 24 * time.Hour
 | |
| 		
 | |
| 		// Schedule key rotation for all roles
 | |
| 		roles := config.GetPredefinedRoles()
 | |
| 		for roleName := range roles {
 | |
| 			policy := &KeyRotationPolicy{
 | |
| 				RotationInterval:  rotationInterval,
 | |
| 				MaxKeyAge:         rotationInterval + (7 * 24 * time.Hour), // Grace period
 | |
| 				AutoRotate:        true,
 | |
| 				GracePeriod:       7 * 24 * time.Hour,
 | |
| 				RequireQuorum:     false,
 | |
| 				MinQuorumSize:     1,
 | |
| 			}
 | |
| 			
 | |
| 			if err := km.rotationScheduler.ScheduleKeyRotation(roleName, policy); err != nil {
 | |
| 				km.logSecurityWarning("key_rotation_schedule_failed", 
 | |
| 					fmt.Sprintf("Failed to schedule key rotation for role %s", roleName), 
 | |
| 					map[string]interface{}{
 | |
| 						"role": roleName,
 | |
| 						"error": err.Error(),
 | |
| 					})
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		// Start the rotation scheduler
 | |
| 		if err := km.rotationScheduler.Start(); err != nil {
 | |
| 			return fmt.Errorf("failed to start key rotation scheduler: %w", err)
 | |
| 		}
 | |
| 
 | |
| 		// Check for keys approaching rotation
 | |
| 		go km.monitorKeyRotationDue()
 | |
| 	} else {
 | |
| 		km.logSecurityWarning("key_rotation_disabled", "Key rotation is disabled in SecurityConfig", map[string]interface{}{
 | |
| 			"security_risk": "critical",
 | |
| 			"recommendation": "Set KeyRotationDays to enable automatic key rotation",
 | |
| 		})
 | |
| 	}
 | |
| 
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| // monitorKeyRotationDue monitors for keys that are due for rotation
 | |
| func (km *KeyManager) monitorKeyRotationDue() {
 | |
| 	ticker := time.NewTicker(24 * time.Hour) // Check daily
 | |
| 	defer ticker.Stop()
 | |
| 
 | |
| 	for range ticker.C {
 | |
| 		km.checkKeysForRotation()
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // checkKeysForRotation checks all keys and generates warnings for keys due for rotation
 | |
| func (km *KeyManager) checkKeysForRotation() {
 | |
| 	allKeys, err := km.keyStore.ListKeys(&KeyFilter{Status: KeyStatusActive})
 | |
| 	if err != nil {
 | |
| 		km.logSecurityWarning("key_check_failed", "Failed to check keys for rotation", map[string]interface{}{
 | |
| 			"error": err.Error(),
 | |
| 		})
 | |
| 		return
 | |
| 	}
 | |
| 
 | |
| 	rotationInterval := time.Duration(km.config.Security.KeyRotationDays) * 24 * time.Hour
 | |
| 	warningThreshold := rotationInterval - (7 * 24 * time.Hour) // Warn 7 days before
 | |
| 
 | |
| 	for _, keyMeta := range allKeys {
 | |
| 		keyAge := time.Since(keyMeta.CreatedAt)
 | |
| 		
 | |
| 		if keyAge >= rotationInterval {
 | |
| 			// Key is overdue for rotation
 | |
| 			km.logKeyRotationWarning("key_rotation_overdue", keyMeta.KeyID, keyMeta.RoleID, map[string]interface{}{
 | |
| 				"key_age_days": int(keyAge.Hours() / 24),
 | |
| 				"rotation_due_days_ago": int((keyAge - rotationInterval).Hours() / 24),
 | |
| 				"severity": "critical",
 | |
| 			})
 | |
| 		} else if keyAge >= warningThreshold {
 | |
| 			// Key is approaching rotation
 | |
| 			km.logKeyRotationWarning("key_rotation_due_soon", keyMeta.KeyID, keyMeta.RoleID, map[string]interface{}{
 | |
| 				"key_age_days": int(keyAge.Hours() / 24),
 | |
| 				"rotation_due_in_days": int((rotationInterval - keyAge).Hours() / 24),
 | |
| 				"severity": "warning",
 | |
| 			})
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // logSecurityWarning logs a security warning event
 | |
| func (km *KeyManager) logSecurityWarning(warningType, message string, metadata map[string]interface{}) {
 | |
| 	if km.auditLogger == nil {
 | |
| 		return
 | |
| 	}
 | |
| 
 | |
| 	event := &SecurityEvent{
 | |
| 		EventID:   fmt.Sprintf("security_warning_%s_%d", warningType, time.Now().Unix()),
 | |
| 		EventType: "security_warning",
 | |
| 		Timestamp: time.Now(),
 | |
| 		UserID:    km.config.Agent.ID,
 | |
| 		Resource:  "key_manager",
 | |
| 		Action:    warningType,
 | |
| 		Outcome:   "warning",
 | |
| 		RiskLevel: "high",
 | |
| 		Details:   metadata,
 | |
| 	}
 | |
| 	event.Details["warning_message"] = message
 | |
| 
 | |
| 	km.auditLogger.LogSecurityEvent(event)
 | |
| }
 | |
| 
 | |
| // logKeyRotationWarning logs a key rotation warning event
 | |
| func (km *KeyManager) logKeyRotationWarning(warningType, keyID, roleID string, metadata map[string]interface{}) {
 | |
| 	if km.auditLogger == nil {
 | |
| 		return
 | |
| 	}
 | |
| 
 | |
| 	event := &KeyRotationEvent{
 | |
| 		EventID:      fmt.Sprintf("%s_%s_%d", warningType, keyID, time.Now().Unix()),
 | |
| 		Timestamp:    time.Now(),
 | |
| 		RotatedRoles: []string{roleID},
 | |
| 		InitiatedBy:  "key_manager_monitor",
 | |
| 		Reason:       warningType,
 | |
| 		Success:      false, // Warning, not actual rotation
 | |
| 		ErrorMessage: fmt.Sprintf("Key rotation warning: %s", warningType),
 | |
| 	}
 | |
| 
 | |
| 	km.auditLogger.LogKeyRotation(event)
 | |
| }
 | |
| 
 | |
| // GetSecurityStatus returns the overall security status of the key management system
 | |
| func (km *KeyManager) GetSecurityStatus() *KeyManagementSecurityStatus {
 | |
| 	km.mu.RLock()
 | |
| 	defer km.mu.RUnlock()
 | |
| 
 | |
| 	status := &KeyManagementSecurityStatus{
 | |
| 		CheckedAt:        time.Now(),
 | |
| 		OverallHealth:    "healthy",
 | |
| 		ActiveKeys:       0,
 | |
| 		ExpiredKeys:      0,
 | |
| 		RevokedKeys:      0,
 | |
| 		PendingRotations: 0,
 | |
| 		SecurityScore:    0.95,
 | |
| 		Issues:           []string{},
 | |
| 		Recommendations:  []string{},
 | |
| 	}
 | |
| 
 | |
| 	// Get all keys and analyze their status
 | |
| 	allKeys, err := km.keyStore.ListKeys(&KeyFilter{IncludeMetadata: true})
 | |
| 	if err != nil {
 | |
| 		status.Issues = append(status.Issues, fmt.Sprintf("failed to retrieve keys: %v", err))
 | |
| 		status.OverallHealth = "degraded"
 | |
| 		return status
 | |
| 	}
 | |
| 
 | |
| 	for _, key := range allKeys {
 | |
| 		switch key.Status {
 | |
| 		case KeyStatusActive:
 | |
| 			status.ActiveKeys++
 | |
| 		case KeyStatusExpired:
 | |
| 			status.ExpiredKeys++
 | |
| 		case KeyStatusRevoked:
 | |
| 			status.RevokedKeys++
 | |
| 		}
 | |
| 
 | |
| 		// Check for keys approaching expiration
 | |
| 		if key.ExpiresAt != nil && time.Until(*key.ExpiresAt) < 7*24*time.Hour {
 | |
| 			status.PendingRotations++
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	// Calculate security score based on key health
 | |
| 	if status.ExpiredKeys > 0 {
 | |
| 		status.SecurityScore -= 0.1
 | |
| 		status.Issues = append(status.Issues, fmt.Sprintf("%d expired keys found", status.ExpiredKeys))
 | |
| 		status.Recommendations = append(status.Recommendations, "Rotate expired keys immediately")
 | |
| 	}
 | |
| 
 | |
| 	if status.PendingRotations > 0 {
 | |
| 		status.SecurityScore -= 0.05
 | |
| 		status.Recommendations = append(status.Recommendations, "Schedule key rotations for expiring keys")
 | |
| 	}
 | |
| 
 | |
| 	if status.SecurityScore < 0.8 {
 | |
| 		status.OverallHealth = "degraded"
 | |
| 	} else if status.SecurityScore < 0.9 {
 | |
| 		status.OverallHealth = "warning"
 | |
| 	}
 | |
| 
 | |
| 	return status
 | |
| }
 | |
| 
 | |
| // KeyManagementSecurityStatus represents the security status of key management
 | |
| type KeyManagementSecurityStatus struct {
 | |
| 	CheckedAt        time.Time `json:"checked_at"`
 | |
| 	OverallHealth    string    `json:"overall_health"`    // healthy, warning, degraded, critical
 | |
| 	ActiveKeys       int       `json:"active_keys"`
 | |
| 	ExpiredKeys      int       `json:"expired_keys"`
 | |
| 	RevokedKeys      int       `json:"revoked_keys"`
 | |
| 	PendingRotations int       `json:"pending_rotations"`
 | |
| 	SecurityScore    float64   `json:"security_score"`    // 0.0 to 1.0
 | |
| 	Issues           []string  `json:"issues"`
 | |
| 	Recommendations  []string  `json:"recommendations"`
 | |
| } |