Wanted to understand the Keychain behaviour in case Device is rebooted and Locked state:
SecItemCopyMatching: this API throws error saying Keychain locked(-25800), AS EXPECTED
SecItemDelete: this API doesn't throws any error on deletion(how this is happened even though device is locked): but as per logs looks deleted the key ?? NOT SURE WHAT's happening in the issue case as explained below, later key not found from the keychain
SecItemAdd: this API also throws error saying Keychain locked(-25800), AS EXPECTED
Here I see that below is executed without any error, meant Delete API succeeded without any error(device is still locked)
OSStatus status = SecItemDelete((__bridge CFDictionaryRef)query);
But below SAVE API failed with error KEYCHAIN LOCKED but it is not same for Delete API as mentioned above on the same time
status = SecItemAdd((__bridge CFDictionaryRef)query, NULL);
ERROR:
2025-06-17 12:12:30.265810 +0530 securityd WAVE PTX[235]/1#12 LF=0 add Error Domain=NSOSStatusErrorDomain Code=-25308 "ks_crypt: e00002e2 failed to 'od' item (class 7, bag: 0) Access to item attempted while keychain is locked (hibernation?)." UserInfo={numberOfErrorsDeep=0, NSDescription=ks_crypt: e00002e2 failed to 'od' item (class 7, bag: 0) Access to item attempted while keychain is locked (hibernation?).} default HK
So the Query is Does SecItemDelete should also fail(shouldn't delete the key) in case of Device in locked state(First reboot), right? : I dont find any logs indicating the success or failure in this case
If this is not deleted at this moment, then next client queries the same key post device First Unlock says key not exist
`2025-06-17 12:15:11.172081 +0530 symptomsd First unlock occurred, proceeding default
2025-06-17 12:15:28.914700 +0530 WAVE PTX <ALA_ERROR> [DB_ENCRYPTION] Error retrieving key from the Keychain: -25300 default
`
Code: keyIdentifier is same for both case
+ (BOOL)saveSymetricKeyToKeychain:(NSData *)symmetricKeyData keyIdentifier:(NSString *)keyIdentifier
{
NSString *appGrpIdentifier = [SQLiteExtUtil getAppGroupIdentifier];
NSDictionary *query = @{
(__bridge id)kSecClass: (__bridge id)kSecClassKey,
(__bridge id)kSecAttrApplicationTag: keyIdentifier,
(__bridge id)kSecValueData: symmetricKeyData,
(__bridge id)kSecAttrKeyClass: (__bridge id)kSecAttrKeyClassSymmetric,
(__bridge id)kSecAttrAccessible: (__bridge id)kSecAttrAccessibleAfterFirstUnlock,
(__bridge id)kSecAttrAccessGroup: appGrpIdentifier
};
// First, check if the key already exists in the Keychain. To handle downgrade client upgrade usecases
OSStatus status = SecItemDelete((__bridge CFDictionaryRef)query);
if (status != errSecSuccess && status != errSecItemNotFound) {
NSLog(@"<ALA_ERROR> [DB_ENCRYPTION] Error deleting existing key: %d", (int)status);
return NO;
}
// Now add the key to the Keychain
status = SecItemAdd((__bridge CFDictionaryRef)query, NULL);
if (status == errSecSuccess) {
NSLog(@"[DB_ENCRYPTION] Key successfully stored in the Keychain");
return YES;
} else {
NSLog(@"<ALA_ERROR> [DB_ENCRYPTION] Error storing key in the Keychain: %d", (int)status);
return NO;
}
}
// Retrieve a symmetric key from the Keychain
+ (NSData *)retrieveSymmetricKeyFromKeychain:(NSString *)keyIdentifier
{
NSDictionary *query = @{
(__bridge id)kSecClass: (__bridge id)kSecClassKey,
(__bridge id)kSecAttrApplicationTag: keyIdentifier,
(__bridge id)kSecReturnData: (__bridge id)kCFBooleanTrue,
(__bridge id)kSecAttrKeyClass: (__bridge id)kSecAttrKeyClassSymmetric
};
CFTypeRef result = NULL;
OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef)query, &result);
if (status == errSecSuccess) {
NSLog(@"[DB_ENCRYPTION] Key successfully retrieved from the Keychain");
return (__bridge NSData *)result;
} else {
NSLog(@"<ALA_ERROR> [DB_ENCRYPTION] Error retrieving key from the Keychain: %d", (int)status);
return nil;
}
}