Files
bitchat/bitchatTests/NoiseIdentityPersistenceTests.swift
T
jack b61904bee9 Remove message retention and /save command
- Delete MessageRetentionService.swift
- Remove retentionEnabledChannels from ChatViewModel
- Remove /save command handling
- Remove retention UI elements from ContentView
- Remove channelRetention message type from protocol
- Update documentation and tests
2025-07-16 15:59:38 +02:00

202 lines
7.3 KiB
Swift

//
// NoiseIdentityPersistenceTests.swift
// bitchatTests
//
// Tests for Noise Protocol identity key persistence
//
import XCTest
@testable import bitchat
class NoiseIdentityPersistenceTests: XCTestCase {
override func setUp() {
super.setUp()
// Clean up any existing test data
cleanupTestData()
}
override func tearDown() {
// Clean up after tests
cleanupTestData()
super.tearDown()
}
private func cleanupTestData() {
// Clear any existing identity keys
_ = KeychainManager.shared.deleteIdentityKey(forKey: "noiseStaticKey")
_ = KeychainManager.shared.deleteIdentityKey(forKey: "messageRetentionKey")
// Clear any UserDefaults that might interfere
UserDefaults.standard.removeObject(forKey: "bitchat.noiseIdentityKey")
UserDefaults.standard.removeObject(forKey: "bitchat.messageRetentionKey")
UserDefaults.standard.synchronize()
}
// MARK: - Identity Persistence Tests
func testIdentityPersistsAcrossInstances() {
// Create first instance
let service1 = NoiseEncryptionService()
let fingerprint1 = service1.getIdentityFingerprint()
let publicKey1 = service1.getStaticPublicKeyData()
XCTAssertFalse(fingerprint1.isEmpty, "Fingerprint should not be empty")
XCTAssertEqual(publicKey1.count, 32, "Public key should be 32 bytes")
// Create second instance
let service2 = NoiseEncryptionService()
let fingerprint2 = service2.getIdentityFingerprint()
let publicKey2 = service2.getStaticPublicKeyData()
// Verify same identity
XCTAssertEqual(fingerprint1, fingerprint2, "Fingerprint should persist across instances")
XCTAssertEqual(publicKey1, publicKey2, "Public key should persist across instances")
}
func testIdentityNotStoredInUserDefaults() {
// Create service to generate identity
_ = NoiseEncryptionService()
// Verify identity is NOT in UserDefaults
let userDefaultsData = UserDefaults.standard.data(forKey: "bitchat.noiseIdentityKey")
XCTAssertNil(userDefaultsData, "Identity key should NOT be stored in UserDefaults")
}
func testIdentityStoredInKeychain() {
// Create service to generate identity
_ = NoiseEncryptionService()
// Verify identity IS in Keychain
let keychainData = KeychainManager.shared.getIdentityKey(forKey: "noiseStaticKey")
XCTAssertNotNil(keychainData, "Identity key should be stored in Keychain")
XCTAssertEqual(keychainData?.count, 32, "Identity key should be 32 bytes")
}
func testPanicModeClearsIdentity() {
// Create service and get initial fingerprint
let service1 = NoiseEncryptionService()
let fingerprint1 = service1.getIdentityFingerprint()
// Clear identity (panic mode)
service1.clearPersistentIdentity()
// Create new service and verify new identity
let service2 = NoiseEncryptionService()
let fingerprint2 = service2.getIdentityFingerprint()
XCTAssertNotEqual(fingerprint1, fingerprint2, "New identity should be created after panic mode")
}
func testMultipleRapidInstantiations() {
// Create multiple services rapidly
var fingerprints: [String] = []
for _ in 0..<10 {
let service = NoiseEncryptionService()
fingerprints.append(service.getIdentityFingerprint())
}
// Verify all fingerprints are the same
let firstFingerprint = fingerprints[0]
for fingerprint in fingerprints {
XCTAssertEqual(fingerprint, firstFingerprint, "All instances should have same identity")
}
}
func testKeychainAccessFailureHandling() {
// This test would require mocking KeychainManager, but we can at least
// verify the service initializes even if keychain is problematic
let service = NoiseEncryptionService()
XCTAssertFalse(service.getIdentityFingerprint().isEmpty, "Service should initialize with valid identity")
}
// MARK: - Message Retention Key Tests
// Message retention feature has been removed
// MARK: - Keychain Service Name Tests
func testKeychainServiceName() {
// Verify we're using the correct service name
let expectedService = "chat.bitchat"
// Save a test item
let testKey = "test_service_verification"
let testData = "test".data(using: .utf8)!
_ = KeychainManager.shared.saveIdentityKey(testData, forKey: testKey)
// Query directly to verify service name
let query: [String: Any] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrService as String: expectedService,
kSecAttrAccount as String: "identity_\(testKey)",
kSecMatchLimit as String: kSecMatchLimitOne,
kSecReturnData as String: true
]
var result: AnyObject?
let status = SecItemCopyMatching(query as CFDictionary, &result)
XCTAssertEqual(status, errSecSuccess, "Should find item with expected service name")
XCTAssertNotNil(result as? Data, "Should retrieve data")
// Clean up
_ = KeychainManager.shared.deleteIdentityKey(forKey: testKey)
}
// MARK: - Legacy Cleanup Tests
func testLegacyKeychainCleanup() {
// Create some legacy items with old service names
let legacyServices = [
"com.bitchat.passwords",
"com.bitchat.noise.identity",
"bitchat.keychain"
]
// Add test items with legacy service names
for service in legacyServices {
let addQuery: [String: Any] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrService as String: service,
kSecAttrAccount as String: "test_legacy_item",
kSecValueData as String: "test".data(using: .utf8)!
]
// Add item (ignore if already exists)
_ = SecItemAdd(addQuery as CFDictionary, nil)
}
// Run aggressive cleanup
let deletedCount = KeychainManager.shared.aggressiveCleanupLegacyItems()
// Verify items were deleted
XCTAssertGreaterThan(deletedCount, 0, "Should delete at least some legacy items")
// Verify legacy items are gone
for service in legacyServices {
let query: [String: Any] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrService as String: service,
kSecMatchLimit as String: kSecMatchLimitOne
]
var result: AnyObject?
let status = SecItemCopyMatching(query as CFDictionary, &result)
XCTAssertEqual(status, errSecItemNotFound, "Legacy service '\(service)' should be deleted")
}
}
// MARK: - Performance Tests
func testIdentityLoadPerformance() {
// Ensure identity exists
_ = NoiseEncryptionService()
measure {
// Measure how long it takes to load identity
_ = NoiseEncryptionService()
}
}
}