mirror of
https://github.com/maxgoedjen/secretive.git
synced 2025-10-07 19:50:57 +00:00
WIP
This commit is contained in:
parent
940b6b1b86
commit
a0cf74f9dc
@ -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",
|
||||
|
@ -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<Date>?
|
||||
|
||||
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))..<Date(timeIntervalSince1970: TimeInterval(validBefore
|
||||
))
|
||||
let criticalOptionsReader = try dataParser.readNextChunkAsSubReader()
|
||||
let extensionsReader = try dataParser.readNextChunkAsSubReader()
|
||||
_ = try dataParser.readNextChunk() // reserved
|
||||
let signatureKey = try dataParser.readNextChunk()
|
||||
let signature = try dataParser.readNextChunk()
|
||||
|
||||
print(pkw(data: signatureKey), pkw(data: publicKey), pkw(data: signature))
|
||||
|
||||
|
||||
return OpenSSHCertificate(
|
||||
type: type,
|
||||
name: name,
|
||||
data: data,
|
||||
publicKey: publicKey,
|
||||
principals: principals,
|
||||
keyID: keyIdentifier,
|
||||
serial: serialNumber,
|
||||
validityRange: validityRange
|
||||
)
|
||||
} catch {
|
||||
throw .parsingFailed
|
||||
}
|
||||
}
|
||||
|
||||
func pkw(data: Data) -> String {
|
||||
let base64 = Data(SHA256.hash(data: data)).base64EncodedString()
|
||||
let paddingRange = base64.index(base64.endIndex, offsetBy: -2)..<base64.endIndex
|
||||
let cleaned = base64.replacingOccurrences(of: "=", with: "", range: paddingRange)
|
||||
return "SHA256:\(cleaned)"
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -4,6 +4,7 @@ import Foundation
|
||||
final class OpenSSHReader {
|
||||
|
||||
var remaining: Data
|
||||
var done = false
|
||||
|
||||
/// Initialize the reader with an OpenSSH data payload.
|
||||
/// - Parameter data: The data to read.
|
||||
@ -14,22 +15,28 @@ final class OpenSSHReader {
|
||||
/// Reads the next chunk of data from the playload.
|
||||
/// - Returns: The next chunk of data.
|
||||
func readNextChunk(convertEndianness: Bool = true) throws(OpenSSHReaderError) -> 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..<length
|
||||
let dataRange = 0..<Int(length)
|
||||
let ret = Data(remaining[dataRange])
|
||||
remaining.removeSubrange(dataRange)
|
||||
if remaining.isEmpty {
|
||||
done = true
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
func readNextBytes<T>(as: T.Type) throws(OpenSSHReaderError) -> T {
|
||||
func readNextBytes<T: FixedWidthInteger>(as: T.Type, convertEndianness: Bool = true) throws(OpenSSHReaderError) -> T {
|
||||
let size = MemoryLayout<T>.size
|
||||
guard remaining.count >= size else { throw .beyondBounds }
|
||||
let lengthRange = 0..<size
|
||||
let lengthChunk = remaining[lengthRange]
|
||||
remaining.removeSubrange(lengthRange)
|
||||
return unsafe lengthChunk.bytes.unsafeLoad(as: T.self)
|
||||
if remaining.isEmpty {
|
||||
done = true
|
||||
}
|
||||
let value = unsafe lengthChunk.bytes.unsafeLoad(as: T.self)
|
||||
return convertEndianness ? T(value.bigEndian) : T(value)
|
||||
}
|
||||
|
||||
func readNextChunkAsString(convertEndianness: Bool = true) throws(OpenSSHReaderError) -> String {
|
||||
|
@ -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;
|
||||
|
@ -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 {
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user