how to store secret key in/for system extension

Hi. I have a private cryptographic key that I want to generate and store for use by the system extension only (a network extension NETransparentProxyProvider). The ideal properties I want is:

  • only accessible by extension
  • never leave extension
  • not be accessible by root user or other apps

Here is what I have tried so far (by/within the system extension):

  • app data container / local storage: this works, but is accessible by root user
  • app data shared container (storage): this works, but also acccessible by root user
  • system keyring: works, but also accesible by root user

System extension by itself does not seem to be able to store/load secrets in app protected keyring.

The host application however can store in app protected keyring.... So I though, let's use an app group (as access group) and have it like this shared between host and (system) extension... but nop... (system) extension cannot access the secret...

Ok... so than I thought:

  • manual low-level XPC calls.... Also that doesn't work, got something almost to work but seemed to require an entire 3rd (launchd/daemon) service.... way to complex for what I want... also seems that as a root user I can use debug tools to also access it

There is however the SendMessage/HandleMessage thing available for TransparentProxy.... that does work... but

(1) also doesn't seem the most secure (2) the docs clearly state cannot rely on that for this state as the system extension can be started while the host app is not active.... (e.g. at startup)

So that is not a solution either....

I went in so many different directions and rabbit holes in the last days.... this feels like a lot harder than it should be? How do other VPN/Proxy like solutions store secrets that are unique to an extension???? I am hoping there is something available here that I am simply missing despite all my effort... any guidance greatly appreciated...

Answered by DTS Engineer in 885731022

I want to clarify a few points from both your original post and Albert’s response.


You’re using the term keyring, which is not a thing on Apple platforms. Rather, the equivalent thing is called the keychain. Using the right term will help, for example, when you go searching for documentation.


macOS has two keychain implementations:

  • The file-based keychain
  • The data protection keychain

We generally recommend the data protection keychain. However, you’re building a Network Extension transparent proxy, and those are generally packaged as a system extension [1]. Sysexen are roughly equivalent to a launchd daemon, and cannot use the data protection keychain. They can only use a file-based keychain, typically the System keychain.

TN3137 On Mac keychain APIs and implementations talks about this stuff in much more detail.


When talking about extensions on Apple platforms, it’s important to get your terminology straight.

  • The application in which the extension is embedded is called the container application.
  • The host application is the application using the extension.

In this case, the host application isn’t actually an application, but rather the system itself.


have it like this shared between [container app] and (system) extension

This won’t work because:

  • App group containers are per user.
  • Your container app and sysex are running as different users (a normal user and root, respectively). See Network Extension Provider Packaging for more on that.

On the IPC front, you wrote:

There is however the SendMessage/HandleMessage thing available for TransparentProxy

Yeah, don’t go down that path. The app-provider messaging feature is useful on iOS, where XPC is very limited. On macOS, you’ll want to use XPC. This is something else I cover in Network Extension Provider Packaging.

Also that doesn't work

You’ve probably bumped into a sandbox restriction. Your sysex must be sandboxed which limits the XPC endpoint names it can use. The solution is to scope that within an app group. Network Extension Provider Packaging talks about that.

IMPORTANT App groups have a long and complex history on macOS. For the full story, see App Groups: macOS vs iOS: Working Towards Harmony.


Coming back to the big picture, you wrote:

this feels like a lot harder than it should be?

To understand that difficulty you need to think carefully about your goals:

  • You want to create a resource (your cryptographic key) on the user’s device.
  • Such that your program can use it.
  • But the user can’t access it.

That set of requirements means you’re effectively building a DRM system. DRM systems are hard, especially on relatively open platforms like the Mac.

Note The keychain isn’t designed to help with DRM. It’s designed to protect the users’s secrets from attackers, not to protect your secrets from the user.

Any DRM system represents a trade-off between security, code complexity, and reliability. The more secure you make it, the more complex the code, and the greater likelihood of long-term binary compatibility. DTS doesn’t officially support DRM development [2]. That’s because our goal is to keep developers on the path to long-term binary compatibility, and complex DRM systems routinely cause such problems.

So, you have a decision to make: How important is protecting this key from the user? You could put it in the System keychain, where it’ll be reasonably protected from other code but not protected from the user at all. Or you could take extra steps to protect it from the user, like scrambling the key with another key that’s baked into your app.

Of course, an advanced user could disassemble your code and work out what that scrambling key is. So, you protect the scrambling key with the Secure Enclave, so that only your app can descramble it. But that’s not perfect security because an even more advanced user could disable SIP and extract the key from your process’s memory. So you could detect that…

And you can see where we’re going here. The is exactly the DRM path, and you need to think carefully before going too far down it.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

[1] See TN3134 Network Extension provider deployment

[2] We do support developers using Apple’s DRM systems, like FairPlay.

Thanks so much for the post.

Have you considered to use the keychain https://aninterestingwebsite.com/documentation/security/keychain-services the intersection of System Extensions, the Keychain/Security framework (which is heavily user-session oriented), and the NetworkExtension framework.

I would personally recommend to look at Data Protection Keychain with Entitlement Enforcement. To guarantee that a private key never leaves the extension and cannot be extracted, you must generate the key.

When your extension reboots or reconnects, you query the keychain for the reference. Because you are using kSecUseDataProtectionKeychain = true and an access group. I would recommend you to look into that as well https://aninterestingwebsite.com/documentation/security/ksecusedataprotectionkeychain

You asked how other VPNs do this. Usually, I believe they do not generate the key inside the extension.

Since your requirement is to generate the key uniquely for the extension I recommend to looks at the By using Secure Enclave (kSecAttrTokenIDSecureEnclave) combined with the Data Protection Keychain and a Keychain Access Group. https://aninterestingwebsite.com/documentation/security/ksecattrtokenidsecureenclave

Hope this gives you an overview of what you need to implement without knowing your requirements in detail. I’m sure if you can provide those requirements more engineers will be happy to provide their opinion.

Good luck.

Albert
  Worldwide Developer Relations.

I want to clarify a few points from both your original post and Albert’s response.


You’re using the term keyring, which is not a thing on Apple platforms. Rather, the equivalent thing is called the keychain. Using the right term will help, for example, when you go searching for documentation.


macOS has two keychain implementations:

  • The file-based keychain
  • The data protection keychain

We generally recommend the data protection keychain. However, you’re building a Network Extension transparent proxy, and those are generally packaged as a system extension [1]. Sysexen are roughly equivalent to a launchd daemon, and cannot use the data protection keychain. They can only use a file-based keychain, typically the System keychain.

TN3137 On Mac keychain APIs and implementations talks about this stuff in much more detail.


When talking about extensions on Apple platforms, it’s important to get your terminology straight.

  • The application in which the extension is embedded is called the container application.
  • The host application is the application using the extension.

In this case, the host application isn’t actually an application, but rather the system itself.


have it like this shared between [container app] and (system) extension

This won’t work because:

  • App group containers are per user.
  • Your container app and sysex are running as different users (a normal user and root, respectively). See Network Extension Provider Packaging for more on that.

On the IPC front, you wrote:

There is however the SendMessage/HandleMessage thing available for TransparentProxy

Yeah, don’t go down that path. The app-provider messaging feature is useful on iOS, where XPC is very limited. On macOS, you’ll want to use XPC. This is something else I cover in Network Extension Provider Packaging.

Also that doesn't work

You’ve probably bumped into a sandbox restriction. Your sysex must be sandboxed which limits the XPC endpoint names it can use. The solution is to scope that within an app group. Network Extension Provider Packaging talks about that.

IMPORTANT App groups have a long and complex history on macOS. For the full story, see App Groups: macOS vs iOS: Working Towards Harmony.


Coming back to the big picture, you wrote:

this feels like a lot harder than it should be?

To understand that difficulty you need to think carefully about your goals:

  • You want to create a resource (your cryptographic key) on the user’s device.
  • Such that your program can use it.
  • But the user can’t access it.

That set of requirements means you’re effectively building a DRM system. DRM systems are hard, especially on relatively open platforms like the Mac.

Note The keychain isn’t designed to help with DRM. It’s designed to protect the users’s secrets from attackers, not to protect your secrets from the user.

Any DRM system represents a trade-off between security, code complexity, and reliability. The more secure you make it, the more complex the code, and the greater likelihood of long-term binary compatibility. DTS doesn’t officially support DRM development [2]. That’s because our goal is to keep developers on the path to long-term binary compatibility, and complex DRM systems routinely cause such problems.

So, you have a decision to make: How important is protecting this key from the user? You could put it in the System keychain, where it’ll be reasonably protected from other code but not protected from the user at all. Or you could take extra steps to protect it from the user, like scrambling the key with another key that’s baked into your app.

Of course, an advanced user could disassemble your code and work out what that scrambling key is. So, you protect the scrambling key with the Secure Enclave, so that only your app can descramble it. But that’s not perfect security because an even more advanced user could disable SIP and extract the key from your process’s memory. So you could detect that…

And you can see where we’re going here. The is exactly the DRM path, and you need to think carefully before going too far down it.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

[1] See TN3134 Network Extension provider deployment

[2] We do support developers using Apple’s DRM systems, like FairPlay.

Thank you very much Quinn! That clarifies a lot... Using the system keychain is for sure the easiest path...

The cryptographic secret I am trying to protect is in this case a local CA private key for developer security products to block malware etc...

I suppose it shouldn't matter that the root user can access it as the CA is isolated and specific to that device, so not like there's a security risk there and I suppose if you have an attacker with root access that is not the user, I suppose you anyway have bigger issues than this...

Also thank you for correcting me with the terminology..

So to recap?

For a system extension, that has such a secret specific and only used by that extension (e.g. the ca private key), is the system keychain the recommended place to store this?

Also a second question. I was trying to use the secure enclave to get a private key so that i can perhaps encrypt/decrypt data stored in the system extension app container but also here I was running into errors... Can you clarify Quinn if it is possible for a system extension to use/own a private (persistent) key via the secure enclave? As I keep running into one error after another? Are there official docs/examples on that?

how to store secret key in/for system extension
 
 
Q