mirror of
https://github.com/permissionlesstech/bitchat.git
synced 2026-07-26 02:05:19 +00:00
Refactor: Testable Keychain and Identity Manager (#584)
* Make static functions instance functions to be testable * Injectable KeychainManager + Mock + updated tests * Remove `pendingActions` from identity manager (dead code) * Remove `getHandshakeState` from identity manager (dead code) * Remove `getAllSocialIdentities` from identity manager (dead code) * Remove `getCryptographicIdentity` from identity manager (dead code) * Remove `resolveIdentity` from identity manager (dead code) * Identity Manager: minor clean up * Put Identity Manager behind a protocol * Remove Keychain and Identity Manager singletons * Tests: include MockKeychain/MockIdentityManager in project; init identityManager in CommandProcessorTests --------- Co-authored-by: jack <jackjackbits@users.noreply.github.com>
This commit is contained in:
@@ -490,6 +490,7 @@ final class NoiseSymmetricState {
|
||||
final class NoiseHandshakeState {
|
||||
private let role: NoiseRole
|
||||
private let pattern: NoisePattern
|
||||
private let keychain: KeychainManagerProtocol
|
||||
private var symmetricState: NoiseSymmetricState
|
||||
|
||||
// Keys
|
||||
@@ -505,9 +506,16 @@ final class NoiseHandshakeState {
|
||||
private var messagePatterns: [[NoiseMessagePattern]] = []
|
||||
private var currentPattern = 0
|
||||
|
||||
init(role: NoiseRole, pattern: NoisePattern, localStaticKey: Curve25519.KeyAgreement.PrivateKey? = nil, remoteStaticKey: Curve25519.KeyAgreement.PublicKey? = nil) {
|
||||
init(
|
||||
role: NoiseRole,
|
||||
pattern: NoisePattern,
|
||||
keychain: KeychainManagerProtocol,
|
||||
localStaticKey: Curve25519.KeyAgreement.PrivateKey? = nil,
|
||||
remoteStaticKey: Curve25519.KeyAgreement.PublicKey? = nil
|
||||
) {
|
||||
self.role = role
|
||||
self.pattern = pattern
|
||||
self.keychain = keychain
|
||||
|
||||
// Initialize static keys
|
||||
if let localKey = localStaticKey {
|
||||
@@ -578,7 +586,7 @@ final class NoiseHandshakeState {
|
||||
var sharedData = shared.withUnsafeBytes { Data($0) }
|
||||
symmetricState.mixKey(sharedData)
|
||||
// Clear sensitive shared secret
|
||||
KeychainManager.secureClear(&sharedData)
|
||||
keychain.secureClear(&sharedData)
|
||||
|
||||
case .es:
|
||||
// DH(ephemeral, static) - direction depends on role
|
||||
@@ -626,7 +634,7 @@ final class NoiseHandshakeState {
|
||||
var sharedData = shared.withUnsafeBytes { Data($0) }
|
||||
symmetricState.mixKey(sharedData)
|
||||
// Clear sensitive shared secret
|
||||
KeychainManager.secureClear(&sharedData)
|
||||
keychain.secureClear(&sharedData)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -714,7 +722,7 @@ final class NoiseHandshakeState {
|
||||
var sharedData = shared.withUnsafeBytes { Data($0) }
|
||||
symmetricState.mixKey(sharedData)
|
||||
// Clear sensitive shared secret
|
||||
KeychainManager.secureClear(&sharedData)
|
||||
keychain.secureClear(&sharedData)
|
||||
} else {
|
||||
guard let localStatic = localStaticPrivate,
|
||||
let remoteEphemeral = remoteEphemeralPublic else {
|
||||
@@ -724,7 +732,7 @@ final class NoiseHandshakeState {
|
||||
var sharedData = shared.withUnsafeBytes { Data($0) }
|
||||
symmetricState.mixKey(sharedData)
|
||||
// Clear sensitive shared secret
|
||||
KeychainManager.secureClear(&sharedData)
|
||||
keychain.secureClear(&sharedData)
|
||||
}
|
||||
|
||||
case .se:
|
||||
@@ -737,7 +745,7 @@ final class NoiseHandshakeState {
|
||||
var sharedData = shared.withUnsafeBytes { Data($0) }
|
||||
symmetricState.mixKey(sharedData)
|
||||
// Clear sensitive shared secret
|
||||
KeychainManager.secureClear(&sharedData)
|
||||
keychain.secureClear(&sharedData)
|
||||
} else {
|
||||
guard let localEphemeral = localEphemeralPrivate,
|
||||
let remoteStatic = remoteStaticPublic else {
|
||||
@@ -747,7 +755,7 @@ final class NoiseHandshakeState {
|
||||
var sharedData = shared.withUnsafeBytes { Data($0) }
|
||||
symmetricState.mixKey(sharedData)
|
||||
// Clear sensitive shared secret
|
||||
KeychainManager.secureClear(&sharedData)
|
||||
keychain.secureClear(&sharedData)
|
||||
}
|
||||
|
||||
case .ss:
|
||||
|
||||
@@ -36,6 +36,7 @@ enum NoiseSessionState: Equatable {
|
||||
class NoiseSession {
|
||||
let peerID: String
|
||||
let role: NoiseRole
|
||||
private let keychain: KeychainManagerProtocol
|
||||
private var state: NoiseSessionState = .uninitialized
|
||||
private var handshakeState: NoiseHandshakeState?
|
||||
private var sendCipher: NoiseCipherState?
|
||||
@@ -52,9 +53,16 @@ class NoiseSession {
|
||||
// Thread safety
|
||||
private let sessionQueue = DispatchQueue(label: "chat.bitchat.noise.session", attributes: .concurrent)
|
||||
|
||||
init(peerID: String, role: NoiseRole, localStaticKey: Curve25519.KeyAgreement.PrivateKey, remoteStaticKey: Curve25519.KeyAgreement.PublicKey? = nil) {
|
||||
init(
|
||||
peerID: String,
|
||||
role: NoiseRole,
|
||||
keychain: KeychainManagerProtocol,
|
||||
localStaticKey: Curve25519.KeyAgreement.PrivateKey,
|
||||
remoteStaticKey: Curve25519.KeyAgreement.PublicKey? = nil
|
||||
) {
|
||||
self.peerID = peerID
|
||||
self.role = role
|
||||
self.keychain = keychain
|
||||
self.localStaticKey = localStaticKey
|
||||
self.remoteStaticPublicKey = remoteStaticKey
|
||||
}
|
||||
@@ -71,6 +79,7 @@ class NoiseSession {
|
||||
handshakeState = NoiseHandshakeState(
|
||||
role: role,
|
||||
pattern: .XX,
|
||||
keychain: keychain,
|
||||
localStaticKey: localStaticKey,
|
||||
remoteStaticKey: nil
|
||||
)
|
||||
@@ -98,6 +107,7 @@ class NoiseSession {
|
||||
handshakeState = NoiseHandshakeState(
|
||||
role: role,
|
||||
pattern: .XX,
|
||||
keychain: keychain,
|
||||
localStaticKey: localStaticKey,
|
||||
remoteStaticKey: nil
|
||||
)
|
||||
@@ -230,13 +240,13 @@ class NoiseSession {
|
||||
// Clear sent handshake messages
|
||||
for i in 0..<sentHandshakeMessages.count {
|
||||
var message = sentHandshakeMessages[i]
|
||||
KeychainManager.secureClear(&message)
|
||||
keychain.secureClear(&message)
|
||||
}
|
||||
sentHandshakeMessages.removeAll()
|
||||
|
||||
// Clear handshake hash
|
||||
if var hash = handshakeHash {
|
||||
KeychainManager.secureClear(&hash)
|
||||
keychain.secureClear(&hash)
|
||||
}
|
||||
handshakeHash = nil
|
||||
|
||||
@@ -252,14 +262,16 @@ class NoiseSession {
|
||||
final class NoiseSessionManager {
|
||||
private var sessions: [String: NoiseSession] = [:]
|
||||
private let localStaticKey: Curve25519.KeyAgreement.PrivateKey
|
||||
private let keychain: KeychainManagerProtocol
|
||||
private let managerQueue = DispatchQueue(label: "chat.bitchat.noise.manager", attributes: .concurrent)
|
||||
|
||||
// Callbacks
|
||||
var onSessionEstablished: ((String, Curve25519.KeyAgreement.PublicKey) -> Void)?
|
||||
var onSessionFailed: ((String, Error) -> Void)?
|
||||
|
||||
init(localStaticKey: Curve25519.KeyAgreement.PrivateKey) {
|
||||
init(localStaticKey: Curve25519.KeyAgreement.PrivateKey, keychain: KeychainManagerProtocol) {
|
||||
self.localStaticKey = localStaticKey
|
||||
self.keychain = keychain
|
||||
}
|
||||
|
||||
// MARK: - Session Management
|
||||
@@ -269,6 +281,7 @@ final class NoiseSessionManager {
|
||||
let session = SecureNoiseSession(
|
||||
peerID: peerID,
|
||||
role: role,
|
||||
keychain: keychain,
|
||||
localStaticKey: localStaticKey
|
||||
)
|
||||
sessions[peerID] = session
|
||||
@@ -320,6 +333,7 @@ final class NoiseSessionManager {
|
||||
let session = SecureNoiseSession(
|
||||
peerID: peerID,
|
||||
role: .initiator,
|
||||
keychain: keychain,
|
||||
localStaticKey: localStaticKey
|
||||
)
|
||||
sessions[peerID] = session
|
||||
@@ -370,6 +384,7 @@ final class NoiseSessionManager {
|
||||
let newSession = SecureNoiseSession(
|
||||
peerID: peerID,
|
||||
role: .responder,
|
||||
keychain: keychain,
|
||||
localStaticKey: localStaticKey
|
||||
)
|
||||
sessions[peerID] = newSession
|
||||
|
||||
Reference in New Issue
Block a user