diff --git a/SecretAgent/Notifier.swift b/SecretAgent/Notifier.swift index 8167a54..ffa8059 100644 --- a/SecretAgent/Notifier.swift +++ b/SecretAgent/Notifier.swift @@ -14,8 +14,42 @@ class Notifier { let ignoreAction = UNNotificationAction(identifier: Constants.ignoreActionIdentitifier, title: "Ignore", options: []) let updateCategory = UNNotificationCategory(identifier: Constants.updateCategoryIdentitifier, actions: [updateAction, ignoreAction], intentIdentifiers: [], options: []) let criticalUpdateCategory = UNNotificationCategory(identifier: Constants.criticalUpdateCategoryIdentitifier, actions: [updateAction], intentIdentifiers: [], options: []) - UNUserNotificationCenter.current().setNotificationCategories([updateCategory, criticalUpdateCategory]) + + let rawDurations = [ + Measurement(value: 1, unit: UnitDuration.minutes), + Measurement(value: 5, unit: UnitDuration.minutes), + Measurement(value: 1, unit: UnitDuration.hours), + Measurement(value: 24, unit: UnitDuration.hours) + ] + + let doNotPersistAction = UNNotificationAction(identifier: Constants.doNotPersistActionIdentitifier, title: "Do Not Unlock", options: []) + var allPersistenceActions = [doNotPersistAction] + + let formatter = DateComponentsFormatter() + formatter.unitsStyle = .spellOut + formatter.allowedUnits = [.hour, .minute, .day] + + for duration in rawDurations { + let seconds = duration.converted(to: .seconds).value + guard let string = formatter.string(from: seconds)?.capitalized else { continue } + let identifier = Constants.persistAuthenticationCategoryIdentitifier.appending("\(seconds)") + let action = UNNotificationAction(identifier: identifier, title: string, options: []) + notificationDelegate.persistOptions[identifier] = seconds + allPersistenceActions.append(action) + } + + let persistAuthenticationCategory = UNNotificationCategory(identifier: Constants.persistAuthenticationCategoryIdentitifier, actions: allPersistenceActions, intentIdentifiers: [], options: []) + if persistAuthenticationCategory.responds(to: Selector(("actionsMenuTitle"))) { + persistAuthenticationCategory.setValue("Leave Unlocked", forKey: "_actionsMenuTitle") + } + UNUserNotificationCenter.current().setNotificationCategories([updateCategory, criticalUpdateCategory, persistAuthenticationCategory]) UNUserNotificationCenter.current().delegate = notificationDelegate + + notificationDelegate.persistAuthentication = { secret, store, duration in + guard let duration = duration else { return } + try? store.persistAuthentication(secret: secret, forDuration: duration) + } + } func prompt() { @@ -23,11 +57,21 @@ class Notifier { notificationCenter.requestAuthorization(options: .alert) { _, _ in } } - func notify(accessTo secret: AnySecret, by provenance: SigningRequestProvenance) { + func notify(accessTo secret: AnySecret, from store: AnySecretStore, by provenance: SigningRequestProvenance, requiredAuthentication: Bool) { + notificationDelegate.pendingPersistableSecrets[secret.id.description] = secret + notificationDelegate.pendingPersistableStores[store.id.description] = store let notificationCenter = UNUserNotificationCenter.current() let notificationContent = UNMutableNotificationContent() notificationContent.title = "Signed Request from \(provenance.origin.displayName)" notificationContent.subtitle = "Using secret \"\(secret.name)\"" + notificationContent.userInfo[Constants.persistSecretIDKey] = secret.id.description + notificationContent.userInfo[Constants.persistStoreIDKey] = store.id.description + if #available(macOS 12.0, *) { + notificationContent.interruptionLevel = .timeSensitive + } + if requiredAuthentication { + notificationContent.categoryIdentifier = Constants.persistAuthenticationCategoryIdentitifier + } if let iconURL = provenance.origin.iconURL, let attachment = try? UNNotificationAttachment(identifier: "icon", url: iconURL, options: nil) { notificationContent.attachments = [attachment] } @@ -41,6 +85,9 @@ class Notifier { let notificationCenter = UNUserNotificationCenter.current() let notificationContent = UNMutableNotificationContent() if update.critical { + if #available(macOS 12.0, *) { + notificationContent.interruptionLevel = .critical + } notificationContent.title = "Critical Security Update - \(update.name)" } else { notificationContent.title = "Update Available - \(update.name)" @@ -56,11 +103,11 @@ class Notifier { extension Notifier: SigningWitness { - func speakNowOrForeverHoldYourPeace(forAccessTo secret: AnySecret, by provenance: SigningRequestProvenance) throws { + func speakNowOrForeverHoldYourPeace(forAccessTo secret: AnySecret, from store: AnySecretStore, by provenance: SigningRequestProvenance) throws { } - func witness(accessTo secret: AnySecret, by provenance: SigningRequestProvenance) throws { - notify(accessTo: secret, by: provenance) + func witness(accessTo secret: AnySecret, from store: AnySecretStore, by provenance: SigningRequestProvenance, requiredAuthentication: Bool) throws { + notify(accessTo: secret, from: store, by: provenance, requiredAuthentication: requiredAuthentication) } } @@ -68,10 +115,20 @@ extension Notifier: SigningWitness { extension Notifier { enum Constants { + + // Update notifications static let updateCategoryIdentitifier = "com.maxgoedjen.Secretive.SecretAgent.update" static let criticalUpdateCategoryIdentitifier = "com.maxgoedjen.Secretive.SecretAgent.update.critical" static let updateActionIdentitifier = "com.maxgoedjen.Secretive.SecretAgent.update.updateaction" static let ignoreActionIdentitifier = "com.maxgoedjen.Secretive.SecretAgent.update.ignoreaction" + + // Authorization persistence notificatoins + static let persistAuthenticationCategoryIdentitifier = "com.maxgoedjen.Secretive.SecretAgent.persistauthentication" + static let doNotPersistActionIdentitifier = "com.maxgoedjen.Secretive.SecretAgent.persistauthentication.donotpersist" + static let persistForActionIdentitifierPrefix = "com.maxgoedjen.Secretive.SecretAgent.persistauthentication.persist." + + static let persistSecretIDKey = "com.maxgoedjen.Secretive.SecretAgent.persistauthentication.secretidkey" + static let persistStoreIDKey = "com.maxgoedjen.Secretive.SecretAgent.persistauthentication.storeidkey" } } @@ -80,12 +137,30 @@ class NotificationDelegate: NSObject, UNUserNotificationCenterDelegate { fileprivate var release: Release? fileprivate var ignore: ((Release) -> Void)? + fileprivate var persistAuthentication: ((AnySecret, AnySecretStore, TimeInterval?) -> Void)? + fileprivate var persistOptions: [String: TimeInterval] = [:] + fileprivate var pendingPersistableStores: [String: AnySecretStore] = [:] + fileprivate var pendingPersistableSecrets: [String: AnySecret] = [:] func userNotificationCenter(_ center: UNUserNotificationCenter, openSettingsFor notification: UNNotification?) { } func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) { + let category = response.notification.request.content.categoryIdentifier + switch category { + case Notifier.Constants.updateCategoryIdentitifier: + handleUpdateResponse(response: response) + case Notifier.Constants.persistAuthenticationCategoryIdentitifier: + handlePersistAuthenticationResponse(response: response) + default: + fatalError() + } + + completionHandler() + } + + func handleUpdateResponse(response: UNNotificationResponse) { guard let update = release else { return } switch response.actionIdentifier { case Notifier.Constants.updateActionIdentitifier, UNNotificationDefaultActionIdentifier: @@ -95,7 +170,14 @@ class NotificationDelegate: NSObject, UNUserNotificationCenterDelegate { default: fatalError() } - completionHandler() + } + + func handlePersistAuthenticationResponse(response: UNNotificationResponse) { + guard let secretID = response.notification.request.content.userInfo[Notifier.Constants.persistSecretIDKey] as? String, let secret = pendingPersistableSecrets[secretID], + let storeID = response.notification.request.content.userInfo[Notifier.Constants.persistStoreIDKey] as? String, let store = pendingPersistableStores[storeID] + else { return } + pendingPersistableSecrets[secretID] = nil + persistAuthentication?(secret, store, persistOptions[response.actionIdentifier]) } func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) { diff --git a/SecretAgentKit/Agent.swift b/SecretAgentKit/Agent.swift index b71b277..3e69037 100644 --- a/SecretAgentKit/Agent.swift +++ b/SecretAgentKit/Agent.swift @@ -89,11 +89,12 @@ extension Agent { } if let witness = witness { - try witness.speakNowOrForeverHoldYourPeace(forAccessTo: secret, by: provenance) + try witness.speakNowOrForeverHoldYourPeace(forAccessTo: secret, from: store, by: provenance) } let dataToSign = reader.readNextChunk() - let derSignature = try store.sign(data: dataToSign, with: secret, for: provenance) + let signed = try store.sign(data: dataToSign, with: secret, for: provenance) + let derSignature = signed.data let curveData = writer.curveType(for: secret.algorithm, length: secret.keySize).data(using: .utf8)! @@ -134,7 +135,7 @@ extension Agent { signedData.append(writer.lengthAndData(of: sub)) if let witness = witness { - try witness.witness(accessTo: secret, by: provenance) + try witness.witness(accessTo: secret, from: store, by: provenance, requiredAuthentication: signed.requiredAuthentication) } Logger().debug("Agent signed request") diff --git a/SecretAgentKit/SigningWitness.swift b/SecretAgentKit/SigningWitness.swift index d85467c..59e2b6b 100644 --- a/SecretAgentKit/SigningWitness.swift +++ b/SecretAgentKit/SigningWitness.swift @@ -3,7 +3,7 @@ import SecretKit public protocol SigningWitness { - func speakNowOrForeverHoldYourPeace(forAccessTo secret: AnySecret, by provenance: SigningRequestProvenance) throws - func witness(accessTo secret: AnySecret, by provenance: SigningRequestProvenance) throws + func speakNowOrForeverHoldYourPeace(forAccessTo secret: AnySecret, from store: AnySecretStore, by provenance: SigningRequestProvenance) throws + func witness(accessTo secret: AnySecret, from store: AnySecretStore, by provenance: SigningRequestProvenance, requiredAuthentication: Bool) throws } diff --git a/SecretAgentKitTests/StubStore.swift b/SecretAgentKitTests/StubStore.swift index 7f4fb38..42db26a 100644 --- a/SecretAgentKitTests/StubStore.swift +++ b/SecretAgentKitTests/StubStore.swift @@ -48,7 +48,7 @@ extension Stub { print("Public Key OpenSSH: \(OpenSSHKeyWriter().openSSHString(secret: secret))") } - public func sign(data: Data, with secret: Secret, for provenance: SigningRequestProvenance) throws -> Data { + public func sign(data: Data, with secret: Secret, for provenance: SigningRequestProvenance) throws -> SignedData { guard !shouldThrow else { throw NSError(domain: "test", code: 0, userInfo: nil) } @@ -67,7 +67,10 @@ extension Stub { default: fatalError() } - return SecKeyCreateSignature(privateKey, signatureAlgorithm, data as CFData, nil)! as Data + return SignedData(data: SecKeyCreateSignature(privateKey, signatureAlgorithm, data as CFData, nil)! as Data, requiredAuthentication: false) + } + + public func persistAuthentication(secret: Stub.Secret, forDuration duration: TimeInterval) throws { } } diff --git a/SecretAgentKitTests/StubWitness.swift b/SecretAgentKitTests/StubWitness.swift index 230c009..965ce5f 100644 --- a/SecretAgentKitTests/StubWitness.swift +++ b/SecretAgentKitTests/StubWitness.swift @@ -10,14 +10,14 @@ struct StubWitness { extension StubWitness: SigningWitness { - func speakNowOrForeverHoldYourPeace(forAccessTo secret: AnySecret, by provenance: SigningRequestProvenance) throws { +func speakNowOrForeverHoldYourPeace(forAccessTo secret: AnySecret, from store: AnySecretStore, by provenance: SigningRequestProvenance) throws { let objection = speakNow(secret, provenance) if objection { throw TheresMyChance() } } - func witness(accessTo secret: AnySecret, by provenance: SigningRequestProvenance) throws { +func witness(accessTo secret: AnySecret, from store: AnySecretStore, by provenance: SigningRequestProvenance, requiredAuthentication: Bool) throws { witness(secret, provenance) } diff --git a/SecretKit/Common/Erasers/AnySecretStore.swift b/SecretKit/Common/Erasers/AnySecretStore.swift index 8f35e4d..7b565fe 100644 --- a/SecretKit/Common/Erasers/AnySecretStore.swift +++ b/SecretKit/Common/Erasers/AnySecretStore.swift @@ -8,7 +8,9 @@ public class AnySecretStore: SecretStore { private let _id: () -> UUID private let _name: () -> String private let _secrets: () -> [AnySecret] - private let _sign: (Data, AnySecret, SigningRequestProvenance) throws -> Data + private let _sign: (Data, AnySecret, SigningRequestProvenance) throws -> SignedData + private let _persistAuthentication: (AnySecret, TimeInterval) throws -> Void + private var sink: AnyCancellable? public init(_ secretStore: SecretStoreType) where SecretStoreType: SecretStore { @@ -18,6 +20,7 @@ public class AnySecretStore: SecretStore { _id = { secretStore.id } _secrets = { secretStore.secrets.map { AnySecret($0) } } _sign = { try secretStore.sign(data: $0, with: $1.base as! SecretStoreType.SecretType, for: $2) } + _persistAuthentication = { try secretStore.persistAuthentication(secret: $0.base as! SecretStoreType.SecretType, forDuration: $1) } sink = secretStore.objectWillChange.sink { _ in self.objectWillChange.send() } @@ -39,10 +42,14 @@ public class AnySecretStore: SecretStore { return _secrets() } - public func sign(data: Data, with secret: AnySecret, for provenance: SigningRequestProvenance) throws -> Data { + public func sign(data: Data, with secret: AnySecret, for provenance: SigningRequestProvenance) throws -> SignedData { try _sign(data, secret, provenance) } + public func persistAuthentication(secret: AnySecret, forDuration duration: TimeInterval) throws { + try _persistAuthentication(secret, duration) + } + } public class AnySecretStoreModifiable: AnySecretStore, SecretStoreModifiable { @@ -69,4 +76,5 @@ public class AnySecretStoreModifiable: AnySecretStore, SecretStoreModifiable { public func update(secret: AnySecret, name: String) throws { try _update(secret, name) } + } diff --git a/SecretKit/Common/Types/SecretStore.swift b/SecretKit/Common/Types/SecretStore.swift index ac88693..9a8a76e 100644 --- a/SecretKit/Common/Types/SecretStore.swift +++ b/SecretKit/Common/Types/SecretStore.swift @@ -9,7 +9,9 @@ public protocol SecretStore: ObservableObject, Identifiable { var name: String { get } var secrets: [SecretType] { get } - func sign(data: Data, with secret: SecretType, for provenance: SigningRequestProvenance) throws -> Data + func sign(data: Data, with secret: SecretType, for provenance: SigningRequestProvenance) throws -> SignedData + + func persistAuthentication(secret: SecretType, forDuration duration: TimeInterval) throws } diff --git a/SecretKit/Common/Types/SignedData.swift b/SecretKit/Common/Types/SignedData.swift new file mode 100644 index 0000000..bcaf171 --- /dev/null +++ b/SecretKit/Common/Types/SignedData.swift @@ -0,0 +1,13 @@ +import Foundation + +public struct SignedData { + + public let data: Data + public let requiredAuthentication: Bool + + public init(data: Data, requiredAuthentication: Bool) { + self.data = data + self.requiredAuthentication = requiredAuthentication + } + +} diff --git a/SecretKit/SecureEnclave/SecureEnclaveStore.swift b/SecretKit/SecureEnclave/SecureEnclaveStore.swift index 2e42783..2a18a83 100644 --- a/SecretKit/SecureEnclave/SecureEnclaveStore.swift +++ b/SecretKit/SecureEnclave/SecureEnclaveStore.swift @@ -17,6 +17,8 @@ extension SecureEnclave { public let name = NSLocalizedString("Secure Enclave", comment: "Secure Enclave") @Published public private(set) var secrets: [Secret] = [] + private var persistedAuthenticationContexts: [Secret: PersistentAuthenticationContext] = [:] + public init() { DistributedNotificationCenter.default().addObserver(forName: .secretStoreUpdated, object: nil, queue: .main) { _ in self.reloadSecrets(notify: false) @@ -93,10 +95,16 @@ extension SecureEnclave { reloadSecrets() } - public func sign(data: Data, with secret: SecretType, for provenance: SigningRequestProvenance) throws -> Data { - let context = LAContext() + public func sign(data: Data, with secret: SecretType, for provenance: SigningRequestProvenance) throws -> SignedData { + let context: LAContext + if let existing = persistedAuthenticationContexts[secret], existing.valid { + context = existing.context + } else { + let newContext = LAContext() + newContext.localizedCancelTitle = "Deny" + context = newContext + } context.localizedReason = "sign a request from \"\(provenance.origin.displayName)\" using secret \"\(secret.name)\"" - context.localizedCancelTitle = "Deny" let attributes = [ kSecClass: kSecClassKey, kSecAttrKeyClass: kSecAttrKeyClassPrivate, @@ -117,10 +125,37 @@ extension SecureEnclave { } let key = untypedSafe as! SecKey var signError: SecurityError? + + let signingStartTime = Date() guard let signature = SecKeyCreateSignature(key, .ecdsaSignatureMessageX962SHA256, data as CFData, &signError) else { throw SigningError(error: signError) } - return signature as Data + let signatureDuration = Date().timeIntervalSince(signingStartTime) + // Hack to determine if the user had to authenticate to sign. + // Since there's now way to inspect SecAccessControl to determine (afaict). + let requiredAuthentication = signatureDuration > Constants.unauthenticatedThreshold + + return SignedData(data: signature as Data, requiredAuthentication: requiredAuthentication) + } + + public func persistAuthentication(secret: Secret, forDuration duration: TimeInterval) throws { + let newContext = LAContext() + newContext.localizedCancelTitle = "Deny" + + let formatter = DateComponentsFormatter() + formatter.unitsStyle = .spellOut + formatter.allowedUnits = [.hour, .minute, .day] + + if let durationString = formatter.string(from: duration) { + newContext.localizedReason = "unlock secret \"\(secret.name)\" for \(durationString)" + } else { + newContext.localizedReason = "unlock secret \"\(secret.name)\"" + } + newContext.evaluatePolicy(LAPolicy.deviceOwnerAuthentication, localizedReason: newContext.localizedReason) { [weak self] success, _ in + guard success else { return } + let context = PersistentAuthenticationContext(secret: secret, context: newContext, duration: duration) + self?.persistedAuthenticationContexts[secret] = context + } } } @@ -177,6 +212,7 @@ extension SecureEnclave.Store { throw SecureEnclave.KeychainError(statusCode: status) } } + } extension SecureEnclave { @@ -202,6 +238,30 @@ extension SecureEnclave { enum Constants { static let keyTag = "com.maxgoedjen.secretive.secureenclave.key".data(using: .utf8)! as CFData static let keyType = kSecAttrKeyTypeECSECPrimeRandom + static let unauthenticatedThreshold: TimeInterval = 0.05 + } + +} + +extension SecureEnclave { + + private struct PersistentAuthenticationContext { + + let secret: Secret + let context: LAContext + // Monotonic time instead of Date() to prevent people setting the clock back. + let expiration: UInt64 + + init(secret: Secret, context: LAContext, duration: TimeInterval) { + self.secret = secret + self.context = context + let durationInNanoSeconds = Measurement(value: duration, unit: UnitDuration.seconds).converted(to: .nanoseconds).value + self.expiration = clock_gettime_nsec_np(CLOCK_MONOTONIC) + UInt64(durationInNanoSeconds) + } + + var valid: Bool { + clock_gettime_nsec_np(CLOCK_MONOTONIC) < expiration + } } } diff --git a/SecretKit/SmartCard/SmartCardStore.swift b/SecretKit/SmartCard/SmartCardStore.swift index e56c862..978ea27 100644 --- a/SecretKit/SmartCard/SmartCardStore.swift +++ b/SecretKit/SmartCard/SmartCardStore.swift @@ -43,7 +43,7 @@ extension SmartCard { fatalError("Keys must be deleted on the smart card.") } - public func sign(data: Data, with secret: SecretType, for provenance: SigningRequestProvenance) throws -> Data { + public func sign(data: Data, with secret: SecretType, for provenance: SigningRequestProvenance) throws -> SignedData { guard let tokenID = tokenID else { fatalError() } let context = LAContext() context.localizedReason = "sign a request from \"\(provenance.origin.displayName)\" using secret \"\(secret.name)\"" @@ -78,7 +78,10 @@ extension SmartCard { guard let signature = SecKeyCreateSignature(key, signatureAlgorithm, data as CFData, &signError) else { throw SigningError(error: signError) } - return signature as Data + return SignedData(data: signature as Data, requiredAuthentication: false) + } + + public func persistAuthentication(secret: SmartCard.Secret, forDuration: TimeInterval) throws { } } diff --git a/Secretive.xcodeproj/project.pbxproj b/Secretive.xcodeproj/project.pbxproj index c458f90..630f5c5 100644 --- a/Secretive.xcodeproj/project.pbxproj +++ b/Secretive.xcodeproj/project.pbxproj @@ -13,6 +13,7 @@ 50153E22250DECA300525160 /* SecretListItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50153E21250DECA300525160 /* SecretListItemView.swift */; }; 5018F54F24064786002EB505 /* Notifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5018F54E24064786002EB505 /* Notifier.swift */; }; 501B7AE1251C56F700776EC7 /* SigningRequestProvenance.swift in Sources */ = {isa = PBXBuildFile; fileRef = 507CE4F32420A8C10029F750 /* SigningRequestProvenance.swift */; }; + 5035FF6E2737A2F4006FE1F6 /* SignedData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5035FF6D2737A2F4006FE1F6 /* SignedData.swift */; }; 50524B442420969E008DBD97 /* OpenSSHWriterTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50524B432420969D008DBD97 /* OpenSSHWriterTests.swift */; }; 50571E0324393C2600F76F6C /* JustUpdatedChecker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50571E0224393C2600F76F6C /* JustUpdatedChecker.swift */; }; 50571E0524393D1500F76F6C /* LaunchAgentController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50571E0424393D1500F76F6C /* LaunchAgentController.swift */; }; @@ -228,6 +229,7 @@ 50153E1F250AFCB200525160 /* UpdateView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UpdateView.swift; sourceTree = ""; }; 50153E21250DECA300525160 /* SecretListItemView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SecretListItemView.swift; sourceTree = ""; }; 5018F54E24064786002EB505 /* Notifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Notifier.swift; sourceTree = ""; }; + 5035FF6D2737A2F4006FE1F6 /* SignedData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SignedData.swift; sourceTree = ""; }; 50524B432420969D008DBD97 /* OpenSSHWriterTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OpenSSHWriterTests.swift; sourceTree = ""; }; 50571E0224393C2600F76F6C /* JustUpdatedChecker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JustUpdatedChecker.swift; sourceTree = ""; }; 50571E0424393D1500F76F6C /* LaunchAgentController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LaunchAgentController.swift; sourceTree = ""; }; @@ -394,6 +396,7 @@ 507CE4F32420A8C10029F750 /* SigningRequestProvenance.swift */, 50617DCA23FCECA10099B055 /* Secret.swift */, 50617DC623FCE4EA0099B055 /* SecretStore.swift */, + 5035FF6D2737A2F4006FE1F6 /* SignedData.swift */, ); path = Types; sourceTree = ""; @@ -1059,6 +1062,7 @@ 5099A02923FE35240062B6F2 /* SmartCardStore.swift in Sources */, 5099A02B23FE352C0062B6F2 /* SmartCardSecret.swift in Sources */, 50C385A3240789E600AF2719 /* OpenSSHReader.swift in Sources */, + 5035FF6E2737A2F4006FE1F6 /* SignedData.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/Secretive/Preview Content/PreviewStore.swift b/Secretive/Preview Content/PreviewStore.swift index dc5e6cb..4d4fbdb 100644 --- a/Secretive/Preview Content/PreviewStore.swift +++ b/Secretive/Preview Content/PreviewStore.swift @@ -35,8 +35,11 @@ extension Preview { self.secrets.append(contentsOf: new) } - func sign(data: Data, with secret: Preview.Secret, for provenance: SigningRequestProvenance) throws -> Data { - return data + func sign(data: Data, with secret: Preview.Secret, for provenance: SigningRequestProvenance) throws -> SignedData { + return SignedData(data: data, requiredAuthentication: false) + } + + func persistAuthentication(secret: Preview.Secret, forDuration duration: TimeInterval) throws { } }