2 * Copyright (c) 2013-2014 Apple Inc. All Rights Reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
21 * @APPLE_LICENSE_HEADER_END@
29 #include <utilities/SecAKSWrappers.h>
30 #include <utilities/SecCFWrappers.h>
31 #include <utilities/SecCFError.h>
33 #if TARGET_OS_SIMULATOR
34 # define change_notification "com.apple.will.never.happen"
35 #elif TARGET_OS_IPHONE
36 # include <MobileKeyBag/MobileKeyBag.h>
37 # define change_notification kMobileKeyBagLockStatusNotificationID
39 # include <AppleKeyStoreEvents.h>
40 # define change_notification kAppleKeyStoreLockStatusNotificationID
42 # error "unsupported target platform"
45 #if TARGET_OS_OSX && TARGET_HAS_KEYSTORE
47 const keybag_handle_t keybagHandle
= session_keybag_handle
;
48 #elif TARGET_HAS_KEYSTORE // iOS, but not simulator
49 const keybag_handle_t keybagHandle
= device_keybag_handle
;
52 #if TARGET_HAS_KEYSTORE
53 const AKSAssertionType_t lockAssertType
= kAKSAssertTypeOther
;
56 CFGiblisGetSingleton(dispatch_queue_t
, GetKeybagAssertionQueue
, sUserKeyBagAssertionLockQueue
, ^{
57 *sUserKeyBagAssertionLockQueue
= dispatch_queue_create("AKS Lock Assertion Queue", NULL
);
60 #if TARGET_HAS_KEYSTORE
61 static uint32_t count
= 0;
63 const char * const kUserKeybagStateChangeNotification
= change_notification
;
65 bool SecAKSUserKeybagHoldLockAssertion(uint64_t timeout
, CFErrorRef
*error
){
66 #if !TARGET_HAS_KEYSTORE
69 __block kern_return_t status
= kAKSReturnSuccess
;
71 dispatch_sync(GetKeybagAssertionQueue(), ^{
73 secnotice("lockassertions", "Requesting lock assertion for %lld seconds", timeout
);
74 status
= aks_assert_hold(keybagHandle
, lockAssertType
, timeout
);
77 if (status
== kAKSReturnSuccess
)
80 return SecKernError(status
, error
, CFSTR("Kern return error"));
81 #endif /* !TARGET_HAS_KEYSTORE */
84 bool SecAKSUserKeybagDropLockAssertion(CFErrorRef
*error
){
85 #if !TARGET_HAS_KEYSTORE
88 __block kern_return_t status
= kAKSReturnSuccess
;
90 dispatch_sync(GetKeybagAssertionQueue(), ^{
91 if (count
&& (--count
== 0)) {
92 secnotice("lockassertions", "Dropping lock assertion");
93 status
= aks_assert_drop(keybagHandle
, lockAssertType
);
97 return SecKernError(status
, error
, CFSTR("Kern return error"));
98 #endif /* !TARGET_HAS_KEYSTORE */
100 bool SecAKSDoWithUserBagLockAssertion(CFErrorRef
*error
, dispatch_block_t action
)
102 #if !TARGET_HAS_KEYSTORE
106 // Acquire lock assertion, ref count?
109 uint64_t timeout
= 60ull;
110 if (SecAKSUserKeybagHoldLockAssertion(timeout
, error
)) {
112 status
= SecAKSUserKeybagDropLockAssertion(error
);
115 #endif /* !TARGET_HAS_KEYSTORE */
118 bool SecAKSDoWithUserBagLockAssertionSoftly(dispatch_block_t action
)
120 #if !TARGET_HAS_KEYSTORE
124 uint64_t timeout
= 60ull;
125 CFErrorRef localError
= NULL
;
127 bool heldAssertion
= SecAKSUserKeybagHoldLockAssertion(timeout
, &localError
);
128 if (!heldAssertion
) {
129 secnotice("secaks", "SecAKSDoWithUserBagLockAssertionSoftly: failed to get assertion proceeding bravely: %@", localError
);
130 CFReleaseNull(localError
);
136 (void)SecAKSUserKeybagDropLockAssertion(&localError
);
137 CFReleaseNull(localError
);
140 #endif /* !TARGET_HAS_KEYSTORE */
144 CFDataRef
SecAKSCopyBackupBagWithSecret(size_t size
, uint8_t *secret
, CFErrorRef
*error
) {
145 #if !TARGET_HAS_KEYSTORE
148 CFDataRef result
= NULL
;
149 void *keybagBytes
= NULL
;
152 keybag_handle_t backupKeybagHandle
= -1;
155 require_quiet(SecRequirementError(0 <= size
&& size
<= INT_MAX
, error
, CFSTR("Invalid size: %zu"), size
), fail
);
157 ret
= aks_create_bag(secret
, (int) size
, kAppleKeyStoreAsymmetricBackupBag
, &backupKeybagHandle
);
159 require_quiet(SecKernError(ret
, error
, CFSTR("bag allocation failed: %d"), ret
), fail
);
161 ret
= aks_save_bag(backupKeybagHandle
, &keybagBytes
, &keybagSize
);
163 require_quiet(SecKernError(ret
, error
, CFSTR("save bag failed: %d"), ret
), fail
);
165 ret
= aks_unload_bag(backupKeybagHandle
);
167 if (ret
!= KERN_SUCCESS
) {
168 secerror("unload bag failed: %d", ret
);
171 result
= CFDataCreate(kCFAllocatorDefault
, keybagBytes
, keybagSize
);
173 require_quiet(SecAllocationError(result
, error
, CFSTR("Bag CFData Allocation Failed")), fail
);
182 keyclass_t
SecAKSSanitizedKeyclass(keyclass_t keyclass
)
184 keyclass_t sanitizedKeyclass
= keyclass
;
185 #if TARGET_HAS_KEYSTORE
186 if (keyclass
> key_class_last
) {
187 // idea is that AKS may return a keyclass value with extra bits above key_class_last from aks_wrap_key, but we only keep metadata keys for the canonical key classes
188 // so just sanitize all our inputs to the canonical values
189 sanitizedKeyclass
= keyclass
& key_class_last
;
190 secinfo("SanitizeKeyclass", "sanitizing request for keyclass %d to keyclass %d", keyclass
, sanitizedKeyclass
);
193 return sanitizedKeyclass
;