mirror of
https://github.com/maxgoedjen/secretive.git
synced 2024-12-22 04:07:09 +00:00
parent
cf58630065
commit
171981de9f
@ -1,4 +1,4 @@
|
||||
// swift-tools-version:5.5
|
||||
// swift-tools-version:5.9
|
||||
// The swift-tools-version declares the minimum version of Swift required to build this package.
|
||||
|
||||
import PackageDescription
|
||||
@ -33,23 +33,28 @@ let package = Package(
|
||||
targets: [
|
||||
.target(
|
||||
name: "SecretKit",
|
||||
dependencies: []
|
||||
dependencies: [],
|
||||
swiftSettings: [.enableExperimentalFeature("StrictConcurrency"), .unsafeFlags(["-warnings-as-errors"])]
|
||||
),
|
||||
.testTarget(
|
||||
name: "SecretKitTests",
|
||||
dependencies: ["SecretKit", "SecureEnclaveSecretKit", "SmartCardSecretKit"]
|
||||
dependencies: ["SecretKit", "SecureEnclaveSecretKit", "SmartCardSecretKit"],
|
||||
swiftSettings: [.enableExperimentalFeature("StrictConcurrency"), .unsafeFlags(["-warnings-as-errors"])]
|
||||
),
|
||||
.target(
|
||||
name: "SecureEnclaveSecretKit",
|
||||
dependencies: ["SecretKit"]
|
||||
dependencies: ["SecretKit"],
|
||||
swiftSettings: [.enableExperimentalFeature("StrictConcurrency"), .unsafeFlags(["-warnings-as-errors"])]
|
||||
),
|
||||
.target(
|
||||
name: "SmartCardSecretKit",
|
||||
dependencies: ["SecretKit"]
|
||||
dependencies: ["SecretKit"],
|
||||
swiftSettings: [.enableExperimentalFeature("StrictConcurrency"), .unsafeFlags(["-warnings-as-errors"])]
|
||||
),
|
||||
.target(
|
||||
name: "SecretAgentKit",
|
||||
dependencies: ["SecretKit", "SecretAgentKitHeaders"]
|
||||
dependencies: ["SecretKit", "SecretAgentKitHeaders"],
|
||||
swiftSettings: [.enableExperimentalFeature("StrictConcurrency"), .unsafeFlags(["-warnings-as-errors"])]
|
||||
),
|
||||
.systemLibrary(
|
||||
name: "SecretAgentKitHeaders"
|
||||
|
@ -2,7 +2,7 @@ import Foundation
|
||||
import Combine
|
||||
|
||||
/// A concrete implementation of ``UpdaterProtocol`` which considers the current release and OS version.
|
||||
public class Updater: ObservableObject, UpdaterProtocol {
|
||||
public final class Updater: ObservableObject, UpdaterProtocol {
|
||||
|
||||
@Published public var update: Release?
|
||||
public let testBuild: Bool
|
||||
|
@ -5,7 +5,7 @@ import SecretKit
|
||||
import AppKit
|
||||
|
||||
/// 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 class Agent {
|
||||
public final class Agent {
|
||||
|
||||
private let storeList: SecretStoreList
|
||||
private let witness: SigningWitness?
|
||||
|
11
Sources/Packages/Sources/SecretAgentKit/Sendability.swift
Normal file
11
Sources/Packages/Sources/SecretAgentKit/Sendability.swift
Normal file
@ -0,0 +1,11 @@
|
||||
import Foundation
|
||||
|
||||
struct UncheckedSendable<T>: @unchecked Sendable {
|
||||
|
||||
let value: T
|
||||
|
||||
init(_ value: T) {
|
||||
self.value = value
|
||||
}
|
||||
|
||||
}
|
@ -2,7 +2,7 @@ import Foundation
|
||||
import OSLog
|
||||
|
||||
/// A controller that manages socket configuration and request dispatching.
|
||||
public class SocketController {
|
||||
public final class SocketController {
|
||||
|
||||
/// The active FileHandle.
|
||||
private var fileHandle: FileHandle?
|
||||
@ -10,7 +10,7 @@ public class SocketController {
|
||||
private var port: SocketPort?
|
||||
/// A handler that will be notified when a new read/write handle is available.
|
||||
/// False if no data could be read
|
||||
public var handler: ((FileHandleReader, FileHandleWriter) async -> Bool)?
|
||||
public var handler: (@Sendable (FileHandleReader, FileHandleWriter) async -> Bool)?
|
||||
/// Logger.
|
||||
private let logger = Logger(subsystem: "com.maxgoedjen.secretive.secretagent", category: "SocketController")
|
||||
|
||||
@ -69,7 +69,7 @@ public class SocketController {
|
||||
@objc func handleConnectionAccept(notification: Notification) {
|
||||
logger.debug("Socket controller accepted connection")
|
||||
guard let new = notification.userInfo?[NSFileHandleNotificationFileHandleItem] as? FileHandle else { return }
|
||||
Task {
|
||||
Task { [handler, fileHandle] in
|
||||
_ = await handler?(new, new)
|
||||
await new.waitForDataInBackgroundAndNotifyOnMainActor()
|
||||
await fileHandle?.acceptConnectionInBackgroundAndNotifyOnMainActor()
|
||||
@ -82,12 +82,12 @@ public class SocketController {
|
||||
logger.debug("Socket controller has new data available")
|
||||
guard let new = notification.object as? FileHandle else { return }
|
||||
logger.debug("Socket controller received new file handle")
|
||||
Task {
|
||||
Task { [handler, logger = UncheckedSendable(logger)] in
|
||||
if((await handler?(new, new)) == true) {
|
||||
logger.debug("Socket controller handled data, wait for more data")
|
||||
logger.value.debug("Socket controller handled data, wait for more data")
|
||||
await new.waitForDataInBackgroundAndNotifyOnMainActor()
|
||||
} else {
|
||||
logger.debug("Socket controller called with empty data, socked closed")
|
||||
logger.value.debug("Socket controller called with empty data, socked closed")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -71,7 +71,7 @@ public class AnySecretStore: SecretStore {
|
||||
|
||||
}
|
||||
|
||||
public class AnySecretStoreModifiable: AnySecretStore, SecretStoreModifiable {
|
||||
public final class AnySecretStoreModifiable: AnySecretStore, SecretStoreModifiable {
|
||||
|
||||
private let _create: (String, Bool) throws -> Void
|
||||
private let _delete: (AnySecret) throws -> Void
|
||||
|
@ -2,7 +2,7 @@ import Foundation
|
||||
import OSLog
|
||||
|
||||
/// Manages storage and lookup for OpenSSH certificates.
|
||||
public class OpenSSHCertificateHandler {
|
||||
public final class OpenSSHCertificateHandler {
|
||||
|
||||
private let publicKeyFileStoreController = PublicKeyFileStoreController(homeDirectory: NSHomeDirectory())
|
||||
private let logger = Logger(subsystem: "com.maxgoedjen.secretive.secretagent", category: "OpenSSHCertificateHandler")
|
||||
|
@ -1,7 +1,7 @@
|
||||
import Foundation
|
||||
|
||||
/// Reads OpenSSH protocol data.
|
||||
public class OpenSSHReader {
|
||||
public final class OpenSSHReader {
|
||||
|
||||
var remaining: Data
|
||||
|
||||
|
@ -2,7 +2,7 @@ import Foundation
|
||||
import OSLog
|
||||
|
||||
/// Controller responsible for writing public keys to disk, so that they're easily accessible by scripts.
|
||||
public class PublicKeyFileStoreController {
|
||||
public final class PublicKeyFileStoreController {
|
||||
|
||||
private let logger = Logger(subsystem: "com.maxgoedjen.secretive.secretagent", category: "PublicKeyFileStoreController")
|
||||
private let directory: String
|
||||
|
@ -2,7 +2,7 @@ import Foundation
|
||||
import Combine
|
||||
|
||||
/// A "Store Store," which holds a list of type-erased stores.
|
||||
public class SecretStoreList: ObservableObject {
|
||||
public final class SecretStoreList: ObservableObject {
|
||||
|
||||
/// The Stores managed by the SecretStoreList.
|
||||
@Published public var stores: [AnySecretStore] = []
|
||||
|
@ -8,7 +8,7 @@ import SecretKit
|
||||
extension SecureEnclave {
|
||||
|
||||
/// An implementation of Store backed by the Secure Enclave.
|
||||
public class Store: SecretStoreModifiable {
|
||||
public final class Store: SecretStoreModifiable {
|
||||
|
||||
public var isAvailable: Bool {
|
||||
// For some reason, as of build time, CryptoKit.SecureEnclave.isAvailable always returns false
|
||||
@ -24,8 +24,8 @@ extension SecureEnclave {
|
||||
|
||||
/// Initializes a Store.
|
||||
public init() {
|
||||
DistributedNotificationCenter.default().addObserver(forName: .secretStoreUpdated, object: nil, queue: .main) { _ in
|
||||
self.reloadSecretsInternal(notifyAgent: false)
|
||||
DistributedNotificationCenter.default().addObserver(forName: .secretStoreUpdated, object: nil, queue: .main) { [reload = reloadSecretsInternal(notifyAgent:)] _ in
|
||||
reload(false)
|
||||
}
|
||||
loadSecrets()
|
||||
}
|
||||
@ -211,7 +211,7 @@ extension SecureEnclave.Store {
|
||||
|
||||
/// Reloads all secrets from the store.
|
||||
/// - Parameter notifyAgent: A boolean indicating whether a distributed notification should be posted, notifying other processes (ie, the SecretAgent) to reload their stores as well.
|
||||
private func reloadSecretsInternal(notifyAgent: Bool = true) {
|
||||
@Sendable private func reloadSecretsInternal(notifyAgent: Bool = true) {
|
||||
let before = secrets
|
||||
secrets.removeAll()
|
||||
loadSecrets()
|
||||
|
@ -8,7 +8,7 @@ import SecretKit
|
||||
extension SmartCard {
|
||||
|
||||
/// An implementation of Store backed by a Smart Card.
|
||||
public class Store: SecretStore {
|
||||
public final class Store: SecretStore {
|
||||
|
||||
@Published public var isAvailable: Bool = false
|
||||
public let id = UUID()
|
||||
@ -20,12 +20,14 @@ extension SmartCard {
|
||||
/// Initializes a Store.
|
||||
public init() {
|
||||
tokenID = watcher.nonSecureEnclaveTokens.first
|
||||
watcher.setInsertionHandler { string in
|
||||
watcher.setInsertionHandler { [reload = reloadSecretsInternal] string in
|
||||
guard self.tokenID == nil else { return }
|
||||
guard !string.contains("setoken") else { return }
|
||||
|
||||
self.tokenID = string
|
||||
self.reloadSecrets()
|
||||
DispatchQueue.main.async {
|
||||
reload()
|
||||
}
|
||||
self.watcher.addRemovalHandler(self.smartcardRemoved, forTokenID: string)
|
||||
}
|
||||
if let tokenID = tokenID {
|
||||
@ -106,15 +108,7 @@ extension SmartCard {
|
||||
|
||||
/// Reloads all secrets from the store.
|
||||
public func reloadSecrets() {
|
||||
DispatchQueue.main.async {
|
||||
self.isAvailable = self.tokenID != nil
|
||||
let before = self.secrets
|
||||
self.secrets.removeAll()
|
||||
self.loadSecrets()
|
||||
if self.secrets != before {
|
||||
NotificationCenter.default.post(name: .secretStoreReloaded, object: self)
|
||||
}
|
||||
}
|
||||
reloadSecretsInternal()
|
||||
}
|
||||
|
||||
}
|
||||
@ -123,6 +117,16 @@ extension SmartCard {
|
||||
|
||||
extension SmartCard.Store {
|
||||
|
||||
@Sendable private func reloadSecretsInternal() {
|
||||
self.isAvailable = self.tokenID != nil
|
||||
let before = self.secrets
|
||||
self.secrets.removeAll()
|
||||
self.loadSecrets()
|
||||
if self.secrets != before {
|
||||
NotificationCenter.default.post(name: .secretStoreReloaded, object: self)
|
||||
}
|
||||
}
|
||||
|
||||
/// Resets the token ID and reloads secrets.
|
||||
/// - Parameter tokenID: The ID of the token that was removed.
|
||||
private func smartcardRemoved(for tokenID: String? = nil) {
|
||||
|
@ -6,7 +6,7 @@ struct Stub {}
|
||||
|
||||
extension Stub {
|
||||
|
||||
public class Store: SecretStore {
|
||||
public final class Store: SecretStore {
|
||||
|
||||
public let isAvailable = true
|
||||
public let id = UUID()
|
||||
|
@ -3,7 +3,7 @@
|
||||
archiveVersion = 1;
|
||||
classes = {
|
||||
};
|
||||
objectVersion = 52;
|
||||
objectVersion = 54;
|
||||
objects = {
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
@ -691,6 +691,7 @@
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.maxgoedjen.Secretive.Host;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
SWIFT_STRICT_CONCURRENCY = complete;
|
||||
SWIFT_VERSION = 5.0;
|
||||
};
|
||||
name = Debug;
|
||||
@ -718,6 +719,7 @@
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.maxgoedjen.Secretive.Host;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "Secretive - Host";
|
||||
SWIFT_STRICT_CONCURRENCY = complete;
|
||||
SWIFT_VERSION = 5.0;
|
||||
};
|
||||
name = Release;
|
||||
@ -848,6 +850,7 @@
|
||||
MARKETING_VERSION = 1;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.maxgoedjen.Secretive.Host;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_STRICT_CONCURRENCY = complete;
|
||||
SWIFT_VERSION = 5.0;
|
||||
};
|
||||
name = Test;
|
||||
@ -891,6 +894,7 @@
|
||||
MARKETING_VERSION = 1;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.maxgoedjen.Secretive.SecretAgent;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_STRICT_CONCURRENCY = complete;
|
||||
SWIFT_VERSION = 5.0;
|
||||
};
|
||||
name = Test;
|
||||
@ -914,6 +918,7 @@
|
||||
MARKETING_VERSION = 1;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.maxgoedjen.Secretive.SecretAgent;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_STRICT_CONCURRENCY = complete;
|
||||
SWIFT_VERSION = 5.0;
|
||||
};
|
||||
name = Debug;
|
||||
@ -939,6 +944,7 @@
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.maxgoedjen.Secretive.SecretAgent;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "Secretive - Secret Agent";
|
||||
SWIFT_STRICT_CONCURRENCY = complete;
|
||||
SWIFT_VERSION = 5.0;
|
||||
};
|
||||
name = Release;
|
||||
|
Loading…
Reference in New Issue
Block a user