mirror of
https://github.com/permissionlesstech/bitchat.git
synced 2026-07-25 03:05:19 +00:00
Remove dead code and placeholders
- Remove NoisePostQuantum.swift entirely (placeholder with no implementation) - Remove Double Ratchet placeholder code from NoiseChannelKeyRotation.swift - Remove NoisePostQuantumTests that tested mock implementations - Handle TODO for version negotiation rejection (now properly disconnects) - Remove legacy comment about removed message type 0x02 - Keep deprecated ownerID field as it's still used for compatibility This cleanup removes ~400 lines of placeholder code that was not being used and unlikely to be implemented in the near future.
This commit is contained in:
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -3646,7 +3646,16 @@ 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
|
||||
handlePeerDisconnection(peerID)
|
||||
|
||||
// Notify delegate about incompatible peer
|
||||
DispatchQueue.main.async { [weak self] in
|
||||
if let delegate = self?.delegate as? ChatViewModel {
|
||||
delegate.showSystemMessage("Unable to connect to \(self?.peerNicknames[peerID] ?? peerID): \(ack.reason ?? "Incompatible protocol version")")
|
||||
}
|
||||
}
|
||||
} else {
|
||||
SecurityLogger.log("Version negotiation successful with \(peerID): agreed on version \(ack.agreedVersion)",
|
||||
category: SecurityLogger.session, level: .debug)
|
||||
|
||||
@@ -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