mirror of
https://github.com/permissionlesstech/bitchat.git
synced 2026-07-25 08:45:19 +00:00
refactor: remove all dead legacy and migration code
- Remove unused migrateSession() functions (never called in production) - NoiseSession.migrateSession() - 13 lines - NoiseEncryptionService.migratePeerSession() - 18 lines - Test for migration functionality - 17 lines - Remove unnecessary keychain cleanup code (no legacy data existed) - cleanupLegacyKeychainItems() - 62 lines - aggressiveCleanupLegacyItems() - 72 lines - resetCleanupFlag() - 4 lines - Simplified panic mode to just use deleteAllKeychainData() Total removed: 194 lines of dead/unnecessary code Analysis revealed: - KeychainManager introduced July 5, 2025 - Cleanup code added July 15, 2025 (10 days later) - Legacy service names were never used in production - Migration functions were incomplete implementation never called - Peer ID rotation remains active (not legacy)
This commit is contained in:
@@ -296,19 +296,6 @@ class NoiseSessionManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func migrateSession(from oldPeerID: String, to newPeerID: String) {
|
|
||||||
managerQueue.sync(flags: .barrier) {
|
|
||||||
// Check if we have a session for the old peer ID
|
|
||||||
if let session = sessions[oldPeerID] {
|
|
||||||
// Move the session to the new peer ID
|
|
||||||
sessions[newPeerID] = session
|
|
||||||
_ = sessions.removeValue(forKey: oldPeerID)
|
|
||||||
|
|
||||||
SecureLogger.log("Migrated Noise session from \(oldPeerID) to \(newPeerID)", category: SecureLogger.noise, level: .info)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func getEstablishedSessions() -> [String: NoiseSession] {
|
func getEstablishedSessions() -> [String: NoiseSession] {
|
||||||
return managerQueue.sync {
|
return managerQueue.sync {
|
||||||
return sessions.filter { $0.value.isEstablished() }
|
return sessions.filter { $0.value.isEstablished() }
|
||||||
|
|||||||
@@ -17,73 +17,7 @@ class KeychainManager {
|
|||||||
private let service = "chat.bitchat"
|
private let service = "chat.bitchat"
|
||||||
private let appGroup = "group.chat.bitchat"
|
private let appGroup = "group.chat.bitchat"
|
||||||
|
|
||||||
private init() {
|
private init() {}
|
||||||
// Clean up legacy keychain items on first run
|
|
||||||
cleanupLegacyKeychainItems()
|
|
||||||
}
|
|
||||||
|
|
||||||
private func cleanupLegacyKeychainItems() {
|
|
||||||
// Check if we've already done cleanup
|
|
||||||
let cleanupKey = "bitchat.keychain.cleanup.v2"
|
|
||||||
if UserDefaults.standard.bool(forKey: cleanupKey) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// List of old service names to migrate from
|
|
||||||
let legacyServices = [
|
|
||||||
"com.bitchat.passwords",
|
|
||||||
"com.bitchat.deviceidentity",
|
|
||||||
"com.bitchat.noise.identity",
|
|
||||||
"chat.bitchat.passwords",
|
|
||||||
"bitchat.keychain"
|
|
||||||
]
|
|
||||||
|
|
||||||
var migratedItems = 0
|
|
||||||
|
|
||||||
// Try to migrate identity keys
|
|
||||||
for oldService in legacyServices {
|
|
||||||
// Check for noise identity key
|
|
||||||
let query: [String: Any] = [
|
|
||||||
kSecClass as String: kSecClassGenericPassword,
|
|
||||||
kSecAttrService as String: oldService,
|
|
||||||
kSecAttrAccount as String: "identity_noiseStaticKey",
|
|
||||||
kSecReturnData as String: true
|
|
||||||
]
|
|
||||||
|
|
||||||
var result: AnyObject?
|
|
||||||
let status = SecItemCopyMatching(query as CFDictionary, &result)
|
|
||||||
|
|
||||||
if status == errSecSuccess, let data = result as? Data {
|
|
||||||
// Save to new service
|
|
||||||
if saveIdentityKey(data, forKey: "noiseStaticKey") {
|
|
||||||
migratedItems += 1
|
|
||||||
SecureLogger.logKeyOperation("migrate", keyType: "noiseStaticKey", success: true)
|
|
||||||
}
|
|
||||||
// Delete from old service
|
|
||||||
let deleteQuery: [String: Any] = [
|
|
||||||
kSecClass as String: kSecClassGenericPassword,
|
|
||||||
kSecAttrService as String: oldService,
|
|
||||||
kSecAttrAccount as String: "identity_noiseStaticKey"
|
|
||||||
]
|
|
||||||
SecItemDelete(deleteQuery as CFDictionary)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Clean up all other legacy items
|
|
||||||
for oldService in legacyServices {
|
|
||||||
let deleteQuery: [String: Any] = [
|
|
||||||
kSecClass as String: kSecClassGenericPassword,
|
|
||||||
kSecAttrService as String: oldService
|
|
||||||
]
|
|
||||||
|
|
||||||
SecItemDelete(deleteQuery as CFDictionary)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Mark cleanup as done
|
|
||||||
UserDefaults.standard.set(true, forKey: cleanupKey)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private func isSandboxed() -> Bool {
|
private func isSandboxed() -> Bool {
|
||||||
@@ -240,11 +174,6 @@ class KeychainManager {
|
|||||||
return status == errSecSuccess || status == errSecItemNotFound
|
return status == errSecSuccess || status == errSecItemNotFound
|
||||||
}
|
}
|
||||||
|
|
||||||
// Force cleanup to run again (for development/testing)
|
|
||||||
func resetCleanupFlag() {
|
|
||||||
UserDefaults.standard.removeObject(forKey: "bitchat.keychain.cleanup.v2")
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Delete ALL keychain data for panic mode
|
// Delete ALL keychain data for panic mode
|
||||||
func deleteAllKeychainData() -> Bool {
|
func deleteAllKeychainData() -> Bool {
|
||||||
@@ -384,76 +313,4 @@ class KeychainManager {
|
|||||||
let key = "identity_noiseStaticKey"
|
let key = "identity_noiseStaticKey"
|
||||||
return retrieveData(forKey: key) != nil
|
return retrieveData(forKey: key) != nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Aggressive cleanup for legacy items - can be called manually
|
|
||||||
func aggressiveCleanupLegacyItems() -> Int {
|
|
||||||
var deletedCount = 0
|
|
||||||
|
|
||||||
// List of KNOWN bitchat service names from our development history
|
|
||||||
let knownBitchatServices = [
|
|
||||||
"com.bitchat.passwords",
|
|
||||||
"com.bitchat.deviceidentity",
|
|
||||||
"com.bitchat.noise.identity",
|
|
||||||
"chat.bitchat.passwords",
|
|
||||||
"bitchat.keychain",
|
|
||||||
"Bitchat",
|
|
||||||
"BitChat"
|
|
||||||
]
|
|
||||||
|
|
||||||
// First, delete all items from known legacy services
|
|
||||||
for legacyService in knownBitchatServices {
|
|
||||||
let deleteQuery: [String: Any] = [
|
|
||||||
kSecClass as String: kSecClassGenericPassword,
|
|
||||||
kSecAttrService as String: legacyService
|
|
||||||
]
|
|
||||||
|
|
||||||
let status = SecItemDelete(deleteQuery as CFDictionary)
|
|
||||||
if status == errSecSuccess {
|
|
||||||
deletedCount += 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Now search for items that have our specific account patterns with bitchat service names
|
|
||||||
let searchQuery: [String: Any] = [
|
|
||||||
kSecClass as String: kSecClassGenericPassword,
|
|
||||||
kSecMatchLimit as String: kSecMatchLimitAll,
|
|
||||||
kSecReturnAttributes as String: true
|
|
||||||
]
|
|
||||||
|
|
||||||
var result: AnyObject?
|
|
||||||
let status = SecItemCopyMatching(searchQuery as CFDictionary, &result)
|
|
||||||
|
|
||||||
if status == errSecSuccess, let items = result as? [[String: Any]] {
|
|
||||||
for item in items {
|
|
||||||
let account = item[kSecAttrAccount as String] as? String ?? ""
|
|
||||||
let service = item[kSecAttrService as String] as? String ?? ""
|
|
||||||
|
|
||||||
// ONLY delete if service name contains "bitchat" somewhere
|
|
||||||
// This ensures we never touch other apps' keychain items
|
|
||||||
var shouldDelete = false
|
|
||||||
|
|
||||||
// Check if service contains "bitchat" (case insensitive) but NOT our current service
|
|
||||||
let serviceLower = service.lowercased()
|
|
||||||
if service != self.service && serviceLower.contains("bitchat") {
|
|
||||||
shouldDelete = true
|
|
||||||
}
|
|
||||||
|
|
||||||
if shouldDelete {
|
|
||||||
// Build precise delete query
|
|
||||||
let deleteQuery: [String: Any] = [
|
|
||||||
kSecClass as String: kSecClassGenericPassword,
|
|
||||||
kSecAttrService as String: service,
|
|
||||||
kSecAttrAccount as String: account
|
|
||||||
]
|
|
||||||
|
|
||||||
let deleteStatus = SecItemDelete(deleteQuery as CFDictionary)
|
|
||||||
if deleteStatus == errSecSuccess {
|
|
||||||
deletedCount += 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return deletedCount
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -419,24 +419,6 @@ class NoiseEncryptionService {
|
|||||||
SecureLogger.logSecurityEvent(.sessionExpired(peerID: peerID))
|
SecureLogger.logSecurityEvent(.sessionExpired(peerID: peerID))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Migrate session when peer ID changes
|
|
||||||
func migratePeerSession(from oldPeerID: String, to newPeerID: String, fingerprint: String) {
|
|
||||||
// First update the fingerprint mappings
|
|
||||||
serviceQueue.sync(flags: .barrier) {
|
|
||||||
// Remove old mapping
|
|
||||||
if let oldFingerprint = peerFingerprints[oldPeerID], oldFingerprint == fingerprint {
|
|
||||||
peerFingerprints.removeValue(forKey: oldPeerID)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add new mapping
|
|
||||||
peerFingerprints[newPeerID] = fingerprint
|
|
||||||
fingerprintToPeerID[fingerprint] = newPeerID
|
|
||||||
}
|
|
||||||
|
|
||||||
// Migrate the session in session manager
|
|
||||||
sessionManager.migrateSession(from: oldPeerID, to: newPeerID)
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: - Private Helpers
|
// MARK: - Private Helpers
|
||||||
|
|
||||||
private func handleSessionEstablished(peerID: String, remoteStaticKey: Curve25519.KeyAgreement.PublicKey) {
|
private func handleSessionEstablished(peerID: String, remoteStaticKey: Curve25519.KeyAgreement.PublicKey) {
|
||||||
|
|||||||
@@ -1474,10 +1474,7 @@ class ChatViewModel: ObservableObject, BitchatDelegate {
|
|||||||
privateChats.removeAll()
|
privateChats.removeAll()
|
||||||
unreadPrivateMessages.removeAll()
|
unreadPrivateMessages.removeAll()
|
||||||
|
|
||||||
// First run aggressive cleanup to get rid of all legacy items
|
// Delete all keychain data
|
||||||
_ = KeychainManager.shared.aggressiveCleanupLegacyItems()
|
|
||||||
|
|
||||||
// Then delete all current keychain data
|
|
||||||
_ = KeychainManager.shared.deleteAllKeychainData()
|
_ = KeychainManager.shared.deleteAllKeychainData()
|
||||||
|
|
||||||
// Clear UserDefaults identity fallbacks
|
// Clear UserDefaults identity fallbacks
|
||||||
|
|||||||
@@ -226,23 +226,6 @@ final class NoiseProtocolTests: XCTestCase {
|
|||||||
XCTAssertEqual(decrypted, plaintext)
|
XCTAssertEqual(decrypted, plaintext)
|
||||||
}
|
}
|
||||||
|
|
||||||
func testSessionMigration() throws {
|
|
||||||
let manager = NoiseSessionManager(localStaticKey: aliceKey)
|
|
||||||
|
|
||||||
// Create and establish a session
|
|
||||||
_ = try manager.initiateHandshake(with: TestConstants.testPeerID2)
|
|
||||||
|
|
||||||
// Migrate to new peer ID
|
|
||||||
let newPeerID = TestConstants.testPeerID3
|
|
||||||
manager.migrateSession(from: TestConstants.testPeerID2, to: newPeerID)
|
|
||||||
|
|
||||||
// Old peer ID should not have session
|
|
||||||
XCTAssertNil(manager.getSession(for: TestConstants.testPeerID2))
|
|
||||||
|
|
||||||
// New peer ID should have the session
|
|
||||||
XCTAssertNotNil(manager.getSession(for: newPeerID))
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: - Security Tests
|
// MARK: - Security Tests
|
||||||
|
|
||||||
func testTamperedCiphertextDetection() throws {
|
func testTamperedCiphertextDetection() throws {
|
||||||
|
|||||||
Reference in New Issue
Block a user