diff --git a/Sources/Packages/Package.swift b/Sources/Packages/Package.swift index 52f187a..7d7dfee 100644 --- a/Sources/Packages/Package.swift +++ b/Sources/Packages/Package.swift @@ -19,9 +19,12 @@ let package = Package( .library( name: "SmartCardSecretKit", targets: ["SmartCardSecretKit"]), + .library( + name: "CertificateKit", + targets: ["CertificateKit"]), .library( name: "SecretAgentKit", - targets: ["SecretAgentKit", "XPCWrappers"]), + targets: ["SecretAgentKit"]), .library( name: "Brief", targets: ["Brief"]), @@ -58,9 +61,15 @@ let package = Package( resources: [localization], swiftSettings: swiftSettings, ), + .target( + name: "CertificateKit", + dependencies: ["SecretKit"], + resources: [localization], +// swiftSettings: swiftSettings, + ), .target( name: "SecretAgentKit", - dependencies: ["SecretKit", "SSHProtocolKit"], + dependencies: ["SecretKit", "SSHProtocolKit", "CertificateKit"], resources: [localization], swiftSettings: swiftSettings, ), @@ -72,7 +81,7 @@ let package = Package( name: "SSHProtocolKit", dependencies: ["SecretKit"], resources: [localization], - swiftSettings: swiftSettings, +// swiftSettings: swiftSettings, ), .testTarget( name: "SSHProtocolKitTests", diff --git a/Sources/Packages/Sources/SSHProtocolKit/OpenSSHCertificate.swift b/Sources/Packages/Sources/SSHProtocolKit/OpenSSHCertificate.swift index 59fc367..0f3a7c1 100644 --- a/Sources/Packages/Sources/SSHProtocolKit/OpenSSHCertificate.swift +++ b/Sources/Packages/Sources/SSHProtocolKit/OpenSSHCertificate.swift @@ -1,5 +1,6 @@ import Foundation import OSLog +import CryptoKit public struct OpenSSHCertificate: Sendable, Codable, Equatable, Hashable, Identifiable, CustomDebugStringConvertible { @@ -8,6 +9,12 @@ public struct OpenSSHCertificate: Sendable, Codable, Equatable, Hashable, Identi public let name: String? public let data: Data + public var publicKey: Data + public var principals: [String] + public var keyID: String + public var serial: UInt64 + public var validityRange: Range? + public var debugDescription: String { "OpenSSH Certificate \(name, default: "Unnamed"): \(data.formatted(.hex()))" } @@ -54,7 +61,53 @@ public struct OpenSSHCertificateParser: OpenSSHCertificateParserProtocol, Sendab throw OpenSSHCertificateError.parsingFailed } let name = elements.first - return OpenSSHCertificate(type: type, name: name, data: decoded) + do { + let dataParser = OpenSSHReader(data: decoded) + _ = try dataParser.readNextChunkAsString() // Redundant key type + _ = try dataParser.readNextChunk() // Nonce + _ = try dataParser.readNextChunkAsString() // curve + let publicKey = try dataParser.readNextChunk() + let serialNumber = try dataParser.readNextBytes(as: UInt64.self, convertEndianness: true) + let role = try dataParser.readNextBytes(as: UInt32.self, convertEndianness: true) + let keyIdentifier = try dataParser.readNextChunkAsString() + let principalsReader = try dataParser.readNextChunkAsSubReader() + var principals: [String] = [] + while !principalsReader.done { + try principals.append(principalsReader.readNextChunkAsString()) + } + let validAfter = try dataParser.readNextBytes(as: UInt64.self, convertEndianness: true) + let validBefore = try dataParser.readNextBytes(as: UInt64.self, convertEndianness: true) + let validityRange = Date(timeIntervalSince1970: TimeInterval(validAfter)).. String { + let base64 = Data(SHA256.hash(data: data)).base64EncodedString() + let paddingRange = base64.index(base64.endIndex, offsetBy: -2).. Data { - let littleEndianLength = try readNextBytes(as: UInt32.self) - let length = convertEndianness ? Int(littleEndianLength.bigEndian) : Int(littleEndianLength) + let length = try readNextBytes(as: UInt32.self, convertEndianness: convertEndianness) guard remaining.count >= length else { throw .beyondBounds } - let dataRange = 0..(as: T.Type) throws(OpenSSHReaderError) -> T { + func readNextBytes(as: T.Type, convertEndianness: Bool = true) throws(OpenSSHReaderError) -> T { let size = MemoryLayout.size guard remaining.count >= size else { throw .beyondBounds } let lengthRange = 0.. String { diff --git a/Sources/Secretive.xcodeproj/project.pbxproj b/Sources/Secretive.xcodeproj/project.pbxproj index e87d550..3a2381f 100644 --- a/Sources/Secretive.xcodeproj/project.pbxproj +++ b/Sources/Secretive.xcodeproj/project.pbxproj @@ -34,6 +34,7 @@ 50571E0324393C2600F76F6C /* JustUpdatedChecker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50571E0224393C2600F76F6C /* JustUpdatedChecker.swift */; }; 50571E0524393D1500F76F6C /* LaunchAgentController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50571E0424393D1500F76F6C /* LaunchAgentController.swift */; }; 505993512E7E59FB0092CFFA /* XPCCertificateParser.swift in Sources */ = {isa = PBXBuildFile; fileRef = 505993502E7E59F70092CFFA /* XPCCertificateParser.swift */; }; + 505993532E7E70C90092CFFA /* CertificateKit in Frameworks */ = {isa = PBXBuildFile; productRef = 505993522E7E70C90092CFFA /* CertificateKit */; }; 50617D8323FCE48E0099B055 /* App.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50617D8223FCE48E0099B055 /* App.swift */; }; 50617D8523FCE48E0099B055 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50617D8423FCE48E0099B055 /* ContentView.swift */; }; 50617D8A23FCE48E0099B055 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 50617D8923FCE48E0099B055 /* Preview Assets.xcassets */; }; @@ -273,6 +274,7 @@ files = ( 5003EF3B278005E800DF2006 /* SecretKit in Frameworks */, 501421622781262300BBAA70 /* Brief in Frameworks */, + 505993532E7E70C90092CFFA /* CertificateKit in Frameworks */, 5003EF5F2780081600DF2006 /* SecureEnclaveSecretKit in Frameworks */, 5003EF612780081600DF2006 /* SmartCardSecretKit in Frameworks */, ); @@ -560,6 +562,7 @@ 5003EF5E2780081600DF2006 /* SecureEnclaveSecretKit */, 5003EF602780081600DF2006 /* SmartCardSecretKit */, 501421612781262300BBAA70 /* Brief */, + 505993522E7E70C90092CFFA /* CertificateKit */, ); productName = Secretive; productReference = 50617D7F23FCE48E0099B055 /* Secretive.app */; @@ -1785,6 +1788,10 @@ isa = XCSwiftPackageProductDependency; productName = Brief; }; + 505993522E7E70C90092CFFA /* CertificateKit */ = { + isa = XCSwiftPackageProductDependency; + productName = CertificateKit; + }; 50692D2C2E6FDC000043C7BB /* XPCWrappers */ = { isa = XCSwiftPackageProductDependency; productName = XPCWrappers; diff --git a/Sources/Secretive/App.swift b/Sources/Secretive/App.swift index 4c3dff1..5143a78 100644 --- a/Sources/Secretive/App.swift +++ b/Sources/Secretive/App.swift @@ -3,6 +3,7 @@ import SecretKit import SecureEnclaveSecretKit import SmartCardSecretKit import Brief +import CertificateKit @main struct Secretive: App { @@ -14,6 +15,7 @@ struct Secretive: App { WindowGroup { ContentView() .environment(EnvironmentValues._secretStoreList) + .environment(EnvironmentValues._certificateStore) .onReceive(NotificationCenter.default.publisher(for: NSApplication.didBecomeActiveNotification)) { _ in @AppStorage("defaultsHasRunSetup") var hasRunSetup = false guard hasRunSetup else { return } @@ -121,6 +123,8 @@ extension EnvironmentValues { return list }() + @MainActor fileprivate static let _certificateStore: CertificateStore = CertificateStore() + private static let _agentStatusChecker = AgentStatusChecker() @Entry var agentStatusChecker: any AgentStatusCheckerProtocol = _agentStatusChecker private static let _updater: any UpdaterProtocol = { @@ -135,6 +139,10 @@ extension EnvironmentValues { @MainActor var secretStoreList: SecretStoreList { EnvironmentValues._secretStoreList } + + @MainActor var certificateStore: CertificateStore { + EnvironmentValues._certificateStore + } } extension FocusedValues { diff --git a/Sources/Secretive/Views/Views/ContentView.swift b/Sources/Secretive/Views/Views/ContentView.swift index 7c1d102..1f9fe67 100644 --- a/Sources/Secretive/Views/Views/ContentView.swift +++ b/Sources/Secretive/Views/Views/ContentView.swift @@ -14,6 +14,7 @@ struct ContentView: View { @Environment(\.colorScheme) private var colorScheme @Environment(\.openWindow) private var openWindow @Environment(\.secretStoreList) private var storeList + @Environment(\.certificateStore) private var certificateStore @Environment(\.updater) private var updater @Environment(\.agentStatusChecker) private var agentStatusChecker @@ -49,6 +50,12 @@ struct ContentView: View { let data = try! Data(contentsOf: url) let parser = try! await XPCCertificateParser() let cert = try! await parser.parse(data: data) + let secret = storeList.allSecrets.first { secret in + secret.name == cert.name + } + guard let secret = secret ?? storeList.allSecrets.first else { return } + print(cert.data.formatted(.hex())) + certificateStore.saveCertificate(cert.data, for: secret) print(cert) } return true