mirror of
https://github.com/maxgoedjen/secretive.git
synced 2026-04-09 18:57:22 +02:00
Compare commits
13 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0e0f6b2924 | ||
|
|
845b1ec313 | ||
|
|
595de41f03 | ||
|
|
d82f404166 | ||
|
|
a3bfcb316c | ||
|
|
bba4fb9e7c | ||
|
|
32a1a0bca9 | ||
|
|
bb0b6d8dc3 | ||
|
|
c63d87cbec | ||
|
|
fd95771fed | ||
|
|
267fe4bf27 | ||
|
|
a0cf74f9dc | ||
|
|
940b6b1b86 |
2
.github/workflows/codeql.yml
vendored
2
.github/workflows/codeql.yml
vendored
@@ -37,7 +37,7 @@ jobs:
|
||||
build-mode: ${{ matrix.build-mode }}
|
||||
- if: matrix.build-mode == 'manual'
|
||||
name: "Select Xcode"
|
||||
run: sudo xcrun xcode-select -s /Applications/Xcode_26.0.app
|
||||
run: sudo xcrun xcode-select -s /Applications/Xcode_26.2.app
|
||||
- if: matrix.build-mode == 'manual'
|
||||
name: "Build"
|
||||
run: xcrun xcodebuild -project Sources/Secretive.xcodeproj -scheme Secretive CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO
|
||||
|
||||
3
.github/workflows/nightly.yml
vendored
3
.github/workflows/nightly.yml
vendored
@@ -3,7 +3,6 @@ name: Nightly
|
||||
on:
|
||||
schedule:
|
||||
- cron: "0 8 * * *"
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
@@ -26,7 +25,7 @@ jobs:
|
||||
APPLE_API_KEY_ID: ${{ secrets.APPLE_API_KEY_ID }}
|
||||
run: ./.github/scripts/signing.sh
|
||||
- name: Set Environment
|
||||
run: sudo xcrun xcode-select -s /Applications/Xcode_26.0.app
|
||||
run: sudo xcrun xcode-select -s /Applications/Xcode_26.2.app
|
||||
- name: Update Build Number
|
||||
env:
|
||||
RUN_ID: ${{ github.run_id }}
|
||||
|
||||
64
.github/workflows/oneoff.yml
vendored
Normal file
64
.github/workflows/oneoff.yml
vendored
Normal file
@@ -0,0 +1,64 @@
|
||||
name: One-Off Build
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: macos-26
|
||||
permissions:
|
||||
id-token: write
|
||||
contents: write
|
||||
attestations: write
|
||||
actions: read
|
||||
timeout-minutes: 10
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
- name: Setup Signing
|
||||
env:
|
||||
SIGNING_DATA: ${{ secrets.SIGNING_DATA }}
|
||||
SIGNING_PASSWORD: ${{ secrets.SIGNING_PASSWORD }}
|
||||
HOST_PROFILE_DATA: ${{ secrets.HOST_PROFILE_DATA }}
|
||||
AGENT_PROFILE_DATA: ${{ secrets.AGENT_PROFILE_DATA }}
|
||||
APPLE_API_KEY_DATA: ${{ secrets.APPLE_API_KEY_DATA }}
|
||||
APPLE_API_KEY_ID: ${{ secrets.APPLE_API_KEY_ID }}
|
||||
run: ./.github/scripts/signing.sh
|
||||
- name: Set Environment
|
||||
run: sudo xcrun xcode-select -s /Applications/Xcode_26.2.app
|
||||
- name: Update Build Number
|
||||
env:
|
||||
RUN_ID: ${{ github.run_id }}
|
||||
run: |
|
||||
DATE=$(date "+%Y-%m-%d")
|
||||
sed -i '' -e "s/GITHUB_CI_VERSION/0.0.0_oneoff-$DATE/g" Sources/Config/Config.xcconfig
|
||||
sed -i '' -e "s/GITHUB_BUILD_NUMBER/1.$RUN_ID/g" Sources/Config/Config.xcconfig
|
||||
sed -i '' -e "s/GITHUB_BUILD_URL/https:\/\/github.com\/maxgoedjen\/secretive\/actions\/runs\/$RUN_ID/g" Sources/Config/Config.xcconfig
|
||||
- name: Build
|
||||
run: xcrun xcodebuild -project Sources/Secretive.xcodeproj -scheme Secretive -configuration Release -archivePath Archive.xcarchive archive
|
||||
- name: Move to Artifact Folder
|
||||
run: mkdir Artifact; cp -r Archive.xcarchive/Products/Applications/Secretive.app Artifact
|
||||
- name: Upload App to Artifacts
|
||||
id: upload
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: Secretive
|
||||
path: Artifact
|
||||
- name: Download Zipped Artifact
|
||||
id: download
|
||||
env:
|
||||
ZIP_ID: ${{ steps.upload.outputs.artifact-id }}
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: |
|
||||
curl -L -H "Authorization: Bearer $GITHUB_TOKEN" -L \
|
||||
https://api.github.com/repos/maxgoedjen/secretive/actions/artifacts/$ZIP_ID/zip > Secretive.zip
|
||||
- name: Notarize
|
||||
env:
|
||||
APPLE_API_KEY_ID: ${{ secrets.APPLE_API_KEY_ID }}
|
||||
APPLE_API_ISSUER: ${{ secrets.APPLE_API_ISSUER }}
|
||||
run: xcrun notarytool submit --key ~/.private_keys/AuthKey_$APPLE_API_KEY_ID.p8 --key-id $APPLE_API_KEY_ID --issuer $APPLE_API_ISSUER Secretive.zip
|
||||
- name: Attest
|
||||
id: attest
|
||||
uses: actions/attest-build-provenance@v2
|
||||
with:
|
||||
subject-name: "Secretive.zip"
|
||||
subject-digest: sha256:${{ steps.upload.outputs.artifact-digest }}
|
||||
4
.github/workflows/release.yml
vendored
4
.github/workflows/release.yml
vendored
@@ -22,7 +22,7 @@ jobs:
|
||||
APPLE_API_KEY_ID: ${{ secrets.APPLE_API_KEY_ID }}
|
||||
run: ./.github/scripts/signing.sh
|
||||
- name: Set Environment
|
||||
run: sudo xcrun xcode-select -s /Applications/Xcode_26.0.app
|
||||
run: sudo xcrun xcode-select -s /Applications/Xcode_26.2.app
|
||||
- name: Test
|
||||
run: xcrun xcodebuild -project Sources/Secretive.xcodeproj -scheme PackageTests test
|
||||
# SPM doesn't seem to pick up on the tests currently?
|
||||
@@ -47,7 +47,7 @@ jobs:
|
||||
APPLE_API_KEY_ID: ${{ secrets.APPLE_API_KEY_ID }}
|
||||
run: ./.github/scripts/signing.sh
|
||||
- name: Set Environment
|
||||
run: sudo xcrun xcode-select -s /Applications/Xcode_26.0.app
|
||||
run: sudo xcrun xcode-select -s /Applications/Xcode_26.2.app
|
||||
- name: Update Build Number
|
||||
env:
|
||||
TAG_NAME: ${{ github.ref }}
|
||||
|
||||
2
.github/workflows/test.yml
vendored
2
.github/workflows/test.yml
vendored
@@ -10,7 +10,7 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
- name: Set Environment
|
||||
run: sudo xcrun xcode-select -s /Applications/Xcode_26.0.app
|
||||
run: sudo xcrun xcode-select -s /Applications/Xcode_26.2.app
|
||||
- name: Test Main Packages
|
||||
run: xcrun xcodebuild -project Sources/Secretive.xcodeproj -scheme PackageTests test
|
||||
# SPM doesn't seem to pick up on the tests currently?
|
||||
|
||||
@@ -19,15 +19,24 @@ let package = Package(
|
||||
.library(
|
||||
name: "SmartCardSecretKit",
|
||||
targets: ["SmartCardSecretKit"]),
|
||||
.library(
|
||||
name: "CertificateKit",
|
||||
targets: ["CertificateKit"]),
|
||||
.library(
|
||||
name: "SecretAgentKit",
|
||||
targets: ["SecretAgentKit", "XPCWrappers"]),
|
||||
targets: ["SecretAgentKit"]),
|
||||
.library(
|
||||
name: "Common",
|
||||
targets: ["Common"]),
|
||||
.library(
|
||||
name: "Brief",
|
||||
targets: ["Brief"]),
|
||||
.library(
|
||||
name: "XPCWrappers",
|
||||
targets: ["XPCWrappers"]),
|
||||
.library(
|
||||
name: "SSHProtocolKit",
|
||||
targets: ["SSHProtocolKit"]),
|
||||
],
|
||||
dependencies: [
|
||||
],
|
||||
@@ -55,9 +64,15 @@ let package = Package(
|
||||
resources: [localization],
|
||||
swiftSettings: swiftSettings,
|
||||
),
|
||||
.target(
|
||||
name: "CertificateKit",
|
||||
dependencies: ["SecretKit", "SSHProtocolKit"],
|
||||
resources: [localization],
|
||||
// swiftSettings: swiftSettings,
|
||||
),
|
||||
.target(
|
||||
name: "SecretAgentKit",
|
||||
dependencies: ["SecretKit"],
|
||||
dependencies: ["SecretKit", "SSHProtocolKit", "CertificateKit"],
|
||||
resources: [localization],
|
||||
swiftSettings: swiftSettings,
|
||||
),
|
||||
@@ -65,9 +80,25 @@ let package = Package(
|
||||
name: "SecretAgentKitTests",
|
||||
dependencies: ["SecretAgentKit"],
|
||||
),
|
||||
.target(
|
||||
name: "SSHProtocolKit",
|
||||
dependencies: ["SecretKit"],
|
||||
resources: [localization],
|
||||
// swiftSettings: swiftSettings,
|
||||
),
|
||||
.testTarget(
|
||||
name: "SSHProtocolKitTests",
|
||||
dependencies: ["SSHProtocolKit"],
|
||||
),
|
||||
.target(
|
||||
name: "Common",
|
||||
dependencies: [],
|
||||
resources: [localization],
|
||||
swiftSettings: swiftSettings,
|
||||
),
|
||||
.target(
|
||||
name: "Brief",
|
||||
dependencies: ["XPCWrappers"],
|
||||
dependencies: ["XPCWrappers", "SSHProtocolKit"],
|
||||
resources: [localization],
|
||||
swiftSettings: swiftSettings,
|
||||
),
|
||||
|
||||
@@ -75,14 +75,14 @@ extension Updater {
|
||||
.reversed()
|
||||
.filter({ !$0.prerelease })
|
||||
.first(where: { $0.minimumOSVersion <= osVersion }) else { return }
|
||||
guard !userIgnored(release: release) else { return }
|
||||
guard !release.prerelease else { return }
|
||||
let latestVersion = SemVer(release.name)
|
||||
if latestVersion > currentVersion {
|
||||
// guard !userIgnored(release: release) else { return }
|
||||
// guard !release.prerelease else { return }
|
||||
// let latestVersion = SemVer(release.name)
|
||||
// if latestVersion > currentVersion {
|
||||
await MainActor.run {
|
||||
state.update = release
|
||||
}
|
||||
}
|
||||
// }
|
||||
}
|
||||
|
||||
/// Checks whether the user has ignored a release.
|
||||
|
||||
15
Sources/Packages/Sources/CertificateKit/Certificate.swift
Normal file
15
Sources/Packages/Sources/CertificateKit/Certificate.swift
Normal file
@@ -0,0 +1,15 @@
|
||||
import Foundation
|
||||
//import SecretKit
|
||||
|
||||
//extension SecureEnclave {
|
||||
|
||||
public struct Certificate: Sendable, Codable, Equatable, Hashable, Identifiable {
|
||||
|
||||
public var id: Int { hashValue }
|
||||
public var type: String
|
||||
public let name: String?
|
||||
public let data: Data
|
||||
|
||||
}
|
||||
|
||||
//}
|
||||
@@ -0,0 +1,91 @@
|
||||
import Foundation
|
||||
import Security
|
||||
import CryptoTokenKit
|
||||
import CryptoKit
|
||||
import os
|
||||
import SSHProtocolKit
|
||||
|
||||
public struct CertificateKitMigrator {
|
||||
|
||||
private let logger = Logger(subsystem: "com.maxgoedjen.secretive.migration", category: "CertificateKitMigrator")
|
||||
let directory: URL
|
||||
|
||||
/// Initializes a PublicKeyFileStoreController.
|
||||
public init(homeDirectory: URL) {
|
||||
directory = homeDirectory.appending(component: "PublicKeys")
|
||||
}
|
||||
|
||||
@MainActor public func migrate() throws {
|
||||
let fileCerts = try FileManager.default
|
||||
.contentsOfDirectory(atPath: directory.path())
|
||||
.filter { $0.hasSuffix("-cert.pub") }
|
||||
Task {
|
||||
for path in fileCerts {
|
||||
let url = directory.appending(component: path)
|
||||
let data = try! Data(contentsOf: url)
|
||||
// let parser = try! await XPCCertificateParser()
|
||||
let parser = OpenSSHCertificateParser()
|
||||
let cert = try! await parser.parse(data: data)
|
||||
print(cert)
|
||||
// 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)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// let privateAttributes = KeychainDictionary([
|
||||
// kSecClass: kSecClassKey,
|
||||
// kSecAttrKeyType: Constants.oldKeyType,
|
||||
// kSecAttrApplicationTag: SecureEnclave.Store.Constants.keyTag,
|
||||
// kSecAttrKeyClass: kSecAttrKeyClassPrivate,
|
||||
// kSecReturnRef: true,
|
||||
// kSecMatchLimit: kSecMatchLimitAll,
|
||||
// kSecReturnAttributes: true
|
||||
// ])
|
||||
// var privateUntyped: CFTypeRef?
|
||||
// unsafe SecItemCopyMatching(privateAttributes, &privateUntyped)
|
||||
// guard let privateTyped = privateUntyped as? [[CFString: Any]] else { return }
|
||||
// let migratedPublicKeys = Set(store.secrets.map(\.publicKey))
|
||||
// var migratedAny = false
|
||||
// for key in privateTyped {
|
||||
// let name = key[kSecAttrLabel] as? String ?? String(localized: .unnamedSecret)
|
||||
// let id = key[kSecAttrApplicationLabel] as! Data
|
||||
// guard !id.contains(Constants.migrationMagicNumber) else {
|
||||
// logger.log("Skipping \(name), already migrated.")
|
||||
// continue
|
||||
// }
|
||||
// let ref = key[kSecValueRef] as! SecKey
|
||||
// let attributes = SecKeyCopyAttributes(ref) as! [CFString: Any]
|
||||
// let tokenObjectID = unsafe attributes[Constants.tokenObjectID] as! Data
|
||||
// let accessControl = attributes[kSecAttrAccessControl] as! SecAccessControl
|
||||
// // Best guess.
|
||||
// let auth: AuthenticationRequirement = String(describing: accessControl)
|
||||
// .contains("DeviceOwnerAuthentication") ? .presenceRequired : .unknown
|
||||
// do {
|
||||
// let parsed = try CryptoKit.SecureEnclave.P256.Signing.PrivateKey(dataRepresentation: tokenObjectID)
|
||||
// let secret = Secret(id: UUID().uuidString, name: name, publicKey: parsed.publicKey.x963Representation, attributes: Attributes(keyType: .init(algorithm: .ecdsa, size: 256), authentication: auth))
|
||||
// guard !migratedPublicKeys.contains(parsed.publicKey.x963Representation) else {
|
||||
// logger.log("Skipping \(name), public key already present. Marking as migrated.")
|
||||
// markMigrated(secret: secret, oldID: id)
|
||||
// continue
|
||||
// }
|
||||
// logger.log("Migrating \(name).")
|
||||
// try store.saveKey(tokenObjectID, name: name, attributes: secret.attributes)
|
||||
// logger.log("Migrated \(name).")
|
||||
// markMigrated(secret: secret, oldID: id)
|
||||
// migratedAny = true
|
||||
// } catch {
|
||||
// logger.error("Failed to migrate \(name): \(error.localizedDescription).")
|
||||
// }
|
||||
// }
|
||||
// if migratedAny {
|
||||
// store.reloadSecrets()
|
||||
// }
|
||||
}
|
||||
|
||||
}
|
||||
134
Sources/Packages/Sources/CertificateKit/CertificateStore.swift
Normal file
134
Sources/Packages/Sources/CertificateKit/CertificateStore.swift
Normal file
@@ -0,0 +1,134 @@
|
||||
import Foundation
|
||||
import Observation
|
||||
import Security
|
||||
import os
|
||||
import SecretKit
|
||||
|
||||
@Observable public final class CertificateStore {
|
||||
|
||||
@MainActor private var certificates: [Certificate] = []
|
||||
|
||||
/// Initializes a Store.
|
||||
@MainActor public init() {
|
||||
loadCertificates()
|
||||
Task {
|
||||
// for await note in DistributedNotificationCenter.default().notifications(named: .certificateStoreUpdated) {
|
||||
// guard Constants.notificationToken != (note.object as? String) else {
|
||||
// // Don't reload if we're the ones triggering this by reloading.
|
||||
// continue
|
||||
// }
|
||||
// loadCertificates()
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
@MainActor public func reloadCertificates() {
|
||||
let before = certificates
|
||||
certificates.removeAll()
|
||||
loadCertificates()
|
||||
if certificates != before {
|
||||
// NotificationCenter.default.post(name: .certificateStoreReloaded, object: self)
|
||||
// DistributedNotificationCenter.default().postNotificationName(.certificateStoreUpdated, object: Constants.notificationToken, deliverImmediately: true)
|
||||
}
|
||||
}
|
||||
|
||||
@MainActor public func saveCertificate(_ data: Data, for secret: any Secret) {
|
||||
let certificate = SecCertificateCreateWithData(nil, data as CFData)
|
||||
print(certificate as Any)
|
||||
}
|
||||
|
||||
@MainActor public func certificates(for secret: any Secret) -> [Certificate] {
|
||||
[]
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
extension CertificateStore {
|
||||
|
||||
/// Loads all certificates from the store.
|
||||
@MainActor private func loadCertificates() {
|
||||
// let queryAttributes = KeychainDictionary([
|
||||
// kSecClass: Constants.keyClass,
|
||||
// kSecAttrService: Constants.keyTag,
|
||||
// kSecUseDataProtectionKeychain: true,
|
||||
// kSecReturnData: true,
|
||||
// kSecMatchLimit: kSecMatchLimitAll,
|
||||
// kSecReturnAttributes: true
|
||||
// ])
|
||||
// var untyped: CFTypeRef?
|
||||
// unsafe SecItemCopyMatching(queryAttributes, &untyped)
|
||||
// guard let typed = untyped as? [[CFString: Any]] else { return }
|
||||
// let wrapped: [SecureEnclave.Certificates] = typed.compactMap {
|
||||
// do {
|
||||
// let name = $0[kSecAttrLabel] as? String ?? String(localized: "unnamed_certificate")
|
||||
// guard let attributesData = $0[kSecAttrGeneric] as? Data,
|
||||
// let id = $0[kSecAttrAccount] as? String else {
|
||||
// throw MissingAttributesError()
|
||||
// }
|
||||
// let attributes = try JSONDecoder().decode(Attributes.self, from: attributesData)
|
||||
// let keyData = $0[kSecValueData] as! Data
|
||||
// let publicKey: Data
|
||||
// switch attributes.keyType {
|
||||
// case .ecdsa256:
|
||||
// let key = try CryptoKit.SecureEnclave.P256.Signing.PrivateKey(dataRepresentation: keyData)
|
||||
// publicKey = key.publicKey.x963Representation
|
||||
// case .mldsa65:
|
||||
// guard #available(macOS 26.0, *) else { throw UnsupportedAlgorithmError() }
|
||||
// let key = try CryptoKit.SecureEnclave.MLDSA65.PrivateKey(dataRepresentation: keyData)
|
||||
// publicKey = key.publicKey.rawRepresentation
|
||||
// case .mldsa87:
|
||||
// guard #available(macOS 26.0, *) else { throw UnsupportedAlgorithmError() }
|
||||
// let key = try CryptoKit.SecureEnclave.MLDSA87.PrivateKey(dataRepresentation: keyData)
|
||||
// publicKey = key.publicKey.rawRepresentation
|
||||
// default:
|
||||
// throw UnsupportedAlgorithmError()
|
||||
// }
|
||||
// return SecureEnclave.Certificates(id: id, name: name, publicKey: publicKey, attributes: attributes)
|
||||
// } catch {
|
||||
// return nil
|
||||
// }
|
||||
// }
|
||||
// certificates.append(contentsOf: wrapped)
|
||||
}
|
||||
|
||||
/// Saves a public key.
|
||||
/// - Parameters:
|
||||
/// - key: The data representation key to save.
|
||||
/// - name: A user-facing name for the key.
|
||||
/// - attributes: Attributes of the key.
|
||||
/// - Note: Despite the name, the "Data" of the key is _not_ actual key material. This is an opaque data representation that the SEP can manipulate.
|
||||
// @discardableResult
|
||||
// func saveKey(_ key: Data, name: String, attributes: Attributes) throws -> String {
|
||||
// let attributes = try JSONEncoder().encode(attributes)
|
||||
// let id = UUID().uuidString
|
||||
// let keychainAttributes = KeychainDictionary([
|
||||
// kSecClass: Constants.keyClass,
|
||||
// kSecAttrService: Constants.keyTag,
|
||||
// kSecUseDataProtectionKeychain: true,
|
||||
// kSecAttrAccessible: kSecAttrAccessibleWhenUnlockedThisDeviceOnly,
|
||||
// kSecAttrAccount: id,
|
||||
// kSecValueData: key,
|
||||
// kSecAttrLabel: name,
|
||||
// kSecAttrGeneric: attributes
|
||||
// ])
|
||||
// let status = SecItemAdd(keychainAttributes, nil)
|
||||
// if status != errSecSuccess {
|
||||
// throw KeychainError(statusCode: status)
|
||||
// }
|
||||
// return id
|
||||
// }
|
||||
|
||||
}
|
||||
|
||||
extension CertificateStore {
|
||||
|
||||
enum Constants {
|
||||
static let keyClass = kSecClassCertificate as String
|
||||
static let keyTag = Data("com.maxgoedjen.certificateive.certificate".utf8)
|
||||
static let notificationToken = UUID().uuidString
|
||||
}
|
||||
|
||||
struct UnsupportedAlgorithmError: Error {}
|
||||
|
||||
}
|
||||
@@ -2,19 +2,23 @@ import Foundation
|
||||
|
||||
extension URL {
|
||||
|
||||
static var agentHomeURL: URL {
|
||||
public static var agentHomeURL: URL {
|
||||
URL(fileURLWithPath: URL.homeDirectory.path().replacingOccurrences(of: Bundle.hostBundleID, with: Bundle.agentBundleID))
|
||||
}
|
||||
|
||||
static var socketPath: String {
|
||||
public static var socketPath: String {
|
||||
#if DEBUG
|
||||
URL.agentHomeURL.appendingPathComponent("socket-debug.ssh").path()
|
||||
#else
|
||||
URL.agentHomeURL.appendingPathComponent("socket.ssh").path()
|
||||
#endif
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extension String {
|
||||
|
||||
var normalizedPathAndFolder: (String, String) {
|
||||
public var normalizedPathAndFolder: (String, String) {
|
||||
// All foundation-based normalization methods replace this with the container directly.
|
||||
let processedPath = replacingOccurrences(of: "~", with: "/Users/\(NSUserName())")
|
||||
let url = URL(filePath: processedPath)
|
||||
37
Sources/Packages/Sources/SSHProtocolKit/Data+Hex.swift
Normal file
37
Sources/Packages/Sources/SSHProtocolKit/Data+Hex.swift
Normal file
@@ -0,0 +1,37 @@
|
||||
import Foundation
|
||||
import CryptoKit
|
||||
|
||||
public struct HexDataStyle<SequenceType: Sequence>: Hashable, Codable {
|
||||
|
||||
let separator: String
|
||||
|
||||
public init(separator: String) {
|
||||
self.separator = separator
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extension HexDataStyle: FormatStyle where SequenceType.Element == UInt8 {
|
||||
|
||||
public func format(_ value: SequenceType) -> String {
|
||||
value
|
||||
.compactMap { ("0" + String($0, radix: 16, uppercase: false)).suffix(2) }
|
||||
.joined(separator: separator)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extension FormatStyle where Self == HexDataStyle<Data> {
|
||||
|
||||
public static func hex(separator: String = "") -> HexDataStyle<Data> {
|
||||
HexDataStyle(separator: separator)
|
||||
}
|
||||
|
||||
}
|
||||
extension FormatStyle where Self == HexDataStyle<Insecure.MD5Digest> {
|
||||
|
||||
public static func hex(separator: String = ":") -> HexDataStyle<Insecure.MD5Digest> {
|
||||
HexDataStyle(separator: separator)
|
||||
}
|
||||
|
||||
}
|
||||
102
Sources/Packages/Sources/SSHProtocolKit/OpenSSHCertificate.swift
Normal file
102
Sources/Packages/Sources/SSHProtocolKit/OpenSSHCertificate.swift
Normal file
@@ -0,0 +1,102 @@
|
||||
import Foundation
|
||||
import OSLog
|
||||
import CryptoKit
|
||||
|
||||
public struct OpenSSHCertificate: Sendable, Codable, Equatable, Hashable, Identifiable, CustomDebugStringConvertible {
|
||||
|
||||
public var id: Int { hashValue }
|
||||
public var type: CertificateType
|
||||
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()))"
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extension OpenSSHCertificate {
|
||||
|
||||
public enum CertificateType: String, Sendable, Codable {
|
||||
case ecdsa256 = "ecdsa-sha2-nistp256-cert-v01@openssh.com"
|
||||
case ecdsa384 = "ecdsa-sha2-nistp384-cert-v01@openssh.com"
|
||||
case nistp521 = "ecdsa-sha2-nistp521-cert-v01@openssh.com"
|
||||
|
||||
var keyIdentifier: String {
|
||||
rawValue.replacingOccurrences(of: "-cert-v01@openssh.com", with: "")
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public protocol OpenSSHCertificateParserProtocol {
|
||||
func parse(data: Data) async throws -> OpenSSHCertificate
|
||||
}
|
||||
|
||||
public struct OpenSSHCertificateParser: OpenSSHCertificateParserProtocol, Sendable {
|
||||
|
||||
private let logger = Logger(subsystem: "com.maxgoedjen.secretive", category: "OpenSSHCertificateParser")
|
||||
|
||||
public init() {
|
||||
}
|
||||
|
||||
public func parse(data: Data) throws(OpenSSHCertificateError) -> OpenSSHCertificate {
|
||||
let string = String(decoding: data, as: UTF8.self)
|
||||
var elements = string
|
||||
.trimmingCharacters(in: .whitespacesAndNewlines)
|
||||
.components(separatedBy: " ")
|
||||
guard elements.count >= 2 else {
|
||||
throw OpenSSHCertificateError.parsingFailed
|
||||
}
|
||||
let typeString = elements.removeFirst()
|
||||
guard let type = OpenSSHCertificate.CertificateType(rawValue: typeString) else { throw .unsupportedType }
|
||||
let encodedKey = elements.removeFirst()
|
||||
guard let decoded = Data(base64Encoded: encodedKey) else {
|
||||
throw OpenSSHCertificateError.parsingFailed
|
||||
}
|
||||
let name = elements.first
|
||||
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))
|
||||
|
||||
return OpenSSHCertificate(
|
||||
type: type,
|
||||
name: name,
|
||||
data: data,
|
||||
publicKey: publicKey,
|
||||
principals: principals,
|
||||
keyID: keyIdentifier,
|
||||
serial: serialNumber,
|
||||
validityRange: validityRange
|
||||
)
|
||||
} catch {
|
||||
throw .parsingFailed
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public enum OpenSSHCertificateError: Error, Codable {
|
||||
case unsupportedType
|
||||
case parsingFailed
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
import Foundation
|
||||
import CryptoKit
|
||||
import SecretKit
|
||||
|
||||
/// Generates OpenSSH representations of the public key sof secrets.
|
||||
public struct OpenSSHPublicKeyWriter: Sendable {
|
||||
@@ -49,9 +50,7 @@ public struct OpenSSHPublicKeyWriter: Sendable {
|
||||
/// Generates an OpenSSH MD5 fingerprint string.
|
||||
/// - Returns: OpenSSH MD5 fingerprint string.
|
||||
public func openSSHMD5Fingerprint<SecretType: Secret>(secret: SecretType) -> String {
|
||||
Insecure.MD5.hash(data: data(secret: secret))
|
||||
.compactMap { ("0" + String($0, radix: 16, uppercase: false)).suffix(2) }
|
||||
.joined(separator: ":")
|
||||
Insecure.MD5.hash(data: data(secret: secret)).formatted(.hex(separator: ":"))
|
||||
}
|
||||
|
||||
public func comment<SecretType: Secret>(secret: SecretType) -> String {
|
||||
@@ -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 {
|
||||
@@ -1,5 +1,6 @@
|
||||
import Foundation
|
||||
import CryptoKit
|
||||
import SecretKit
|
||||
|
||||
/// Generates OpenSSH representations of Secrets.
|
||||
public struct OpenSSHSignatureWriter: Sendable {
|
||||
@@ -1,5 +1,6 @@
|
||||
import Foundation
|
||||
import OSLog
|
||||
import SecretKit
|
||||
|
||||
/// Controller responsible for writing public keys to disk, so that they're easily accessible by scripts.
|
||||
public final class PublicKeyFileStoreController: Sendable {
|
||||
@@ -74,21 +74,16 @@ extension SSHAgentInputParser {
|
||||
func certificatePublicKeyBlob(from hash: Data) -> Data? {
|
||||
let reader = OpenSSHReader(data: hash)
|
||||
do {
|
||||
let certType = String(decoding: try reader.readNextChunk(), as: UTF8.self)
|
||||
switch certType {
|
||||
case "ecdsa-sha2-nistp256-cert-v01@openssh.com",
|
||||
"ecdsa-sha2-nistp384-cert-v01@openssh.com",
|
||||
"ecdsa-sha2-nistp521-cert-v01@openssh.com":
|
||||
_ = try reader.readNextChunk() // nonce
|
||||
let curveIdentifier = try reader.readNextChunk()
|
||||
let publicKey = try reader.readNextChunk()
|
||||
let openSSHIdentifier = certType.replacingOccurrences(of: "-cert-v01@openssh.com", with: "")
|
||||
return openSSHIdentifier.lengthAndData +
|
||||
curveIdentifier.lengthAndData +
|
||||
let certType = try reader.readNextChunkAsString()
|
||||
guard let certType = OpenSSHCertificate.CertificateType(rawValue: certType) else { return nil }
|
||||
_ = try reader.readNextChunk() // nonce
|
||||
let curveIdentifier = try reader.readNextChunk()
|
||||
let publicKey = try reader.readNextChunk()
|
||||
let openSSHIdentifier = certType.keyIdentifier
|
||||
return openSSHIdentifier.lengthAndData +
|
||||
curveIdentifier.lengthAndData +
|
||||
publicKey.lengthAndData
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
|
||||
} catch {
|
||||
return nil
|
||||
}
|
||||
@@ -3,6 +3,7 @@ import CryptoKit
|
||||
import OSLog
|
||||
import SecretKit
|
||||
import AppKit
|
||||
import SSHProtocolKit
|
||||
|
||||
/// The `Agent` is an implementation of an SSH agent. It manages coordination and access between a socket, traces requests, notifies witnesses and passes requests to stores.
|
||||
public final class Agent: Sendable {
|
||||
@@ -11,7 +12,7 @@ public final class Agent: Sendable {
|
||||
private let witness: SigningWitness?
|
||||
private let publicKeyWriter = OpenSSHPublicKeyWriter()
|
||||
private let signatureWriter = OpenSSHSignatureWriter()
|
||||
private let certificateHandler = OpenSSHCertificateHandler()
|
||||
// private let certificateHandler = OpenSSHCertificateHandler()
|
||||
private let logger = Logger(subsystem: "com.maxgoedjen.secretive.secretagent", category: "Agent")
|
||||
|
||||
/// Initializes an agent with a store list and a witness.
|
||||
@@ -23,7 +24,7 @@ public final class Agent: Sendable {
|
||||
self.storeList = storeList
|
||||
self.witness = witness
|
||||
Task { @MainActor in
|
||||
await certificateHandler.reloadCertificates(for: storeList.allSecrets)
|
||||
// await certificateHandler.reloadCertificates(for: storeList.allSecrets)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,6 +48,7 @@ extension Agent {
|
||||
logger.debug("Agent returned \(SSHAgent.Response.agentSignResponse.debugDescription)")
|
||||
case .unknown(let value):
|
||||
logger.error("Agent received unknown request of type \(value).")
|
||||
throw UnhandledRequestError()
|
||||
default:
|
||||
logger.debug("Agent received valid request of type \(request.debugDescription), but not currently supported.")
|
||||
throw UnhandledRequestError()
|
||||
@@ -66,7 +68,7 @@ extension Agent {
|
||||
/// - Returns: An OpenSSH formatted Data payload listing the identities available for signing operations.
|
||||
func identities() async -> Data {
|
||||
let secrets = await storeList.allSecrets
|
||||
await certificateHandler.reloadCertificates(for: secrets)
|
||||
// await certificateHandler.reloadCertificates(for: secrets)
|
||||
var count = 0
|
||||
var keyData = Data()
|
||||
|
||||
@@ -75,12 +77,12 @@ extension Agent {
|
||||
keyData.append(keyBlob.lengthAndData)
|
||||
keyData.append(publicKeyWriter.comment(secret: secret).lengthAndData)
|
||||
count += 1
|
||||
|
||||
if let (certificateData, name) = try? await certificateHandler.keyBlobAndName(for: secret) {
|
||||
keyData.append(certificateData.lengthAndData)
|
||||
keyData.append(name.lengthAndData)
|
||||
count += 1
|
||||
}
|
||||
|
||||
// if let (certificateData, name) = try? await certificateHandler.keyBlobAndName(for: secret) {
|
||||
// keyData.append(certificateData.lengthAndData)
|
||||
// keyData.append(name.lengthAndData)
|
||||
// count += 1
|
||||
// }
|
||||
}
|
||||
logger.log("Agent enumerated \(count) identities")
|
||||
var countBigEndian = UInt32(count).bigEndian
|
||||
@@ -95,7 +97,7 @@ extension Agent {
|
||||
/// - Returns: An OpenSSH formatted Data payload containing the signed data response.
|
||||
func sign(data: Data, keyBlob: Data, provenance: SigningRequestProvenance) async throws -> Data {
|
||||
guard let (secret, store) = await secret(matching: keyBlob) else {
|
||||
let keyBlobHex = keyBlob.compactMap { ("0" + String($0, radix: 16, uppercase: false)).suffix(2) }.joined()
|
||||
let keyBlobHex = keyBlob.formatted(.hex())
|
||||
logger.debug("Agent did not have a key matching \(keyBlobHex)")
|
||||
throw NoMatchingKeyError()
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import Foundation
|
||||
import OSLog
|
||||
import SecretKit
|
||||
import SSHProtocolKit
|
||||
|
||||
/// Manages storage and lookup for OpenSSH certificates.
|
||||
public actor OpenSSHCertificateHandler: Sendable {
|
||||
@@ -21,9 +22,6 @@ public actor OpenSSHCertificateHandler: Sendable {
|
||||
logger.log("No certificates, short circuiting")
|
||||
return
|
||||
}
|
||||
keyBlobsAndNames = secrets.reduce(into: [:]) { partialResult, next in
|
||||
partialResult[next] = try? loadKeyblobAndName(for: next)
|
||||
}
|
||||
}
|
||||
|
||||
/// Attempts to find an OpenSSH Certificate that corresponds to a ``Secret``
|
||||
@@ -32,57 +30,6 @@ public actor OpenSSHCertificateHandler: Sendable {
|
||||
public func keyBlobAndName<SecretType: Secret>(for secret: SecretType) throws -> (Data, Data)? {
|
||||
keyBlobsAndNames[AnySecret(secret)]
|
||||
}
|
||||
|
||||
/// Attempts to find an OpenSSH Certificate that corresponds to a ``Secret``
|
||||
/// - Parameter secret: The secret to search for a certificate with
|
||||
/// - Returns: A (``Data``, ``Data``) tuple containing the certificate and certificate name, respectively.
|
||||
private func loadKeyblobAndName<SecretType: Secret>(for secret: SecretType) throws -> (Data, Data)? {
|
||||
let certificatePath = publicKeyFileStoreController.sshCertificatePath(for: secret)
|
||||
guard FileManager.default.fileExists(atPath: certificatePath) else {
|
||||
return nil
|
||||
}
|
||||
|
||||
logger.debug("Found certificate for \(secret.name)")
|
||||
let certContent = try String(contentsOfFile:certificatePath, encoding: .utf8)
|
||||
let certElements = certContent.trimmingCharacters(in: .whitespacesAndNewlines).components(separatedBy: " ")
|
||||
|
||||
guard certElements.count >= 2 else {
|
||||
logger.warning("Certificate found for \(secret.name) but failed to load")
|
||||
throw OpenSSHCertificateError.parsingFailed
|
||||
}
|
||||
guard let certDecoded = Data(base64Encoded: certElements[1] as String) else {
|
||||
logger.warning("Certificate found for \(secret.name) but failed to decode base64 key")
|
||||
throw OpenSSHCertificateError.parsingFailed
|
||||
}
|
||||
|
||||
if certElements.count >= 3 {
|
||||
let certName = Data(certElements[2].utf8)
|
||||
return (certDecoded, certName)
|
||||
}
|
||||
let certName = Data(secret.name.utf8)
|
||||
logger.info("Certificate for \(secret.name) does not have a name tag, using secret name instead")
|
||||
return (certDecoded, certName)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extension OpenSSHCertificateHandler {
|
||||
|
||||
enum OpenSSHCertificateError: LocalizedError {
|
||||
case unsupportedType
|
||||
case parsingFailed
|
||||
case doesNotExist
|
||||
|
||||
public var errorDescription: String? {
|
||||
switch self {
|
||||
case .unsupportedType:
|
||||
return "The key type was unsupported"
|
||||
case .parsingFailed:
|
||||
return "Failed to properly parse the SSH certificate"
|
||||
case .doesNotExist:
|
||||
return "Certificate does not exist"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -36,7 +36,7 @@ extension SigningRequestTracer {
|
||||
/// - Parameter pid: The process ID to look up.
|
||||
/// - Returns: A ``SecretKit.SigningRequestProvenance.Process`` describing the process.
|
||||
func process(from pid: Int32) -> SigningRequestProvenance.Process {
|
||||
var pidAndNameInfo = self.pidAndNameInfo(from: pid)
|
||||
var pidAndNameInfo = unsafe self.pidAndNameInfo(from: pid)
|
||||
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)
|
||||
|
||||
@@ -36,16 +36,21 @@ public struct SocketController {
|
||||
logger.debug("Socket controller path is clear")
|
||||
port = SocketPort(path: path)
|
||||
fileHandle = FileHandle(fileDescriptor: port.socket, closeOnDealloc: true)
|
||||
Task { [fileHandle, sessionsContinuation, logger] in
|
||||
for await notification in NotificationCenter.default.notifications(named: .NSFileHandleConnectionAccepted) {
|
||||
Task { @MainActor [fileHandle, sessionsContinuation, logger] in
|
||||
// Create the sequence before triggering the notification to
|
||||
// ensure it will not be missed.
|
||||
let connectionAcceptedNotifications = NotificationCenter.default.notifications(named: .NSFileHandleConnectionAccepted)
|
||||
|
||||
fileHandle.acceptConnectionInBackgroundAndNotify()
|
||||
|
||||
for await notification in connectionAcceptedNotifications {
|
||||
logger.debug("Socket controller accepted connection")
|
||||
guard let new = notification.userInfo?[NSFileHandleNotificationFileHandleItem] as? FileHandle else { continue }
|
||||
let session = Session(fileHandle: new)
|
||||
sessionsContinuation.yield(session)
|
||||
await fileHandle.acceptConnectionInBackgroundAndNotifyOnMainActor()
|
||||
fileHandle.acceptConnectionInBackgroundAndNotify()
|
||||
}
|
||||
}
|
||||
fileHandle.acceptConnectionInBackgroundAndNotify(forModes: [RunLoop.Mode.common])
|
||||
logger.debug("Socket listening at \(path)")
|
||||
}
|
||||
|
||||
@@ -77,8 +82,14 @@ extension SocketController {
|
||||
self.fileHandle = fileHandle
|
||||
provenance = SigningRequestTracer().provenance(from: fileHandle)
|
||||
(messages, messagesContinuation) = AsyncStream.makeStream()
|
||||
Task { [messagesContinuation, logger] in
|
||||
for await _ in NotificationCenter.default.notifications(named: .NSFileHandleDataAvailable, object: fileHandle) {
|
||||
Task { @MainActor [messagesContinuation, logger] in
|
||||
// Create the sequence before triggering the notification to
|
||||
// ensure it will not be missed.
|
||||
let dataAvailableNotifications = NotificationCenter.default.notifications(named: .NSFileHandleDataAvailable, object: fileHandle)
|
||||
|
||||
fileHandle.waitForDataInBackgroundAndNotify()
|
||||
|
||||
for await _ in dataAvailableNotifications {
|
||||
let data = fileHandle.availableData
|
||||
guard !data.isEmpty else {
|
||||
logger.debug("Socket controller received empty data, ending continuation.")
|
||||
@@ -90,16 +101,13 @@ extension SocketController {
|
||||
logger.debug("Socket controller yielded data.")
|
||||
}
|
||||
}
|
||||
Task {
|
||||
await fileHandle.waitForDataInBackgroundAndNotifyOnMainActor()
|
||||
}
|
||||
}
|
||||
|
||||
/// Writes new data to the socket.
|
||||
/// - Parameter data: The data to write.
|
||||
public func write(_ data: Data) async throws {
|
||||
try fileHandle.write(contentsOf: data)
|
||||
await fileHandle.waitForDataInBackgroundAndNotifyOnMainActor()
|
||||
@MainActor public func write(_ data: Data) throws {
|
||||
try fileHandle.write(contentsOf: data)
|
||||
fileHandle.waitForDataInBackgroundAndNotify()
|
||||
}
|
||||
|
||||
/// Closes the socket and cleans up resources.
|
||||
@@ -113,22 +121,6 @@ extension SocketController {
|
||||
|
||||
}
|
||||
|
||||
private extension FileHandle {
|
||||
|
||||
/// Ensures waitForDataInBackgroundAndNotify will be called on the main actor.
|
||||
@MainActor func waitForDataInBackgroundAndNotifyOnMainActor() {
|
||||
waitForDataInBackgroundAndNotify()
|
||||
}
|
||||
|
||||
|
||||
/// Ensures acceptConnectionInBackgroundAndNotify will be called on the main actor.
|
||||
/// - Parameter modes: the runloop modes to use.
|
||||
@MainActor func acceptConnectionInBackgroundAndNotifyOnMainActor(forModes modes: [RunLoop.Mode]? = [RunLoop.Mode.common]) {
|
||||
acceptConnectionInBackgroundAndNotify(forModes: modes)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private extension SocketPort {
|
||||
|
||||
convenience init(path: String) {
|
||||
|
||||
@@ -6,6 +6,9 @@ import SmartCardSecretKit
|
||||
import SecretAgentKit
|
||||
import Brief
|
||||
import Observation
|
||||
import SSHProtocolKit
|
||||
import CertificateKit
|
||||
import Common
|
||||
|
||||
@main
|
||||
class AppDelegate: NSObject, NSApplicationDelegate {
|
||||
@@ -15,6 +18,8 @@ class AppDelegate: NSObject, NSApplicationDelegate {
|
||||
let cryptoKit = SecureEnclave.Store()
|
||||
let migrator = SecureEnclave.CryptoKitMigrator()
|
||||
try? migrator.migrate(to: cryptoKit)
|
||||
let certsMigrator = CertificateKitMigrator(homeDirectory: URL.homeDirectory)
|
||||
try? certsMigrator.migrate()
|
||||
list.add(store: cryptoKit)
|
||||
list.add(store: SmartCard.Store())
|
||||
return list
|
||||
@@ -26,7 +31,7 @@ class AppDelegate: NSObject, NSApplicationDelegate {
|
||||
Agent(storeList: storeList, witness: notifier)
|
||||
}()
|
||||
private lazy var socketController: SocketController = {
|
||||
let path = (NSHomeDirectory() as NSString).appendingPathComponent("socket.ssh") as String
|
||||
let path = URL.socketPath as String
|
||||
return SocketController(path: path)
|
||||
}()
|
||||
private let logger = Logger(subsystem: "com.maxgoedjen.secretive.secretagent", category: "AppDelegate")
|
||||
@@ -41,7 +46,7 @@ class AppDelegate: NSObject, NSApplicationDelegate {
|
||||
for await message in session.messages {
|
||||
let request = try await inputParser.parse(data: message)
|
||||
let agentResponse = await agent.handle(request: request, provenance: session.provenance)
|
||||
try await session.write(agentResponse)
|
||||
try session.write(agentResponse)
|
||||
}
|
||||
} catch {
|
||||
try session.close()
|
||||
|
||||
@@ -2,6 +2,16 @@
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>com.apple.security.hardened-process</key>
|
||||
<true/>
|
||||
<key>com.apple.security.hardened-process.dyld-ro</key>
|
||||
<true/>
|
||||
<key>com.apple.security.hardened-process.enhanced-security-version</key>
|
||||
<integer>1</integer>
|
||||
<key>com.apple.security.hardened-process.hardened-heap</key>
|
||||
<true/>
|
||||
<key>com.apple.security.hardened-process.platform-restrictions</key>
|
||||
<integer>2</integer>
|
||||
<key>com.apple.security.smartcard</key>
|
||||
<true/>
|
||||
<key>keychain-access-groups</key>
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import Foundation
|
||||
import SecretAgentKit
|
||||
import OSLog
|
||||
import SSHProtocolKit
|
||||
import Brief
|
||||
import XPCWrappers
|
||||
import OSLog
|
||||
|
||||
/// Delegates all agent input parsing to an XPC service which wraps OpenSSH
|
||||
public final class XPCAgentInputParser: SSHAgentInputParserProtocol {
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>com.apple.security.hardened-process</key>
|
||||
<true/>
|
||||
<key>com.apple.security.hardened-process.checked-allocations</key>
|
||||
<true/>
|
||||
<key>com.apple.security.hardened-process.checked-allocations.enable-pure-data</key>
|
||||
<true/>
|
||||
<key>com.apple.security.hardened-process.checked-allocations.no-tagged-receive</key>
|
||||
<true/>
|
||||
<key>com.apple.security.hardened-process.dyld-ro</key>
|
||||
<true/>
|
||||
<key>com.apple.security.hardened-process.enhanced-security-version</key>
|
||||
<integer>1</integer>
|
||||
<key>com.apple.security.hardened-process.hardened-heap</key>
|
||||
<true/>
|
||||
<key>com.apple.security.hardened-process.platform-restrictions</key>
|
||||
<integer>2</integer>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -1,7 +1,7 @@
|
||||
import Foundation
|
||||
import OSLog
|
||||
import XPCWrappers
|
||||
import SecretAgentKit
|
||||
import SSHProtocolKit
|
||||
|
||||
final class SecretAgentInputParser: NSObject, XPCProtocol {
|
||||
|
||||
|
||||
@@ -9,7 +9,6 @@
|
||||
/* Begin PBXBuildFile section */
|
||||
2C4A9D2F2636FFD3008CC8E2 /* EditSecretView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C4A9D2E2636FFD3008CC8E2 /* EditSecretView.swift */; };
|
||||
50020BB024064869003D4025 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50020BAF24064869003D4025 /* AppDelegate.swift */; };
|
||||
50033AC327813F1700253856 /* BundleIDs.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50033AC227813F1700253856 /* BundleIDs.swift */; };
|
||||
5003EF3B278005E800DF2006 /* SecretKit in Frameworks */ = {isa = PBXBuildFile; productRef = 5003EF3A278005E800DF2006 /* SecretKit */; };
|
||||
5003EF3D278005F300DF2006 /* Brief in Frameworks */ = {isa = PBXBuildFile; productRef = 5003EF3C278005F300DF2006 /* Brief */; };
|
||||
5003EF3F278005F300DF2006 /* SecretAgentKit in Frameworks */ = {isa = PBXBuildFile; productRef = 5003EF3E278005F300DF2006 /* SecretAgentKit */; };
|
||||
@@ -26,12 +25,13 @@
|
||||
50153E22250DECA300525160 /* SecretListItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50153E21250DECA300525160 /* SecretListItemView.swift */; };
|
||||
501578132E6C0479004A37D0 /* XPCInputParser.swift in Sources */ = {isa = PBXBuildFile; fileRef = 501578122E6C0479004A37D0 /* XPCInputParser.swift */; };
|
||||
5018F54F24064786002EB505 /* Notifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5018F54E24064786002EB505 /* Notifier.swift */; };
|
||||
504788EC2E680DC800B4556F /* URLs.swift in Sources */ = {isa = PBXBuildFile; fileRef = 504788EB2E680DC400B4556F /* URLs.swift */; };
|
||||
504788F22E681F3A00B4556F /* Instructions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 504788F12E681F3A00B4556F /* Instructions.swift */; };
|
||||
504788F42E681F6900B4556F /* ToolConfigurationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 504788F32E681F6900B4556F /* ToolConfigurationView.swift */; };
|
||||
504788F62E68206F00B4556F /* GettingStartedView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 504788F52E68206F00B4556F /* GettingStartedView.swift */; };
|
||||
504789232E697DD300B4556F /* BoxBackgroundStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 504789222E697DD300B4556F /* BoxBackgroundStyle.swift */; };
|
||||
50571E0324393C2600F76F6C /* JustUpdatedChecker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50571E0224393C2600F76F6C /* JustUpdatedChecker.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 */; };
|
||||
@@ -48,7 +48,6 @@
|
||||
50692E5B2E6FF9D20043C7BB /* SecretAgentInputParser.xpc in Embed XPC Services */ = {isa = PBXBuildFile; fileRef = 50692E502E6FF9D20043C7BB /* SecretAgentInputParser.xpc */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
|
||||
50692E682E6FF9E20043C7BB /* main.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50692E632E6FF9E20043C7BB /* main.swift */; };
|
||||
50692E692E6FF9E20043C7BB /* SecretAgentInputParser.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50692E642E6FF9E20043C7BB /* SecretAgentInputParser.swift */; };
|
||||
50692E6C2E6FFA510043C7BB /* SecretAgentKit in Frameworks */ = {isa = PBXBuildFile; productRef = 50692E6B2E6FFA510043C7BB /* SecretAgentKit */; };
|
||||
50692E6D2E6FFA5F0043C7BB /* SecretiveUpdater.xpc in Embed XPC Services */ = {isa = PBXBuildFile; fileRef = 50692D122E6FDB880043C7BB /* SecretiveUpdater.xpc */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
|
||||
50692E702E6FFA6E0043C7BB /* SecretAgentInputParser.xpc in Embed XPC Services */ = {isa = PBXBuildFile; fileRef = 50692E502E6FF9D20043C7BB /* SecretAgentInputParser.xpc */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
|
||||
5079BA0F250F29BF00EA86F4 /* StoreListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5079BA0E250F29BF00EA86F4 /* StoreListView.swift */; };
|
||||
@@ -69,10 +68,19 @@
|
||||
50BDCB762E6450950072D2E7 /* ConfigurationItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50BDCB752E6450950072D2E7 /* ConfigurationItemView.swift */; };
|
||||
50C385A52407A76D00AF2719 /* SecretDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50C385A42407A76D00AF2719 /* SecretDetailView.swift */; };
|
||||
50CF4ABC2E601B0F005588DC /* ActionButtonStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50CF4ABB2E601B0F005588DC /* ActionButtonStyle.swift */; };
|
||||
50E0145C2EDB9CDF00B121F1 /* Common in Frameworks */ = {isa = PBXBuildFile; productRef = 50E0145B2EDB9CDF00B121F1 /* Common */; };
|
||||
50E0145E2EDB9CE400B121F1 /* Common in Frameworks */ = {isa = PBXBuildFile; productRef = 50E0145D2EDB9CE400B121F1 /* Common */; };
|
||||
50E4C4532E73C78C00C73783 /* WindowBackgroundStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50E4C4522E73C78900C73783 /* WindowBackgroundStyle.swift */; };
|
||||
50E4C4C32E7765DF00C73783 /* AboutView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50E4C4C22E7765DF00C73783 /* AboutView.swift */; };
|
||||
50E4C4C82E777E4200C73783 /* AppIcon.icon in Resources */ = {isa = PBXBuildFile; fileRef = 50E4C4C72E777E4200C73783 /* AppIcon.icon */; };
|
||||
50E4C4C92E777E4200C73783 /* AppIcon.icon in Resources */ = {isa = PBXBuildFile; fileRef = 50E4C4C72E777E4200C73783 /* AppIcon.icon */; };
|
||||
50E4C4D92E77C4B300C73783 /* SecretiveCertificateParser.xpc in Embed XPC Services */ = {isa = PBXBuildFile; fileRef = 50E4C4CE2E77C4B300C73783 /* SecretiveCertificateParser.xpc */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
|
||||
50E4C4E82E77C53000C73783 /* main.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50E4C4E32E77C53000C73783 /* main.swift */; };
|
||||
50E4C4E92E77C53000C73783 /* SecretiveCertificateParser.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50E4C4E52E77C53000C73783 /* SecretiveCertificateParser.swift */; };
|
||||
50E4C4ED2E77C55E00C73783 /* XPCWrappers in Frameworks */ = {isa = PBXBuildFile; productRef = 50E4C4EC2E77C55E00C73783 /* XPCWrappers */; };
|
||||
50E4C4EF2E77C8FC00C73783 /* SSHProtocolKit in Frameworks */ = {isa = PBXBuildFile; productRef = 50E4C4EE2E77C8FC00C73783 /* SSHProtocolKit */; };
|
||||
50E4C4F12E77C90300C73783 /* SSHProtocolKit in Frameworks */ = {isa = PBXBuildFile; productRef = 50E4C4F02E77C90300C73783 /* SSHProtocolKit */; };
|
||||
50E4C4F32E77CAC600C73783 /* XPCWrappers in Frameworks */ = {isa = PBXBuildFile; productRef = 50E4C4F22E77CAC600C73783 /* XPCWrappers */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXContainerItemProxy section */
|
||||
@@ -118,6 +126,13 @@
|
||||
remoteGlobalIDString = 50692E4F2E6FF9D20043C7BB;
|
||||
remoteInfo = SecretAgentInputParser;
|
||||
};
|
||||
50E4C4D72E77C4B300C73783 /* PBXContainerItemProxy */ = {
|
||||
isa = PBXContainerItemProxy;
|
||||
containerPortal = 50617D7723FCE48D0099B055 /* Project object */;
|
||||
proxyType = 1;
|
||||
remoteGlobalIDString = 50E4C4CD2E77C4B300C73783;
|
||||
remoteInfo = SecretiveCertificateParser;
|
||||
};
|
||||
/* End PBXContainerItemProxy section */
|
||||
|
||||
/* Begin PBXCopyFilesBuildPhase section */
|
||||
@@ -128,6 +143,7 @@
|
||||
dstSubfolderSpec = 16;
|
||||
files = (
|
||||
50692D1D2E6FDB880043C7BB /* SecretiveUpdater.xpc in Embed XPC Services */,
|
||||
50E4C4D92E77C4B300C73783 /* SecretiveCertificateParser.xpc in Embed XPC Services */,
|
||||
50692E5B2E6FF9D20043C7BB /* SecretAgentInputParser.xpc in Embed XPC Services */,
|
||||
);
|
||||
name = "Embed XPC Services";
|
||||
@@ -180,20 +196,19 @@
|
||||
/* Begin PBXFileReference section */
|
||||
2C4A9D2E2636FFD3008CC8E2 /* EditSecretView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditSecretView.swift; sourceTree = "<group>"; };
|
||||
50020BAF24064869003D4025 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
|
||||
50033AC227813F1700253856 /* BundleIDs.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BundleIDs.swift; sourceTree = "<group>"; };
|
||||
5003EF39278005C800DF2006 /* Packages */ = {isa = PBXFileReference; lastKnownFileType = wrapper; path = Packages; sourceTree = "<group>"; };
|
||||
5008C23D2E525D8200507AC2 /* Localizable.xcstrings */ = {isa = PBXFileReference; lastKnownFileType = text.json.xcstrings; name = Localizable.xcstrings; path = Packages/Resources/Localizable.xcstrings; sourceTree = SOURCE_ROOT; };
|
||||
50153E1F250AFCB200525160 /* UpdateView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UpdateView.swift; sourceTree = "<group>"; };
|
||||
50153E21250DECA300525160 /* SecretListItemView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SecretListItemView.swift; sourceTree = "<group>"; };
|
||||
501578122E6C0479004A37D0 /* XPCInputParser.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XPCInputParser.swift; sourceTree = "<group>"; };
|
||||
5018F54E24064786002EB505 /* Notifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Notifier.swift; sourceTree = "<group>"; };
|
||||
504788EB2E680DC400B4556F /* URLs.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = URLs.swift; sourceTree = "<group>"; };
|
||||
504788F12E681F3A00B4556F /* Instructions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Instructions.swift; sourceTree = "<group>"; };
|
||||
504788F32E681F6900B4556F /* ToolConfigurationView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ToolConfigurationView.swift; sourceTree = "<group>"; };
|
||||
504788F52E68206F00B4556F /* GettingStartedView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GettingStartedView.swift; sourceTree = "<group>"; };
|
||||
504789222E697DD300B4556F /* BoxBackgroundStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BoxBackgroundStyle.swift; sourceTree = "<group>"; };
|
||||
50571E0224393C2600F76F6C /* JustUpdatedChecker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JustUpdatedChecker.swift; sourceTree = "<group>"; };
|
||||
5059933F2E7A3B5B0092CFFA /* en */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = en; path = en.lproj/Main.storyboard; sourceTree = "<group>"; };
|
||||
505993502E7E59F70092CFFA /* XPCCertificateParser.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XPCCertificateParser.swift; sourceTree = "<group>"; };
|
||||
50617D7F23FCE48E0099B055 /* Secretive.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Secretive.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
50617D8223FCE48E0099B055 /* App.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = App.swift; sourceTree = "<group>"; };
|
||||
50617D8423FCE48E0099B055 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = "<group>"; };
|
||||
@@ -239,6 +254,13 @@
|
||||
50E4C4522E73C78900C73783 /* WindowBackgroundStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WindowBackgroundStyle.swift; sourceTree = "<group>"; };
|
||||
50E4C4C22E7765DF00C73783 /* AboutView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AboutView.swift; sourceTree = "<group>"; };
|
||||
50E4C4C72E777E4200C73783 /* AppIcon.icon */ = {isa = PBXFileReference; lastKnownFileType = folder.iconcomposer.icon; path = AppIcon.icon; sourceTree = "<group>"; };
|
||||
50E4C4CE2E77C4B300C73783 /* SecretiveCertificateParser.xpc */ = {isa = PBXFileReference; explicitFileType = "wrapper.xpc-service"; includeInIndex = 0; path = SecretiveCertificateParser.xpc; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
50E4C4E02E77C4EE00C73783 /* SecretAgentInputParser.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = SecretAgentInputParser.entitlements; sourceTree = "<group>"; };
|
||||
50E4C4E12E77C4FA00C73783 /* SecretiveUpdater.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = SecretiveUpdater.entitlements; sourceTree = "<group>"; };
|
||||
50E4C4E22E77C53000C73783 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
50E4C4E32E77C53000C73783 /* main.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = main.swift; sourceTree = "<group>"; };
|
||||
50E4C4E42E77C53000C73783 /* SecretiveCertificateParser.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = SecretiveCertificateParser.entitlements; sourceTree = "<group>"; };
|
||||
50E4C4E52E77C53000C73783 /* SecretiveCertificateParser.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SecretiveCertificateParser.swift; sourceTree = "<group>"; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
@@ -246,8 +268,10 @@
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
50E0145C2EDB9CDF00B121F1 /* Common in Frameworks */,
|
||||
5003EF3B278005E800DF2006 /* SecretKit in Frameworks */,
|
||||
501421622781262300BBAA70 /* Brief in Frameworks */,
|
||||
505993532E7E70C90092CFFA /* CertificateKit in Frameworks */,
|
||||
5003EF5F2780081600DF2006 /* SecureEnclaveSecretKit in Frameworks */,
|
||||
5003EF612780081600DF2006 /* SmartCardSecretKit in Frameworks */,
|
||||
);
|
||||
@@ -266,7 +290,8 @@
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
50692E6C2E6FFA510043C7BB /* SecretAgentKit in Frameworks */,
|
||||
50E4C4F32E77CAC600C73783 /* XPCWrappers in Frameworks */,
|
||||
50E4C4F12E77C90300C73783 /* SSHProtocolKit in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
@@ -279,20 +304,22 @@
|
||||
5003EF652780081B00DF2006 /* SmartCardSecretKit in Frameworks */,
|
||||
5003EF3F278005F300DF2006 /* SecretAgentKit in Frameworks */,
|
||||
5003EF41278005FA00DF2006 /* SecretKit in Frameworks */,
|
||||
50E0145E2EDB9CE400B121F1 /* Common in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
50E4C4CB2E77C4B300C73783 /* Frameworks */ = {
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
50E4C4EF2E77C8FC00C73783 /* SSHProtocolKit in Frameworks */,
|
||||
50E4C4ED2E77C55E00C73783 /* XPCWrappers in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXFrameworksBuildPhase section */
|
||||
|
||||
/* Begin PBXGroup section */
|
||||
50033AC427813F1C00253856 /* Helpers */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
50033AC227813F1700253856 /* BundleIDs.swift */,
|
||||
);
|
||||
path = Helpers;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
504788ED2E681EB200B4556F /* Modifiers */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
@@ -354,6 +381,7 @@
|
||||
508A58AF241E144C0069DC07 /* Config */,
|
||||
50692D272E6FDB8D0043C7BB /* SecretiveUpdater */,
|
||||
50692E662E6FF9E20043C7BB /* SecretAgentInputParser */,
|
||||
50E4C4E72E77C53000C73783 /* SecretiveCertificateParser */,
|
||||
50617D8023FCE48E0099B055 /* Products */,
|
||||
5099A08B240243730062B6F2 /* Frameworks */,
|
||||
);
|
||||
@@ -366,6 +394,7 @@
|
||||
50A3B78A24026B7500D209EA /* SecretAgent.app */,
|
||||
50692D122E6FDB880043C7BB /* SecretiveUpdater.xpc */,
|
||||
50692E502E6FF9D20043C7BB /* SecretAgentInputParser.xpc */,
|
||||
50E4C4CE2E77C4B300C73783 /* SecretiveCertificateParser.xpc */,
|
||||
);
|
||||
name = Products;
|
||||
sourceTree = "<group>";
|
||||
@@ -376,7 +405,6 @@
|
||||
50617D8223FCE48E0099B055 /* App.swift */,
|
||||
508A58B0241ED1C40069DC07 /* Views */,
|
||||
508A58B1241ED1EA0069DC07 /* Controllers */,
|
||||
50033AC427813F1C00253856 /* Helpers */,
|
||||
50617D8E23FCE48E0099B055 /* Info.plist */,
|
||||
508BF28D25B4F005009EFB7E /* InternetAccessPolicy.plist */,
|
||||
50E4C4C72E777E4200C73783 /* AppIcon.icon */,
|
||||
@@ -401,6 +429,7 @@
|
||||
50692D272E6FDB8D0043C7BB /* SecretiveUpdater */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
50E4C4E12E77C4FA00C73783 /* SecretiveUpdater.entitlements */,
|
||||
50692D232E6FDB8D0043C7BB /* Info.plist */,
|
||||
50692BA52E6D5CC90043C7BB /* InternetAccessPolicy.plist */,
|
||||
50692D242E6FDB8D0043C7BB /* main.swift */,
|
||||
@@ -412,6 +441,7 @@
|
||||
50692E662E6FF9E20043C7BB /* SecretAgentInputParser */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
50E4C4E02E77C4EE00C73783 /* SecretAgentInputParser.entitlements */,
|
||||
50692E622E6FF9E20043C7BB /* Info.plist */,
|
||||
50692E632E6FF9E20043C7BB /* main.swift */,
|
||||
50692E642E6FF9E20043C7BB /* SecretAgentInputParser.swift */,
|
||||
@@ -442,10 +472,10 @@
|
||||
508A58B1241ED1EA0069DC07 /* Controllers */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
504788EB2E680DC400B4556F /* URLs.swift */,
|
||||
508A58B2241ED2180069DC07 /* AgentStatusChecker.swift */,
|
||||
5091D2BB25183B830049FD9B /* ApplicationDirectoryController.swift */,
|
||||
50571E0224393C2600F76F6C /* JustUpdatedChecker.swift */,
|
||||
505993502E7E59F70092CFFA /* XPCCertificateParser.swift */,
|
||||
);
|
||||
path = Controllers;
|
||||
sourceTree = "<group>";
|
||||
@@ -480,6 +510,17 @@
|
||||
path = "Preview Content";
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
50E4C4E72E77C53000C73783 /* SecretiveCertificateParser */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
50E4C4E42E77C53000C73783 /* SecretiveCertificateParser.entitlements */,
|
||||
50E4C4E22E77C53000C73783 /* Info.plist */,
|
||||
50E4C4E32E77C53000C73783 /* main.swift */,
|
||||
50E4C4E52E77C53000C73783 /* SecretiveCertificateParser.swift */,
|
||||
);
|
||||
path = SecretiveCertificateParser;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
/* End PBXGroup section */
|
||||
|
||||
/* Begin PBXNativeTarget section */
|
||||
@@ -500,6 +541,7 @@
|
||||
50142167278126B500BBAA70 /* PBXTargetDependency */,
|
||||
50692D1C2E6FDB880043C7BB /* PBXTargetDependency */,
|
||||
50692E5A2E6FF9D20043C7BB /* PBXTargetDependency */,
|
||||
50E4C4D82E77C4B300C73783 /* PBXTargetDependency */,
|
||||
);
|
||||
name = Secretive;
|
||||
packageProductDependencies = (
|
||||
@@ -507,6 +549,8 @@
|
||||
5003EF5E2780081600DF2006 /* SecureEnclaveSecretKit */,
|
||||
5003EF602780081600DF2006 /* SmartCardSecretKit */,
|
||||
501421612781262300BBAA70 /* Brief */,
|
||||
505993522E7E70C90092CFFA /* CertificateKit */,
|
||||
50E0145B2EDB9CDF00B121F1 /* Common */,
|
||||
);
|
||||
productName = Secretive;
|
||||
productReference = 50617D7F23FCE48E0099B055 /* Secretive.app */;
|
||||
@@ -547,7 +591,8 @@
|
||||
);
|
||||
name = SecretAgentInputParser;
|
||||
packageProductDependencies = (
|
||||
50692E6B2E6FFA510043C7BB /* SecretAgentKit */,
|
||||
50E4C4F02E77C90300C73783 /* SSHProtocolKit */,
|
||||
50E4C4F22E77CAC600C73783 /* XPCWrappers */,
|
||||
);
|
||||
productName = SecretAgentInputParser;
|
||||
productReference = 50692E502E6FF9D20043C7BB /* SecretAgentInputParser.xpc */;
|
||||
@@ -577,11 +622,33 @@
|
||||
5003EF40278005FA00DF2006 /* SecretKit */,
|
||||
5003EF622780081B00DF2006 /* SecureEnclaveSecretKit */,
|
||||
5003EF642780081B00DF2006 /* SmartCardSecretKit */,
|
||||
50E0145D2EDB9CE400B121F1 /* Common */,
|
||||
);
|
||||
productName = SecretAgent;
|
||||
productReference = 50A3B78A24026B7500D209EA /* SecretAgent.app */;
|
||||
productType = "com.apple.product-type.application";
|
||||
};
|
||||
50E4C4CD2E77C4B300C73783 /* SecretiveCertificateParser */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = 50E4C4DB2E77C4B300C73783 /* Build configuration list for PBXNativeTarget "SecretiveCertificateParser" */;
|
||||
buildPhases = (
|
||||
50E4C4CA2E77C4B300C73783 /* Sources */,
|
||||
50E4C4CB2E77C4B300C73783 /* Frameworks */,
|
||||
50E4C4CC2E77C4B300C73783 /* Resources */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
dependencies = (
|
||||
);
|
||||
name = SecretiveCertificateParser;
|
||||
packageProductDependencies = (
|
||||
50E4C4EC2E77C55E00C73783 /* XPCWrappers */,
|
||||
50E4C4EE2E77C8FC00C73783 /* SSHProtocolKit */,
|
||||
);
|
||||
productName = SecretiveCertificateParser;
|
||||
productReference = 50E4C4CE2E77C4B300C73783 /* SecretiveCertificateParser.xpc */;
|
||||
productType = "com.apple.product-type.xpc-service";
|
||||
};
|
||||
/* End PBXNativeTarget section */
|
||||
|
||||
/* Begin PBXProject section */
|
||||
@@ -605,6 +672,9 @@
|
||||
50A3B78924026B7500D209EA = {
|
||||
CreatedOnToolsVersion = 11.4;
|
||||
};
|
||||
50E4C4CD2E77C4B300C73783 = {
|
||||
CreatedOnToolsVersion = 26.0;
|
||||
};
|
||||
};
|
||||
};
|
||||
buildConfigurationList = 50617D7A23FCE48D0099B055 /* Build configuration list for PBXProject "Secretive" */;
|
||||
@@ -632,6 +702,7 @@
|
||||
50A3B78924026B7500D209EA /* SecretAgent */,
|
||||
50692D112E6FDB880043C7BB /* SecretiveUpdater */,
|
||||
50692E4F2E6FF9D20043C7BB /* SecretAgentInputParser */,
|
||||
50E4C4CD2E77C4B300C73783 /* SecretiveCertificateParser */,
|
||||
);
|
||||
};
|
||||
/* End PBXProject section */
|
||||
@@ -674,6 +745,13 @@
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
50E4C4CC2E77C4B300C73783 /* Resources */ = {
|
||||
isa = PBXResourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXResourcesBuildPhase section */
|
||||
|
||||
/* Begin PBXSourcesBuildPhase section */
|
||||
@@ -687,17 +765,16 @@
|
||||
2C4A9D2F2636FFD3008CC8E2 /* EditSecretView.swift in Sources */,
|
||||
50E4C4532E73C78C00C73783 /* WindowBackgroundStyle.swift in Sources */,
|
||||
5091D2BC25183B830049FD9B /* ApplicationDirectoryController.swift in Sources */,
|
||||
504788EC2E680DC800B4556F /* URLs.swift in Sources */,
|
||||
504789232E697DD300B4556F /* BoxBackgroundStyle.swift in Sources */,
|
||||
5066A6C22516F303004B5A36 /* SetupView.swift in Sources */,
|
||||
5065E313295517C500E16645 /* ToolbarButtonStyle.swift in Sources */,
|
||||
50617D8523FCE48E0099B055 /* ContentView.swift in Sources */,
|
||||
504788F62E68206F00B4556F /* GettingStartedView.swift in Sources */,
|
||||
50CF4ABC2E601B0F005588DC /* ActionButtonStyle.swift in Sources */,
|
||||
505993512E7E59FB0092CFFA /* XPCCertificateParser.swift in Sources */,
|
||||
50571E0324393C2600F76F6C /* JustUpdatedChecker.swift in Sources */,
|
||||
5079BA0F250F29BF00EA86F4 /* StoreListView.swift in Sources */,
|
||||
50617DD223FCEFA90099B055 /* PreviewStore.swift in Sources */,
|
||||
50033AC327813F1700253856 /* BundleIDs.swift in Sources */,
|
||||
50BDCB722E63BAF20072D2E7 /* AgentStatusView.swift in Sources */,
|
||||
508A58B3241ED2180069DC07 /* AgentStatusChecker.swift in Sources */,
|
||||
50C385A52407A76D00AF2719 /* SecretDetailView.swift in Sources */,
|
||||
@@ -745,6 +822,15 @@
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
50E4C4CA2E77C4B300C73783 /* Sources */ = {
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
50E4C4E82E77C53000C73783 /* main.swift in Sources */,
|
||||
50E4C4E92E77C53000C73783 /* SecretiveCertificateParser.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXSourcesBuildPhase section */
|
||||
|
||||
/* Begin PBXTargetDependency section */
|
||||
@@ -777,6 +863,11 @@
|
||||
target = 50692E4F2E6FF9D20043C7BB /* SecretAgentInputParser */;
|
||||
targetProxy = 50692E712E6FFA6E0043C7BB /* PBXContainerItemProxy */;
|
||||
};
|
||||
50E4C4D82E77C4B300C73783 /* PBXTargetDependency */ = {
|
||||
isa = PBXTargetDependency;
|
||||
target = 50E4C4CD2E77C4B300C73783 /* SecretiveCertificateParser */;
|
||||
targetProxy = 50E4C4D72E77C4B300C73783 /* PBXContainerItemProxy */;
|
||||
};
|
||||
/* End PBXTargetDependency section */
|
||||
|
||||
/* Begin PBXVariantGroup section */
|
||||
@@ -1020,6 +1111,7 @@
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
|
||||
CODE_SIGN_ENTITLEMENTS = SecretiveUpdater/SecretiveUpdater.entitlements;
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
@@ -1027,9 +1119,11 @@
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
DEVELOPMENT_TEAM = Z72PRUAWF6;
|
||||
ENABLE_APP_SANDBOX = YES;
|
||||
ENABLE_ENHANCED_SECURITY = YES;
|
||||
ENABLE_HARDENED_RUNTIME = YES;
|
||||
ENABLE_INCOMING_NETWORK_CONNECTIONS = NO;
|
||||
ENABLE_OUTGOING_NETWORK_CONNECTIONS = YES;
|
||||
ENABLE_POINTER_AUTHENTICATION = YES;
|
||||
ENABLE_RESOURCE_ACCESS_AUDIO_INPUT = NO;
|
||||
ENABLE_RESOURCE_ACCESS_BLUETOOTH = NO;
|
||||
ENABLE_RESOURCE_ACCESS_CALENDARS = NO;
|
||||
@@ -1059,49 +1153,11 @@
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
50692D212E6FDB880043C7BB /* Test */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
ENABLE_APP_SANDBOX = YES;
|
||||
ENABLE_HARDENED_RUNTIME = YES;
|
||||
ENABLE_INCOMING_NETWORK_CONNECTIONS = NO;
|
||||
ENABLE_OUTGOING_NETWORK_CONNECTIONS = YES;
|
||||
ENABLE_RESOURCE_ACCESS_AUDIO_INPUT = NO;
|
||||
ENABLE_RESOURCE_ACCESS_BLUETOOTH = NO;
|
||||
ENABLE_RESOURCE_ACCESS_CALENDARS = NO;
|
||||
ENABLE_RESOURCE_ACCESS_CAMERA = NO;
|
||||
ENABLE_RESOURCE_ACCESS_CONTACTS = NO;
|
||||
ENABLE_RESOURCE_ACCESS_LOCATION = NO;
|
||||
ENABLE_RESOURCE_ACCESS_PRINTING = NO;
|
||||
ENABLE_RESOURCE_ACCESS_USB = NO;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu17;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
INFOPLIST_FILE = SecretiveUpdater/Info.plist;
|
||||
INFOPLIST_KEY_CFBundleDisplayName = SecretiveUpdater;
|
||||
INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2025 Max Goedjen. All rights reserved.";
|
||||
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
|
||||
MACOSX_DEPLOYMENT_TARGET = 14.0;
|
||||
MARKETING_VERSION = 1.0;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.maxgoedjen.Secretive.SecretiveUpdater;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
REGISTER_APP_GROUPS = YES;
|
||||
SKIP_INSTALL = YES;
|
||||
STRING_CATALOG_GENERATE_SYMBOLS = YES;
|
||||
SWIFT_APPROACHABLE_CONCURRENCY = YES;
|
||||
SWIFT_EMIT_LOC_STRINGS = YES;
|
||||
SWIFT_UPCOMING_FEATURE_MEMBER_IMPORT_VISIBILITY = YES;
|
||||
SWIFT_VERSION = 5.0;
|
||||
};
|
||||
name = Test;
|
||||
};
|
||||
50692D222E6FDB880043C7BB /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
|
||||
CODE_SIGN_ENTITLEMENTS = SecretiveUpdater/SecretiveUpdater.entitlements;
|
||||
CODE_SIGN_IDENTITY = "Developer ID Application";
|
||||
CODE_SIGN_STYLE = Manual;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
@@ -1109,9 +1165,11 @@
|
||||
DEVELOPMENT_TEAM = "";
|
||||
"DEVELOPMENT_TEAM[sdk=macosx*]" = Z72PRUAWF6;
|
||||
ENABLE_APP_SANDBOX = YES;
|
||||
ENABLE_ENHANCED_SECURITY = YES;
|
||||
ENABLE_HARDENED_RUNTIME = YES;
|
||||
ENABLE_INCOMING_NETWORK_CONNECTIONS = NO;
|
||||
ENABLE_OUTGOING_NETWORK_CONNECTIONS = YES;
|
||||
ENABLE_POINTER_AUTHENTICATION = YES;
|
||||
ENABLE_RESOURCE_ACCESS_AUDIO_INPUT = NO;
|
||||
ENABLE_RESOURCE_ACCESS_BLUETOOTH = NO;
|
||||
ENABLE_RESOURCE_ACCESS_CALENDARS = NO;
|
||||
@@ -1145,13 +1203,16 @@
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
|
||||
CODE_SIGN_ENTITLEMENTS = SecretAgentInputParser/SecretAgentInputParser.entitlements;
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
DEVELOPMENT_TEAM = Z72PRUAWF6;
|
||||
ENABLE_APP_SANDBOX = YES;
|
||||
ENABLE_ENHANCED_SECURITY = YES;
|
||||
ENABLE_HARDENED_RUNTIME = YES;
|
||||
ENABLE_POINTER_AUTHENTICATION = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu17;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
INFOPLIST_FILE = SecretAgentInputParser/Info.plist;
|
||||
@@ -1173,39 +1234,11 @@
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
50692E5F2E6FF9D20043C7BB /* Test */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
ENABLE_APP_SANDBOX = YES;
|
||||
ENABLE_HARDENED_RUNTIME = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu17;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
INFOPLIST_FILE = SecretAgentInputParser/Info.plist;
|
||||
INFOPLIST_KEY_CFBundleDisplayName = SecretAgentInputParser;
|
||||
INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2025 Max Goedjen. All rights reserved.";
|
||||
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
|
||||
MACOSX_DEPLOYMENT_TARGET = 14.0;
|
||||
MARKETING_VERSION = 1.0;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.maxgoedjen.Secretive.SecretAgentInputParser;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
REGISTER_APP_GROUPS = YES;
|
||||
SKIP_INSTALL = YES;
|
||||
STRING_CATALOG_GENERATE_SYMBOLS = YES;
|
||||
SWIFT_APPROACHABLE_CONCURRENCY = YES;
|
||||
SWIFT_EMIT_LOC_STRINGS = YES;
|
||||
SWIFT_UPCOMING_FEATURE_MEMBER_IMPORT_VISIBILITY = YES;
|
||||
SWIFT_VERSION = 5.0;
|
||||
};
|
||||
name = Test;
|
||||
};
|
||||
50692E602E6FF9D20043C7BB /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
|
||||
CODE_SIGN_ENTITLEMENTS = SecretAgentInputParser/SecretAgentInputParser.entitlements;
|
||||
CODE_SIGN_IDENTITY = "Developer ID Application";
|
||||
CODE_SIGN_STYLE = Manual;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
@@ -1213,7 +1246,9 @@
|
||||
DEVELOPMENT_TEAM = "";
|
||||
"DEVELOPMENT_TEAM[sdk=macosx*]" = Z72PRUAWF6;
|
||||
ENABLE_APP_SANDBOX = YES;
|
||||
ENABLE_ENHANCED_SECURITY = YES;
|
||||
ENABLE_HARDENED_RUNTIME = YES;
|
||||
ENABLE_POINTER_AUTHENTICATION = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu17;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
INFOPLIST_FILE = SecretAgentInputParser/Info.plist;
|
||||
@@ -1235,152 +1270,6 @@
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
508A5914241EF1A00069DC07 /* Test */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = 508A58AB241E121B0069DC07 /* Config.xcconfig */;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
|
||||
CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
|
||||
CLANG_ANALYZER_NONNULL = YES;
|
||||
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CLANG_ENABLE_OBJC_ARC = YES;
|
||||
CLANG_ENABLE_OBJC_WEAK = YES;
|
||||
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
|
||||
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||
CLANG_WARN_COMMA = YES;
|
||||
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
|
||||
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
|
||||
CLANG_WARN_EMPTY_BODY = YES;
|
||||
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||
CLANG_WARN_INFINITE_RECURSION = YES;
|
||||
CLANG_WARN_INT_CONVERSION = YES;
|
||||
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
|
||||
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
|
||||
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
||||
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
COPY_PHASE_STRIP = NO;
|
||||
DEAD_CODE_STRIPPING = YES;
|
||||
DEBUG_INFORMATION_FORMAT = dwarf;
|
||||
ENABLE_ENHANCED_SECURITY = YES;
|
||||
ENABLE_POINTER_AUTHENTICATION = YES;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
ENABLE_TESTABILITY = YES;
|
||||
ENABLE_USER_SCRIPT_SANDBOXING = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu11;
|
||||
GCC_DYNAMIC_NO_PIC = NO;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
GCC_OPTIMIZATION_LEVEL = 0;
|
||||
GCC_PREPROCESSOR_DEFINITIONS = (
|
||||
"DEBUG=1",
|
||||
"$(inherited)",
|
||||
);
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
MACOSX_DEPLOYMENT_TARGET = 15.0;
|
||||
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
|
||||
MTL_FAST_MATH = YES;
|
||||
ONLY_ACTIVE_ARCH = YES;
|
||||
OTHER_SWIFT_FLAGS = "";
|
||||
SDKROOT = macosx;
|
||||
STRING_CATALOG_GENERATE_SYMBOLS = YES;
|
||||
STRIP_INSTALLED_PRODUCT = NO;
|
||||
STRIP_SWIFT_SYMBOLS = NO;
|
||||
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;
|
||||
};
|
||||
name = Test;
|
||||
};
|
||||
508A5915241EF1A00069DC07 /* Test */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CODE_SIGN_ENTITLEMENTS = Secretive/Secretive.entitlements;
|
||||
CODE_SIGN_STYLE = Manual;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
DEAD_CODE_STRIPPING = YES;
|
||||
DEVELOPMENT_ASSET_PATHS = "\"Secretive/Preview Content\"";
|
||||
ENABLE_APP_SANDBOX = YES;
|
||||
ENABLE_ENHANCED_SECURITY = YES;
|
||||
ENABLE_HARDENED_RUNTIME = NO;
|
||||
ENABLE_INCOMING_NETWORK_CONNECTIONS = NO;
|
||||
ENABLE_OUTGOING_NETWORK_CONNECTIONS = NO;
|
||||
ENABLE_POINTER_AUTHENTICATION = YES;
|
||||
ENABLE_PREVIEWS = YES;
|
||||
ENABLE_RESOURCE_ACCESS_AUDIO_INPUT = NO;
|
||||
ENABLE_RESOURCE_ACCESS_BLUETOOTH = NO;
|
||||
ENABLE_RESOURCE_ACCESS_CALENDARS = NO;
|
||||
ENABLE_RESOURCE_ACCESS_CAMERA = NO;
|
||||
ENABLE_RESOURCE_ACCESS_CONTACTS = NO;
|
||||
ENABLE_RESOURCE_ACCESS_LOCATION = NO;
|
||||
ENABLE_RESOURCE_ACCESS_PRINTING = NO;
|
||||
ENABLE_RESOURCE_ACCESS_USB = NO;
|
||||
INFOPLIST_FILE = Secretive/Info.plist;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/../Frameworks",
|
||||
);
|
||||
MACOSX_DEPLOYMENT_TARGET = 14.0;
|
||||
MARKETING_VERSION = 1;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.maxgoedjen.Secretive.Host;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
};
|
||||
name = Test;
|
||||
};
|
||||
508A5917241EF1A00069DC07 /* Test */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CODE_SIGN_STYLE = Manual;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
DEAD_CODE_STRIPPING = YES;
|
||||
DEVELOPMENT_ASSET_PATHS = "\"SecretAgent/Preview Content\"";
|
||||
ENABLE_APP_SANDBOX = YES;
|
||||
ENABLE_HARDENED_RUNTIME = YES;
|
||||
ENABLE_INCOMING_NETWORK_CONNECTIONS = NO;
|
||||
ENABLE_OUTGOING_NETWORK_CONNECTIONS = NO;
|
||||
ENABLE_PREVIEWS = YES;
|
||||
ENABLE_RESOURCE_ACCESS_AUDIO_INPUT = NO;
|
||||
ENABLE_RESOURCE_ACCESS_BLUETOOTH = NO;
|
||||
ENABLE_RESOURCE_ACCESS_CALENDARS = NO;
|
||||
ENABLE_RESOURCE_ACCESS_CAMERA = NO;
|
||||
ENABLE_RESOURCE_ACCESS_CONTACTS = NO;
|
||||
ENABLE_RESOURCE_ACCESS_LOCATION = NO;
|
||||
ENABLE_RESOURCE_ACCESS_PRINTING = NO;
|
||||
ENABLE_RESOURCE_ACCESS_USB = NO;
|
||||
INFOPLIST_FILE = SecretAgent/Info.plist;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/../Frameworks",
|
||||
);
|
||||
MACOSX_DEPLOYMENT_TARGET = 14.0;
|
||||
MARKETING_VERSION = 1;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.maxgoedjen.Secretive.SecretAgent;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
};
|
||||
name = Test;
|
||||
};
|
||||
50A3B79B24026B7600D209EA /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
@@ -1392,9 +1281,11 @@
|
||||
DEVELOPMENT_ASSET_PATHS = "\"SecretAgent/Preview Content\"";
|
||||
DEVELOPMENT_TEAM = Z72PRUAWF6;
|
||||
ENABLE_APP_SANDBOX = YES;
|
||||
ENABLE_ENHANCED_SECURITY = YES;
|
||||
ENABLE_HARDENED_RUNTIME = YES;
|
||||
ENABLE_INCOMING_NETWORK_CONNECTIONS = NO;
|
||||
ENABLE_OUTGOING_NETWORK_CONNECTIONS = NO;
|
||||
ENABLE_POINTER_AUTHENTICATION = YES;
|
||||
ENABLE_PREVIEWS = YES;
|
||||
ENABLE_RESOURCE_ACCESS_AUDIO_INPUT = NO;
|
||||
ENABLE_RESOURCE_ACCESS_BLUETOOTH = NO;
|
||||
@@ -1428,9 +1319,11 @@
|
||||
DEVELOPMENT_ASSET_PATHS = "\"SecretAgent/Preview Content\"";
|
||||
DEVELOPMENT_TEAM = Z72PRUAWF6;
|
||||
ENABLE_APP_SANDBOX = YES;
|
||||
ENABLE_ENHANCED_SECURITY = YES;
|
||||
ENABLE_HARDENED_RUNTIME = YES;
|
||||
ENABLE_INCOMING_NETWORK_CONNECTIONS = NO;
|
||||
ENABLE_OUTGOING_NETWORK_CONNECTIONS = NO;
|
||||
ENABLE_POINTER_AUTHENTICATION = YES;
|
||||
ENABLE_PREVIEWS = YES;
|
||||
ENABLE_RESOURCE_ACCESS_AUDIO_INPUT = NO;
|
||||
ENABLE_RESOURCE_ACCESS_BLUETOOTH = NO;
|
||||
@@ -1453,6 +1346,76 @@
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
50E4C4DC2E77C4B300C73783 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
|
||||
CODE_SIGN_ENTITLEMENTS = SecretiveCertificateParser/SecretiveCertificateParser.entitlements;
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
DEVELOPMENT_TEAM = Z72PRUAWF6;
|
||||
ENABLE_APP_SANDBOX = YES;
|
||||
ENABLE_ENHANCED_SECURITY = YES;
|
||||
ENABLE_HARDENED_RUNTIME = YES;
|
||||
ENABLE_POINTER_AUTHENTICATION = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu17;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
INFOPLIST_FILE = SecretiveCertificateParser/Info.plist;
|
||||
INFOPLIST_KEY_CFBundleDisplayName = SecretiveCertificateParser;
|
||||
INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2025 Max Goedjen. All rights reserved.";
|
||||
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
|
||||
MACOSX_DEPLOYMENT_TARGET = 26.0;
|
||||
MARKETING_VERSION = 1.0;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.maxgoedjen.Secretive.SecretiveCertificateParser;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
REGISTER_APP_GROUPS = YES;
|
||||
SKIP_INSTALL = YES;
|
||||
STRING_CATALOG_GENERATE_SYMBOLS = YES;
|
||||
SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)";
|
||||
SWIFT_APPROACHABLE_CONCURRENCY = YES;
|
||||
SWIFT_EMIT_LOC_STRINGS = YES;
|
||||
SWIFT_UPCOMING_FEATURE_MEMBER_IMPORT_VISIBILITY = YES;
|
||||
SWIFT_VERSION = 5.0;
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
50E4C4DE2E77C4B300C73783 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
|
||||
CODE_SIGN_ENTITLEMENTS = SecretiveCertificateParser/SecretiveCertificateParser.entitlements;
|
||||
CODE_SIGN_IDENTITY = "Developer ID Application";
|
||||
CODE_SIGN_STYLE = Manual;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
DEVELOPMENT_TEAM = "";
|
||||
ENABLE_APP_SANDBOX = YES;
|
||||
ENABLE_ENHANCED_SECURITY = YES;
|
||||
ENABLE_HARDENED_RUNTIME = YES;
|
||||
ENABLE_POINTER_AUTHENTICATION = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu17;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
INFOPLIST_FILE = SecretiveCertificateParser/Info.plist;
|
||||
INFOPLIST_KEY_CFBundleDisplayName = SecretiveCertificateParser;
|
||||
INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2025 Max Goedjen. All rights reserved.";
|
||||
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
|
||||
MACOSX_DEPLOYMENT_TARGET = 26.0;
|
||||
MARKETING_VERSION = 1.0;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.maxgoedjen.Secretive.SecretiveCertificateParser;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
REGISTER_APP_GROUPS = YES;
|
||||
SKIP_INSTALL = YES;
|
||||
STRING_CATALOG_GENERATE_SYMBOLS = YES;
|
||||
SWIFT_APPROACHABLE_CONCURRENCY = YES;
|
||||
SWIFT_EMIT_LOC_STRINGS = YES;
|
||||
SWIFT_UPCOMING_FEATURE_MEMBER_IMPORT_VISIBILITY = YES;
|
||||
SWIFT_VERSION = 5.0;
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
/* End XCBuildConfiguration section */
|
||||
|
||||
/* Begin XCConfigurationList section */
|
||||
@@ -1460,7 +1423,6 @@
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
50617D9B23FCE48E0099B055 /* Debug */,
|
||||
508A5914241EF1A00069DC07 /* Test */,
|
||||
50617D9C23FCE48E0099B055 /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
@@ -1470,7 +1432,6 @@
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
50617D9E23FCE48E0099B055 /* Debug */,
|
||||
508A5915241EF1A00069DC07 /* Test */,
|
||||
50617D9F23FCE48E0099B055 /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
@@ -1480,7 +1441,6 @@
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
50692D202E6FDB880043C7BB /* Debug */,
|
||||
50692D212E6FDB880043C7BB /* Test */,
|
||||
50692D222E6FDB880043C7BB /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
@@ -1490,7 +1450,6 @@
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
50692E5E2E6FF9D20043C7BB /* Debug */,
|
||||
50692E5F2E6FF9D20043C7BB /* Test */,
|
||||
50692E602E6FF9D20043C7BB /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
@@ -1500,12 +1459,20 @@
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
50A3B79B24026B7600D209EA /* Debug */,
|
||||
508A5917241EF1A00069DC07 /* Test */,
|
||||
50A3B79C24026B7600D209EA /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
50E4C4DB2E77C4B300C73783 /* Build configuration list for PBXNativeTarget "SecretiveCertificateParser" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
50E4C4DC2E77C4B300C73783 /* Debug */,
|
||||
50E4C4DE2E77C4B300C73783 /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
/* End XCConfigurationList section */
|
||||
|
||||
/* Begin XCSwiftPackageProductDependency section */
|
||||
@@ -1545,6 +1512,10 @@
|
||||
isa = XCSwiftPackageProductDependency;
|
||||
productName = Brief;
|
||||
};
|
||||
505993522E7E70C90092CFFA /* CertificateKit */ = {
|
||||
isa = XCSwiftPackageProductDependency;
|
||||
productName = CertificateKit;
|
||||
};
|
||||
50692D2C2E6FDC000043C7BB /* XPCWrappers */ = {
|
||||
isa = XCSwiftPackageProductDependency;
|
||||
productName = XPCWrappers;
|
||||
@@ -1553,9 +1524,29 @@
|
||||
isa = XCSwiftPackageProductDependency;
|
||||
productName = Brief;
|
||||
};
|
||||
50692E6B2E6FFA510043C7BB /* SecretAgentKit */ = {
|
||||
50E0145B2EDB9CDF00B121F1 /* Common */ = {
|
||||
isa = XCSwiftPackageProductDependency;
|
||||
productName = SecretAgentKit;
|
||||
productName = Common;
|
||||
};
|
||||
50E0145D2EDB9CE400B121F1 /* Common */ = {
|
||||
isa = XCSwiftPackageProductDependency;
|
||||
productName = Common;
|
||||
};
|
||||
50E4C4EC2E77C55E00C73783 /* XPCWrappers */ = {
|
||||
isa = XCSwiftPackageProductDependency;
|
||||
productName = XPCWrappers;
|
||||
};
|
||||
50E4C4EE2E77C8FC00C73783 /* SSHProtocolKit */ = {
|
||||
isa = XCSwiftPackageProductDependency;
|
||||
productName = SSHProtocolKit;
|
||||
};
|
||||
50E4C4F02E77C90300C73783 /* SSHProtocolKit */ = {
|
||||
isa = XCSwiftPackageProductDependency;
|
||||
productName = SSHProtocolKit;
|
||||
};
|
||||
50E4C4F22E77CAC600C73783 /* XPCWrappers */ = {
|
||||
isa = XCSwiftPackageProductDependency;
|
||||
productName = XPCWrappers;
|
||||
};
|
||||
/* End XCSwiftPackageProductDependency section */
|
||||
};
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
</BuildActionEntries>
|
||||
</BuildAction>
|
||||
<TestAction
|
||||
buildConfiguration = "Test"
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||
|
||||
@@ -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
|
||||
Task {
|
||||
@AppStorage("defaultsHasRunSetup") var hasRunSetup = false
|
||||
@@ -92,15 +94,18 @@ extension EnvironmentValues {
|
||||
@MainActor fileprivate static let _secretStoreList: SecretStoreList = {
|
||||
let list = SecretStoreList()
|
||||
let cryptoKit = SecureEnclave.Store()
|
||||
let migrator = SecureEnclave.CryptoKitMigrator()
|
||||
try? migrator.migrate(to: cryptoKit)
|
||||
let cryptoKitMigrator = SecureEnclave.CryptoKitMigrator()
|
||||
try? cryptoKitMigrator.migrate(to: cryptoKit)
|
||||
list.add(store: cryptoKit)
|
||||
list.add(store: SmartCard.Store())
|
||||
return list
|
||||
}()
|
||||
|
||||
@MainActor fileprivate static let _certificateStore: CertificateStore = CertificateStore()
|
||||
|
||||
private static let _agentLaunchController = AgentLaunchController()
|
||||
@Entry var agentLaunchController: any AgentLaunchControllerProtocol = _agentLaunchController
|
||||
|
||||
private static let _updater: any UpdaterProtocol = {
|
||||
@AppStorage("defaultsHasRunSetup") var hasRunSetup = false
|
||||
return Updater(checkOnLaunch: hasRunSetup)
|
||||
@@ -113,6 +118,10 @@ extension EnvironmentValues {
|
||||
@MainActor var secretStoreList: SecretStoreList {
|
||||
EnvironmentValues._secretStoreList
|
||||
}
|
||||
|
||||
@MainActor var certificateStore: CertificateStore {
|
||||
EnvironmentValues._certificateStore
|
||||
}
|
||||
}
|
||||
|
||||
extension FocusedValues {
|
||||
|
||||
@@ -4,6 +4,7 @@ import SecretKit
|
||||
import Observation
|
||||
import OSLog
|
||||
import ServiceManagement
|
||||
import Common
|
||||
|
||||
@MainActor protocol AgentLaunchControllerProtocol: Observable, Sendable {
|
||||
var running: Bool { get }
|
||||
|
||||
29
Sources/Secretive/Controllers/XPCCertificateParser.swift
Normal file
29
Sources/Secretive/Controllers/XPCCertificateParser.swift
Normal file
@@ -0,0 +1,29 @@
|
||||
import Foundation
|
||||
import OSLog
|
||||
import SSHProtocolKit
|
||||
import Brief
|
||||
import XPCWrappers
|
||||
|
||||
/// Delegates all agent input parsing to an XPC service which wraps OpenSSH
|
||||
public final class XPCCertificateParser: OpenSSHCertificateParserProtocol {
|
||||
|
||||
private let logger = Logger(subsystem: "com.maxgoedjen.secretive", category: "XPCCertificateParser")
|
||||
private let session: XPCTypedSession<OpenSSHCertificate, OpenSSHCertificateError>
|
||||
|
||||
public init() async throws {
|
||||
logger.debug("Creating XPCCertificateParser")
|
||||
session = try await XPCTypedSession(serviceName: "com.maxgoedjen.Secretive.SecretiveCertificateParser", warmup: true)
|
||||
logger.debug("XPCCertificateParser is warmed up.")
|
||||
}
|
||||
|
||||
public func parse(data: Data) async throws -> OpenSSHCertificate {
|
||||
logger.debug("Parsing input")
|
||||
defer { logger.debug("Parsed input") }
|
||||
return try await session.send(data)
|
||||
}
|
||||
|
||||
deinit {
|
||||
session.complete()
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
import SwiftUI
|
||||
import SecretKit
|
||||
import SSHProtocolKit
|
||||
|
||||
struct ToolConfigurationView: View {
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import SwiftUI
|
||||
import SecretKit
|
||||
import SSHProtocolKit
|
||||
|
||||
struct SecretDetailView<SecretType: Secret>: View {
|
||||
|
||||
|
||||
@@ -24,6 +24,18 @@ struct SecretListItemView: View {
|
||||
Text(secret.name)
|
||||
}
|
||||
}
|
||||
.sheet(isPresented: $isRenaming, onDismiss: {
|
||||
renamedSecret(secret)
|
||||
}, content: {
|
||||
if let modifiable = store as? AnySecretStoreModifiable {
|
||||
EditSecretView(store: modifiable, secret: secret)
|
||||
}
|
||||
})
|
||||
.showingDeleteConfirmation(isPresented: $isDeleting, secret, store as? AnySecretStoreModifiable) { deleted in
|
||||
if deleted {
|
||||
deletedSecret(secret)
|
||||
}
|
||||
}
|
||||
.contextMenu {
|
||||
if store is AnySecretStoreModifiable {
|
||||
Button(action: { isRenaming = true }) {
|
||||
@@ -36,17 +48,5 @@ struct SecretListItemView: View {
|
||||
}
|
||||
}
|
||||
}
|
||||
.showingDeleteConfirmation(isPresented: $isDeleting, secret, store as? AnySecretStoreModifiable) { deleted in
|
||||
if deleted {
|
||||
deletedSecret(secret)
|
||||
}
|
||||
}
|
||||
.sheet(isPresented: $isRenaming, onDismiss: {
|
||||
renamedSecret(secret)
|
||||
}, content: {
|
||||
if let modifiable = store as? AnySecretStoreModifiable {
|
||||
EditSecretView(store: modifiable, secret: secret)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ import SecretKit
|
||||
import SecureEnclaveSecretKit
|
||||
import SmartCardSecretKit
|
||||
import Brief
|
||||
import SSHProtocolKit
|
||||
|
||||
struct ContentView: View {
|
||||
|
||||
@@ -13,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(\.agentLaunchController) private var agentLaunchController
|
||||
|
||||
@@ -42,6 +44,22 @@ struct ContentView: View {
|
||||
runningSetup = true
|
||||
}
|
||||
}
|
||||
.dropDestination(for: URL.self) { items, location in
|
||||
guard let url = items.first, url.pathExtension == "pub" else { return false }
|
||||
Task {
|
||||
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
|
||||
} isTargeted: { _ in }
|
||||
.focusedSceneValue(\.showCreateSecret, .init(isEnabled: !runningSetup) {
|
||||
showingCreation = true
|
||||
})
|
||||
|
||||
11
Sources/SecretiveCertificateParser/Info.plist
Normal file
11
Sources/SecretiveCertificateParser/Info.plist
Normal file
@@ -0,0 +1,11 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>XPCService</key>
|
||||
<dict>
|
||||
<key>ServiceType</key>
|
||||
<string>Application</string>
|
||||
</dict>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -0,0 +1,22 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>com.apple.security.hardened-process</key>
|
||||
<true/>
|
||||
<key>com.apple.security.hardened-process.checked-allocations</key>
|
||||
<true/>
|
||||
<key>com.apple.security.hardened-process.checked-allocations.enable-pure-data</key>
|
||||
<true/>
|
||||
<key>com.apple.security.hardened-process.checked-allocations.no-tagged-receive</key>
|
||||
<true/>
|
||||
<key>com.apple.security.hardened-process.dyld-ro</key>
|
||||
<true/>
|
||||
<key>com.apple.security.hardened-process.enhanced-security-version</key>
|
||||
<integer>1</integer>
|
||||
<key>com.apple.security.hardened-process.hardened-heap</key>
|
||||
<true/>
|
||||
<key>com.apple.security.hardened-process.platform-restrictions</key>
|
||||
<integer>2</integer>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -0,0 +1,17 @@
|
||||
import Foundation
|
||||
import OSLog
|
||||
import XPCWrappers
|
||||
import SSHProtocolKit
|
||||
|
||||
final class SecretiveCertificateParser: NSObject, XPCProtocol {
|
||||
|
||||
private let logger = Logger(subsystem: "com.maxgoedjen.secretive.SecretiveCertificateParser", category: "SecretiveCertificateParser")
|
||||
|
||||
func process(_ data: Data) async throws -> OpenSSHCertificate {
|
||||
let parser = OpenSSHCertificateParser()
|
||||
let result = try parser.parse(data: data)
|
||||
logger.log("Parser parsed certificate \(result.debugDescription)")
|
||||
return result
|
||||
}
|
||||
|
||||
}
|
||||
7
Sources/SecretiveCertificateParser/main.swift
Normal file
7
Sources/SecretiveCertificateParser/main.swift
Normal file
@@ -0,0 +1,7 @@
|
||||
import Foundation
|
||||
import XPCWrappers
|
||||
|
||||
let delegate = XPCServiceDelegate(exportedObject: SecretiveCertificateParser())
|
||||
let listener = NSXPCListener.service()
|
||||
listener.delegate = delegate
|
||||
listener.resume()
|
||||
22
Sources/SecretiveUpdater/SecretiveUpdater.entitlements
Normal file
22
Sources/SecretiveUpdater/SecretiveUpdater.entitlements
Normal file
@@ -0,0 +1,22 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>com.apple.security.hardened-process</key>
|
||||
<true/>
|
||||
<key>com.apple.security.hardened-process.checked-allocations</key>
|
||||
<true/>
|
||||
<key>com.apple.security.hardened-process.checked-allocations.enable-pure-data</key>
|
||||
<true/>
|
||||
<key>com.apple.security.hardened-process.checked-allocations.no-tagged-receive</key>
|
||||
<true/>
|
||||
<key>com.apple.security.hardened-process.dyld-ro</key>
|
||||
<true/>
|
||||
<key>com.apple.security.hardened-process.enhanced-security-version</key>
|
||||
<integer>1</integer>
|
||||
<key>com.apple.security.hardened-process.hardened-heap</key>
|
||||
<true/>
|
||||
<key>com.apple.security.hardened-process.platform-restrictions</key>
|
||||
<integer>2</integer>
|
||||
</dict>
|
||||
</plist>
|
||||
Reference in New Issue
Block a user