diff --git a/.github/readme/apple_watch_auth_mac.png b/.github/readme/apple_watch_auth_mac.png index 6bd1e06..7038307 100644 Binary files a/.github/readme/apple_watch_auth_mac.png and b/.github/readme/apple_watch_auth_mac.png differ diff --git a/SecretAgentKit/Agent.swift b/SecretAgentKit/Agent.swift index 879ca92..02727c3 100644 --- a/SecretAgentKit/Agent.swift +++ b/SecretAgentKit/Agent.swift @@ -93,7 +93,7 @@ extension Agent { } let dataToSign = reader.readNextChunk() - let derSignature = try store.sign(data: dataToSign, with: secret) + let derSignature = try store.sign(data: dataToSign, with: secret, for: provenance) let curveData = writer.curveType(for: secret.algorithm, length: secret.keySize).data(using: .utf8)! diff --git a/SecretAgentKit/SigningRequestTracer.swift b/SecretAgentKit/SigningRequestTracer.swift index 8c6288d..04bbfb8 100644 --- a/SecretAgentKit/SigningRequestTracer.swift +++ b/SecretAgentKit/SigningRequestTracer.swift @@ -1,6 +1,7 @@ import Foundation import AppKit import Security +import SecretKit struct SigningRequestTracer { diff --git a/SecretAgentKitTests/StubStore.swift b/SecretAgentKitTests/StubStore.swift index a226796..c96092c 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) throws -> Data { + public func sign(data: Data, with secret: Secret, for provenance: SigningRequestProvenance) throws -> Data { guard !shouldThrow else { throw NSError() } diff --git a/SecretKit/Common/Erasers/AnySecretStore.swift b/SecretKit/Common/Erasers/AnySecretStore.swift index 12b1ea0..99f88cb 100644 --- a/SecretKit/Common/Erasers/AnySecretStore.swift +++ b/SecretKit/Common/Erasers/AnySecretStore.swift @@ -8,7 +8,7 @@ public class AnySecretStore: SecretStore { private let _id: () -> UUID private let _name: () -> String private let _secrets: () -> [AnySecret] - private let _sign: (Data, AnySecret) throws -> Data + private let _sign: (Data, AnySecret, SigningRequestProvenance) throws -> Data private var sink: AnyCancellable? public init(_ secretStore: SecretStoreType) where SecretStoreType: SecretStore { @@ -17,7 +17,7 @@ public class AnySecretStore: SecretStore { _name = { secretStore.name } _id = { secretStore.id } _secrets = { secretStore.secrets.map { AnySecret($0) } } - _sign = { try secretStore.sign(data: $0, with: $1.base as! SecretStoreType.SecretType) } + _sign = { try secretStore.sign(data: $0, with: $1.base as! SecretStoreType.SecretType, for: $2) } sink = secretStore.objectWillChange.sink { _ in self.objectWillChange.send() } @@ -39,8 +39,8 @@ public class AnySecretStore: SecretStore { return _secrets() } - public func sign(data: Data, with secret: AnySecret) throws -> Data { - try _sign(data, secret) + public func sign(data: Data, with secret: AnySecret, for provenance: SigningRequestProvenance) throws -> Data { + try _sign(data, secret, provenance) } } diff --git a/SecretKit/Common/Types/SecretStore.swift b/SecretKit/Common/Types/SecretStore.swift index ef0488d..2ab51aa 100644 --- a/SecretKit/Common/Types/SecretStore.swift +++ b/SecretKit/Common/Types/SecretStore.swift @@ -9,7 +9,7 @@ public protocol SecretStore: ObservableObject, Identifiable { var name: String { get } var secrets: [SecretType] { get } - func sign(data: Data, with secret: SecretType) throws -> Data + func sign(data: Data, with secret: SecretType, for provenance: SigningRequestProvenance) throws -> Data } diff --git a/SecretAgentKit/SigningRequestProvenance.swift b/SecretKit/Common/Types/SigningRequestProvenance.swift similarity index 79% rename from SecretAgentKit/SigningRequestProvenance.swift rename to SecretKit/Common/Types/SigningRequestProvenance.swift index b112ae5..c6a3c99 100644 --- a/SecretAgentKit/SigningRequestProvenance.swift +++ b/SecretKit/Common/Types/SigningRequestProvenance.swift @@ -17,7 +17,7 @@ extension SigningRequestProvenance { } public var intact: Bool { - return chain.reduce(true) { $0 && $1.validSignature } + chain.allSatisfy { $0.validSignature } } } @@ -30,9 +30,9 @@ extension SigningRequestProvenance { public let name: String public let path: String public let validSignature: Bool - let parentPID: Int32? + public let parentPID: Int32? - init(pid: Int32, name: String, path: String, validSignature: Bool, parentPID: Int32?) { + public init(pid: Int32, name: String, path: String, validSignature: Bool, parentPID: Int32?) { self.pid = pid self.name = name self.path = path diff --git a/SecretKit/SecureEnclave/SecureEnclaveStore.swift b/SecretKit/SecureEnclave/SecureEnclaveStore.swift index a0e00a2..9bc2ec3 100644 --- a/SecretKit/SecureEnclave/SecureEnclaveStore.swift +++ b/SecretKit/SecureEnclave/SecureEnclaveStore.swift @@ -1,6 +1,7 @@ import Foundation import Security import CryptoTokenKit +import LocalAuthentication extension SecureEnclave { @@ -75,7 +76,10 @@ extension SecureEnclave { reloadSecrets() } - public func sign(data: Data, with secret: SecretType) throws -> Data { + public func sign(data: Data, with secret: SecretType, for provenance: SigningRequestProvenance) throws -> Data { + let context = LAContext() + context.localizedReason = "sign a request from \"\(provenance.origin.name)\" using secret \"\(secret.name)\"" + context.localizedCancelTitle = "Deny" let attributes = [ kSecClass: kSecClassKey, kSecAttrKeyClass: kSecAttrKeyClassPrivate, @@ -83,6 +87,7 @@ extension SecureEnclave { kSecAttrKeyType: Constants.keyType, kSecAttrTokenID: kSecAttrTokenIDSecureEnclave, kSecAttrApplicationTag: Constants.keyTag, + kSecUseAuthenticationContext: context, kSecReturnRef: true ] as CFDictionary var untyped: CFTypeRef? diff --git a/SecretKit/SmartCard/SmartCardStore.swift b/SecretKit/SmartCard/SmartCardStore.swift index 2fdbe4d..133447f 100644 --- a/SecretKit/SmartCard/SmartCardStore.swift +++ b/SecretKit/SmartCard/SmartCardStore.swift @@ -1,6 +1,7 @@ import Foundation import Security import CryptoTokenKit +import LocalAuthentication // TODO: Might need to split this up into "sub-stores?" // ie, each token has its own Store. @@ -43,13 +44,17 @@ extension SmartCard { fatalError("Keys must be deleted on the smart card.") } - public func sign(data: Data, with secret: SecretType) throws -> Data { + public func sign(data: Data, with secret: SecretType, for provenance: SigningRequestProvenance) throws -> Data { guard let tokenID = tokenID else { fatalError() } + let context = LAContext() + context.localizedReason = "sign a request from \"\(provenance.origin.name)\" using secret \"\(secret.name)\"" + context.localizedCancelTitle = "Deny" let attributes = [ kSecClass: kSecClassKey, kSecAttrKeyClass: kSecAttrKeyClassPrivate, kSecAttrApplicationLabel: secret.id as CFData, kSecAttrTokenID: tokenID, + kSecUseAuthenticationContext: context, kSecReturnRef: true ] as CFDictionary var untyped: CFTypeRef? diff --git a/Secretive.xcodeproj/project.pbxproj b/Secretive.xcodeproj/project.pbxproj index 4da54fc..21bb736 100644 --- a/Secretive.xcodeproj/project.pbxproj +++ b/Secretive.xcodeproj/project.pbxproj @@ -11,6 +11,7 @@ 50153E20250AFCB200525160 /* UpdateView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50153E1F250AFCB200525160 /* UpdateView.swift */; }; 50153E22250DECA300525160 /* SecretListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50153E21250DECA300525160 /* SecretListView.swift */; }; 5018F54F24064786002EB505 /* Notifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5018F54E24064786002EB505 /* Notifier.swift */; }; + 501B7AE1251C56F700776EC7 /* SigningRequestProvenance.swift in Sources */ = {isa = PBXBuildFile; fileRef = 507CE4F32420A8C10029F750 /* SigningRequestProvenance.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 */; }; @@ -49,7 +50,6 @@ 507CE4ED2420A3C70029F750 /* Agent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50A3B79F24026B9900D209EA /* Agent.swift */; }; 507CE4EE2420A3CA0029F750 /* SocketController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50A3B79D24026B9900D209EA /* SocketController.swift */; }; 507CE4F02420A4C50029F750 /* SigningWitness.swift in Sources */ = {isa = PBXBuildFile; fileRef = 507CE4EF2420A4C50029F750 /* SigningWitness.swift */; }; - 507CE4F42420A8C10029F750 /* SigningRequestProvenance.swift in Sources */ = {isa = PBXBuildFile; fileRef = 507CE4F32420A8C10029F750 /* SigningRequestProvenance.swift */; }; 507CE4F62420A96F0029F750 /* SigningRequestTracer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 507CE4F52420A96F0029F750 /* SigningRequestTracer.swift */; }; 507EE34224281E12003C4FE3 /* FileHandleProtocols.swift in Sources */ = {isa = PBXBuildFile; fileRef = 507EE34124281E12003C4FE3 /* FileHandleProtocols.swift */; }; 507EE34624281F89003C4FE3 /* StubFileHandleReader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 507EE34524281F89003C4FE3 /* StubFileHandleReader.swift */; }; @@ -381,6 +381,7 @@ 504BA92D243171F20064740E /* Types */ = { isa = PBXGroup; children = ( + 507CE4F32420A8C10029F750 /* SigningRequestProvenance.swift */, 50617DCA23FCECA10099B055 /* Secret.swift */, 50617DC623FCE4EA0099B055 /* SecretStore.swift */, ); @@ -593,7 +594,6 @@ 5099A089240242C20062B6F2 /* SSHAgentProtocol.swift */, 50A3B79D24026B9900D209EA /* SocketController.swift */, 507CE4EF2420A4C50029F750 /* SigningWitness.swift */, - 507CE4F32420A8C10029F750 /* SigningRequestProvenance.swift */, 507CE4F52420A96F0029F750 /* SigningRequestTracer.swift */, 50A3B79F24026B9900D209EA /* Agent.swift */, 507EE34124281E12003C4FE3 /* FileHandleProtocols.swift */, @@ -1026,6 +1026,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 501B7AE1251C56F700776EC7 /* SigningRequestProvenance.swift in Sources */, 50617DC723FCE4EA0099B055 /* SecretStore.swift in Sources */, 5099A02723FE34FA0062B6F2 /* SmartCard.swift in Sources */, 50617DCB23FCECA10099B055 /* Secret.swift in Sources */, @@ -1078,7 +1079,6 @@ 507CE4ED2420A3C70029F750 /* Agent.swift in Sources */, 507CE4F02420A4C50029F750 /* SigningWitness.swift in Sources */, 507CE4F62420A96F0029F750 /* SigningRequestTracer.swift in Sources */, - 507CE4F42420A8C10029F750 /* SigningRequestProvenance.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/Secretive/Preview Content/PreviewStore.swift b/Secretive/Preview Content/PreviewStore.swift index 892eaaa..40b79b9 100644 --- a/Secretive/Preview Content/PreviewStore.swift +++ b/Secretive/Preview Content/PreviewStore.swift @@ -35,7 +35,7 @@ extension Preview { self.secrets.append(contentsOf: new) } - func sign(data: Data, with secret: Preview.Secret) throws -> Data { + func sign(data: Data, with secret: Preview.Secret, for provenance: SigningRequestProvenance) throws -> Data { return data }