Search code examples
objective-cnsdictionaryios14ios15

iOS CFMutableDictionaryRef crashes on iOS 15 and newer but still work on iOS14


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


Solution

  • 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.