Compare commits

..

15 Commits

Author SHA1 Message Date
8bbf489146 Relaunch agent if updated (#86) 2020-04-04 15:16:31 -07:00
30148ee3a4 Revert "Xcode 11.4 changes"
This reverts commit c306801149.
2020-04-03 00:44:13 -07:00
c306801149 Xcode 11.4 changes 2020-04-02 18:22:59 -07:00
9c60a0b7dd Merge branch 'master' of github.com:maxgoedjen/secretive 2020-04-02 00:54:14 -07:00
5b38ef00c1 Fix #84 2020-04-02 00:53:55 -07:00
ccbf92785d Fix padding bug (#83) 2020-04-02 00:43:45 -07:00
f1e8e43f62 Update FAQ.md 2020-04-01 22:11:45 -07:00
7b1563f167 Update FAQ.md 2020-03-31 23:11:04 -07:00
cca070dbe4 Update FAQ.md 2020-03-30 23:44:38 -07:00
31e51e45c1 Move types to common (#81) 2020-03-29 17:26:43 -07:00
4bfb3a0012 Rephrase access control. 2020-03-28 18:08:54 -07:00
a0ed880386 Fix #80. 2020-03-27 19:24:37 -07:00
92b9648e04 FAQ (#76) 2020-03-25 23:35:02 -07:00
4a2a342670 Create FUNDING.yml 2020-03-24 22:16:04 -07:00
1d147d8e34 Update README.md 2020-03-24 22:04:49 -07:00
12 changed files with 145 additions and 17 deletions

1
.github/FUNDING.yml vendored Normal file
View File

@ -0,0 +1 @@
github: maxgoedjen

37
FAQ.md Normal file
View File

@ -0,0 +1,37 @@
# 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:
Tower - [Instructions](https://www.git-tower.com/help/mac/integration/environment)
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).
### 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 have a non-security related bug
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
Sweet! Please check out the [contributing guidelines](CONTRIBUTING.md) and go from there.

View File

@ -14,7 +14,7 @@ The most common setup for SSH keys is just keeping them on disk, guarded by prop
### Access Control ### 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.
<img src="/.github/readme/touchid.png" alt="Screenshot of Secretive authenticating with Touch ID"> <img src="/.github/readme/touchid.png" alt="Screenshot of Secretive authenticating with Touch ID">
@ -30,15 +30,11 @@ For Macs without Secure Enclaves, you can configure a Smart Card (such as a Yubi
## Getting Started ## Getting Started
### Setup for Third Party Apps ### FAQ
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`. There's a [FAQ here](FAQ.md).
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) ### Auditable Build Process
### Security Considerations
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. 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.
@ -52,4 +48,4 @@ Beacuse secrets in the Secure Enclave are not exportable, they are not able to b
## Security ## 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."

View File

@ -113,8 +113,17 @@ extension Agent {
let rawLength = rawRepresentation.count/2 let rawLength = rawRepresentation.count/2
let r = rawRepresentation[0..<rawLength] // Check if we need to pad with 0x00 to prevent certain
let s = rawRepresentation[rawLength...] // ssh servers from thinking r or s is negative
let paddingRange: ClosedRange<UInt8> = 0x80...0xFF
var r = Data(rawRepresentation[0..<rawLength])
if paddingRange ~= r.first! {
r.insert(0x00, at: 0)
}
var s = Data(rawRepresentation[rawLength...])
if paddingRange ~= s.first! {
s.insert(0x00, at: 0)
}
var signatureChunk = Data() var signatureChunk = Data()
signatureChunk.append(writer.lengthAndData(of: r)) signatureChunk.append(writer.lengthAndData(of: r))

View File

@ -49,8 +49,15 @@ class AgentTests: XCTestCase {
_ = inner.readNextChunk() _ = inner.readNextChunk()
let signedData = inner.readNextChunk() let signedData = inner.readNextChunk()
let rsData = OpenSSHReader(data: signedData) let rsData = OpenSSHReader(data: signedData)
let r = rsData.readNextChunk() var r = rsData.readNextChunk()
let s = 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 var rs = r
rs.append(s) rs.append(s)
let signature = try! P256.Signing.ECDSASignature(rawRepresentation: rs) let signature = try! P256.Signing.ECDSASignature(rawRepresentation: rs)

View File

@ -10,6 +10,8 @@
50020BB024064869003D4025 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50020BAF24064869003D4025 /* AppDelegate.swift */; }; 50020BB024064869003D4025 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50020BAF24064869003D4025 /* AppDelegate.swift */; };
5018F54F24064786002EB505 /* Notifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5018F54E24064786002EB505 /* Notifier.swift */; }; 5018F54F24064786002EB505 /* Notifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5018F54E24064786002EB505 /* Notifier.swift */; };
50524B442420969E008DBD97 /* OpenSSHWriterTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50524B432420969D008DBD97 /* OpenSSHWriterTests.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 */; }; 50617D8323FCE48E0099B055 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50617D8223FCE48E0099B055 /* AppDelegate.swift */; };
50617D8523FCE48E0099B055 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50617D8423FCE48E0099B055 /* ContentView.swift */; }; 50617D8523FCE48E0099B055 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50617D8423FCE48E0099B055 /* ContentView.swift */; };
50617D8723FCE48E0099B055 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 50617D8623FCE48E0099B055 /* Assets.xcassets */; }; 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 = "<group>"; }; 50020BAF24064869003D4025 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
5018F54E24064786002EB505 /* Notifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Notifier.swift; sourceTree = "<group>"; }; 5018F54E24064786002EB505 /* Notifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Notifier.swift; sourceTree = "<group>"; };
50524B432420969D008DBD97 /* OpenSSHWriterTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OpenSSHWriterTests.swift; sourceTree = "<group>"; }; 50524B432420969D008DBD97 /* OpenSSHWriterTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OpenSSHWriterTests.swift; sourceTree = "<group>"; };
50571E0224393C2600F76F6C /* JustUpdatedChecker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JustUpdatedChecker.swift; sourceTree = "<group>"; };
50571E0424393D1500F76F6C /* LaunchAgentController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LaunchAgentController.swift; sourceTree = "<group>"; };
50617D7F23FCE48E0099B055 /* Secretive.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Secretive.app; sourceTree = BUILT_PRODUCTS_DIR; }; 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 = "<group>"; }; 50617D8223FCE48E0099B055 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
50617D8423FCE48E0099B055 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = "<group>"; }; 50617D8423FCE48E0099B055 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = "<group>"; };
@ -346,6 +350,15 @@
/* End PBXFrameworksBuildPhase section */ /* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */ /* Begin PBXGroup section */
504BA92D243171F20064740E /* Types */ = {
isa = PBXGroup;
children = (
50617DCA23FCECA10099B055 /* Secret.swift */,
50617DC623FCE4EA0099B055 /* SecretStore.swift */,
);
path = Types;
sourceTree = "<group>";
};
50617D7623FCE48D0099B055 = { 50617D7623FCE48D0099B055 = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
@ -418,8 +431,6 @@
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
50617DAA23FCE4AB0099B055 /* SecretKit.h */, 50617DAA23FCE4AB0099B055 /* SecretKit.h */,
50617DCA23FCECA10099B055 /* Secret.swift */,
50617DC623FCE4EA0099B055 /* SecretStore.swift */,
5099A02C23FE56D70062B6F2 /* Common */, 5099A02C23FE56D70062B6F2 /* Common */,
50617DCC23FCECEE0099B055 /* SecureEnclave */, 50617DCC23FCECEE0099B055 /* SecureEnclave */,
5099A02523FE34DE0062B6F2 /* SmartCard */, 5099A02523FE34DE0062B6F2 /* SmartCard */,
@ -505,6 +516,8 @@
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
508A58B2241ED2180069DC07 /* AgentStatusChecker.swift */, 508A58B2241ED2180069DC07 /* AgentStatusChecker.swift */,
50571E0224393C2600F76F6C /* JustUpdatedChecker.swift */,
50571E0424393D1500F76F6C /* LaunchAgentController.swift */,
); );
path = Controllers; path = Controllers;
sourceTree = "<group>"; sourceTree = "<group>";
@ -522,6 +535,7 @@
5099A02C23FE56D70062B6F2 /* Common */ = { 5099A02C23FE56D70062B6F2 /* Common */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
504BA92D243171F20064740E /* Types */,
5068389F2415EA4F00F55094 /* Erasers */, 5068389F2415EA4F00F55094 /* Erasers */,
506838A42415EA6800F55094 /* OpenSSH */, 506838A42415EA6800F55094 /* OpenSSH */,
5068389D241471CD00F55094 /* SecretStoreList.swift */, 5068389D241471CD00F55094 /* SecretStoreList.swift */,
@ -908,10 +922,12 @@
files = ( files = (
50C385A9240B636500AF2719 /* SetupView.swift in Sources */, 50C385A9240B636500AF2719 /* SetupView.swift in Sources */,
50617D8523FCE48E0099B055 /* ContentView.swift in Sources */, 50617D8523FCE48E0099B055 /* ContentView.swift in Sources */,
50571E0324393C2600F76F6C /* JustUpdatedChecker.swift in Sources */,
50617DD223FCEFA90099B055 /* PreviewStore.swift in Sources */, 50617DD223FCEFA90099B055 /* PreviewStore.swift in Sources */,
508A58B3241ED2180069DC07 /* AgentStatusChecker.swift in Sources */, 508A58B3241ED2180069DC07 /* AgentStatusChecker.swift in Sources */,
50C385A52407A76D00AF2719 /* SecretDetailView.swift in Sources */, 50C385A52407A76D00AF2719 /* SecretDetailView.swift in Sources */,
5099A02423FD2AAA0062B6F2 /* CreateSecretView.swift in Sources */, 5099A02423FD2AAA0062B6F2 /* CreateSecretView.swift in Sources */,
50571E0524393D1500F76F6C /* LaunchAgentController.swift in Sources */,
50B8550D24138C4F009958AC /* DeleteSecretView.swift in Sources */, 50B8550D24138C4F009958AC /* DeleteSecretView.swift in Sources */,
50BB046B2418AAAE00D6E079 /* EmptyStoreView.swift in Sources */, 50BB046B2418AAAE00D6E079 /* EmptyStoreView.swift in Sources */,
50731669241E00C20023809E /* NoticeView.swift in Sources */, 50731669241E00C20023809E /* NoticeView.swift in Sources */,

View File

@ -17,6 +17,7 @@ class AppDelegate: NSObject, NSApplicationDelegate {
}() }()
let updater = Updater() let updater = Updater()
let agentStatusChecker = AgentStatusChecker() let agentStatusChecker = AgentStatusChecker()
let justUpdatedChecker = JustUpdatedChecker()
func applicationDidFinishLaunching(_ aNotification: Notification) { func applicationDidFinishLaunching(_ aNotification: Notification) {
let contentView = ContentView(storeList: storeList, updater: updater, agentStatusChecker: agentStatusChecker, runSetupBlock: { self.runSetup(sender: nil) }) 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 newMenuItem.isEnabled = true
} }
runSetupIfNeeded() runSetupIfNeeded()
relaunchAgentIfNeeded()
} }
func applicationDidBecomeActive(_ notification: Notification) { func applicationDidBecomeActive(_ notification: Notification) {
@ -89,6 +91,12 @@ extension AppDelegate {
} }
} }
func relaunchAgentIfNeeded() {
if agentStatusChecker.running && justUpdatedChecker.justUpdated {
LaunchAgentController().relaunch()
}
}
} }
extension AppDelegate { extension AppDelegate {

View File

@ -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"
}
}

View File

@ -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)
}
}

View File

@ -1,6 +1,5 @@
import Foundation import Foundation
import SwiftUI import SwiftUI
import ServiceManagement
struct SetupView: View { struct SetupView: View {
@ -117,7 +116,7 @@ struct SetupStepCommandView: View {
extension SetupView { extension SetupView {
func installLaunchAgent() -> Bool { func installLaunchAgent() -> Bool {
SMLoginItemSetEnabled("com.maxgoedjen.Secretive.SecretAgent" as CFString, true) LaunchAgentController().install()
} }
func markAsDone() -> Bool { func markAsDone() -> Bool {