diff --git a/Sources/Packages/Package.swift b/Sources/Packages/Package.swift index 375a7d3..4dcb725 100644 --- a/Sources/Packages/Package.swift +++ b/Sources/Packages/Package.swift @@ -90,5 +90,6 @@ var swiftSettings: [PackageDescription.SwiftSetting] { [ .swiftLanguageMode(.v6), .treatAllWarnings(as: .error), + .strictMemorySafety() ] } diff --git a/Sources/Packages/Sources/Brief/Updater.swift b/Sources/Packages/Sources/Brief/Updater.swift index 950fa19..12be1ee 100644 --- a/Sources/Packages/Sources/Brief/Updater.swift +++ b/Sources/Packages/Sources/Brief/Updater.swift @@ -47,7 +47,7 @@ import XPCWrappers /// Manually trigger an update check. public func checkForUpdates() async throws { - let session = try XPCTypedSession<[Release], Never>(serviceName: "com.maxgoedjen.Secretive.SecretiveUpdater") + let session = try await XPCTypedSession<[Release], Never>(serviceName: "com.maxgoedjen.Secretive.SecretiveUpdater") await evaluate(releases: try await session.send()) session.complete() } diff --git a/Sources/Packages/Sources/SecretAgentKit/Agent.swift b/Sources/Packages/Sources/SecretAgentKit/Agent.swift index 7fe268d..fbd739e 100644 --- a/Sources/Packages/Sources/SecretAgentKit/Agent.swift +++ b/Sources/Packages/Sources/SecretAgentKit/Agent.swift @@ -84,7 +84,7 @@ extension Agent { } logger.log("Agent enumerated \(count) identities") var countBigEndian = UInt32(count).bigEndian - let countData = Data(bytes: &countBigEndian, count: MemoryLayout.size) + let countData = unsafe Data(bytes: &countBigEndian, count: MemoryLayout.size) return countData + keyData } @@ -150,7 +150,7 @@ extension SSHAgent.Response { var data: Data { var raw = self.rawValue - return Data(bytes: &raw, count: MemoryLayout.size) + return unsafe Data(bytes: &raw, count: MemoryLayout.size) } } diff --git a/Sources/Packages/Sources/SecretAgentKit/FileHandleProtocols.swift b/Sources/Packages/Sources/SecretAgentKit/FileHandleProtocols.swift index f95865e..8baedd6 100644 --- a/Sources/Packages/Sources/SecretAgentKit/FileHandleProtocols.swift +++ b/Sources/Packages/Sources/SecretAgentKit/FileHandleProtocols.swift @@ -5,8 +5,8 @@ extension FileHandle { public var pidOfConnectedProcess: Int32 { let pidPointer = UnsafeMutableRawPointer.allocate(byteCount: MemoryLayout.size, alignment: 1) var len = socklen_t(MemoryLayout.size) - getsockopt(fileDescriptor, SOCK_STREAM, LOCAL_PEERPID, pidPointer, &len) - return pidPointer.load(as: Int32.self) + unsafe getsockopt(fileDescriptor, SOCK_STREAM, LOCAL_PEERPID, pidPointer, &len) + return unsafe pidPointer.load(as: Int32.self) } } diff --git a/Sources/Packages/Sources/SecretAgentKit/OpenSSHReader.swift b/Sources/Packages/Sources/SecretAgentKit/OpenSSHReader.swift index d4d5103..a3508e3 100644 --- a/Sources/Packages/Sources/SecretAgentKit/OpenSSHReader.swift +++ b/Sources/Packages/Sources/SecretAgentKit/OpenSSHReader.swift @@ -29,7 +29,7 @@ final class OpenSSHReader { let lengthRange = 0.. String { diff --git a/Sources/Packages/Sources/SecretAgentKit/SSHAgentInputParser.swift b/Sources/Packages/Sources/SecretAgentKit/SSHAgentInputParser.swift index c6a44f6..6e9a2ee 100644 --- a/Sources/Packages/Sources/SecretAgentKit/SSHAgentInputParser.swift +++ b/Sources/Packages/Sources/SecretAgentKit/SSHAgentInputParser.swift @@ -2,7 +2,7 @@ import Foundation import OSLog import SecretKit -public protocol SSHAgentInputParserProtocol: Sendable { +public protocol SSHAgentInputParserProtocol { func parse(data: Data) async throws -> SSHAgent.Request @@ -21,7 +21,7 @@ public struct SSHAgentInputParser: SSHAgentInputParserProtocol { guard data.count > 4 else { throw .invalidData } - let specifiedLength = (data[0..<4].bytes.unsafeLoad(as: UInt32.self).bigEndian) + 4 + let specifiedLength = unsafe (data[0..<4].bytes.unsafeLoad(as: UInt32.self).bigEndian) + 4 let rawRequestInt = data[4] let remainingDataRange = 5.. kinfo_proc { - var len = MemoryLayout.size + var len = unsafe MemoryLayout.size let infoPointer = UnsafeMutableRawPointer.allocate(byteCount: len, alignment: 1) var name: [Int32] = [CTL_KERN, KERN_PROC, KERN_PROC_PID, pid] - sysctl(&name, UInt32(name.count), infoPointer, &len, nil, 0) - return infoPointer.load(as: kinfo_proc.self) + unsafe sysctl(&name, UInt32(name.count), infoPointer, &len, nil, 0) + return unsafe infoPointer.load(as: kinfo_proc.self) } /// Generates a ``SecretKit.SigningRequestProvenance.Process`` from a provided process ID. @@ -37,18 +37,18 @@ extension SigningRequestTracer { /// - Returns: A ``SecretKit.SigningRequestProvenance.Process`` describing the process. func process(from pid: Int32) -> SigningRequestProvenance.Process { var pidAndNameInfo = self.pidAndNameInfo(from: pid) - let ppid = pidAndNameInfo.kp_eproc.e_ppid != 0 ? pidAndNameInfo.kp_eproc.e_ppid : nil - let procName = withUnsafeMutablePointer(to: &pidAndNameInfo.kp_proc.p_comm.0) { pointer in - String(cString: pointer) + let ppid = unsafe pidAndNameInfo.kp_eproc.e_ppid != 0 ? pidAndNameInfo.kp_eproc.e_ppid : nil + let procName = unsafe withUnsafeMutablePointer(to: &pidAndNameInfo.kp_proc.p_comm.0) { pointer in + unsafe String(cString: pointer) } let pathPointer = UnsafeMutablePointer.allocate(capacity: Int(MAXPATHLEN)) - _ = proc_pidpath(pid, pathPointer, UInt32(MAXPATHLEN)) - let path = String(cString: pathPointer) + _ = unsafe proc_pidpath(pid, pathPointer, UInt32(MAXPATHLEN)) + let path = unsafe String(cString: pathPointer) var secCode: Unmanaged! let flags: SecCSFlags = [.considerExpiration, .enforceRevocationChecks] - SecCodeCreateWithPID(pid, SecCSFlags(), &secCode) - let valid = SecCodeCheckValidity(secCode.takeRetainedValue(), flags, nil) == errSecSuccess + unsafe SecCodeCreateWithPID(pid, SecCSFlags(), &secCode) + let valid = unsafe SecCodeCheckValidity(secCode.takeRetainedValue(), flags, nil) == errSecSuccess return SigningRequestProvenance.Process(pid: pid, processName: procName, appName: appName(for: pid), iconURL: iconURL(for: pid), path: path, validSignature: valid, parentPID: ppid) } diff --git a/Sources/Packages/Sources/SecretAgentKit/SocketController.swift b/Sources/Packages/Sources/SecretAgentKit/SocketController.swift index 892324f..9de2564 100644 --- a/Sources/Packages/Sources/SecretAgentKit/SocketController.swift +++ b/Sources/Packages/Sources/SecretAgentKit/SocketController.swift @@ -134,10 +134,10 @@ private extension SocketPort { convenience init(path: String) { var addr = sockaddr_un() - let length = withUnsafeMutablePointer(to: &addr.sun_path.0) { pointer in - path.withCString { cstring in - let len = strlen(cstring) - strncpy(pointer, cstring, len) + let length = unsafe withUnsafeMutablePointer(to: &addr.sun_path.0) { pointer in + unsafe path.withCString { cstring in + let len = unsafe strlen(cstring) + unsafe strncpy(pointer, cstring, len) return len } } @@ -147,10 +147,7 @@ private extension SocketPort { // This mirrors the SUN_LEN macro format. addr.sun_len = UInt8(MemoryLayout.size - MemoryLayout.size(ofValue: addr.sun_path) + length) - let data = withUnsafePointer(to: &addr) { pointer in - Data(bytes: pointer, count: MemoryLayout.size) - } - + let data = unsafe Data(bytes: &addr, count: MemoryLayout.size) self.init(protocolFamily: AF_UNIX, socketType: SOCK_STREAM, protocol: 0, address: data)! } diff --git a/Sources/Packages/Sources/SecretKit/KeychainTypes.swift b/Sources/Packages/Sources/SecretKit/KeychainTypes.swift index 9d08c65..5574f65 100644 --- a/Sources/Packages/Sources/SecretKit/KeychainTypes.swift +++ b/Sources/Packages/Sources/SecretKit/KeychainTypes.swift @@ -36,12 +36,12 @@ public struct KeychainError: Error { /// A signing-related error. public struct SigningError: Error { /// The underlying error reported by the API, if one was returned. - public let error: SecurityError? + public let error: CFError? /// Initializes a SigningError with an optional SecurityError. /// - Parameter statusCode: The SecurityError, if one is applicable. public init(error: SecurityError?) { - self.error = error + self.error = unsafe error?.takeRetainedValue() } } diff --git a/Sources/Packages/Sources/SecretKit/OpenSSH/LengthAndData.swift b/Sources/Packages/Sources/SecretKit/OpenSSH/LengthAndData.swift index dd276a7..9b29a85 100644 --- a/Sources/Packages/Sources/SecretKit/OpenSSH/LengthAndData.swift +++ b/Sources/Packages/Sources/SecretKit/OpenSSH/LengthAndData.swift @@ -7,7 +7,7 @@ extension Data { package var lengthAndData: Data { let rawLength = UInt32(count) var endian = rawLength.bigEndian - return Data(bytes: &endian, count: MemoryLayout.size) + self + return unsafe Data(bytes: &endian, count: MemoryLayout.size) + self } } diff --git a/Sources/Packages/Sources/SecureEnclaveSecretKit/CryptoKitMigrator.swift b/Sources/Packages/Sources/SecureEnclaveSecretKit/CryptoKitMigrator.swift index a4c69d2..cf6b3c0 100644 --- a/Sources/Packages/Sources/SecureEnclaveSecretKit/CryptoKitMigrator.swift +++ b/Sources/Packages/Sources/SecureEnclaveSecretKit/CryptoKitMigrator.swift @@ -27,7 +27,7 @@ extension SecureEnclave { kSecReturnAttributes: true ]) var privateUntyped: CFTypeRef? - SecItemCopyMatching(privateAttributes, &privateUntyped) + unsafe SecItemCopyMatching(privateAttributes, &privateUntyped) guard let privateTyped = privateUntyped as? [[CFString: Any]] else { return } let migratedPublicKeys = Set(store.secrets.map(\.publicKey)) var migratedAny = false @@ -40,7 +40,7 @@ extension SecureEnclave { } let ref = key[kSecValueRef] as! SecKey let attributes = SecKeyCopyAttributes(ref) as! [CFString: Any] - let tokenObjectID = attributes[Constants.tokenObjectID] as! Data + let tokenObjectID = unsafe attributes[Constants.tokenObjectID] as! Data let accessControl = attributes[kSecAttrAccessControl] as! SecAccessControl // Best guess. let auth: AuthenticationRequirement = String(describing: accessControl) diff --git a/Sources/Packages/Sources/SecureEnclaveSecretKit/PersistentAuthenticationHandler.swift b/Sources/Packages/Sources/SecureEnclaveSecretKit/PersistentAuthenticationHandler.swift index cc10c7f..79d4a30 100644 --- a/Sources/Packages/Sources/SecureEnclaveSecretKit/PersistentAuthenticationHandler.swift +++ b/Sources/Packages/Sources/SecureEnclaveSecretKit/PersistentAuthenticationHandler.swift @@ -21,7 +21,7 @@ extension SecureEnclave { /// - duration: The duration of the authorization context, in seconds. init(secret: Secret, context: LAContext, duration: TimeInterval) { self.secret = secret - self.context = context + unsafe self.context = context let durationInNanoSeconds = Measurement(value: duration, unit: UnitDuration.seconds).converted(to: .nanoseconds).value self.monotonicExpiration = clock_gettime_nsec_np(CLOCK_MONOTONIC) + UInt64(durationInNanoSeconds) } diff --git a/Sources/Packages/Sources/SecureEnclaveSecretKit/SecureEnclaveStore.swift b/Sources/Packages/Sources/SecureEnclaveSecretKit/SecureEnclaveStore.swift index 433759d..5156900 100644 --- a/Sources/Packages/Sources/SecureEnclaveSecretKit/SecureEnclaveStore.swift +++ b/Sources/Packages/Sources/SecureEnclaveSecretKit/SecureEnclaveStore.swift @@ -2,7 +2,7 @@ import Foundation import Observation import Security import CryptoKit -@preconcurrency import LocalAuthentication +import LocalAuthentication import SecretKit import os @@ -40,7 +40,7 @@ extension SecureEnclave { public func sign(data: Data, with secret: Secret, for provenance: SigningRequestProvenance) async throws -> Data { var context: LAContext if let existing = await persistentAuthenticationHandler.existingPersistedAuthenticationContext(secret: secret) { - context = existing.context + context = unsafe existing.context } else { let newContext = LAContext() newContext.localizedReason = String(localized: .authContextRequestSignatureDescription(appName: provenance.origin.displayName, secretName: secret.name)) @@ -57,7 +57,7 @@ extension SecureEnclave { kSecReturnData: true, ]) var untyped: CFTypeRef? - let status = SecItemCopyMatching(queryAttributes, &untyped) + let status = unsafe SecItemCopyMatching(queryAttributes, &untyped) if status != errSecSuccess { throw KeychainError(statusCode: status) } @@ -121,12 +121,12 @@ extension SecureEnclave { fatalError() } let access = - SecAccessControlCreateWithFlags(kCFAllocatorDefault, + unsafe SecAccessControlCreateWithFlags(kCFAllocatorDefault, kSecAttrAccessibleWhenUnlockedThisDeviceOnly, flags, &accessError) - if let error = accessError { - throw error.takeRetainedValue() as Error + if let error = unsafe accessError { + throw unsafe error.takeRetainedValue() as Error } let dataRep: Data let publicKey: Data @@ -214,7 +214,7 @@ extension SecureEnclave.Store { kSecReturnAttributes: true ]) var untyped: CFTypeRef? - SecItemCopyMatching(queryAttributes, &untyped) + unsafe SecItemCopyMatching(queryAttributes, &untyped) guard let typed = untyped as? [[CFString: Any]] else { return } let wrapped: [SecureEnclave.Secret] = typed.compactMap { do { diff --git a/Sources/Packages/Sources/SmartCardSecretKit/SmartCardStore.swift b/Sources/Packages/Sources/SmartCardSecretKit/SmartCardStore.swift index 3cf29b7..a636fde 100644 --- a/Sources/Packages/Sources/SmartCardSecretKit/SmartCardStore.swift +++ b/Sources/Packages/Sources/SmartCardSecretKit/SmartCardStore.swift @@ -1,7 +1,7 @@ import Foundation import Observation import Security -@preconcurrency import CryptoTokenKit +@unsafe @preconcurrency import CryptoTokenKit import LocalAuthentication import SecretKit @@ -70,7 +70,7 @@ extension SmartCard { kSecReturnRef: true ]) var untyped: CFTypeRef? - let status = SecItemCopyMatching(attributes, &untyped) + let status = unsafe SecItemCopyMatching(attributes, &untyped) if status != errSecSuccess { throw KeychainError(statusCode: status) } @@ -80,8 +80,8 @@ extension SmartCard { let key = untypedSafe as! SecKey var signError: SecurityError? guard let algorithm = signatureAlgorithm(for: secret) else { throw UnsupportKeyType() } - guard let signature = SecKeyCreateSignature(key, algorithm, data as CFData, &signError) else { - throw SigningError(error: signError) + guard let signature = unsafe SecKeyCreateSignature(key, algorithm, data as CFData, &signError) else { + throw unsafe SigningError(error: signError) } return signature as Data } @@ -152,7 +152,7 @@ extension SmartCard.Store { kSecReturnAttributes: true ]) var untyped: CFTypeRef? - SecItemCopyMatching(attributes, &untyped) + unsafe SecItemCopyMatching(attributes, &untyped) guard let typed = untyped as? [[CFString: Any]] else { return } let wrapped: [SecretType] = typed.compactMap { let name = $0[kSecAttrLabel] as? String ?? String(localized: .unnamedSecret) diff --git a/Sources/Packages/Sources/XPCWrappers/XPCTypedSession.swift b/Sources/Packages/Sources/XPCWrappers/XPCTypedSession.swift index 5dd87a0..fc73a34 100644 --- a/Sources/Packages/Sources/XPCWrappers/XPCTypedSession.swift +++ b/Sources/Packages/Sources/XPCWrappers/XPCTypedSession.swift @@ -1,21 +1,20 @@ import Foundation -public struct XPCTypedSession: Sendable { +public struct XPCTypedSession: ~Copyable { - private nonisolated(unsafe) let connection: NSXPCConnection - private var proxy: _XPCProtocol + private let connection: NSXPCConnection + private let proxy: _XPCProtocol - public init(serviceName: String, warmup: Bool = false) throws { - connection = NSXPCConnection(serviceName: serviceName) + public init(serviceName: String, warmup: Bool = false) async throws { + let connection = NSXPCConnection(serviceName: serviceName) connection.remoteObjectInterface = NSXPCInterface(with: (any _XPCProtocol).self) connection.setCodeSigningRequirement("anchor apple generic and certificate leaf[subject.OU] = Z72PRUAWF6") connection.resume() guard let proxy = connection.remoteObjectProxy as? _XPCProtocol else { fatalError() } + self.connection = connection self.proxy = proxy if warmup { - Task { [self] in - _ = try? await send() - } + _ = try? await send() } } diff --git a/Sources/SecretAgent/AppDelegate.swift b/Sources/SecretAgent/AppDelegate.swift index 237f595..28bef7e 100644 --- a/Sources/SecretAgent/AppDelegate.swift +++ b/Sources/SecretAgent/AppDelegate.swift @@ -34,7 +34,7 @@ class AppDelegate: NSObject, NSApplicationDelegate { func applicationDidFinishLaunching(_ aNotification: Notification) { logger.debug("SecretAgent finished launching") Task { - let inputParser = try XPCAgentInputParser() + let inputParser = try await XPCAgentInputParser() for await session in socketController.sessions { Task { do { diff --git a/Sources/SecretAgent/XPCInputParser.swift b/Sources/SecretAgent/XPCInputParser.swift index 3253bd9..33d179f 100644 --- a/Sources/SecretAgent/XPCInputParser.swift +++ b/Sources/SecretAgent/XPCInputParser.swift @@ -8,8 +8,8 @@ public final class XPCAgentInputParser: SSHAgentInputParserProtocol { private let session: XPCTypedSession - public init() throws { - session = try XPCTypedSession(serviceName: "com.maxgoedjen.Secretive.SecretAgentInputParser", warmup: true) + public init() async throws { + session = try await XPCTypedSession(serviceName: "com.maxgoedjen.Secretive.SecretAgentInputParser", warmup: true) } public func parse(data: Data) async throws -> SSHAgent.Request { diff --git a/Sources/Secretive.xcodeproj/project.pbxproj b/Sources/Secretive.xcodeproj/project.pbxproj index 00031a7..ab820fa 100644 --- a/Sources/Secretive.xcodeproj/project.pbxproj +++ b/Sources/Secretive.xcodeproj/project.pbxproj @@ -862,6 +862,7 @@ SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_STRICT_MEMORY_SAFETY = YES; SWIFT_TREAT_WARNINGS_AS_ERRORS = YES; SWIFT_VERSION = 6.0; }; @@ -930,6 +931,7 @@ SWIFT_COMPILATION_MODE = wholemodule; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_OPTIMIZATION_LEVEL = "-O"; + SWIFT_STRICT_MEMORY_SAFETY = YES; SWIFT_TREAT_WARNINGS_AS_ERRORS = YES; SWIFT_VERSION = 6.0; }; @@ -1304,6 +1306,7 @@ SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_STRICT_MEMORY_SAFETY = YES; SWIFT_TREAT_WARNINGS_AS_ERRORS = YES; SWIFT_VERSION = 6.0; };