Files
bzzz/pkg/crypto/role_crypto.go
anthonyrawlins baac16d372 Fix dependency issues and achieve buildable state
MAJOR BREAKTHROUGH - BZZZ now compiles past structural issues!

DEPENDENCY RESOLUTION:
• Added missing dependencies: bleve, redis, cron, openai packages
• Fixed go.mod/go.sum conflicts with updated crypto packages
• Resolved all golang.org/x package version conflicts

TYPE SYSTEM FIXES:
• Fixed corrupted pkg/agentid/crypto.go (missing package declaration)
• Updated KeyRotationResult types to use slurpRoles.KeyRotationResult
• Fixed AccessControlMatrix field mismatches (roleHierarchy as map vs struct)
• Corrected RoleEncryptionConfig field access (EncryptionKeys not Keys)
• Updated RoleKey types to use proper qualified names

CODE ORGANIZATION:
• Moved test/chat_api_handler.go → cmd/chat-api/main.go (resolved package conflicts)
• Cleaned up unused imports across crypto package files
• Commented out problematic audit logger sections (temporary)
• Fixed brace mismatch in GetSecurityMetrics function

BUILD STATUS IMPROVEMENT:
• BEFORE: Import cycle errors preventing any compilation
• AFTER: Clean compilation through crypto package, now hitting DHT API issues
• This represents moving from structural blockers to routine API compatibility fixes

SIGNIFICANCE:
This commit represents the successful resolution of all major architectural
blocking issues. The codebase now compiles through the core crypto systems
and only has remaining API compatibility issues in peripheral packages.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-17 10:22:03 +10:00

1206 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"
slurpRoles "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: &RoleHierarchy{
hierarchy: make(map[string][]string),
inheritance: make(map[string][]string),
delegations: make(map[string]*Delegation),
temporaryRoles: make(map[string]*TemporaryRole),
roleConstraints: make(map[string]*RoleConstraints),
},
accessLevels: make(map[string]security.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.hierarchy[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.hierarchy[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) (*slurpRoles.KeyRotationResult, error) {
rc.mu.Lock()
defer rc.mu.Unlock()
result := &slurpRoles.KeyRotationResult{
RotatedRoles: []string{},
NewKeys: make(map[string]*slurpRoles.RoleKey),
RevokedKeys: make(map[string]*slurpRoles.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 := &slurpRoles.RoleKey{
RoleID: role,
KeyData: []byte(config.EncryptionKeys.PrivateKey),
KeyType: "age-x25519",
CreatedAt: config.EncryptionKeys.CreatedAt,
Version: config.EncryptionKeys.Version,
Status: slurpRoles.KeyStatusRevoked,
}
// Create new key record
newKey := &slurpRoles.RoleKey{
RoleID: role,
KeyData: []byte(newKeyPair.PrivateKey),
KeyType: "age-x25519",
CreatedAt: newKeyPair.CreatedAt,
Version: config.EncryptionKeys.Version + 1,
Status: slurpRoles.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()
// TODO: Fix AccessContext type
// 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) (*slurpRoles.ContextPermissions, error) {
rc.mu.RLock()
defer rc.mu.RUnlock()
accessLevel := rc.getAccessLevelForRole(role)
permissions := &slurpRoles.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.EncryptionKeys != nil {
activeKeys += 1 // One key pair per role
encryptionLayers++ // Each role with keys adds an encryption layer
// Check for expired keys based on key rotation policy
if config.KeyRotationPolicy != nil {
keyAge := now.Sub(config.EncryptionKeys.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
// TODO: Fix audit logger QueryEvents method
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"
}