SecItemCopyMatching return -25308 when device is locked

We generate key pair and import cert with access flag kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly in our application. We then have notification extension that access shared keychain to retrieve the identity when device is locked. We use SecItemCopyMatching to get all SecIdentity with its attributes as below:

let queryDict: [String: Any] = [
            kSecClass as String: kSecClassIdentity,
            kSecReturnAttributes as String: true,
            kSecReturnRef as String: true,
            kSecMatchLimit as String: kSecMatchLimitAll
        ]
var oss = SecItemCopyMatching(queryDict as CFDictionary, &attrs)

When the code runs on notification extension with locked device, it failed on some devices (not on all devices) with error -25308. The sysdiagnose shows:

default	securityd	2024-03-14 10:49:13.844996 -0400	129	0x110115	SecDbKeychainItemV7: cannot decrypt metadata key because the keychain is locked (-25308)
default	securityd	2024-03-14 10:49:13.845036 -0400	129	0x110091	NSExtension[2222]/1#7 LF=0 copy_matching Error Domain=NSOSStatusErrorDomain Code=-25308 "ks_crypt: e00002e2 failed to 'od' item (class 6, bag: 0) Access to item attempted while keychain is locked." UserInfo={numberOfErrorsDeep=0, NSDescription=ks_crypt: e00002e2 failed to 'od' item (class 6, bag: 0) Access to item attempted while keychain is locked.}

We also try to use access flag kSecAttrAccessibleAlwaysThisDeviceOnly, but it doesn't make difference. Since securityd reports "cannot decrypt metadata key because keychain is locked". I wonder if it is because we query attributes for all identity. Is it a bug on Security framework?

Thanks, Ying

Accepted Reply

i actually already asked user to try a build with change to use kSecAttrAccessibleAlwaysThisDeviceOnly when generating key pair & importing cert, but the problem still exist.

Did this test delete all the previous identities before adding this new one? If not, please try this.

My goal of this test is to see whether this problem is tied to the specific private key that you previously generated.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

Replies

Hey hey, just yesterday I wrote up Investigating hard-to-reproduce keychain problems, which will likely come in handy here (-:

You wrote:

it failed on some devices (not on all devices)

I’d like to clarify this. On the devices affected by this problem, does it always fail? Or is there some intermittent aspect to this?

Also, do you have access to the device that fails like this? Or are you in touch with a user to whom you might send a TestFlight build?

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

Hi Quinn, On the devices affected by this problem, it always happen. I don’t have access to the devices but i can try to borrow them and i can send Testflight build to the user. i actually already asked user to try a build with change to use kSecAttrAccessibleAlwaysThisDeviceOnly when generating key pair & importing cert, but the problem still exist.

Hi Quinn,

I read your post Investigate hard-to-reproduce keychain problems

In our code we log the OSStatus when SecItem calls fail, that tells the error -25308 is from SecItemCopyMathing that try to query all SecIdentity with their attributes. The logs shows below including our extension (NSExtension) logs.

Our extension is notification service extension, when push notification arrives, the extension needs to access keychain to get identity. The reason we query all identities because we need to get identity that certificate matched the cached public key hash. On MacOS, we actually query kSecClassCertificate with kSecAttrPublicKeyHash to get exactly matched cert and then use SecIdentityCreateWithCertificate to get identity. But SecIdentityCreateWithCertificate API only available on MacOS and we don't know how to get SecIdentity with SecCertificate on iOS. So we made code to query all SecIdentity with their attributes and then compare the public key hash to find the matched one.

Do you think the failure is due to query attributes? I ask this because sysdiag logs -25308 with "cannot decrypt metadata key".

It is also very confused, because the working device always work, and non-working device always failed with -25308. Plus, if we keep the non-working device unlocked, the problem goes away. So it tells the problem is due to keychain access control when device is locked. But even using kSecAttrAccessibleAlwaysThisDeviceOnly during import won't solve the problem.

default	NSExtension	2024-03-14 10:49:13.843947 -0400	2222	0x110111	System Keychain Always Supported set via feature flag to disabled
default	NSExtension	2024-03-14 10:49:13.843955 -0400	2222	0x110111	[0x11de3a710] activating connection: mach=true listener=false peer=false name=com.apple.securityd
default	NSExtension	2024-03-14 10:49:13.844064 -0400	2222	0x110111	Adding securityd connection to pool, total now 1
default	securityd	2024-03-14 10:49:13.844114 -0400	129	0x110091	[0x7d8a0c1c0] activating connection: mach=false listener=false peer=true name=com.apple.securityd.peer.0x7d8a0c1c0
default	securityd	2024-03-14 10:49:13.844996 -0400	129	0x110115	SecDbKeychainItemV7: cannot decrypt metadata key because the keychain is locked (-25308)
default	securityd	2024-03-14 10:49:13.845036 -0400	129	0x110091	NSExtension[2222]/1#7 LF=0 copy_matching Error Domain=NSOSStatusErrorDomain Code=-25308 "ks_crypt: e00002e2 failed to 'od' item (class 6, bag: 0) Access to item attempted while keychain is locked." UserInfo={numberOfErrorsDeep=0, NSDescription=ks_crypt: e00002e2 failed to 'od' item (class 6, bag: 0) Access to item attempted while keychain is locked.}
error	NSExtension	2024-03-14 10:49:13.846383 -0400	2222	0x110111	E/ KeyChainKeyStore.swift:1580 getSecIdentity() SecItemCopyMatching error: User interaction is not allowed.

i actually already asked user to try a build with change to use kSecAttrAccessibleAlwaysThisDeviceOnly when generating key pair & importing cert, but the problem still exist.

Did this test delete all the previous identities before adding this new one? If not, please try this.

My goal of this test is to see whether this problem is tied to the specific private key that you previously generated.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

Hi Quinn,

You are right, it is related to previously generated key. There is legacy key that was generated without setting any access flag, so by default it can only be accessible when device is locked. When we enumerate all identities on locked device, it caused the problem. After delete all previous identities, the problem goes away.

Thank you so much for the help! Ying

Add a Comment