I'm working on a daemon that's going to be controled from launchd. I would like to use a secure enclave key to back the private key this daemon is going to be using to communicate with an external service. I have same simple code put together for how to generate that key:
#include <CoreFoundation/CoreFoundation.h>
#include <Security/Security.h>
void generateSeKey(SecKeyRef *publicKey, SecKeyRef *privateKey) {
OSStatus rc;
CFErrorRef err = NULL;
SecAccessControlRef access = NULL;
CFMutableDictionaryRef params = NULL;
CFMutableDictionaryRef privateKeyAttrs = NULL;
CFMutableDictionaryRef publicKeyAttrs = NULL;
access = SecAccessControlCreateWithFlags(kCFAllocatorDefault,
kSecAttrAccessibleAlways,
kSecAccessControlPrivateKeyUsage,
&err);
if (err != NULL) {
return;
}
params = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFCopyStringDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
CFDictionarySetValue(params, kSecAttrKeyType, kSecAttrKeyTypeECSECPrimeRandom);
CFDictionarySetValue(params, kSecAttrTokenID, kSecAttrTokenIDSecureEnclave);
privateKeyAttrs = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFCopyStringDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
CFDictionarySetValue(privateKeyAttrs, kSecAttrIsPermanent, kCFBooleanFalse);
CFDictionarySetValue(privateKeyAttrs, kSecAttrAccessControl, access);
CFRelease(access);
CFDictionarySetValue(params, kSecPrivateKeyAttrs, privateKeyAttrs);
CFRelease(privateKeyAttrs);
// Set keys to NULL before calling
*publicKey = NULL;
*privateKey = NULL;
rc = SecKeyGeneratePair(params, publicKey, privateKey);
CFRelease(params);
if (rc != errSecSuccess) {
printf("Get error from SecKeyGeneratePair: %d\n", rc);
}
}
int main(int argc, char** argv) {
SecKeyRef publicKey, privateKey;
generateSeKey(&publicKey, &privateKey);
if (publicKey == NULL) {
printf("Public key is NULL\n");
} else {
printf("Public key generated.\n");
}
if (privateKey == NULL) {
printf("Public key is NULL\n");
} else {
printf("Private key generated.\n");
}
return 0;
}
This code works just fine when I compile and run it (either as myself or as root):
$ gcc simple.c -o se -framework Security -framework CoreFoundation
$ ./se
Public key generated.
Private key generated.
$ sudo ./se
Public key generated.
Private key generated.
However, I have discovered that when the process is launched via launchd (or via logging into a proper root login session) the SecKeyGeneratePair call returns errSecInternal.
$ sudo su -
# cd /tmp/src/se
# ./se
Get error from SecKeyGeneratePair: -26276
Public key is NULL
Public key is NULL
Checking out the system log, I see a few errors like this coming from the setoken process:
> initWithExistingContext -> (null), Error Domain=NSCocoaErrorDomain Code=4099 "The connection to service named com.apple.CoreAuthentication.agent was invalidated." UserInfo={NSDebugDescription=The connection to service named com.apple.CoreAuthentication.agent was invalidated.}
I gather something about the login session is important to making this work, but it's not totally clear to me what it is. So the short question is: is it possible to use a secure enclave key from a launchd daemon process? And if so, what am I doing wrong?