diff --git a/Package.swift b/Package.swift index 661cb3fd..841857b3 100644 --- a/Package.swift +++ b/Package.swift @@ -15,6 +15,7 @@ let package = Package( ), ], dependencies:[ + .package(path: "localPackages/BitLogger"), .package(url: "https://github.com/21-DOT-DEV/swift-secp256k1", exact: "0.21.1") ], targets: [ @@ -22,6 +23,7 @@ let package = Package( name: "bitchat", dependencies: [ .product(name: "P256K", package: "swift-secp256k1"), + .product(name: "BitLogger", package: "BitLogger"), .target(name: "TorC"), .target(name: "tor-nolzma") ], diff --git a/bitchat.xcodeproj/project.pbxproj b/bitchat.xcodeproj/project.pbxproj index fb2a79ee..cda979db 100644 --- a/bitchat.xcodeproj/project.pbxproj +++ b/bitchat.xcodeproj/project.pbxproj @@ -14,6 +14,8 @@ 17901751FD8010AFC8E750F2 /* bitchatShareExtension.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = 61F92EBA29C47C0FCC482F1F /* bitchatShareExtension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; 3EE336D150427F736F32B56C /* P256K in Frameworks */ = {isa = PBXBuildFile; productRef = B1D9136AA0083366353BFA2F /* P256K */; }; 885BBED78092484A5B069461 /* P256K in Frameworks */ = {isa = PBXBuildFile; productRef = 4EB6BA1B8464F1EA38F4E286 /* P256K */; }; + A6E3E5702E77036A0032EA8A /* BitLogger in Frameworks */ = {isa = PBXBuildFile; productRef = A6E3E56F2E77036A0032EA8A /* BitLogger */; }; + A6E3E5722E7703760032EA8A /* BitLogger in Frameworks */ = {isa = PBXBuildFile; productRef = A6E3E5712E7703760032EA8A /* BitLogger */; }; E0A1B2C3D4E5F6012345678D /* relays/online_relays_gps.csv in Resources */ = {isa = PBXBuildFile; fileRef = E0A1B2C3D4E5F6012345678A /* relays/online_relays_gps.csv */; }; E0A1B2C3D4E5F6012345678E /* relays/online_relays_gps.csv in Resources */ = {isa = PBXBuildFile; fileRef = E0A1B2C3D4E5F6012345678A /* relays/online_relays_gps.csv */; }; /* End PBXBuildFile section */ @@ -133,6 +135,7 @@ 31F6FDADA63050361C14F3A1 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; files = ( + A6E3E5722E7703760032EA8A /* BitLogger in Frameworks */, 048A88812E76FD18000FBCDD /* libz.tbd in Frameworks */, 0481A3A02E744D6300FC845E /* tor-nolzma.xcframework in Frameworks */, 3EE336D150427F736F32B56C /* P256K in Frameworks */, @@ -141,6 +144,7 @@ B5A5CC493FFB3D8966548140 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; files = ( + A6E3E5702E77036A0032EA8A /* BitLogger in Frameworks */, 0481A35D2E6DA18600FC845E /* libz.tbd in Frameworks */, 0481A3A12E744D6300FC845E /* tor-nolzma.xcframework in Frameworks */, 885BBED78092484A5B069461 /* P256K in Frameworks */, @@ -202,6 +206,7 @@ name = bitchat_macOS; packageProductDependencies = ( B1D9136AA0083366353BFA2F /* P256K */, + A6E3E5712E7703760032EA8A /* BitLogger */, ); productName = bitchat_macOS; productReference = 8F3A7C058C2C8E1A06C8CF8B /* bitchat.app */; @@ -278,6 +283,7 @@ name = bitchat_iOS; packageProductDependencies = ( 4EB6BA1B8464F1EA38F4E286 /* P256K */, + A6E3E56F2E77036A0032EA8A /* BitLogger */, ); productName = bitchat_iOS; productReference = 96D0D41CA19EE5A772AA8434 /* bitchat.app */; @@ -303,6 +309,7 @@ minimizedProjectReferenceProxies = 1; packageReferences = ( B8C407587481BBB190741C93 /* XCRemoteSwiftPackageReference "swift-secp256k1" */, + A6E3E56E2E77036A0032EA8A /* XCLocalSwiftPackageReference "localPackages/BitLogger" */, ); preferredProjectObjectVersion = 90; projectDirPath = ""; @@ -853,6 +860,13 @@ }; /* End XCConfigurationList section */ +/* Begin XCLocalSwiftPackageReference section */ + A6E3E56E2E77036A0032EA8A /* XCLocalSwiftPackageReference "localPackages/BitLogger" */ = { + isa = XCLocalSwiftPackageReference; + relativePath = localPackages/BitLogger; + }; +/* End XCLocalSwiftPackageReference section */ + /* Begin XCRemoteSwiftPackageReference section */ B8C407587481BBB190741C93 /* XCRemoteSwiftPackageReference "swift-secp256k1" */ = { isa = XCRemoteSwiftPackageReference; @@ -870,6 +884,14 @@ package = B8C407587481BBB190741C93 /* XCRemoteSwiftPackageReference "swift-secp256k1" */; productName = P256K; }; + A6E3E56F2E77036A0032EA8A /* BitLogger */ = { + isa = XCSwiftPackageProductDependency; + productName = BitLogger; + }; + A6E3E5712E7703760032EA8A /* BitLogger */ = { + isa = XCSwiftPackageProductDependency; + productName = BitLogger; + }; B1D9136AA0083366353BFA2F /* P256K */ = { isa = XCSwiftPackageProductDependency; package = B8C407587481BBB190741C93 /* XCRemoteSwiftPackageReference "swift-secp256k1" */; diff --git a/bitchat/Identity/SecureIdentityStateManager.swift b/bitchat/Identity/SecureIdentityStateManager.swift index 2b0aaab8..339605b7 100644 --- a/bitchat/Identity/SecureIdentityStateManager.swift +++ b/bitchat/Identity/SecureIdentityStateManager.swift @@ -90,6 +90,7 @@ /// - Advanced conflict resolution /// +import BitLogger import Foundation import CryptoKit diff --git a/bitchat/Noise/NoiseHandshakeCoordinator.swift b/bitchat/Noise/NoiseHandshakeCoordinator.swift index 7bd817d6..4c66c93f 100644 --- a/bitchat/Noise/NoiseHandshakeCoordinator.swift +++ b/bitchat/Noise/NoiseHandshakeCoordinator.swift @@ -6,6 +6,7 @@ // For more information, see // +import BitLogger import Foundation /// Coordinates Noise handshakes to prevent race conditions and ensure reliable encryption establishment diff --git a/bitchat/Noise/NoiseProtocol.swift b/bitchat/Noise/NoiseProtocol.swift index 838599fd..916492f8 100644 --- a/bitchat/Noise/NoiseProtocol.swift +++ b/bitchat/Noise/NoiseProtocol.swift @@ -77,6 +77,7 @@ /// - Noise Specification: http://www.noiseprotocol.org/noise.html /// +import BitLogger import Foundation import CryptoKit diff --git a/bitchat/Noise/NoiseSecurityConsiderations.swift b/bitchat/Noise/NoiseSecurityConsiderations.swift index 67ea3639..67402f1c 100644 --- a/bitchat/Noise/NoiseSecurityConsiderations.swift +++ b/bitchat/Noise/NoiseSecurityConsiderations.swift @@ -6,6 +6,7 @@ // For more information, see // +import BitLogger import Foundation import CryptoKit diff --git a/bitchat/Noise/NoiseSession.swift b/bitchat/Noise/NoiseSession.swift index bc8b58d1..0c9f31b9 100644 --- a/bitchat/Noise/NoiseSession.swift +++ b/bitchat/Noise/NoiseSession.swift @@ -6,6 +6,7 @@ // For more information, see // +import BitLogger import Foundation import CryptoKit diff --git a/bitchat/Nostr/GeoRelayDirectory.swift b/bitchat/Nostr/GeoRelayDirectory.swift index fc6c1be6..ba633751 100644 --- a/bitchat/Nostr/GeoRelayDirectory.swift +++ b/bitchat/Nostr/GeoRelayDirectory.swift @@ -1,3 +1,4 @@ +import BitLogger import Foundation /// Directory of online Nostr relays with approximate GPS locations, used for geohash routing. diff --git a/bitchat/Nostr/NostrProtocol.swift b/bitchat/Nostr/NostrProtocol.swift index 374feed5..4510a182 100644 --- a/bitchat/Nostr/NostrProtocol.swift +++ b/bitchat/Nostr/NostrProtocol.swift @@ -1,3 +1,4 @@ +import BitLogger import Foundation import CryptoKit import P256K diff --git a/bitchat/Nostr/NostrRelayManager.swift b/bitchat/Nostr/NostrRelayManager.swift index 8830d684..8ce0e1d6 100644 --- a/bitchat/Nostr/NostrRelayManager.swift +++ b/bitchat/Nostr/NostrRelayManager.swift @@ -1,3 +1,4 @@ +import BitLogger import Foundation import Network import Combine diff --git a/bitchat/Services/BLEService.swift b/bitchat/Services/BLEService.swift index 7f37a841..17787715 100644 --- a/bitchat/Services/BLEService.swift +++ b/bitchat/Services/BLEService.swift @@ -1,3 +1,4 @@ +import BitLogger import Foundation import CoreBluetooth import Combine diff --git a/bitchat/Services/FavoritesPersistenceService.swift b/bitchat/Services/FavoritesPersistenceService.swift index 7e07c10d..21e7be5d 100644 --- a/bitchat/Services/FavoritesPersistenceService.swift +++ b/bitchat/Services/FavoritesPersistenceService.swift @@ -1,3 +1,4 @@ +import BitLogger import Foundation import Combine diff --git a/bitchat/Services/KeychainManager.swift b/bitchat/Services/KeychainManager.swift index ce378daa..5cd70a7f 100644 --- a/bitchat/Services/KeychainManager.swift +++ b/bitchat/Services/KeychainManager.swift @@ -6,6 +6,7 @@ // For more information, see // +import BitLogger import Foundation import Security diff --git a/bitchat/Services/LocationChannelManager.swift b/bitchat/Services/LocationChannelManager.swift index dbcdda57..eda49a47 100644 --- a/bitchat/Services/LocationChannelManager.swift +++ b/bitchat/Services/LocationChannelManager.swift @@ -1,3 +1,4 @@ +import BitLogger import Foundation import Combine diff --git a/bitchat/Services/LocationNotesManager.swift b/bitchat/Services/LocationNotesManager.swift index e337c555..e9f666fa 100644 --- a/bitchat/Services/LocationNotesManager.swift +++ b/bitchat/Services/LocationNotesManager.swift @@ -1,3 +1,4 @@ +import BitLogger import Foundation /// Persistent location notes (Nostr kind 1) scoped to a street-level geohash (precision 7). diff --git a/bitchat/Services/MessageRouter.swift b/bitchat/Services/MessageRouter.swift index e4e4bb8a..7835c8dc 100644 --- a/bitchat/Services/MessageRouter.swift +++ b/bitchat/Services/MessageRouter.swift @@ -1,3 +1,4 @@ +import BitLogger import Foundation /// Routes messages between BLE and Nostr transports diff --git a/bitchat/Services/NoiseEncryptionService.swift b/bitchat/Services/NoiseEncryptionService.swift index a4757988..0163f2c7 100644 --- a/bitchat/Services/NoiseEncryptionService.swift +++ b/bitchat/Services/NoiseEncryptionService.swift @@ -83,6 +83,7 @@ /// - Background queue for CPU-intensive operations /// +import BitLogger import Foundation import CryptoKit diff --git a/bitchat/Services/NostrTransport.swift b/bitchat/Services/NostrTransport.swift index 65c99af1..8aee7ab3 100644 --- a/bitchat/Services/NostrTransport.swift +++ b/bitchat/Services/NostrTransport.swift @@ -1,3 +1,4 @@ +import BitLogger import Foundation import Combine diff --git a/bitchat/Services/PrivateChatManager.swift b/bitchat/Services/PrivateChatManager.swift index 05389c57..825d24f1 100644 --- a/bitchat/Services/PrivateChatManager.swift +++ b/bitchat/Services/PrivateChatManager.swift @@ -6,6 +6,7 @@ // This is free and unencumbered software released into the public domain. // +import BitLogger import Foundation import SwiftUI diff --git a/bitchat/Services/Tor/TorManager.swift b/bitchat/Services/Tor/TorManager.swift index da07937b..b899c7d0 100644 --- a/bitchat/Services/Tor/TorManager.swift +++ b/bitchat/Services/Tor/TorManager.swift @@ -1,3 +1,4 @@ +import BitLogger import Foundation import Network import Darwin diff --git a/bitchat/Services/UnifiedPeerService.swift b/bitchat/Services/UnifiedPeerService.swift index b7082b1c..962c1c19 100644 --- a/bitchat/Services/UnifiedPeerService.swift +++ b/bitchat/Services/UnifiedPeerService.swift @@ -6,6 +6,7 @@ // This is free and unencumbered software released into the public domain. // +import BitLogger import Foundation import Combine import SwiftUI diff --git a/bitchat/ViewModels/ChatViewModel.swift b/bitchat/ViewModels/ChatViewModel.swift index 266e2b2e..6063aa8d 100644 --- a/bitchat/ViewModels/ChatViewModel.swift +++ b/bitchat/ViewModels/ChatViewModel.swift @@ -77,6 +77,7 @@ /// ``` /// +import BitLogger import Foundation import SwiftUI import CryptoKit diff --git a/localPackages/BitLogger/Package.swift b/localPackages/BitLogger/Package.swift new file mode 100644 index 00000000..f0c4a413 --- /dev/null +++ b/localPackages/BitLogger/Package.swift @@ -0,0 +1,23 @@ +// swift-tools-version: 5.9 + +import PackageDescription + +let package = Package( + name: "BitLogger", + platforms: [ + .iOS(.v16), + .macOS(.v13) + ], + products: [ + .library( + name: "BitLogger", + targets: ["BitLogger"] + ) + ], + targets: [ + .target( + name: "BitLogger", + path: "Sources" + ) + ] +) diff --git a/bitchat/Utils/OSLog+Categories.swift b/localPackages/BitLogger/Sources/OSLog+Categories.swift similarity index 94% rename from bitchat/Utils/OSLog+Categories.swift rename to localPackages/BitLogger/Sources/OSLog+Categories.swift index 7172bf82..7ebe4da0 100644 --- a/bitchat/Utils/OSLog+Categories.swift +++ b/localPackages/BitLogger/Sources/OSLog+Categories.swift @@ -1,6 +1,6 @@ // // OSLog+Categories.swift -// bitchat +// BitLogger // // This is free and unencumbered software released into the public domain. // For more information, see @@ -8,7 +8,7 @@ import os.log -extension OSLog { +public extension OSLog { private static let subsystem = "chat.bitchat" static let noise = OSLog(subsystem: subsystem, category: "noise") diff --git a/bitchat/Utils/SecureLogger.swift b/localPackages/BitLogger/Sources/SecureLogger.swift similarity index 96% rename from bitchat/Utils/SecureLogger.swift rename to localPackages/BitLogger/Sources/SecureLogger.swift index 3846a397..3bdd21bc 100644 --- a/bitchat/Utils/SecureLogger.swift +++ b/localPackages/BitLogger/Sources/SecureLogger.swift @@ -1,6 +1,6 @@ // // SecureLogger.swift -// bitchat +// BitLogger // // This is free and unencumbered software released into the public domain. // For more information, see @@ -11,7 +11,7 @@ import os.log /// Centralized security-aware logging framework /// Provides safe logging that filters sensitive data and security events -final class SecureLogger { +public final class SecureLogger { // MARK: - Timestamp Formatter @@ -85,8 +85,50 @@ final class SecureLogger { private static func shouldLog(_ level: LogLevel) -> Bool { return level.order >= minimumLevel.order } +} + +// MARK: - Public Logging Methods + +public extension SecureLogger { - // MARK: - Security Event Types + static func debug(_ message: @autoclosure () -> String, category: OSLog = .noise, + file: String = #file, line: Int = #line, function: String = #function) { + log(message(), category: category, level: .debug, file: file, line: line, function: function) + } + + static func info(_ message: @autoclosure () -> String, category: OSLog = .noise, + file: String = #file, line: Int = #line, function: String = #function) { + log(message(), category: category, level: .info, file: file, line: line, function: function) + } + + static func warning(_ message: @autoclosure () -> String, category: OSLog = .noise, + file: String = #file, line: Int = #line, function: String = #function) { + log(message(), category: category, level: .warning, file: file, line: line, function: function) + } + + static func error(_ message: @autoclosure () -> String, category: OSLog = .noise, + file: String = #file, line: Int = #line, function: String = #function) { + log(message(), category: category, level: .error, file: file, line: line, function: function) + } + + /// Log errors with context + static func error(_ error: Error, context: @autoclosure () -> String, category: OSLog = .noise, + file: String = #file, line: Int = #line, function: String = #function) { + let location = formatLocation(file: file, line: line, function: function) + let sanitized = sanitize(context()) + let errorDesc = sanitize(error.localizedDescription) + + #if DEBUG + os_log("%{public}@ Error in %{public}@: %{public}@", log: category, type: .error, location, sanitized, errorDesc) + #else + os_log("%{private}@ Error in %{private}@: %{private}@", log: category, type: .error, location, sanitized, errorDesc) + #endif + } +} + +// MARK: Security Event Logging + +public extension SecureLogger { enum SecurityEvent { case handshakeStarted(peerID: String) @@ -111,30 +153,6 @@ final class SecureLogger { } } - // MARK: - Public Logging Methods - - static func debug(_ message: @autoclosure () -> String, category: OSLog = .noise, - file: String = #file, line: Int = #line, function: String = #function) { - log(message(), category: category, level: .debug, file: file, line: line, function: function) - } - - static func info(_ message: @autoclosure () -> String, category: OSLog = .noise, - file: String = #file, line: Int = #line, function: String = #function) { - log(message(), category: category, level: .info, file: file, line: line, function: function) - } - - static func warning(_ message: @autoclosure () -> String, category: OSLog = .noise, - file: String = #file, line: Int = #line, function: String = #function) { - log(message(), category: category, level: .warning, file: file, line: line, function: function) - } - - static func error(_ message: @autoclosure () -> String, category: OSLog = .noise, - file: String = #file, line: Int = #line, function: String = #function) { - log(message(), category: category, level: .error, file: file, line: line, function: function) - } - - // MARK: Security Event Logging - static func debug(_ event: SecurityEvent, file: String = #file, line: Int = #line, function: String = #function) { logSecurityEvent(event, level: .debug, file: file, line: line, function: function) } @@ -150,25 +168,11 @@ final class SecureLogger { static func error(_ event: SecurityEvent, file: String = #file, line: Int = #line, function: String = #function) { logSecurityEvent(event, level: .error, file: file, line: line, function: function) } - - /// Log errors with context - static func error(_ error: Error, context: @autoclosure () -> String, category: OSLog = .noise, - file: String = #file, line: Int = #line, function: String = #function) { - let location = formatLocation(file: file, line: line, function: function) - let sanitized = sanitize(context()) - let errorDesc = sanitize(error.localizedDescription) - - #if DEBUG - os_log("%{public}@ Error in %{public}@: %{public}@", log: category, type: .error, location, sanitized, errorDesc) - #else - os_log("%{private}@ Error in %{private}@: %{private}@", log: category, type: .error, location, sanitized, errorDesc) - #endif - } } // MARK: - Convenience Extensions -extension SecureLogger { +public extension SecureLogger { enum KeyOperation: String, CustomStringConvertible { case load @@ -177,7 +181,7 @@ extension SecureLogger { case delete case save - var description: String { rawValue } + public var description: String { rawValue } } /// Log key management operations @@ -290,8 +294,8 @@ private extension SecureLogger { /// Helper to migrate from print statements to SecureLogger /// Usage: Replace print(...) with secureLog(...) -func secureLog(_ items: Any..., separator: String = " ", terminator: String = "\n", - file: String = #file, line: Int = #line, function: String = #function) { +public func secureLog(_ items: Any..., separator: String = " ", terminator: String = "\n", + file: String = #file, line: Int = #line, function: String = #function) { #if DEBUG let message = items.map { String(describing: $0) }.joined(separator: separator) SecureLogger.debug(message, file: file, line: line, function: function)