This comprehensive refactoring addresses critical architectural issues: IMPORT CYCLE RESOLUTION: • pkg/crypto ↔ pkg/slurp/roles: Created pkg/security/access_levels.go • pkg/ucxl → pkg/dht: Created pkg/storage/interfaces.go • pkg/slurp/leader → pkg/election → pkg/slurp/storage: Moved types to pkg/election/interfaces.go MODULE PATH MIGRATION: • Changed from github.com/anthonyrawlins/bzzz to chorus.services/bzzz • Updated all import statements across 115+ files • Maintains compatibility while removing personal GitHub account dependency TYPE SYSTEM IMPROVEMENTS: • Resolved duplicate type declarations in crypto package • Added missing type definitions (RoleStatus, TimeRestrictions, KeyStatus, KeyRotationResult) • Proper interface segregation to prevent future cycles ARCHITECTURAL BENEFITS: • Build now progresses past structural issues to normal dependency resolution • Cleaner separation of concerns between packages • Eliminates circular dependencies that prevented compilation • Establishes foundation for scalable codebase growth 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
1201 lines
42 KiB
Go
1201 lines
42 KiB
Go
// Package crypto provides role-based encryption for SLURP contextual intelligence.
|
|
//
|
|
// This module extends the existing Age encryption infrastructure to support
|
|
// sophisticated role-based access control with multi-layer encryption,
|
|
// key rotation, and compartmentalized context security.
|
|
//
|
|
// Security Architecture:
|
|
// - Multi-layer encryption: Base context + role-specific overlays
|
|
// - Key derivation from role definitions and Shamir shares
|
|
// - Context compartmentalization prevents cross-role information leakage
|
|
// - Access logging and audit trail for all context access operations
|
|
// - Forward secrecy through regular key rotation
|
|
//
|
|
// Access Control Matrix:
|
|
// - Senior Architect: AccessCritical - system-wide architecture decisions
|
|
// - Frontend Developer: AccessMedium - frontend scope only
|
|
// - Backend Developer: AccessMedium - backend scope only
|
|
// - DevOps Engineer: AccessHigh - infrastructure decisions
|
|
// - Project Manager: AccessCritical - global coordination access
|
|
//
|
|
// Cross-references:
|
|
// - pkg/crypto/age_crypto.go: Base Age encryption implementation
|
|
// - pkg/crypto/shamir.go: Shamir secret sharing for admin keys
|
|
// - pkg/slurp/context/types.go: Context data structures
|
|
// - pkg/slurp/roles/types.go: Role definitions and permissions
|
|
// - docs/SECURITY.md: Complete security model documentation
|
|
|
|
package crypto
|
|
|
|
import (
|
|
"crypto/rand"
|
|
"crypto/sha256"
|
|
"encoding/hex"
|
|
"encoding/json"
|
|
"fmt"
|
|
"sync"
|
|
"time"
|
|
|
|
"golang.org/x/crypto/pbkdf2"
|
|
"chorus.services/bzzz/pkg/config"
|
|
"chorus.services/bzzz/pkg/security"
|
|
"chorus.services/bzzz/pkg/ucxl"
|
|
slurpContext "chorus.services/bzzz/pkg/slurp/context"
|
|
"chorus.services/bzzz/pkg/slurp/roles"
|
|
)
|
|
|
|
// AccessLevel type alias for backward compatibility
|
|
type AccessLevel = security.AccessLevel
|
|
|
|
// Access level constants for backward compatibility
|
|
const (
|
|
AccessPublic = security.AccessLevelPublic
|
|
AccessLow = security.AccessLevelInternal
|
|
AccessMedium = security.AccessLevelConfidential
|
|
AccessHigh = security.AccessLevelSecret
|
|
AccessCritical = security.AccessLevelTopSecret
|
|
)
|
|
|
|
// Note: String() method is provided by security.AccessLevel
|
|
|
|
// RoleEncryptionConfig represents encryption configuration for a role
|
|
type RoleEncryptionConfig struct {
|
|
RoleID string `json:"role_id"`
|
|
AccessLevel AccessLevel `json:"access_level"`
|
|
EncryptionKeys *RoleKeyPair `json:"encryption_keys"`
|
|
KeyVersion int `json:"key_version"`
|
|
KeyRotationPolicy *KeyRotationPolicy `json:"key_rotation_policy"`
|
|
CreatedAt time.Time `json:"created_at"`
|
|
UpdatedAt time.Time `json:"updated_at"`
|
|
}
|
|
|
|
// 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
|
|
}
|
|
|
|
// 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
|
|
}
|
|
|
|
// EncryptedContextData represents encrypted context with access control metadata
|
|
type EncryptedContextData struct {
|
|
UCXLAddress ucxl.Address `json:"ucxl_address"`
|
|
EncryptedLayers []*EncryptionLayer `json:"encrypted_layers"`
|
|
AccessControlMeta *AccessControlMeta `json:"access_control_meta"`
|
|
CreatedAt time.Time `json:"created_at"`
|
|
KeyFingerprints map[string]string `json:"key_fingerprints"` // Role -> key fingerprint mapping
|
|
}
|
|
|
|
// EncryptionLayer represents a single layer of encryption for role-based access
|
|
type EncryptionLayer struct {
|
|
LayerID string `json:"layer_id"`
|
|
TargetRoles []string `json:"target_roles"` // Roles that can decrypt this layer
|
|
RequiredLevel AccessLevel `json:"required_level"` // Minimum access level required
|
|
EncryptedData []byte `json:"encrypted_data"` // Encrypted layer data
|
|
EncryptionMethod string `json:"encryption_method"` // Encryption method used
|
|
KeyFingerprint string `json:"key_fingerprint"` // Fingerprint of encryption key
|
|
CreatedAt time.Time `json:"created_at"` // When layer was created
|
|
ExpiresAt *time.Time `json:"expires_at,omitempty"` // When layer expires
|
|
}
|
|
|
|
// AccessControlMeta contains metadata for access control decisions
|
|
type AccessControlMeta struct {
|
|
Creator string `json:"creator"` // Who created the context
|
|
CreatorRole string `json:"creator_role"` // Role of creator
|
|
ClassificationLevel string `json:"classification_level"` // Data classification
|
|
RequiredClearance AccessLevel `json:"required_clearance"` // Minimum clearance needed
|
|
AccessLog []*AccessLogEntry `json:"access_log"` // Access history
|
|
PolicyReferences []string `json:"policy_references"` // Security policies applied
|
|
CompartmentTags []string `json:"compartment_tags"` // Security compartments
|
|
}
|
|
|
|
// 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
|
|
}
|
|
|
|
// RoleCrypto handles role-based encryption and access control for SLURP contexts
|
|
type RoleCrypto struct {
|
|
mu sync.RWMutex
|
|
config *config.Config
|
|
ageCrypto *AgeCrypto
|
|
adminKeyManager *AdminKeyManager
|
|
roleConfigs map[string]*RoleEncryptionConfig
|
|
accessMatrix *AccessControlMatrix
|
|
auditLogger AuditLogger
|
|
}
|
|
|
|
// AccessControlMatrix and PolicyEngine are defined in access_control.go
|
|
|
|
// SecurityPolicy represents a security policy for access control
|
|
type SecurityPolicy struct {
|
|
ID string `json:"id"`
|
|
Name string `json:"name"`
|
|
Rules []*PolicyRule `json:"rules"`
|
|
Priority int `json:"priority"`
|
|
Enabled bool `json:"enabled"`
|
|
CreatedAt time.Time `json:"created_at"`
|
|
UpdatedAt time.Time `json:"updated_at"`
|
|
}
|
|
|
|
// PolicyRule represents a single rule within a security policy
|
|
// PolicyRule, PolicyAction, and PolicyEffect are defined in access_control.go
|
|
|
|
// AccessContext represents context for access control decisions
|
|
type AccessContext struct {
|
|
UserID string `json:"user_id"`
|
|
Role string `json:"role"`
|
|
Resource ucxl.Address `json:"resource"`
|
|
Action string `json:"action"`
|
|
AccessLevel AccessLevel `json:"access_level"`
|
|
Environment map[string]interface{} `json:"environment"`
|
|
RequestTime time.Time `json:"request_time"`
|
|
}
|
|
|
|
// AccessDecision represents the result of an access control decision
|
|
type AccessDecision struct {
|
|
Decision DecisionResult `json:"decision"`
|
|
Reason string `json:"reason"`
|
|
AppliedPolicies []string `json:"applied_policies"`
|
|
Conditions []string `json:"conditions"`
|
|
EvaluationTime time.Duration `json:"evaluation_time"`
|
|
AuditRequired bool `json:"audit_required"`
|
|
AdditionalMeta map[string]interface{} `json:"additional_meta"`
|
|
}
|
|
|
|
// DecisionResult represents access control decision results
|
|
type DecisionResult string
|
|
|
|
const (
|
|
DecisionPermit DecisionResult = "permit"
|
|
DecisionDeny DecisionResult = "deny"
|
|
DecisionError DecisionResult = "error"
|
|
)
|
|
|
|
// 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)
|
|
}
|
|
|
|
// 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"`
|
|
}
|
|
|
|
// NewRoleCrypto creates a new role-based crypto handler
|
|
func NewRoleCrypto(cfg *config.Config, ageCrypto *AgeCrypto, adminKeyManager *AdminKeyManager, auditLogger AuditLogger) (*RoleCrypto, error) {
|
|
rc := &RoleCrypto{
|
|
config: cfg,
|
|
ageCrypto: ageCrypto,
|
|
adminKeyManager: adminKeyManager,
|
|
roleConfigs: make(map[string]*RoleEncryptionConfig),
|
|
auditLogger: auditLogger,
|
|
}
|
|
|
|
// Initialize access control matrix
|
|
matrix, err := rc.buildAccessControlMatrix()
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to build access control matrix: %w", err)
|
|
}
|
|
rc.accessMatrix = matrix
|
|
|
|
// Initialize role encryption configurations
|
|
if err := rc.initializeRoleConfigs(); err != nil {
|
|
return nil, fmt.Errorf("failed to initialize role configs: %w", err)
|
|
}
|
|
|
|
return rc, nil
|
|
}
|
|
|
|
// buildAccessControlMatrix constructs the role hierarchy and access control matrix
|
|
func (rc *RoleCrypto) buildAccessControlMatrix() (*AccessControlMatrix, error) {
|
|
matrix := &AccessControlMatrix{
|
|
roleHierarchy: make(map[string][]string),
|
|
accessLevels: make(map[string]AccessLevel),
|
|
compartments: make(map[string][]string),
|
|
}
|
|
|
|
// Define role access levels based on security requirements
|
|
roleAccessLevels := map[string]AccessLevel{
|
|
"senior_architect": AccessCritical, // System-wide architecture decisions
|
|
"project_manager": AccessCritical, // Global coordination access
|
|
"devops_engineer": AccessHigh, // Infrastructure decisions
|
|
"backend_developer": AccessMedium, // Backend scope only
|
|
"frontend_developer": AccessMedium, // Frontend scope only
|
|
"qa_engineer": AccessMedium, // Testing scope
|
|
"security_engineer": AccessHigh, // Security scope
|
|
"data_analyst": AccessLow, // Analytics scope
|
|
"intern": AccessLow, // Limited access
|
|
"external_contractor": AccessLow, // External limited access
|
|
}
|
|
|
|
// Define role hierarchy (which roles can access other roles' contexts)
|
|
roleHierarchy := map[string][]string{
|
|
"senior_architect": {"*"}, // Can access everything
|
|
"project_manager": {"*"}, // Can access everything for coordination
|
|
"devops_engineer": {"devops_engineer", "backend_developer", "security_engineer"},
|
|
"security_engineer": {"*"}, // Can access everything for security review
|
|
"backend_developer": {"backend_developer", "qa_engineer"},
|
|
"frontend_developer": {"frontend_developer", "qa_engineer"},
|
|
"qa_engineer": {"qa_engineer", "backend_developer", "frontend_developer"},
|
|
"data_analyst": {"data_analyst"},
|
|
"intern": {"intern"},
|
|
"external_contractor": {"external_contractor"},
|
|
}
|
|
|
|
// Define compartments (which contexts roles can access)
|
|
roleCompartments := map[string][]string{
|
|
"senior_architect": {"architecture", "system", "security", "infrastructure", "frontend", "backend", "data"},
|
|
"project_manager": {"project", "coordination", "planning", "architecture", "frontend", "backend"},
|
|
"devops_engineer": {"infrastructure", "deployment", "monitoring", "security", "backend"},
|
|
"security_engineer": {"security", "audit", "compliance", "infrastructure", "backend", "frontend"},
|
|
"backend_developer": {"backend", "api", "database", "services"},
|
|
"frontend_developer": {"frontend", "ui", "ux", "components"},
|
|
"qa_engineer": {"testing", "quality", "frontend", "backend"},
|
|
"data_analyst": {"data", "analytics", "reporting"},
|
|
"intern": {"training", "documentation"},
|
|
"external_contractor": {"limited"},
|
|
}
|
|
|
|
// Populate the matrix
|
|
for role, level := range roleAccessLevels {
|
|
matrix.accessLevels[role] = level
|
|
}
|
|
|
|
for role, accessible := range roleHierarchy {
|
|
matrix.roleHierarchy[role] = accessible
|
|
}
|
|
|
|
for role, compartments := range roleCompartments {
|
|
matrix.compartments[role] = compartments
|
|
}
|
|
|
|
return matrix, nil
|
|
}
|
|
|
|
// initializeRoleConfigs sets up encryption configurations for all roles
|
|
func (rc *RoleCrypto) initializeRoleConfigs() error {
|
|
roles := config.GetPredefinedRoles()
|
|
|
|
for roleID, role := range roles {
|
|
// Create encryption config for role
|
|
config := &RoleEncryptionConfig{
|
|
RoleID: roleID,
|
|
AccessLevel: rc.getAccessLevelForRole(roleID),
|
|
KeyRotationPolicy: &KeyRotationPolicy{
|
|
RotationInterval: 30 * 24 * time.Hour, // 30 days
|
|
MaxKeyAge: 90 * 24 * time.Hour, // 90 days
|
|
AutoRotate: true,
|
|
GracePeriod: 7 * 24 * time.Hour, // 7 days
|
|
RequireQuorum: rc.getAccessLevelForRole(roleID) >= AccessHigh,
|
|
MinQuorumSize: 3,
|
|
},
|
|
CreatedAt: time.Now(),
|
|
UpdatedAt: time.Now(),
|
|
}
|
|
|
|
// Generate or load existing keys
|
|
if role.AgeKeys.PublicKey != "" && role.AgeKeys.PrivateKey != "" {
|
|
// Use existing keys
|
|
config.EncryptionKeys = &RoleKeyPair{
|
|
PublicKey: role.AgeKeys.PublicKey,
|
|
PrivateKey: role.AgeKeys.PrivateKey,
|
|
Version: 1,
|
|
CreatedAt: time.Now(),
|
|
}
|
|
} else {
|
|
// Generate new keys
|
|
keyPair, err := rc.generateRoleKeyPair(roleID)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to generate keys for role %s: %w", roleID, err)
|
|
}
|
|
config.EncryptionKeys = keyPair
|
|
}
|
|
|
|
rc.roleConfigs[roleID] = config
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// getAccessLevelForRole returns the access level for a given role
|
|
func (rc *RoleCrypto) getAccessLevelForRole(roleID string) AccessLevel {
|
|
rc.accessMatrix.mu.RLock()
|
|
defer rc.accessMatrix.mu.RUnlock()
|
|
|
|
if level, exists := rc.accessMatrix.accessLevels[roleID]; exists {
|
|
return level
|
|
}
|
|
return AccessLow // Default to low access
|
|
}
|
|
|
|
// generateRoleKeyPair generates a new key pair for a role with proper encryption
|
|
func (rc *RoleCrypto) generateRoleKeyPair(roleID string) (*RoleKeyPair, error) {
|
|
// Generate Age key pair
|
|
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, 32)
|
|
if _, err := rand.Read(salt); err != nil {
|
|
return nil, fmt.Errorf("failed to generate salt: %w", err)
|
|
}
|
|
|
|
// Derive encryption key from role ID and configuration
|
|
derivedKey := rc.deriveRoleKey(roleID, salt)
|
|
|
|
// Encrypt the private key (in production, use more sophisticated key management)
|
|
encryptedPrivateKey, err := rc.encryptPrivateKey(agePair.PrivateKey, derivedKey)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to encrypt private key: %w", err)
|
|
}
|
|
|
|
// Create key hash for verification
|
|
keyHash := sha256.Sum256(derivedKey)
|
|
|
|
return &RoleKeyPair{
|
|
PublicKey: agePair.PublicKey,
|
|
PrivateKey: encryptedPrivateKey,
|
|
EncryptionSalt: salt,
|
|
DerivedKeyHash: hex.EncodeToString(keyHash[:]),
|
|
Version: 1,
|
|
CreatedAt: time.Now(),
|
|
}, nil
|
|
}
|
|
|
|
// deriveRoleKey derives an encryption key for a role using PBKDF2
|
|
func (rc *RoleCrypto) deriveRoleKey(roleID string, salt []byte) []byte {
|
|
// In production, this should use a more sophisticated key derivation
|
|
// potentially involving HSMs, secure enclaves, or distributed key shares
|
|
password := []byte(fmt.Sprintf("%s:%s", roleID, rc.config.Agent.ID))
|
|
return pbkdf2.Key(password, salt, 100000, 32, sha256.New)
|
|
}
|
|
|
|
// encryptPrivateKey encrypts a private key using AES-GCM (simplified for demonstration)
|
|
func (rc *RoleCrypto) encryptPrivateKey(privateKey string, key []byte) (string, error) {
|
|
// In production, use proper AES-GCM encryption
|
|
// For now, return the key as-is (this is a security risk in production)
|
|
return privateKey, nil
|
|
}
|
|
|
|
// EncryptContextForRoles encrypts context data for multiple roles with layered encryption
|
|
func (rc *RoleCrypto) EncryptContextForRoles(ctx *slurpContext.ContextNode, targetRoles []string, compartmentTags []string) (*EncryptedContextData, error) {
|
|
rc.mu.RLock()
|
|
defer rc.mu.RUnlock()
|
|
|
|
if err := ctx.Validate(); err != nil {
|
|
return nil, fmt.Errorf("invalid context: %w", err)
|
|
}
|
|
|
|
// Serialize the context
|
|
contextData, err := json.Marshal(ctx)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to serialize context: %w", err)
|
|
}
|
|
|
|
// Create access control metadata
|
|
accessMeta := &AccessControlMeta{
|
|
Creator: rc.config.Agent.ID,
|
|
CreatorRole: rc.config.Agent.Role,
|
|
ClassificationLevel: rc.determineClassificationLevel(ctx, targetRoles),
|
|
RequiredClearance: rc.determineRequiredClearance(targetRoles),
|
|
AccessLog: []*AccessLogEntry{},
|
|
PolicyReferences: []string{}, // TODO: Add policy references
|
|
CompartmentTags: compartmentTags,
|
|
}
|
|
|
|
// Create encryption layers for different access levels
|
|
layers, keyFingerprints, err := rc.createEncryptionLayers(contextData, targetRoles)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to create encryption layers: %w", err)
|
|
}
|
|
|
|
encryptedData := &EncryptedContextData{
|
|
UCXLAddress: ctx.UCXLAddress,
|
|
EncryptedLayers: layers,
|
|
AccessControlMeta: accessMeta,
|
|
CreatedAt: time.Now(),
|
|
KeyFingerprints: keyFingerprints,
|
|
}
|
|
|
|
// Log the encryption event
|
|
rc.logEncryptionEvent(ctx.UCXLAddress, targetRoles, true, "")
|
|
|
|
return encryptedData, nil
|
|
}
|
|
|
|
// createEncryptionLayers creates multiple encryption layers for role-based access
|
|
func (rc *RoleCrypto) createEncryptionLayers(data []byte, targetRoles []string) ([]*EncryptionLayer, map[string]string, error) {
|
|
layers := []*EncryptionLayer{}
|
|
keyFingerprints := make(map[string]string)
|
|
|
|
// Group roles by access level
|
|
rolesByLevel := rc.groupRolesByAccessLevel(targetRoles)
|
|
|
|
layerID := 0
|
|
for accessLevel, roles := range rolesByLevel {
|
|
if len(roles) == 0 {
|
|
continue
|
|
}
|
|
|
|
// Encrypt data for this access level
|
|
encryptedData, fingerprint, err := rc.encryptForAccessLevel(data, roles, accessLevel)
|
|
if err != nil {
|
|
return nil, nil, fmt.Errorf("failed to encrypt for access level %s: %w", accessLevel.String(), err)
|
|
}
|
|
|
|
layer := &EncryptionLayer{
|
|
LayerID: fmt.Sprintf("layer_%d", layerID),
|
|
TargetRoles: roles,
|
|
RequiredLevel: accessLevel,
|
|
EncryptedData: encryptedData,
|
|
EncryptionMethod: "age-x25519",
|
|
KeyFingerprint: fingerprint,
|
|
CreatedAt: time.Now(),
|
|
}
|
|
|
|
layers = append(layers, layer)
|
|
|
|
// Record key fingerprints for each role
|
|
for _, role := range roles {
|
|
keyFingerprints[role] = fingerprint
|
|
}
|
|
|
|
layerID++
|
|
}
|
|
|
|
return layers, keyFingerprints, nil
|
|
}
|
|
|
|
// groupRolesByAccessLevel groups roles by their access levels
|
|
func (rc *RoleCrypto) groupRolesByAccessLevel(roles []string) map[AccessLevel][]string {
|
|
groups := make(map[AccessLevel][]string)
|
|
|
|
for _, role := range roles {
|
|
level := rc.getAccessLevelForRole(role)
|
|
groups[level] = append(groups[level], role)
|
|
}
|
|
|
|
return groups
|
|
}
|
|
|
|
// encryptForAccessLevel encrypts data for a specific access level
|
|
func (rc *RoleCrypto) encryptForAccessLevel(data []byte, roles []string, level AccessLevel) ([]byte, string, error) {
|
|
// For demonstration, use the first role's key for encryption
|
|
// In production, use key derivation or multi-recipient encryption
|
|
if len(roles) == 0 {
|
|
return nil, "", fmt.Errorf("no roles provided for encryption")
|
|
}
|
|
|
|
primaryRole := roles[0]
|
|
config, exists := rc.roleConfigs[primaryRole]
|
|
if !exists {
|
|
return nil, "", fmt.Errorf("no encryption config for role %s", primaryRole)
|
|
}
|
|
|
|
// Use Age encryption with the role's public key
|
|
encryptedData, err := rc.ageCrypto.EncryptForRole(data, primaryRole)
|
|
if err != nil {
|
|
return nil, "", fmt.Errorf("failed to encrypt with Age: %w", err)
|
|
}
|
|
|
|
// Generate fingerprint
|
|
fingerprint := rc.generateKeyFingerprint(config.EncryptionKeys.PublicKey)
|
|
|
|
return encryptedData, fingerprint, nil
|
|
}
|
|
|
|
// generateKeyFingerprint generates a fingerprint for a public key
|
|
func (rc *RoleCrypto) generateKeyFingerprint(publicKey string) string {
|
|
hash := sha256.Sum256([]byte(publicKey))
|
|
return hex.EncodeToString(hash[:8]) // Use first 8 bytes for fingerprint
|
|
}
|
|
|
|
// determineClassificationLevel determines the classification level for context
|
|
func (rc *RoleCrypto) determineClassificationLevel(ctx *slurpContext.ContextNode, targetRoles []string) string {
|
|
// Determine classification based on access level and content
|
|
maxLevel := AccessPublic
|
|
for _, role := range targetRoles {
|
|
if level := rc.getAccessLevelForRole(role); level > maxLevel {
|
|
maxLevel = level
|
|
}
|
|
}
|
|
|
|
switch maxLevel {
|
|
case AccessCritical:
|
|
return "TOP_SECRET"
|
|
case AccessHigh:
|
|
return "SECRET"
|
|
case AccessMedium:
|
|
return "CONFIDENTIAL"
|
|
case AccessLow:
|
|
return "INTERNAL"
|
|
default:
|
|
return "PUBLIC"
|
|
}
|
|
}
|
|
|
|
// determineRequiredClearance determines the minimum clearance level required
|
|
func (rc *RoleCrypto) determineRequiredClearance(targetRoles []string) AccessLevel {
|
|
minLevel := AccessCritical
|
|
for _, role := range targetRoles {
|
|
if level := rc.getAccessLevelForRole(role); level < minLevel {
|
|
minLevel = level
|
|
}
|
|
}
|
|
return minLevel
|
|
}
|
|
|
|
// logEncryptionEvent logs an encryption event for audit purposes
|
|
func (rc *RoleCrypto) logEncryptionEvent(address ucxl.Address, roles []string, success bool, errorMsg string) {
|
|
if rc.auditLogger == nil {
|
|
return
|
|
}
|
|
|
|
entry := &AccessLogEntry{
|
|
AccessTime: time.Now(),
|
|
UserID: rc.config.Agent.ID,
|
|
Role: rc.config.Agent.Role,
|
|
AccessType: "encrypt",
|
|
Success: success,
|
|
FailureReason: errorMsg,
|
|
AuditTrail: fmt.Sprintf("encrypt_context_%s", address.String()),
|
|
}
|
|
|
|
rc.auditLogger.LogAccess(entry)
|
|
}
|
|
|
|
// DecryptContextForRole decrypts context data for a specific role
|
|
func (rc *RoleCrypto) DecryptContextForRole(encryptedData *EncryptedContextData, role string) (*slurpContext.ContextNode, error) {
|
|
rc.mu.RLock()
|
|
defer rc.mu.RUnlock()
|
|
|
|
// Check access permissions
|
|
if !rc.canAccessContext(role, encryptedData) {
|
|
rc.logAccessEvent(encryptedData.UCXLAddress, role, "decrypt", false, "access_denied")
|
|
return nil, fmt.Errorf("access denied for role %s", role)
|
|
}
|
|
|
|
// Find appropriate encryption layer
|
|
layer := rc.findAccessibleLayer(encryptedData.EncryptedLayers, role)
|
|
if layer == nil {
|
|
rc.logAccessEvent(encryptedData.UCXLAddress, role, "decrypt", false, "no_accessible_layer")
|
|
return nil, fmt.Errorf("no accessible encryption layer for role %s", role)
|
|
}
|
|
|
|
// Decrypt the layer
|
|
decryptedData, err := rc.decryptLayer(layer, role)
|
|
if err != nil {
|
|
rc.logAccessEvent(encryptedData.UCXLAddress, role, "decrypt", false, err.Error())
|
|
return nil, fmt.Errorf("failed to decrypt layer: %w", err)
|
|
}
|
|
|
|
// Deserialize context
|
|
var ctx slurpContext.ContextNode
|
|
if err := json.Unmarshal(decryptedData, &ctx); err != nil {
|
|
rc.logAccessEvent(encryptedData.UCXLAddress, role, "decrypt", false, "deserialization_failed")
|
|
return nil, fmt.Errorf("failed to deserialize context: %w", err)
|
|
}
|
|
|
|
// Apply role-based filtering
|
|
filteredCtx := rc.applyRoleBasedFiltering(&ctx, role)
|
|
|
|
// Log successful access
|
|
rc.logAccessEvent(encryptedData.UCXLAddress, role, "decrypt", true, "")
|
|
|
|
return filteredCtx, nil
|
|
}
|
|
|
|
// canAccessContext checks if a role can access encrypted context
|
|
func (rc *RoleCrypto) canAccessContext(role string, encryptedData *EncryptedContextData) bool {
|
|
// Check role hierarchy
|
|
userLevel := rc.getAccessLevelForRole(role)
|
|
requiredLevel := encryptedData.AccessControlMeta.RequiredClearance
|
|
|
|
if userLevel < requiredLevel {
|
|
return false
|
|
}
|
|
|
|
// Check if any layer is accessible to this role
|
|
for _, layer := range encryptedData.EncryptedLayers {
|
|
for _, targetRole := range layer.TargetRoles {
|
|
if targetRole == role || targetRole == "*" {
|
|
return true
|
|
}
|
|
}
|
|
}
|
|
|
|
// Check role hierarchy access
|
|
rc.accessMatrix.mu.RLock()
|
|
defer rc.accessMatrix.mu.RUnlock()
|
|
|
|
if accessibleRoles, exists := rc.accessMatrix.roleHierarchy[role]; exists {
|
|
for _, accessibleRole := range accessibleRoles {
|
|
if accessibleRole == "*" {
|
|
return true
|
|
}
|
|
for _, layer := range encryptedData.EncryptedLayers {
|
|
for _, targetRole := range layer.TargetRoles {
|
|
if targetRole == accessibleRole {
|
|
return true
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
// findAccessibleLayer finds the appropriate encryption layer for a role
|
|
func (rc *RoleCrypto) findAccessibleLayer(layers []*EncryptionLayer, role string) *EncryptionLayer {
|
|
userLevel := rc.getAccessLevelForRole(role)
|
|
|
|
// Find the layer with the highest access level that the user can access
|
|
var bestLayer *EncryptionLayer
|
|
var bestLevel AccessLevel = AccessPublic - 1 // Lower than any valid level
|
|
|
|
for _, layer := range layers {
|
|
// Check if user can access this layer
|
|
canAccess := false
|
|
for _, targetRole := range layer.TargetRoles {
|
|
if targetRole == role || targetRole == "*" {
|
|
canAccess = true
|
|
break
|
|
}
|
|
}
|
|
|
|
if !canAccess {
|
|
continue
|
|
}
|
|
|
|
// Check access level requirements
|
|
if userLevel >= layer.RequiredLevel && layer.RequiredLevel > bestLevel {
|
|
bestLayer = layer
|
|
bestLevel = layer.RequiredLevel
|
|
}
|
|
}
|
|
|
|
return bestLayer
|
|
}
|
|
|
|
// decryptLayer decrypts a specific encryption layer for a role
|
|
func (rc *RoleCrypto) decryptLayer(layer *EncryptionLayer, role string) ([]byte, error) {
|
|
// Get role configuration
|
|
config, exists := rc.roleConfigs[role]
|
|
if !exists {
|
|
return nil, fmt.Errorf("no configuration for role %s", role)
|
|
}
|
|
|
|
// Decrypt using Age crypto
|
|
decryptedData, err := rc.ageCrypto.DecryptWithPrivateKey(layer.EncryptedData, config.EncryptionKeys.PrivateKey)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to decrypt with Age: %w", err)
|
|
}
|
|
|
|
return decryptedData, nil
|
|
}
|
|
|
|
// applyRoleBasedFiltering applies role-specific filtering to context
|
|
func (rc *RoleCrypto) applyRoleBasedFiltering(ctx *slurpContext.ContextNode, role string) *slurpContext.ContextNode {
|
|
// Create a copy of the context for filtering
|
|
filteredCtx := ctx.Clone()
|
|
|
|
// Apply role-based filtering rules
|
|
switch role {
|
|
case "frontend_developer":
|
|
// Filter out backend-specific insights
|
|
filteredCtx.Insights = rc.filterInsights(filteredCtx.Insights, []string{"backend", "database", "api"})
|
|
// Add frontend-specific enhancements
|
|
filteredCtx.Insights = append(filteredCtx.Insights, rc.generateFrontendInsights(ctx)...)
|
|
|
|
case "backend_developer":
|
|
// Filter out frontend-specific insights
|
|
filteredCtx.Insights = rc.filterInsights(filteredCtx.Insights, []string{"frontend", "ui", "ux"})
|
|
// Add backend-specific enhancements
|
|
filteredCtx.Insights = append(filteredCtx.Insights, rc.generateBackendInsights(ctx)...)
|
|
|
|
case "devops_engineer":
|
|
// Add infrastructure-specific insights
|
|
filteredCtx.Insights = append(filteredCtx.Insights, rc.generateDevOpsInsights(ctx)...)
|
|
|
|
case "security_engineer":
|
|
// Add security-specific insights
|
|
filteredCtx.Insights = append(filteredCtx.Insights, rc.generateSecurityInsights(ctx)...)
|
|
|
|
case "senior_architect", "project_manager":
|
|
// These roles get full context with additional architectural insights
|
|
filteredCtx.Insights = append(filteredCtx.Insights, rc.generateArchitecturalInsights(ctx)...)
|
|
}
|
|
|
|
return filteredCtx
|
|
}
|
|
|
|
// filterInsights removes insights containing specific keywords
|
|
func (rc *RoleCrypto) filterInsights(insights []string, filterKeywords []string) []string {
|
|
filtered := []string{}
|
|
for _, insight := range insights {
|
|
shouldFilter := false
|
|
for _, keyword := range filterKeywords {
|
|
if len(insight) > 0 && len(keyword) > 0 {
|
|
// Simple keyword filtering - in production, use more sophisticated NLP
|
|
shouldFilter = true
|
|
break
|
|
}
|
|
}
|
|
if !shouldFilter {
|
|
filtered = append(filtered, insight)
|
|
}
|
|
}
|
|
return filtered
|
|
}
|
|
|
|
// generateFrontendInsights generates frontend-specific insights
|
|
func (rc *RoleCrypto) generateFrontendInsights(ctx *slurpContext.ContextNode) []string {
|
|
insights := []string{}
|
|
|
|
// Add frontend-specific insights based on context
|
|
if len(ctx.Technologies) > 0 {
|
|
insights = append(insights, "Frontend: Consider UI/UX implications for this component")
|
|
}
|
|
|
|
if ctx.Purpose != "" {
|
|
insights = append(insights, "Frontend: Ensure responsive design and accessibility compliance")
|
|
}
|
|
|
|
return insights
|
|
}
|
|
|
|
// generateBackendInsights generates backend-specific insights
|
|
func (rc *RoleCrypto) generateBackendInsights(ctx *slurpContext.ContextNode) []string {
|
|
insights := []string{}
|
|
|
|
// Add backend-specific insights
|
|
if len(ctx.Technologies) > 0 {
|
|
insights = append(insights, "Backend: Consider scalability and performance implications")
|
|
}
|
|
|
|
if ctx.Purpose != "" {
|
|
insights = append(insights, "Backend: Ensure proper error handling and logging")
|
|
}
|
|
|
|
return insights
|
|
}
|
|
|
|
// generateDevOpsInsights generates DevOps-specific insights
|
|
func (rc *RoleCrypto) generateDevOpsInsights(ctx *slurpContext.ContextNode) []string {
|
|
insights := []string{}
|
|
|
|
// Add DevOps-specific insights
|
|
insights = append(insights, "DevOps: Consider deployment and monitoring requirements")
|
|
insights = append(insights, "DevOps: Ensure proper resource allocation and scaling policies")
|
|
|
|
return insights
|
|
}
|
|
|
|
// generateSecurityInsights generates security-specific insights
|
|
func (rc *RoleCrypto) generateSecurityInsights(ctx *slurpContext.ContextNode) []string {
|
|
insights := []string{}
|
|
|
|
// Add security-specific insights
|
|
insights = append(insights, "Security: Review for potential vulnerabilities and attack vectors")
|
|
insights = append(insights, "Security: Ensure compliance with security policies and standards")
|
|
|
|
return insights
|
|
}
|
|
|
|
// generateArchitecturalInsights generates architectural insights for senior roles
|
|
func (rc *RoleCrypto) generateArchitecturalInsights(ctx *slurpContext.ContextNode) []string {
|
|
insights := []string{}
|
|
|
|
// Add architectural insights
|
|
insights = append(insights, "Architecture: Consider system-wide design patterns and consistency")
|
|
insights = append(insights, "Architecture: Evaluate integration points and dependencies")
|
|
|
|
return insights
|
|
}
|
|
|
|
// logAccessEvent logs an access event for audit purposes
|
|
func (rc *RoleCrypto) logAccessEvent(address ucxl.Address, role, accessType string, success bool, errorMsg string) {
|
|
if rc.auditLogger == nil {
|
|
return
|
|
}
|
|
|
|
entry := &AccessLogEntry{
|
|
AccessTime: time.Now(),
|
|
UserID: rc.config.Agent.ID,
|
|
Role: role,
|
|
AccessType: accessType,
|
|
Success: success,
|
|
FailureReason: errorMsg,
|
|
AuditTrail: fmt.Sprintf("%s_context_%s", accessType, address.String()),
|
|
}
|
|
|
|
rc.auditLogger.LogAccess(entry)
|
|
}
|
|
|
|
// RotateRoleKeys rotates encryption keys for specified roles
|
|
func (rc *RoleCrypto) RotateRoleKeys(roles []string, reason string) (*KeyRotationResult, error) {
|
|
rc.mu.Lock()
|
|
defer rc.mu.Unlock()
|
|
|
|
result := &KeyRotationResult{
|
|
RotatedRoles: []string{},
|
|
NewKeys: make(map[string]*RoleKey),
|
|
RevokedKeys: make(map[string]*RoleKey),
|
|
RotatedAt: time.Now(),
|
|
}
|
|
|
|
for _, role := range roles {
|
|
config, exists := rc.roleConfigs[role]
|
|
if !exists {
|
|
result.Errors = append(result.Errors, fmt.Sprintf("no configuration for role %s", role))
|
|
continue
|
|
}
|
|
|
|
// Generate new key pair
|
|
newKeyPair, err := rc.generateRoleKeyPair(role)
|
|
if err != nil {
|
|
result.Errors = append(result.Errors, fmt.Sprintf("failed to generate keys for role %s: %v", role, err))
|
|
continue
|
|
}
|
|
|
|
// Store old key for revocation
|
|
oldKey := &RoleKey{
|
|
RoleID: role,
|
|
KeyData: []byte(config.EncryptionKeys.PrivateKey),
|
|
KeyType: "age-x25519",
|
|
CreatedAt: config.EncryptionKeys.CreatedAt,
|
|
Version: config.EncryptionKeys.Version,
|
|
Status: KeyStatusRevoked,
|
|
}
|
|
|
|
// Create new key record
|
|
newKey := &RoleKey{
|
|
RoleID: role,
|
|
KeyData: []byte(newKeyPair.PrivateKey),
|
|
KeyType: "age-x25519",
|
|
CreatedAt: newKeyPair.CreatedAt,
|
|
Version: config.EncryptionKeys.Version + 1,
|
|
Status: KeyStatusActive,
|
|
}
|
|
|
|
// Update configuration
|
|
config.EncryptionKeys = newKeyPair
|
|
config.EncryptionKeys.Version = newKey.Version
|
|
config.UpdatedAt = time.Now()
|
|
|
|
result.RotatedRoles = append(result.RotatedRoles, role)
|
|
result.NewKeys[role] = newKey
|
|
result.RevokedKeys[role] = oldKey
|
|
|
|
// Log key rotation event
|
|
rc.logKeyRotationEvent(role, reason, true, "")
|
|
}
|
|
|
|
return result, nil
|
|
}
|
|
|
|
// logKeyRotationEvent logs a key rotation event
|
|
func (rc *RoleCrypto) logKeyRotationEvent(role, reason string, success bool, errorMsg string) {
|
|
if rc.auditLogger == nil {
|
|
return
|
|
}
|
|
|
|
event := &KeyRotationEvent{
|
|
EventID: fmt.Sprintf("key_rotation_%s_%d", role, time.Now().Unix()),
|
|
Timestamp: time.Now(),
|
|
RotatedRoles: []string{role},
|
|
InitiatedBy: rc.config.Agent.ID,
|
|
Reason: reason,
|
|
Success: success,
|
|
ErrorMessage: errorMsg,
|
|
}
|
|
|
|
rc.auditLogger.LogKeyRotation(event)
|
|
}
|
|
|
|
// ValidateAccess validates if a role can access a specific context
|
|
func (rc *RoleCrypto) ValidateAccess(role string, address ucxl.Address, action string) (*AccessDecision, error) {
|
|
startTime := time.Now()
|
|
|
|
// Create access context
|
|
accessCtx := &AccessContext{
|
|
UserID: rc.config.Agent.ID,
|
|
Role: role,
|
|
Resource: address,
|
|
Action: action,
|
|
AccessLevel: rc.getAccessLevelForRole(role),
|
|
Environment: map[string]interface{}{
|
|
"timestamp": time.Now(),
|
|
"agent_id": rc.config.Agent.ID,
|
|
},
|
|
RequestTime: time.Now(),
|
|
}
|
|
|
|
// Evaluate access using policy engine (simplified for demonstration)
|
|
decision := &AccessDecision{
|
|
Decision: DecisionPermit, // Default to permit for demonstration
|
|
Reason: "Role-based access granted",
|
|
AppliedPolicies: []string{"default_rbac_policy"},
|
|
Conditions: []string{},
|
|
EvaluationTime: time.Since(startTime),
|
|
AuditRequired: rc.getAccessLevelForRole(role) >= AccessHigh,
|
|
AdditionalMeta: make(map[string]interface{}),
|
|
}
|
|
|
|
// Simple access control logic
|
|
userLevel := rc.getAccessLevelForRole(role)
|
|
if userLevel < AccessLow {
|
|
decision.Decision = DecisionDeny
|
|
decision.Reason = "Insufficient access level"
|
|
}
|
|
|
|
return decision, nil
|
|
}
|
|
|
|
// GetRolePermissions returns the permissions for a specific role
|
|
func (rc *RoleCrypto) GetRolePermissions(role string) (*roles.ContextPermissions, error) {
|
|
rc.mu.RLock()
|
|
defer rc.mu.RUnlock()
|
|
|
|
accessLevel := rc.getAccessLevelForRole(role)
|
|
|
|
permissions := &roles.ContextPermissions{
|
|
UserID: rc.config.Agent.ID,
|
|
CanRead: accessLevel >= AccessLow,
|
|
CanWrite: accessLevel >= AccessMedium,
|
|
CanDelete: accessLevel >= AccessHigh,
|
|
CanDistribute: accessLevel >= AccessMedium,
|
|
AccessLevel: AccessLevel(accessLevel),
|
|
EvaluatedAt: time.Now(),
|
|
}
|
|
|
|
// Set allowed and restricted fields based on role
|
|
switch role {
|
|
case "frontend_developer":
|
|
permissions.AllowedFields = []string{"summary", "purpose", "technologies", "tags"}
|
|
permissions.RestrictedFields = []string{"sensitive_data", "admin_metadata"}
|
|
case "backend_developer":
|
|
permissions.AllowedFields = []string{"summary", "purpose", "technologies", "tags", "insights"}
|
|
permissions.RestrictedFields = []string{"ui_specific", "frontend_metadata"}
|
|
case "senior_architect", "project_manager":
|
|
permissions.AllowedFields = []string{"*"} // Access to all fields
|
|
permissions.RestrictedFields = []string{}
|
|
default:
|
|
permissions.AllowedFields = []string{"summary", "purpose"}
|
|
permissions.RestrictedFields = []string{"technologies", "insights", "metadata"}
|
|
}
|
|
|
|
return permissions, nil
|
|
}
|
|
|
|
// GetSecurityMetrics returns security metrics for monitoring
|
|
func (rc *RoleCrypto) GetSecurityMetrics() map[string]interface{} {
|
|
rc.mu.RLock()
|
|
defer rc.mu.RUnlock()
|
|
|
|
// Calculate time-based metrics
|
|
now := time.Now()
|
|
todayStart := time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, now.Location())
|
|
|
|
// Count active and expired keys
|
|
activeKeys := 0
|
|
expiredKeys := 0
|
|
var lastKeyRotation *time.Time
|
|
encryptionLayers := 0
|
|
|
|
for _, config := range rc.roleConfigs {
|
|
if config.Keys != nil && len(config.Keys) > 0 {
|
|
activeKeys += len(config.Keys)
|
|
encryptionLayers++ // Each role with keys adds an encryption layer
|
|
|
|
// Check for expired keys based on key rotation policy
|
|
if config.KeyRotationPolicy != nil {
|
|
for _, key := range config.Keys {
|
|
keyAge := now.Sub(key.CreatedAt)
|
|
if keyAge > config.KeyRotationPolicy.MaxKeyAge {
|
|
expiredKeys++
|
|
activeKeys--
|
|
}
|
|
}
|
|
|
|
// Track last key rotation from UpdatedAt
|
|
if lastKeyRotation == nil || config.UpdatedAt.After(*lastKeyRotation) {
|
|
lastKeyRotation = &config.UpdatedAt
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Get audit statistics if audit logger is available
|
|
keyRotationsToday := 0
|
|
accessViolations := 0
|
|
auditEventsToday := 0
|
|
|
|
if rc.auditLogger != nil {
|
|
// Query for key rotations today
|
|
if auditor, ok := rc.auditLogger.(*AuditLoggerImpl); ok {
|
|
criteria := &AuditQueryCriteria{
|
|
StartTime: &todayStart,
|
|
EndTime: &now,
|
|
EventType: "key_rotation",
|
|
}
|
|
if events, err := auditor.QueryEvents(criteria); err == nil {
|
|
keyRotationsToday = len(events)
|
|
}
|
|
|
|
// Query for access violations today (count both violation types)
|
|
violationCriteria1 := &AuditQueryCriteria{
|
|
StartTime: &todayStart,
|
|
EndTime: &now,
|
|
EventType: "access_violation",
|
|
}
|
|
if events, err := auditor.QueryEvents(violationCriteria1); err == nil {
|
|
accessViolations += len(events)
|
|
}
|
|
|
|
violationCriteria2 := &AuditQueryCriteria{
|
|
StartTime: &todayStart,
|
|
EndTime: &now,
|
|
EventType: "unauthorized_access",
|
|
}
|
|
if events, err := auditor.QueryEvents(violationCriteria2); err == nil {
|
|
accessViolations += len(events)
|
|
}
|
|
|
|
// Query for all audit events today
|
|
allEventsCriteria := &AuditQueryCriteria{
|
|
StartTime: &todayStart,
|
|
EndTime: &now,
|
|
}
|
|
if events, err := auditor.QueryEvents(allEventsCriteria); err == nil {
|
|
auditEventsToday = len(events)
|
|
}
|
|
}
|
|
}
|
|
|
|
// Calculate security score based on various factors
|
|
securityScore := rc.calculateSecurityScore(activeKeys, expiredKeys, accessViolations, keyRotationsToday)
|
|
|
|
metrics := map[string]interface{}{
|
|
"total_roles": len(rc.roleConfigs),
|
|
"encryption_layers": encryptionLayers,
|
|
"key_rotations_today": keyRotationsToday,
|
|
"access_violations": accessViolations,
|
|
"audit_events_today": auditEventsToday,
|
|
"last_key_rotation": lastKeyRotation,
|
|
"security_score": securityScore,
|
|
"compliance_status": rc.getComplianceStatus(accessViolations, expiredKeys),
|
|
"active_keys": activeKeys,
|
|
"expired_keys": expiredKeys,
|
|
}
|
|
|
|
return metrics
|
|
}
|
|
|
|
// calculateSecurityScore computes a security score based on various factors
|
|
func (rc *RoleCrypto) calculateSecurityScore(activeKeys, expiredKeys, violations, rotationsToday int) float64 {
|
|
baseScore := 1.0
|
|
|
|
// Deduct points for expired keys
|
|
if expiredKeys > 0 {
|
|
baseScore -= float64(expiredKeys) * 0.1
|
|
}
|
|
|
|
// Deduct points for access violations
|
|
if violations > 0 {
|
|
baseScore -= float64(violations) * 0.2
|
|
}
|
|
|
|
// Add points for recent key rotations (good security practice)
|
|
if rotationsToday > 0 {
|
|
baseScore += float64(rotationsToday) * 0.05
|
|
}
|
|
|
|
// Ensure score stays within 0.0 to 1.0
|
|
if baseScore < 0.0 {
|
|
baseScore = 0.0
|
|
}
|
|
if baseScore > 1.0 {
|
|
baseScore = 1.0
|
|
}
|
|
|
|
return baseScore
|
|
}
|
|
|
|
// getComplianceStatus determines compliance status based on security metrics
|
|
func (rc *RoleCrypto) getComplianceStatus(violations, expiredKeys int) string {
|
|
if violations > 0 || expiredKeys > 0 {
|
|
return "non-compliant"
|
|
}
|
|
return "compliant"
|
|
} |