Merge pull request #291 from permissionlesstech/cleanup-dead-code

Remove dead code and fix warnings
This commit is contained in:
jack
2025-07-22 11:44:46 +02:00
committed by GitHub
8 changed files with 31 additions and 421 deletions
-6
View File
@@ -37,10 +37,8 @@
04B6BA572E203D6C0090FE39 /* FingerprintView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 04B6BA532E203D6C0090FE39 /* FingerprintView.swift */; }; 04B6BA572E203D6C0090FE39 /* FingerprintView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 04B6BA532E203D6C0090FE39 /* FingerprintView.swift */; };
04B6BA582E203D6C0090FE39 /* NoiseTestingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 04B6BA542E203D6C0090FE39 /* NoiseTestingView.swift */; }; 04B6BA582E203D6C0090FE39 /* NoiseTestingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 04B6BA542E203D6C0090FE39 /* NoiseTestingView.swift */; };
04B6BA5B2E2041220090FE39 /* NoiseIdentityPersistenceTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 04B6BA5A2E2041220090FE39 /* NoiseIdentityPersistenceTests.swift */; }; 04B6BA5B2E2041220090FE39 /* NoiseIdentityPersistenceTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 04B6BA5A2E2041220090FE39 /* NoiseIdentityPersistenceTests.swift */; };
04B6BA5B2E215FDA0090FE39 /* NoisePostQuantum.swift in Sources */ = {isa = PBXBuildFile; fileRef = 04B6BA5A2E215FDA0090FE39 /* NoisePostQuantum.swift */; };
04B6BA5C2E2041220090FE39 /* NoiseIdentityPersistenceTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 04B6BA5A2E2041220090FE39 /* NoiseIdentityPersistenceTests.swift */; }; 04B6BA5C2E2041220090FE39 /* NoiseIdentityPersistenceTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 04B6BA5A2E2041220090FE39 /* NoiseIdentityPersistenceTests.swift */; };
04B6BA5C2E215FDA0090FE39 /* NoiseChannelKeyRotation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 04B6BA592E215FDA0090FE39 /* NoiseChannelKeyRotation.swift */; }; 04B6BA5C2E215FDA0090FE39 /* NoiseChannelKeyRotation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 04B6BA592E215FDA0090FE39 /* NoiseChannelKeyRotation.swift */; };
04B6BA5D2E215FDA0090FE39 /* NoisePostQuantum.swift in Sources */ = {isa = PBXBuildFile; fileRef = 04B6BA5A2E215FDA0090FE39 /* NoisePostQuantum.swift */; };
04B6BA5E2E215FDA0090FE39 /* NoiseChannelKeyRotation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 04B6BA592E215FDA0090FE39 /* NoiseChannelKeyRotation.swift */; }; 04B6BA5E2E215FDA0090FE39 /* NoiseChannelKeyRotation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 04B6BA592E215FDA0090FE39 /* NoiseChannelKeyRotation.swift */; };
04B6BA672E21601B0090FE39 /* ChannelVerificationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 04B6BA5F2E21601B0090FE39 /* ChannelVerificationTests.swift */; }; 04B6BA672E21601B0090FE39 /* ChannelVerificationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 04B6BA5F2E21601B0090FE39 /* ChannelVerificationTests.swift */; };
04B6BA682E21601B0090FE39 /* NoiseKeyRotationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 04B6BA622E21601B0090FE39 /* NoiseKeyRotationTests.swift */; }; 04B6BA682E21601B0090FE39 /* NoiseKeyRotationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 04B6BA622E21601B0090FE39 /* NoiseKeyRotationTests.swift */; };
@@ -166,7 +164,6 @@
04B6BA542E203D6C0090FE39 /* NoiseTestingView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NoiseTestingView.swift; sourceTree = "<group>"; }; 04B6BA542E203D6C0090FE39 /* NoiseTestingView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NoiseTestingView.swift; sourceTree = "<group>"; };
04B6BA592E215FDA0090FE39 /* NoiseChannelKeyRotation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NoiseChannelKeyRotation.swift; sourceTree = "<group>"; }; 04B6BA592E215FDA0090FE39 /* NoiseChannelKeyRotation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NoiseChannelKeyRotation.swift; sourceTree = "<group>"; };
04B6BA5A2E2041220090FE39 /* NoiseIdentityPersistenceTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NoiseIdentityPersistenceTests.swift; sourceTree = "<group>"; }; 04B6BA5A2E2041220090FE39 /* NoiseIdentityPersistenceTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NoiseIdentityPersistenceTests.swift; sourceTree = "<group>"; };
04B6BA5A2E215FDA0090FE39 /* NoisePostQuantum.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NoisePostQuantum.swift; sourceTree = "<group>"; };
04B6BA5F2E21601B0090FE39 /* ChannelVerificationTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChannelVerificationTests.swift; sourceTree = "<group>"; }; 04B6BA5F2E21601B0090FE39 /* ChannelVerificationTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChannelVerificationTests.swift; sourceTree = "<group>"; };
04B6BA602E21601B0090FE39 /* KeychainIntegrationTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeychainIntegrationTests.swift; sourceTree = "<group>"; }; 04B6BA602E21601B0090FE39 /* KeychainIntegrationTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeychainIntegrationTests.swift; sourceTree = "<group>"; };
04B6BA612E21601B0090FE39 /* NoiseChannelEncryptionTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NoiseChannelEncryptionTests.swift; sourceTree = "<group>"; }; 04B6BA612E21601B0090FE39 /* NoiseChannelEncryptionTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NoiseChannelEncryptionTests.swift; sourceTree = "<group>"; };
@@ -218,7 +215,6 @@
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
04B6BA592E215FDA0090FE39 /* NoiseChannelKeyRotation.swift */, 04B6BA592E215FDA0090FE39 /* NoiseChannelKeyRotation.swift */,
04B6BA5A2E215FDA0090FE39 /* NoisePostQuantum.swift */,
04B6BA402E2035530090FE39 /* NoiseChannelEncryption.swift */, 04B6BA402E2035530090FE39 /* NoiseChannelEncryption.swift */,
04B6BA412E2035530090FE39 /* NoiseProtocol.swift */, 04B6BA412E2035530090FE39 /* NoiseProtocol.swift */,
04B6BA422E2035530090FE39 /* NoiseSecurityConsiderations.swift */, 04B6BA422E2035530090FE39 /* NoiseSecurityConsiderations.swift */,
@@ -555,7 +551,6 @@
4B747085D07A1BCE0F5BA612 /* BinaryProtocol.swift in Sources */, 4B747085D07A1BCE0F5BA612 /* BinaryProtocol.swift in Sources */,
6E7761E21C99F28AE2F9BE5F /* BitchatApp.swift in Sources */, 6E7761E21C99F28AE2F9BE5F /* BitchatApp.swift in Sources */,
04891CAA2E22971E0064A111 /* LRUCache.swift in Sources */, 04891CAA2E22971E0064A111 /* LRUCache.swift in Sources */,
04B6BA5B2E215FDA0090FE39 /* NoisePostQuantum.swift in Sources */,
04B6BA5C2E215FDA0090FE39 /* NoiseChannelKeyRotation.swift in Sources */, 04B6BA5C2E215FDA0090FE39 /* NoiseChannelKeyRotation.swift in Sources */,
923027D6F2F417AFA2488127 /* BitchatProtocol.swift in Sources */, 923027D6F2F417AFA2488127 /* BitchatProtocol.swift in Sources */,
04B6BA452E2035530090FE39 /* NoiseSecurityConsiderations.swift in Sources */, 04B6BA452E2035530090FE39 /* NoiseSecurityConsiderations.swift in Sources */,
@@ -591,7 +586,6 @@
F455F011B3B648ADA233F998 /* BinaryProtocol.swift in Sources */, F455F011B3B648ADA233F998 /* BinaryProtocol.swift in Sources */,
10E68BB889356219189E38EC /* BitchatApp.swift in Sources */, 10E68BB889356219189E38EC /* BitchatApp.swift in Sources */,
04891CA92E22971E0064A111 /* LRUCache.swift in Sources */, 04891CA92E22971E0064A111 /* LRUCache.swift in Sources */,
04B6BA5D2E215FDA0090FE39 /* NoisePostQuantum.swift in Sources */,
04B6BA5E2E215FDA0090FE39 /* NoiseChannelKeyRotation.swift in Sources */, 04B6BA5E2E215FDA0090FE39 /* NoiseChannelKeyRotation.swift in Sources */,
6DE056E1EE9850E9FBF50157 /* BitchatProtocol.swift in Sources */, 6DE056E1EE9850E9FBF50157 /* BitchatProtocol.swift in Sources */,
04B6BA492E2035530090FE39 /* NoiseSecurityConsiderations.swift in Sources */, 04B6BA492E2035530090FE39 /* NoiseSecurityConsiderations.swift in Sources */,
@@ -293,37 +293,4 @@ class NoiseChannelKeyRotation {
_ = KeychainManager.shared.deleteChannelPassword(for: epochKey) _ = KeychainManager.shared.deleteChannelPassword(for: epochKey)
} }
} }
}
// MARK: - Future Double Ratchet Support
/// Placeholder for full Double Ratchet implementation
/// This would handle per-message key derivation and ratcheting
protocol DoubleRatchetProtocol {
/// Initialize a new ratchet session
func initializeRatchet(sharedSecret: Data, isInitiator: Bool) throws
/// Ratchet forward and get next message key
func ratchetEncrypt(_ plaintext: Data) throws -> (ciphertext: Data, header: Data)
/// Ratchet forward using received header and decrypt
func ratchetDecrypt(_ ciphertext: Data, header: Data) throws -> Data
}
/// Message header for Double Ratchet (future use)
struct RatchetHeader: Codable {
let publicKey: Data // Ephemeral public key
let previousChainLength: UInt32
let messageNumber: UInt32
}
/// Placeholder for full implementation
class ChannelDoubleRatchet {
// This would implement the full Double Ratchet algorithm
// adapted for multi-party channels
// Challenges:
// - Sender keys for multi-party
// - Out-of-order delivery
// - State synchronization
// - Performance with many members
} }
-285
View File
@@ -1,285 +0,0 @@
//
// NoisePostQuantum.swift
// bitchat
//
// This is free and unencumbered software released into the public domain.
// For more information, see <https://unlicense.org>
//
import Foundation
import CryptoKit
// MARK: - Post-Quantum Cryptography Framework
/// Framework for integrating post-quantum algorithms with Noise Protocol
/// Currently a placeholder until PQ libraries are available in Swift/iOS
protocol PostQuantumKeyExchange {
associatedtype PublicKey
associatedtype PrivateKey
associatedtype SharedSecret
/// Generate a new keypair
static func generateKeyPair() throws -> (publicKey: PublicKey, privateKey: PrivateKey)
/// Derive shared secret (for initiator)
static func encapsulate(remotePublicKey: PublicKey) throws -> (sharedSecret: SharedSecret, ciphertext: Data)
/// Derive shared secret (for responder)
static func decapsulate(ciphertext: Data, privateKey: PrivateKey) throws -> SharedSecret
/// Get size requirements
static var publicKeySize: Int { get }
static var privateKeySize: Int { get }
static var ciphertextSize: Int { get }
static var sharedSecretSize: Int { get }
}
// MARK: - Hybrid Key Exchange
/// Combines classical (Curve25519) with post-quantum algorithms
class HybridNoiseKeyExchange {
enum Algorithm {
case classicalOnly // Current: Curve25519 only
case hybridKyber768 // Future: Curve25519 + Kyber768
case hybridKyber1024 // Future: Curve25519 + Kyber1024
var name: String {
switch self {
case .classicalOnly:
return "25519"
case .hybridKyber768:
return "25519+Kyber768"
case .hybridKyber1024:
return "25519+Kyber1024"
}
}
var isPostQuantum: Bool {
switch self {
case .classicalOnly:
return false
case .hybridKyber768, .hybridKyber1024:
return true
}
}
}
struct HybridPublicKey {
let classical: Curve25519.KeyAgreement.PublicKey
let postQuantum: Data? // Future: actual PQ public key
var serialized: Data {
var data = classical.rawRepresentation
if let pq = postQuantum {
data.append(pq)
}
return data
}
}
struct HybridPrivateKey {
let classical: Curve25519.KeyAgreement.PrivateKey
let postQuantum: Data? // Future: actual PQ private key
}
struct HybridSharedSecret {
let classical: SharedSecret
let postQuantum: Data? // Future: actual PQ shared secret
/// Combine both secrets using KDF
func combinedSecret() -> SymmetricKey {
var combinedData = classical.withUnsafeBytes { Data($0) }
if let pq = postQuantum {
combinedData.append(pq)
}
// Use HKDF to combine secrets
let hash = SHA256.hash(data: combinedData)
return SymmetricKey(data: Data(hash))
}
}
// MARK: - Key Generation
static func generateKeyPair(algorithm: Algorithm) throws -> (publicKey: HybridPublicKey, privateKey: HybridPrivateKey) {
// Generate classical keypair
let classicalPrivate = Curve25519.KeyAgreement.PrivateKey()
let classicalPublic = classicalPrivate.publicKey
// Generate PQ keypair when available
let pqPublic: Data? = nil
let pqPrivate: Data? = nil
switch algorithm {
case .classicalOnly:
break // No PQ component
case .hybridKyber768, .hybridKyber1024:
// Future: Generate Kyber keypair
// let (pqPub, pqPriv) = try KyberKeyExchange.generateKeyPair()
// pqPublic = pqPub.serialized
// pqPrivate = pqPriv.serialized
break
}
return (
HybridPublicKey(classical: classicalPublic, postQuantum: pqPublic),
HybridPrivateKey(classical: classicalPrivate, postQuantum: pqPrivate)
)
}
// MARK: - Key Agreement
static func performKeyAgreement(
localPrivate: HybridPrivateKey,
remotePublic: HybridPublicKey,
algorithm: Algorithm
) throws -> HybridSharedSecret {
// Perform classical ECDH
let classicalShared = try localPrivate.classical.sharedSecretFromKeyAgreement(
with: remotePublic.classical
)
// Perform PQ key agreement when available
let pqShared: Data? = nil
switch algorithm {
case .classicalOnly:
break // No PQ component
case .hybridKyber768, .hybridKyber1024:
// Future: Perform Kyber decapsulation
// if let pqCiphertext = remotePublic.postQuantum,
// let pqPrivateKey = localPrivate.postQuantum {
// pqShared = try KyberKeyExchange.decapsulate(
// ciphertext: pqCiphertext,
// privateKey: pqPrivateKey
// )
// }
break
}
return HybridSharedSecret(classical: classicalShared, postQuantum: pqShared)
}
}
// MARK: - Modified Noise Pattern for PQ
/// Extended Noise handshake pattern for post-quantum
/// Based on Noise PQ patterns: https://github.com/noiseprotocol/noise_pq_spec
struct NoisePQHandshakePattern {
// Pattern modifiers for PQ
enum Modifier {
case pq1 // First message includes PQ KEM
case pq2 // Second message includes PQ KEM
case pq3 // Third message includes PQ KEM
}
// Example: XXpq1 pattern (XX with PQ in first message)
// -> e, epq
// <- e, ee, eepq, s, es
// -> s, se
// This would modify the Noise XX pattern to include
// post-quantum key encapsulation material
}
// MARK: - Migration Support
/// Helps transition from classical to post-quantum crypto
class NoiseProtocolMigration {
enum MigrationPhase {
case classicalOnly // Current state
case hybridOptional // Support both, prefer hybrid
case hybridRequired // Require hybrid mode
case postQuantumOnly // Future: PQ only
}
struct MigrationConfig {
let currentPhase: MigrationPhase
let preferredAlgorithm: HybridNoiseKeyExchange.Algorithm
let acceptedAlgorithms: Set<HybridNoiseKeyExchange.Algorithm>
let migrationDeadline: Date?
}
/// Check if a peer supports post-quantum
static func checkPQSupport(peerVersion: String) -> Bool {
// Future: Parse peer capabilities
// For now, assume no PQ support
return false
}
/// Get migration configuration
static func getMigrationConfig() -> MigrationConfig {
// Current configuration: classical only
return MigrationConfig(
currentPhase: .classicalOnly,
preferredAlgorithm: .classicalOnly,
acceptedAlgorithms: [.classicalOnly],
migrationDeadline: nil
)
}
}
// MARK: - Future Implementation Notes
/*
Post-Quantum Integration Plan:
1. Wait for stable Swift PQ libraries (e.g., SwiftOQS)
2. Implement Kyber768/1024 wrapper conforming to PostQuantumKeyExchange
3. Update Noise handshake to support hybrid mode
4. Add capability negotiation in protocol
5. Implement gradual rollout with fallback
Challenges:
- Increased message sizes (Kyber768 public key ~1184 bytes)
- Performance impact on mobile devices
- Battery usage considerations
- Backward compatibility
- Library availability and maintenance
Timeline estimate:
- PQ libraries stable in Swift: 2025-2026
- Initial hybrid implementation: 2026
- Full deployment: 2027+
*/
// MARK: - Testing Support
#if DEBUG
/// Mock PQ implementation for testing
class MockPostQuantumKeyExchange: PostQuantumKeyExchange {
typealias PublicKey = Data
typealias PrivateKey = Data
typealias SharedSecret = Data
static func generateKeyPair() throws -> (publicKey: Data, privateKey: Data) {
// Generate mock keys for testing
let privateKey = Data((0..<32).map { _ in UInt8.random(in: 0...255) })
let publicKey = Data((0..<800).map { _ in UInt8.random(in: 0...255) }) // Simulate larger PQ key
return (publicKey, privateKey)
}
static func encapsulate(remotePublicKey: Data) throws -> (sharedSecret: Data, ciphertext: Data) {
let sharedSecret = Data((0..<32).map { _ in UInt8.random(in: 0...255) })
let ciphertext = Data((0..<1088).map { _ in UInt8.random(in: 0...255) }) // Simulate Kyber ciphertext
return (sharedSecret, ciphertext)
}
static func decapsulate(ciphertext: Data, privateKey: Data) throws -> Data {
// Return deterministic secret based on inputs for testing
let combined = ciphertext + privateKey
let hash = SHA256.hash(data: combined)
return Data(hash)
}
static var publicKeySize: Int { 800 }
static var privateKeySize: Int { 1632 }
static var ciphertextSize: Int { 1088 }
static var sharedSecretSize: Int { 32 }
}
#endif
+8 -8
View File
@@ -254,18 +254,18 @@ class NoiseSessionManager {
} }
func removeSession(for peerID: String) { func removeSession(for peerID: String) {
_ = managerQueue.sync(flags: .barrier) { managerQueue.sync(flags: .barrier) {
sessions.removeValue(forKey: peerID) _ = sessions.removeValue(forKey: peerID)
} }
} }
func migrateSession(from oldPeerID: String, to newPeerID: String) { func migrateSession(from oldPeerID: String, to newPeerID: String) {
_ = managerQueue.sync(flags: .barrier) { managerQueue.sync(flags: .barrier) {
// Check if we have a session for the old peer ID // Check if we have a session for the old peer ID
if let session = sessions[oldPeerID] { if let session = sessions[oldPeerID] {
// Move the session to the new peer ID // Move the session to the new peer ID
sessions[newPeerID] = session sessions[newPeerID] = session
sessions.removeValue(forKey: oldPeerID) _ = sessions.removeValue(forKey: oldPeerID)
SecurityLogger.log("Migrated Noise session from \(oldPeerID) to \(newPeerID)", category: SecurityLogger.noise, level: .info) SecurityLogger.log("Migrated Noise session from \(oldPeerID) to \(newPeerID)", category: SecurityLogger.noise, level: .info)
} }
@@ -290,7 +290,7 @@ class NoiseSessionManager {
// Remove any existing non-established session // Remove any existing non-established session
if let existingSession = sessions[peerID], !existingSession.isEstablished() { if let existingSession = sessions[peerID], !existingSession.isEstablished() {
sessions.removeValue(forKey: peerID) _ = sessions.removeValue(forKey: peerID)
} }
// Create new initiator session // Create new initiator session
@@ -306,7 +306,7 @@ class NoiseSessionManager {
return handshakeData return handshakeData
} catch { } catch {
// Clean up failed session // Clean up failed session
sessions.removeValue(forKey: peerID) _ = sessions.removeValue(forKey: peerID)
throw error throw error
} }
} }
@@ -328,7 +328,7 @@ class NoiseSessionManager {
// If we're in the middle of a handshake and receive a new initiation, // If we're in the middle of a handshake and receive a new initiation,
// reset and start fresh (the other side may have restarted) // reset and start fresh (the other side may have restarted)
if existing.getState() == .handshaking && message.count == 32 { if existing.getState() == .handshaking && message.count == 32 {
sessions.removeValue(forKey: peerID) _ = sessions.removeValue(forKey: peerID)
shouldCreateNew = true shouldCreateNew = true
} else { } else {
existingSession = existing existingSession = existing
@@ -369,7 +369,7 @@ class NoiseSessionManager {
return response return response
} catch { } catch {
// Reset the session on handshake failure so next attempt can start fresh // Reset the session on handshake failure so next attempt can start fresh
sessions.removeValue(forKey: peerID) _ = sessions.removeValue(forKey: peerID)
// Schedule callback outside the synchronized block to prevent deadlock // Schedule callback outside the synchronized block to prevent deadlock
DispatchQueue.global().async { [weak self] in DispatchQueue.global().async { [weak self] in
-1
View File
@@ -77,7 +77,6 @@ struct MessagePadding {
enum MessageType: UInt8 { enum MessageType: UInt8 {
case announce = 0x01 case announce = 0x01
// 0x02 was legacy keyExchange - removed
case leave = 0x03 case leave = 0x03
case message = 0x04 // All user messages (private and broadcast) case message = 0x04 // All user messages (private and broadcast)
case fragmentStart = 0x05 case fragmentStart = 0x05
+23 -10
View File
@@ -79,7 +79,6 @@ class BluetoothMeshService: NSObject {
private var activePeers: Set<String> = [] // Track all active peers private var activePeers: Set<String> = [] // Track all active peers
private var peerRSSI: [String: NSNumber] = [:] // Track RSSI values for peers private var peerRSSI: [String: NSNumber] = [:] // Track RSSI values for peers
private var peripheralRSSI: [String: NSNumber] = [:] // Track RSSI by peripheral ID during discovery private var peripheralRSSI: [String: NSNumber] = [:] // Track RSSI by peripheral ID during discovery
private var loggedCryptoErrors = Set<String>() // Track which peers we've logged crypto errors for
// MARK: - Peer Identity Rotation // MARK: - Peer Identity Rotation
// Mappings between ephemeral peer IDs and permanent fingerprints // Mappings between ephemeral peer IDs and permanent fingerprints
@@ -884,8 +883,8 @@ class BluetoothMeshService: NSObject {
private func notifyPeerIDChange(oldPeerID: String, newPeerID: String, fingerprint: String) { private func notifyPeerIDChange(oldPeerID: String, newPeerID: String, fingerprint: String) {
DispatchQueue.main.async { [weak self] in DispatchQueue.main.async { [weak self] in
// Remove old peer ID from active peers and announcedPeers // Remove old peer ID from active peers and announcedPeers
_ = self?.collectionsQueue.sync(flags: .barrier) { self?.collectionsQueue.sync(flags: .barrier) {
self?.activePeers.remove(oldPeerID) _ = self?.activePeers.remove(oldPeerID)
// Don't pre-insert the new peer ID - let the announce packet handle it // Don't pre-insert the new peer ID - let the announce packet handle it
// This ensures the connect message logic works properly // This ensures the connect message logic works properly
} }
@@ -2043,7 +2042,7 @@ class BluetoothMeshService: NSObject {
// IMPORTANT: Remove old peer ID from activePeers to prevent duplicates // IMPORTANT: Remove old peer ID from activePeers to prevent duplicates
collectionsQueue.sync(flags: .barrier) { collectionsQueue.sync(flags: .barrier) {
if self.activePeers.contains(tempID) { if self.activePeers.contains(tempID) {
self.activePeers.remove(tempID) _ = self.activePeers.remove(tempID)
} }
} }
@@ -2160,8 +2159,8 @@ class BluetoothMeshService: NSObject {
if String(data: packet.payload, encoding: .utf8) != nil { if String(data: packet.payload, encoding: .utf8) != nil {
// Remove from active peers with proper locking // Remove from active peers with proper locking
collectionsQueue.sync(flags: .barrier) { collectionsQueue.sync(flags: .barrier) {
self.activePeers.remove(senderID) _ = self.activePeers.remove(senderID)
self.peerNicknames.removeValue(forKey: senderID) _ = self.peerNicknames.removeValue(forKey: senderID)
} }
announcedPeers.remove(senderID) announcedPeers.remove(senderID)
@@ -2848,8 +2847,8 @@ extension BluetoothMeshService: CBCentralManagerDelegate {
if removed { if removed {
} }
announcedPeers.remove(peerID) _ = announcedPeers.remove(peerID)
announcedToPeers.remove(peerID) _ = announcedToPeers.remove(peerID)
} else { } else {
} }
@@ -3646,7 +3645,21 @@ extension BluetoothMeshService: CBPeripheralManagerDelegate {
SecurityLogger.log("Version negotiation rejected by \(peerID): \(ack.reason ?? "Unknown reason")", SecurityLogger.log("Version negotiation rejected by \(peerID): \(ack.reason ?? "Unknown reason")",
category: SecurityLogger.session, level: .error) category: SecurityLogger.session, level: .error)
versionNegotiationState[peerID] = .failed(reason: ack.reason ?? "Version rejected") versionNegotiationState[peerID] = .failed(reason: ack.reason ?? "Version rejected")
// TODO: Handle disconnection
// Clean up state for incompatible peer
collectionsQueue.sync(flags: .barrier) {
_ = self.activePeers.remove(peerID)
_ = self.peerNicknames.removeValue(forKey: peerID)
}
announcedPeers.remove(peerID)
// Clean up any Noise session
noiseService.removePeer(peerID)
// Notify delegate about incompatible peer disconnection
DispatchQueue.main.async { [weak self] in
self?.delegate?.didDisconnectFromPeer(peerID)
}
} else { } else {
SecurityLogger.log("Version negotiation successful with \(peerID): agreed on version \(ack.agreedVersion)", SecurityLogger.log("Version negotiation successful with \(peerID): agreed on version \(ack.agreedVersion)",
category: SecurityLogger.session, level: .debug) category: SecurityLogger.session, level: .debug)
@@ -3900,7 +3913,7 @@ extension BluetoothMeshService: CBPeripheralManagerDelegate {
// Clean up old entries after 10 seconds // Clean up old entries after 10 seconds
DispatchQueue.main.asyncAfter(deadline: .now() + 10.0) { [weak self] in DispatchQueue.main.asyncAfter(deadline: .now() + 10.0) { [weak self] in
self?.collectionsQueue.sync(flags: .barrier) { self?.collectionsQueue.sync(flags: .barrier) {
self?.recentlySentMessages.remove(sendKey) _ = self?.recentlySentMessages.remove(sendKey)
} }
} }
return false return false
@@ -466,6 +466,4 @@ struct NoiseMessage: Codable {
enum NoiseEncryptionError: Error { enum NoiseEncryptionError: Error {
case handshakeRequired case handshakeRequired
case sessionNotEstablished case sessionNotEstablished
case invalidMessage
case handshakeFailed(Error)
} }
-76
View File
@@ -307,80 +307,4 @@ class NoiseKeyRotationTests: XCTestCase {
XCTAssertTrue(decrypted, "Failed to decrypt with any valid key") XCTAssertTrue(decrypted, "Failed to decrypt with any valid key")
} }
}
// MARK: - Post-Quantum Framework Tests
class NoisePostQuantumTests: XCTestCase {
func testHybridKeyGeneration() throws {
let (publicKey, privateKey) = try HybridNoiseKeyExchange.generateKeyPair(algorithm: .classicalOnly)
XCTAssertNotNil(publicKey.classical)
XCTAssertNil(publicKey.postQuantum) // No PQ component yet
XCTAssertEqual(publicKey.serialized.count, 32) // Just Curve25519
XCTAssertNotNil(privateKey.classical)
XCTAssertNil(privateKey.postQuantum)
}
func testHybridKeyAgreement() throws {
// Generate two keypairs
let (alicePub, alicePriv) = try HybridNoiseKeyExchange.generateKeyPair(algorithm: .classicalOnly)
let (bobPub, bobPriv) = try HybridNoiseKeyExchange.generateKeyPair(algorithm: .classicalOnly)
// Perform key agreement
let aliceShared = try HybridNoiseKeyExchange.performKeyAgreement(
localPrivate: alicePriv,
remotePublic: bobPub,
algorithm: .classicalOnly
)
let bobShared = try HybridNoiseKeyExchange.performKeyAgreement(
localPrivate: bobPriv,
remotePublic: alicePub,
algorithm: .classicalOnly
)
// Shared secrets should match
XCTAssertEqual(
aliceShared.combinedSecret().withUnsafeBytes { Data($0) },
bobShared.combinedSecret().withUnsafeBytes { Data($0) }
)
}
func testMigrationConfig() {
let config = NoiseProtocolMigration.getMigrationConfig()
XCTAssertEqual(config.currentPhase, .classicalOnly)
XCTAssertEqual(config.preferredAlgorithm, .classicalOnly)
XCTAssertTrue(config.acceptedAlgorithms.contains(.classicalOnly))
XCTAssertNil(config.migrationDeadline)
}
#if DEBUG
func testMockPostQuantum() throws {
// Test mock PQ implementation
let (publicKey, privateKey) = try MockPostQuantumKeyExchange.generateKeyPair()
XCTAssertEqual(publicKey.count, MockPostQuantumKeyExchange.publicKeySize)
XCTAssertEqual(privateKey.count, 32) // Mock uses smaller private key
let (sharedSecret, ciphertext) = try MockPostQuantumKeyExchange.encapsulate(
remotePublicKey: publicKey
)
XCTAssertEqual(ciphertext.count, MockPostQuantumKeyExchange.ciphertextSize)
XCTAssertEqual(sharedSecret.count, MockPostQuantumKeyExchange.sharedSecretSize)
let decapsulatedSecret = try MockPostQuantumKeyExchange.decapsulate(
ciphertext: ciphertext,
privateKey: privateKey
)
// In real implementation, these would match
// Mock just returns deterministic values
XCTAssertEqual(decapsulatedSecret.count, 32)
}
#endif
} }