3

Introduction

Basically, how/where do I persist encryption keys my executable needs?

Let me explain how my executable looks like. It's basically a Swift script that is compiled using swift build --configuration=release --product=App.

Package.swift:

// swift-tools-version:5.3
import PackageDescription

let package = Package(
    name: "App",
    defaultLocalization: "en",
    platforms: [
        .macOS(.v10_15),
    ],
    products: [
        .executable(name: "App", targets: ["App"]),
    ],
    dependencies: [
        .package(url: "https://github.com/apple/swift-argument-parser", from: "1.0.1"),
        .package(url: "https://github.com/apple/swift-crypto", from: "2.0.0")
    ],
    targets: [
        .target(name: "App", dependencies: [
            .product(name: "ArgumentParser", package: "swift-argument-parser"),
            .product(name: "Crypto", package: "swift-crypto"),
        ]),
    ]
)

Some Ideas

  • I know that Apple provides a Keychain on macOS to store keys. The problem is that my executable is not signed in any way, so it'd be difficult to enforce that only my executable can access the stored keys. (That's what I think at least, correct me if I'm wrong)

Git seems to somehow store user credentials in Keychain. How do they do it? Is their way even secure?

  • The next idea I had was to store the keys in the user's home directory, like .ssh. That seems insecure. As far as I know, any app running on the system could then read the keys, right?

  • Another way to achieve this would be to encrypt the key using a password and then make the user enter their password every time the key is needed. This is the least preferable option because it is inconvenient.

  • The last option I thought of would be to sign my executable somehow. Is there any way to sign my executable so it can have it's own guarded space in the Keychain? What I need is a process to sign my executable without requiring an Apple Developer Account or any other central authority.

Question

How can I securely store encryption keys so only my executable (edit: and the user) can access them?

swift-lynx
  • 3,219
  • 3
  • 26
  • 45
  • When you say "so only my executable can access them," I assume you just want to protect the keys from other applications on the system? The user is already in control of that if you store it in keychain. Applications do not get access to all of keychain. See https://developer.apple.com/documentation/security for various tools available. There is no way to protect these keys from the user on their own machine if that's what you mean. You're free to sign your app with your own (non-Apple) cert and users can validate it by hand, but that's not particularly related to this issue. – Rob Napier Sep 29 '21 at 15:51
  • For self-signing apps, see the "Obtaining Your Signing Identities" in https://developer.apple.com/library/archive/documentation/Security/Conceptual/CodeSigningGuide/Procedures/Procedures.html#//apple_ref/doc/uid/TP40005929-CH4-SW1. These will be checked by installer packages when upgrading (and as I said can be checked by hand), but otherwise it's not incredibly useful. – Rob Napier Sep 29 '21 at 15:53
  • I mean safeguarding it from other apps on the system, not from the user. – swift-lynx Sep 29 '21 at 15:53
  • When self signing, does that allow me to store keys in Keychain so only my executable can access them? (and the user of course) – swift-lynx Sep 29 '21 at 15:54
  • See https://developer.apple.com/documentation/security/keychain_services/access_control_lists – Rob Napier Sep 29 '21 at 15:54
  • The user can always override you, though, and grant access. They'll get a pop-up indicating what item the (untrusted) app is trying to access. – Rob Napier Sep 29 '21 at 15:56
  • 1
    A popup before accessing the item works fine for my use case. I'll try to implement it that way then, thanks a lot. – swift-lynx Sep 29 '21 at 15:59
  • 1
    If your app touches sensitive information, I do recommend that you at least self-sign. This will allow the user to validate your app, and the system to recognize if your app has been modified or replaced (which all sound important for your uses). Apple's notarization service is also worth considering. It doesn't require you to submit your app for approval. It may not meet your needs, but it's worth investigating. https://developer.apple.com/documentation/security/notarizing_macos_software_before_distribution/customizing_the_notarization_workflow – Rob Napier Sep 29 '21 at 16:00
  • 2
    Okay, so if I self sign my app, it is protected against modifications. Then the user can allow my app access to specific keys via popup. The app's integrity is now check-able by user and system and the user can grant it access to specific keys. Now if my executable was modified or replaced, the system wouldn't allow access, because signing isn't valid anymore, except if the user allows again via popup, right? If so, that is exactly what I was looking for. Also, consider putting the information from your comments into an answer ;) – swift-lynx Sep 29 '21 at 16:07

1 Answers1

1

To protect your app from modification, codesign it. You can use a private key or use Apple's notarization service. This will ensure no one modifies your app or distributes an installer that tries to replace your app.

Keychain items your app creates can have access control lists, but even by default, the OS won't allow other apps to read your app's keychain items without being approved by the user. The user will receive a pop-up indicating the item the app is requesting.

So I believe your best bet is to sign your app, and store the data in Keychain. It should generally work as you want out of the box. But of course do a lot of testing. Generally these things fail-secure, so in most cases it won't leak any data to other apps. But you may get more pop-ups than you want the user to deal with if you make mistakes.

Rob Napier
  • 286,113
  • 34
  • 456
  • 610