-1

I wrote two functions in Objective-C to generate a key pair with method SecKeyCreateRandomKey. In one I use the NSDictionary in another CFMutableDictionaryRef.

The function where I use NSDictionary work perfect on iOS 14 and 15. The code is bellow.

- (SecKeyRef) generateKeyPair:(NSString*) tag {
NSLog(@"NativeCrypto: Start");

CFErrorRef error = NULL;

SecAccessControlRef access =
        SecAccessControlCreateWithFlags(kCFAllocatorDefault,
                                        kSecAttrAccessibleWhenUnlocked,
                                        kSecAccessControlPrivateKeyUsage,
                                        nil);
NSDictionary *attributes =
    @{
      (id)kSecAttrKeyType: (id)kSecAttrKeyTypeECSECPrimeRandom,
      (id)kSecAttrTokenID: (id)kSecAttrTokenIDSecureEnclave,
      (id)kSecAttrKeySizeInBits: @(256), // secure enclave *ONLY* support 256 elliptic (secp256r1)
      (id)kSecPrivateKeyAttrs:
          @{ (id)kSecAttrIsPermanent:    @(YES),
             (id)kSecAttrApplicationTag: tag,
             (id)kSecAttrAccessControl:  (__bridge id)access}
      };

SecKeyRef privateKey = SecKeyCreateRandomKey((__bridge CFDictionaryRef)attributes, &error);

NSLog(@"NativeCrypto: privateKey %@", privateKey);
NSLog(@"NativeCrypto: error %@", error);
NSLog(@"NativeCrypto: End");

return privateKey;}@end

But the function where I use CFMutableDictionaryRef doesn't work on iOS 15. The code is bellow.

void bar() {
NSString* keyTag = [NSBundle.mainBundle.bundleIdentifier stringByAppendingString:@".private"];

// Secure Enclave store ONLY 256-bit elliptic curve private keys.
//
// It means that we can generate ONLY kSecAttrKeyTypeEC and kSecAttrKeySizeInBits = 256.
SecAccessControlRef access = SecAccessControlCreateWithFlags(kCFAllocatorDefault, kSecAttrAccessibleWhenUnlocked, kSecAccessControlPrivateKeyUsage, NULL);

CFMutableDictionaryRef privateKeyAttrs = CFDictionaryCreateMutable(kCFAllocatorDefault, 2, NULL, NULL);
CFDictionaryAddValue(privateKeyAttrs, kSecAttrIsPermanent, kCFBooleanTrue);
CFDictionaryAddValue(privateKeyAttrs, kSecAttrApplicationTag, CFBridgingRetain([keyTag dataUsingEncoding:NSUTF8StringEncoding]));
CFDictionaryAddValue(privateKeyAttrs, kSecAttrAccessControl, access);

CFMutableDictionaryRef attributes = CFDictionaryCreateMutable(kCFAllocatorDefault, 3, NULL, NULL);
CFDictionaryAddValue(attributes, kSecAttrKeyType, kSecAttrKeyTypeECSECPrimeRandom);
CFDictionaryAddValue(attributes, kSecAttrKeySizeInBits, CFBridgingRetain([NSNumber numberWithInt:256]));
CFDictionaryAddValue(attributes, kSecAttrTokenID, kSecAttrTokenIDSecureEnclave);
CFDictionaryAddValue(attributes, kSecPrivateKeyAttrs, privateKeyAttrs);

CFErrorRef error;

SecKeyCreateRandomKey(attributes, &error);
if (error == NULL) {
    NSLog(@"generateKeyToSecureEnclave() - error is null");
} else {
    NSError* nsError = (NSError*)CFBridgingRelease(error);
    NSLog(@"generateKeyToSecureEnclave() - error not null %d", (int)nsError.code);
}

CFBridgingRelease(attributes);

NSLog(@"==Keys generated success==");}

Tell me why the second version of the code may not work on iOS 15 given that it works on iOS 14? It is necessary to understand the reason for the incompatibility.

Stacktrace

#0  0x00007fff2019fc38 in objc_retain ()
#1  0x00007fff5a7684c3 in -[TKSEPKey 
initWithAttributes:authContext:error:] ()
#2  0x00007fff5a77136a in -[TKSEPClientTokenSession 
createObjectWithAttributes:error:] ()
#3  0x00007fff211f1a01 in -[SecCTKKey initWithAttributes:error:]()
#4  0x00007fff21214f45 in SecKeyGeneratePair ()
#5  0x00007fff21218884 in SecKeyCreateRandomKey ()
#6  0x000000010c101cac in bar at test.m:33

The line where the application crashes:

enter image description here

Aleksey Mukovin
  • 110
  • 1
  • 5
  • 1
    Could you define what's not working? Is a value nil? is `nsError` not nil? – Larme Jan 31 '22 at 11:09
  • The application crashes when calling the SecKeyCreateRandomKey method in the second case. Attached stacktrace to the question – Aleksey Mukovin Jan 31 '22 at 11:43
  • I'm not familiar with `CFDictionaryCreateMutable`, but is the `capacity` value correct? It's keys/values count -1, should it be keys/values count instead? You might pass `0` instead of `2` & `3` to not specify capacity. – Larme Jan 31 '22 at 11:53
  • 1
    Never say "not working", as it is meaningless. If the issue is a crash, show the crash log. And which line crashes? – matt Jan 31 '22 at 11:55
  • Also, for the tag, in the Objective-C modern code you set a `NSString`, but in the `CFRef` code you set `NSData` (well, equivalent) `CFBridgingRetain([keyTag dataUsingEncoding:NSUTF8StringEncoding])` Shouldn't you set a `CFStringRef` instead? – Larme Jan 31 '22 at 11:58
  • @Larme I set `CFStringRef` instead `CFBridgingRetain([keyTag dataUsingEncoding:NSUTF8StringEncoding])`. `CFStringRef* keyTag2 = @"com.secureenclavetest.test1.private"; CFDictionaryAddValue(privateKeyAttrs, kSecAttrApplicationTag, keyTag2);` And also I set the `capacity` to 0. But it didn't help. – Aleksey Mukovin Feb 01 '22 at 02:56
  • @matt I added a screenshot with the crashes line. But there are no crash logs. – Aleksey Mukovin Feb 01 '22 at 03:02
  • Well I would definitely start by fixing the code in lines 31-3 that the warnings are about. – matt Feb 01 '22 at 04:00
  • Thanks everyone! The question is closed. – Aleksey Mukovin Feb 01 '22 at 08:02

1 Answers1

0

Use kCFTypeDictionaryKeyCallBacks, kCFTypeDictionaryValueCallBacks as parameters while you create CFDictionaryCreateMutable.

CFMutableDictionaryRef privateKeyAttrs = CFDictionaryCreateMutable(kCFAllocatorDefault, 3, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);

CFMutableDictionaryRef attributes = CFDictionaryCreateMutable(kCFAllocatorDefault, 3, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);

CFDictionaryCreateMutable

Tested with iOS 14 and iOS 15.

Aleksey Mukovin
  • 110
  • 1
  • 5