// 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" }