mirror of
https://github.com/maxgoedjen/secretive.git
synced 2026-04-10 03:07:22 +02:00
Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0e0f6b2924 | ||
|
|
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 }}
|
build-mode: ${{ matrix.build-mode }}
|
||||||
- if: matrix.build-mode == 'manual'
|
- if: matrix.build-mode == 'manual'
|
||||||
name: "Select Xcode"
|
name: "Select Xcode"
|
||||||
run: sudo xcrun xcode-select -s /Applications/Xcode_26.4.app
|
run: sudo xcrun xcode-select -s /Applications/Xcode_26.2.app
|
||||||
- if: matrix.build-mode == 'manual'
|
- if: matrix.build-mode == 'manual'
|
||||||
name: "Build"
|
name: "Build"
|
||||||
run: xcrun xcodebuild -project Sources/Secretive.xcodeproj -scheme Secretive CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO
|
run: xcrun xcodebuild -project Sources/Secretive.xcodeproj -scheme Secretive CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO
|
||||||
|
|||||||
2
.github/workflows/nightly.yml
vendored
2
.github/workflows/nightly.yml
vendored
@@ -25,7 +25,7 @@ jobs:
|
|||||||
APPLE_API_KEY_ID: ${{ secrets.APPLE_API_KEY_ID }}
|
APPLE_API_KEY_ID: ${{ secrets.APPLE_API_KEY_ID }}
|
||||||
run: ./.github/scripts/signing.sh
|
run: ./.github/scripts/signing.sh
|
||||||
- name: Set Environment
|
- name: Set Environment
|
||||||
run: sudo xcrun xcode-select -s /Applications/Xcode_26.4.app
|
run: sudo xcrun xcode-select -s /Applications/Xcode_26.2.app
|
||||||
- name: Update Build Number
|
- name: Update Build Number
|
||||||
env:
|
env:
|
||||||
RUN_ID: ${{ github.run_id }}
|
RUN_ID: ${{ github.run_id }}
|
||||||
|
|||||||
2
.github/workflows/oneoff.yml
vendored
2
.github/workflows/oneoff.yml
vendored
@@ -24,7 +24,7 @@ jobs:
|
|||||||
APPLE_API_KEY_ID: ${{ secrets.APPLE_API_KEY_ID }}
|
APPLE_API_KEY_ID: ${{ secrets.APPLE_API_KEY_ID }}
|
||||||
run: ./.github/scripts/signing.sh
|
run: ./.github/scripts/signing.sh
|
||||||
- name: Set Environment
|
- name: Set Environment
|
||||||
run: sudo xcrun xcode-select -s /Applications/Xcode_26.4.app
|
run: sudo xcrun xcode-select -s /Applications/Xcode_26.2.app
|
||||||
- name: Update Build Number
|
- name: Update Build Number
|
||||||
env:
|
env:
|
||||||
RUN_ID: ${{ github.run_id }}
|
RUN_ID: ${{ github.run_id }}
|
||||||
|
|||||||
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 }}
|
APPLE_API_KEY_ID: ${{ secrets.APPLE_API_KEY_ID }}
|
||||||
run: ./.github/scripts/signing.sh
|
run: ./.github/scripts/signing.sh
|
||||||
- name: Set Environment
|
- name: Set Environment
|
||||||
run: sudo xcrun xcode-select -s /Applications/Xcode_26.4.app
|
run: sudo xcrun xcode-select -s /Applications/Xcode_26.2.app
|
||||||
- name: Test
|
- name: Test
|
||||||
run: xcrun xcodebuild -project Sources/Secretive.xcodeproj -scheme PackageTests test
|
run: xcrun xcodebuild -project Sources/Secretive.xcodeproj -scheme PackageTests test
|
||||||
# SPM doesn't seem to pick up on the tests currently?
|
# 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 }}
|
APPLE_API_KEY_ID: ${{ secrets.APPLE_API_KEY_ID }}
|
||||||
run: ./.github/scripts/signing.sh
|
run: ./.github/scripts/signing.sh
|
||||||
- name: Set Environment
|
- name: Set Environment
|
||||||
run: sudo xcrun xcode-select -s /Applications/Xcode_26.4.app
|
run: sudo xcrun xcode-select -s /Applications/Xcode_26.2.app
|
||||||
- name: Update Build Number
|
- name: Update Build Number
|
||||||
env:
|
env:
|
||||||
TAG_NAME: ${{ github.ref }}
|
TAG_NAME: ${{ github.ref }}
|
||||||
|
|||||||
2
.github/workflows/test.yml
vendored
2
.github/workflows/test.yml
vendored
@@ -10,7 +10,7 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v5
|
- uses: actions/checkout@v5
|
||||||
- name: Set Environment
|
- name: Set Environment
|
||||||
run: sudo xcrun xcode-select -s /Applications/Xcode_26.4.app
|
run: sudo xcrun xcode-select -s /Applications/Xcode_26.2.app
|
||||||
- name: Test Main Packages
|
- name: Test Main Packages
|
||||||
run: xcrun xcodebuild -project Sources/Secretive.xcodeproj -scheme PackageTests test
|
run: xcrun xcodebuild -project Sources/Secretive.xcodeproj -scheme PackageTests test
|
||||||
# SPM doesn't seem to pick up on the tests currently?
|
# SPM doesn't seem to pick up on the tests currently?
|
||||||
|
|||||||
4
.gitignore
vendored
4
.gitignore
vendored
@@ -93,7 +93,3 @@ iOSInjectionProject/
|
|||||||
Archive.xcarchive
|
Archive.xcarchive
|
||||||
.DS_Store
|
.DS_Store
|
||||||
contents.xcworkspacedata
|
contents.xcworkspacedata
|
||||||
|
|
||||||
# Per-User Configs
|
|
||||||
|
|
||||||
Sources/Config/OpenSource.xcconfig
|
|
||||||
@@ -22,9 +22,6 @@ let package = Package(
|
|||||||
.library(
|
.library(
|
||||||
name: "SmartCardSecretKit",
|
name: "SmartCardSecretKit",
|
||||||
targets: ["SmartCardSecretKit"]),
|
targets: ["SmartCardSecretKit"]),
|
||||||
.library(
|
|
||||||
name: "SSHProtocolKit",
|
|
||||||
targets: ["SSHProtocolKit"]),
|
|
||||||
],
|
],
|
||||||
dependencies: [
|
dependencies: [
|
||||||
],
|
],
|
||||||
@@ -56,19 +53,6 @@ let package = Package(
|
|||||||
resources: [localization],
|
resources: [localization],
|
||||||
swiftSettings: swiftSettings
|
swiftSettings: swiftSettings
|
||||||
),
|
),
|
||||||
.target(
|
|
||||||
name: "SSHProtocolKit",
|
|
||||||
dependencies: ["SecretKit"],
|
|
||||||
path: "Sources/Packages/Sources/SSHProtocolKit",
|
|
||||||
resources: [localization],
|
|
||||||
swiftSettings: swiftSettings,
|
|
||||||
),
|
|
||||||
.testTarget(
|
|
||||||
name: "SSHProtocolKitTests",
|
|
||||||
dependencies: ["SSHProtocolKit"],
|
|
||||||
path: "Sources/Packages/Tests/SSHProtocolKitTests",
|
|
||||||
swiftSettings: swiftSettings,
|
|
||||||
),
|
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,3 @@
|
|||||||
CI_VERSION = GITHUB_CI_VERSION
|
CI_VERSION = GITHUB_CI_VERSION
|
||||||
CI_BUILD_NUMBER = GITHUB_BUILD_NUMBER
|
CI_BUILD_NUMBER = GITHUB_BUILD_NUMBER
|
||||||
CI_BUILD_LINK = GITHUB_BUILD_URL
|
CI_BUILD_LINK = GITHUB_BUILD_URL
|
||||||
|
|
||||||
#include? "OpenSource.xcconfig"
|
|
||||||
|
|
||||||
SECRETIVE_BASE_BUNDLE_ID = $(SECRETIVE_BASE_BUNDLE_ID_OSS:default=com.maxgoedjen.Secretive)
|
|
||||||
SECRETIVE_DEVELOPMENT_TEAM = $(SECRETIVE_DEVELOPMENT_TEAM_OSS:default=Z72PRUAWF6)
|
|
||||||
|
|||||||
@@ -19,6 +19,9 @@ let package = Package(
|
|||||||
.library(
|
.library(
|
||||||
name: "SmartCardSecretKit",
|
name: "SmartCardSecretKit",
|
||||||
targets: ["SmartCardSecretKit"]),
|
targets: ["SmartCardSecretKit"]),
|
||||||
|
.library(
|
||||||
|
name: "CertificateKit",
|
||||||
|
targets: ["CertificateKit"]),
|
||||||
.library(
|
.library(
|
||||||
name: "SecretAgentKit",
|
name: "SecretAgentKit",
|
||||||
targets: ["SecretAgentKit"]),
|
targets: ["SecretAgentKit"]),
|
||||||
@@ -46,7 +49,7 @@ let package = Package(
|
|||||||
),
|
),
|
||||||
.testTarget(
|
.testTarget(
|
||||||
name: "SecretKitTests",
|
name: "SecretKitTests",
|
||||||
dependencies: ["SecretKit", "SecretAgentKit", "SecureEnclaveSecretKit", "SmartCardSecretKit"],
|
dependencies: ["SecretKit", "SecureEnclaveSecretKit", "SmartCardSecretKit"],
|
||||||
swiftSettings: swiftSettings,
|
swiftSettings: swiftSettings,
|
||||||
),
|
),
|
||||||
.target(
|
.target(
|
||||||
@@ -61,9 +64,15 @@ let package = Package(
|
|||||||
resources: [localization],
|
resources: [localization],
|
||||||
swiftSettings: swiftSettings,
|
swiftSettings: swiftSettings,
|
||||||
),
|
),
|
||||||
|
.target(
|
||||||
|
name: "CertificateKit",
|
||||||
|
dependencies: ["SecretKit", "SSHProtocolKit"],
|
||||||
|
resources: [localization],
|
||||||
|
// swiftSettings: swiftSettings,
|
||||||
|
),
|
||||||
.target(
|
.target(
|
||||||
name: "SecretAgentKit",
|
name: "SecretAgentKit",
|
||||||
dependencies: ["SecretKit", "SSHProtocolKit", "Common"],
|
dependencies: ["SecretKit", "SSHProtocolKit", "CertificateKit"],
|
||||||
resources: [localization],
|
resources: [localization],
|
||||||
swiftSettings: swiftSettings,
|
swiftSettings: swiftSettings,
|
||||||
),
|
),
|
||||||
@@ -75,16 +84,15 @@ let package = Package(
|
|||||||
name: "SSHProtocolKit",
|
name: "SSHProtocolKit",
|
||||||
dependencies: ["SecretKit"],
|
dependencies: ["SecretKit"],
|
||||||
resources: [localization],
|
resources: [localization],
|
||||||
swiftSettings: swiftSettings,
|
// swiftSettings: swiftSettings,
|
||||||
),
|
),
|
||||||
.testTarget(
|
.testTarget(
|
||||||
name: "SSHProtocolKitTests",
|
name: "SSHProtocolKitTests",
|
||||||
dependencies: ["SSHProtocolKit"],
|
dependencies: ["SSHProtocolKit"],
|
||||||
swiftSettings: swiftSettings,
|
|
||||||
),
|
),
|
||||||
.target(
|
.target(
|
||||||
name: "Common",
|
name: "Common",
|
||||||
dependencies: ["SSHProtocolKit", "SecretKit"],
|
dependencies: [],
|
||||||
resources: [localization],
|
resources: [localization],
|
||||||
swiftSettings: swiftSettings,
|
swiftSettings: swiftSettings,
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -75,14 +75,14 @@ extension Updater {
|
|||||||
.reversed()
|
.reversed()
|
||||||
.filter({ !$0.prerelease })
|
.filter({ !$0.prerelease })
|
||||||
.first(where: { $0.minimumOSVersion <= osVersion }) else { return }
|
.first(where: { $0.minimumOSVersion <= osVersion }) else { return }
|
||||||
guard !userIgnored(release: release) else { return }
|
// guard !userIgnored(release: release) else { return }
|
||||||
guard !release.prerelease else { return }
|
// guard !release.prerelease else { return }
|
||||||
let latestVersion = SemVer(release.name)
|
// let latestVersion = SemVer(release.name)
|
||||||
if latestVersion > currentVersion {
|
// if latestVersion > currentVersion {
|
||||||
await MainActor.run {
|
await MainActor.run {
|
||||||
state.update = release
|
state.update = release
|
||||||
}
|
}
|
||||||
}
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Checks whether the user has ignored a 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 {}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,6 +1,4 @@
|
|||||||
import Foundation
|
import Foundation
|
||||||
import SSHProtocolKit
|
|
||||||
import SecretKit
|
|
||||||
|
|
||||||
extension URL {
|
extension URL {
|
||||||
|
|
||||||
@@ -16,20 +14,6 @@ extension URL {
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
public static var publicKeyDirectory: URL {
|
|
||||||
agentHomeURL.appending(component: "PublicKeys")
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The path for a Secret's public key.
|
|
||||||
/// - Parameter secret: The Secret to return the path for.
|
|
||||||
/// - Returns: The path to the Secret's public key.
|
|
||||||
/// - Warning: This method returning a path does not imply that a key has been written to disk already. This method only describes where it will be written to.
|
|
||||||
public static func publicKeyPath<SecretType: Secret>(for secret: SecretType, in directory: URL) -> String {
|
|
||||||
let keyWriter = OpenSSHPublicKeyWriter()
|
|
||||||
let minimalHex = keyWriter.openSSHMD5Fingerprint(secret: secret).replacingOccurrences(of: ":", with: "")
|
|
||||||
return directory.appending(component: "\(minimalHex).pub").path()
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
extension String {
|
extension String {
|
||||||
@@ -43,4 +27,3 @@ extension String {
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
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,20 +1,20 @@
|
|||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
/// Reads OpenSSH protocol data.
|
/// Reads OpenSSH protocol data.
|
||||||
public final class OpenSSHReader {
|
final class OpenSSHReader {
|
||||||
|
|
||||||
var remaining: Data
|
var remaining: Data
|
||||||
var done = false
|
var done = false
|
||||||
|
|
||||||
/// Initialize the reader with an OpenSSH data payload.
|
/// Initialize the reader with an OpenSSH data payload.
|
||||||
/// - Parameter data: The data to read.
|
/// - Parameter data: The data to read.
|
||||||
public init(data: Data) {
|
init(data: Data) {
|
||||||
remaining = Data(data)
|
remaining = Data(data)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Reads the next chunk of data from the playload.
|
/// Reads the next chunk of data from the playload.
|
||||||
/// - Returns: The next chunk of data.
|
/// - Returns: The next chunk of data.
|
||||||
public func readNextChunk(convertEndianness: Bool = true) throws(OpenSSHReaderError) -> Data {
|
func readNextChunk(convertEndianness: Bool = true) throws(OpenSSHReaderError) -> Data {
|
||||||
let length = try readNextBytes(as: UInt32.self, convertEndianness: convertEndianness)
|
let length = try readNextBytes(as: UInt32.self, convertEndianness: convertEndianness)
|
||||||
guard remaining.count >= length else { throw .beyondBounds }
|
guard remaining.count >= length else { throw .beyondBounds }
|
||||||
let dataRange = 0..<Int(length)
|
let dataRange = 0..<Int(length)
|
||||||
@@ -26,7 +26,7 @@ public final class OpenSSHReader {
|
|||||||
return ret
|
return ret
|
||||||
}
|
}
|
||||||
|
|
||||||
public func readNextBytes<T: FixedWidthInteger>(as: T.Type, convertEndianness: Bool = true) throws(OpenSSHReaderError) -> T {
|
func readNextBytes<T: FixedWidthInteger>(as: T.Type, convertEndianness: Bool = true) throws(OpenSSHReaderError) -> T {
|
||||||
let size = MemoryLayout<T>.size
|
let size = MemoryLayout<T>.size
|
||||||
guard remaining.count >= size else { throw .beyondBounds }
|
guard remaining.count >= size else { throw .beyondBounds }
|
||||||
let lengthRange = 0..<size
|
let lengthRange = 0..<size
|
||||||
@@ -39,11 +39,11 @@ public final class OpenSSHReader {
|
|||||||
return convertEndianness ? T(value.bigEndian) : T(value)
|
return convertEndianness ? T(value.bigEndian) : T(value)
|
||||||
}
|
}
|
||||||
|
|
||||||
public func readNextChunkAsString(convertEndianness: Bool = true) throws(OpenSSHReaderError) -> String {
|
func readNextChunkAsString(convertEndianness: Bool = true) throws(OpenSSHReaderError) -> String {
|
||||||
try String(decoding: readNextChunk(convertEndianness: convertEndianness), as: UTF8.self)
|
try String(decoding: readNextChunk(convertEndianness: convertEndianness), as: UTF8.self)
|
||||||
}
|
}
|
||||||
|
|
||||||
public func readNextChunkAsSubReader(convertEndianness: Bool = true) throws(OpenSSHReaderError) -> OpenSSHReader {
|
func readNextChunkAsSubReader(convertEndianness: Bool = true) throws(OpenSSHReaderError) -> OpenSSHReader {
|
||||||
OpenSSHReader(data: try readNextChunk(convertEndianness: convertEndianness))
|
OpenSSHReader(data: try readNextChunk(convertEndianness: convertEndianness))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -30,28 +30,19 @@ public struct OpenSSHSignatureWriter: Sendable {
|
|||||||
|
|
||||||
extension OpenSSHSignatureWriter {
|
extension OpenSSHSignatureWriter {
|
||||||
|
|
||||||
/// Converts a fixed-width big-endian integer (e.g. r/s from CryptoKit rawRepresentation) into an SSH mpint.
|
|
||||||
/// Strips unnecessary leading zeros and prefixes `0x00` if needed to keep the value positive.
|
|
||||||
private func mpint(fromFixedWidthPositiveBytes bytes: Data) -> Data {
|
|
||||||
// mpint zero is encoded as a string with zero bytes of data.
|
|
||||||
guard let firstNonZeroIndex = bytes.firstIndex(where: { $0 != 0x00 }) else {
|
|
||||||
return Data()
|
|
||||||
}
|
|
||||||
|
|
||||||
let trimmed = Data(bytes[firstNonZeroIndex...])
|
|
||||||
|
|
||||||
if let first = trimmed.first, first >= 0x80 {
|
|
||||||
var prefixed = Data([0x00])
|
|
||||||
prefixed.append(trimmed)
|
|
||||||
return prefixed
|
|
||||||
}
|
|
||||||
return trimmed
|
|
||||||
}
|
|
||||||
|
|
||||||
func ecdsaSignature(_ rawRepresentation: Data, keyType: KeyType) -> Data {
|
func ecdsaSignature(_ rawRepresentation: Data, keyType: KeyType) -> Data {
|
||||||
let rawLength = rawRepresentation.count/2
|
let rawLength = rawRepresentation.count/2
|
||||||
let r = mpint(fromFixedWidthPositiveBytes: Data(rawRepresentation[0..<rawLength]))
|
// Check if we need to pad with 0x00 to prevent certain
|
||||||
let s = mpint(fromFixedWidthPositiveBytes: Data(rawRepresentation[rawLength...]))
|
// ssh servers from thinking r or s is negative
|
||||||
|
let paddingRange: ClosedRange<UInt8> = 0x80...0xFF
|
||||||
|
var r = Data(rawRepresentation[0..<rawLength])
|
||||||
|
if paddingRange ~= r.first! {
|
||||||
|
r.insert(0x00, at: 0)
|
||||||
|
}
|
||||||
|
var s = Data(rawRepresentation[rawLength...])
|
||||||
|
if paddingRange ~= s.first! {
|
||||||
|
s.insert(0x00, at: 0)
|
||||||
|
}
|
||||||
|
|
||||||
var signatureChunk = Data()
|
var signatureChunk = Data()
|
||||||
signatureChunk.append(r.lengthAndData)
|
signatureChunk.append(r.lengthAndData)
|
||||||
|
|||||||
@@ -1,8 +1,6 @@
|
|||||||
import Foundation
|
import Foundation
|
||||||
import OSLog
|
import OSLog
|
||||||
import SecretKit
|
import SecretKit
|
||||||
import SSHProtocolKit
|
|
||||||
import Common
|
|
||||||
|
|
||||||
/// Controller responsible for writing public keys to disk, so that they're easily accessible by scripts.
|
/// Controller responsible for writing public keys to disk, so that they're easily accessible by scripts.
|
||||||
public final class PublicKeyFileStoreController: Sendable {
|
public final class PublicKeyFileStoreController: Sendable {
|
||||||
@@ -12,8 +10,8 @@ public final class PublicKeyFileStoreController: Sendable {
|
|||||||
private let keyWriter = OpenSSHPublicKeyWriter()
|
private let keyWriter = OpenSSHPublicKeyWriter()
|
||||||
|
|
||||||
/// Initializes a PublicKeyFileStoreController.
|
/// Initializes a PublicKeyFileStoreController.
|
||||||
public init(directory: URL) {
|
public init(homeDirectory: URL) {
|
||||||
self.directory = directory
|
directory = homeDirectory.appending(component: "PublicKeys")
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Writes out the keys specified to disk.
|
/// Writes out the keys specified to disk.
|
||||||
@@ -22,7 +20,7 @@ public final class PublicKeyFileStoreController: Sendable {
|
|||||||
public func generatePublicKeys(for secrets: [AnySecret], clear: Bool = false) throws {
|
public func generatePublicKeys(for secrets: [AnySecret], clear: Bool = false) throws {
|
||||||
logger.log("Writing public keys to disk")
|
logger.log("Writing public keys to disk")
|
||||||
if clear {
|
if clear {
|
||||||
let validPaths = Set(secrets.map { URL.publicKeyPath(for: $0, in: directory) })
|
let validPaths = Set(secrets.map { publicKeyPath(for: $0) })
|
||||||
.union(Set(secrets.map { sshCertificatePath(for: $0) }))
|
.union(Set(secrets.map { sshCertificatePath(for: $0) }))
|
||||||
let contentsOfDirectory = (try? FileManager.default.contentsOfDirectory(atPath: directory.path())) ?? []
|
let contentsOfDirectory = (try? FileManager.default.contentsOfDirectory(atPath: directory.path())) ?? []
|
||||||
let fullPathContents = contentsOfDirectory.map { directory.appending(path: $0).path() }
|
let fullPathContents = contentsOfDirectory.map { directory.appending(path: $0).path() }
|
||||||
@@ -36,13 +34,21 @@ public final class PublicKeyFileStoreController: Sendable {
|
|||||||
}
|
}
|
||||||
try? FileManager.default.createDirectory(at: directory, withIntermediateDirectories: false, attributes: nil)
|
try? FileManager.default.createDirectory(at: directory, withIntermediateDirectories: false, attributes: nil)
|
||||||
for secret in secrets {
|
for secret in secrets {
|
||||||
let path = URL.publicKeyPath(for: secret, in: directory)
|
let path = publicKeyPath(for: secret)
|
||||||
let data = Data(keyWriter.openSSHString(secret: secret).utf8)
|
let data = Data(keyWriter.openSSHString(secret: secret).utf8)
|
||||||
FileManager.default.createFile(atPath: path, contents: data, attributes: nil)
|
FileManager.default.createFile(atPath: path, contents: data, attributes: nil)
|
||||||
}
|
}
|
||||||
logger.log("Finished writing public keys")
|
logger.log("Finished writing public keys")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The path for a Secret's public key.
|
||||||
|
/// - Parameter secret: The Secret to return the path for.
|
||||||
|
/// - Returns: The path to the Secret's public key.
|
||||||
|
/// - Warning: This method returning a path does not imply that a key has been written to disk already. This method only describes where it will be written to.
|
||||||
|
public func publicKeyPath<SecretType: Secret>(for secret: SecretType) -> String {
|
||||||
|
let minimalHex = keyWriter.openSSHMD5Fingerprint(secret: secret).replacingOccurrences(of: ":", with: "")
|
||||||
|
return directory.appending(component: "\(minimalHex).pub").path()
|
||||||
|
}
|
||||||
|
|
||||||
/// Short-circuit check to ship enumerating a bunch of paths if there's nothing in the cert directory.
|
/// Short-circuit check to ship enumerating a bunch of paths if there's nothing in the cert directory.
|
||||||
public var hasAnyCertificates: Bool {
|
public var hasAnyCertificates: Bool {
|
||||||
@@ -1,12 +1,11 @@
|
|||||||
import Foundation
|
import Foundation
|
||||||
import OSLog
|
import OSLog
|
||||||
import SecretKit
|
import SecretKit
|
||||||
import SSHProtocolKit
|
|
||||||
|
|
||||||
public protocol SSHAgentInputParserProtocol {
|
public protocol SSHAgentInputParserProtocol {
|
||||||
|
|
||||||
func parse(data: Data) async throws -> SSHAgent.Request
|
func parse(data: Data) async throws -> SSHAgent.Request
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public struct SSHAgentInputParser: SSHAgentInputParserProtocol {
|
public struct SSHAgentInputParser: SSHAgentInputParserProtocol {
|
||||||
@@ -14,7 +13,7 @@ public struct SSHAgentInputParser: SSHAgentInputParserProtocol {
|
|||||||
private let logger = Logger(subsystem: "com.maxgoedjen.secretive.secretagent", category: "InputParser")
|
private let logger = Logger(subsystem: "com.maxgoedjen.secretive.secretagent", category: "InputParser")
|
||||||
|
|
||||||
public init() {
|
public init() {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public func parse(data: Data) throws(AgentParsingError) -> SSHAgent.Request {
|
public func parse(data: Data) throws(AgentParsingError) -> SSHAgent.Request {
|
||||||
@@ -75,21 +74,16 @@ extension SSHAgentInputParser {
|
|||||||
func certificatePublicKeyBlob(from hash: Data) -> Data? {
|
func certificatePublicKeyBlob(from hash: Data) -> Data? {
|
||||||
let reader = OpenSSHReader(data: hash)
|
let reader = OpenSSHReader(data: hash)
|
||||||
do {
|
do {
|
||||||
let certType = String(decoding: try reader.readNextChunk(), as: UTF8.self)
|
let certType = try reader.readNextChunkAsString()
|
||||||
switch certType {
|
guard let certType = OpenSSHCertificate.CertificateType(rawValue: certType) else { return nil }
|
||||||
case "ecdsa-sha2-nistp256-cert-v01@openssh.com",
|
_ = try reader.readNextChunk() // nonce
|
||||||
"ecdsa-sha2-nistp384-cert-v01@openssh.com",
|
let curveIdentifier = try reader.readNextChunk()
|
||||||
"ecdsa-sha2-nistp521-cert-v01@openssh.com":
|
let publicKey = try reader.readNextChunk()
|
||||||
_ = try reader.readNextChunk() // nonce
|
let openSSHIdentifier = certType.keyIdentifier
|
||||||
let curveIdentifier = try reader.readNextChunk()
|
return openSSHIdentifier.lengthAndData +
|
||||||
let publicKey = try reader.readNextChunk()
|
curveIdentifier.lengthAndData +
|
||||||
let openSSHIdentifier = certType.replacingOccurrences(of: "-cert-v01@openssh.com", with: "")
|
|
||||||
return openSSHIdentifier.lengthAndData +
|
|
||||||
curveIdentifier.lengthAndData +
|
|
||||||
publicKey.lengthAndData
|
publicKey.lengthAndData
|
||||||
default:
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
} catch {
|
} catch {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -12,7 +12,7 @@ public final class Agent: Sendable {
|
|||||||
private let witness: SigningWitness?
|
private let witness: SigningWitness?
|
||||||
private let publicKeyWriter = OpenSSHPublicKeyWriter()
|
private let publicKeyWriter = OpenSSHPublicKeyWriter()
|
||||||
private let signatureWriter = OpenSSHSignatureWriter()
|
private let signatureWriter = OpenSSHSignatureWriter()
|
||||||
private let certificateHandler = OpenSSHCertificateHandler()
|
// private let certificateHandler = OpenSSHCertificateHandler()
|
||||||
private let logger = Logger(subsystem: "com.maxgoedjen.secretive.secretagent", category: "Agent")
|
private let logger = Logger(subsystem: "com.maxgoedjen.secretive.secretagent", category: "Agent")
|
||||||
|
|
||||||
/// Initializes an agent with a store list and a witness.
|
/// Initializes an agent with a store list and a witness.
|
||||||
@@ -24,7 +24,7 @@ public final class Agent: Sendable {
|
|||||||
self.storeList = storeList
|
self.storeList = storeList
|
||||||
self.witness = witness
|
self.witness = witness
|
||||||
Task { @MainActor in
|
Task { @MainActor in
|
||||||
await certificateHandler.reloadCertificates(for: storeList.allSecrets)
|
// await certificateHandler.reloadCertificates(for: storeList.allSecrets)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -68,7 +68,7 @@ extension Agent {
|
|||||||
/// - Returns: An OpenSSH formatted Data payload listing the identities available for signing operations.
|
/// - Returns: An OpenSSH formatted Data payload listing the identities available for signing operations.
|
||||||
func identities() async -> Data {
|
func identities() async -> Data {
|
||||||
let secrets = await storeList.allSecrets
|
let secrets = await storeList.allSecrets
|
||||||
await certificateHandler.reloadCertificates(for: secrets)
|
// await certificateHandler.reloadCertificates(for: secrets)
|
||||||
var count = 0
|
var count = 0
|
||||||
var keyData = Data()
|
var keyData = Data()
|
||||||
|
|
||||||
@@ -77,12 +77,12 @@ extension Agent {
|
|||||||
keyData.append(keyBlob.lengthAndData)
|
keyData.append(keyBlob.lengthAndData)
|
||||||
keyData.append(publicKeyWriter.comment(secret: secret).lengthAndData)
|
keyData.append(publicKeyWriter.comment(secret: secret).lengthAndData)
|
||||||
count += 1
|
count += 1
|
||||||
|
|
||||||
if let (certificateData, name) = try? await certificateHandler.keyBlobAndName(for: secret) {
|
// if let (certificateData, name) = try? await certificateHandler.keyBlobAndName(for: secret) {
|
||||||
keyData.append(certificateData.lengthAndData)
|
// keyData.append(certificateData.lengthAndData)
|
||||||
keyData.append(name.lengthAndData)
|
// keyData.append(name.lengthAndData)
|
||||||
count += 1
|
// count += 1
|
||||||
}
|
// }
|
||||||
}
|
}
|
||||||
logger.log("Agent enumerated \(count) identities")
|
logger.log("Agent enumerated \(count) identities")
|
||||||
var countBigEndian = UInt32(count).bigEndian
|
var countBigEndian = UInt32(count).bigEndian
|
||||||
@@ -97,7 +97,7 @@ extension Agent {
|
|||||||
/// - Returns: An OpenSSH formatted Data payload containing the signed data response.
|
/// - Returns: An OpenSSH formatted Data payload containing the signed data response.
|
||||||
func sign(data: Data, keyBlob: Data, provenance: SigningRequestProvenance) async throws -> Data {
|
func sign(data: Data, keyBlob: Data, provenance: SigningRequestProvenance) async throws -> Data {
|
||||||
guard let (secret, store) = await secret(matching: keyBlob) else {
|
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)")
|
logger.debug("Agent did not have a key matching \(keyBlobHex)")
|
||||||
throw NoMatchingKeyError()
|
throw NoMatchingKeyError()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import SSHProtocolKit
|
|||||||
/// Manages storage and lookup for OpenSSH certificates.
|
/// Manages storage and lookup for OpenSSH certificates.
|
||||||
public actor OpenSSHCertificateHandler: Sendable {
|
public actor OpenSSHCertificateHandler: Sendable {
|
||||||
|
|
||||||
private let publicKeyFileStoreController = PublicKeyFileStoreController(directory: URL.publicKeyDirectory)
|
private let publicKeyFileStoreController = PublicKeyFileStoreController(homeDirectory: URL.homeDirectory)
|
||||||
private let logger = Logger(subsystem: "com.maxgoedjen.secretive.secretagent", category: "OpenSSHCertificateHandler")
|
private let logger = Logger(subsystem: "com.maxgoedjen.secretive.secretagent", category: "OpenSSHCertificateHandler")
|
||||||
private let writer = OpenSSHPublicKeyWriter()
|
private let writer = OpenSSHPublicKeyWriter()
|
||||||
private var keyBlobsAndNames: [AnySecret: (Data, Data)] = [:]
|
private var keyBlobsAndNames: [AnySecret: (Data, Data)] = [:]
|
||||||
@@ -22,9 +22,6 @@ public actor OpenSSHCertificateHandler: Sendable {
|
|||||||
logger.log("No certificates, short circuiting")
|
logger.log("No certificates, short circuiting")
|
||||||
return
|
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``
|
/// Attempts to find an OpenSSH Certificate that corresponds to a ``Secret``
|
||||||
@@ -33,57 +30,6 @@ public actor OpenSSHCertificateHandler: Sendable {
|
|||||||
public func keyBlobAndName<SecretType: Secret>(for secret: SecretType) throws -> (Data, Data)? {
|
public func keyBlobAndName<SecretType: Secret>(for secret: SecretType) throws -> (Data, Data)? {
|
||||||
keyBlobsAndNames[AnySecret(secret)]
|
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"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,69 +0,0 @@
|
|||||||
import LocalAuthentication
|
|
||||||
|
|
||||||
/// A context describing a persisted authentication.
|
|
||||||
package final class PersistentAuthenticationContext<SecretType: Secret>: PersistedAuthenticationContext {
|
|
||||||
|
|
||||||
/// The Secret to persist authentication for.
|
|
||||||
let secret: SecretType
|
|
||||||
/// The LAContext used to authorize the persistent context.
|
|
||||||
package nonisolated(unsafe) let context: LAContext
|
|
||||||
/// An expiration date for the context.
|
|
||||||
/// - Note - Monotonic time instead of Date() to prevent people setting the clock back.
|
|
||||||
let monotonicExpiration: UInt64
|
|
||||||
|
|
||||||
/// Initializes a context.
|
|
||||||
/// - Parameters:
|
|
||||||
/// - secret: The Secret to persist authentication for.
|
|
||||||
/// - context: The LAContext used to authorize the persistent context.
|
|
||||||
/// - duration: The duration of the authorization context, in seconds.
|
|
||||||
init(secret: SecretType, context: LAContext, duration: TimeInterval) {
|
|
||||||
self.secret = secret
|
|
||||||
unsafe self.context = context
|
|
||||||
let durationInNanoSeconds = Measurement(value: duration, unit: UnitDuration.seconds).converted(to: .nanoseconds).value
|
|
||||||
self.monotonicExpiration = clock_gettime_nsec_np(CLOCK_MONOTONIC) + UInt64(durationInNanoSeconds)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A boolean describing whether or not the context is still valid.
|
|
||||||
package var valid: Bool {
|
|
||||||
clock_gettime_nsec_np(CLOCK_MONOTONIC) < monotonicExpiration
|
|
||||||
}
|
|
||||||
|
|
||||||
package var expiration: Date {
|
|
||||||
let remainingNanoseconds = monotonicExpiration - clock_gettime_nsec_np(CLOCK_MONOTONIC)
|
|
||||||
let remainingInSeconds = Measurement(value: Double(remainingNanoseconds), unit: UnitDuration.nanoseconds).converted(to: .seconds).value
|
|
||||||
return Date(timeIntervalSinceNow: remainingInSeconds)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
package actor PersistentAuthenticationHandler<SecretType: Secret>: Sendable {
|
|
||||||
|
|
||||||
private var persistedAuthenticationContexts: [SecretType: PersistentAuthenticationContext<SecretType>] = [:]
|
|
||||||
|
|
||||||
package init() {
|
|
||||||
}
|
|
||||||
|
|
||||||
package func existingPersistedAuthenticationContext(secret: SecretType) -> PersistentAuthenticationContext<SecretType>? {
|
|
||||||
guard let persisted = persistedAuthenticationContexts[secret], persisted.valid else { return nil }
|
|
||||||
return persisted
|
|
||||||
}
|
|
||||||
|
|
||||||
package func persistAuthentication(secret: SecretType, forDuration duration: TimeInterval) async throws {
|
|
||||||
let newContext = LAContext()
|
|
||||||
newContext.touchIDAuthenticationAllowableReuseDuration = duration
|
|
||||||
newContext.localizedCancelTitle = String(localized: .authContextRequestDenyButton)
|
|
||||||
|
|
||||||
let formatter = DateComponentsFormatter()
|
|
||||||
formatter.unitsStyle = .spellOut
|
|
||||||
formatter.allowedUnits = [.hour, .minute, .day]
|
|
||||||
|
|
||||||
|
|
||||||
let durationString = formatter.string(from: duration)!
|
|
||||||
newContext.localizedReason = String(localized: .authContextPersistForDuration(secretName: secret.name, duration: durationString))
|
|
||||||
let success = try await newContext.evaluatePolicy(.deviceOwnerAuthentication, localizedReason: newContext.localizedReason)
|
|
||||||
guard success else { return }
|
|
||||||
let context = PersistentAuthenticationContext(secret: secret, context: newContext, duration: duration)
|
|
||||||
persistedAuthenticationContexts[secret] = context
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -0,0 +1,70 @@
|
|||||||
|
import LocalAuthentication
|
||||||
|
import SecretKit
|
||||||
|
|
||||||
|
extension SecureEnclave {
|
||||||
|
|
||||||
|
/// A context describing a persisted authentication.
|
||||||
|
final class PersistentAuthenticationContext: PersistedAuthenticationContext {
|
||||||
|
|
||||||
|
/// The Secret to persist authentication for.
|
||||||
|
let secret: Secret
|
||||||
|
/// The LAContext used to authorize the persistent context.
|
||||||
|
nonisolated(unsafe) let context: LAContext
|
||||||
|
/// An expiration date for the context.
|
||||||
|
/// - Note - Monotonic time instead of Date() to prevent people setting the clock back.
|
||||||
|
let monotonicExpiration: UInt64
|
||||||
|
|
||||||
|
/// Initializes a context.
|
||||||
|
/// - Parameters:
|
||||||
|
/// - secret: The Secret to persist authentication for.
|
||||||
|
/// - context: The LAContext used to authorize the persistent context.
|
||||||
|
/// - duration: The duration of the authorization context, in seconds.
|
||||||
|
init(secret: Secret, context: LAContext, duration: TimeInterval) {
|
||||||
|
self.secret = secret
|
||||||
|
unsafe self.context = context
|
||||||
|
let durationInNanoSeconds = Measurement(value: duration, unit: UnitDuration.seconds).converted(to: .nanoseconds).value
|
||||||
|
self.monotonicExpiration = clock_gettime_nsec_np(CLOCK_MONOTONIC) + UInt64(durationInNanoSeconds)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A boolean describing whether or not the context is still valid.
|
||||||
|
var valid: Bool {
|
||||||
|
clock_gettime_nsec_np(CLOCK_MONOTONIC) < monotonicExpiration
|
||||||
|
}
|
||||||
|
|
||||||
|
var expiration: Date {
|
||||||
|
let remainingNanoseconds = monotonicExpiration - clock_gettime_nsec_np(CLOCK_MONOTONIC)
|
||||||
|
let remainingInSeconds = Measurement(value: Double(remainingNanoseconds), unit: UnitDuration.nanoseconds).converted(to: .seconds).value
|
||||||
|
return Date(timeIntervalSinceNow: remainingInSeconds)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
actor PersistentAuthenticationHandler: Sendable {
|
||||||
|
|
||||||
|
private var persistedAuthenticationContexts: [Secret: PersistentAuthenticationContext] = [:]
|
||||||
|
|
||||||
|
func existingPersistedAuthenticationContext(secret: Secret) -> PersistentAuthenticationContext? {
|
||||||
|
guard let persisted = persistedAuthenticationContexts[secret], persisted.valid else { return nil }
|
||||||
|
return persisted
|
||||||
|
}
|
||||||
|
|
||||||
|
func persistAuthentication(secret: Secret, forDuration duration: TimeInterval) async throws {
|
||||||
|
let newContext = LAContext()
|
||||||
|
newContext.touchIDAuthenticationAllowableReuseDuration = duration
|
||||||
|
newContext.localizedCancelTitle = String(localized: .authContextRequestDenyButton)
|
||||||
|
|
||||||
|
let formatter = DateComponentsFormatter()
|
||||||
|
formatter.unitsStyle = .spellOut
|
||||||
|
formatter.allowedUnits = [.hour, .minute, .day]
|
||||||
|
|
||||||
|
|
||||||
|
let durationString = formatter.string(from: duration)!
|
||||||
|
newContext.localizedReason = String(localized: .authContextPersistForDuration(secretName: secret.name, duration: durationString))
|
||||||
|
let success = try await newContext.evaluatePolicy(.deviceOwnerAuthentication, localizedReason: newContext.localizedReason)
|
||||||
|
guard success else { return }
|
||||||
|
let context = PersistentAuthenticationContext(secret: secret, context: newContext, duration: duration)
|
||||||
|
persistedAuthenticationContexts[secret] = context
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -17,7 +17,7 @@ extension SecureEnclave {
|
|||||||
}
|
}
|
||||||
public let id = UUID()
|
public let id = UUID()
|
||||||
public let name = String(localized: .secureEnclave)
|
public let name = String(localized: .secureEnclave)
|
||||||
private let persistentAuthenticationHandler = PersistentAuthenticationHandler<Secret>()
|
private let persistentAuthenticationHandler = PersistentAuthenticationHandler()
|
||||||
|
|
||||||
/// Initializes a Store.
|
/// Initializes a Store.
|
||||||
@MainActor public init() {
|
@MainActor public init() {
|
||||||
|
|||||||
@@ -34,7 +34,6 @@ extension SmartCard {
|
|||||||
public var secrets: [Secret] {
|
public var secrets: [Secret] {
|
||||||
state.secrets
|
state.secrets
|
||||||
}
|
}
|
||||||
private let persistentAuthenticationHandler = PersistentAuthenticationHandler<Secret>()
|
|
||||||
|
|
||||||
/// Initializes a Store.
|
/// Initializes a Store.
|
||||||
public init() {
|
public init() {
|
||||||
@@ -59,15 +58,9 @@ extension SmartCard {
|
|||||||
|
|
||||||
public func sign(data: Data, with secret: Secret, for provenance: SigningRequestProvenance) async throws -> Data {
|
public func sign(data: Data, with secret: Secret, for provenance: SigningRequestProvenance) async throws -> Data {
|
||||||
guard let tokenID = await state.tokenID else { fatalError() }
|
guard let tokenID = await state.tokenID else { fatalError() }
|
||||||
var context: LAContext
|
let context = LAContext()
|
||||||
if let existing = await persistentAuthenticationHandler.existingPersistedAuthenticationContext(secret: secret) {
|
context.localizedReason = String(localized: .authContextRequestSignatureDescription(appName: provenance.origin.displayName, secretName: secret.name))
|
||||||
context = unsafe existing.context
|
context.localizedCancelTitle = String(localized: .authContextRequestDenyButton)
|
||||||
} else {
|
|
||||||
let newContext = LAContext()
|
|
||||||
newContext.localizedReason = String(localized: .authContextRequestSignatureDescription(appName: provenance.origin.displayName, secretName: secret.name))
|
|
||||||
newContext.localizedCancelTitle = String(localized: .authContextRequestDenyButton)
|
|
||||||
context = newContext
|
|
||||||
}
|
|
||||||
let attributes = KeychainDictionary([
|
let attributes = KeychainDictionary([
|
||||||
kSecClass: kSecClassKey,
|
kSecClass: kSecClassKey,
|
||||||
kSecAttrKeyClass: kSecAttrKeyClassPrivate,
|
kSecAttrKeyClass: kSecAttrKeyClassPrivate,
|
||||||
@@ -93,12 +86,11 @@ extension SmartCard {
|
|||||||
return signature as Data
|
return signature as Data
|
||||||
}
|
}
|
||||||
|
|
||||||
public func existingPersistedAuthenticationContext(secret: Secret) async -> PersistedAuthenticationContext? {
|
public func existingPersistedAuthenticationContext(secret: Secret) -> PersistedAuthenticationContext? {
|
||||||
await persistentAuthenticationHandler.existingPersistedAuthenticationContext(secret: secret)
|
nil
|
||||||
}
|
}
|
||||||
|
|
||||||
public func persistAuthentication(secret: Secret, forDuration duration: TimeInterval) async throws {
|
public func persistAuthentication(secret: Secret, forDuration: TimeInterval) throws {
|
||||||
try await persistentAuthenticationHandler.persistAuthentication(secret: secret, forDuration: duration)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Reloads all secrets from the store.
|
/// Reloads all secrets from the store.
|
||||||
@@ -171,7 +163,7 @@ extension SmartCard.Store {
|
|||||||
let publicKeySecRef = SecKeyCopyPublicKey(publicKeyRef)!
|
let publicKeySecRef = SecKeyCopyPublicKey(publicKeyRef)!
|
||||||
let publicKeyAttributes = SecKeyCopyAttributes(publicKeySecRef) as! [CFString: Any]
|
let publicKeyAttributes = SecKeyCopyAttributes(publicKeySecRef) as! [CFString: Any]
|
||||||
let publicKey = publicKeyAttributes[kSecValueData] as! Data
|
let publicKey = publicKeyAttributes[kSecValueData] as! Data
|
||||||
let attributes = Attributes(keyType: KeyType(secAttr: algorithmSecAttr, size: keySize)!, authentication: .presenceRequired)
|
let attributes = Attributes(keyType: KeyType(secAttr: algorithmSecAttr, size: keySize)!, authentication: .unknown)
|
||||||
let secret = SmartCard.Secret(id: tokenID, name: name, publicKey: publicKey, attributes: attributes)
|
let secret = SmartCard.Secret(id: tokenID, name: name, publicKey: publicKey, attributes: attributes)
|
||||||
guard signatureAlgorithm(for: secret) != nil else { return nil }
|
guard signatureAlgorithm(for: secret) != nil else { return nil }
|
||||||
return secret
|
return secret
|
||||||
|
|||||||
@@ -1,26 +0,0 @@
|
|||||||
import Foundation
|
|
||||||
|
|
||||||
extension ProcessInfo {
|
|
||||||
private static let fallbackTeamID = "Z72PRUAWF6"
|
|
||||||
|
|
||||||
private static let teamID: String = {
|
|
||||||
#if DEBUG
|
|
||||||
guard let task = SecTaskCreateFromSelf(nil) else {
|
|
||||||
assertionFailure("SecTaskCreateFromSelf failed")
|
|
||||||
return fallbackTeamID
|
|
||||||
}
|
|
||||||
|
|
||||||
guard let value = SecTaskCopyValueForEntitlement(task, "com.apple.developer.team-identifier" as CFString, nil) as? String else {
|
|
||||||
assertionFailure("SecTaskCopyValueForEntitlement(com.apple.developer.team-identifier) failed")
|
|
||||||
return fallbackTeamID
|
|
||||||
}
|
|
||||||
|
|
||||||
return value
|
|
||||||
#else
|
|
||||||
/// Always use hardcoded team ID for release builds, just in case.
|
|
||||||
return fallbackTeamID
|
|
||||||
#endif
|
|
||||||
}()
|
|
||||||
|
|
||||||
public var teamID: String { Self.teamID }
|
|
||||||
}
|
|
||||||
@@ -12,7 +12,7 @@ public final class XPCServiceDelegate: NSObject, NSXPCListenerDelegate {
|
|||||||
newConnection.exportedInterface = NSXPCInterface(with: (any _XPCProtocol).self)
|
newConnection.exportedInterface = NSXPCInterface(with: (any _XPCProtocol).self)
|
||||||
let exportedObject = exportedObject
|
let exportedObject = exportedObject
|
||||||
newConnection.exportedObject = exportedObject
|
newConnection.exportedObject = exportedObject
|
||||||
newConnection.setCodeSigningRequirement("anchor apple generic and certificate leaf[subject.OU] = \"\(ProcessInfo.processInfo.teamID)\"")
|
newConnection.setCodeSigningRequirement("anchor apple generic and certificate leaf[subject.OU] = Z72PRUAWF6")
|
||||||
newConnection.resume()
|
newConnection.resume()
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ public struct XPCTypedSession<ResponseType: Codable & Sendable, ErrorType: Error
|
|||||||
public init(serviceName: String, warmup: Bool = false) async throws {
|
public init(serviceName: String, warmup: Bool = false) async throws {
|
||||||
let connection = NSXPCConnection(serviceName: serviceName)
|
let connection = NSXPCConnection(serviceName: serviceName)
|
||||||
connection.remoteObjectInterface = NSXPCInterface(with: (any _XPCProtocol).self)
|
connection.remoteObjectInterface = NSXPCInterface(with: (any _XPCProtocol).self)
|
||||||
connection.setCodeSigningRequirement("anchor apple generic and certificate leaf[subject.OU] = \"\(ProcessInfo.processInfo.teamID)\"")
|
connection.setCodeSigningRequirement("anchor apple generic and certificate leaf[subject.OU] = Z72PRUAWF6")
|
||||||
connection.resume()
|
connection.resume()
|
||||||
guard let proxy = connection.remoteObjectProxy as? _XPCProtocol else { fatalError() }
|
guard let proxy = connection.remoteObjectProxy as? _XPCProtocol else { fatalError() }
|
||||||
self.connection = connection
|
self.connection = connection
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
import Foundation
|
import Foundation
|
||||||
import Testing
|
import Testing
|
||||||
@testable import SecretKit
|
@testable import SecretKit
|
||||||
import SSHProtocolKit
|
@testable import SecureEnclaveSecretKit
|
||||||
|
@testable import SmartCardSecretKit
|
||||||
|
|
||||||
@Suite struct OpenSSHPublicKeyWriterTests {
|
@Suite struct OpenSSHPublicKeyWriterTests {
|
||||||
|
|
||||||
@@ -46,8 +47,8 @@ import SSHProtocolKit
|
|||||||
extension OpenSSHPublicKeyWriterTests {
|
extension OpenSSHPublicKeyWriterTests {
|
||||||
|
|
||||||
enum Constants {
|
enum Constants {
|
||||||
static let ecdsa256Secret = TestSecret(id: Data(), name: "Test Key (ECDSA 256)", publicKey: Data(base64Encoded: "BOVEjgAA5PHqRgwykjN5qM21uWCHFSY/Sqo5gkHAkn+e1MMQKHOLga7ucB9b3mif33MBid59GRK9GEPVlMiSQwo=")!, attributes: Attributes(keyType: KeyType(algorithm: .ecdsa, size: 256), authentication: .notRequired, publicKeyAttribution: "test@example.com"))
|
static let ecdsa256Secret = SmartCard.Secret(id: Data(), name: "Test Key (ECDSA 256)", publicKey: Data(base64Encoded: "BOVEjgAA5PHqRgwykjN5qM21uWCHFSY/Sqo5gkHAkn+e1MMQKHOLga7ucB9b3mif33MBid59GRK9GEPVlMiSQwo=")!, attributes: Attributes(keyType: KeyType(algorithm: .ecdsa, size: 256), authentication: .notRequired, publicKeyAttribution: "test@example.com"))
|
||||||
static let ecdsa384Secret = TestSecret(id: Data(), name: "Test Key (ECDSA 384)", publicKey: Data(base64Encoded: "BG2MNc/C5OTHFE2tBvbZCVcpOGa8vBMquiTLkH4lwkeqOPxhi+PyYUfQZMTRJNPiTyWPoMBqNiCIFRVv60yPN/AHufHaOgbdTP42EgMlMMImkAjYUEv9DESHTVIs2PW1yQ==")!, attributes: Attributes(keyType: KeyType(algorithm: .ecdsa, size: 384), authentication: .notRequired, publicKeyAttribution: "test@example.com"))
|
static let ecdsa384Secret = SmartCard.Secret(id: Data(), name: "Test Key (ECDSA 384)", publicKey: Data(base64Encoded: "BG2MNc/C5OTHFE2tBvbZCVcpOGa8vBMquiTLkH4lwkeqOPxhi+PyYUfQZMTRJNPiTyWPoMBqNiCIFRVv60yPN/AHufHaOgbdTP42EgMlMMImkAjYUEv9DESHTVIs2PW1yQ==")!, attributes: Attributes(keyType: KeyType(algorithm: .ecdsa, size: 384), authentication: .notRequired, publicKeyAttribution: "test@example.com"))
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,83 +0,0 @@
|
|||||||
import Foundation
|
|
||||||
import Testing
|
|
||||||
import SSHProtocolKit
|
|
||||||
@testable import SecretKit
|
|
||||||
|
|
||||||
@Suite struct OpenSSHSignatureWriterTests {
|
|
||||||
|
|
||||||
private let writer = OpenSSHSignatureWriter()
|
|
||||||
|
|
||||||
@Test func ecdsaMpintStripsUnnecessaryLeadingZeros() throws {
|
|
||||||
let secret = Constants.ecdsa256Secret
|
|
||||||
|
|
||||||
// r has a leading 0x00 followed by 0x01 (< 0x80): the mpint must not keep the leading zero.
|
|
||||||
let rBytes: [UInt8] = [0x00] + (1...31).map { UInt8($0) }
|
|
||||||
let r = Data(rBytes)
|
|
||||||
// s has two leading 0x00 bytes followed by 0x7f (< 0x80): the mpint must not keep the leading zeros.
|
|
||||||
let sBytes: [UInt8] = [0x00, 0x00, 0x7f] + Array(repeating: UInt8(0x01), count: 29)
|
|
||||||
let s = Data(sBytes)
|
|
||||||
let rawRepresentation = r + s
|
|
||||||
|
|
||||||
let response = writer.data(secret: secret, signature: rawRepresentation)
|
|
||||||
let (parsedR, parsedS) = try parseEcdsaSignatureMpints(from: response)
|
|
||||||
|
|
||||||
#expect(parsedR == Data((1...31).map { UInt8($0) }))
|
|
||||||
#expect(parsedS == Data([0x7f] + Array(repeating: UInt8(0x01), count: 29)))
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test func ecdsaMpintPrefixesZeroWhenHighBitSet() throws {
|
|
||||||
let secret = Constants.ecdsa256Secret
|
|
||||||
|
|
||||||
// r starts with 0x80 (high bit set): mpint must be prefixed with 0x00.
|
|
||||||
let r = Data([UInt8(0x80)] + Array(repeating: UInt8(0x01), count: 31))
|
|
||||||
let s = Data([UInt8(0x01)] + Array(repeating: UInt8(0x02), count: 31))
|
|
||||||
let rawRepresentation = r + s
|
|
||||||
|
|
||||||
let response = writer.data(secret: secret, signature: rawRepresentation)
|
|
||||||
let (parsedR, parsedS) = try parseEcdsaSignatureMpints(from: response)
|
|
||||||
|
|
||||||
#expect(parsedR == Data([0x00, 0x80] + Array(repeating: UInt8(0x01), count: 31)))
|
|
||||||
#expect(parsedS == Data([0x01] + Array(repeating: UInt8(0x02), count: 31)))
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private extension OpenSSHSignatureWriterTests {
|
|
||||||
|
|
||||||
enum Constants {
|
|
||||||
static let ecdsa256Secret = TestSecret(
|
|
||||||
id: Data(),
|
|
||||||
name: "Test Key (ECDSA 256)",
|
|
||||||
publicKey: Data(repeating: 0x01, count: 65),
|
|
||||||
attributes: Attributes(
|
|
||||||
keyType: KeyType(algorithm: .ecdsa, size: 256),
|
|
||||||
authentication: .notRequired,
|
|
||||||
publicKeyAttribution: "test@example.com"
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
enum ParseError: Error {
|
|
||||||
case eof
|
|
||||||
case invalidAlgorithm
|
|
||||||
}
|
|
||||||
|
|
||||||
func parseEcdsaSignatureMpints(from openSSHSignedData: Data) throws -> (r: Data, s: Data) {
|
|
||||||
let reader = OpenSSHReader(data: openSSHSignedData)
|
|
||||||
|
|
||||||
// Prefix
|
|
||||||
_ = try reader.readNextBytes(as: UInt32.self)
|
|
||||||
|
|
||||||
let algorithm = try reader.readNextChunkAsString()
|
|
||||||
guard algorithm == "ecdsa-sha2-nistp256" else {
|
|
||||||
throw ParseError.invalidAlgorithm
|
|
||||||
}
|
|
||||||
|
|
||||||
let sigReader = try reader.readNextChunkAsSubReader()
|
|
||||||
let r = try sigReader.readNextChunk()
|
|
||||||
let s = try sigReader.readNextChunk()
|
|
||||||
return (r, s)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
import Foundation
|
|
||||||
import SecretKit
|
|
||||||
|
|
||||||
public struct TestSecret: SecretKit.Secret {
|
|
||||||
|
|
||||||
public let id: Data
|
|
||||||
public let name: String
|
|
||||||
public let publicKey: Data
|
|
||||||
public var attributes: Attributes
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,7 +1,6 @@
|
|||||||
import Foundation
|
import Foundation
|
||||||
import Testing
|
import Testing
|
||||||
import CryptoKit
|
import CryptoKit
|
||||||
@testable import SSHProtocolKit
|
|
||||||
@testable import SecretKit
|
@testable import SecretKit
|
||||||
@testable import SecretAgentKit
|
@testable import SecretAgentKit
|
||||||
|
|
||||||
@@ -45,8 +44,8 @@ import CryptoKit
|
|||||||
let agent = Agent(storeList: list)
|
let agent = Agent(storeList: list)
|
||||||
let response = await agent.handle(request: request, provenance: .test)
|
let response = await agent.handle(request: request, provenance: .test)
|
||||||
let responseReader = OpenSSHReader(data: response)
|
let responseReader = OpenSSHReader(data: response)
|
||||||
let length = try responseReader.readNextBytes(as: UInt32.self)
|
let length = try responseReader.readNextBytes(as: UInt32.self).bigEndian
|
||||||
let type = try responseReader.readNextBytes(as: UInt8.self)
|
let type = try responseReader.readNextBytes(as: UInt8.self).bigEndian
|
||||||
#expect(length == response.count - MemoryLayout<UInt32>.size)
|
#expect(length == response.count - MemoryLayout<UInt32>.size)
|
||||||
#expect(type == SSHAgent.Response.agentSignResponse.rawValue)
|
#expect(type == SSHAgent.Response.agentSignResponse.rawValue)
|
||||||
let outer = OpenSSHReader(data: responseReader.remaining)
|
let outer = OpenSSHReader(data: responseReader.remaining)
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
import Foundation
|
import Foundation
|
||||||
import Testing
|
import Testing
|
||||||
import SSHProtocolKit
|
@testable import SecretAgentKit
|
||||||
|
@testable import SecureEnclaveSecretKit
|
||||||
|
@testable import SmartCardSecretKit
|
||||||
|
|
||||||
@Suite struct OpenSSHReaderTests {
|
@Suite struct OpenSSHReaderTests {
|
||||||
|
|
||||||
@@ -1,7 +1,6 @@
|
|||||||
import Foundation
|
import Foundation
|
||||||
import SecretKit
|
import SecretKit
|
||||||
import CryptoKit
|
import CryptoKit
|
||||||
import SSHProtocolKit
|
|
||||||
|
|
||||||
struct Stub {}
|
struct Stub {}
|
||||||
|
|
||||||
|
|||||||
@@ -6,6 +6,8 @@ import SmartCardSecretKit
|
|||||||
import SecretAgentKit
|
import SecretAgentKit
|
||||||
import Brief
|
import Brief
|
||||||
import Observation
|
import Observation
|
||||||
|
import SSHProtocolKit
|
||||||
|
import CertificateKit
|
||||||
import Common
|
import Common
|
||||||
|
|
||||||
@main
|
@main
|
||||||
@@ -16,13 +18,15 @@ class AppDelegate: NSObject, NSApplicationDelegate {
|
|||||||
let cryptoKit = SecureEnclave.Store()
|
let cryptoKit = SecureEnclave.Store()
|
||||||
let migrator = SecureEnclave.CryptoKitMigrator()
|
let migrator = SecureEnclave.CryptoKitMigrator()
|
||||||
try? migrator.migrate(to: cryptoKit)
|
try? migrator.migrate(to: cryptoKit)
|
||||||
|
let certsMigrator = CertificateKitMigrator(homeDirectory: URL.homeDirectory)
|
||||||
|
try? certsMigrator.migrate()
|
||||||
list.add(store: cryptoKit)
|
list.add(store: cryptoKit)
|
||||||
list.add(store: SmartCard.Store())
|
list.add(store: SmartCard.Store())
|
||||||
return list
|
return list
|
||||||
}()
|
}()
|
||||||
private let updater = Updater(checkOnLaunch: true)
|
private let updater = Updater(checkOnLaunch: true)
|
||||||
private let notifier = Notifier()
|
private let notifier = Notifier()
|
||||||
private let publicKeyFileStoreController = PublicKeyFileStoreController(directory: URL.publicKeyDirectory)
|
private let publicKeyFileStoreController = PublicKeyFileStoreController(homeDirectory: URL.homeDirectory)
|
||||||
private lazy var agent: Agent = {
|
private lazy var agent: Agent = {
|
||||||
Agent(storeList: storeList, witness: notifier)
|
Agent(storeList: storeList, witness: notifier)
|
||||||
}()
|
}()
|
||||||
@@ -35,9 +39,9 @@ class AppDelegate: NSObject, NSApplicationDelegate {
|
|||||||
func applicationDidFinishLaunching(_ aNotification: Notification) {
|
func applicationDidFinishLaunching(_ aNotification: Notification) {
|
||||||
logger.debug("SecretAgent finished launching")
|
logger.debug("SecretAgent finished launching")
|
||||||
Task {
|
Task {
|
||||||
|
let inputParser = try await XPCAgentInputParser()
|
||||||
for await session in socketController.sessions {
|
for await session in socketController.sessions {
|
||||||
Task {
|
Task {
|
||||||
let inputParser = try await XPCAgentInputParser()
|
|
||||||
do {
|
do {
|
||||||
for await message in session.messages {
|
for await message in session.messages {
|
||||||
let request = try await inputParser.parse(data: message)
|
let request = try await inputParser.parse(data: message)
|
||||||
|
|||||||
@@ -4,22 +4,16 @@
|
|||||||
<dict>
|
<dict>
|
||||||
<key>com.apple.security.hardened-process</key>
|
<key>com.apple.security.hardened-process</key>
|
||||||
<true/>
|
<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>
|
<key>com.apple.security.hardened-process.dyld-ro</key>
|
||||||
<true/>
|
<true/>
|
||||||
<key>com.apple.security.hardened-process.enhanced-security-version-string</key>
|
<key>com.apple.security.hardened-process.enhanced-security-version</key>
|
||||||
<string>1</string>
|
<integer>1</integer>
|
||||||
<key>com.apple.security.hardened-process.hardened-heap</key>
|
<key>com.apple.security.hardened-process.hardened-heap</key>
|
||||||
<true/>
|
<true/>
|
||||||
|
<key>com.apple.security.hardened-process.platform-restrictions</key>
|
||||||
|
<integer>2</integer>
|
||||||
<key>com.apple.security.smartcard</key>
|
<key>com.apple.security.smartcard</key>
|
||||||
<true/>
|
<true/>
|
||||||
<key>com.apple.security.hardened-process.platform-restrictions-string</key>
|
|
||||||
<string>2</string>
|
|
||||||
<key>keychain-access-groups</key>
|
<key>keychain-access-groups</key>
|
||||||
<array>
|
<array>
|
||||||
<string>$(AppIdentifierPrefix)com.maxgoedjen.Secretive</string>
|
<string>$(AppIdentifierPrefix)com.maxgoedjen.Secretive</string>
|
||||||
|
|||||||
@@ -1,9 +1,8 @@
|
|||||||
import Foundation
|
import Foundation
|
||||||
import SecretAgentKit
|
|
||||||
import Brief
|
|
||||||
import XPCWrappers
|
|
||||||
import OSLog
|
import OSLog
|
||||||
import SSHProtocolKit
|
import SSHProtocolKit
|
||||||
|
import Brief
|
||||||
|
import XPCWrappers
|
||||||
|
|
||||||
/// Delegates all agent input parsing to an XPC service which wraps OpenSSH
|
/// Delegates all agent input parsing to an XPC service which wraps OpenSSH
|
||||||
public final class XPCAgentInputParser: SSHAgentInputParserProtocol {
|
public final class XPCAgentInputParser: SSHAgentInputParserProtocol {
|
||||||
|
|||||||
@@ -12,11 +12,11 @@
|
|||||||
<true/>
|
<true/>
|
||||||
<key>com.apple.security.hardened-process.dyld-ro</key>
|
<key>com.apple.security.hardened-process.dyld-ro</key>
|
||||||
<true/>
|
<true/>
|
||||||
|
<key>com.apple.security.hardened-process.enhanced-security-version</key>
|
||||||
|
<integer>1</integer>
|
||||||
<key>com.apple.security.hardened-process.hardened-heap</key>
|
<key>com.apple.security.hardened-process.hardened-heap</key>
|
||||||
<true/>
|
<true/>
|
||||||
<key>com.apple.security.hardened-process.enhanced-security-version-string</key>
|
<key>com.apple.security.hardened-process.platform-restrictions</key>
|
||||||
<string>1</string>
|
<integer>2</integer>
|
||||||
<key>com.apple.security.hardened-process.platform-restrictions-string</key>
|
|
||||||
<string>2</string>
|
|
||||||
</dict>
|
</dict>
|
||||||
</plist>
|
</plist>
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import Foundation
|
import Foundation
|
||||||
import OSLog
|
import OSLog
|
||||||
import XPCWrappers
|
import XPCWrappers
|
||||||
import SecretAgentKit
|
|
||||||
import SSHProtocolKit
|
import SSHProtocolKit
|
||||||
|
|
||||||
final class SecretAgentInputParser: NSObject, XPCProtocol {
|
final class SecretAgentInputParser: NSObject, XPCProtocol {
|
||||||
|
|||||||
@@ -9,7 +9,6 @@
|
|||||||
/* Begin PBXBuildFile section */
|
/* Begin PBXBuildFile section */
|
||||||
2C4A9D2F2636FFD3008CC8E2 /* EditSecretView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C4A9D2E2636FFD3008CC8E2 /* EditSecretView.swift */; };
|
2C4A9D2F2636FFD3008CC8E2 /* EditSecretView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C4A9D2E2636FFD3008CC8E2 /* EditSecretView.swift */; };
|
||||||
50020BB024064869003D4025 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50020BAF24064869003D4025 /* AppDelegate.swift */; };
|
50020BB024064869003D4025 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50020BAF24064869003D4025 /* AppDelegate.swift */; };
|
||||||
5002C3AB2EEF483300FFAD22 /* XPCWrappers in Frameworks */ = {isa = PBXBuildFile; productRef = 5002C3AA2EEF483300FFAD22 /* XPCWrappers */; };
|
|
||||||
5003EF3B278005E800DF2006 /* SecretKit in Frameworks */ = {isa = PBXBuildFile; productRef = 5003EF3A278005E800DF2006 /* SecretKit */; };
|
5003EF3B278005E800DF2006 /* SecretKit in Frameworks */ = {isa = PBXBuildFile; productRef = 5003EF3A278005E800DF2006 /* SecretKit */; };
|
||||||
5003EF3D278005F300DF2006 /* Brief in Frameworks */ = {isa = PBXBuildFile; productRef = 5003EF3C278005F300DF2006 /* Brief */; };
|
5003EF3D278005F300DF2006 /* Brief in Frameworks */ = {isa = PBXBuildFile; productRef = 5003EF3C278005F300DF2006 /* Brief */; };
|
||||||
5003EF3F278005F300DF2006 /* SecretAgentKit in Frameworks */ = {isa = PBXBuildFile; productRef = 5003EF3E278005F300DF2006 /* SecretAgentKit */; };
|
5003EF3F278005F300DF2006 /* SecretAgentKit in Frameworks */ = {isa = PBXBuildFile; productRef = 5003EF3E278005F300DF2006 /* SecretAgentKit */; };
|
||||||
@@ -31,6 +30,8 @@
|
|||||||
504788F62E68206F00B4556F /* GettingStartedView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 504788F52E68206F00B4556F /* GettingStartedView.swift */; };
|
504788F62E68206F00B4556F /* GettingStartedView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 504788F52E68206F00B4556F /* GettingStartedView.swift */; };
|
||||||
504789232E697DD300B4556F /* BoxBackgroundStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 504789222E697DD300B4556F /* BoxBackgroundStyle.swift */; };
|
504789232E697DD300B4556F /* BoxBackgroundStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 504789222E697DD300B4556F /* BoxBackgroundStyle.swift */; };
|
||||||
50571E0324393C2600F76F6C /* JustUpdatedChecker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50571E0224393C2600F76F6C /* JustUpdatedChecker.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 */; };
|
50617D8323FCE48E0099B055 /* App.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50617D8223FCE48E0099B055 /* App.swift */; };
|
||||||
50617D8523FCE48E0099B055 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50617D8423FCE48E0099B055 /* ContentView.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 */; };
|
50617D8A23FCE48E0099B055 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 50617D8923FCE48E0099B055 /* Preview Assets.xcassets */; };
|
||||||
@@ -47,7 +48,6 @@
|
|||||||
50692E5B2E6FF9D20043C7BB /* SecretAgentInputParser.xpc in Embed XPC Services */ = {isa = PBXBuildFile; fileRef = 50692E502E6FF9D20043C7BB /* SecretAgentInputParser.xpc */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
|
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 */; };
|
50692E682E6FF9E20043C7BB /* main.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50692E632E6FF9E20043C7BB /* main.swift */; };
|
||||||
50692E692E6FF9E20043C7BB /* SecretAgentInputParser.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50692E642E6FF9E20043C7BB /* SecretAgentInputParser.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, ); }; };
|
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, ); }; };
|
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 */; };
|
5079BA0F250F29BF00EA86F4 /* StoreListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5079BA0E250F29BF00EA86F4 /* StoreListView.swift */; };
|
||||||
@@ -61,7 +61,6 @@
|
|||||||
50A3B79424026B7600D209EA /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 50A3B79324026B7600D209EA /* Preview Assets.xcassets */; };
|
50A3B79424026B7600D209EA /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 50A3B79324026B7600D209EA /* Preview Assets.xcassets */; };
|
||||||
50A3B79724026B7600D209EA /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 50A3B79524026B7600D209EA /* Main.storyboard */; };
|
50A3B79724026B7600D209EA /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 50A3B79524026B7600D209EA /* Main.storyboard */; };
|
||||||
50AE97002E5C1A420018C710 /* IntegrationsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50AE96FF2E5C1A420018C710 /* IntegrationsView.swift */; };
|
50AE97002E5C1A420018C710 /* IntegrationsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50AE96FF2E5C1A420018C710 /* IntegrationsView.swift */; };
|
||||||
50B832C02F62202A00D2FCB8 /* InternetAccessPolicy.plist in Resources */ = {isa = PBXBuildFile; fileRef = 50692BA52E6D5CC90043C7BB /* InternetAccessPolicy.plist */; };
|
|
||||||
50B8550D24138C4F009958AC /* DeleteSecretView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50B8550C24138C4F009958AC /* DeleteSecretView.swift */; };
|
50B8550D24138C4F009958AC /* DeleteSecretView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50B8550C24138C4F009958AC /* DeleteSecretView.swift */; };
|
||||||
50BB046B2418AAAE00D6E079 /* EmptyStoreView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50BB046A2418AAAE00D6E079 /* EmptyStoreView.swift */; };
|
50BB046B2418AAAE00D6E079 /* EmptyStoreView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50BB046A2418AAAE00D6E079 /* EmptyStoreView.swift */; };
|
||||||
50BDCB722E63BAF20072D2E7 /* AgentStatusView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50BDCB712E63BAF20072D2E7 /* AgentStatusView.swift */; };
|
50BDCB722E63BAF20072D2E7 /* AgentStatusView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50BDCB712E63BAF20072D2E7 /* AgentStatusView.swift */; };
|
||||||
@@ -75,6 +74,13 @@
|
|||||||
50E4C4C32E7765DF00C73783 /* AboutView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50E4C4C22E7765DF00C73783 /* AboutView.swift */; };
|
50E4C4C32E7765DF00C73783 /* AboutView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50E4C4C22E7765DF00C73783 /* AboutView.swift */; };
|
||||||
50E4C4C82E777E4200C73783 /* AppIcon.icon in Resources */ = {isa = PBXBuildFile; fileRef = 50E4C4C72E777E4200C73783 /* AppIcon.icon */; };
|
50E4C4C82E777E4200C73783 /* AppIcon.icon in Resources */ = {isa = PBXBuildFile; fileRef = 50E4C4C72E777E4200C73783 /* AppIcon.icon */; };
|
||||||
50E4C4C92E777E4200C73783 /* 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 */
|
/* End PBXBuildFile section */
|
||||||
|
|
||||||
/* Begin PBXContainerItemProxy section */
|
/* Begin PBXContainerItemProxy section */
|
||||||
@@ -120,6 +126,13 @@
|
|||||||
remoteGlobalIDString = 50692E4F2E6FF9D20043C7BB;
|
remoteGlobalIDString = 50692E4F2E6FF9D20043C7BB;
|
||||||
remoteInfo = SecretAgentInputParser;
|
remoteInfo = SecretAgentInputParser;
|
||||||
};
|
};
|
||||||
|
50E4C4D72E77C4B300C73783 /* PBXContainerItemProxy */ = {
|
||||||
|
isa = PBXContainerItemProxy;
|
||||||
|
containerPortal = 50617D7723FCE48D0099B055 /* Project object */;
|
||||||
|
proxyType = 1;
|
||||||
|
remoteGlobalIDString = 50E4C4CD2E77C4B300C73783;
|
||||||
|
remoteInfo = SecretiveCertificateParser;
|
||||||
|
};
|
||||||
/* End PBXContainerItemProxy section */
|
/* End PBXContainerItemProxy section */
|
||||||
|
|
||||||
/* Begin PBXCopyFilesBuildPhase section */
|
/* Begin PBXCopyFilesBuildPhase section */
|
||||||
@@ -130,6 +143,7 @@
|
|||||||
dstSubfolderSpec = 16;
|
dstSubfolderSpec = 16;
|
||||||
files = (
|
files = (
|
||||||
50692D1D2E6FDB880043C7BB /* SecretiveUpdater.xpc in Embed XPC Services */,
|
50692D1D2E6FDB880043C7BB /* SecretiveUpdater.xpc in Embed XPC Services */,
|
||||||
|
50E4C4D92E77C4B300C73783 /* SecretiveCertificateParser.xpc in Embed XPC Services */,
|
||||||
50692E5B2E6FF9D20043C7BB /* SecretAgentInputParser.xpc in Embed XPC Services */,
|
50692E5B2E6FF9D20043C7BB /* SecretAgentInputParser.xpc in Embed XPC Services */,
|
||||||
);
|
);
|
||||||
name = "Embed XPC Services";
|
name = "Embed XPC Services";
|
||||||
@@ -183,8 +197,6 @@
|
|||||||
2C4A9D2E2636FFD3008CC8E2 /* EditSecretView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditSecretView.swift; sourceTree = "<group>"; };
|
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>"; };
|
50020BAF24064869003D4025 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
|
||||||
5003EF39278005C800DF2006 /* Packages */ = {isa = PBXFileReference; lastKnownFileType = wrapper; path = Packages; sourceTree = "<group>"; };
|
5003EF39278005C800DF2006 /* Packages */ = {isa = PBXFileReference; lastKnownFileType = wrapper; path = Packages; sourceTree = "<group>"; };
|
||||||
500666D02F04786900328939 /* SecretiveUpdater.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = SecretiveUpdater.entitlements; sourceTree = "<group>"; };
|
|
||||||
500666D12F04787200328939 /* SecretAgentInputParser.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = SecretAgentInputParser.entitlements; sourceTree = "<group>"; };
|
|
||||||
5008C23D2E525D8200507AC2 /* Localizable.xcstrings */ = {isa = PBXFileReference; lastKnownFileType = text.json.xcstrings; name = Localizable.xcstrings; path = Packages/Resources/Localizable.xcstrings; sourceTree = SOURCE_ROOT; };
|
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>"; };
|
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>"; };
|
50153E21250DECA300525160 /* SecretListItemView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SecretListItemView.swift; sourceTree = "<group>"; };
|
||||||
@@ -196,6 +208,7 @@
|
|||||||
504789222E697DD300B4556F /* BoxBackgroundStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BoxBackgroundStyle.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>"; };
|
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>"; };
|
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; };
|
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>"; };
|
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>"; };
|
50617D8423FCE48E0099B055 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = "<group>"; };
|
||||||
@@ -241,7 +254,13 @@
|
|||||||
50E4C4522E73C78900C73783 /* WindowBackgroundStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WindowBackgroundStyle.swift; sourceTree = "<group>"; };
|
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>"; };
|
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>"; };
|
50E4C4C72E777E4200C73783 /* AppIcon.icon */ = {isa = PBXFileReference; lastKnownFileType = folder.iconcomposer.icon; path = AppIcon.icon; sourceTree = "<group>"; };
|
||||||
F418C9A82F0C57F000E9ADF8 /* OpenSource.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = OpenSource.xcconfig; 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 */
|
/* End PBXFileReference section */
|
||||||
|
|
||||||
/* Begin PBXFrameworksBuildPhase section */
|
/* Begin PBXFrameworksBuildPhase section */
|
||||||
@@ -252,6 +271,7 @@
|
|||||||
50E0145C2EDB9CDF00B121F1 /* Common in Frameworks */,
|
50E0145C2EDB9CDF00B121F1 /* Common in Frameworks */,
|
||||||
5003EF3B278005E800DF2006 /* SecretKit in Frameworks */,
|
5003EF3B278005E800DF2006 /* SecretKit in Frameworks */,
|
||||||
501421622781262300BBAA70 /* Brief in Frameworks */,
|
501421622781262300BBAA70 /* Brief in Frameworks */,
|
||||||
|
505993532E7E70C90092CFFA /* CertificateKit in Frameworks */,
|
||||||
5003EF5F2780081600DF2006 /* SecureEnclaveSecretKit in Frameworks */,
|
5003EF5F2780081600DF2006 /* SecureEnclaveSecretKit in Frameworks */,
|
||||||
5003EF612780081600DF2006 /* SmartCardSecretKit in Frameworks */,
|
5003EF612780081600DF2006 /* SmartCardSecretKit in Frameworks */,
|
||||||
);
|
);
|
||||||
@@ -270,8 +290,8 @@
|
|||||||
isa = PBXFrameworksBuildPhase;
|
isa = PBXFrameworksBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
5002C3AB2EEF483300FFAD22 /* XPCWrappers in Frameworks */,
|
50E4C4F32E77CAC600C73783 /* XPCWrappers in Frameworks */,
|
||||||
50692E6C2E6FFA510043C7BB /* SecretAgentKit in Frameworks */,
|
50E4C4F12E77C90300C73783 /* SSHProtocolKit in Frameworks */,
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
@@ -288,6 +308,15 @@
|
|||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
|
50E4C4CB2E77C4B300C73783 /* Frameworks */ = {
|
||||||
|
isa = PBXFrameworksBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
50E4C4EF2E77C8FC00C73783 /* SSHProtocolKit in Frameworks */,
|
||||||
|
50E4C4ED2E77C55E00C73783 /* XPCWrappers in Frameworks */,
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
};
|
||||||
/* End PBXFrameworksBuildPhase section */
|
/* End PBXFrameworksBuildPhase section */
|
||||||
|
|
||||||
/* Begin PBXGroup section */
|
/* Begin PBXGroup section */
|
||||||
@@ -352,6 +381,7 @@
|
|||||||
508A58AF241E144C0069DC07 /* Config */,
|
508A58AF241E144C0069DC07 /* Config */,
|
||||||
50692D272E6FDB8D0043C7BB /* SecretiveUpdater */,
|
50692D272E6FDB8D0043C7BB /* SecretiveUpdater */,
|
||||||
50692E662E6FF9E20043C7BB /* SecretAgentInputParser */,
|
50692E662E6FF9E20043C7BB /* SecretAgentInputParser */,
|
||||||
|
50E4C4E72E77C53000C73783 /* SecretiveCertificateParser */,
|
||||||
50617D8023FCE48E0099B055 /* Products */,
|
50617D8023FCE48E0099B055 /* Products */,
|
||||||
5099A08B240243730062B6F2 /* Frameworks */,
|
5099A08B240243730062B6F2 /* Frameworks */,
|
||||||
);
|
);
|
||||||
@@ -364,6 +394,7 @@
|
|||||||
50A3B78A24026B7500D209EA /* SecretAgent.app */,
|
50A3B78A24026B7500D209EA /* SecretAgent.app */,
|
||||||
50692D122E6FDB880043C7BB /* SecretiveUpdater.xpc */,
|
50692D122E6FDB880043C7BB /* SecretiveUpdater.xpc */,
|
||||||
50692E502E6FF9D20043C7BB /* SecretAgentInputParser.xpc */,
|
50692E502E6FF9D20043C7BB /* SecretAgentInputParser.xpc */,
|
||||||
|
50E4C4CE2E77C4B300C73783 /* SecretiveCertificateParser.xpc */,
|
||||||
);
|
);
|
||||||
name = Products;
|
name = Products;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@@ -398,7 +429,7 @@
|
|||||||
50692D272E6FDB8D0043C7BB /* SecretiveUpdater */ = {
|
50692D272E6FDB8D0043C7BB /* SecretiveUpdater */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
500666D02F04786900328939 /* SecretiveUpdater.entitlements */,
|
50E4C4E12E77C4FA00C73783 /* SecretiveUpdater.entitlements */,
|
||||||
50692D232E6FDB8D0043C7BB /* Info.plist */,
|
50692D232E6FDB8D0043C7BB /* Info.plist */,
|
||||||
50692BA52E6D5CC90043C7BB /* InternetAccessPolicy.plist */,
|
50692BA52E6D5CC90043C7BB /* InternetAccessPolicy.plist */,
|
||||||
50692D242E6FDB8D0043C7BB /* main.swift */,
|
50692D242E6FDB8D0043C7BB /* main.swift */,
|
||||||
@@ -410,7 +441,7 @@
|
|||||||
50692E662E6FF9E20043C7BB /* SecretAgentInputParser */ = {
|
50692E662E6FF9E20043C7BB /* SecretAgentInputParser */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
500666D12F04787200328939 /* SecretAgentInputParser.entitlements */,
|
50E4C4E02E77C4EE00C73783 /* SecretAgentInputParser.entitlements */,
|
||||||
50692E622E6FF9E20043C7BB /* Info.plist */,
|
50692E622E6FF9E20043C7BB /* Info.plist */,
|
||||||
50692E632E6FF9E20043C7BB /* main.swift */,
|
50692E632E6FF9E20043C7BB /* main.swift */,
|
||||||
50692E642E6FF9E20043C7BB /* SecretAgentInputParser.swift */,
|
50692E642E6FF9E20043C7BB /* SecretAgentInputParser.swift */,
|
||||||
@@ -423,7 +454,6 @@
|
|||||||
children = (
|
children = (
|
||||||
508A590F241EEF6D0069DC07 /* Secretive.xctestplan */,
|
508A590F241EEF6D0069DC07 /* Secretive.xctestplan */,
|
||||||
508A58AB241E121B0069DC07 /* Config.xcconfig */,
|
508A58AB241E121B0069DC07 /* Config.xcconfig */,
|
||||||
F418C9A82F0C57F000E9ADF8 /* OpenSource.xcconfig */,
|
|
||||||
);
|
);
|
||||||
path = Config;
|
path = Config;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@@ -445,6 +475,7 @@
|
|||||||
508A58B2241ED2180069DC07 /* AgentStatusChecker.swift */,
|
508A58B2241ED2180069DC07 /* AgentStatusChecker.swift */,
|
||||||
5091D2BB25183B830049FD9B /* ApplicationDirectoryController.swift */,
|
5091D2BB25183B830049FD9B /* ApplicationDirectoryController.swift */,
|
||||||
50571E0224393C2600F76F6C /* JustUpdatedChecker.swift */,
|
50571E0224393C2600F76F6C /* JustUpdatedChecker.swift */,
|
||||||
|
505993502E7E59F70092CFFA /* XPCCertificateParser.swift */,
|
||||||
);
|
);
|
||||||
path = Controllers;
|
path = Controllers;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@@ -479,6 +510,17 @@
|
|||||||
path = "Preview Content";
|
path = "Preview Content";
|
||||||
sourceTree = "<group>";
|
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 */
|
/* End PBXGroup section */
|
||||||
|
|
||||||
/* Begin PBXNativeTarget section */
|
/* Begin PBXNativeTarget section */
|
||||||
@@ -499,6 +541,7 @@
|
|||||||
50142167278126B500BBAA70 /* PBXTargetDependency */,
|
50142167278126B500BBAA70 /* PBXTargetDependency */,
|
||||||
50692D1C2E6FDB880043C7BB /* PBXTargetDependency */,
|
50692D1C2E6FDB880043C7BB /* PBXTargetDependency */,
|
||||||
50692E5A2E6FF9D20043C7BB /* PBXTargetDependency */,
|
50692E5A2E6FF9D20043C7BB /* PBXTargetDependency */,
|
||||||
|
50E4C4D82E77C4B300C73783 /* PBXTargetDependency */,
|
||||||
);
|
);
|
||||||
name = Secretive;
|
name = Secretive;
|
||||||
packageProductDependencies = (
|
packageProductDependencies = (
|
||||||
@@ -506,6 +549,7 @@
|
|||||||
5003EF5E2780081600DF2006 /* SecureEnclaveSecretKit */,
|
5003EF5E2780081600DF2006 /* SecureEnclaveSecretKit */,
|
||||||
5003EF602780081600DF2006 /* SmartCardSecretKit */,
|
5003EF602780081600DF2006 /* SmartCardSecretKit */,
|
||||||
501421612781262300BBAA70 /* Brief */,
|
501421612781262300BBAA70 /* Brief */,
|
||||||
|
505993522E7E70C90092CFFA /* CertificateKit */,
|
||||||
50E0145B2EDB9CDF00B121F1 /* Common */,
|
50E0145B2EDB9CDF00B121F1 /* Common */,
|
||||||
);
|
);
|
||||||
productName = Secretive;
|
productName = Secretive;
|
||||||
@@ -547,8 +591,8 @@
|
|||||||
);
|
);
|
||||||
name = SecretAgentInputParser;
|
name = SecretAgentInputParser;
|
||||||
packageProductDependencies = (
|
packageProductDependencies = (
|
||||||
50692E6B2E6FFA510043C7BB /* SecretAgentKit */,
|
50E4C4F02E77C90300C73783 /* SSHProtocolKit */,
|
||||||
5002C3AA2EEF483300FFAD22 /* XPCWrappers */,
|
50E4C4F22E77CAC600C73783 /* XPCWrappers */,
|
||||||
);
|
);
|
||||||
productName = SecretAgentInputParser;
|
productName = SecretAgentInputParser;
|
||||||
productReference = 50692E502E6FF9D20043C7BB /* SecretAgentInputParser.xpc */;
|
productReference = 50692E502E6FF9D20043C7BB /* SecretAgentInputParser.xpc */;
|
||||||
@@ -584,6 +628,27 @@
|
|||||||
productReference = 50A3B78A24026B7500D209EA /* SecretAgent.app */;
|
productReference = 50A3B78A24026B7500D209EA /* SecretAgent.app */;
|
||||||
productType = "com.apple.product-type.application";
|
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 */
|
/* End PBXNativeTarget section */
|
||||||
|
|
||||||
/* Begin PBXProject section */
|
/* Begin PBXProject section */
|
||||||
@@ -592,7 +657,7 @@
|
|||||||
attributes = {
|
attributes = {
|
||||||
BuildIndependentTargetsInParallel = YES;
|
BuildIndependentTargetsInParallel = YES;
|
||||||
LastSwiftUpdateCheck = 2600;
|
LastSwiftUpdateCheck = 2600;
|
||||||
LastUpgradeCheck = 2640;
|
LastUpgradeCheck = 2600;
|
||||||
ORGANIZATIONNAME = "Max Goedjen";
|
ORGANIZATIONNAME = "Max Goedjen";
|
||||||
TargetAttributes = {
|
TargetAttributes = {
|
||||||
50617D7E23FCE48D0099B055 = {
|
50617D7E23FCE48D0099B055 = {
|
||||||
@@ -607,6 +672,9 @@
|
|||||||
50A3B78924026B7500D209EA = {
|
50A3B78924026B7500D209EA = {
|
||||||
CreatedOnToolsVersion = 11.4;
|
CreatedOnToolsVersion = 11.4;
|
||||||
};
|
};
|
||||||
|
50E4C4CD2E77C4B300C73783 = {
|
||||||
|
CreatedOnToolsVersion = 26.0;
|
||||||
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
buildConfigurationList = 50617D7A23FCE48D0099B055 /* Build configuration list for PBXProject "Secretive" */;
|
buildConfigurationList = 50617D7A23FCE48D0099B055 /* Build configuration list for PBXProject "Secretive" */;
|
||||||
@@ -634,6 +702,7 @@
|
|||||||
50A3B78924026B7500D209EA /* SecretAgent */,
|
50A3B78924026B7500D209EA /* SecretAgent */,
|
||||||
50692D112E6FDB880043C7BB /* SecretiveUpdater */,
|
50692D112E6FDB880043C7BB /* SecretiveUpdater */,
|
||||||
50692E4F2E6FF9D20043C7BB /* SecretAgentInputParser */,
|
50692E4F2E6FF9D20043C7BB /* SecretAgentInputParser */,
|
||||||
|
50E4C4CD2E77C4B300C73783 /* SecretiveCertificateParser */,
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
/* End PBXProject section */
|
/* End PBXProject section */
|
||||||
@@ -654,7 +723,6 @@
|
|||||||
isa = PBXResourcesBuildPhase;
|
isa = PBXResourcesBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
50B832C02F62202A00D2FCB8 /* InternetAccessPolicy.plist in Resources */,
|
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
@@ -677,6 +745,13 @@
|
|||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
|
50E4C4CC2E77C4B300C73783 /* Resources */ = {
|
||||||
|
isa = PBXResourcesBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
};
|
||||||
/* End PBXResourcesBuildPhase section */
|
/* End PBXResourcesBuildPhase section */
|
||||||
|
|
||||||
/* Begin PBXSourcesBuildPhase section */
|
/* Begin PBXSourcesBuildPhase section */
|
||||||
@@ -696,6 +771,7 @@
|
|||||||
50617D8523FCE48E0099B055 /* ContentView.swift in Sources */,
|
50617D8523FCE48E0099B055 /* ContentView.swift in Sources */,
|
||||||
504788F62E68206F00B4556F /* GettingStartedView.swift in Sources */,
|
504788F62E68206F00B4556F /* GettingStartedView.swift in Sources */,
|
||||||
50CF4ABC2E601B0F005588DC /* ActionButtonStyle.swift in Sources */,
|
50CF4ABC2E601B0F005588DC /* ActionButtonStyle.swift in Sources */,
|
||||||
|
505993512E7E59FB0092CFFA /* XPCCertificateParser.swift in Sources */,
|
||||||
50571E0324393C2600F76F6C /* JustUpdatedChecker.swift in Sources */,
|
50571E0324393C2600F76F6C /* JustUpdatedChecker.swift in Sources */,
|
||||||
5079BA0F250F29BF00EA86F4 /* StoreListView.swift in Sources */,
|
5079BA0F250F29BF00EA86F4 /* StoreListView.swift in Sources */,
|
||||||
50617DD223FCEFA90099B055 /* PreviewStore.swift in Sources */,
|
50617DD223FCEFA90099B055 /* PreviewStore.swift in Sources */,
|
||||||
@@ -746,6 +822,15 @@
|
|||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
|
50E4C4CA2E77C4B300C73783 /* Sources */ = {
|
||||||
|
isa = PBXSourcesBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
50E4C4E82E77C53000C73783 /* main.swift in Sources */,
|
||||||
|
50E4C4E92E77C53000C73783 /* SecretiveCertificateParser.swift in Sources */,
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
};
|
||||||
/* End PBXSourcesBuildPhase section */
|
/* End PBXSourcesBuildPhase section */
|
||||||
|
|
||||||
/* Begin PBXTargetDependency section */
|
/* Begin PBXTargetDependency section */
|
||||||
@@ -778,6 +863,11 @@
|
|||||||
target = 50692E4F2E6FF9D20043C7BB /* SecretAgentInputParser */;
|
target = 50692E4F2E6FF9D20043C7BB /* SecretAgentInputParser */;
|
||||||
targetProxy = 50692E712E6FFA6E0043C7BB /* PBXContainerItemProxy */;
|
targetProxy = 50692E712E6FFA6E0043C7BB /* PBXContainerItemProxy */;
|
||||||
};
|
};
|
||||||
|
50E4C4D82E77C4B300C73783 /* PBXTargetDependency */ = {
|
||||||
|
isa = PBXTargetDependency;
|
||||||
|
target = 50E4C4CD2E77C4B300C73783 /* SecretiveCertificateParser */;
|
||||||
|
targetProxy = 50E4C4D72E77C4B300C73783 /* PBXContainerItemProxy */;
|
||||||
|
};
|
||||||
/* End PBXTargetDependency section */
|
/* End PBXTargetDependency section */
|
||||||
|
|
||||||
/* Begin PBXVariantGroup section */
|
/* Begin PBXVariantGroup section */
|
||||||
@@ -948,7 +1038,7 @@
|
|||||||
CURRENT_PROJECT_VERSION = 1;
|
CURRENT_PROJECT_VERSION = 1;
|
||||||
DEAD_CODE_STRIPPING = YES;
|
DEAD_CODE_STRIPPING = YES;
|
||||||
DEVELOPMENT_ASSET_PATHS = "\"Secretive/Preview Content\"";
|
DEVELOPMENT_ASSET_PATHS = "\"Secretive/Preview Content\"";
|
||||||
DEVELOPMENT_TEAM = "$(SECRETIVE_DEVELOPMENT_TEAM)";
|
DEVELOPMENT_TEAM = Z72PRUAWF6;
|
||||||
ENABLE_APP_SANDBOX = YES;
|
ENABLE_APP_SANDBOX = YES;
|
||||||
ENABLE_ENHANCED_SECURITY = YES;
|
ENABLE_ENHANCED_SECURITY = YES;
|
||||||
ENABLE_HARDENED_RUNTIME = YES;
|
ENABLE_HARDENED_RUNTIME = YES;
|
||||||
@@ -971,7 +1061,7 @@
|
|||||||
);
|
);
|
||||||
MACOSX_DEPLOYMENT_TARGET = 14.0;
|
MACOSX_DEPLOYMENT_TARGET = 14.0;
|
||||||
MARKETING_VERSION = 1;
|
MARKETING_VERSION = 1;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = "$(SECRETIVE_BASE_BUNDLE_ID).Host";
|
PRODUCT_BUNDLE_IDENTIFIER = com.maxgoedjen.Secretive.Host;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||||
};
|
};
|
||||||
@@ -988,7 +1078,7 @@
|
|||||||
CURRENT_PROJECT_VERSION = 1;
|
CURRENT_PROJECT_VERSION = 1;
|
||||||
DEAD_CODE_STRIPPING = YES;
|
DEAD_CODE_STRIPPING = YES;
|
||||||
DEVELOPMENT_ASSET_PATHS = "\"Secretive/Preview Content\"";
|
DEVELOPMENT_ASSET_PATHS = "\"Secretive/Preview Content\"";
|
||||||
DEVELOPMENT_TEAM = "$(SECRETIVE_DEVELOPMENT_TEAM)";
|
DEVELOPMENT_TEAM = Z72PRUAWF6;
|
||||||
ENABLE_APP_SANDBOX = YES;
|
ENABLE_APP_SANDBOX = YES;
|
||||||
ENABLE_ENHANCED_SECURITY = YES;
|
ENABLE_ENHANCED_SECURITY = YES;
|
||||||
ENABLE_HARDENED_RUNTIME = YES;
|
ENABLE_HARDENED_RUNTIME = YES;
|
||||||
@@ -1011,7 +1101,7 @@
|
|||||||
);
|
);
|
||||||
MACOSX_DEPLOYMENT_TARGET = 14.0;
|
MACOSX_DEPLOYMENT_TARGET = 14.0;
|
||||||
MARKETING_VERSION = 1;
|
MARKETING_VERSION = 1;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = "$(SECRETIVE_BASE_BUNDLE_ID).Host";
|
PRODUCT_BUNDLE_IDENTIFIER = com.maxgoedjen.Secretive.Host;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
PROVISIONING_PROFILE_SPECIFIER = "Secretive - Host";
|
PROVISIONING_PROFILE_SPECIFIER = "Secretive - Host";
|
||||||
};
|
};
|
||||||
@@ -1027,7 +1117,7 @@
|
|||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
COMBINE_HIDPI_IMAGES = YES;
|
COMBINE_HIDPI_IMAGES = YES;
|
||||||
CURRENT_PROJECT_VERSION = 1;
|
CURRENT_PROJECT_VERSION = 1;
|
||||||
DEVELOPMENT_TEAM = "$(SECRETIVE_DEVELOPMENT_TEAM)";
|
DEVELOPMENT_TEAM = Z72PRUAWF6;
|
||||||
ENABLE_APP_SANDBOX = YES;
|
ENABLE_APP_SANDBOX = YES;
|
||||||
ENABLE_ENHANCED_SECURITY = YES;
|
ENABLE_ENHANCED_SECURITY = YES;
|
||||||
ENABLE_HARDENED_RUNTIME = YES;
|
ENABLE_HARDENED_RUNTIME = YES;
|
||||||
@@ -1050,7 +1140,7 @@
|
|||||||
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
|
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
|
||||||
MACOSX_DEPLOYMENT_TARGET = 14.0;
|
MACOSX_DEPLOYMENT_TARGET = 14.0;
|
||||||
MARKETING_VERSION = 1.0;
|
MARKETING_VERSION = 1.0;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = "$(SECRETIVE_BASE_BUNDLE_ID).SecretiveUpdater";
|
PRODUCT_BUNDLE_IDENTIFIER = com.maxgoedjen.Secretive.SecretiveUpdater;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
REGISTER_APP_GROUPS = YES;
|
REGISTER_APP_GROUPS = YES;
|
||||||
SKIP_INSTALL = YES;
|
SKIP_INSTALL = YES;
|
||||||
@@ -1063,48 +1153,6 @@
|
|||||||
};
|
};
|
||||||
name = Debug;
|
name = Debug;
|
||||||
};
|
};
|
||||||
50692D212E6FDB880043C7BB /* Test */ = {
|
|
||||||
isa = XCBuildConfiguration;
|
|
||||||
buildSettings = {
|
|
||||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
|
|
||||||
CODE_SIGN_ENTITLEMENTS = SecretiveUpdater/SecretiveUpdater.entitlements;
|
|
||||||
CODE_SIGN_STYLE = Automatic;
|
|
||||||
COMBINE_HIDPI_IMAGES = YES;
|
|
||||||
CURRENT_PROJECT_VERSION = 1;
|
|
||||||
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;
|
|
||||||
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 = "$(SECRETIVE_BASE_BUNDLE_ID).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 */ = {
|
50692D222E6FDB880043C7BB /* Release */ = {
|
||||||
isa = XCBuildConfiguration;
|
isa = XCBuildConfiguration;
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
@@ -1115,7 +1163,7 @@
|
|||||||
COMBINE_HIDPI_IMAGES = YES;
|
COMBINE_HIDPI_IMAGES = YES;
|
||||||
CURRENT_PROJECT_VERSION = 1;
|
CURRENT_PROJECT_VERSION = 1;
|
||||||
DEVELOPMENT_TEAM = "";
|
DEVELOPMENT_TEAM = "";
|
||||||
"DEVELOPMENT_TEAM[sdk=macosx*]" = "$(SECRETIVE_DEVELOPMENT_TEAM)";
|
"DEVELOPMENT_TEAM[sdk=macosx*]" = Z72PRUAWF6;
|
||||||
ENABLE_APP_SANDBOX = YES;
|
ENABLE_APP_SANDBOX = YES;
|
||||||
ENABLE_ENHANCED_SECURITY = YES;
|
ENABLE_ENHANCED_SECURITY = YES;
|
||||||
ENABLE_HARDENED_RUNTIME = YES;
|
ENABLE_HARDENED_RUNTIME = YES;
|
||||||
@@ -1138,7 +1186,7 @@
|
|||||||
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
|
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
|
||||||
MACOSX_DEPLOYMENT_TARGET = 14.0;
|
MACOSX_DEPLOYMENT_TARGET = 14.0;
|
||||||
MARKETING_VERSION = 1.0;
|
MARKETING_VERSION = 1.0;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = "$(SECRETIVE_BASE_BUNDLE_ID).SecretiveUpdater";
|
PRODUCT_BUNDLE_IDENTIFIER = com.maxgoedjen.Secretive.SecretiveUpdater;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||||
REGISTER_APP_GROUPS = YES;
|
REGISTER_APP_GROUPS = YES;
|
||||||
@@ -1160,7 +1208,7 @@
|
|||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
COMBINE_HIDPI_IMAGES = YES;
|
COMBINE_HIDPI_IMAGES = YES;
|
||||||
CURRENT_PROJECT_VERSION = 1;
|
CURRENT_PROJECT_VERSION = 1;
|
||||||
DEVELOPMENT_TEAM = "$(SECRETIVE_DEVELOPMENT_TEAM)";
|
DEVELOPMENT_TEAM = Z72PRUAWF6;
|
||||||
ENABLE_APP_SANDBOX = YES;
|
ENABLE_APP_SANDBOX = YES;
|
||||||
ENABLE_ENHANCED_SECURITY = YES;
|
ENABLE_ENHANCED_SECURITY = YES;
|
||||||
ENABLE_HARDENED_RUNTIME = YES;
|
ENABLE_HARDENED_RUNTIME = YES;
|
||||||
@@ -1173,7 +1221,7 @@
|
|||||||
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
|
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
|
||||||
MACOSX_DEPLOYMENT_TARGET = 14.0;
|
MACOSX_DEPLOYMENT_TARGET = 14.0;
|
||||||
MARKETING_VERSION = 1.0;
|
MARKETING_VERSION = 1.0;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = "$(SECRETIVE_BASE_BUNDLE_ID).SecretAgentInputParser";
|
PRODUCT_BUNDLE_IDENTIFIER = com.maxgoedjen.Secretive.SecretAgentInputParser;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
REGISTER_APP_GROUPS = YES;
|
REGISTER_APP_GROUPS = YES;
|
||||||
SKIP_INSTALL = YES;
|
SKIP_INSTALL = YES;
|
||||||
@@ -1186,38 +1234,6 @@
|
|||||||
};
|
};
|
||||||
name = Debug;
|
name = Debug;
|
||||||
};
|
};
|
||||||
50692E5F2E6FF9D20043C7BB /* Test */ = {
|
|
||||||
isa = XCBuildConfiguration;
|
|
||||||
buildSettings = {
|
|
||||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
|
|
||||||
CODE_SIGN_ENTITLEMENTS = SecretAgentInputParser/SecretAgentInputParser.entitlements;
|
|
||||||
CODE_SIGN_STYLE = Automatic;
|
|
||||||
COMBINE_HIDPI_IMAGES = YES;
|
|
||||||
CURRENT_PROJECT_VERSION = 1;
|
|
||||||
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;
|
|
||||||
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 = "$(SECRETIVE_BASE_BUNDLE_ID).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 */ = {
|
50692E602E6FF9D20043C7BB /* Release */ = {
|
||||||
isa = XCBuildConfiguration;
|
isa = XCBuildConfiguration;
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
@@ -1228,7 +1244,7 @@
|
|||||||
COMBINE_HIDPI_IMAGES = YES;
|
COMBINE_HIDPI_IMAGES = YES;
|
||||||
CURRENT_PROJECT_VERSION = 1;
|
CURRENT_PROJECT_VERSION = 1;
|
||||||
DEVELOPMENT_TEAM = "";
|
DEVELOPMENT_TEAM = "";
|
||||||
"DEVELOPMENT_TEAM[sdk=macosx*]" = "$(SECRETIVE_DEVELOPMENT_TEAM)";
|
"DEVELOPMENT_TEAM[sdk=macosx*]" = Z72PRUAWF6;
|
||||||
ENABLE_APP_SANDBOX = YES;
|
ENABLE_APP_SANDBOX = YES;
|
||||||
ENABLE_ENHANCED_SECURITY = YES;
|
ENABLE_ENHANCED_SECURITY = YES;
|
||||||
ENABLE_HARDENED_RUNTIME = YES;
|
ENABLE_HARDENED_RUNTIME = YES;
|
||||||
@@ -1241,7 +1257,7 @@
|
|||||||
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
|
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
|
||||||
MACOSX_DEPLOYMENT_TARGET = 14.0;
|
MACOSX_DEPLOYMENT_TARGET = 14.0;
|
||||||
MARKETING_VERSION = 1.0;
|
MARKETING_VERSION = 1.0;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = "$(SECRETIVE_BASE_BUNDLE_ID).SecretAgentInputParser";
|
PRODUCT_BUNDLE_IDENTIFIER = com.maxgoedjen.Secretive.SecretAgentInputParser;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||||
REGISTER_APP_GROUPS = YES;
|
REGISTER_APP_GROUPS = YES;
|
||||||
@@ -1254,155 +1270,6 @@
|
|||||||
};
|
};
|
||||||
name = Release;
|
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 = "$(SECRETIVE_BASE_BUNDLE_ID).Host";
|
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
|
||||||
};
|
|
||||||
name = Test;
|
|
||||||
};
|
|
||||||
508A5917241EF1A00069DC07 /* Test */ = {
|
|
||||||
isa = XCBuildConfiguration;
|
|
||||||
buildSettings = {
|
|
||||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
|
||||||
CODE_SIGN_ENTITLEMENTS = SecretAgent/SecretAgent.entitlements;
|
|
||||||
CODE_SIGN_STYLE = Manual;
|
|
||||||
COMBINE_HIDPI_IMAGES = YES;
|
|
||||||
DEAD_CODE_STRIPPING = YES;
|
|
||||||
DEVELOPMENT_ASSET_PATHS = "\"SecretAgent/Preview Content\"";
|
|
||||||
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;
|
|
||||||
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 = "$(SECRETIVE_BASE_BUNDLE_ID).SecretAgent";
|
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
|
||||||
};
|
|
||||||
name = Test;
|
|
||||||
};
|
|
||||||
50A3B79B24026B7600D209EA /* Debug */ = {
|
50A3B79B24026B7600D209EA /* Debug */ = {
|
||||||
isa = XCBuildConfiguration;
|
isa = XCBuildConfiguration;
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
@@ -1412,7 +1279,7 @@
|
|||||||
COMBINE_HIDPI_IMAGES = YES;
|
COMBINE_HIDPI_IMAGES = YES;
|
||||||
DEAD_CODE_STRIPPING = YES;
|
DEAD_CODE_STRIPPING = YES;
|
||||||
DEVELOPMENT_ASSET_PATHS = "\"SecretAgent/Preview Content\"";
|
DEVELOPMENT_ASSET_PATHS = "\"SecretAgent/Preview Content\"";
|
||||||
DEVELOPMENT_TEAM = "$(SECRETIVE_DEVELOPMENT_TEAM)";
|
DEVELOPMENT_TEAM = Z72PRUAWF6;
|
||||||
ENABLE_APP_SANDBOX = YES;
|
ENABLE_APP_SANDBOX = YES;
|
||||||
ENABLE_ENHANCED_SECURITY = YES;
|
ENABLE_ENHANCED_SECURITY = YES;
|
||||||
ENABLE_HARDENED_RUNTIME = YES;
|
ENABLE_HARDENED_RUNTIME = YES;
|
||||||
@@ -1435,7 +1302,7 @@
|
|||||||
);
|
);
|
||||||
MACOSX_DEPLOYMENT_TARGET = 14.0;
|
MACOSX_DEPLOYMENT_TARGET = 14.0;
|
||||||
MARKETING_VERSION = 1;
|
MARKETING_VERSION = 1;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = "$(SECRETIVE_BASE_BUNDLE_ID).SecretAgent";
|
PRODUCT_BUNDLE_IDENTIFIER = com.maxgoedjen.Secretive.SecretAgent;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
};
|
};
|
||||||
name = Debug;
|
name = Debug;
|
||||||
@@ -1450,7 +1317,7 @@
|
|||||||
COMBINE_HIDPI_IMAGES = YES;
|
COMBINE_HIDPI_IMAGES = YES;
|
||||||
DEAD_CODE_STRIPPING = YES;
|
DEAD_CODE_STRIPPING = YES;
|
||||||
DEVELOPMENT_ASSET_PATHS = "\"SecretAgent/Preview Content\"";
|
DEVELOPMENT_ASSET_PATHS = "\"SecretAgent/Preview Content\"";
|
||||||
DEVELOPMENT_TEAM = "$(SECRETIVE_DEVELOPMENT_TEAM)";
|
DEVELOPMENT_TEAM = Z72PRUAWF6;
|
||||||
ENABLE_APP_SANDBOX = YES;
|
ENABLE_APP_SANDBOX = YES;
|
||||||
ENABLE_ENHANCED_SECURITY = YES;
|
ENABLE_ENHANCED_SECURITY = YES;
|
||||||
ENABLE_HARDENED_RUNTIME = YES;
|
ENABLE_HARDENED_RUNTIME = YES;
|
||||||
@@ -1473,12 +1340,82 @@
|
|||||||
);
|
);
|
||||||
MACOSX_DEPLOYMENT_TARGET = 14.0;
|
MACOSX_DEPLOYMENT_TARGET = 14.0;
|
||||||
MARKETING_VERSION = 1;
|
MARKETING_VERSION = 1;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = "$(SECRETIVE_BASE_BUNDLE_ID).SecretAgent";
|
PRODUCT_BUNDLE_IDENTIFIER = com.maxgoedjen.Secretive.SecretAgent;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
PROVISIONING_PROFILE_SPECIFIER = "Secretive - Secret Agent";
|
PROVISIONING_PROFILE_SPECIFIER = "Secretive - Secret Agent";
|
||||||
};
|
};
|
||||||
name = Release;
|
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 */
|
/* End XCBuildConfiguration section */
|
||||||
|
|
||||||
/* Begin XCConfigurationList section */
|
/* Begin XCConfigurationList section */
|
||||||
@@ -1486,7 +1423,6 @@
|
|||||||
isa = XCConfigurationList;
|
isa = XCConfigurationList;
|
||||||
buildConfigurations = (
|
buildConfigurations = (
|
||||||
50617D9B23FCE48E0099B055 /* Debug */,
|
50617D9B23FCE48E0099B055 /* Debug */,
|
||||||
508A5914241EF1A00069DC07 /* Test */,
|
|
||||||
50617D9C23FCE48E0099B055 /* Release */,
|
50617D9C23FCE48E0099B055 /* Release */,
|
||||||
);
|
);
|
||||||
defaultConfigurationIsVisible = 0;
|
defaultConfigurationIsVisible = 0;
|
||||||
@@ -1496,7 +1432,6 @@
|
|||||||
isa = XCConfigurationList;
|
isa = XCConfigurationList;
|
||||||
buildConfigurations = (
|
buildConfigurations = (
|
||||||
50617D9E23FCE48E0099B055 /* Debug */,
|
50617D9E23FCE48E0099B055 /* Debug */,
|
||||||
508A5915241EF1A00069DC07 /* Test */,
|
|
||||||
50617D9F23FCE48E0099B055 /* Release */,
|
50617D9F23FCE48E0099B055 /* Release */,
|
||||||
);
|
);
|
||||||
defaultConfigurationIsVisible = 0;
|
defaultConfigurationIsVisible = 0;
|
||||||
@@ -1506,7 +1441,6 @@
|
|||||||
isa = XCConfigurationList;
|
isa = XCConfigurationList;
|
||||||
buildConfigurations = (
|
buildConfigurations = (
|
||||||
50692D202E6FDB880043C7BB /* Debug */,
|
50692D202E6FDB880043C7BB /* Debug */,
|
||||||
50692D212E6FDB880043C7BB /* Test */,
|
|
||||||
50692D222E6FDB880043C7BB /* Release */,
|
50692D222E6FDB880043C7BB /* Release */,
|
||||||
);
|
);
|
||||||
defaultConfigurationIsVisible = 0;
|
defaultConfigurationIsVisible = 0;
|
||||||
@@ -1516,7 +1450,6 @@
|
|||||||
isa = XCConfigurationList;
|
isa = XCConfigurationList;
|
||||||
buildConfigurations = (
|
buildConfigurations = (
|
||||||
50692E5E2E6FF9D20043C7BB /* Debug */,
|
50692E5E2E6FF9D20043C7BB /* Debug */,
|
||||||
50692E5F2E6FF9D20043C7BB /* Test */,
|
|
||||||
50692E602E6FF9D20043C7BB /* Release */,
|
50692E602E6FF9D20043C7BB /* Release */,
|
||||||
);
|
);
|
||||||
defaultConfigurationIsVisible = 0;
|
defaultConfigurationIsVisible = 0;
|
||||||
@@ -1526,19 +1459,23 @@
|
|||||||
isa = XCConfigurationList;
|
isa = XCConfigurationList;
|
||||||
buildConfigurations = (
|
buildConfigurations = (
|
||||||
50A3B79B24026B7600D209EA /* Debug */,
|
50A3B79B24026B7600D209EA /* Debug */,
|
||||||
508A5917241EF1A00069DC07 /* Test */,
|
|
||||||
50A3B79C24026B7600D209EA /* Release */,
|
50A3B79C24026B7600D209EA /* Release */,
|
||||||
);
|
);
|
||||||
defaultConfigurationIsVisible = 0;
|
defaultConfigurationIsVisible = 0;
|
||||||
defaultConfigurationName = Release;
|
defaultConfigurationName = Release;
|
||||||
};
|
};
|
||||||
|
50E4C4DB2E77C4B300C73783 /* Build configuration list for PBXNativeTarget "SecretiveCertificateParser" */ = {
|
||||||
|
isa = XCConfigurationList;
|
||||||
|
buildConfigurations = (
|
||||||
|
50E4C4DC2E77C4B300C73783 /* Debug */,
|
||||||
|
50E4C4DE2E77C4B300C73783 /* Release */,
|
||||||
|
);
|
||||||
|
defaultConfigurationIsVisible = 0;
|
||||||
|
defaultConfigurationName = Release;
|
||||||
|
};
|
||||||
/* End XCConfigurationList section */
|
/* End XCConfigurationList section */
|
||||||
|
|
||||||
/* Begin XCSwiftPackageProductDependency section */
|
/* Begin XCSwiftPackageProductDependency section */
|
||||||
5002C3AA2EEF483300FFAD22 /* XPCWrappers */ = {
|
|
||||||
isa = XCSwiftPackageProductDependency;
|
|
||||||
productName = XPCWrappers;
|
|
||||||
};
|
|
||||||
5003EF3A278005E800DF2006 /* SecretKit */ = {
|
5003EF3A278005E800DF2006 /* SecretKit */ = {
|
||||||
isa = XCSwiftPackageProductDependency;
|
isa = XCSwiftPackageProductDependency;
|
||||||
productName = SecretKit;
|
productName = SecretKit;
|
||||||
@@ -1575,6 +1512,10 @@
|
|||||||
isa = XCSwiftPackageProductDependency;
|
isa = XCSwiftPackageProductDependency;
|
||||||
productName = Brief;
|
productName = Brief;
|
||||||
};
|
};
|
||||||
|
505993522E7E70C90092CFFA /* CertificateKit */ = {
|
||||||
|
isa = XCSwiftPackageProductDependency;
|
||||||
|
productName = CertificateKit;
|
||||||
|
};
|
||||||
50692D2C2E6FDC000043C7BB /* XPCWrappers */ = {
|
50692D2C2E6FDC000043C7BB /* XPCWrappers */ = {
|
||||||
isa = XCSwiftPackageProductDependency;
|
isa = XCSwiftPackageProductDependency;
|
||||||
productName = XPCWrappers;
|
productName = XPCWrappers;
|
||||||
@@ -1583,10 +1524,6 @@
|
|||||||
isa = XCSwiftPackageProductDependency;
|
isa = XCSwiftPackageProductDependency;
|
||||||
productName = Brief;
|
productName = Brief;
|
||||||
};
|
};
|
||||||
50692E6B2E6FFA510043C7BB /* SecretAgentKit */ = {
|
|
||||||
isa = XCSwiftPackageProductDependency;
|
|
||||||
productName = SecretAgentKit;
|
|
||||||
};
|
|
||||||
50E0145B2EDB9CDF00B121F1 /* Common */ = {
|
50E0145B2EDB9CDF00B121F1 /* Common */ = {
|
||||||
isa = XCSwiftPackageProductDependency;
|
isa = XCSwiftPackageProductDependency;
|
||||||
productName = Common;
|
productName = Common;
|
||||||
@@ -1595,6 +1532,22 @@
|
|||||||
isa = XCSwiftPackageProductDependency;
|
isa = XCSwiftPackageProductDependency;
|
||||||
productName = Common;
|
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 */
|
/* End XCSwiftPackageProductDependency section */
|
||||||
};
|
};
|
||||||
rootObject = 50617D7723FCE48D0099B055 /* Project object */;
|
rootObject = 50617D7723FCE48D0099B055 /* Project object */;
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<Scheme
|
<Scheme
|
||||||
LastUpgradeVersion = "2640"
|
LastUpgradeVersion = "2600"
|
||||||
version = "1.7">
|
version = "1.7">
|
||||||
<BuildAction
|
<BuildAction
|
||||||
parallelizeBuildables = "YES"
|
parallelizeBuildables = "YES"
|
||||||
@@ -14,8 +14,7 @@
|
|||||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||||
<TestPlans>
|
<TestPlans>
|
||||||
<TestPlanReference
|
<TestPlanReference
|
||||||
reference = "container:Config/Secretive.xctestplan"
|
reference = "container:Config/Secretive.xctestplan">
|
||||||
default = "YES">
|
|
||||||
</TestPlanReference>
|
</TestPlanReference>
|
||||||
</TestPlans>
|
</TestPlans>
|
||||||
</TestAction>
|
</TestAction>
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<Scheme
|
<Scheme
|
||||||
LastUpgradeVersion = "2640"
|
LastUpgradeVersion = "2600"
|
||||||
version = "1.7">
|
version = "1.7">
|
||||||
<BuildAction
|
<BuildAction
|
||||||
parallelizeBuildables = "YES"
|
parallelizeBuildables = "YES"
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<Scheme
|
<Scheme
|
||||||
LastUpgradeVersion = "2640"
|
LastUpgradeVersion = "2600"
|
||||||
version = "1.7">
|
version = "1.7">
|
||||||
<BuildAction
|
<BuildAction
|
||||||
parallelizeBuildables = "YES"
|
parallelizeBuildables = "YES"
|
||||||
@@ -23,7 +23,7 @@
|
|||||||
</BuildActionEntries>
|
</BuildActionEntries>
|
||||||
</BuildAction>
|
</BuildAction>
|
||||||
<TestAction
|
<TestAction
|
||||||
buildConfiguration = "Test"
|
buildConfiguration = "Debug"
|
||||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import SecretKit
|
|||||||
import SecureEnclaveSecretKit
|
import SecureEnclaveSecretKit
|
||||||
import SmartCardSecretKit
|
import SmartCardSecretKit
|
||||||
import Brief
|
import Brief
|
||||||
|
import CertificateKit
|
||||||
|
|
||||||
@main
|
@main
|
||||||
struct Secretive: App {
|
struct Secretive: App {
|
||||||
@@ -14,6 +15,7 @@ struct Secretive: App {
|
|||||||
WindowGroup {
|
WindowGroup {
|
||||||
ContentView()
|
ContentView()
|
||||||
.environment(EnvironmentValues._secretStoreList)
|
.environment(EnvironmentValues._secretStoreList)
|
||||||
|
.environment(EnvironmentValues._certificateStore)
|
||||||
.onReceive(NotificationCenter.default.publisher(for: NSApplication.didBecomeActiveNotification)) { _ in
|
.onReceive(NotificationCenter.default.publisher(for: NSApplication.didBecomeActiveNotification)) { _ in
|
||||||
Task {
|
Task {
|
||||||
@AppStorage("defaultsHasRunSetup") var hasRunSetup = false
|
@AppStorage("defaultsHasRunSetup") var hasRunSetup = false
|
||||||
@@ -92,15 +94,18 @@ extension EnvironmentValues {
|
|||||||
@MainActor fileprivate static let _secretStoreList: SecretStoreList = {
|
@MainActor fileprivate static let _secretStoreList: SecretStoreList = {
|
||||||
let list = SecretStoreList()
|
let list = SecretStoreList()
|
||||||
let cryptoKit = SecureEnclave.Store()
|
let cryptoKit = SecureEnclave.Store()
|
||||||
let migrator = SecureEnclave.CryptoKitMigrator()
|
let cryptoKitMigrator = SecureEnclave.CryptoKitMigrator()
|
||||||
try? migrator.migrate(to: cryptoKit)
|
try? cryptoKitMigrator.migrate(to: cryptoKit)
|
||||||
list.add(store: cryptoKit)
|
list.add(store: cryptoKit)
|
||||||
list.add(store: SmartCard.Store())
|
list.add(store: SmartCard.Store())
|
||||||
return list
|
return list
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
@MainActor fileprivate static let _certificateStore: CertificateStore = CertificateStore()
|
||||||
|
|
||||||
private static let _agentLaunchController = AgentLaunchController()
|
private static let _agentLaunchController = AgentLaunchController()
|
||||||
@Entry var agentLaunchController: any AgentLaunchControllerProtocol = _agentLaunchController
|
@Entry var agentLaunchController: any AgentLaunchControllerProtocol = _agentLaunchController
|
||||||
|
|
||||||
private static let _updater: any UpdaterProtocol = {
|
private static let _updater: any UpdaterProtocol = {
|
||||||
@AppStorage("defaultsHasRunSetup") var hasRunSetup = false
|
@AppStorage("defaultsHasRunSetup") var hasRunSetup = false
|
||||||
return Updater(checkOnLaunch: hasRunSetup)
|
return Updater(checkOnLaunch: hasRunSetup)
|
||||||
@@ -113,6 +118,10 @@ extension EnvironmentValues {
|
|||||||
@MainActor var secretStoreList: SecretStoreList {
|
@MainActor var secretStoreList: SecretStoreList {
|
||||||
EnvironmentValues._secretStoreList
|
EnvironmentValues._secretStoreList
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@MainActor var certificateStore: CertificateStore {
|
||||||
|
EnvironmentValues._certificateStore
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension FocusedValues {
|
extension FocusedValues {
|
||||||
|
|||||||
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()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -4,22 +4,16 @@
|
|||||||
<dict>
|
<dict>
|
||||||
<key>com.apple.security.hardened-process</key>
|
<key>com.apple.security.hardened-process</key>
|
||||||
<true/>
|
<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>
|
<key>com.apple.security.hardened-process.dyld-ro</key>
|
||||||
<true/>
|
<true/>
|
||||||
|
<key>com.apple.security.hardened-process.enhanced-security-version</key>
|
||||||
|
<integer>1</integer>
|
||||||
<key>com.apple.security.hardened-process.hardened-heap</key>
|
<key>com.apple.security.hardened-process.hardened-heap</key>
|
||||||
<true/>
|
<true/>
|
||||||
<key>com.apple.security.hardened-process.enhanced-security-version-string</key>
|
<key>com.apple.security.hardened-process.platform-restrictions</key>
|
||||||
<string>1</string>
|
<integer>2</integer>
|
||||||
<key>com.apple.security.smartcard</key>
|
<key>com.apple.security.smartcard</key>
|
||||||
<true/>
|
<true/>
|
||||||
<key>com.apple.security.hardened-process.platform-restrictions-string</key>
|
|
||||||
<string>2</string>
|
|
||||||
<key>keychain-access-groups</key>
|
<key>keychain-access-groups</key>
|
||||||
<array>
|
<array>
|
||||||
<string>$(AppIdentifierPrefix)com.maxgoedjen.Secretive</string>
|
<string>$(AppIdentifierPrefix)com.maxgoedjen.Secretive</string>
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import SwiftUI
|
import SwiftUI
|
||||||
import SecretKit
|
import SecretKit
|
||||||
import SSHProtocolKit
|
import SSHProtocolKit
|
||||||
import Common
|
|
||||||
|
|
||||||
struct ToolConfigurationView: View {
|
struct ToolConfigurationView: View {
|
||||||
|
|
||||||
@@ -113,9 +112,10 @@ struct ToolConfigurationView: View {
|
|||||||
let writer = OpenSSHPublicKeyWriter()
|
let writer = OpenSSHPublicKeyWriter()
|
||||||
let gitAllowedSignersString = [email.isEmpty ? String(localized: .integrationsConfigureUsingEmailPlaceholder) : email, writer.openSSHString(secret: selectedSecret)]
|
let gitAllowedSignersString = [email.isEmpty ? String(localized: .integrationsConfigureUsingEmailPlaceholder) : email, writer.openSSHString(secret: selectedSecret)]
|
||||||
.joined(separator: " ")
|
.joined(separator: " ")
|
||||||
|
let fileController = PublicKeyFileStoreController(homeDirectory: URL.agentHomeURL)
|
||||||
return text
|
return text
|
||||||
.replacingOccurrences(of: Instructions.Constants.publicKeyPlaceholder, with: gitAllowedSignersString)
|
.replacingOccurrences(of: Instructions.Constants.publicKeyPlaceholder, with: gitAllowedSignersString)
|
||||||
.replacingOccurrences(of: Instructions.Constants.publicKeyPathPlaceholder, with: URL.publicKeyPath(for: selectedSecret, in: URL.publicKeyDirectory))
|
.replacingOccurrences(of: Instructions.Constants.publicKeyPathPlaceholder, with: fileController.publicKeyPath(for: selectedSecret))
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import SwiftUI
|
import SwiftUI
|
||||||
import SecretKit
|
import SecretKit
|
||||||
import Common
|
|
||||||
import SSHProtocolKit
|
import SSHProtocolKit
|
||||||
|
|
||||||
struct SecretDetailView<SecretType: Secret>: View {
|
struct SecretDetailView<SecretType: Secret>: View {
|
||||||
@@ -8,6 +7,7 @@ struct SecretDetailView<SecretType: Secret>: View {
|
|||||||
let secret: SecretType
|
let secret: SecretType
|
||||||
|
|
||||||
private let keyWriter = OpenSSHPublicKeyWriter()
|
private let keyWriter = OpenSSHPublicKeyWriter()
|
||||||
|
private let publicKeyFileStoreController = PublicKeyFileStoreController(homeDirectory: URL.agentHomeURL)
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
ScrollView {
|
ScrollView {
|
||||||
@@ -22,7 +22,7 @@ struct SecretDetailView<SecretType: Secret>: View {
|
|||||||
CopyableView(title: .secretDetailPublicKeyLabel, image: Image(systemName: "key"), text: keyString)
|
CopyableView(title: .secretDetailPublicKeyLabel, image: Image(systemName: "key"), text: keyString)
|
||||||
Spacer()
|
Spacer()
|
||||||
.frame(height: 20)
|
.frame(height: 20)
|
||||||
CopyableView(title: .secretDetailPublicKeyPathLabel, image: Image(systemName: "lock.doc"), text: URL.publicKeyPath(for: secret, in: URL.publicKeyDirectory), showRevealInFinder: true)
|
CopyableView(title: .secretDetailPublicKeyPathLabel, image: Image(systemName: "lock.doc"), text: publicKeyFileStoreController.publicKeyPath(for: secret), showRevealInFinder: true)
|
||||||
Spacer()
|
Spacer()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import SecretKit
|
|||||||
import SecureEnclaveSecretKit
|
import SecureEnclaveSecretKit
|
||||||
import SmartCardSecretKit
|
import SmartCardSecretKit
|
||||||
import Brief
|
import Brief
|
||||||
|
import SSHProtocolKit
|
||||||
|
|
||||||
struct ContentView: View {
|
struct ContentView: View {
|
||||||
|
|
||||||
@@ -13,6 +14,7 @@ struct ContentView: View {
|
|||||||
@Environment(\.colorScheme) private var colorScheme
|
@Environment(\.colorScheme) private var colorScheme
|
||||||
@Environment(\.openWindow) private var openWindow
|
@Environment(\.openWindow) private var openWindow
|
||||||
@Environment(\.secretStoreList) private var storeList
|
@Environment(\.secretStoreList) private var storeList
|
||||||
|
@Environment(\.certificateStore) private var certificateStore
|
||||||
@Environment(\.updater) private var updater
|
@Environment(\.updater) private var updater
|
||||||
@Environment(\.agentLaunchController) private var agentLaunchController
|
@Environment(\.agentLaunchController) private var agentLaunchController
|
||||||
|
|
||||||
@@ -42,6 +44,22 @@ struct ContentView: View {
|
|||||||
runningSetup = true
|
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) {
|
.focusedSceneValue(\.showCreateSecret, .init(isEnabled: !runningSetup) {
|
||||||
showingCreation = true
|
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()
|
||||||
@@ -12,11 +12,11 @@
|
|||||||
<true/>
|
<true/>
|
||||||
<key>com.apple.security.hardened-process.dyld-ro</key>
|
<key>com.apple.security.hardened-process.dyld-ro</key>
|
||||||
<true/>
|
<true/>
|
||||||
<key>com.apple.security.hardened-process.enhanced-security-version-string</key>
|
<key>com.apple.security.hardened-process.enhanced-security-version</key>
|
||||||
<string>1</string>
|
<integer>1</integer>
|
||||||
<key>com.apple.security.hardened-process.hardened-heap</key>
|
<key>com.apple.security.hardened-process.hardened-heap</key>
|
||||||
<true/>
|
<true/>
|
||||||
<key>com.apple.security.hardened-process.platform-restrictions-string</key>
|
<key>com.apple.security.hardened-process.platform-restrictions</key>
|
||||||
<string>2</string>
|
<integer>2</integer>
|
||||||
</dict>
|
</dict>
|
||||||
</plist>
|
</plist>
|
||||||
|
|||||||
@@ -1,56 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
TEAM_ID_FILE=Sources/Config/OpenSource.xcconfig
|
|
||||||
|
|
||||||
function print_team_ids() {
|
|
||||||
echo ""
|
|
||||||
echo "FYI, here are the team IDs found in your Xcode preferences:"
|
|
||||||
echo ""
|
|
||||||
|
|
||||||
XCODEPREFS="$HOME/Library/Preferences/com.apple.dt.Xcode.plist"
|
|
||||||
TEAM_KEYS=(`/usr/libexec/PlistBuddy -c "Print :IDEProvisioningTeams" "$XCODEPREFS" | perl -lne 'print $1 if /^ (\S*) =/'`)
|
|
||||||
|
|
||||||
for KEY in $TEAM_KEYS
|
|
||||||
do
|
|
||||||
i=0
|
|
||||||
while true ; do
|
|
||||||
NAME=$(/usr/libexec/PlistBuddy -c "Print :IDEProvisioningTeams:$KEY:$i:teamName" "$XCODEPREFS" 2>/dev/null)
|
|
||||||
TEAMID=$(/usr/libexec/PlistBuddy -c "Print :IDEProvisioningTeams:$KEY:$i:teamID" "$XCODEPREFS" 2>/dev/null)
|
|
||||||
|
|
||||||
if [ $? -ne 0 ]; then
|
|
||||||
break
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "$TEAMID - $NAME"
|
|
||||||
|
|
||||||
i=$(($i + 1))
|
|
||||||
done
|
|
||||||
done
|
|
||||||
}
|
|
||||||
|
|
||||||
if [ -z "$1" ]; then
|
|
||||||
print_team_ids
|
|
||||||
echo ""
|
|
||||||
echo "> What is your Apple Developer Team ID? (looks like 1A23BDCD)"
|
|
||||||
read TEAM_ID
|
|
||||||
else
|
|
||||||
TEAM_ID=$1
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ -z "$TEAM_ID" ]; then
|
|
||||||
echo "You must enter a team id"
|
|
||||||
print_team_ids
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "Setting team ID to $TEAM_ID"
|
|
||||||
|
|
||||||
echo "// This file was automatically generated, do not edit directly." > $TEAM_ID_FILE
|
|
||||||
echo "" >> $TEAM_ID_FILE
|
|
||||||
echo "SECRETIVE_BASE_BUNDLE_ID_OSS=${TEAM_ID}.com.example.Secretive" >> $TEAM_ID_FILE
|
|
||||||
echo "SECRETIVE_DEVELOPMENT_TEAM_OSS=${TEAM_ID}" >> $TEAM_ID_FILE
|
|
||||||
|
|
||||||
echo ""
|
|
||||||
echo "Successfully generated configuration at $TEAM_ID_FILE, you may now build the app using the \"Secretive\" target"
|
|
||||||
echo "You may need to close and re-open the project in Xcode if it's already open"
|
|
||||||
echo ""
|
|
||||||
Reference in New Issue
Block a user