From 1f7ebcfe756ef7ec5b9ecc0b8cf2db4fd33740cb Mon Sep 17 00:00:00 2001 From: Max Goedjen Date: Mon, 23 Mar 2020 21:19:40 -0700 Subject: [PATCH 01/20] MIT notice on brief --- Brief/Info.plist | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Brief/Info.plist b/Brief/Info.plist index d5ff04c..48059f2 100644 --- a/Brief/Info.plist +++ b/Brief/Info.plist @@ -19,6 +19,6 @@ CFBundleVersion $(CURRENT_PROJECT_VERSION) NSHumanReadableCopyright - Copyright © 2020 Max Goedjen. All rights reserved. + $(PRODUCT_NAME) is MIT Licensed. From 4dcc9b113d8ad3fae530dddc25e0c20ba2bfca11 Mon Sep 17 00:00:00 2001 From: Max Goedjen Date: Mon, 23 Mar 2020 23:22:22 -0700 Subject: [PATCH 02/20] Agent Tests (#74) --- SecretAgent/AppDelegate.swift | 2 +- SecretAgentKit/Agent.swift | 30 ++-- SecretAgentKit/FileHandleProtocols.swift | 26 +++ SecretAgentKit/SSHAgentProtocol.swift | 2 +- SecretAgentKit/SigningRequestProvenance.swift | 4 +- SecretAgentKit/SigningRequestTracer.swift | 8 +- SecretAgentKit/SocketController.swift | 6 +- SecretAgentKitTests/AgentTests.swift | 162 ++++++++++++++++++ SecretAgentKitTests/SecretAgentKitTests.swift | 11 -- .../StubFileHandleReader.swift | 14 ++ .../StubFileHandleWriter.swift | 11 ++ SecretAgentKitTests/StubStore.swift | 113 ++++++++++++ SecretAgentKitTests/StubWitness.swift | 32 ++++ SecretKit/Secret.swift | 2 +- Secretive.xcodeproj/project.pbxproj | 39 ++++- 15 files changed, 418 insertions(+), 44 deletions(-) create mode 100644 SecretAgentKit/FileHandleProtocols.swift create mode 100644 SecretAgentKitTests/AgentTests.swift delete mode 100644 SecretAgentKitTests/SecretAgentKitTests.swift create mode 100644 SecretAgentKitTests/StubFileHandleReader.swift create mode 100644 SecretAgentKitTests/StubFileHandleWriter.swift create mode 100644 SecretAgentKitTests/StubStore.swift create mode 100644 SecretAgentKitTests/StubWitness.swift diff --git a/SecretAgent/AppDelegate.swift b/SecretAgent/AppDelegate.swift index e1cfe7c..0f0b0a2 100644 --- a/SecretAgent/AppDelegate.swift +++ b/SecretAgent/AppDelegate.swift @@ -28,7 +28,7 @@ class AppDelegate: NSObject, NSApplicationDelegate { func applicationDidFinishLaunching(_ aNotification: Notification) { os_log(.debug, "SecretAgent finished launching") DispatchQueue.main.async { - self.socketController.handler = self.agent.handle(fileHandle:) + self.socketController.handler = self.agent.handle(reader:writer:) } notifier.prompt() updateSink = updater.$update.sink { update in diff --git a/SecretAgentKit/Agent.swift b/SecretAgentKit/Agent.swift index 25185ca..f10f882 100644 --- a/SecretAgentKit/Agent.swift +++ b/SecretAgentKit/Agent.swift @@ -21,28 +21,34 @@ public class Agent { extension Agent { - public func handle(fileHandle: FileHandle) { + public func handle(reader: FileHandleReader, writer: FileHandleWriter) { os_log(.debug, "Agent handling new data") - let data = fileHandle.availableData + let data = reader.availableData guard !data.isEmpty else { return } let requestTypeInt = data[4] - guard let requestType = SSHAgent.RequestType(rawValue: requestTypeInt) else { return } + guard let requestType = SSHAgent.RequestType(rawValue: requestTypeInt) else { + writer.write(OpenSSHKeyWriter().lengthAndData(of: SSHAgent.ResponseType.agentFailure.data)) + os_log(.debug, "Agent returned %@", SSHAgent.ResponseType.agentFailure.debugDescription) + return + } os_log(.debug, "Agent handling request of type %@", requestType.debugDescription) let subData = Data(data[5...]) - handle(requestType: requestType, data: subData, fileHandle: fileHandle) + let response = handle(requestType: requestType, data: subData, reader: reader) + writer.write(response) } - func handle(requestType: SSHAgent.RequestType, data: Data, fileHandle: FileHandle) { + func handle(requestType: SSHAgent.RequestType, data: Data, reader: FileHandleReader) -> Data { var response = Data() do { switch requestType { case .requestIdentities: response.append(SSHAgent.ResponseType.agentIdentitiesAnswer.data) - response.append(try identities()) + response.append(identities()) os_log(.debug, "Agent returned %@", SSHAgent.ResponseType.agentIdentitiesAnswer.debugDescription) case .signRequest: + let provenance = requestTracer.provenance(from: reader) response.append(SSHAgent.ResponseType.agentSignResponse.data) - response.append(try sign(data: data, from: fileHandle.fileDescriptor)) + response.append(try sign(data: data, provenance: provenance)) os_log(.debug, "Agent returned %@", SSHAgent.ResponseType.agentSignResponse.debugDescription) } } catch { @@ -51,14 +57,14 @@ extension Agent { os_log(.debug, "Agent returned %@", SSHAgent.ResponseType.agentFailure.debugDescription) } let full = OpenSSHKeyWriter().lengthAndData(of: response) - fileHandle.write(full) + return full } } extension Agent { - func identities() throws -> Data { + func identities() -> Data { // TODO: RESTORE ONCE XCODE 11.4 IS GM let secrets = storeList.stores.flatMap { $0.secrets } // let secrets = storeList.stores.flatMap(\.secrets) @@ -76,7 +82,7 @@ extension Agent { return countData + keyData } - func sign(data: Data, from pid: Int32) throws -> Data { + func sign(data: Data, provenance: SigningRequestProvenance) throws -> Data { let reader = OpenSSHReader(data: data) let hash = reader.readNextChunk() guard let (store, secret) = secret(matching: hash) else { @@ -84,7 +90,6 @@ extension Agent { throw AgentError.noMatchingKey } - let provenance = requestTracer.provenance(from: pid) if let witness = witness { try witness.speakNowOrForeverHoldYourPeace(forAccessTo: secret, by: provenance) } @@ -103,7 +108,7 @@ extension Agent { case (.ellipticCurve, 384): rawRepresentation = try CryptoKit.P384.Signing.ECDSASignature(derRepresentation: derSignature).rawRepresentation default: - fatalError() + throw AgentError.unsupportedKeyType } @@ -154,6 +159,7 @@ extension Agent { enum AgentError: Error { case unhandledType case noMatchingKey + case unsupportedKeyType } } diff --git a/SecretAgentKit/FileHandleProtocols.swift b/SecretAgentKit/FileHandleProtocols.swift new file mode 100644 index 0000000..ba806ff --- /dev/null +++ b/SecretAgentKit/FileHandleProtocols.swift @@ -0,0 +1,26 @@ +import Foundation + +public protocol FileHandleReader { + + var availableData: Data { get } + var fileDescriptor: Int32 { get } + var pidOfConnectedProcess: Int32 { get } + +} + +public protocol FileHandleWriter { + + func write(_ data: Data) + +} + +extension FileHandle: FileHandleReader, FileHandleWriter { + + public var pidOfConnectedProcess: Int32 { + let pidPointer = UnsafeMutableRawPointer.allocate(byteCount: 4, alignment: 1) + var len = socklen_t(MemoryLayout.size) + getsockopt(fileDescriptor, SOCK_STREAM, LOCAL_PEERPID, pidPointer, &len) + return pidPointer.load(as: Int32.self) + } + +} diff --git a/SecretAgentKit/SSHAgentProtocol.swift b/SecretAgentKit/SSHAgentProtocol.swift index 6164875..3de8ef5 100644 --- a/SecretAgentKit/SSHAgentProtocol.swift +++ b/SecretAgentKit/SSHAgentProtocol.swift @@ -12,7 +12,7 @@ extension SSHAgent { switch self { case .requestIdentities: return "RequestIdentities" - default: + case .signRequest: return "SignRequest" } } diff --git a/SecretAgentKit/SigningRequestProvenance.swift b/SecretAgentKit/SigningRequestProvenance.swift index f06a1fa..b112ae5 100644 --- a/SecretAgentKit/SigningRequestProvenance.swift +++ b/SecretAgentKit/SigningRequestProvenance.swift @@ -1,7 +1,7 @@ import Foundation import AppKit -public struct SigningRequestProvenance { +public struct SigningRequestProvenance: Equatable { public var chain: [Process] public init(root: Process) { @@ -24,7 +24,7 @@ extension SigningRequestProvenance { extension SigningRequestProvenance { - public struct Process { + public struct Process: Equatable { public let pid: Int32 public let name: String diff --git a/SecretAgentKit/SigningRequestTracer.swift b/SecretAgentKit/SigningRequestTracer.swift index 625ad5e..8c6288d 100644 --- a/SecretAgentKit/SigningRequestTracer.swift +++ b/SecretAgentKit/SigningRequestTracer.swift @@ -4,12 +4,8 @@ import Security struct SigningRequestTracer { - func provenance(from pid: Int32) -> SigningRequestProvenance { - let pidPointer = UnsafeMutableRawPointer.allocate(byteCount: 4, alignment: 1) - var len = socklen_t(MemoryLayout.size) - getsockopt(pid, SOCK_STREAM, LOCAL_PEERPID, pidPointer, &len) - let pid = pidPointer.load(as: Int32.self) - let firstInfo = process(from: pid) + func provenance(from fileHandleReader: FileHandleReader) -> SigningRequestProvenance { + let firstInfo = process(from: fileHandleReader.pidOfConnectedProcess) var provenance = SigningRequestProvenance(root: firstInfo) while NSRunningApplication(processIdentifier: provenance.origin.pid) == nil && provenance.origin.parentPID != nil { diff --git a/SecretAgentKit/SocketController.swift b/SecretAgentKit/SocketController.swift index ea82b16..1fc51d4 100644 --- a/SecretAgentKit/SocketController.swift +++ b/SecretAgentKit/SocketController.swift @@ -5,7 +5,7 @@ public class SocketController { fileprivate var fileHandle: FileHandle? fileprivate var port: SocketPort? - public var handler: ((FileHandle) -> Void)? + public var handler: ((FileHandleReader, FileHandleWriter) -> Void)? public init(path: String) { os_log(.debug, "Socket controller setting up at %@", path) @@ -52,7 +52,7 @@ public class SocketController { @objc func handleConnectionAccept(notification: Notification) { os_log(.debug, "Socket controller accepted connection") guard let new = notification.userInfo?[NSFileHandleNotificationFileHandleItem] as? FileHandle else { return } - handler?(new) + handler?(new, new) new.waitForDataInBackgroundAndNotify() fileHandle?.acceptConnectionInBackgroundAndNotify(forModes: [RunLoop.current.currentMode!]) } @@ -61,7 +61,7 @@ public class SocketController { os_log(.debug, "Socket controller has new data available") guard let new = notification.object as? FileHandle else { return } os_log(.debug, "Socket controller received new file handle") - handler?(new) + handler?(new, new) } } diff --git a/SecretAgentKitTests/AgentTests.swift b/SecretAgentKitTests/AgentTests.swift new file mode 100644 index 0000000..c5c7bfd --- /dev/null +++ b/SecretAgentKitTests/AgentTests.swift @@ -0,0 +1,162 @@ +import Foundation +import XCTest +import CryptoKit +@testable import SecretKit +@testable import SecretAgentKit + +class AgentTests: XCTestCase { + + let stubWriter = StubFileHandleWriter() + + // MARK: Identity Listing + + func testEmptyStores() { + let stubReader = StubFileHandleReader(availableData: Constants.Requests.requestIdentities) + let agent = Agent(storeList: SecretStoreList()) + agent.handle(reader: stubReader, writer: stubWriter) + XCTAssertEqual(stubWriter.data, Constants.Responses.requestIdentitiesEmpty) + } + + func testIdentitiesList() { + let stubReader = StubFileHandleReader(availableData: Constants.Requests.requestIdentities) + let list = storeList(with: [Constants.Secrets.ecdsa256Secret, Constants.Secrets.ecdsa384Secret]) + let agent = Agent(storeList: list) + agent.handle(reader: stubReader, writer: stubWriter) + XCTAssertEqual(stubWriter.data, Constants.Responses.requestIdentitiesMultiple) + } + + // MARK: Signatures + + func testNoMatchingIdentities() { + let stubReader = StubFileHandleReader(availableData: Constants.Requests.requestSignatureWithNoneMatching) + let list = storeList(with: [Constants.Secrets.ecdsa256Secret, Constants.Secrets.ecdsa384Secret]) + let agent = Agent(storeList: list) + agent.handle(reader: stubReader, writer: stubWriter) +// XCTAssertEqual(stubWriter.data, Constants.Responses.requestFailure) + } + + func testSignature() { + let stubReader = StubFileHandleReader(availableData: Constants.Requests.requestSignature) + let requestReader = OpenSSHReader(data: Constants.Requests.requestSignature[5...]) + _ = requestReader.readNextChunk() + let dataToSign = requestReader.readNextChunk() + let list = storeList(with: [Constants.Secrets.ecdsa256Secret, Constants.Secrets.ecdsa384Secret]) + let agent = Agent(storeList: list) + agent.handle(reader: stubReader, writer: stubWriter) + let outer = OpenSSHReader(data: stubWriter.data[5...]) + let payload = outer.readNextChunk() + let inner = OpenSSHReader(data: payload) + _ = inner.readNextChunk() + let signedData = inner.readNextChunk() + let rsData = OpenSSHReader(data: signedData) + let r = rsData.readNextChunk() + let s = rsData.readNextChunk() + var rs = r + rs.append(s) + let signature = try! P256.Signing.ECDSASignature(rawRepresentation: rs) + let valid = try! P256.Signing.PublicKey(x963Representation: Constants.Secrets.ecdsa256Secret.publicKey).isValidSignature(signature, for: dataToSign) + XCTAssertTrue(valid) + } + + // MARK: Witness protocol + + func testWitnessObjectionStopsRequest() { + let stubReader = StubFileHandleReader(availableData: Constants.Requests.requestSignature) + let list = storeList(with: [Constants.Secrets.ecdsa256Secret]) + let witness = StubWitness(speakNow: { _,_ in + return true + }, witness: { _, _ in }) + let agent = Agent(storeList: list, witness: witness) + agent.handle(reader: stubReader, writer: stubWriter) + XCTAssertEqual(stubWriter.data, Constants.Responses.requestFailure) + } + + func testWitnessSignature() { + let stubReader = StubFileHandleReader(availableData: Constants.Requests.requestSignature) + let list = storeList(with: [Constants.Secrets.ecdsa256Secret]) + var witnessed = false + let witness = StubWitness(speakNow: { _, trace in + return false + }, witness: { _, trace in + witnessed = true + }) + let agent = Agent(storeList: list, witness: witness) + agent.handle(reader: stubReader, writer: stubWriter) + XCTAssertTrue(witnessed) + } + + func testRequestTracing() { + let stubReader = StubFileHandleReader(availableData: Constants.Requests.requestSignature) + let list = storeList(with: [Constants.Secrets.ecdsa256Secret]) + var speakNowTrace: SigningRequestProvenance! = nil + var witnessTrace: SigningRequestProvenance! = nil + let witness = StubWitness(speakNow: { _, trace in + speakNowTrace = trace + return false + }, witness: { _, trace in + witnessTrace = trace + }) + let agent = Agent(storeList: list, witness: witness) + agent.handle(reader: stubReader, writer: stubWriter) + XCTAssertEqual(witnessTrace, speakNowTrace) + XCTAssertEqual(witnessTrace.origin.name, "Finder") + XCTAssertEqual(witnessTrace.origin.validSignature, true) + XCTAssertEqual(witnessTrace.origin.parentPID, 1) + } + + // MARK: Exception Handling + + func testSignatureException() { + let stubReader = StubFileHandleReader(availableData: Constants.Requests.requestSignature) + let list = storeList(with: [Constants.Secrets.ecdsa256Secret, Constants.Secrets.ecdsa384Secret]) + let store = list.stores.first?.base as! Stub.Store + store.shouldThrow = true + let agent = Agent(storeList: list) + agent.handle(reader: stubReader, writer: stubWriter) + XCTAssertEqual(stubWriter.data, Constants.Responses.requestFailure) + } + + // MARK: Unsupported + + func testUnhandledAdd() { + let stubReader = StubFileHandleReader(availableData: Constants.Requests.addIdentity) + let agent = Agent(storeList: SecretStoreList()) + agent.handle(reader: stubReader, writer: stubWriter) + XCTAssertEqual(stubWriter.data, Constants.Responses.requestFailure) + } + +} + +extension AgentTests { + + func storeList(with secrets: [Stub.Secret]) -> SecretStoreList { + let store = Stub.Store() + store.secrets.append(contentsOf: secrets) + let storeList = SecretStoreList() + storeList.add(store: store) + return storeList + } + + enum Constants { + + enum Requests { + static let requestIdentities = Data(base64Encoded: "AAAAAQs=")! + static let addIdentity = Data(base64Encoded: "AAAAARE=")! + static let requestSignatureWithNoneMatching = Data(base64Encoded: "AAABhA0AAACIAAAAE2VjZHNhLXNoYTItbmlzdHAzODQAAAAIbmlzdHAzODQAAABhBEqCbkJbOHy5S1wVCaJoKPmpS0egM4frMqllgnlRRQ/Uvnn6EVS8oV03cPA2Bz0EdESyRKA/sbmn0aBtgjIwGELxu45UXEW1TEz6TxyS0u3vuIqR3Wo1CrQWRDnkrG/pBQAAAO8AAAAgbqmrqPUtJ8mmrtaSVexjMYyXWNqjHSnoto7zgv86xvcyAAAAA2dpdAAAAA5zc2gtY29ubmVjdGlvbgAAAAlwdWJsaWNrZXkBAAAAE2VjZHNhLXNoYTItbmlzdHAzODQAAACIAAAAE2VjZHNhLXNoYTItbmlzdHAzODQAAAAIbmlzdHAzODQAAABhBEqCbkJbOHy5S1wVCaJoKPmpS0egM4frMqllgnlRRQ/Uvnn6EVS8oV03cPA2Bz0EdESyRKA/sbmn0aBtgjIwGELxu45UXEW1TEz6TxyS0u3vuIqR3Wo1CrQWRDnkrG/pBQAAAAA=")! + static let requestSignature = Data(base64Encoded: "AAABRA0AAABoAAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBKzOkUiVJEcACMtAd9X7xalbc0FYZyhbmv2dsWl4IP2GWIi+RcsaHQNw+nAIQ8CKEYmLnl0VLDp5Ef8KMhgIy08AAADPAAAAIBIFsbCZ4/dhBmLNGHm0GKj7EJ4N8k/jXRxlyg+LFIYzMgAAAANnaXQAAAAOc3NoLWNvbm5lY3Rpb24AAAAJcHVibGlja2V5AQAAABNlY2RzYS1zaGEyLW5pc3RwMjU2AAAAaAAAABNlY2RzYS1zaGEyLW5pc3RwMjU2AAAACG5pc3RwMjU2AAAAQQSszpFIlSRHAAjLQHfV+8WpW3NBWGcoW5r9nbFpeCD9hliIvkXLGh0DcPpwCEPAihGJi55dFSw6eRH/CjIYCMtPAAAAAA==")! + } + + enum Responses { + static let requestIdentitiesEmpty = Data(base64Encoded: "AAAABQwAAAAA")! + static let requestIdentitiesMultiple = Data(base64Encoded: "AAABKwwAAAACAAAAaAAAABNlY2RzYS1zaGEyLW5pc3RwMjU2AAAACG5pc3RwMjU2AAAAQQSszpFIlSRHAAjLQHfV+8WpW3NBWGcoW5r9nbFpeCD9hliIvkXLGh0DcPpwCEPAihGJi55dFSw6eRH/CjIYCMtPAAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAACIAAAAE2VjZHNhLXNoYTItbmlzdHAzODQAAAAIbmlzdHAzODQAAABhBLKSzA5q3jCb3q0JKigvcxfWVGrJ+bklpG0Zc9YzUwrbsh9SipvlSJi+sHQI+O0m88DOpRBAtuAHX60euD/Yv250tovN7/+MEFbXGZ/hLdd0BoFpWbLfJcQj806KJGlcDAAAABNlY2RzYS1zaGEyLW5pc3RwMzg0")! + static let requestFailure = Data(base64Encoded: "AAAAAQU=")! + } + + enum Secrets { + static let ecdsa256Secret = Stub.Secret(keySize: 256, publicKey: Data(base64Encoded: "BKzOkUiVJEcACMtAd9X7xalbc0FYZyhbmv2dsWl4IP2GWIi+RcsaHQNw+nAIQ8CKEYmLnl0VLDp5Ef8KMhgIy08=")!, privateKey: Data(base64Encoded: "BKzOkUiVJEcACMtAd9X7xalbc0FYZyhbmv2dsWl4IP2GWIi+RcsaHQNw+nAIQ8CKEYmLnl0VLDp5Ef8KMhgIy09nw780wy/TSfUmzj15iJkV234AaCLNl+H8qFL6qK8VIg==")!) + static let ecdsa384Secret = Stub.Secret(keySize: 384, publicKey: Data(base64Encoded: "BLKSzA5q3jCb3q0JKigvcxfWVGrJ+bklpG0Zc9YzUwrbsh9SipvlSJi+sHQI+O0m88DOpRBAtuAHX60euD/Yv250tovN7/+MEFbXGZ/hLdd0BoFpWbLfJcQj806KJGlcDA==")!, privateKey: Data(base64Encoded: "BLKSzA5q3jCb3q0JKigvcxfWVGrJ+bklpG0Zc9YzUwrbsh9SipvlSJi+sHQI+O0m88DOpRBAtuAHX60euD/Yv250tovN7/+MEFbXGZ/hLdd0BoFpWbLfJcQj806KJGlcDHNapAOzrt9E+9QC4/KYoXS7Uw4pmdAz53uIj02tttiq3c0ZyIQ7XoscWWRqRrz8Kw==")!) + } + + } + +} diff --git a/SecretAgentKitTests/SecretAgentKitTests.swift b/SecretAgentKitTests/SecretAgentKitTests.swift deleted file mode 100644 index 540c1fc..0000000 --- a/SecretAgentKitTests/SecretAgentKitTests.swift +++ /dev/null @@ -1,11 +0,0 @@ -import XCTest -@testable import SecretAgentKit - -class SecretAgentKitTests: XCTestCase { - - func testExample() throws { - // This is an example of a functional test case. - // Use XCTAssert and related functions to verify your tests produce the correct results. - } - -} diff --git a/SecretAgentKitTests/StubFileHandleReader.swift b/SecretAgentKitTests/StubFileHandleReader.swift new file mode 100644 index 0000000..a9bf274 --- /dev/null +++ b/SecretAgentKitTests/StubFileHandleReader.swift @@ -0,0 +1,14 @@ +import SecretAgentKit +import AppKit + +struct StubFileHandleReader: FileHandleReader { + + let availableData: Data + var fileDescriptor: Int32 { + NSWorkspace.shared.runningApplications.filter({ $0.localizedName == "Finder" }).first!.processIdentifier + } + var pidOfConnectedProcess: Int32 { + fileDescriptor + } + +} diff --git a/SecretAgentKitTests/StubFileHandleWriter.swift b/SecretAgentKitTests/StubFileHandleWriter.swift new file mode 100644 index 0000000..5b63eff --- /dev/null +++ b/SecretAgentKitTests/StubFileHandleWriter.swift @@ -0,0 +1,11 @@ +import SecretAgentKit + +class StubFileHandleWriter: FileHandleWriter { + + var data = Data() + + func write(_ data: Data) { + self.data.append(data) + } + +} diff --git a/SecretAgentKitTests/StubStore.swift b/SecretAgentKitTests/StubStore.swift new file mode 100644 index 0000000..a226796 --- /dev/null +++ b/SecretAgentKitTests/StubStore.swift @@ -0,0 +1,113 @@ +import SecretKit +import CryptoKit + +struct Stub {} + +extension Stub { + + public class Store: SecretStore { + + public let isAvailable = true + public let id = UUID() + public let name = "Stub" + public var secrets: [Secret] = [] + public var shouldThrow = false + + public init() { +// try! create(size: 256) +// try! create(size: 384) + } + + public func create(size: Int) throws { + let flags: SecAccessControlCreateFlags = [] + let access = + SecAccessControlCreateWithFlags(kCFAllocatorDefault, + kSecAttrAccessibleWhenUnlockedThisDeviceOnly, + flags, + nil) as Any + + let attributes = [ + kSecAttrLabel: name, + kSecAttrKeyType: kSecAttrKeyTypeECSECPrimeRandom, + kSecAttrKeySizeInBits: size, + kSecPrivateKeyAttrs: [ + kSecAttrIsPermanent: true, + kSecAttrAccessControl: access + ] + ] as CFDictionary + + var privateKey: SecKey! = nil + var publicKey: SecKey! = nil + SecKeyGeneratePair(attributes, &publicKey, &privateKey) + let publicAttributes = SecKeyCopyAttributes(publicKey) as! [CFString: Any] + let privateAttributes = SecKeyCopyAttributes(privateKey) as! [CFString: Any] + let publicData = (publicAttributes[kSecValueData] as! Data) + let privateData = (privateAttributes[kSecValueData] as! Data) + let secret = Secret(keySize: size, publicKey: publicData, privateKey: privateData) + print(secret) + print("Public Key OpenSSH: \(OpenSSHKeyWriter().openSSHString(secret: secret))") + } + + public func sign(data: Data, with secret: Secret) throws -> Data { + guard !shouldThrow else { + throw NSError() + } + let privateKey = SecKeyCreateWithData(secret.privateKey as CFData, [ + kSecAttrKeyType: kSecAttrKeyTypeECSECPrimeRandom, + kSecAttrKeySizeInBits: secret.keySize, + kSecAttrKeyClass: kSecAttrKeyClassPrivate + ] as CFDictionary + , nil)! + let signatureAlgorithm: SecKeyAlgorithm + switch secret.keySize { + case 256: + signatureAlgorithm = .ecdsaSignatureMessageX962SHA256 + case 384: + signatureAlgorithm = .ecdsaSignatureMessageX962SHA384 + default: + fatalError() + } + return SecKeyCreateSignature(privateKey, signatureAlgorithm, data as CFData, nil)! as Data + } + + } + +} + +extension Stub { + + struct Secret: SecretKit.Secret, CustomDebugStringConvertible { + + let id = UUID().uuidString.data(using: .utf8)! + let name = UUID().uuidString + let algorithm = Algorithm.ellipticCurve + + let keySize: Int + let publicKey: Data + let privateKey: Data + + init(keySize: Int, publicKey: Data, privateKey: Data) { + self.keySize = keySize + self.publicKey = publicKey + self.privateKey = privateKey + } + + var debugDescription: String { + """ + Key Size \(keySize) + Private: \(privateKey.base64EncodedString()) + Public: \(publicKey.base64EncodedString()) + """ + } + + } + +} + + +extension Stub.Store { + + struct StubError: Error { + } + +} diff --git a/SecretAgentKitTests/StubWitness.swift b/SecretAgentKitTests/StubWitness.swift new file mode 100644 index 0000000..230c009 --- /dev/null +++ b/SecretAgentKitTests/StubWitness.swift @@ -0,0 +1,32 @@ +import SecretKit +import SecretAgentKit + +struct StubWitness { + + let speakNow: (AnySecret, SigningRequestProvenance) -> Bool + let witness: (AnySecret, SigningRequestProvenance) -> () + +} + +extension StubWitness: SigningWitness { + + func speakNowOrForeverHoldYourPeace(forAccessTo secret: AnySecret, by provenance: SigningRequestProvenance) throws { + let objection = speakNow(secret, provenance) + if objection { + throw TheresMyChance() + } + } + + func witness(accessTo secret: AnySecret, by provenance: SigningRequestProvenance) throws { + witness(secret, provenance) + } + +} + +extension StubWitness { + + struct TheresMyChance: Error { + + } + +} diff --git a/SecretKit/Secret.swift b/SecretKit/Secret.swift index 7e54b4c..db71045 100644 --- a/SecretKit/Secret.swift +++ b/SecretKit/Secret.swift @@ -7,7 +7,7 @@ public protocol Secret: Identifiable, Hashable { } -public enum Algorithm { +public enum Algorithm: Hashable { case ellipticCurve public init(secAttr: NSNumber) { let secAttrString = secAttr.stringValue as CFString diff --git a/Secretive.xcodeproj/project.pbxproj b/Secretive.xcodeproj/project.pbxproj index bfb8b48..01a2ce8 100644 --- a/Secretive.xcodeproj/project.pbxproj +++ b/Secretive.xcodeproj/project.pbxproj @@ -45,6 +45,11 @@ 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 */; }; + 507EE34824281FB8003C4FE3 /* StubFileHandleWriter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 507EE34724281FB8003C4FE3 /* StubFileHandleWriter.swift */; }; + 507EE34A2428263B003C4FE3 /* StubStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 507EE3492428263B003C4FE3 /* StubStore.swift */; }; + 507EE34E2428784F003C4FE3 /* StubWitness.swift in Sources */ = {isa = PBXBuildFile; fileRef = 507EE34D2428784F003C4FE3 /* StubWitness.swift */; }; 508A58AA241E06B40069DC07 /* PreviewUpdater.swift in Sources */ = {isa = PBXBuildFile; fileRef = 508A58A9241E06B40069DC07 /* PreviewUpdater.swift */; }; 508A58B3241ED2180069DC07 /* AgentStatusChecker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 508A58B2241ED2180069DC07 /* AgentStatusChecker.swift */; }; 508A58B5241ED48F0069DC07 /* PreviewAgentStatusChecker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 508A58B4241ED48F0069DC07 /* PreviewAgentStatusChecker.swift */; }; @@ -56,7 +61,7 @@ 5099A02B23FE352C0062B6F2 /* SmartCardSecret.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5099A02A23FE352C0062B6F2 /* SmartCardSecret.swift */; }; 5099A02E23FE56E10062B6F2 /* OpenSSHKeyWriter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5099A02D23FE56E10062B6F2 /* OpenSSHKeyWriter.swift */; }; 5099A075240242BA0062B6F2 /* SecretAgentKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5099A06C240242BA0062B6F2 /* SecretAgentKit.framework */; }; - 5099A07C240242BA0062B6F2 /* SecretAgentKitTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5099A07B240242BA0062B6F2 /* SecretAgentKitTests.swift */; }; + 5099A07C240242BA0062B6F2 /* AgentTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5099A07B240242BA0062B6F2 /* AgentTests.swift */; }; 5099A07E240242BA0062B6F2 /* SecretAgentKit.h in Headers */ = {isa = PBXBuildFile; fileRef = 5099A06E240242BA0062B6F2 /* SecretAgentKit.h */; settings = {ATTRIBUTES = (Public, ); }; }; 5099A08A240242C20062B6F2 /* SSHAgentProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5099A089240242C20062B6F2 /* SSHAgentProtocol.swift */; }; 50A3B79124026B7600D209EA /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 50A3B79024026B7600D209EA /* Assets.xcassets */; }; @@ -238,6 +243,11 @@ 507CE4EF2420A4C50029F750 /* SigningWitness.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SigningWitness.swift; sourceTree = ""; }; 507CE4F32420A8C10029F750 /* SigningRequestProvenance.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SigningRequestProvenance.swift; sourceTree = ""; }; 507CE4F52420A96F0029F750 /* SigningRequestTracer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SigningRequestTracer.swift; sourceTree = ""; }; + 507EE34124281E12003C4FE3 /* FileHandleProtocols.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FileHandleProtocols.swift; sourceTree = ""; }; + 507EE34524281F89003C4FE3 /* StubFileHandleReader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StubFileHandleReader.swift; sourceTree = ""; }; + 507EE34724281FB8003C4FE3 /* StubFileHandleWriter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StubFileHandleWriter.swift; sourceTree = ""; }; + 507EE3492428263B003C4FE3 /* StubStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StubStore.swift; sourceTree = ""; }; + 507EE34D2428784F003C4FE3 /* StubWitness.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StubWitness.swift; sourceTree = ""; }; 508A58A9241E06B40069DC07 /* PreviewUpdater.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreviewUpdater.swift; sourceTree = ""; }; 508A58AB241E121B0069DC07 /* Config.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Config.xcconfig; sourceTree = ""; }; 508A58B2241ED2180069DC07 /* AgentStatusChecker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AgentStatusChecker.swift; sourceTree = ""; }; @@ -252,7 +262,7 @@ 5099A06E240242BA0062B6F2 /* SecretAgentKit.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SecretAgentKit.h; sourceTree = ""; }; 5099A06F240242BA0062B6F2 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 5099A074240242BA0062B6F2 /* SecretAgentKitTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = SecretAgentKitTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; - 5099A07B240242BA0062B6F2 /* SecretAgentKitTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SecretAgentKitTests.swift; sourceTree = ""; }; + 5099A07B240242BA0062B6F2 /* AgentTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AgentTests.swift; sourceTree = ""; }; 5099A07D240242BA0062B6F2 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 5099A089240242C20062B6F2 /* SSHAgentProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SSHAgentProtocol.swift; sourceTree = ""; }; 50A3B78A24026B7500D209EA /* SecretAgent.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = SecretAgent.app; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -529,6 +539,7 @@ 507CE4F32420A8C10029F750 /* SigningRequestProvenance.swift */, 507CE4F52420A96F0029F750 /* SigningRequestTracer.swift */, 50A3B79F24026B9900D209EA /* Agent.swift */, + 507EE34124281E12003C4FE3 /* FileHandleProtocols.swift */, 5099A06F240242BA0062B6F2 /* Info.plist */, ); path = SecretAgentKit; @@ -537,7 +548,11 @@ 5099A07A240242BA0062B6F2 /* SecretAgentKitTests */ = { isa = PBXGroup; children = ( - 5099A07B240242BA0062B6F2 /* SecretAgentKitTests.swift */, + 5099A07B240242BA0062B6F2 /* AgentTests.swift */, + 507EE34524281F89003C4FE3 /* StubFileHandleReader.swift */, + 507EE34724281FB8003C4FE3 /* StubFileHandleWriter.swift */, + 507EE34D2428784F003C4FE3 /* StubWitness.swift */, + 507EE3492428263B003C4FE3 /* StubStore.swift */, 5099A07D240242BA0062B6F2 /* Info.plist */, ); path = SecretAgentKitTests; @@ -957,6 +972,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 507EE34224281E12003C4FE3 /* FileHandleProtocols.swift in Sources */, 507CE4EE2420A3CA0029F750 /* SocketController.swift in Sources */, 5099A08A240242C20062B6F2 /* SSHAgentProtocol.swift in Sources */, 507CE4ED2420A3C70029F750 /* Agent.swift in Sources */, @@ -970,7 +986,11 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 5099A07C240242BA0062B6F2 /* SecretAgentKitTests.swift in Sources */, + 507EE34E2428784F003C4FE3 /* StubWitness.swift in Sources */, + 507EE34624281F89003C4FE3 /* StubFileHandleReader.swift in Sources */, + 507EE34A2428263B003C4FE3 /* StubStore.swift in Sources */, + 5099A07C240242BA0062B6F2 /* AgentTests.swift in Sources */, + 507EE34824281FB8003C4FE3 /* StubFileHandleWriter.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1627,8 +1647,8 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; - CODE_SIGN_IDENTITY = "-"; - CODE_SIGN_STYLE = Automatic; + CODE_SIGN_IDENTITY = "Developer ID Application"; + CODE_SIGN_STYLE = Manual; COMBINE_HIDPI_IMAGES = YES; DEVELOPMENT_TEAM = Z72PRUAWF6; INFOPLIST_FILE = SecretKitTests/Info.plist; @@ -1639,6 +1659,7 @@ ); PRODUCT_BUNDLE_IDENTIFIER = com.maxgoedjen.SecretKitTests; PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; SWIFT_VERSION = 5.0; }; name = Test; @@ -1677,7 +1698,8 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; - CODE_SIGN_IDENTITY = "-"; + CODE_SIGN_IDENTITY = "Developer ID Application"; + CODE_SIGN_STYLE = Manual; COMBINE_HIDPI_IMAGES = YES; DEVELOPMENT_TEAM = Z72PRUAWF6; INFOPLIST_FILE = SecretAgentKitTests/Info.plist; @@ -1688,6 +1710,7 @@ ); PRODUCT_BUNDLE_IDENTIFIER = com.maxgoedjen.SecretAgentKitTests; PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; SWIFT_VERSION = 5.0; }; name = Test; @@ -1755,6 +1778,7 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; DEVELOPMENT_TEAM = Z72PRUAWF6; INFOPLIST_FILE = SecretAgentKitTests/Info.plist; @@ -1773,6 +1797,7 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; DEVELOPMENT_TEAM = Z72PRUAWF6; INFOPLIST_FILE = SecretAgentKitTests/Info.plist; From 1d147d8e34376baf257ea46b5e5fae384ef91842 Mon Sep 17 00:00:00 2001 From: Max Goedjen Date: Tue, 24 Mar 2020 22:04:49 -0700 Subject: [PATCH 03/20] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 9797135..0c9e89b 100644 --- a/README.md +++ b/README.md @@ -38,7 +38,7 @@ For non-command-line based apps, like GUI Git clients, you may need to go throug [Tower](https://www.git-tower.com/help/mac/integration/environment) -### Security Considerations +### Auditable Build Process Builds are produced by GitHub Actions with an auditable build and release generation process. Each build has a "Document SHAs" step, which will output SHA checksums for the build produced by the GitHub Action, so you can verify that the source code for a given build corresponds to any given release. From 4a2a342670611b75ff90a99a768e3dd76771c287 Mon Sep 17 00:00:00 2001 From: Max Goedjen Date: Tue, 24 Mar 2020 22:16:04 -0700 Subject: [PATCH 04/20] Create FUNDING.yml --- .github/FUNDING.yml | 1 + 1 file changed, 1 insertion(+) create mode 100644 .github/FUNDING.yml diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 0000000..dca0b48 --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1 @@ +github: maxgoedjen From 92b9648e04e3defee4af4a3dd9662452ab5cf909 Mon Sep 17 00:00:00 2001 From: Max Goedjen Date: Wed, 25 Mar 2020 23:35:02 -0700 Subject: [PATCH 05/20] FAQ (#76) --- FAQ.md | 25 +++++++++++++++++++++++++ README.md | 10 +++------- 2 files changed, 28 insertions(+), 7 deletions(-) create mode 100644 FAQ.md diff --git a/FAQ.md b/FAQ.md new file mode 100644 index 0000000..612f079 --- /dev/null +++ b/FAQ.md @@ -0,0 +1,25 @@ +# FAQ + +### Secretive doesn't work with my git client + +Secretive relies on the `SSH_AUTH_SOCK` environment variable being respected. The `git` and `ssh` command line tools natively respect this, but third party apps may require some configuration to work. A non-exhaustive list of clients is provided here: + +Tower - [Instructions](https://www.git-tower.com/help/mac/integration/environment) + +GitHub Desktop: Should just work, no configuration needed + +### Why should I trust you? + +You shouldn't, for a piece of software like this. Secretive, by design, has an auditable build process. Each build has a fully auditable build log, showing the source it was built from and a SHA of the build product. You can check the SHA of the zip you download against the SHA output in the build log (which is linked in the About window). + +### I want to build Secretive from source + +Awesome! Just bear in mind that because an app only has access to the keychain items that it created, if you have secrets that you created with the prebuilt version of Secretive, you'll be unable to access them using your own custom build (since you'll have changed the bundled ID). + +### I have a security issue + +Please contact [max.goedjen@gmail.com](mailto:max.goedjen@gmail.com) with a subject containing "SECRETIVE SECURITY" immediately with details, and I'll address the issue and credit you ASAP. + +### I want to contribute to Secretive + +Sweet! Please check out the [contributing guidelines](CONTRIBUTING.md) and go from there. \ No newline at end of file diff --git a/README.md b/README.md index 0c9e89b..5b592ba 100644 --- a/README.md +++ b/README.md @@ -30,13 +30,9 @@ For Macs without Secure Enclaves, you can configure a Smart Card (such as a Yubi ## Getting Started -### Setup for Third Party Apps - -When you first launch Secretive, you'll be prompted to set up your command line environment. You can redisplay this prompt at any time by going to `Menu > Help -> Set Up Helper App`. -For non-command-line based apps, like GUI Git clients, you may need to go through app-specific setup. - -[Tower](https://www.git-tower.com/help/mac/integration/environment) +### FAQ +There's a [FAQ here](FAQ.md). ### Auditable Build Process @@ -52,4 +48,4 @@ Beacuse secrets in the Secure Enclave are not exportable, they are not able to b ## Security -If you discover any vulnerabilities in this project, please notify max.goedjen@gmail.com +If you discover any vulnerabilities in this project, please notify [max.goedjen@gmail.com](mailto:max.goedjen@gmail.com) with the subject containing "SECRETIVE SECURITY." From a0ed8803860e73aff7458c564601e41435795a9d Mon Sep 17 00:00:00 2001 From: Max Goedjen Date: Fri, 27 Mar 2020 19:24:37 -0700 Subject: [PATCH 06/20] Fix #80. --- FAQ.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/FAQ.md b/FAQ.md index 612f079..ab28f05 100644 --- a/FAQ.md +++ b/FAQ.md @@ -1,5 +1,9 @@ # FAQ +### How do I import my current SSH keys, or export my Secretive Keys? + +The secure enclave doesn't allow import or export of private keys. For any new computer, you should just create a new set of keys. If you're using a smart card, you _might_ be able to export your private key from the vendor's software. + ### Secretive doesn't work with my git client Secretive relies on the `SSH_AUTH_SOCK` environment variable being respected. The `git` and `ssh` command line tools natively respect this, but third party apps may require some configuration to work. A non-exhaustive list of clients is provided here: @@ -22,4 +26,4 @@ Please contact [max.goedjen@gmail.com](mailto:max.goedjen@gmail.com) with a subj ### I want to contribute to Secretive -Sweet! Please check out the [contributing guidelines](CONTRIBUTING.md) and go from there. \ No newline at end of file +Sweet! Please check out the [contributing guidelines](CONTRIBUTING.md) and go from there. From 4bfb3a0012ca08e0b27529058c7634038a655c4b Mon Sep 17 00:00:00 2001 From: Max Goedjen Date: Sat, 28 Mar 2020 18:08:54 -0700 Subject: [PATCH 07/20] Rephrase access control. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 5b592ba..52a07fe 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ The most common setup for SSH keys is just keeping them on disk, guarded by prop ### Access Control -If your Mac has a Secure Enclave, it also has support for strong biometric access controls like Touch ID. You can configure your key so that they require Touch ID (or Watch) authentication before they're accessed. +If your Mac has a Secure Enclave, it also has support for strong access controls like Touch ID, or authentication with Apple Watch. You can configure your key so that they require Touch ID (or Watch) authentication before they're accessed. Screenshot of Secretive authenticating with Touch ID From 31e51e45c1c47f6764e41a668aa13d6cbd76cbf5 Mon Sep 17 00:00:00 2001 From: Max Goedjen Date: Sun, 29 Mar 2020 17:26:43 -0700 Subject: [PATCH 08/20] Move types to common (#81) --- SecretKit/{ => Common/Types}/Secret.swift | 0 SecretKit/{ => Common/Types}/SecretStore.swift | 0 Secretive.xcodeproj/project.pbxproj | 12 ++++++++++-- 3 files changed, 10 insertions(+), 2 deletions(-) rename SecretKit/{ => Common/Types}/Secret.swift (100%) rename SecretKit/{ => Common/Types}/SecretStore.swift (100%) diff --git a/SecretKit/Secret.swift b/SecretKit/Common/Types/Secret.swift similarity index 100% rename from SecretKit/Secret.swift rename to SecretKit/Common/Types/Secret.swift diff --git a/SecretKit/SecretStore.swift b/SecretKit/Common/Types/SecretStore.swift similarity index 100% rename from SecretKit/SecretStore.swift rename to SecretKit/Common/Types/SecretStore.swift diff --git a/Secretive.xcodeproj/project.pbxproj b/Secretive.xcodeproj/project.pbxproj index 01a2ce8..dd04dae 100644 --- a/Secretive.xcodeproj/project.pbxproj +++ b/Secretive.xcodeproj/project.pbxproj @@ -346,6 +346,15 @@ /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ + 504BA92D243171F20064740E /* Types */ = { + isa = PBXGroup; + children = ( + 50617DCA23FCECA10099B055 /* Secret.swift */, + 50617DC623FCE4EA0099B055 /* SecretStore.swift */, + ); + path = Types; + sourceTree = ""; + }; 50617D7623FCE48D0099B055 = { isa = PBXGroup; children = ( @@ -418,8 +427,6 @@ isa = PBXGroup; children = ( 50617DAA23FCE4AB0099B055 /* SecretKit.h */, - 50617DCA23FCECA10099B055 /* Secret.swift */, - 50617DC623FCE4EA0099B055 /* SecretStore.swift */, 5099A02C23FE56D70062B6F2 /* Common */, 50617DCC23FCECEE0099B055 /* SecureEnclave */, 5099A02523FE34DE0062B6F2 /* SmartCard */, @@ -522,6 +529,7 @@ 5099A02C23FE56D70062B6F2 /* Common */ = { isa = PBXGroup; children = ( + 504BA92D243171F20064740E /* Types */, 5068389F2415EA4F00F55094 /* Erasers */, 506838A42415EA6800F55094 /* OpenSSH */, 5068389D241471CD00F55094 /* SecretStoreList.swift */, From cca070dbe4a19477f8edd68b3caaefe08ec2f438 Mon Sep 17 00:00:00 2001 From: Max Goedjen Date: Mon, 30 Mar 2020 23:44:38 -0700 Subject: [PATCH 09/20] Update FAQ.md --- FAQ.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/FAQ.md b/FAQ.md index ab28f05..d4a15e3 100644 --- a/FAQ.md +++ b/FAQ.md @@ -24,6 +24,10 @@ Awesome! Just bear in mind that because an app only has access to the keychain i Please contact [max.goedjen@gmail.com](mailto:max.goedjen@gmail.com) with a subject containing "SECRETIVE SECURITY" immediately with details, and I'll address the issue and credit you ASAP. +### I have a non-security related bug + +Please file a [GitHub issue](https://github.com/maxgoedjen/secretive/issues) for it. + ### I want to contribute to Secretive Sweet! Please check out the [contributing guidelines](CONTRIBUTING.md) and go from there. From 7b1563f167cd3665b87d5751ef46f36ebf77b7e5 Mon Sep 17 00:00:00 2001 From: Max Goedjen Date: Tue, 31 Mar 2020 23:11:04 -0700 Subject: [PATCH 10/20] Update FAQ.md --- FAQ.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/FAQ.md b/FAQ.md index d4a15e3..a564d59 100644 --- a/FAQ.md +++ b/FAQ.md @@ -12,6 +12,10 @@ Tower - [Instructions](https://www.git-tower.com/help/mac/integration/environmen GitHub Desktop: Should just work, no configuration needed +### Secretive isn't working for me + +Please run `ssh -Tv git@github.com` in your terminal and paste the output in a [new GitHub issue](https://github.com/maxgoedjen/secretive/issues/new) with a description of your issue. + ### Why should I trust you? You shouldn't, for a piece of software like this. Secretive, by design, has an auditable build process. Each build has a fully auditable build log, showing the source it was built from and a SHA of the build product. You can check the SHA of the zip you download against the SHA output in the build log (which is linked in the About window). @@ -26,7 +30,7 @@ Please contact [max.goedjen@gmail.com](mailto:max.goedjen@gmail.com) with a subj ### I have a non-security related bug -Please file a [GitHub issue](https://github.com/maxgoedjen/secretive/issues) for it. +Please file a [GitHub issue](https://github.com/maxgoedjen/secretive/issues/new) for it. ### I want to contribute to Secretive From f1e8e43f626f739d75ddcc2d5816d8de7c505921 Mon Sep 17 00:00:00 2001 From: Max Goedjen Date: Wed, 1 Apr 2020 22:11:45 -0700 Subject: [PATCH 11/20] Update FAQ.md --- FAQ.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/FAQ.md b/FAQ.md index a564d59..9d39fa6 100644 --- a/FAQ.md +++ b/FAQ.md @@ -30,7 +30,7 @@ Please contact [max.goedjen@gmail.com](mailto:max.goedjen@gmail.com) with a subj ### I have a non-security related bug -Please file a [GitHub issue](https://github.com/maxgoedjen/secretive/issues/new) for it. +Please file a [GitHub issue](https://github.com/maxgoedjen/secretive/issues/new) for it. I will not provide email support with the exception of the critical security issues mentioned above. ### I want to contribute to Secretive From ccbf92785d060f7a3f2e5e446844c1fb9fc1b98c Mon Sep 17 00:00:00 2001 From: Max Goedjen Date: Thu, 2 Apr 2020 00:43:45 -0700 Subject: [PATCH 12/20] Fix padding bug (#83) --- SecretAgentKit/Agent.swift | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/SecretAgentKit/Agent.swift b/SecretAgentKit/Agent.swift index f10f882..6ba1d32 100644 --- a/SecretAgentKit/Agent.swift +++ b/SecretAgentKit/Agent.swift @@ -113,8 +113,17 @@ extension Agent { let rawLength = rawRepresentation.count/2 - let r = rawRepresentation[0.. = 0x80...0xFF + var r = Data(rawRepresentation[0.. Date: Thu, 2 Apr 2020 00:53:55 -0700 Subject: [PATCH 13/20] Fix #84 --- SecretAgentKitTests/AgentTests.swift | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/SecretAgentKitTests/AgentTests.swift b/SecretAgentKitTests/AgentTests.swift index c5c7bfd..e068b7b 100644 --- a/SecretAgentKitTests/AgentTests.swift +++ b/SecretAgentKitTests/AgentTests.swift @@ -49,8 +49,15 @@ class AgentTests: XCTestCase { _ = inner.readNextChunk() let signedData = inner.readNextChunk() let rsData = OpenSSHReader(data: signedData) - let r = rsData.readNextChunk() - let s = rsData.readNextChunk() + var r = rsData.readNextChunk() + var s = rsData.readNextChunk() + // This is fine IRL, but it freaks out CryptoKit + if r[0] == 0 { + r.removeFirst() + } + if s[0] == 0 { + s.removeFirst() + } var rs = r rs.append(s) let signature = try! P256.Signing.ECDSASignature(rawRepresentation: rs) From c3068011496f75964196afe7a362ac292d8328e6 Mon Sep 17 00:00:00 2001 From: Max Goedjen Date: Thu, 2 Apr 2020 18:22:59 -0700 Subject: [PATCH 14/20] Xcode 11.4 changes --- SecretAgentKit/Agent.swift | 4 +--- Secretive/Views/ContentView.swift | 2 +- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/SecretAgentKit/Agent.swift b/SecretAgentKit/Agent.swift index 6ba1d32..9c9a789 100644 --- a/SecretAgentKit/Agent.swift +++ b/SecretAgentKit/Agent.swift @@ -65,9 +65,7 @@ extension Agent { extension Agent { func identities() -> Data { - // TODO: RESTORE ONCE XCODE 11.4 IS GM - let secrets = storeList.stores.flatMap { $0.secrets } -// let secrets = storeList.stores.flatMap(\.secrets) + let secrets = storeList.stores.flatMap(\.secrets) var count = UInt32(secrets.count).bigEndian let countData = Data(bytes: &count, count: UInt32.bitWidth/8) var keyData = Data() diff --git a/Secretive/Views/ContentView.swift b/Secretive/Views/ContentView.swift index 7ce9325..31b5bf7 100644 --- a/Secretive/Views/ContentView.swift +++ b/Secretive/Views/ContentView.swift @@ -110,7 +110,7 @@ struct ContentView Date: Fri, 3 Apr 2020 00:44:13 -0700 Subject: [PATCH 15/20] Revert "Xcode 11.4 changes" This reverts commit c3068011496f75964196afe7a362ac292d8328e6. --- SecretAgentKit/Agent.swift | 4 +++- Secretive/Views/ContentView.swift | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/SecretAgentKit/Agent.swift b/SecretAgentKit/Agent.swift index 9c9a789..6ba1d32 100644 --- a/SecretAgentKit/Agent.swift +++ b/SecretAgentKit/Agent.swift @@ -65,7 +65,9 @@ extension Agent { extension Agent { func identities() -> Data { - let secrets = storeList.stores.flatMap(\.secrets) + // TODO: RESTORE ONCE XCODE 11.4 IS GM + let secrets = storeList.stores.flatMap { $0.secrets } +// let secrets = storeList.stores.flatMap(\.secrets) var count = UInt32(secrets.count).bigEndian let countData = Data(bytes: &count, count: UInt32.bitWidth/8) var keyData = Data() diff --git a/Secretive/Views/ContentView.swift b/Secretive/Views/ContentView.swift index 31b5bf7..7ce9325 100644 --- a/Secretive/Views/ContentView.swift +++ b/Secretive/Views/ContentView.swift @@ -110,7 +110,7 @@ struct ContentView Date: Sat, 4 Apr 2020 15:16:31 -0700 Subject: [PATCH 16/20] Relaunch agent if updated (#86) --- Secretive.xcodeproj/project.pbxproj | 8 +++++ Secretive/AppDelegate.swift | 8 +++++ .../Controllers/JustUpdatedChecker.swift | 36 +++++++++++++++++++ .../Controllers/LaunchAgentController.swift | 19 ++++++++++ Secretive/Views/SetupView.swift | 3 +- 5 files changed, 72 insertions(+), 2 deletions(-) create mode 100644 Secretive/Controllers/JustUpdatedChecker.swift create mode 100644 Secretive/Controllers/LaunchAgentController.swift diff --git a/Secretive.xcodeproj/project.pbxproj b/Secretive.xcodeproj/project.pbxproj index dd04dae..14190a1 100644 --- a/Secretive.xcodeproj/project.pbxproj +++ b/Secretive.xcodeproj/project.pbxproj @@ -10,6 +10,8 @@ 50020BB024064869003D4025 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50020BAF24064869003D4025 /* AppDelegate.swift */; }; 5018F54F24064786002EB505 /* Notifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5018F54E24064786002EB505 /* Notifier.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 */; }; 50617D8323FCE48E0099B055 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50617D8223FCE48E0099B055 /* AppDelegate.swift */; }; 50617D8523FCE48E0099B055 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50617D8423FCE48E0099B055 /* ContentView.swift */; }; 50617D8723FCE48E0099B055 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 50617D8623FCE48E0099B055 /* Assets.xcassets */; }; @@ -206,6 +208,8 @@ 50020BAF24064869003D4025 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 5018F54E24064786002EB505 /* Notifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Notifier.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 = ""; }; 50617D7F23FCE48E0099B055 /* Secretive.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Secretive.app; sourceTree = BUILT_PRODUCTS_DIR; }; 50617D8223FCE48E0099B055 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 50617D8423FCE48E0099B055 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; }; @@ -512,6 +516,8 @@ isa = PBXGroup; children = ( 508A58B2241ED2180069DC07 /* AgentStatusChecker.swift */, + 50571E0224393C2600F76F6C /* JustUpdatedChecker.swift */, + 50571E0424393D1500F76F6C /* LaunchAgentController.swift */, ); path = Controllers; sourceTree = ""; @@ -916,10 +922,12 @@ files = ( 50C385A9240B636500AF2719 /* SetupView.swift in Sources */, 50617D8523FCE48E0099B055 /* ContentView.swift in Sources */, + 50571E0324393C2600F76F6C /* JustUpdatedChecker.swift in Sources */, 50617DD223FCEFA90099B055 /* PreviewStore.swift in Sources */, 508A58B3241ED2180069DC07 /* AgentStatusChecker.swift in Sources */, 50C385A52407A76D00AF2719 /* SecretDetailView.swift in Sources */, 5099A02423FD2AAA0062B6F2 /* CreateSecretView.swift in Sources */, + 50571E0524393D1500F76F6C /* LaunchAgentController.swift in Sources */, 50B8550D24138C4F009958AC /* DeleteSecretView.swift in Sources */, 50BB046B2418AAAE00D6E079 /* EmptyStoreView.swift in Sources */, 50731669241E00C20023809E /* NoticeView.swift in Sources */, diff --git a/Secretive/AppDelegate.swift b/Secretive/AppDelegate.swift index d2e26aa..c9e116d 100644 --- a/Secretive/AppDelegate.swift +++ b/Secretive/AppDelegate.swift @@ -17,6 +17,7 @@ class AppDelegate: NSObject, NSApplicationDelegate { }() let updater = Updater() let agentStatusChecker = AgentStatusChecker() + let justUpdatedChecker = JustUpdatedChecker() func applicationDidFinishLaunching(_ aNotification: Notification) { let contentView = ContentView(storeList: storeList, updater: updater, agentStatusChecker: agentStatusChecker, runSetupBlock: { self.runSetup(sender: nil) }) @@ -40,6 +41,7 @@ class AppDelegate: NSObject, NSApplicationDelegate { newMenuItem.isEnabled = true } runSetupIfNeeded() + relaunchAgentIfNeeded() } func applicationDidBecomeActive(_ notification: Notification) { @@ -89,6 +91,12 @@ extension AppDelegate { } } + func relaunchAgentIfNeeded() { + if agentStatusChecker.running && justUpdatedChecker.justUpdated { + LaunchAgentController().relaunch() + } + } + } extension AppDelegate { diff --git a/Secretive/Controllers/JustUpdatedChecker.swift b/Secretive/Controllers/JustUpdatedChecker.swift new file mode 100644 index 0000000..95a7ed3 --- /dev/null +++ b/Secretive/Controllers/JustUpdatedChecker.swift @@ -0,0 +1,36 @@ +import Foundation +import Combine +import AppKit + +protocol JustUpdatedCheckerProtocol: ObservableObject { + var justUpdated: Bool { get } +} + +class JustUpdatedChecker: ObservableObject, JustUpdatedCheckerProtocol { + + @Published var justUpdated: Bool = false + + init() { + check() + } + + func check() { + let lastBuild = UserDefaults.standard.object(forKey: Constants.previousVersionUserDefaultsKey) as? String ?? "None" + let currentBuild = Bundle.main.infoDictionary!["CFBundleShortVersionString"] as! String + UserDefaults.standard.set(currentBuild, forKey: Constants.previousVersionUserDefaultsKey) + if lastBuild != currentBuild { + justUpdated = true + } + } + + + +} + +extension JustUpdatedChecker { + + enum Constants { + static let previousVersionUserDefaultsKey = "com.maxgoedjen.Secretive.lastBuild" + } + +} diff --git a/Secretive/Controllers/LaunchAgentController.swift b/Secretive/Controllers/LaunchAgentController.swift new file mode 100644 index 0000000..8077da5 --- /dev/null +++ b/Secretive/Controllers/LaunchAgentController.swift @@ -0,0 +1,19 @@ +import Foundation +import ServiceManagement + +struct LaunchAgentController { + + func install() -> Bool { + setEnabled(true) + } + + func relaunch() { + _ = setEnabled(false) + _ = setEnabled(true) + } + + private func setEnabled(_ enabled: Bool) -> Bool { + SMLoginItemSetEnabled("com.maxgoedjen.Secretive.SecretAgent" as CFString, enabled) + } + +} diff --git a/Secretive/Views/SetupView.swift b/Secretive/Views/SetupView.swift index c9f55a1..b475d77 100644 --- a/Secretive/Views/SetupView.swift +++ b/Secretive/Views/SetupView.swift @@ -1,6 +1,5 @@ import Foundation import SwiftUI -import ServiceManagement struct SetupView: View { @@ -117,7 +116,7 @@ struct SetupStepCommandView: View { extension SetupView { func installLaunchAgent() -> Bool { - SMLoginItemSetEnabled("com.maxgoedjen.Secretive.SecretAgent" as CFString, true) + LaunchAgentController().install() } func markAsDone() -> Bool { From 6dbb3b7fb3efb8dba0cc93d352e3bd2c841da4a5 Mon Sep 17 00:00:00 2001 From: Max Goedjen Date: Sun, 5 Apr 2020 16:05:45 -0700 Subject: [PATCH 17/20] Add back Xcode 11.4 changes (#87) --- .github/workflows/release.yml | 2 ++ .github/workflows/test.yml | 2 ++ SecretAgentKit/Agent.swift | 4 +--- Secretive/Views/ContentView.swift | 2 +- 4 files changed, 6 insertions(+), 4 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index be94bad..4522a2e 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -17,6 +17,8 @@ jobs: HOST_PROFILE_DATA: ${{ secrets.HOST_PROFILE_DATA }} AGENT_PROFILE_DATA: ${{ secrets.AGENT_PROFILE_DATA }} run: ./.github/scripts/signing.sh + - name: Set Environment + run: sudo xcrun xcode-select -s /Applications/Xcode_11.4.app - name: Test run: xcrun xcodebuild test -project Secretive.xcodeproj -scheme Secretive build: diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 3c08203..7e686c3 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -14,5 +14,7 @@ jobs: HOST_PROFILE_DATA: ${{ secrets.HOST_PROFILE_DATA }} AGENT_PROFILE_DATA: ${{ secrets.AGENT_PROFILE_DATA }} run: ./.github/scripts/signing.sh + - name: Set Environment + run: sudo xcrun xcode-select -s /Applications/Xcode_11.4.app - name: Test run: xcrun xcodebuild test -project Secretive.xcodeproj -scheme Secretive diff --git a/SecretAgentKit/Agent.swift b/SecretAgentKit/Agent.swift index 6ba1d32..9c9a789 100644 --- a/SecretAgentKit/Agent.swift +++ b/SecretAgentKit/Agent.swift @@ -65,9 +65,7 @@ extension Agent { extension Agent { func identities() -> Data { - // TODO: RESTORE ONCE XCODE 11.4 IS GM - let secrets = storeList.stores.flatMap { $0.secrets } -// let secrets = storeList.stores.flatMap(\.secrets) + let secrets = storeList.stores.flatMap(\.secrets) var count = UInt32(secrets.count).bigEndian let countData = Data(bytes: &count, count: UInt32.bitWidth/8) var keyData = Data() diff --git a/Secretive/Views/ContentView.swift b/Secretive/Views/ContentView.swift index 7ce9325..31b5bf7 100644 --- a/Secretive/Views/ContentView.swift +++ b/Secretive/Views/ContentView.swift @@ -110,7 +110,7 @@ struct ContentView Date: Mon, 6 Apr 2020 21:32:22 -0700 Subject: [PATCH 18/20] Fix typo (#89) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 52a07fe..0a24778 100644 --- a/README.md +++ b/README.md @@ -44,7 +44,7 @@ While Secretive uses the Secure Enclave for key storage, it still relies on Keyc ### Backups and Transfers to New Machines -Beacuse secrets in the Secure Enclave are not exportable, they are not able to be backed up, and you will not be able to transfer them to a new machine. If you get a new Mac, just create a new set of secrets specific to that Mac. +Because secrets in the Secure Enclave are not exportable, they are not able to be backed up, and you will not be able to transfer them to a new machine. If you get a new Mac, just create a new set of secrets specific to that Mac. ## Security From b234100aa566a0da5b805031c5751ce057298d2b Mon Sep 17 00:00:00 2001 From: Max Goedjen Date: Fri, 15 May 2020 23:19:00 -0700 Subject: [PATCH 19/20] Fileprivate -> private (#91) --- SecretAgent/AppDelegate.swift | 2 +- SecretAgent/Notifier.swift | 2 +- SecretAgentKit/Agent.swift | 8 ++++---- SecretAgentKit/SocketController.swift | 4 ++-- SecretKit/Common/Erasers/AnySecret.swift | 12 ++++++------ SecretKit/Common/Erasers/AnySecretStore.swift | 16 ++++++++-------- SecretKit/Common/SecretStoreList.swift | 4 ++-- SecretKit/SecureEnclave/SecureEnclaveStore.swift | 12 ++++++------ SecretKit/SmartCard/SmartCardStore.swift | 14 +++++++------- Secretive/Views/ContentView.swift | 8 ++++---- Secretive/Views/DeleteSecretView.swift | 2 +- 11 files changed, 42 insertions(+), 42 deletions(-) diff --git a/SecretAgent/AppDelegate.swift b/SecretAgent/AppDelegate.swift index 0f0b0a2..b8dbd10 100644 --- a/SecretAgent/AppDelegate.swift +++ b/SecretAgent/AppDelegate.swift @@ -23,7 +23,7 @@ class AppDelegate: NSObject, NSApplicationDelegate { let path = (NSHomeDirectory() as NSString).appendingPathComponent("socket.ssh") as String return SocketController(path: path) }() - fileprivate var updateSink: AnyCancellable? + private var updateSink: AnyCancellable? func applicationDidFinishLaunching(_ aNotification: Notification) { os_log(.debug, "SecretAgent finished launching") diff --git a/SecretAgent/Notifier.swift b/SecretAgent/Notifier.swift index cbfce20..94a526e 100644 --- a/SecretAgent/Notifier.swift +++ b/SecretAgent/Notifier.swift @@ -7,7 +7,7 @@ import Brief class Notifier { - fileprivate let notificationDelegate = NotificationDelegate() + private let notificationDelegate = NotificationDelegate() init() { let updateAction = UNNotificationAction(identifier: Constants.updateActionIdentitifier, title: "Update", options: []) diff --git a/SecretAgentKit/Agent.swift b/SecretAgentKit/Agent.swift index 9c9a789..879ca92 100644 --- a/SecretAgentKit/Agent.swift +++ b/SecretAgentKit/Agent.swift @@ -6,10 +6,10 @@ import AppKit public class Agent { - fileprivate let storeList: SecretStoreList - fileprivate let witness: SigningWitness? - fileprivate let writer = OpenSSHKeyWriter() - fileprivate let requestTracer = SigningRequestTracer() + private let storeList: SecretStoreList + private let witness: SigningWitness? + private let writer = OpenSSHKeyWriter() + private let requestTracer = SigningRequestTracer() public init(storeList: SecretStoreList, witness: SigningWitness? = nil) { os_log(.debug, "Agent is running") diff --git a/SecretAgentKit/SocketController.swift b/SecretAgentKit/SocketController.swift index 1fc51d4..7b6a14b 100644 --- a/SecretAgentKit/SocketController.swift +++ b/SecretAgentKit/SocketController.swift @@ -3,8 +3,8 @@ import OSLog public class SocketController { - fileprivate var fileHandle: FileHandle? - fileprivate var port: SocketPort? + private var fileHandle: FileHandle? + private var port: SocketPort? public var handler: ((FileHandleReader, FileHandleWriter) -> Void)? public init(path: String) { diff --git a/SecretKit/Common/Erasers/AnySecret.swift b/SecretKit/Common/Erasers/AnySecret.swift index 7243f93..a3c0415 100644 --- a/SecretKit/Common/Erasers/AnySecret.swift +++ b/SecretKit/Common/Erasers/AnySecret.swift @@ -3,12 +3,12 @@ import Foundation public struct AnySecret: Secret { let base: Any - fileprivate let hashable: AnyHashable - fileprivate let _id: () -> AnyHashable - fileprivate let _name: () -> String - fileprivate let _algorithm: () -> Algorithm - fileprivate let _keySize: () -> Int - fileprivate let _publicKey: () -> Data + private let hashable: AnyHashable + private let _id: () -> AnyHashable + private let _name: () -> String + private let _algorithm: () -> Algorithm + private let _keySize: () -> Int + private let _publicKey: () -> Data public init(_ secret: T) where T: Secret { if let secret = secret as? AnySecret { diff --git a/SecretKit/Common/Erasers/AnySecretStore.swift b/SecretKit/Common/Erasers/AnySecretStore.swift index 322c31b..12b1ea0 100644 --- a/SecretKit/Common/Erasers/AnySecretStore.swift +++ b/SecretKit/Common/Erasers/AnySecretStore.swift @@ -4,12 +4,12 @@ import Combine public class AnySecretStore: SecretStore { let base: Any - fileprivate let _isAvailable: () -> Bool - fileprivate let _id: () -> UUID - fileprivate let _name: () -> String - fileprivate let _secrets: () -> [AnySecret] - fileprivate let _sign: (Data, AnySecret) throws -> Data - fileprivate var sink: AnyCancellable? + private let _isAvailable: () -> Bool + private let _id: () -> UUID + private let _name: () -> String + private let _secrets: () -> [AnySecret] + private let _sign: (Data, AnySecret) throws -> Data + private var sink: AnyCancellable? public init(_ secretStore: SecretStoreType) where SecretStoreType: SecretStore { base = secretStore @@ -47,8 +47,8 @@ public class AnySecretStore: SecretStore { public class AnySecretStoreModifiable: AnySecretStore, SecretStoreModifiable { - fileprivate let _create: (String, Bool) throws -> Void - fileprivate let _delete: (AnySecret) throws -> Void + private let _create: (String, Bool) throws -> Void + private let _delete: (AnySecret) throws -> Void public init(modifiable secretStore: SecretStoreType) where SecretStoreType: SecretStoreModifiable { _create = { try secretStore.create(name: $0, requiresAuthentication: $1) } diff --git a/SecretKit/Common/SecretStoreList.swift b/SecretKit/Common/SecretStoreList.swift index 885332a..0263a76 100644 --- a/SecretKit/Common/SecretStoreList.swift +++ b/SecretKit/Common/SecretStoreList.swift @@ -5,7 +5,7 @@ public class SecretStoreList: ObservableObject { @Published public var stores: [AnySecretStore] = [] @Published public var modifiableStore: AnySecretStoreModifiable? - fileprivate var sinks: [AnyCancellable] = [] + private var sinks: [AnyCancellable] = [] public init() { } @@ -28,7 +28,7 @@ public class SecretStoreList: ObservableObject { extension SecretStoreList { - fileprivate func addInternal(store: AnySecretStore) { + private func addInternal(store: AnySecretStore) { stores.append(store) let sink = store.objectWillChange.sink { self.objectWillChange.send() diff --git a/SecretKit/SecureEnclave/SecureEnclaveStore.swift b/SecretKit/SecureEnclave/SecureEnclaveStore.swift index 5baf358..a0e00a2 100644 --- a/SecretKit/SecureEnclave/SecureEnclaveStore.swift +++ b/SecretKit/SecureEnclave/SecureEnclaveStore.swift @@ -14,7 +14,7 @@ extension SecureEnclave { } public let id = UUID() public let name = NSLocalizedString("Secure Enclave", comment: "Secure Enclave") - @Published public fileprivate(set) var secrets: [Secret] = [] + @Published public private(set) var secrets: [Secret] = [] public init() { DistributedNotificationCenter.default().addObserver(forName: .secretStoreUpdated, object: nil, queue: .main) { _ in @@ -107,7 +107,7 @@ extension SecureEnclave { extension SecureEnclave.Store { - fileprivate func reloadSecrets(notify: Bool = true) { + private func reloadSecrets(notify: Bool = true) { secrets.removeAll() loadSecrets() if notify { @@ -115,7 +115,7 @@ extension SecureEnclave.Store { } } - fileprivate func loadSecrets() { + private func loadSecrets() { let attributes = [ kSecClass: kSecClassKey, kSecAttrKeyType: SecureEnclave.Constants.keyType, @@ -139,7 +139,7 @@ extension SecureEnclave.Store { secrets.append(contentsOf: wrapped) } - fileprivate func savePublicKey(_ publicKey: SecKey, name: String) throws { + private func savePublicKey(_ publicKey: SecKey, name: String) throws { let attributes = [ kSecClass: kSecClassKey, kSecAttrKeyType: SecureEnclave.Constants.keyType, @@ -178,8 +178,8 @@ extension SecureEnclave { extension SecureEnclave { enum Constants { - fileprivate static let keyTag = "com.maxgoedjen.secretive.secureenclave.key".data(using: .utf8)! as CFData - fileprivate static let keyType = kSecAttrKeyTypeECSECPrimeRandom + static let keyTag = "com.maxgoedjen.secretive.secureenclave.key".data(using: .utf8)! as CFData + static let keyType = kSecAttrKeyTypeECSECPrimeRandom } } diff --git a/SecretKit/SmartCard/SmartCardStore.swift b/SecretKit/SmartCard/SmartCardStore.swift index 6d01ce7..2fdbe4d 100644 --- a/SecretKit/SmartCard/SmartCardStore.swift +++ b/SecretKit/SmartCard/SmartCardStore.swift @@ -11,10 +11,10 @@ extension SmartCard { // TODO: Read actual smart card name, eg "YubiKey 5c" @Published public var isAvailable: Bool = false public let id = UUID() - public fileprivate(set) var name = NSLocalizedString("Smart Card", comment: "Smart Card") - @Published public fileprivate(set) var secrets: [Secret] = [] - fileprivate let watcher = TKTokenWatcher() - fileprivate var tokenID: String? + public private(set) var name = NSLocalizedString("Smart Card", comment: "Smart Card") + @Published public private(set) var secrets: [Secret] = [] + private let watcher = TKTokenWatcher() + private var tokenID: String? public init() { tokenID = watcher.nonSecureEnclaveTokens.first @@ -83,12 +83,12 @@ extension SmartCard { extension SmartCard.Store { - fileprivate func smartcardRemoved(for tokenID: String? = nil) { + private func smartcardRemoved(for tokenID: String? = nil) { self.tokenID = nil reloadSecrets() } - fileprivate func reloadSecrets() { + private func reloadSecrets() { DispatchQueue.main.async { self.isAvailable = self.tokenID != nil self.secrets.removeAll() @@ -96,7 +96,7 @@ extension SmartCard.Store { } } - fileprivate func loadSecrets() { + private func loadSecrets() { guard let tokenID = tokenID else { return } // Hack to read name if there's only one smart card let slotNames = TKSmartCardSlotManager().slotNames diff --git a/Secretive/Views/ContentView.swift b/Secretive/Views/ContentView.swift index 31b5bf7..aa23966 100644 --- a/Secretive/Views/ContentView.swift +++ b/Secretive/Views/ContentView.swift @@ -9,9 +9,9 @@ struct ContentView Void)? - @State fileprivate var active: AnySecret.ID? - @State fileprivate var showingDeletion = false - @State fileprivate var deletingSecret: AnySecret? + @State private var active: AnySecret.ID? + @State private var showingDeletion = false + @State private var deletingSecret: AnySecret? var body: some View { VStack { @@ -115,7 +115,7 @@ struct ContentView: View { @State var confirm = "" - fileprivate var dismissalBlock: (Bool) -> () + private var dismissalBlock: (Bool) -> () init(secret: StoreType.SecretType, store: StoreType, dismissalBlock: @escaping (Bool) -> ()) { self.secret = secret From bc25576c1f1051f87b71fa1797fca10839488ce1 Mon Sep 17 00:00:00 2001 From: Max Goedjen Date: Sat, 27 Jun 2020 18:45:22 -0700 Subject: [PATCH 20/20] Add installation instructions (#102) Fixes #101 --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index 0a24778..4f5282c 100644 --- a/README.md +++ b/README.md @@ -30,6 +30,10 @@ For Macs without Secure Enclaves, you can configure a Smart Card (such as a Yubi ## Getting Started +### Installation + +You can download the latest release over on the [Releases Page](https://github.com/maxgoedjen/secretive/releases) + ### FAQ There's a [FAQ here](FAQ.md).