Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[RFC] Keychain-backed settings #536

Draft
wants to merge 3 commits into
base: main
Choose a base branch
from
Draft

Conversation

PBHDK
Copy link
Contributor

@PBHDK PBHDK commented Feb 29, 2024

Hi @maxgoedjen !

As promised in #524 , here's my current progress in the form of an RFC pull request. I was hoping to get some feedback and make sure that I'm running in the right direction 🙂

This pull request adds:

  • An API for storing to and reading from the keychain via simple get and set functions.
  • Replace the use of UserDefaults in JustUpdatedChecker.swift.
  • A settings screen which can be accessed via File -> Settings... or CMD + ,.
  • The ability to disable the SSH comment in the new settings screen by selecting "None".

Here's how the Settings looks:

Screenshot 2024-02-29 at 20 19 45

Concrete questions I have:

  • What styles should I add for the SSH comments?
  • How should the different SSH comment styles be presented? I guess it would make sense to add some kind of preview.
  • What else belongs in the settings screen?
  • Did I understand the keychain suggestion correctly? Should it be used somewhere, or is the API enough for now?
  • Should the settings have an "About" or "Acknowledgements" tab?
  • Re: how UserDefaults are replaced inJustUpdatedChecker.swift, is this how it's supposed to work? There is a slight reinterpretation of the keychain functionality happening here since the keychain thinks that we're storing a password, which strictly speaking isn't the case 🙂

Copy link
Owner

@maxgoedjen maxgoedjen left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey @PBHDK – made a first review pass here. I think your fundamental approach here is solid, but currently the stuff that needs to be in secure store is in defaults, and the stuff in defaults needs to be in secure store ;)

}

extension SettingsStore {
static func set(key: String, value: String) -> Bool {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's make these non-static (and maybe switch over to square bracket syntax) and inject the store through environmentObject like the other stores are here https://github.com/maxgoedjen/secretive/blob/main/Sources/Secretive/App.swift#L27

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep!

@@ -4,6 +4,7 @@ import SecretKit
struct SecretDetailView<SecretType: Secret>: View {

@State var secret: SecretType
@AppStorage("com.maxgoedjen.Secretive.commentStyle") var style: CommentStyle = .keyAndHost
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is just going to be pulling directly from UserDefaults, we want this plugged into the SettingsStore instead.

Comment on lines -18 to -20
let lastBuild = UserDefaults.standard.object(forKey: Constants.previousVersionUserDefaultsKey) as? String ?? "None"
let lastBuild = SettingsStore.get(key: Constants.previousVersionUserDefaultsKey) ?? "None"
let currentBuild = Bundle.main.infoDictionary!["CFBundleShortVersionString"] as! String
UserDefaults.standard.set(currentBuild, forKey: Constants.previousVersionUserDefaultsKey)
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's keep these in UserDefaults as-is for now, these don't need to be in secure storage.

Comment on lines +46 to +51
switch style {
case CommentStyle.none:
keyWriter.openSSHString(secret: secret, comment: "")
default:
keyWriter.openSSHString(secret: secret, comment: "\(dashedKeyName)@\(dashedHostName)")
}
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
switch style {
case CommentStyle.none:
keyWriter.openSSHString(secret: secret, comment: "")
default:
keyWriter.openSSHString(secret: secret, comment: "\(dashedKeyName)@\(dashedHostName)")
}
switch style {
case .none:
keyWriter.openSSHString(secret: secret, comment: "")
case .keyAndHost:
keyWriter.openSSHString(secret: secret, comment: "\(dashedKeyName)@\(dashedHostName)")
}

@@ -42,9 +43,13 @@ struct SecretDetailView<SecretType: Secret>: View {
}

var keyString: String {
keyWriter.openSSHString(secret: secret, comment: "\(dashedKeyName)@\(dashedHostName)")
switch style {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As this method gets more complicated, it MIGHT make sense to move it into OpenSSHKeyWriter – just instead of taking a comment: String parameter, we define that enum inside OpenSSHKeyWriter and pass an enum value commentStyle: CommentStyle

Copy link
Contributor Author

@PBHDK PBHDK Apr 8, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

dashedKeyName and dashedHostName are both defined in SecretDetailView.swift. That means we would also have to move those into OpenSSHKeyWriter.swift too, yes?

EDIT: forgot to tag you, @maxgoedjen 🙂

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@maxgoedjen just giving this a polite bump in case it got lost 🙂

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah they're just there since I think that's the only place that used them til now - feel free to move them somewhere more appropriate

Comment on lines +1 to +8
//
// SettingsView.swift
// Secretive
//
// Created by Paul Heidekrüger on 05.02.24.
// Copyright © 2024 Max Goedjen. All rights reserved.
//

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
//
// SettingsView.swift
// Secretive
//
// Created by Paul Heidekrüger on 05.02.24.
// Copyright © 2024 Max Goedjen. All rights reserved.
//

Comment on lines +1 to +8
//
// SettingsHelper.swift
// Secretive
//
// Created by Paul Heidekrüger on 27.02.24.
// Copyright © 2024 Max Goedjen. All rights reserved.
//

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
//
// SettingsHelper.swift
// Secretive
//
// Created by Paul Heidekrüger on 27.02.24.
// Copyright © 2024 Max Goedjen. All rights reserved.
//

}

extension SettingsStore {
static func set(key: String, value: String) -> Bool {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably makes sense to have the core get/set methods her expose Data directly (and maybe have a couple thin wrapper methods for string).

import Foundation

class SettingsStore {
static let service = "com.maxgoedjen.Secretive"
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

kSecAttrAccount as String: key,
kSecAttrServer as String: service,
kSecValueData as String: valueData]
// FIXME: Make this non-blocking as described here: https://developer.apple.com/documentation/security/1401659-secitemadd
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The only way you'll really get around this is by caching these values. I'm not super worried about this in this context, assuming you're not seeing any noticeable chug from it.

@maxgoedjen
Copy link
Owner

Answering your questions from PR:

  • What styles should I add for the SSH comments?
    I put a few in comments, but I think a few variations of user@host, user@secretive.host, secretive@host, etc probably make sense. If we can figure out a way to make it an actual format string with a few components the user can rearrange, I'd love that, but may be a little out of scope.

  • How should the different SSH comment styles be presented? I guess it would make sense to add some kind of preview.
    A preview is a great idea. Having a CopyableView below the editor with a dummy secret (or one from their actual store) and what the comment would look like would be cool.

  • What else belongs in the settings screen?
    Mainly we just want the comment style there for now. If/when I can figure out a CLI interface, enabling it will also happen there.

  • Did I understand the keychain suggestion correctly? Should it be used somewhere, or is the API enough for now?
    Kinda – commented in the code, but I think it's basically just backwards now. Comment style goes in keychain, update checker sticks with user defaults.

  • Should the settings have an "About" or "Acknowledgements" tab?
    macOS convention is to have that be a different thing, it's already there in Secretive->About Secretive, so let's keep that there for now.

  • Re: how UserDefaults are replaced inJustUpdatedChecker.swift, is this how it's supposed to work? There is a slight reinterpretation of the keychain functionality happening here since the keychain thinks that we're storing a password, which strictly speaking isn't the case 🙂
    Commented on that there, but basically we don't want to be using this for updated checker. That being said, GenericPassword is a verrrrrry broad keychain category, it's okay to store whatever in there. Doesn't have to be a literal password.

@PBHDK
Copy link
Contributor Author

PBHDK commented Mar 4, 2024

Thanks so much for the detailed comments, @maxgoedjen !

I'll try to address them ASAP, but it might end up being next week since I have to prioritise some other work in the next few days.

PBHDK added a commit to PBHDK/secretive that referenced this pull request Apr 5, 2024
subscripts and make settingsStore an environmentObject

Fixes: maxgoedjen#536 (comment)
PBHDK added a commit to PBHDK/secretive that referenced this pull request Apr 6, 2024
PBHDK added a commit to PBHDK/secretive that referenced this pull request Apr 9, 2024
subscripts and make settingsStore an environmentObject

Fixes: maxgoedjen#536 (comment)
PBHDK added a commit to PBHDK/secretive that referenced this pull request Apr 9, 2024
@ChristopherA
Copy link

We too have been puzzling on a number of best practices for SSH keys. In particular, we believe key separation not only for each computer, but separation of keys "key usage/proof purpose" is critical, and thus you should NOT use the same key for auth & signing. This doesn't seem like something Secretive can support right now.

My document is still an early draft, but will give you some of our thinking on naming conventions for ssh key files.

https://gist.github.com/ChristopherA/3d6a2f39c4b623a1a287b3fb7e0aa05b

Regarding key comments, GitHub doesn't seem to preserve them when uploaded, however, I do try to make the "title" field in GitHub match the SSH comment. You can see my own GitHub signing keys at https://api.github.com/users/ChristopherA/ssh_signing_keys along with their titles/comments. They are not completely consistent yet as I'm still working on our conventions to support best practices there.

(If you are interested, this will give you a clue where we hope to go with ssh signatures in the more long-term https://gist.github.com/ChristopherA/dcf963c3849bc0d67b35c215efd15f86 & https://github.com/BlockchainCommons/ssh-envelope-python )

SSH signing and improving digital attribution and provenance is an important project for us this quarter, so welcome any advice.

@maxgoedjen
Copy link
Owner

you should NOT use the same key for auth & signing. This doesn't seem like something Secretive can support right now.

That's (assuming I'm understanding you correctly) definitely not the case. I personally have a different key for SSH access and signing git commits (which you can see in my profile). Is there something about this flow that doesn't work for you? Essentially I just have one key that I tell git to use for signing commits, and another one for SSH access.

@ChristopherA
Copy link

That's (assuming I'm understanding you correctly) definitely not the case. I personally have a different key for SSH access and signing git commits (which you can see in my profile). Is there something about this flow that doesn't work for you? Essentially I just have one key that I tell git to use for signing commits, and another one for SSH access.

Yes, that is what is required. I think the docs for configuring this are not clear. When searching for an answer, I saw somewhere (in an issues or PR?) that you needed to put the same public key in both the auth and signing.

@ChristopherA
Copy link

I found the errant doc:

https://github.com/maxgoedjen/secretive/pull/439/files

  1. Add it to your GitHub account as an Auth and a Signing key, i.e. add it twice.

@maxgoedjen
Copy link
Owner

Yeah that's not merged ;) – there's no requirement on Secretive's side for that to be the case at all, it's totally agnostic of key usage. Some people may find that to be the most convenient setup, but there's no requirement to that effect.

@ChristopherA
Copy link

Thanks. We are still experimenting with various proof-of-concepts to support best practices, but when we are satisfied, I'll consider a improved PR for docs here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants