I'm trying to modify Chromium to generate a key pair in the Secure Enclave on MacOS, but I'm always getting the following error
OSStatus error -34018 - failed to generate asymmetric keypair
from the following code.
CFErrorRef access_error = NULL;
SecAccessControlRef access_control = SecAccessControlCreateWithFlags(
kCFAllocatorDefault,
kSecAttrAccessibleWhenUnlockedThisDeviceOnly,
kSecAccessControlPrivateKeyUsage,
&access_error
);
NSDictionary *attributes = @{
(__bridge id)kSecAttrKeyType: (__bridge id)kSecAttrKeyTypeECSECPrimeRandom,
(__bridge id)kSecAttrKeySizeInBits: @256,
(__bridge id)kSecAttrTokenID: (__bridge id)kSecAttrTokenIDSecureEnclave,
(__bridge id)kSecPrivateKeyAttrs: @{
(__bridge id)kSecAttrIsPermanent: @YES,
(__bridge id)kSecAttrAccessControl: (__bridge id)access_control
}
};
CFErrorRef creation_error = NULL;
SecKeyRef key_ref = SecKeyCreateRandomKey((__bridge CFDictionaryRef)attributes, &creation_error);
if (creation_error != NULL)
LOG(ERROR) << "Failed to create key: " << CFErrorCopyDescription(creation_error);
-34018 seems to map to errSecMissingEntitlement, but I'm not sure what the missing entitlement is. Chromium comes with several entitlements configured, and I've signed and notarized the app, but it still does not work with the installed app from the notarized .dmg file.
Running
codesign -d --entitlements - Chromium.app
returns
[Dict]
[Key] com.apple.application-identifier
[Value]
[String] <redacted-teamid>.<redacted-bundleid>
[Key] keychain-access-groups
[Value]
[Array]
[String] <redacted-teamid>.<redacted-bundleid>.devicetrust
[String] <redacted-teamid>.<redacted-bundleid>.webauthn
Checking the embedded .provisionprofile (and the DER encoded profile) per these instructions also shows these entitlements in the notarized .app.
The same code works in a local Xcode project, so I figure it must be how I've configured Chromium, but I can't figure out what the missing piece is.
I am able to create keys in the keychain, in Chromium, if I remove the kSecAttrAccessControl and kSecAttrTokenID attributes. It's only when I'm trying to access the secure enclave, it seems.
I've tried multiple things to find the issue:
I've read the docs
* Protecting keys with the Secure Enclave | Apple Developer Documentation
I've tried setting different keychain-access-groups, using the default, and other suggestions, described here
errSecMissingEntitlement | Apple Developer Documentation
I've searched online and found similar issues, but none of the solutions helped
SecItemAdd results in 34018 | Apple Developer Forums
Keychain error -34018 (errSecMissi… | Apple Developer Forums
Error -34018 errSecMissingEntitlem… | Apple Developer Forums
Troubleshooting -34018 Keychain Er… | Apple Developer Forums
I've read through Apple's OSS Distribution to see the implementation of SecCreateRandomKey
Security/SecKey.cpp at 154ef3d9d6f57f0374aa5d6c4b412e8653c1eebe · apple-oss-distributions/Security (github.com)
I've reviewed the two existing implementations in Chromium, and tried to directly call them instead of rolling my own
https://source.chromium.org/chromium/chromium/src/+/main:device/fido/mac/credential_store.mm
https://source.chromium.org/chromium/chromium/src/+/main:chrome/browser/enterprise/connectors/device_trust/key_management/core/mac/secure_enclave_helper_impl.mm
Does anyone see what the issue could be?
This is all running on my M2 MacBook Pro on Ventura 13.3, if it matters.
Post
Replies
Boosts
Views
Activity
I have a sample iOS app in Xcode that I run in the iOS 17.5 Simulator. It creates a WKWebView and configures a proxy via the ProxyConfiguration API, it works as expected unless the proxy tries to establish mTLS. It seems there is no way to handle the client certificate request when using a proxy. If I navigate to a page that requests mTLS without a proxy configured, it works as expected. Here is a minimal repro:
#import "ViewController.h"
#import <WebKit/WebKit.h>
@import Foundation;
@import WebKit;
@interface ViewController () <WKNavigationDelegate>
@property (nonatomic,strong) WKWebView* webView;
@property (nonatomic, strong) WKWebViewConfiguration * webConfig;
@end
@implementation ViewController
- (void)loadView {
[super loadView];
nw_protocol_options_t tls_options = nw_tls_create_options();
sec_protocol_options_t sec_options = nw_tls_copy_sec_protocol_options(tls_options);
sec_protocol_options_set_challenge_block(
sec_options,
^(sec_protocol_metadata_t metadata, sec_protocol_challenge_complete_t challenge_complete) {
NSLog(@"Inside of challenge block");
challenge_complete(nil);
},
dispatch_get_main_queue());
nw_endpoint_t proxy_endpoint =
nw_endpoint_create_host(GetHost(), GetPort());
nw_relay_hop_t relay =
nw_relay_hop_create(nil, proxy_endpoint, tls_options);
nw_proxy_config_t proxy_config =
nw_proxy_config_create_relay(relay, nil);
nw_proxy_config_add_match_domain(proxy_config, "api.ipify.org");
self.webConfig = [[WKWebViewConfiguration alloc] init];
self.webConfig.websiteDataStore = [WKWebsiteDataStore nonPersistentDataStore];
self.webConfig.websiteDataStore.proxyConfigurations = @[ proxy_config ];
self.webView = [[WKWebView alloc] initWithFrame:self.view.frame configuration:self.webConfig];
self.webView.navigationDelegate = self;
[self.view addSubview:self.webView];
}
- (void)viewDidLoad {
[super viewDidLoad];
NSLog(@"%s",__func__);
NSURL* url = [[NSURL alloc] initWithString:@"https://api.ipify.org"];
NSURLRequest* request = [[NSURLRequest alloc] initWithURL:url];
[self.webView loadRequest:request];
}
- (void)webView:(WKWebView *)webView didStartProvisionalNavigation:(WKNavigation *)navigation {
NSLog(@"%s",__func__);
}
- (void)webView:(WKWebView *)webView didFailProvisionalNavigation:(WKNavigation *)navigation withError:(NSError *)error {
NSLog(@"%s. Error %@",__func__,error);
}
- (void)webView:(WKWebView *)webView didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition, NSURLCredential *))completionHandler {
NSLog(@"%s",__func__);
NSLog(@"protection space: %@", challenge.protectionSpace.authenticationMethod);
completionHandler(NSURLSessionAuthChallengePerformDefaultHandling, nil);
}
@end
The logs for this code show:
-[ViewController viewDidLoad]
-[ViewController webView:didStartProvisionalNavigation:]
-[ViewController webView:didFailProvisionalNavigation:withError:]. Error Error Domain=NSURLErrorDomain Code=-1206 "The server “api.ipify.org” requires a client certificate."
If we don't set up the ProxyConfiguration and navigate to a site that requires mTLS, the logs look like this:
-[ViewController viewDidLoad]
-[ViewController webView:didReceiveAuthenticationChallenge:completionHandler:]
protection space: NSURLAuthenticationMethodServerTrust
-[ViewController webView:didReceiveAuthenticationChallenge:completionHandler:]
protection space: NSURLAuthenticationMethodClientCertificate
-[ViewController webView:didStartProvisionalNavigation:]
//...
Eventually the request fails but the key difference is that didReceiveAuthenticationChallenge was invoked. When using the ProxyConfiguration neither that function nor the block we set via sec_protocol_options_set_challenge_block were run.
I also tried to provide the client identity via sec_protocol_options_set_local_identity to no avail, and I've tried configuring these options too but they had no effect
sec_protocol_options_add_tls_application_protocol(sec_options, "h2");
sec_protocol_options_set_max_tls_protocol_version(sec_options, tls_protocol_version_TLSv13);
sec_protocol_options_set_peer_authentication_required(sec_options, true);
Am I missing something? Or is this a bug in the ProxyConfiguration API?