mirror of
https://github.com/maxgoedjen/secretive.git
synced 2026-04-09 18:57:22 +02:00
Compare commits
7 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4033a5b947 | ||
|
|
2cc0157290 | ||
|
|
faa622e379 | ||
|
|
9f2c6d9e84 | ||
|
|
afb48529c7 | ||
|
|
6c56039ece | ||
|
|
2b712864d6 |
2
.github/workflows/codeql.yml
vendored
2
.github/workflows/codeql.yml
vendored
@@ -37,7 +37,7 @@ jobs:
|
||||
build-mode: ${{ matrix.build-mode }}
|
||||
- if: matrix.build-mode == 'manual'
|
||||
name: "Select Xcode"
|
||||
run: sudo xcrun xcode-select -s /Applications/Xcode_26.2.app
|
||||
run: sudo xcrun xcode-select -s /Applications/Xcode_26.4.app
|
||||
- if: matrix.build-mode == 'manual'
|
||||
name: "Build"
|
||||
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 }}
|
||||
run: ./.github/scripts/signing.sh
|
||||
- name: Set Environment
|
||||
run: sudo xcrun xcode-select -s /Applications/Xcode_26.2.app
|
||||
run: sudo xcrun xcode-select -s /Applications/Xcode_26.4.app
|
||||
- name: Update Build Number
|
||||
env:
|
||||
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 }}
|
||||
run: ./.github/scripts/signing.sh
|
||||
- name: Set Environment
|
||||
run: sudo xcrun xcode-select -s /Applications/Xcode_26.2.app
|
||||
run: sudo xcrun xcode-select -s /Applications/Xcode_26.4.app
|
||||
- name: Update Build Number
|
||||
env:
|
||||
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 }}
|
||||
run: ./.github/scripts/signing.sh
|
||||
- name: Set Environment
|
||||
run: sudo xcrun xcode-select -s /Applications/Xcode_26.2.app
|
||||
run: sudo xcrun xcode-select -s /Applications/Xcode_26.4.app
|
||||
- name: Test
|
||||
run: xcrun xcodebuild -project Sources/Secretive.xcodeproj -scheme PackageTests test
|
||||
# SPM doesn't seem to pick up on the tests currently?
|
||||
@@ -47,7 +47,7 @@ jobs:
|
||||
APPLE_API_KEY_ID: ${{ secrets.APPLE_API_KEY_ID }}
|
||||
run: ./.github/scripts/signing.sh
|
||||
- name: Set Environment
|
||||
run: sudo xcrun xcode-select -s /Applications/Xcode_26.2.app
|
||||
run: sudo xcrun xcode-select -s /Applications/Xcode_26.4.app
|
||||
- name: Update Build Number
|
||||
env:
|
||||
TAG_NAME: ${{ github.ref }}
|
||||
|
||||
2
.github/workflows/test.yml
vendored
2
.github/workflows/test.yml
vendored
@@ -10,7 +10,7 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
- name: Set Environment
|
||||
run: sudo xcrun xcode-select -s /Applications/Xcode_26.2.app
|
||||
run: sudo xcrun xcode-select -s /Applications/Xcode_26.4.app
|
||||
- name: Test Main Packages
|
||||
run: xcrun xcodebuild -project Sources/Secretive.xcodeproj -scheme PackageTests test
|
||||
# SPM doesn't seem to pick up on the tests currently?
|
||||
|
||||
4
.gitignore
vendored
4
.gitignore
vendored
@@ -93,3 +93,7 @@ iOSInjectionProject/
|
||||
Archive.xcarchive
|
||||
.DS_Store
|
||||
contents.xcworkspacedata
|
||||
|
||||
# Per-User Configs
|
||||
|
||||
Sources/Config/OpenSource.xcconfig
|
||||
@@ -22,6 +22,9 @@ let package = Package(
|
||||
.library(
|
||||
name: "SmartCardSecretKit",
|
||||
targets: ["SmartCardSecretKit"]),
|
||||
.library(
|
||||
name: "SSHProtocolKit",
|
||||
targets: ["SSHProtocolKit"]),
|
||||
],
|
||||
dependencies: [
|
||||
],
|
||||
@@ -53,6 +56,19 @@ let package = Package(
|
||||
resources: [localization],
|
||||
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,3 +1,8 @@
|
||||
CI_VERSION = GITHUB_CI_VERSION
|
||||
CI_BUILD_NUMBER = GITHUB_BUILD_NUMBER
|
||||
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)
|
||||
|
||||
@@ -21,7 +21,7 @@ let package = Package(
|
||||
targets: ["SmartCardSecretKit"]),
|
||||
.library(
|
||||
name: "SecretAgentKit",
|
||||
targets: ["SecretAgentKit", "XPCWrappers"]),
|
||||
targets: ["SecretAgentKit"]),
|
||||
.library(
|
||||
name: "Common",
|
||||
targets: ["Common"]),
|
||||
@@ -31,6 +31,9 @@ let package = Package(
|
||||
.library(
|
||||
name: "XPCWrappers",
|
||||
targets: ["XPCWrappers"]),
|
||||
.library(
|
||||
name: "SSHProtocolKit",
|
||||
targets: ["SSHProtocolKit"]),
|
||||
],
|
||||
dependencies: [
|
||||
],
|
||||
@@ -43,7 +46,7 @@ let package = Package(
|
||||
),
|
||||
.testTarget(
|
||||
name: "SecretKitTests",
|
||||
dependencies: ["SecretKit", "SecureEnclaveSecretKit", "SmartCardSecretKit"],
|
||||
dependencies: ["SecretKit", "SecretAgentKit", "SecureEnclaveSecretKit", "SmartCardSecretKit"],
|
||||
swiftSettings: swiftSettings,
|
||||
),
|
||||
.target(
|
||||
@@ -60,7 +63,7 @@ let package = Package(
|
||||
),
|
||||
.target(
|
||||
name: "SecretAgentKit",
|
||||
dependencies: ["SecretKit"],
|
||||
dependencies: ["SecretKit", "SSHProtocolKit", "Common"],
|
||||
resources: [localization],
|
||||
swiftSettings: swiftSettings,
|
||||
),
|
||||
@@ -68,15 +71,26 @@ let package = Package(
|
||||
name: "SecretAgentKitTests",
|
||||
dependencies: ["SecretAgentKit"],
|
||||
),
|
||||
.target(
|
||||
name: "SSHProtocolKit",
|
||||
dependencies: ["SecretKit"],
|
||||
resources: [localization],
|
||||
swiftSettings: swiftSettings,
|
||||
),
|
||||
.testTarget(
|
||||
name: "SSHProtocolKitTests",
|
||||
dependencies: ["SSHProtocolKit"],
|
||||
swiftSettings: swiftSettings,
|
||||
),
|
||||
.target(
|
||||
name: "Common",
|
||||
dependencies: [],
|
||||
dependencies: ["SSHProtocolKit", "SecretKit"],
|
||||
resources: [localization],
|
||||
swiftSettings: swiftSettings,
|
||||
),
|
||||
.target(
|
||||
name: "Brief",
|
||||
dependencies: ["XPCWrappers"],
|
||||
dependencies: ["XPCWrappers", "SSHProtocolKit"],
|
||||
resources: [localization],
|
||||
swiftSettings: swiftSettings,
|
||||
),
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
import Foundation
|
||||
import SSHProtocolKit
|
||||
import SecretKit
|
||||
|
||||
extension URL {
|
||||
|
||||
@@ -14,6 +16,20 @@ extension URL {
|
||||
#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 {
|
||||
@@ -27,3 +43,4 @@ extension String {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
37
Sources/Packages/Sources/SSHProtocolKit/Data+Hex.swift
Normal file
37
Sources/Packages/Sources/SSHProtocolKit/Data+Hex.swift
Normal file
@@ -0,0 +1,37 @@
|
||||
import Foundation
|
||||
import CryptoKit
|
||||
|
||||
public struct HexDataStyle<SequenceType: Sequence>: Hashable, Codable {
|
||||
|
||||
let separator: String
|
||||
|
||||
public init(separator: String) {
|
||||
self.separator = separator
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extension HexDataStyle: FormatStyle where SequenceType.Element == UInt8 {
|
||||
|
||||
public func format(_ value: SequenceType) -> String {
|
||||
value
|
||||
.compactMap { ("0" + String($0, radix: 16, uppercase: false)).suffix(2) }
|
||||
.joined(separator: separator)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extension FormatStyle where Self == HexDataStyle<Data> {
|
||||
|
||||
public static func hex(separator: String = "") -> HexDataStyle<Data> {
|
||||
HexDataStyle(separator: separator)
|
||||
}
|
||||
|
||||
}
|
||||
extension FormatStyle where Self == HexDataStyle<Insecure.MD5Digest> {
|
||||
|
||||
public static func hex(separator: String = ":") -> HexDataStyle<Insecure.MD5Digest> {
|
||||
HexDataStyle(separator: separator)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
import Foundation
|
||||
import CryptoKit
|
||||
import SecretKit
|
||||
|
||||
/// Generates OpenSSH representations of the public key sof secrets.
|
||||
public struct OpenSSHPublicKeyWriter: Sendable {
|
||||
@@ -49,9 +50,7 @@ public struct OpenSSHPublicKeyWriter: Sendable {
|
||||
/// Generates an OpenSSH MD5 fingerprint string.
|
||||
/// - Returns: OpenSSH MD5 fingerprint string.
|
||||
public func openSSHMD5Fingerprint<SecretType: Secret>(secret: SecretType) -> String {
|
||||
Insecure.MD5.hash(data: data(secret: secret))
|
||||
.compactMap { ("0" + String($0, radix: 16, uppercase: false)).suffix(2) }
|
||||
.joined(separator: ":")
|
||||
Insecure.MD5.hash(data: data(secret: secret)).formatted(.hex(separator: ":"))
|
||||
}
|
||||
|
||||
public func comment<SecretType: Secret>(secret: SecretType) -> String {
|
||||
@@ -1,42 +1,49 @@
|
||||
import Foundation
|
||||
|
||||
/// Reads OpenSSH protocol data.
|
||||
final class OpenSSHReader {
|
||||
public final class OpenSSHReader {
|
||||
|
||||
var remaining: Data
|
||||
var done = false
|
||||
|
||||
/// Initialize the reader with an OpenSSH data payload.
|
||||
/// - Parameter data: The data to read.
|
||||
init(data: Data) {
|
||||
public init(data: Data) {
|
||||
remaining = Data(data)
|
||||
}
|
||||
|
||||
/// Reads the next chunk of data from the playload.
|
||||
/// - Returns: The next chunk of data.
|
||||
func readNextChunk(convertEndianness: Bool = true) throws(OpenSSHReaderError) -> Data {
|
||||
let littleEndianLength = try readNextBytes(as: UInt32.self)
|
||||
let length = convertEndianness ? Int(littleEndianLength.bigEndian) : Int(littleEndianLength)
|
||||
public func readNextChunk(convertEndianness: Bool = true) throws(OpenSSHReaderError) -> Data {
|
||||
let length = try readNextBytes(as: UInt32.self, convertEndianness: convertEndianness)
|
||||
guard remaining.count >= length else { throw .beyondBounds }
|
||||
let dataRange = 0..<length
|
||||
let dataRange = 0..<Int(length)
|
||||
let ret = Data(remaining[dataRange])
|
||||
remaining.removeSubrange(dataRange)
|
||||
if remaining.isEmpty {
|
||||
done = true
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
func readNextBytes<T>(as: T.Type) throws(OpenSSHReaderError) -> T {
|
||||
public func readNextBytes<T: FixedWidthInteger>(as: T.Type, convertEndianness: Bool = true) throws(OpenSSHReaderError) -> T {
|
||||
let size = MemoryLayout<T>.size
|
||||
guard remaining.count >= size else { throw .beyondBounds }
|
||||
let lengthRange = 0..<size
|
||||
let lengthChunk = remaining[lengthRange]
|
||||
remaining.removeSubrange(lengthRange)
|
||||
return unsafe lengthChunk.bytes.unsafeLoad(as: T.self)
|
||||
if remaining.isEmpty {
|
||||
done = true
|
||||
}
|
||||
let value = unsafe lengthChunk.bytes.unsafeLoad(as: T.self)
|
||||
return convertEndianness ? T(value.bigEndian) : T(value)
|
||||
}
|
||||
|
||||
func readNextChunkAsString(convertEndianness: Bool = true) throws(OpenSSHReaderError) -> String {
|
||||
public func readNextChunkAsString(convertEndianness: Bool = true) throws(OpenSSHReaderError) -> String {
|
||||
try String(decoding: readNextChunk(convertEndianness: convertEndianness), as: UTF8.self)
|
||||
}
|
||||
|
||||
func readNextChunkAsSubReader(convertEndianness: Bool = true) throws(OpenSSHReaderError) -> OpenSSHReader {
|
||||
public func readNextChunkAsSubReader(convertEndianness: Bool = true) throws(OpenSSHReaderError) -> OpenSSHReader {
|
||||
OpenSSHReader(data: try readNextChunk(convertEndianness: convertEndianness))
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import Foundation
|
||||
import CryptoKit
|
||||
import SecretKit
|
||||
|
||||
/// Generates OpenSSH representations of Secrets.
|
||||
public struct OpenSSHSignatureWriter: Sendable {
|
||||
@@ -29,19 +30,28 @@ public struct OpenSSHSignatureWriter: Sendable {
|
||||
|
||||
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 {
|
||||
let rawLength = rawRepresentation.count/2
|
||||
// Check if we need to pad with 0x00 to prevent certain
|
||||
// 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)
|
||||
}
|
||||
let r = mpint(fromFixedWidthPositiveBytes: Data(rawRepresentation[0..<rawLength]))
|
||||
let s = mpint(fromFixedWidthPositiveBytes: Data(rawRepresentation[rawLength...]))
|
||||
|
||||
var signatureChunk = Data()
|
||||
signatureChunk.append(r.lengthAndData)
|
||||
@@ -3,6 +3,7 @@ import CryptoKit
|
||||
import OSLog
|
||||
import SecretKit
|
||||
import AppKit
|
||||
import SSHProtocolKit
|
||||
|
||||
/// The `Agent` is an implementation of an SSH agent. It manages coordination and access between a socket, traces requests, notifies witnesses and passes requests to stores.
|
||||
public final class Agent: Sendable {
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
import Foundation
|
||||
import OSLog
|
||||
import SecretKit
|
||||
import SSHProtocolKit
|
||||
|
||||
/// Manages storage and lookup for OpenSSH certificates.
|
||||
public actor OpenSSHCertificateHandler: Sendable {
|
||||
|
||||
private let publicKeyFileStoreController = PublicKeyFileStoreController(homeDirectory: URL.homeDirectory)
|
||||
private let publicKeyFileStoreController = PublicKeyFileStoreController(directory: URL.publicKeyDirectory)
|
||||
private let logger = Logger(subsystem: "com.maxgoedjen.secretive.secretagent", category: "OpenSSHCertificateHandler")
|
||||
private let writer = OpenSSHPublicKeyWriter()
|
||||
private var keyBlobsAndNames: [AnySecret: (Data, Data)] = [:]
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
import Foundation
|
||||
import OSLog
|
||||
import SecretKit
|
||||
import SSHProtocolKit
|
||||
import Common
|
||||
|
||||
/// Controller responsible for writing public keys to disk, so that they're easily accessible by scripts.
|
||||
public final class PublicKeyFileStoreController: Sendable {
|
||||
@@ -9,8 +12,8 @@ public final class PublicKeyFileStoreController: Sendable {
|
||||
private let keyWriter = OpenSSHPublicKeyWriter()
|
||||
|
||||
/// Initializes a PublicKeyFileStoreController.
|
||||
public init(homeDirectory: URL) {
|
||||
directory = homeDirectory.appending(component: "PublicKeys")
|
||||
public init(directory: URL) {
|
||||
self.directory = directory
|
||||
}
|
||||
|
||||
/// Writes out the keys specified to disk.
|
||||
@@ -19,7 +22,7 @@ public final class PublicKeyFileStoreController: Sendable {
|
||||
public func generatePublicKeys(for secrets: [AnySecret], clear: Bool = false) throws {
|
||||
logger.log("Writing public keys to disk")
|
||||
if clear {
|
||||
let validPaths = Set(secrets.map { publicKeyPath(for: $0) })
|
||||
let validPaths = Set(secrets.map { URL.publicKeyPath(for: $0, in: directory) })
|
||||
.union(Set(secrets.map { sshCertificatePath(for: $0) }))
|
||||
let contentsOfDirectory = (try? FileManager.default.contentsOfDirectory(atPath: directory.path())) ?? []
|
||||
let fullPathContents = contentsOfDirectory.map { directory.appending(path: $0).path() }
|
||||
@@ -33,21 +36,13 @@ public final class PublicKeyFileStoreController: Sendable {
|
||||
}
|
||||
try? FileManager.default.createDirectory(at: directory, withIntermediateDirectories: false, attributes: nil)
|
||||
for secret in secrets {
|
||||
let path = publicKeyPath(for: secret)
|
||||
let path = URL.publicKeyPath(for: secret, in: directory)
|
||||
let data = Data(keyWriter.openSSHString(secret: secret).utf8)
|
||||
FileManager.default.createFile(atPath: path, contents: data, attributes: nil)
|
||||
}
|
||||
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.
|
||||
public var hasAnyCertificates: Bool {
|
||||
@@ -1,6 +1,7 @@
|
||||
import Foundation
|
||||
import OSLog
|
||||
import SecretKit
|
||||
import SSHProtocolKit
|
||||
|
||||
public protocol SSHAgentInputParserProtocol {
|
||||
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
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
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,70 +0,0 @@
|
||||
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 name = String(localized: .secureEnclave)
|
||||
private let persistentAuthenticationHandler = PersistentAuthenticationHandler()
|
||||
private let persistentAuthenticationHandler = PersistentAuthenticationHandler<Secret>()
|
||||
|
||||
/// Initializes a Store.
|
||||
@MainActor public init() {
|
||||
|
||||
@@ -34,6 +34,7 @@ extension SmartCard {
|
||||
public var secrets: [Secret] {
|
||||
state.secrets
|
||||
}
|
||||
private let persistentAuthenticationHandler = PersistentAuthenticationHandler<Secret>()
|
||||
|
||||
/// Initializes a Store.
|
||||
public init() {
|
||||
@@ -58,9 +59,15 @@ extension SmartCard {
|
||||
|
||||
public func sign(data: Data, with secret: Secret, for provenance: SigningRequestProvenance) async throws -> Data {
|
||||
guard let tokenID = await state.tokenID else { fatalError() }
|
||||
let context = LAContext()
|
||||
context.localizedReason = String(localized: .authContextRequestSignatureDescription(appName: provenance.origin.displayName, secretName: secret.name))
|
||||
context.localizedCancelTitle = String(localized: .authContextRequestDenyButton)
|
||||
var context: LAContext
|
||||
if let existing = await persistentAuthenticationHandler.existingPersistedAuthenticationContext(secret: secret) {
|
||||
context = unsafe existing.context
|
||||
} 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([
|
||||
kSecClass: kSecClassKey,
|
||||
kSecAttrKeyClass: kSecAttrKeyClassPrivate,
|
||||
@@ -86,11 +93,12 @@ extension SmartCard {
|
||||
return signature as Data
|
||||
}
|
||||
|
||||
public func existingPersistedAuthenticationContext(secret: Secret) -> PersistedAuthenticationContext? {
|
||||
nil
|
||||
public func existingPersistedAuthenticationContext(secret: Secret) async -> PersistedAuthenticationContext? {
|
||||
await persistentAuthenticationHandler.existingPersistedAuthenticationContext(secret: secret)
|
||||
}
|
||||
|
||||
public func persistAuthentication(secret: Secret, forDuration: TimeInterval) throws {
|
||||
public func persistAuthentication(secret: Secret, forDuration duration: TimeInterval) async throws {
|
||||
try await persistentAuthenticationHandler.persistAuthentication(secret: secret, forDuration: duration)
|
||||
}
|
||||
|
||||
/// Reloads all secrets from the store.
|
||||
@@ -163,7 +171,7 @@ extension SmartCard.Store {
|
||||
let publicKeySecRef = SecKeyCopyPublicKey(publicKeyRef)!
|
||||
let publicKeyAttributes = SecKeyCopyAttributes(publicKeySecRef) as! [CFString: Any]
|
||||
let publicKey = publicKeyAttributes[kSecValueData] as! Data
|
||||
let attributes = Attributes(keyType: KeyType(secAttr: algorithmSecAttr, size: keySize)!, authentication: .unknown)
|
||||
let attributes = Attributes(keyType: KeyType(secAttr: algorithmSecAttr, size: keySize)!, authentication: .presenceRequired)
|
||||
let secret = SmartCard.Secret(id: tokenID, name: name, publicKey: publicKey, attributes: attributes)
|
||||
guard signatureAlgorithm(for: secret) != nil else { return nil }
|
||||
return secret
|
||||
|
||||
26
Sources/Packages/Sources/XPCWrappers/TeamID.swift
Normal file
26
Sources/Packages/Sources/XPCWrappers/TeamID.swift
Normal file
@@ -0,0 +1,26 @@
|
||||
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)
|
||||
let exportedObject = exportedObject
|
||||
newConnection.exportedObject = exportedObject
|
||||
newConnection.setCodeSigningRequirement("anchor apple generic and certificate leaf[subject.OU] = Z72PRUAWF6")
|
||||
newConnection.setCodeSigningRequirement("anchor apple generic and certificate leaf[subject.OU] = \"\(ProcessInfo.processInfo.teamID)\"")
|
||||
newConnection.resume()
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ public struct XPCTypedSession<ResponseType: Codable & Sendable, ErrorType: Error
|
||||
public init(serviceName: String, warmup: Bool = false) async throws {
|
||||
let connection = NSXPCConnection(serviceName: serviceName)
|
||||
connection.remoteObjectInterface = NSXPCInterface(with: (any _XPCProtocol).self)
|
||||
connection.setCodeSigningRequirement("anchor apple generic and certificate leaf[subject.OU] = Z72PRUAWF6")
|
||||
connection.setCodeSigningRequirement("anchor apple generic and certificate leaf[subject.OU] = \"\(ProcessInfo.processInfo.teamID)\"")
|
||||
connection.resume()
|
||||
guard let proxy = connection.remoteObjectProxy as? _XPCProtocol else { fatalError() }
|
||||
self.connection = connection
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import Foundation
|
||||
import Testing
|
||||
@testable import SecretKit
|
||||
@testable import SecureEnclaveSecretKit
|
||||
@testable import SmartCardSecretKit
|
||||
import SSHProtocolKit
|
||||
|
||||
@Suite struct OpenSSHPublicKeyWriterTests {
|
||||
|
||||
@@ -47,8 +46,8 @@ import Testing
|
||||
extension OpenSSHPublicKeyWriterTests {
|
||||
|
||||
enum Constants {
|
||||
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 = 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"))
|
||||
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 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"))
|
||||
|
||||
}
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
import Foundation
|
||||
import Testing
|
||||
@testable import SecretAgentKit
|
||||
@testable import SecureEnclaveSecretKit
|
||||
@testable import SmartCardSecretKit
|
||||
import SSHProtocolKit
|
||||
|
||||
@Suite struct OpenSSHReaderTests {
|
||||
|
||||
@@ -0,0 +1,83 @@
|
||||
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)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
11
Sources/Packages/Tests/SSHProtocolKitTests/TestSecret.swift
Normal file
11
Sources/Packages/Tests/SSHProtocolKitTests/TestSecret.swift
Normal file
@@ -0,0 +1,11 @@
|
||||
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,6 +1,7 @@
|
||||
import Foundation
|
||||
import Testing
|
||||
import CryptoKit
|
||||
@testable import SSHProtocolKit
|
||||
@testable import SecretKit
|
||||
@testable import SecretAgentKit
|
||||
|
||||
@@ -44,8 +45,8 @@ import CryptoKit
|
||||
let agent = Agent(storeList: list)
|
||||
let response = await agent.handle(request: request, provenance: .test)
|
||||
let responseReader = OpenSSHReader(data: response)
|
||||
let length = try responseReader.readNextBytes(as: UInt32.self).bigEndian
|
||||
let type = try responseReader.readNextBytes(as: UInt8.self).bigEndian
|
||||
let length = try responseReader.readNextBytes(as: UInt32.self)
|
||||
let type = try responseReader.readNextBytes(as: UInt8.self)
|
||||
#expect(length == response.count - MemoryLayout<UInt32>.size)
|
||||
#expect(type == SSHAgent.Response.agentSignResponse.rawValue)
|
||||
let outer = OpenSSHReader(data: responseReader.remaining)
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import Foundation
|
||||
import SecretKit
|
||||
import CryptoKit
|
||||
import SSHProtocolKit
|
||||
|
||||
struct Stub {}
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@ class AppDelegate: NSObject, NSApplicationDelegate {
|
||||
}()
|
||||
private let updater = Updater(checkOnLaunch: true)
|
||||
private let notifier = Notifier()
|
||||
private let publicKeyFileStoreController = PublicKeyFileStoreController(homeDirectory: URL.homeDirectory)
|
||||
private let publicKeyFileStoreController = PublicKeyFileStoreController(directory: URL.publicKeyDirectory)
|
||||
private lazy var agent: Agent = {
|
||||
Agent(storeList: storeList, witness: notifier)
|
||||
}()
|
||||
@@ -35,9 +35,9 @@ class AppDelegate: NSObject, NSApplicationDelegate {
|
||||
func applicationDidFinishLaunching(_ aNotification: Notification) {
|
||||
logger.debug("SecretAgent finished launching")
|
||||
Task {
|
||||
let inputParser = try await XPCAgentInputParser()
|
||||
for await session in socketController.sessions {
|
||||
Task {
|
||||
let inputParser = try await XPCAgentInputParser()
|
||||
do {
|
||||
for await message in session.messages {
|
||||
let request = try await inputParser.parse(data: message)
|
||||
|
||||
@@ -2,8 +2,24 @@
|
||||
<!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-string</key>
|
||||
<string>1</string>
|
||||
<key>com.apple.security.hardened-process.hardened-heap</key>
|
||||
<true/>
|
||||
<key>com.apple.security.smartcard</key>
|
||||
<true/>
|
||||
<key>com.apple.security.hardened-process.platform-restrictions-string</key>
|
||||
<string>2</string>
|
||||
<key>keychain-access-groups</key>
|
||||
<array>
|
||||
<string>$(AppIdentifierPrefix)com.maxgoedjen.Secretive</string>
|
||||
|
||||
@@ -3,6 +3,7 @@ import SecretAgentKit
|
||||
import Brief
|
||||
import XPCWrappers
|
||||
import OSLog
|
||||
import SSHProtocolKit
|
||||
|
||||
/// Delegates all agent input parsing to an XPC service which wraps OpenSSH
|
||||
public final class XPCAgentInputParser: SSHAgentInputParserProtocol {
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>com.apple.security.hardened-process</key>
|
||||
<true/>
|
||||
<key>com.apple.security.hardened-process.checked-allocations</key>
|
||||
<true/>
|
||||
<key>com.apple.security.hardened-process.checked-allocations.enable-pure-data</key>
|
||||
<true/>
|
||||
<key>com.apple.security.hardened-process.checked-allocations.no-tagged-receive</key>
|
||||
<true/>
|
||||
<key>com.apple.security.hardened-process.dyld-ro</key>
|
||||
<true/>
|
||||
<key>com.apple.security.hardened-process.hardened-heap</key>
|
||||
<true/>
|
||||
<key>com.apple.security.hardened-process.enhanced-security-version-string</key>
|
||||
<string>1</string>
|
||||
<key>com.apple.security.hardened-process.platform-restrictions-string</key>
|
||||
<string>2</string>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -2,6 +2,7 @@ import Foundation
|
||||
import OSLog
|
||||
import XPCWrappers
|
||||
import SecretAgentKit
|
||||
import SSHProtocolKit
|
||||
|
||||
final class SecretAgentInputParser: NSObject, XPCProtocol {
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
/* Begin PBXBuildFile section */
|
||||
2C4A9D2F2636FFD3008CC8E2 /* EditSecretView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C4A9D2E2636FFD3008CC8E2 /* EditSecretView.swift */; };
|
||||
50020BB024064869003D4025 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50020BAF24064869003D4025 /* AppDelegate.swift */; };
|
||||
5002C3AB2EEF483300FFAD22 /* XPCWrappers in Frameworks */ = {isa = PBXBuildFile; productRef = 5002C3AA2EEF483300FFAD22 /* XPCWrappers */; };
|
||||
5003EF3B278005E800DF2006 /* SecretKit in Frameworks */ = {isa = PBXBuildFile; productRef = 5003EF3A278005E800DF2006 /* SecretKit */; };
|
||||
5003EF3D278005F300DF2006 /* Brief in Frameworks */ = {isa = PBXBuildFile; productRef = 5003EF3C278005F300DF2006 /* Brief */; };
|
||||
5003EF3F278005F300DF2006 /* SecretAgentKit in Frameworks */ = {isa = PBXBuildFile; productRef = 5003EF3E278005F300DF2006 /* SecretAgentKit */; };
|
||||
@@ -60,6 +61,7 @@
|
||||
50A3B79424026B7600D209EA /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 50A3B79324026B7600D209EA /* Preview Assets.xcassets */; };
|
||||
50A3B79724026B7600D209EA /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 50A3B79524026B7600D209EA /* Main.storyboard */; };
|
||||
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 */; };
|
||||
50BB046B2418AAAE00D6E079 /* EmptyStoreView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50BB046A2418AAAE00D6E079 /* EmptyStoreView.swift */; };
|
||||
50BDCB722E63BAF20072D2E7 /* AgentStatusView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50BDCB712E63BAF20072D2E7 /* AgentStatusView.swift */; };
|
||||
@@ -181,6 +183,8 @@
|
||||
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>"; };
|
||||
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; };
|
||||
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>"; };
|
||||
@@ -237,6 +241,7 @@
|
||||
50E4C4522E73C78900C73783 /* WindowBackgroundStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WindowBackgroundStyle.swift; sourceTree = "<group>"; };
|
||||
50E4C4C22E7765DF00C73783 /* AboutView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AboutView.swift; sourceTree = "<group>"; };
|
||||
50E4C4C72E777E4200C73783 /* AppIcon.icon */ = {isa = PBXFileReference; lastKnownFileType = folder.iconcomposer.icon; path = AppIcon.icon; sourceTree = "<group>"; };
|
||||
F418C9A82F0C57F000E9ADF8 /* OpenSource.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = OpenSource.xcconfig; sourceTree = "<group>"; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
@@ -265,6 +270,7 @@
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
5002C3AB2EEF483300FFAD22 /* XPCWrappers in Frameworks */,
|
||||
50692E6C2E6FFA510043C7BB /* SecretAgentKit in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
@@ -392,6 +398,7 @@
|
||||
50692D272E6FDB8D0043C7BB /* SecretiveUpdater */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
500666D02F04786900328939 /* SecretiveUpdater.entitlements */,
|
||||
50692D232E6FDB8D0043C7BB /* Info.plist */,
|
||||
50692BA52E6D5CC90043C7BB /* InternetAccessPolicy.plist */,
|
||||
50692D242E6FDB8D0043C7BB /* main.swift */,
|
||||
@@ -403,6 +410,7 @@
|
||||
50692E662E6FF9E20043C7BB /* SecretAgentInputParser */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
500666D12F04787200328939 /* SecretAgentInputParser.entitlements */,
|
||||
50692E622E6FF9E20043C7BB /* Info.plist */,
|
||||
50692E632E6FF9E20043C7BB /* main.swift */,
|
||||
50692E642E6FF9E20043C7BB /* SecretAgentInputParser.swift */,
|
||||
@@ -415,6 +423,7 @@
|
||||
children = (
|
||||
508A590F241EEF6D0069DC07 /* Secretive.xctestplan */,
|
||||
508A58AB241E121B0069DC07 /* Config.xcconfig */,
|
||||
F418C9A82F0C57F000E9ADF8 /* OpenSource.xcconfig */,
|
||||
);
|
||||
path = Config;
|
||||
sourceTree = "<group>";
|
||||
@@ -539,6 +548,7 @@
|
||||
name = SecretAgentInputParser;
|
||||
packageProductDependencies = (
|
||||
50692E6B2E6FFA510043C7BB /* SecretAgentKit */,
|
||||
5002C3AA2EEF483300FFAD22 /* XPCWrappers */,
|
||||
);
|
||||
productName = SecretAgentInputParser;
|
||||
productReference = 50692E502E6FF9D20043C7BB /* SecretAgentInputParser.xpc */;
|
||||
@@ -582,7 +592,7 @@
|
||||
attributes = {
|
||||
BuildIndependentTargetsInParallel = YES;
|
||||
LastSwiftUpdateCheck = 2600;
|
||||
LastUpgradeCheck = 2600;
|
||||
LastUpgradeCheck = 2640;
|
||||
ORGANIZATIONNAME = "Max Goedjen";
|
||||
TargetAttributes = {
|
||||
50617D7E23FCE48D0099B055 = {
|
||||
@@ -644,6 +654,7 @@
|
||||
isa = PBXResourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
50B832C02F62202A00D2FCB8 /* InternetAccessPolicy.plist in Resources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
@@ -937,7 +948,7 @@
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
DEAD_CODE_STRIPPING = YES;
|
||||
DEVELOPMENT_ASSET_PATHS = "\"Secretive/Preview Content\"";
|
||||
DEVELOPMENT_TEAM = Z72PRUAWF6;
|
||||
DEVELOPMENT_TEAM = "$(SECRETIVE_DEVELOPMENT_TEAM)";
|
||||
ENABLE_APP_SANDBOX = YES;
|
||||
ENABLE_ENHANCED_SECURITY = YES;
|
||||
ENABLE_HARDENED_RUNTIME = YES;
|
||||
@@ -960,7 +971,7 @@
|
||||
);
|
||||
MACOSX_DEPLOYMENT_TARGET = 14.0;
|
||||
MARKETING_VERSION = 1;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.maxgoedjen.Secretive.Host;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "$(SECRETIVE_BASE_BUNDLE_ID).Host";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
};
|
||||
@@ -977,7 +988,7 @@
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
DEAD_CODE_STRIPPING = YES;
|
||||
DEVELOPMENT_ASSET_PATHS = "\"Secretive/Preview Content\"";
|
||||
DEVELOPMENT_TEAM = Z72PRUAWF6;
|
||||
DEVELOPMENT_TEAM = "$(SECRETIVE_DEVELOPMENT_TEAM)";
|
||||
ENABLE_APP_SANDBOX = YES;
|
||||
ENABLE_ENHANCED_SECURITY = YES;
|
||||
ENABLE_HARDENED_RUNTIME = YES;
|
||||
@@ -1000,7 +1011,7 @@
|
||||
);
|
||||
MACOSX_DEPLOYMENT_TARGET = 14.0;
|
||||
MARKETING_VERSION = 1;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.maxgoedjen.Secretive.Host;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "$(SECRETIVE_BASE_BUNDLE_ID).Host";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "Secretive - Host";
|
||||
};
|
||||
@@ -1010,16 +1021,19 @@
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
|
||||
CODE_SIGN_ENTITLEMENTS = SecretiveUpdater/SecretiveUpdater.entitlements;
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
DEVELOPMENT_TEAM = Z72PRUAWF6;
|
||||
DEVELOPMENT_TEAM = "$(SECRETIVE_DEVELOPMENT_TEAM)";
|
||||
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;
|
||||
@@ -1036,7 +1050,7 @@
|
||||
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
|
||||
MACOSX_DEPLOYMENT_TARGET = 14.0;
|
||||
MARKETING_VERSION = 1.0;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.maxgoedjen.Secretive.SecretiveUpdater;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "$(SECRETIVE_BASE_BUNDLE_ID).SecretiveUpdater";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
REGISTER_APP_GROUPS = YES;
|
||||
SKIP_INSTALL = YES;
|
||||
@@ -1053,13 +1067,16 @@
|
||||
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;
|
||||
@@ -1076,7 +1093,7 @@
|
||||
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
|
||||
MACOSX_DEPLOYMENT_TARGET = 14.0;
|
||||
MARKETING_VERSION = 1.0;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.maxgoedjen.Secretive.SecretiveUpdater;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "$(SECRETIVE_BASE_BUNDLE_ID).SecretiveUpdater";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
REGISTER_APP_GROUPS = YES;
|
||||
SKIP_INSTALL = YES;
|
||||
@@ -1092,16 +1109,19 @@
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
|
||||
CODE_SIGN_ENTITLEMENTS = SecretiveUpdater/SecretiveUpdater.entitlements;
|
||||
CODE_SIGN_IDENTITY = "Developer ID Application";
|
||||
CODE_SIGN_STYLE = Manual;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
DEVELOPMENT_TEAM = "";
|
||||
"DEVELOPMENT_TEAM[sdk=macosx*]" = Z72PRUAWF6;
|
||||
"DEVELOPMENT_TEAM[sdk=macosx*]" = "$(SECRETIVE_DEVELOPMENT_TEAM)";
|
||||
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;
|
||||
@@ -1118,7 +1138,7 @@
|
||||
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
|
||||
MACOSX_DEPLOYMENT_TARGET = 14.0;
|
||||
MARKETING_VERSION = 1.0;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.maxgoedjen.Secretive.SecretiveUpdater;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "$(SECRETIVE_BASE_BUNDLE_ID).SecretiveUpdater";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
REGISTER_APP_GROUPS = YES;
|
||||
@@ -1135,13 +1155,16 @@
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
|
||||
CODE_SIGN_ENTITLEMENTS = SecretAgentInputParser/SecretAgentInputParser.entitlements;
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
DEVELOPMENT_TEAM = Z72PRUAWF6;
|
||||
DEVELOPMENT_TEAM = "$(SECRETIVE_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 = SecretAgentInputParser/Info.plist;
|
||||
@@ -1150,7 +1173,7 @@
|
||||
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
|
||||
MACOSX_DEPLOYMENT_TARGET = 14.0;
|
||||
MARKETING_VERSION = 1.0;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.maxgoedjen.Secretive.SecretAgentInputParser;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "$(SECRETIVE_BASE_BUNDLE_ID).SecretAgentInputParser";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
REGISTER_APP_GROUPS = YES;
|
||||
SKIP_INSTALL = YES;
|
||||
@@ -1167,11 +1190,14 @@
|
||||
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;
|
||||
@@ -1180,7 +1206,7 @@
|
||||
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
|
||||
MACOSX_DEPLOYMENT_TARGET = 14.0;
|
||||
MARKETING_VERSION = 1.0;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.maxgoedjen.Secretive.SecretAgentInputParser;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "$(SECRETIVE_BASE_BUNDLE_ID).SecretAgentInputParser";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
REGISTER_APP_GROUPS = YES;
|
||||
SKIP_INSTALL = YES;
|
||||
@@ -1196,14 +1222,17 @@
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
|
||||
CODE_SIGN_ENTITLEMENTS = SecretAgentInputParser/SecretAgentInputParser.entitlements;
|
||||
CODE_SIGN_IDENTITY = "Developer ID Application";
|
||||
CODE_SIGN_STYLE = Manual;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
DEVELOPMENT_TEAM = "";
|
||||
"DEVELOPMENT_TEAM[sdk=macosx*]" = Z72PRUAWF6;
|
||||
"DEVELOPMENT_TEAM[sdk=macosx*]" = "$(SECRETIVE_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 = SecretAgentInputParser/Info.plist;
|
||||
@@ -1212,7 +1241,7 @@
|
||||
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
|
||||
MACOSX_DEPLOYMENT_TARGET = 14.0;
|
||||
MARKETING_VERSION = 1.0;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.maxgoedjen.Secretive.SecretAgentInputParser;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "$(SECRETIVE_BASE_BUNDLE_ID).SecretAgentInputParser";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
REGISTER_APP_GROUPS = YES;
|
||||
@@ -1333,7 +1362,7 @@
|
||||
);
|
||||
MACOSX_DEPLOYMENT_TARGET = 14.0;
|
||||
MARKETING_VERSION = 1;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.maxgoedjen.Secretive.Host;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "$(SECRETIVE_BASE_BUNDLE_ID).Host";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
};
|
||||
name = Test;
|
||||
@@ -1342,14 +1371,17 @@
|
||||
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;
|
||||
@@ -1366,7 +1398,7 @@
|
||||
);
|
||||
MACOSX_DEPLOYMENT_TARGET = 14.0;
|
||||
MARKETING_VERSION = 1;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.maxgoedjen.Secretive.SecretAgent;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "$(SECRETIVE_BASE_BUNDLE_ID).SecretAgent";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
};
|
||||
name = Test;
|
||||
@@ -1380,11 +1412,13 @@
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
DEAD_CODE_STRIPPING = YES;
|
||||
DEVELOPMENT_ASSET_PATHS = "\"SecretAgent/Preview Content\"";
|
||||
DEVELOPMENT_TEAM = Z72PRUAWF6;
|
||||
DEVELOPMENT_TEAM = "$(SECRETIVE_DEVELOPMENT_TEAM)";
|
||||
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;
|
||||
@@ -1401,7 +1435,7 @@
|
||||
);
|
||||
MACOSX_DEPLOYMENT_TARGET = 14.0;
|
||||
MARKETING_VERSION = 1;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.maxgoedjen.Secretive.SecretAgent;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "$(SECRETIVE_BASE_BUNDLE_ID).SecretAgent";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
};
|
||||
name = Debug;
|
||||
@@ -1416,11 +1450,13 @@
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
DEAD_CODE_STRIPPING = YES;
|
||||
DEVELOPMENT_ASSET_PATHS = "\"SecretAgent/Preview Content\"";
|
||||
DEVELOPMENT_TEAM = Z72PRUAWF6;
|
||||
DEVELOPMENT_TEAM = "$(SECRETIVE_DEVELOPMENT_TEAM)";
|
||||
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;
|
||||
@@ -1437,7 +1473,7 @@
|
||||
);
|
||||
MACOSX_DEPLOYMENT_TARGET = 14.0;
|
||||
MARKETING_VERSION = 1;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.maxgoedjen.Secretive.SecretAgent;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "$(SECRETIVE_BASE_BUNDLE_ID).SecretAgent";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "Secretive - Secret Agent";
|
||||
};
|
||||
@@ -1499,6 +1535,10 @@
|
||||
/* End XCConfigurationList section */
|
||||
|
||||
/* Begin XCSwiftPackageProductDependency section */
|
||||
5002C3AA2EEF483300FFAD22 /* XPCWrappers */ = {
|
||||
isa = XCSwiftPackageProductDependency;
|
||||
productName = XPCWrappers;
|
||||
};
|
||||
5003EF3A278005E800DF2006 /* SecretKit */ = {
|
||||
isa = XCSwiftPackageProductDependency;
|
||||
productName = SecretKit;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "2600"
|
||||
LastUpgradeVersion = "2640"
|
||||
version = "1.7">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
@@ -14,7 +14,8 @@
|
||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||
<TestPlans>
|
||||
<TestPlanReference
|
||||
reference = "container:Config/Secretive.xctestplan">
|
||||
reference = "container:Config/Secretive.xctestplan"
|
||||
default = "YES">
|
||||
</TestPlanReference>
|
||||
</TestPlans>
|
||||
</TestAction>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "2600"
|
||||
LastUpgradeVersion = "2640"
|
||||
version = "1.7">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "2600"
|
||||
LastUpgradeVersion = "2640"
|
||||
version = "1.7">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
|
||||
@@ -4,16 +4,22 @@
|
||||
<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>
|
||||
<key>com.apple.security.hardened-process.enhanced-security-version-string</key>
|
||||
<string>1</string>
|
||||
<key>com.apple.security.smartcard</key>
|
||||
<true/>
|
||||
<key>com.apple.security.hardened-process.platform-restrictions-string</key>
|
||||
<string>2</string>
|
||||
<key>keychain-access-groups</key>
|
||||
<array>
|
||||
<string>$(AppIdentifierPrefix)com.maxgoedjen.Secretive</string>
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import SwiftUI
|
||||
import SecretKit
|
||||
import SSHProtocolKit
|
||||
import Common
|
||||
|
||||
struct ToolConfigurationView: View {
|
||||
|
||||
@@ -111,10 +113,9 @@ struct ToolConfigurationView: View {
|
||||
let writer = OpenSSHPublicKeyWriter()
|
||||
let gitAllowedSignersString = [email.isEmpty ? String(localized: .integrationsConfigureUsingEmailPlaceholder) : email, writer.openSSHString(secret: selectedSecret)]
|
||||
.joined(separator: " ")
|
||||
let fileController = PublicKeyFileStoreController(homeDirectory: URL.agentHomeURL)
|
||||
return text
|
||||
.replacingOccurrences(of: Instructions.Constants.publicKeyPlaceholder, with: gitAllowedSignersString)
|
||||
.replacingOccurrences(of: Instructions.Constants.publicKeyPathPlaceholder, with: fileController.publicKeyPath(for: selectedSecret))
|
||||
.replacingOccurrences(of: Instructions.Constants.publicKeyPathPlaceholder, with: URL.publicKeyPath(for: selectedSecret, in: URL.publicKeyDirectory))
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
import SwiftUI
|
||||
import SecretKit
|
||||
import Common
|
||||
import SSHProtocolKit
|
||||
|
||||
struct SecretDetailView<SecretType: Secret>: View {
|
||||
|
||||
let secret: SecretType
|
||||
|
||||
private let keyWriter = OpenSSHPublicKeyWriter()
|
||||
private let publicKeyFileStoreController = PublicKeyFileStoreController(homeDirectory: URL.agentHomeURL)
|
||||
|
||||
var body: some View {
|
||||
ScrollView {
|
||||
@@ -21,7 +22,7 @@ struct SecretDetailView<SecretType: Secret>: View {
|
||||
CopyableView(title: .secretDetailPublicKeyLabel, image: Image(systemName: "key"), text: keyString)
|
||||
Spacer()
|
||||
.frame(height: 20)
|
||||
CopyableView(title: .secretDetailPublicKeyPathLabel, image: Image(systemName: "lock.doc"), text: publicKeyFileStoreController.publicKeyPath(for: secret), showRevealInFinder: true)
|
||||
CopyableView(title: .secretDetailPublicKeyPathLabel, image: Image(systemName: "lock.doc"), text: URL.publicKeyPath(for: secret, in: URL.publicKeyDirectory), showRevealInFinder: true)
|
||||
Spacer()
|
||||
}
|
||||
}
|
||||
|
||||
22
Sources/SecretiveUpdater/SecretiveUpdater.entitlements
Normal file
22
Sources/SecretiveUpdater/SecretiveUpdater.entitlements
Normal file
@@ -0,0 +1,22 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>com.apple.security.hardened-process</key>
|
||||
<true/>
|
||||
<key>com.apple.security.hardened-process.checked-allocations</key>
|
||||
<true/>
|
||||
<key>com.apple.security.hardened-process.checked-allocations.enable-pure-data</key>
|
||||
<true/>
|
||||
<key>com.apple.security.hardened-process.checked-allocations.no-tagged-receive</key>
|
||||
<true/>
|
||||
<key>com.apple.security.hardened-process.dyld-ro</key>
|
||||
<true/>
|
||||
<key>com.apple.security.hardened-process.enhanced-security-version-string</key>
|
||||
<string>1</string>
|
||||
<key>com.apple.security.hardened-process.hardened-heap</key>
|
||||
<true/>
|
||||
<key>com.apple.security.hardened-process.platform-restrictions-string</key>
|
||||
<string>2</string>
|
||||
</dict>
|
||||
</plist>
|
||||
56
configure_team_id.sh
Executable file
56
configure_team_id.sh
Executable file
@@ -0,0 +1,56 @@
|
||||
#!/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