 9bdcbe0447
			
		
	
	9bdcbe0447
	
	
	
		
			
			Major integrations and fixes: - Added BACKBEAT SDK integration for P2P operation timing - Implemented beat-aware status tracking for distributed operations - Added Docker secrets support for secure license management - Resolved KACHING license validation via HTTPS/TLS - Updated docker-compose configuration for clean stack deployment - Disabled rollback policies to prevent deployment failures - Added license credential storage (CHORUS-DEV-MULTI-001) Technical improvements: - BACKBEAT P2P operation tracking with phase management - Enhanced configuration system with file-based secrets - Improved error handling for license validation - Clean separation of KACHING and CHORUS deployment stacks 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
		
			
				
	
	
		
			959 lines
		
	
	
		
			31 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			959 lines
		
	
	
		
			31 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| // Package crypto_test provides comprehensive tests for role-based encryption.
 | |
| //
 | |
| // This test suite validates the enterprise-grade security features including:
 | |
| // - Multi-layer encryption and decryption operations
 | |
| // - Role-based access control and permission enforcement
 | |
| // - Key management and rotation procedures
 | |
| // - Audit logging and compliance monitoring
 | |
| // - Performance and security benchmarks
 | |
| // - Edge cases and error handling
 | |
| //
 | |
| // Test Categories:
 | |
| // - Unit tests for individual components
 | |
| // - Integration tests for end-to-end workflows
 | |
| // - Security tests for vulnerability assessment
 | |
| // - Performance tests for scalability validation
 | |
| // - Compliance tests for regulatory requirements
 | |
| 
 | |
| package crypto
 | |
| 
 | |
| import (
 | |
| 	"context"
 | |
| 	"encoding/json"
 | |
| 	"fmt"
 | |
| 	"strings"
 | |
| 	"testing"
 | |
| 	"time"
 | |
| 
 | |
| 	"github.com/stretchr/testify/assert"
 | |
| 	"github.com/stretchr/testify/require"
 | |
| 	"github.com/stretchr/testify/suite"
 | |
| 
 | |
| 	"chorus/pkg/config"
 | |
| 	"chorus/pkg/ucxl"
 | |
| 	slurpContext "chorus/pkg/slurp/context"
 | |
| )
 | |
| 
 | |
| // RoleCryptoTestSuite provides comprehensive testing for role-based encryption
 | |
| type RoleCryptoTestSuite struct {
 | |
| 	suite.Suite
 | |
| 	config      *config.Config
 | |
| 	ageCrypto   *AgeCrypto
 | |
| 	auditLogger *MockAuditLogger
 | |
| 	roleCrypto  *RoleCrypto
 | |
| 	keyManager  *KeyManager
 | |
| 	keyStore    *MockKeyStore
 | |
| 	accessControl *AccessControlMatrix
 | |
| }
 | |
| 
 | |
| // MockAuditLogger implements AuditLogger for testing
 | |
| type MockAuditLogger struct {
 | |
| 	accessLogs    []*AccessLogEntry
 | |
| 	keyRotations  []*KeyRotationEvent
 | |
| 	securityEvents []*SecurityEvent
 | |
| }
 | |
| 
 | |
| func (m *MockAuditLogger) LogAccess(entry *AccessLogEntry) error {
 | |
| 	m.accessLogs = append(m.accessLogs, entry)
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| func (m *MockAuditLogger) LogKeyRotation(event *KeyRotationEvent) error {
 | |
| 	m.keyRotations = append(m.keyRotations, event)
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| func (m *MockAuditLogger) LogSecurityEvent(event *SecurityEvent) error {
 | |
| 	m.securityEvents = append(m.securityEvents, event)
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| func (m *MockAuditLogger) GetAuditTrail(criteria *AuditCriteria) ([]*AuditEvent, error) {
 | |
| 	events := []*AuditEvent{}
 | |
| 	for _, access := range m.accessLogs {
 | |
| 		events = append(events, &AuditEvent{
 | |
| 			EventID:   fmt.Sprintf("access_%s", access.UserID),
 | |
| 			EventType: "access",
 | |
| 			Timestamp: access.AccessTime,
 | |
| 			UserID:    access.UserID,
 | |
| 			Data: map[string]interface{}{
 | |
| 				"access_type": access.AccessType,
 | |
| 				"success":     access.Success,
 | |
| 			},
 | |
| 		})
 | |
| 	}
 | |
| 	return events, nil
 | |
| }
 | |
| 
 | |
| // MockKeyStore implements KeyStore for testing
 | |
| type MockKeyStore struct {
 | |
| 	keys map[string]*SecureKeyData
 | |
| }
 | |
| 
 | |
| func NewMockKeyStore() *MockKeyStore {
 | |
| 	return &MockKeyStore{
 | |
| 		keys: make(map[string]*SecureKeyData),
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func (m *MockKeyStore) StoreKey(keyID string, keyData *SecureKeyData) error {
 | |
| 	m.keys[keyID] = keyData
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| func (m *MockKeyStore) RetrieveKey(keyID string) (*SecureKeyData, error) {
 | |
| 	if key, exists := m.keys[keyID]; exists {
 | |
| 		return key, nil
 | |
| 	}
 | |
| 	return nil, fmt.Errorf("key not found: %s", keyID)
 | |
| }
 | |
| 
 | |
| func (m *MockKeyStore) DeleteKey(keyID string) error {
 | |
| 	delete(m.keys, keyID)
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| func (m *MockKeyStore) ListKeys(filter *KeyFilter) ([]*KeyMetadata, error) {
 | |
| 	metadata := []*KeyMetadata{}
 | |
| 	for keyID, keyData := range m.keys {
 | |
| 		if filter != nil && filter.RoleID != "" {
 | |
| 			if roleID, ok := keyData.Metadata["role_id"].(string); !ok || roleID != filter.RoleID {
 | |
| 				continue
 | |
| 			}
 | |
| 		}
 | |
| 		
 | |
| 		meta := &KeyMetadata{
 | |
| 			KeyID:         keyID,
 | |
| 			KeyType:       keyData.KeyType,
 | |
| 			CreatedAt:     keyData.CreatedAt,
 | |
| 			Status:        keyData.Status,
 | |
| 			SecurityLevel: AccessMedium, // Default for tests
 | |
| 		}
 | |
| 		
 | |
| 		if roleID, ok := keyData.Metadata["role_id"].(string); ok {
 | |
| 			meta.RoleID = roleID
 | |
| 		}
 | |
| 		
 | |
| 		metadata = append(metadata, meta)
 | |
| 	}
 | |
| 	return metadata, nil
 | |
| }
 | |
| 
 | |
| func (m *MockKeyStore) BackupKeys(criteria *BackupCriteria) (*KeyBackup, error) {
 | |
| 	return &KeyBackup{
 | |
| 		BackupID:    fmt.Sprintf("backup_%d", time.Now().Unix()),
 | |
| 		CreatedAt:   time.Now(),
 | |
| 		KeyCount:    len(m.keys),
 | |
| 		Checksum:    "mock_checksum",
 | |
| 	}, nil
 | |
| }
 | |
| 
 | |
| func (m *MockKeyStore) RestoreKeys(backup *KeyBackup) error {
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| // MockPolicyEngine implements PolicyEngine for testing
 | |
| type MockPolicyEngine struct {
 | |
| 	policies map[string]*AccessPolicy
 | |
| }
 | |
| 
 | |
| func NewMockPolicyEngine() *MockPolicyEngine {
 | |
| 	return &MockPolicyEngine{
 | |
| 		policies: make(map[string]*AccessPolicy),
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func (m *MockPolicyEngine) EvaluatePolicy(ctx context.Context, request *AccessRequest) (*PolicyDecision, error) {
 | |
| 	// Simple mock evaluation - permit by default for testing
 | |
| 	decision := &PolicyDecision{
 | |
| 		RequestID:       request.RequestID,
 | |
| 		Decision:        DecisionPermit,
 | |
| 		Reason:          "Mock policy evaluation - permit",
 | |
| 		MatchedPolicies: []string{"mock_policy"},
 | |
| 		AppliedRules:    []string{"mock_rule"},
 | |
| 		ConfidenceScore: 0.95,
 | |
| 		RiskScore:       0.2,
 | |
| 		EvaluationTime:  10 * time.Millisecond,
 | |
| 		EvaluatedAt:     time.Now(),
 | |
| 	}
 | |
| 
 | |
| 	// Deny access for test cases that need denial
 | |
| 	if strings.Contains(request.UserID, "unauthorized") {
 | |
| 		decision.Decision = DecisionDeny
 | |
| 		decision.Reason = "Unauthorized user"
 | |
| 		decision.RiskScore = 0.9
 | |
| 	}
 | |
| 
 | |
| 	return decision, nil
 | |
| }
 | |
| 
 | |
| func (m *MockPolicyEngine) CompilePolicy(policy *AccessPolicy) (*CompiledPolicy, error) {
 | |
| 	return &CompiledPolicy{
 | |
| 		PolicyID:        policy.PolicyID,
 | |
| 		CompiledAt:      time.Now(),
 | |
| 		CompilerVersion: "mock_v1.0",
 | |
| 	}, nil
 | |
| }
 | |
| 
 | |
| func (m *MockPolicyEngine) ValidatePolicy(policy *AccessPolicy) (*PolicyValidationResult, error) {
 | |
| 	return &PolicyValidationResult{
 | |
| 		Valid:          true,
 | |
| 		Errors:         []string{},
 | |
| 		Warnings:       []string{},
 | |
| 		ValidatedAt:    time.Now(),
 | |
| 		ValidationTime: 5 * time.Millisecond,
 | |
| 	}, nil
 | |
| }
 | |
| 
 | |
| func (m *MockPolicyEngine) LoadPolicies(policies []*AccessPolicy) error {
 | |
| 	for _, policy := range policies {
 | |
| 		m.policies[policy.PolicyID] = policy
 | |
| 	}
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| func (m *MockPolicyEngine) ReloadPolicies() error {
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| // MockAttributeProvider implements AttributeProvider for testing
 | |
| type MockAttributeProvider struct{}
 | |
| 
 | |
| func (m *MockAttributeProvider) GetUserAttributes(userID string) (*UserAttributes, error) {
 | |
| 	return &UserAttributes{
 | |
| 		UserID:         userID,
 | |
| 		Department:     "Engineering",
 | |
| 		Title:          "Software Engineer",
 | |
| 		ClearanceLevel: "medium",
 | |
| 		EmploymentType: "full_time",
 | |
| 		StartDate:      time.Now().AddDate(-1, 0, 0),
 | |
| 		Location:       "headquarters",
 | |
| 	}, nil
 | |
| }
 | |
| 
 | |
| func (m *MockAttributeProvider) GetResourceAttributes(resource string) (*ResourceAttributes, error) {
 | |
| 	return &ResourceAttributes{
 | |
| 		ResourceID:     resource,
 | |
| 		Classification: "internal",
 | |
| 		Sensitivity:    "medium",
 | |
| 		DataType:       "context",
 | |
| 		CreatedAt:      time.Now().AddDate(0, -1, 0),
 | |
| 		UpdatedAt:      time.Now(),
 | |
| 	}, nil
 | |
| }
 | |
| 
 | |
| func (m *MockAttributeProvider) GetEnvironmentAttributes() (*EnvironmentAttributes, error) {
 | |
| 	return &EnvironmentAttributes{
 | |
| 		CurrentTime:     time.Now(),
 | |
| 		BusinessHours:   true,
 | |
| 		NetworkZone:     "internal",
 | |
| 		DeviceType:      "workstation",
 | |
| 		DeviceTrust:     "trusted",
 | |
| 		ConnectionType:  "wired",
 | |
| 		ThreatLevel:     "low",
 | |
| 		ComplianceMode:  "standard",
 | |
| 		MaintenanceMode: false,
 | |
| 	}, nil
 | |
| }
 | |
| 
 | |
| func (m *MockAttributeProvider) GetContextAttributes(ctx context.Context) (*ContextAttributes, error) {
 | |
| 	return &ContextAttributes{
 | |
| 		RequestType:        "api",
 | |
| 		ApplicationContext: "slurp_system",
 | |
| 		BusinessContext:    "development",
 | |
| 		TechnicalContext:   "microservice",
 | |
| 		ComplianceContext:  "internal",
 | |
| 		RiskContext:        "low",
 | |
| 	}, nil
 | |
| }
 | |
| 
 | |
| // SetupSuite initializes the test suite
 | |
| func (suite *RoleCryptoTestSuite) SetupSuite() {
 | |
| 	// Create test configuration
 | |
| 	suite.config = &config.Config{
 | |
| 		Agent: config.Agent{
 | |
| 			ID:   "test_agent",
 | |
| 			Role: "backend_developer",
 | |
| 		},
 | |
| 	}
 | |
| 
 | |
| 	// Initialize components
 | |
| 	suite.auditLogger = &MockAuditLogger{
 | |
| 		accessLogs:     []*AccessLogEntry{},
 | |
| 		keyRotations:   []*KeyRotationEvent{},
 | |
| 		securityEvents: []*SecurityEvent{},
 | |
| 	}
 | |
| 
 | |
| 	suite.ageCrypto = NewAgeCrypto(suite.config)
 | |
| 
 | |
| 	suite.keyStore = NewMockKeyStore()
 | |
| 	
 | |
| 	var err error
 | |
| 	suite.keyManager, err = NewKeyManager(suite.config, suite.keyStore, suite.auditLogger)
 | |
| 	suite.Require().NoError(err)
 | |
| 
 | |
| 	adminKeyManager := NewAdminKeyManager(suite.config, "test_node")
 | |
| 	
 | |
| 	suite.roleCrypto, err = NewRoleCrypto(suite.config, suite.ageCrypto, adminKeyManager, suite.auditLogger)
 | |
| 	suite.Require().NoError(err)
 | |
| 
 | |
| 	// Initialize access control
 | |
| 	policyEngine := NewMockPolicyEngine()
 | |
| 	attributeProvider := &MockAttributeProvider{}
 | |
| 	
 | |
| 	suite.accessControl, err = NewAccessControlMatrix(suite.config, policyEngine, attributeProvider, suite.auditLogger)
 | |
| 	suite.Require().NoError(err)
 | |
| }
 | |
| 
 | |
| // TestBasicEncryptionDecryption tests basic encryption and decryption functionality
 | |
| func (suite *RoleCryptoTestSuite) TestBasicEncryptionDecryption() {
 | |
| 	// Create test context
 | |
| 	address, err := ucxl.Parse("context://test/basic/encryption")
 | |
| 	suite.Require().NoError(err)
 | |
| 
 | |
| 	testContext := &slurpContext.ContextNode{
 | |
| 		Path:        "/test/basic/encryption",
 | |
| 		UCXLAddress: address,
 | |
| 		Summary:     "Test context for basic encryption",
 | |
| 		Purpose:     "Testing encryption functionality",
 | |
| 		Technologies: []string{"go", "crypto"},
 | |
| 		Tags:        []string{"test", "encryption"},
 | |
| 		Insights:    []string{"This is a test insight"},
 | |
| 		GeneratedAt: time.Now(),
 | |
| 		RAGConfidence: 0.95,
 | |
| 		EncryptedFor: []string{"backend_developer"},
 | |
| 		AccessLevel:  slurpContext.AccessMedium,
 | |
| 	}
 | |
| 
 | |
| 	// Test encryption
 | |
| 	targetRoles := []string{"backend_developer", "senior_architect"}
 | |
| 	compartmentTags := []string{"development", "testing"}
 | |
| 
 | |
| 	encryptedData, err := suite.roleCrypto.EncryptContextForRoles(testContext, targetRoles, compartmentTags)
 | |
| 	suite.Require().NoError(err)
 | |
| 	suite.NotNil(encryptedData)
 | |
| 	suite.Equal(address, encryptedData.UCXLAddress)
 | |
| 	suite.NotEmpty(encryptedData.EncryptedLayers)
 | |
| 	suite.NotNil(encryptedData.AccessControlMeta)
 | |
| 
 | |
| 	// Test decryption
 | |
| 	decryptedContext, err := suite.roleCrypto.DecryptContextForRole(encryptedData, "backend_developer")
 | |
| 	suite.Require().NoError(err)
 | |
| 	suite.NotNil(decryptedContext)
 | |
| 	suite.Equal(testContext.Summary, decryptedContext.Summary)
 | |
| 	suite.Equal(testContext.Purpose, decryptedContext.Purpose)
 | |
| 
 | |
| 	// Verify audit logging
 | |
| 	suite.True(len(suite.auditLogger.accessLogs) > 0)
 | |
| }
 | |
| 
 | |
| // TestRoleBasedAccess tests role-based access control
 | |
| func (suite *RoleCryptoTestSuite) TestRoleBasedAccess() {
 | |
| 	address, err := ucxl.Parse("context://test/rbac/access")
 | |
| 	suite.Require().NoError(err)
 | |
| 
 | |
| 	testContext := &slurpContext.ContextNode{
 | |
| 		Path:        "/test/rbac/access",
 | |
| 		UCXLAddress: address,
 | |
| 		Summary:     "Test context for RBAC",
 | |
| 		Purpose:     "Testing role-based access control",
 | |
| 		Technologies: []string{"security", "rbac"},
 | |
| 		Tags:        []string{"test", "security"},
 | |
| 		EncryptedFor: []string{"senior_architect"},
 | |
| 		AccessLevel:  slurpContext.AccessHigh,
 | |
| 		GeneratedAt: time.Now(),
 | |
| 		RAGConfidence: 0.9,
 | |
| 	}
 | |
| 
 | |
| 	// Encrypt for high-privilege role only
 | |
| 	encryptedData, err := suite.roleCrypto.EncryptContextForRoles(testContext, []string{"senior_architect"}, []string{"security"})
 | |
| 	suite.Require().NoError(err)
 | |
| 
 | |
| 	// Test access with authorized role
 | |
| 	decryptedContext, err := suite.roleCrypto.DecryptContextForRole(encryptedData, "senior_architect")
 | |
| 	suite.Require().NoError(err)
 | |
| 	suite.NotNil(decryptedContext)
 | |
| 
 | |
| 	// Test access with unauthorized role (should fail)
 | |
| 	_, err = suite.roleCrypto.DecryptContextForRole(encryptedData, "intern")
 | |
| 	suite.Error(err)
 | |
| 	suite.Contains(err.Error(), "access denied")
 | |
| }
 | |
| 
 | |
| // TestMultiLayerEncryption tests multi-layer encryption with different access levels
 | |
| func (suite *RoleCryptoTestSuite) TestMultiLayerEncryption() {
 | |
| 	address, err := ucxl.Parse("context://test/multilayer/encryption")
 | |
| 	suite.Require().NoError(err)
 | |
| 
 | |
| 	testContext := &slurpContext.ContextNode{
 | |
| 		Path:        "/test/multilayer/encryption",
 | |
| 		UCXLAddress: address,
 | |
| 		Summary:     "Test context for multi-layer encryption",
 | |
| 		Purpose:     "Testing layered encryption",
 | |
| 		Technologies: []string{"encryption", "security"},
 | |
| 		Tags:        []string{"test", "multilayer"},
 | |
| 		Insights:    []string{"Multi-layer security insight", "Advanced encryption insight"},
 | |
| 		EncryptedFor: []string{"backend_developer", "senior_architect", "devops_engineer"},
 | |
| 		AccessLevel:  slurpContext.AccessMedium,
 | |
| 		GeneratedAt: time.Now(),
 | |
| 		RAGConfidence: 0.85,
 | |
| 	}
 | |
| 
 | |
| 	// Encrypt for multiple roles with different access levels
 | |
| 	targetRoles := []string{"backend_developer", "senior_architect", "devops_engineer"}
 | |
| 	encryptedData, err := suite.roleCrypto.EncryptContextForRoles(testContext, targetRoles, []string{"development"})
 | |
| 	suite.Require().NoError(err)
 | |
| 
 | |
| 	// Verify multiple encryption layers
 | |
| 	suite.True(len(encryptedData.EncryptedLayers) > 0)
 | |
| 	suite.NotEmpty(encryptedData.KeyFingerprints)
 | |
| 
 | |
| 	// Test decryption with different roles
 | |
| 	for _, role := range targetRoles {
 | |
| 		decryptedContext, err := suite.roleCrypto.DecryptContextForRole(encryptedData, role)
 | |
| 		suite.Require().NoError(err, "Failed to decrypt for role: %s", role)
 | |
| 		suite.NotNil(decryptedContext)
 | |
| 		suite.Equal(testContext.Summary, decryptedContext.Summary)
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // TestRoleBasedFiltering tests role-specific context filtering
 | |
| func (suite *RoleCryptoTestSuite) TestRoleBasedFiltering() {
 | |
| 	address, err := ucxl.Parse("context://test/filtering/context")
 | |
| 	suite.Require().NoError(err)
 | |
| 
 | |
| 	testContext := &slurpContext.ContextNode{
 | |
| 		Path:        "/test/filtering/context",
 | |
| 		UCXLAddress: address,
 | |
| 		Summary:     "Test context for filtering",
 | |
| 		Purpose:     "Testing role-based filtering",
 | |
| 		Technologies: []string{"frontend", "backend", "database"},
 | |
| 		Tags:        []string{"test", "filtering"},
 | |
| 		Insights:    []string{"Frontend insight", "Backend insight", "Database insight"},
 | |
| 		EncryptedFor: []string{"frontend_developer", "backend_developer"},
 | |
| 		AccessLevel:  slurpContext.AccessMedium,
 | |
| 		GeneratedAt: time.Now(),
 | |
| 		RAGConfidence: 0.8,
 | |
| 	}
 | |
| 
 | |
| 	encryptedData, err := suite.roleCrypto.EncryptContextForRoles(testContext, []string{"frontend_developer", "backend_developer"}, []string{"development"})
 | |
| 	suite.Require().NoError(err)
 | |
| 
 | |
| 	// Test frontend developer access (should get frontend-specific insights)
 | |
| 	frontendContext, err := suite.roleCrypto.DecryptContextForRole(encryptedData, "frontend_developer")
 | |
| 	suite.Require().NoError(err)
 | |
| 	suite.Contains(strings.Join(frontendContext.Insights, " "), "Frontend")
 | |
| 
 | |
| 	// Test backend developer access (should get backend-specific insights)
 | |
| 	backendContext, err := suite.roleCrypto.DecryptContextForRole(encryptedData, "backend_developer")
 | |
| 	suite.Require().NoError(err)
 | |
| 	suite.Contains(strings.Join(backendContext.Insights, " "), "Backend")
 | |
| }
 | |
| 
 | |
| // TestKeyManagement tests key generation and management
 | |
| func (suite *RoleCryptoTestSuite) TestKeyManagement() {
 | |
| 	// Test key generation
 | |
| 	keyPair, err := suite.keyManager.GenerateRoleKey("test_role", "age-x25519")
 | |
| 	suite.Require().NoError(err)
 | |
| 	suite.NotNil(keyPair)
 | |
| 	suite.NotEmpty(keyPair.PublicKey)
 | |
| 	suite.NotEmpty(keyPair.PrivateKey)
 | |
| 	suite.True(strings.HasPrefix(keyPair.PublicKey, "age1"))
 | |
| 
 | |
| 	// Test key retrieval
 | |
| 	metadata, err := suite.keyManager.GetKeyMetadata(&KeyFilter{RoleID: "test_role"})
 | |
| 	suite.Require().NoError(err)
 | |
| 	suite.True(len(metadata) > 0)
 | |
| 
 | |
| 	// Test key integrity verification
 | |
| 	keyID := fmt.Sprintf("test_role_age-x25519_v%d", keyPair.Version)
 | |
| 	verificationResult, err := suite.keyManager.VerifyKeyIntegrity(keyID)
 | |
| 	suite.Require().NoError(err)
 | |
| 	suite.NotNil(verificationResult)
 | |
| 	suite.True(verificationResult.IntegrityOK)
 | |
| }
 | |
| 
 | |
| // TestKeyRotation tests automatic key rotation
 | |
| func (suite *RoleCryptoTestSuite) TestKeyRotation() {
 | |
| 	// Create initial key
 | |
| 	originalKeyPair, err := suite.keyManager.GenerateRoleKey("rotation_test_role", "age-x25519")
 | |
| 	suite.Require().NoError(err)
 | |
| 
 | |
| 	// Perform key rotation
 | |
| 	rotationResult, err := suite.keyManager.RotateKey("rotation_test_role", "test_rotation")
 | |
| 	suite.Require().NoError(err)
 | |
| 	suite.NotNil(rotationResult)
 | |
| 	suite.Contains(rotationResult.RotatedRoles, "rotation_test_role")
 | |
| 	suite.NotEmpty(rotationResult.NewKeys)
 | |
| 	suite.NotEmpty(rotationResult.RevokedKeys)
 | |
| 
 | |
| 	// Verify new key is different from original
 | |
| 	newKey := rotationResult.NewKeys["rotation_test_role"]
 | |
| 	suite.NotEqual(originalKeyPair.PublicKey, string(newKey.KeyData))
 | |
| 
 | |
| 	// Verify audit logging
 | |
| 	suite.True(len(suite.auditLogger.keyRotations) > 0)
 | |
| 	rotation := suite.auditLogger.keyRotations[len(suite.auditLogger.keyRotations)-1]
 | |
| 	suite.Equal("test_rotation", rotation.Reason)
 | |
| 	suite.True(rotation.Success)
 | |
| }
 | |
| 
 | |
| // TestAccessControlMatrix tests the access control matrix functionality
 | |
| func (suite *RoleCryptoTestSuite) TestAccessControlMatrix() {
 | |
| 	ctx := context.Background()
 | |
| 	
 | |
| 	// Create access request
 | |
| 	request := &AccessRequest{
 | |
| 		RequestID:    "test_request_001",
 | |
| 		Timestamp:    time.Now(),
 | |
| 		UserID:       "test_user",
 | |
| 		Roles:        []string{"backend_developer"},
 | |
| 		Resource:     "context://test/access/resource",
 | |
| 		ResourceType: "context",
 | |
| 		Action:       "read",
 | |
| 		ActionType:   "data_access",
 | |
| 		SessionID:    "test_session_001",
 | |
| 		IPAddress:    "192.168.1.100",
 | |
| 		UserAgent:    "TestAgent/1.0",
 | |
| 		Priority:     1,
 | |
| 		Justification: "Testing access control",
 | |
| 		Metadata:     make(map[string]interface{}),
 | |
| 	}
 | |
| 
 | |
| 	// Test access evaluation
 | |
| 	decision, err := suite.accessControl.CheckAccess(ctx, request)
 | |
| 	suite.Require().NoError(err)
 | |
| 	suite.NotNil(decision)
 | |
| 	suite.Equal(DecisionPermit, decision.Decision)
 | |
| 	suite.True(decision.ConfidenceScore > 0)
 | |
| 	suite.True(decision.EvaluationTime > 0)
 | |
| 
 | |
| 	// Test unauthorized access
 | |
| 	unauthorizedRequest := &AccessRequest{
 | |
| 		RequestID:    "test_request_002",
 | |
| 		Timestamp:    time.Now(),
 | |
| 		UserID:       "unauthorized_user",
 | |
| 		Roles:        []string{"intern"},
 | |
| 		Resource:     "context://test/sensitive/resource",
 | |
| 		ResourceType: "context",
 | |
| 		Action:       "write",
 | |
| 		ActionType:   "data_modification",
 | |
| 		SessionID:    "test_session_002",
 | |
| 		IPAddress:    "192.168.1.200",
 | |
| 		UserAgent:    "TestAgent/1.0",
 | |
| 		Priority:     1,
 | |
| 		Justification: "Testing unauthorized access",
 | |
| 		Metadata:     make(map[string]interface{}),
 | |
| 	}
 | |
| 
 | |
| 	unauthorizedDecision, err := suite.accessControl.CheckAccess(ctx, unauthorizedRequest)
 | |
| 	suite.Require().NoError(err)
 | |
| 	suite.Equal(DecisionDeny, unauthorizedDecision.Decision)
 | |
| }
 | |
| 
 | |
| // TestBypassTokens tests bypass token functionality
 | |
| func (suite *RoleCryptoTestSuite) TestBypassTokens() {
 | |
| 	// Create bypass token
 | |
| 	token, err := suite.accessControl.CreateBypassToken(
 | |
| 		"admin_user",
 | |
| 		"Emergency maintenance",
 | |
| 		[]string{"context://emergency/*"},
 | |
| 		1*time.Hour,
 | |
| 		5,
 | |
| 	)
 | |
| 	suite.Require().NoError(err)
 | |
| 	suite.NotNil(token)
 | |
| 	suite.Equal(BypassTokenStatusActive, token.Status)
 | |
| 	suite.Equal(0, token.UsageCount)
 | |
| 
 | |
| 	// Test access with bypass token
 | |
| 	ctx := context.Background()
 | |
| 	request := &AccessRequest{
 | |
| 		RequestID:    "bypass_test_001",
 | |
| 		Timestamp:    time.Now(),
 | |
| 		UserID:       "regular_user",
 | |
| 		Roles:        []string{"intern"},
 | |
| 		Resource:     "context://emergency/system",
 | |
| 		Action:       "read",
 | |
| 		Metadata:     map[string]interface{}{"bypass_token": token.TokenID},
 | |
| 	}
 | |
| 
 | |
| 	decision, err := suite.accessControl.CheckAccess(ctx, request)
 | |
| 	suite.Require().NoError(err)
 | |
| 	suite.Equal(DecisionPermit, decision.Decision)
 | |
| 	suite.Contains(decision.Reason, "Bypass token")
 | |
| 
 | |
| 	// Verify token usage was recorded
 | |
| 	suite.Equal(1, token.UsageCount)
 | |
| }
 | |
| 
 | |
| // TestSecurityMetrics tests security metrics collection
 | |
| func (suite *RoleCryptoTestSuite) TestSecurityMetrics() {
 | |
| 	// Get role crypto metrics
 | |
| 	metrics := suite.roleCrypto.GetSecurityMetrics()
 | |
| 	suite.NotNil(metrics)
 | |
| 	suite.Contains(metrics, "total_roles")
 | |
| 	suite.Contains(metrics, "security_score")
 | |
| 
 | |
| 	// Get access control metrics
 | |
| 	acMetrics := suite.accessControl.GetAccessControlMetrics()
 | |
| 	suite.NotNil(acMetrics)
 | |
| 	suite.Contains(acMetrics, "total_evaluations")
 | |
| 	suite.Contains(acMetrics, "enforcement_mode")
 | |
| 
 | |
| 	// Get key management security status
 | |
| 	keyStatus := suite.keyManager.GetSecurityStatus()
 | |
| 	suite.NotNil(keyStatus)
 | |
| 	suite.Contains([]string{"healthy", "warning", "degraded", "critical"}, keyStatus.OverallHealth)
 | |
| 	suite.True(keyStatus.SecurityScore >= 0 && keyStatus.SecurityScore <= 1)
 | |
| }
 | |
| 
 | |
| // TestComplianceFeatures tests compliance and audit features
 | |
| func (suite *RoleCryptoTestSuite) TestComplianceFeatures() {
 | |
| 	// Test audit trail retrieval
 | |
| 	criteria := &AuditCriteria{
 | |
| 		StartTime: &[]time.Time{time.Now().Add(-1 * time.Hour)}[0],
 | |
| 		EndTime:   &[]time.Time{time.Now()}[0],
 | |
| 		UserID:    "test_user",
 | |
| 		Limit:     100,
 | |
| 	}
 | |
| 
 | |
| 	auditTrail, err := suite.auditLogger.GetAuditTrail(criteria)
 | |
| 	suite.Require().NoError(err)
 | |
| 	suite.NotNil(auditTrail)
 | |
| 
 | |
| 	// Verify audit events have required fields
 | |
| 	if len(auditTrail) > 0 {
 | |
| 		event := auditTrail[0]
 | |
| 		suite.NotEmpty(event.EventID)
 | |
| 		suite.NotEmpty(event.EventType)
 | |
| 		suite.NotEmpty(event.UserID)
 | |
| 		suite.NotZero(event.Timestamp)
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // TestErrorHandling tests error handling and edge cases
 | |
| func (suite *RoleCryptoTestSuite) TestErrorHandling() {
 | |
| 	// Test encryption with invalid context
 | |
| 	invalidContext := &slurpContext.ContextNode{
 | |
| 		Path:        "", // Invalid: empty path
 | |
| 		Summary:     "", // Invalid: empty summary
 | |
| 		GeneratedAt: time.Now(),
 | |
| 	}
 | |
| 
 | |
| 	_, err := suite.roleCrypto.EncryptContextForRoles(invalidContext, []string{"backend_developer"}, []string{})
 | |
| 	suite.Error(err)
 | |
| 	suite.Contains(err.Error(), "invalid context")
 | |
| 
 | |
| 	// Test decryption with invalid role
 | |
| 	address, _ := ucxl.Parse("context://test/valid/context")
 | |
| 	validContext := &slurpContext.ContextNode{
 | |
| 		Path:        "/test/valid/context",
 | |
| 		UCXLAddress: address,
 | |
| 		Summary:     "Valid test context",
 | |
| 		Purpose:     "Testing error handling",
 | |
| 		GeneratedAt: time.Now(),
 | |
| 		RAGConfidence: 0.8,
 | |
| 		EncryptedFor: []string{"backend_developer"},
 | |
| 		AccessLevel:  slurpContext.AccessMedium,
 | |
| 	}
 | |
| 
 | |
| 	encryptedData, err := suite.roleCrypto.EncryptContextForRoles(validContext, []string{"backend_developer"}, []string{})
 | |
| 	suite.Require().NoError(err)
 | |
| 
 | |
| 	_, err = suite.roleCrypto.DecryptContextForRole(encryptedData, "non_existent_role")
 | |
| 	suite.Error(err)
 | |
| 	suite.Contains(err.Error(), "access denied")
 | |
| 
 | |
| 	// Test key generation with invalid parameters
 | |
| 	_, err = suite.keyManager.GenerateRoleKey("", "invalid_type")
 | |
| 	suite.Error(err)
 | |
| }
 | |
| 
 | |
| // TestPerformance tests performance characteristics
 | |
| func (suite *RoleCryptoTestSuite) TestPerformance() {
 | |
| 	// Create test context
 | |
| 	address, _ := ucxl.Parse("context://test/performance/benchmark")
 | |
| 	testContext := &slurpContext.ContextNode{
 | |
| 		Path:        "/test/performance/benchmark",
 | |
| 		UCXLAddress: address,
 | |
| 		Summary:     "Performance test context",
 | |
| 		Purpose:     "Testing encryption performance",
 | |
| 		Technologies: []string{"performance", "crypto"},
 | |
| 		Tags:        []string{"test", "benchmark"},
 | |
| 		Insights:    make([]string, 100), // Large insights array
 | |
| 		GeneratedAt: time.Now(),
 | |
| 		RAGConfidence: 0.9,
 | |
| 		EncryptedFor: []string{"backend_developer"},
 | |
| 		AccessLevel:  slurpContext.AccessMedium,
 | |
| 	}
 | |
| 
 | |
| 	// Fill insights with test data
 | |
| 	for i := 0; i < 100; i++ {
 | |
| 		testContext.Insights[i] = fmt.Sprintf("Performance test insight #%d", i+1)
 | |
| 	}
 | |
| 
 | |
| 	// Benchmark encryption
 | |
| 	start := time.Now()
 | |
| 	encryptedData, err := suite.roleCrypto.EncryptContextForRoles(testContext, []string{"backend_developer"}, []string{"performance"})
 | |
| 	encryptionTime := time.Since(start)
 | |
| 	
 | |
| 	suite.Require().NoError(err)
 | |
| 	suite.True(encryptionTime < 100*time.Millisecond, "Encryption took too long: %v", encryptionTime)
 | |
| 
 | |
| 	// Benchmark decryption
 | |
| 	start = time.Now()
 | |
| 	_, err = suite.roleCrypto.DecryptContextForRole(encryptedData, "backend_developer")
 | |
| 	decryptionTime := time.Since(start)
 | |
| 	
 | |
| 	suite.Require().NoError(err)
 | |
| 	suite.True(decryptionTime < 50*time.Millisecond, "Decryption took too long: %v", decryptionTime)
 | |
| 
 | |
| 	// Test concurrent operations
 | |
| 	concurrentOps := 10
 | |
| 	results := make(chan error, concurrentOps)
 | |
| 
 | |
| 	for i := 0; i < concurrentOps; i++ {
 | |
| 		go func(index int) {
 | |
| 			address, _ := ucxl.Parse(fmt.Sprintf("context://test/concurrent/%d", index))
 | |
| 			ctx := &slurpContext.ContextNode{
 | |
| 				Path:        fmt.Sprintf("/test/concurrent/%d", index),
 | |
| 				UCXLAddress: address,
 | |
| 				Summary:     fmt.Sprintf("Concurrent test context %d", index),
 | |
| 				Purpose:     "Testing concurrent operations",
 | |
| 				GeneratedAt: time.Now(),
 | |
| 				RAGConfidence: 0.8,
 | |
| 				EncryptedFor: []string{"backend_developer"},
 | |
| 				AccessLevel:  slurpContext.AccessMedium,
 | |
| 			}
 | |
| 
 | |
| 			encrypted, err := suite.roleCrypto.EncryptContextForRoles(ctx, []string{"backend_developer"}, []string{"concurrent"})
 | |
| 			if err != nil {
 | |
| 				results <- err
 | |
| 				return
 | |
| 			}
 | |
| 
 | |
| 			_, err = suite.roleCrypto.DecryptContextForRole(encrypted, "backend_developer")
 | |
| 			results <- err
 | |
| 		}(i)
 | |
| 	}
 | |
| 
 | |
| 	// Wait for all operations to complete
 | |
| 	for i := 0; i < concurrentOps; i++ {
 | |
| 		err := <-results
 | |
| 		suite.NoError(err, "Concurrent operation %d failed", i)
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // TestSecurityVulnerabilities tests for common security vulnerabilities
 | |
| func (suite *RoleCryptoTestSuite) TestSecurityVulnerabilities() {
 | |
| 	// Test for timing attacks (encryption should take consistent time)
 | |
| 	address, _ := ucxl.Parse("context://test/security/timing")
 | |
| 	baseContext := &slurpContext.ContextNode{
 | |
| 		Path:        "/test/security/timing",
 | |
| 		UCXLAddress: address,
 | |
| 		Summary:     "Timing attack test",
 | |
| 		Purpose:     "Testing for timing vulnerabilities",
 | |
| 		GeneratedAt: time.Now(),
 | |
| 		RAGConfidence: 0.8,
 | |
| 		EncryptedFor: []string{"backend_developer"},
 | |
| 		AccessLevel:  slurpContext.AccessMedium,
 | |
| 	}
 | |
| 
 | |
| 	// Measure encryption times for different content sizes
 | |
| 	var times []time.Duration
 | |
| 	for i := 0; i < 10; i++ {
 | |
| 		testContext := *baseContext
 | |
| 		testContext.Insights = make([]string, i*10) // Varying content size
 | |
| 		for j := range testContext.Insights {
 | |
| 			testContext.Insights[j] = fmt.Sprintf("Insight %d", j)
 | |
| 		}
 | |
| 
 | |
| 		start := time.Now()
 | |
| 		_, err := suite.roleCrypto.EncryptContextForRoles(&testContext, []string{"backend_developer"}, []string{"timing"})
 | |
| 		duration := time.Since(start)
 | |
| 		
 | |
| 		suite.Require().NoError(err)
 | |
| 		times = append(times, duration)
 | |
| 	}
 | |
| 
 | |
| 	// Check that times don't vary too much (basic timing attack protection)
 | |
| 	maxTime := times[0]
 | |
| 	minTime := times[0]
 | |
| 	for _, t := range times {
 | |
| 		if t > maxTime {
 | |
| 			maxTime = t
 | |
| 		}
 | |
| 		if t < minTime {
 | |
| 			minTime = t
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	// Times should not vary by more than 100% (basic check)
 | |
| 	variance := float64(maxTime-minTime) / float64(minTime)
 | |
| 	suite.True(variance < 2.0, "Encryption times vary too much: %v", variance)
 | |
| 
 | |
| 	// Test for privilege escalation (lower privilege role shouldn't access higher privilege content)
 | |
| 	highPrivContext := &slurpContext.ContextNode{
 | |
| 		Path:        "/test/security/privilege",
 | |
| 		UCXLAddress: address,
 | |
| 		Summary:     "High privilege content",
 | |
| 		Purpose:     "Testing privilege escalation protection",
 | |
| 		GeneratedAt: time.Now(),
 | |
| 		RAGConfidence: 0.9,
 | |
| 		EncryptedFor: []string{"senior_architect"},
 | |
| 		AccessLevel:  slurpContext.AccessCritical,
 | |
| 	}
 | |
| 
 | |
| 	encryptedHighPriv, err := suite.roleCrypto.EncryptContextForRoles(highPrivContext, []string{"senior_architect"}, []string{"security"})
 | |
| 	suite.Require().NoError(err)
 | |
| 
 | |
| 	// Attempt access with lower privilege role
 | |
| 	_, err = suite.roleCrypto.DecryptContextForRole(encryptedHighPriv, "intern")
 | |
| 	suite.Error(err, "Lower privilege role should not access higher privilege content")
 | |
| 
 | |
| 	// Test for information leakage in error messages
 | |
| 	suite.NotContains(err.Error(), "senior_architect", "Error message should not leak role information")
 | |
| 	suite.NotContains(err.Error(), highPrivContext.Summary, "Error message should not leak content information")
 | |
| }
 | |
| 
 | |
| // BenchmarkEncryption benchmarks encryption operations
 | |
| func BenchmarkEncryption(b *testing.B) {
 | |
| 	// Setup
 | |
| 	config := &config.Config{
 | |
| 		Agent: config.Agent{ID: "bench_agent", Role: "backend_developer"},
 | |
| 	}
 | |
| 	auditLogger := &MockAuditLogger{}
 | |
| 	ageCrypto := NewAgeCrypto(config)
 | |
| 	adminKeyManager := NewAdminKeyManager(config, "bench_node")
 | |
| 	
 | |
| 	roleCrypto, err := NewRoleCrypto(config, ageCrypto, adminKeyManager, auditLogger)
 | |
| 	require.NoError(b, err)
 | |
| 
 | |
| 	address, _ := ucxl.Parse("context://benchmark/encryption")
 | |
| 	testContext := &slurpContext.ContextNode{
 | |
| 		Path:        "/benchmark/encryption",
 | |
| 		UCXLAddress: address,
 | |
| 		Summary:     "Benchmark context for encryption testing",
 | |
| 		Purpose:     "Performance testing of encryption operations",
 | |
| 		Technologies: []string{"crypto", "benchmark"},
 | |
| 		Tags:        []string{"benchmark", "performance"},
 | |
| 		Insights:    []string{"Benchmark insight 1", "Benchmark insight 2"},
 | |
| 		GeneratedAt: time.Now(),
 | |
| 		RAGConfidence: 0.85,
 | |
| 		EncryptedFor: []string{"backend_developer"},
 | |
| 		AccessLevel:  slurpContext.AccessMedium,
 | |
| 	}
 | |
| 
 | |
| 	b.ResetTimer()
 | |
| 	b.RunParallel(func(pb *testing.PB) {
 | |
| 		for pb.Next() {
 | |
| 			_, err := roleCrypto.EncryptContextForRoles(testContext, []string{"backend_developer"}, []string{"benchmark"})
 | |
| 			if err != nil {
 | |
| 				b.Fatalf("Encryption failed: %v", err)
 | |
| 			}
 | |
| 		}
 | |
| 	})
 | |
| }
 | |
| 
 | |
| // BenchmarkDecryption benchmarks decryption operations
 | |
| func BenchmarkDecryption(b *testing.B) {
 | |
| 	// Setup
 | |
| 	config := &config.Config{
 | |
| 		Agent: config.Agent{ID: "bench_agent", Role: "backend_developer"},
 | |
| 	}
 | |
| 	auditLogger := &MockAuditLogger{}
 | |
| 	ageCrypto := NewAgeCrypto(config)
 | |
| 	adminKeyManager := NewAdminKeyManager(config, "bench_node")
 | |
| 	
 | |
| 	roleCrypto, err := NewRoleCrypto(config, ageCrypto, adminKeyManager, auditLogger)
 | |
| 	require.NoError(b, err)
 | |
| 
 | |
| 	address, _ := ucxl.Parse("context://benchmark/decryption")
 | |
| 	testContext := &slurpContext.ContextNode{
 | |
| 		Path:        "/benchmark/decryption",
 | |
| 		UCXLAddress: address,
 | |
| 		Summary:     "Benchmark context for decryption testing",
 | |
| 		Purpose:     "Performance testing of decryption operations",
 | |
| 		Technologies: []string{"crypto", "benchmark"},
 | |
| 		Tags:        []string{"benchmark", "performance"},
 | |
| 		Insights:    []string{"Benchmark insight 1", "Benchmark insight 2"},
 | |
| 		GeneratedAt: time.Now(),
 | |
| 		RAGConfidence: 0.85,
 | |
| 		EncryptedFor: []string{"backend_developer"},
 | |
| 		AccessLevel:  slurpContext.AccessMedium,
 | |
| 	}
 | |
| 
 | |
| 	// Pre-encrypt context for benchmarking decryption
 | |
| 	encryptedData, err := roleCrypto.EncryptContextForRoles(testContext, []string{"backend_developer"}, []string{"benchmark"})
 | |
| 	require.NoError(b, err)
 | |
| 
 | |
| 	b.ResetTimer()
 | |
| 	b.RunParallel(func(pb *testing.PB) {
 | |
| 		for pb.Next() {
 | |
| 			_, err := roleCrypto.DecryptContextForRole(encryptedData, "backend_developer")
 | |
| 			if err != nil {
 | |
| 				b.Fatalf("Decryption failed: %v", err)
 | |
| 			}
 | |
| 		}
 | |
| 	})
 | |
| }
 | |
| 
 | |
| // BenchmarkAccessControl benchmarks access control evaluation
 | |
| func BenchmarkAccessControl(b *testing.B) {
 | |
| 	// Setup
 | |
| 	config := &config.Config{
 | |
| 		Agent: config.Agent{ID: "bench_agent", Role: "backend_developer"},
 | |
| 	}
 | |
| 	auditLogger := &MockAuditLogger{}
 | |
| 	policyEngine := NewMockPolicyEngine()
 | |
| 	attributeProvider := &MockAttributeProvider{}
 | |
| 	
 | |
| 	accessControl, err := NewAccessControlMatrix(config, policyEngine, attributeProvider, auditLogger)
 | |
| 	require.NoError(b, err)
 | |
| 
 | |
| 	ctx := context.Background()
 | |
| 	request := &AccessRequest{
 | |
| 		RequestID:    "benchmark_request",
 | |
| 		Timestamp:    time.Now(),
 | |
| 		UserID:       "bench_user",
 | |
| 		Roles:        []string{"backend_developer"},
 | |
| 		Resource:     "context://benchmark/access",
 | |
| 		ResourceType: "context",
 | |
| 		Action:       "read",
 | |
| 		ActionType:   "data_access",
 | |
| 		SessionID:    "bench_session",
 | |
| 		IPAddress:    "192.168.1.100",
 | |
| 		UserAgent:    "BenchmarkAgent/1.0",
 | |
| 		Priority:     1,
 | |
| 		Metadata:     make(map[string]interface{}),
 | |
| 	}
 | |
| 
 | |
| 	b.ResetTimer()
 | |
| 	b.RunParallel(func(pb *testing.PB) {
 | |
| 		for pb.Next() {
 | |
| 			_, err := accessControl.CheckAccess(ctx, request)
 | |
| 			if err != nil {
 | |
| 				b.Fatalf("Access control evaluation failed: %v", err)
 | |
| 			}
 | |
| 		}
 | |
| 	})
 | |
| }
 | |
| 
 | |
| // TestMain sets up and tears down the test suite
 | |
| func TestMain(m *testing.M) {
 | |
| 	// Setup any global test resources here
 | |
| 	
 | |
| 	// Run tests
 | |
| 	code := m.Run()
 | |
| 	
 | |
| 	// Cleanup any global test resources here
 | |
| 	
 | |
| 	// Exit with the same code as the test run
 | |
| 	fmt.Printf("Test suite completed with exit code: %d\n", code)
 | |
| }
 | |
| 
 | |
| // Run the test suite
 | |
| func TestRoleCryptoTestSuite(t *testing.T) {
 | |
| 	suite.Run(t, new(RoleCryptoTestSuite))
 | |
| } |