mirror of
https://github.com/permissionlesstech/bitchat.git
synced 2026-07-25 01:45:20 +00:00
Merge pull request #291 from permissionlesstech/cleanup-dead-code
Remove dead code and fix warnings
This commit is contained in:
@@ -37,10 +37,8 @@
|
||||
04B6BA572E203D6C0090FE39 /* FingerprintView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 04B6BA532E203D6C0090FE39 /* FingerprintView.swift */; };
|
||||
04B6BA582E203D6C0090FE39 /* NoiseTestingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 04B6BA542E203D6C0090FE39 /* NoiseTestingView.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 */; };
|
||||
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 */; };
|
||||
04B6BA672E21601B0090FE39 /* ChannelVerificationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 04B6BA5F2E21601B0090FE39 /* ChannelVerificationTests.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>"; };
|
||||
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>"; };
|
||||
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>"; };
|
||||
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>"; };
|
||||
@@ -218,7 +215,6 @@
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
04B6BA592E215FDA0090FE39 /* NoiseChannelKeyRotation.swift */,
|
||||
04B6BA5A2E215FDA0090FE39 /* NoisePostQuantum.swift */,
|
||||
04B6BA402E2035530090FE39 /* NoiseChannelEncryption.swift */,
|
||||
04B6BA412E2035530090FE39 /* NoiseProtocol.swift */,
|
||||
04B6BA422E2035530090FE39 /* NoiseSecurityConsiderations.swift */,
|
||||
@@ -555,7 +551,6 @@
|
||||
4B747085D07A1BCE0F5BA612 /* BinaryProtocol.swift in Sources */,
|
||||
6E7761E21C99F28AE2F9BE5F /* BitchatApp.swift in Sources */,
|
||||
04891CAA2E22971E0064A111 /* LRUCache.swift in Sources */,
|
||||
04B6BA5B2E215FDA0090FE39 /* NoisePostQuantum.swift in Sources */,
|
||||
04B6BA5C2E215FDA0090FE39 /* NoiseChannelKeyRotation.swift in Sources */,
|
||||
923027D6F2F417AFA2488127 /* BitchatProtocol.swift in Sources */,
|
||||
04B6BA452E2035530090FE39 /* NoiseSecurityConsiderations.swift in Sources */,
|
||||
@@ -591,7 +586,6 @@
|
||||
F455F011B3B648ADA233F998 /* BinaryProtocol.swift in Sources */,
|
||||
10E68BB889356219189E38EC /* BitchatApp.swift in Sources */,
|
||||
04891CA92E22971E0064A111 /* LRUCache.swift in Sources */,
|
||||
04B6BA5D2E215FDA0090FE39 /* NoisePostQuantum.swift in Sources */,
|
||||
04B6BA5E2E215FDA0090FE39 /* NoiseChannelKeyRotation.swift in Sources */,
|
||||
6DE056E1EE9850E9FBF50157 /* BitchatProtocol.swift in Sources */,
|
||||
04B6BA492E2035530090FE39 /* NoiseSecurityConsiderations.swift in Sources */,
|
||||
|
||||
@@ -293,37 +293,4 @@ class NoiseChannelKeyRotation {
|
||||
_ = 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
|
||||
}
|
||||
@@ -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
|
||||
@@ -254,18 +254,18 @@ class NoiseSessionManager {
|
||||
}
|
||||
|
||||
func removeSession(for peerID: String) {
|
||||
_ = managerQueue.sync(flags: .barrier) {
|
||||
sessions.removeValue(forKey: peerID)
|
||||
managerQueue.sync(flags: .barrier) {
|
||||
_ = sessions.removeValue(forKey: peerID)
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
if let session = sessions[oldPeerID] {
|
||||
// Move the session to the new peer ID
|
||||
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)
|
||||
}
|
||||
@@ -290,7 +290,7 @@ class NoiseSessionManager {
|
||||
|
||||
// Remove any existing non-established session
|
||||
if let existingSession = sessions[peerID], !existingSession.isEstablished() {
|
||||
sessions.removeValue(forKey: peerID)
|
||||
_ = sessions.removeValue(forKey: peerID)
|
||||
}
|
||||
|
||||
// Create new initiator session
|
||||
@@ -306,7 +306,7 @@ class NoiseSessionManager {
|
||||
return handshakeData
|
||||
} catch {
|
||||
// Clean up failed session
|
||||
sessions.removeValue(forKey: peerID)
|
||||
_ = sessions.removeValue(forKey: peerID)
|
||||
throw error
|
||||
}
|
||||
}
|
||||
@@ -328,7 +328,7 @@ class NoiseSessionManager {
|
||||
// If we're in the middle of a handshake and receive a new initiation,
|
||||
// reset and start fresh (the other side may have restarted)
|
||||
if existing.getState() == .handshaking && message.count == 32 {
|
||||
sessions.removeValue(forKey: peerID)
|
||||
_ = sessions.removeValue(forKey: peerID)
|
||||
shouldCreateNew = true
|
||||
} else {
|
||||
existingSession = existing
|
||||
@@ -369,7 +369,7 @@ class NoiseSessionManager {
|
||||
return response
|
||||
} catch {
|
||||
// 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
|
||||
DispatchQueue.global().async { [weak self] in
|
||||
|
||||
@@ -77,7 +77,6 @@ struct MessagePadding {
|
||||
|
||||
enum MessageType: UInt8 {
|
||||
case announce = 0x01
|
||||
// 0x02 was legacy keyExchange - removed
|
||||
case leave = 0x03
|
||||
case message = 0x04 // All user messages (private and broadcast)
|
||||
case fragmentStart = 0x05
|
||||
|
||||
@@ -79,7 +79,6 @@ class BluetoothMeshService: NSObject {
|
||||
private var activePeers: Set<String> = [] // Track all active 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 loggedCryptoErrors = Set<String>() // Track which peers we've logged crypto errors for
|
||||
|
||||
// MARK: - Peer Identity Rotation
|
||||
// Mappings between ephemeral peer IDs and permanent fingerprints
|
||||
@@ -884,8 +883,8 @@ class BluetoothMeshService: NSObject {
|
||||
private func notifyPeerIDChange(oldPeerID: String, newPeerID: String, fingerprint: String) {
|
||||
DispatchQueue.main.async { [weak self] in
|
||||
// Remove old peer ID from active peers and announcedPeers
|
||||
_ = self?.collectionsQueue.sync(flags: .barrier) {
|
||||
self?.activePeers.remove(oldPeerID)
|
||||
self?.collectionsQueue.sync(flags: .barrier) {
|
||||
_ = self?.activePeers.remove(oldPeerID)
|
||||
// Don't pre-insert the new peer ID - let the announce packet handle it
|
||||
// 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
|
||||
collectionsQueue.sync(flags: .barrier) {
|
||||
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 {
|
||||
// Remove from active peers with proper locking
|
||||
collectionsQueue.sync(flags: .barrier) {
|
||||
self.activePeers.remove(senderID)
|
||||
self.peerNicknames.removeValue(forKey: senderID)
|
||||
_ = self.activePeers.remove(senderID)
|
||||
_ = self.peerNicknames.removeValue(forKey: senderID)
|
||||
}
|
||||
|
||||
announcedPeers.remove(senderID)
|
||||
@@ -2848,8 +2847,8 @@ extension BluetoothMeshService: CBCentralManagerDelegate {
|
||||
if removed {
|
||||
}
|
||||
|
||||
announcedPeers.remove(peerID)
|
||||
announcedToPeers.remove(peerID)
|
||||
_ = announcedPeers.remove(peerID)
|
||||
_ = announcedToPeers.remove(peerID)
|
||||
} else {
|
||||
}
|
||||
|
||||
@@ -3646,7 +3645,21 @@ extension BluetoothMeshService: CBPeripheralManagerDelegate {
|
||||
SecurityLogger.log("Version negotiation rejected by \(peerID): \(ack.reason ?? "Unknown reason")",
|
||||
category: SecurityLogger.session, level: .error)
|
||||
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 {
|
||||
SecurityLogger.log("Version negotiation successful with \(peerID): agreed on version \(ack.agreedVersion)",
|
||||
category: SecurityLogger.session, level: .debug)
|
||||
@@ -3900,7 +3913,7 @@ extension BluetoothMeshService: CBPeripheralManagerDelegate {
|
||||
// Clean up old entries after 10 seconds
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 10.0) { [weak self] in
|
||||
self?.collectionsQueue.sync(flags: .barrier) {
|
||||
self?.recentlySentMessages.remove(sendKey)
|
||||
_ = self?.recentlySentMessages.remove(sendKey)
|
||||
}
|
||||
}
|
||||
return false
|
||||
|
||||
@@ -466,6 +466,4 @@ struct NoiseMessage: Codable {
|
||||
enum NoiseEncryptionError: Error {
|
||||
case handshakeRequired
|
||||
case sessionNotEstablished
|
||||
case invalidMessage
|
||||
case handshakeFailed(Error)
|
||||
}
|
||||
@@ -307,80 +307,4 @@ class NoiseKeyRotationTests: XCTestCase {
|
||||
|
||||
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
|
||||
}
|
||||
Reference in New Issue
Block a user