]> git.saurik.com Git - apple/security.git/blob - secdxctests/KeychainXCTest.m
Security-58286.200.222.tar.gz
[apple/security.git] / secdxctests / KeychainXCTest.m
1 /*
2 * Copyright (c) 2018 Apple Inc. All Rights Reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
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
11 * file.
12 *
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.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24 #import "KeychainXCTest.h"
25 #import "SecDbKeychainItem.h"
26 #import "SecdTestKeychainUtilities.h"
27 #import "CKKS.h"
28 #import "SecDbKeychainItemV7.h"
29 #import "SecItemPriv.h"
30 #import "SecTaskPriv.h"
31 #import "server_security_helpers.h"
32 #import "SecItemServer.h"
33 #import "spi.h"
34 #import "SecDbKeychainSerializedItemV7.h"
35 #import "SecDbKeychainSerializedMetadata.h"
36 #import "SecDbKeychainSerializedSecretData.h"
37 #import "SecDbKeychainSerializedAKSWrappedKey.h"
38 #import "SecCDKeychain.h"
39 #import <utilities/SecCFWrappers.h>
40 #import <SecurityFoundation/SFEncryptionOperation.h>
41 #import <SecurityFoundation/SFCryptoServicesErrors.h>
42 #import <SecurityFoundation/SFKeychain.h>
43 #import <XCTest/XCTest.h>
44 #import <OCMock/OCMock.h>
45
46 #if USE_KEYSTORE
47
48 @interface SecDbKeychainItemV7 ()
49
50 + (SFAESKeySpecifier*)keySpecifier;
51
52 @end
53
54 @interface FakeAKSRefKey : NSObject <SecAKSRefKey>
55 @end
56
57 @implementation FakeAKSRefKey {
58 SFAESKey* _key;
59 }
60
61 - (instancetype)initWithKeybag:(keybag_handle_t)keybag keyclass:(keyclass_t)keyclass
62 {
63 if (self = [super init]) {
64 _key = [[SFAESKey alloc] initRandomKeyWithSpecifier:[[SFAESKeySpecifier alloc] initWithBitSize:SFAESKeyBitSize256] error:nil];
65 }
66
67 return self;
68 }
69
70 - (instancetype)initWithBlob:(NSData*)blob keybag:(keybag_handle_t)keybag
71 {
72 if (self = [super init]) {
73 _key = [[SFAESKey alloc] initWithData:blob specifier:[[SFAESKeySpecifier alloc] initWithBitSize:SFAESKeyBitSize256] error:nil];
74 }
75
76 return self;
77 }
78
79 - (NSData*)wrappedDataForKey:(SFAESKey*)key
80 {
81 SFAuthenticatedEncryptionOperation* encryptionOperation = [[SFAuthenticatedEncryptionOperation alloc] initWithKeySpecifier:[[SFAESKeySpecifier alloc] initWithBitSize:SFAESKeyBitSize256]];
82 return [NSKeyedArchiver archivedDataWithRootObject:[encryptionOperation encrypt:key.keyData withKey:_key error:nil] requiringSecureCoding:YES error:nil];
83 }
84
85 - (SFAESKey*)keyWithWrappedData:(NSData*)wrappedKeyData
86 {
87 SFAESKeySpecifier* keySpecifier = [[SFAESKeySpecifier alloc] initWithBitSize:SFAESKeyBitSize256];
88 SFAuthenticatedEncryptionOperation* encryptionOperation = [[SFAuthenticatedEncryptionOperation alloc] initWithKeySpecifier:keySpecifier];
89 NSData* keyData = [encryptionOperation decrypt:[NSKeyedUnarchiver unarchivedObjectOfClass:[SFAuthenticatedCiphertext class] fromData:wrappedKeyData error:nil] withKey:_key error:nil];
90 return [[SFAESKey alloc] initWithData:keyData specifier:keySpecifier error:nil];
91 }
92
93 - (NSData*)refKeyBlob
94 {
95 return _key.keyData;
96 }
97
98 @end
99
100 @implementation SFKeychainServerFakeConnection {
101 NSArray* _fakeAccessGroups;
102 }
103
104 - (void)setFakeAccessGroups:(NSArray*)fakeAccessGroups
105 {
106 _fakeAccessGroups = fakeAccessGroups.copy;
107 }
108
109 - (NSArray*)clientAccessGroups
110 {
111 return _fakeAccessGroups ?: @[@"com.apple.token"];
112 }
113
114 @end
115
116 @implementation KeychainXCTest {
117 id _keychainPartialMock;
118 CFArrayRef _originalAccessGroups;
119
120 }
121
122 @synthesize keychainPartialMock = _keychainPartialMock;
123
124 + (void)setUp
125 {
126 [super setUp];
127
128 SecCKKSDisable();
129 securityd_init(NULL);
130 }
131
132 - (void)setUp
133 {
134 [super setUp];
135
136 self.lockState = LockStateUnlocked;
137 self.allowDecryption = true;
138 self.didAKSDecrypt = NO;
139 self.simulateRolledAKSKey = NO;
140
141
142 self.keyclassUsedForAKSDecryption = 0;
143
144 self.keySpecifier = [[SFAESKeySpecifier alloc] initWithBitSize:SFAESKeyBitSize256];
145 [self setNewFakeAKSKey:[NSData dataWithBytes:"1234567890123456789012" length:32]];
146
147 [SecDbKeychainMetadataKeyStore resetSharedStore];
148
149 self.mockSecDbKeychainItemV7 = OCMClassMock([SecDbKeychainItemV7 class]);
150 [[[[self.mockSecDbKeychainItemV7 stub] andCall:@selector(fakeAKSEncryptWithKeybag:keyclass:keyData:outKeyclass:wrappedKey:error:) onObject:self] ignoringNonObjectArgs] aksEncryptWithKeybag:0 keyclass:0 keyData:[OCMArg any] outKeyclass:NULL wrappedKey:[OCMArg any] error:NULL];
151 [[[[self.mockSecDbKeychainItemV7 stub] andCall:@selector(fakeAKSDecryptWithKeybag:keyclass:wrappedKeyData:outKeyclass:unwrappedKey:error:) onObject:self] ignoringNonObjectArgs] aksDecryptWithKeybag:0 keyclass:0 wrappedKeyData:[OCMArg any] outKeyclass:NULL unwrappedKey:[OCMArg any] error:NULL];
152 [[[self.mockSecDbKeychainItemV7 stub] andCall:@selector(decryptionOperation) onObject:self] decryptionOperation];
153
154 // bring back with <rdar://problem/37523001>
155 // [[[self.mockSecDbKeychainItemV7 stub] andCall:@selector(isKeychainUnlocked) onObject:self] isKeychainUnlocked];
156
157 id refKeyMock = OCMClassMock([SecAKSRefKey class]);
158 [[[refKeyMock stub] andCall:@selector(alloc) onObject:[FakeAKSRefKey class]] alloc];
159
160 NSArray* partsOfName = [self.name componentsSeparatedByCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:@" ]"]];
161 secd_test_setup_temp_keychain([partsOfName[1] UTF8String], NULL);
162
163 _originalAccessGroups = SecAccessGroupsGetCurrent();
164 }
165
166 - (void)tearDown
167 {
168 [self.mockSecDbKeychainItemV7 stopMocking];
169 SecAccessGroupsSetCurrent(_originalAccessGroups);
170
171 [super tearDown];
172 }
173
174 - (bool)isKeychainUnlocked
175 {
176 return self.lockState == LockStateUnlocked;
177 }
178
179 - (id)decryptionOperation
180 {
181 return self.allowDecryption ? [[SFAuthenticatedEncryptionOperation alloc] initWithKeySpecifier:[SecDbKeychainItemV7 keySpecifier]] : nil;
182 }
183
184 - (bool)setNewFakeAKSKey:(NSData*)newKeyData
185 {
186 NSError* error = nil;
187 self.fakeAKSKey = [[SFAESKey alloc] initWithData:newKeyData specifier:self.keySpecifier error:&error];
188 XCTAssertNil(error, "Should be no error making a fake AKS key");
189 XCTAssertNotNil(self.fakeAKSKey, "Should have received a fake AKS key");
190 return true;
191 }
192
193 - (bool)fakeAKSEncryptWithKeybag:(keybag_handle_t)keybag
194 keyclass:(keyclass_t)keyclass
195 keyData:(NSData*)keyData
196 outKeyclass:(keyclass_t*)outKeyclass
197 wrappedKey:(NSMutableData*)wrappedKey
198 error:(NSError**)error
199 {
200 if (self.lockState == LockStateLockedAndDisallowAKS) {
201 if (error) {
202 *error = [NSError errorWithDomain:(__bridge NSString *)kSecErrorDomain code:errSecInteractionNotAllowed userInfo:NULL];
203 }
204 return false;
205 }
206
207 uint32_t keyLength = (uint32_t)keyData.length;
208 const uint8_t* keyBytes = keyData.bytes;
209
210 NSData* dataToEncrypt = [NSData dataWithBytes:keyBytes length:keyLength];
211 NSError* localError = nil;
212
213 SFAuthenticatedEncryptionOperation* encryptionOperation = [[SFAuthenticatedEncryptionOperation alloc] initWithKeySpecifier:self.keySpecifier];
214 encryptionOperation.authenticationCodeLength = 8;
215 SFAuthenticatedCiphertext* ciphertext = [encryptionOperation encrypt:dataToEncrypt withKey:self.fakeAKSKey error:&localError];
216
217 if (error) {
218 *error = localError;
219 }
220
221 if (ciphertext) {
222 void* wrappedKeyMutableBytes = wrappedKey.mutableBytes;
223 memcpy(wrappedKeyMutableBytes, ciphertext.ciphertext.bytes, 32);
224 memcpy(wrappedKeyMutableBytes + 32, ciphertext.initializationVector.bytes, 32);
225 memcpy(wrappedKeyMutableBytes + 64, ciphertext.authenticationCode.bytes, 8);
226
227 if (self.simulateRolledAKSKey && outKeyclass) {
228 *outKeyclass = keyclass | (key_class_last + 1);
229 } else if (outKeyclass) {
230 *outKeyclass = keyclass;
231 }
232
233 return true;
234 }
235 else {
236 return false;
237 }
238 }
239
240 - (bool)fakeAKSDecryptWithKeybag:(keybag_handle_t)keybag
241 keyclass:(keyclass_t)keyclass
242 wrappedKeyData:(NSData*)wrappedKeyData
243 outKeyclass:(keyclass_t*)outKeyclass
244 unwrappedKey:(NSMutableData*)unwrappedKey
245 error:(NSError**)error
246 {
247 if (self.lockState == LockStateLockedAndDisallowAKS) {
248 if (error) {
249 *error = [NSError errorWithDomain:(__bridge NSString *)kSecErrorDomain code:errSecInteractionNotAllowed userInfo:NULL];
250 }
251 return false;
252 }
253
254 if (self.simulateRolledAKSKey && keyclass < key_class_last) {
255 // let's make decryption fail like it would if this were an old metadata key entry made with a generational AKS key, but we didn't store that info in the database
256 return false;
257 }
258
259 const uint8_t* wrappedKeyBytes = wrappedKeyData.bytes;
260
261 NSData* ciphertextData = [NSData dataWithBytes:wrappedKeyBytes length:32];
262 NSData* ivData = [NSData dataWithBytes:wrappedKeyBytes + 32 length:32];
263 NSData* authCodeData = [NSData dataWithBytes:wrappedKeyBytes + 64 length:8];
264 SFAuthenticatedCiphertext* ciphertext = [[SFAuthenticatedCiphertext alloc] initWithCiphertext:ciphertextData authenticationCode:authCodeData initializationVector:ivData];
265
266 NSError* localError = nil;
267
268 SFAuthenticatedEncryptionOperation* encryptionOperation = [[SFAuthenticatedEncryptionOperation alloc] initWithKeySpecifier:self.keySpecifier];
269 encryptionOperation.authenticationCodeLength = 8;
270 NSData* decryptedData = [encryptionOperation decrypt:ciphertext withKey:self.fakeAKSKey error:&localError];
271
272 // in real securityd, we go through AKS rather than SFCryptoServices
273 // we need to translate the error for proper handling
274 if ([localError.domain isEqualToString:SFCryptoServicesErrorDomain] && localError.code == SFCryptoServicesErrorDecryptionFailed) {
275 if (!self.simulateRolledAKSKey && keyclass > key_class_last) {
276 // for this case we want to simulate what happens when we try decrypting with a rolled keyclass on a device which has never been rolled, which is it ends up with a NotPermitted error from AKS which the security layer translates as locked keybag
277 localError = [NSError errorWithDomain:NSOSStatusErrorDomain code:errSecInteractionNotAllowed userInfo:nil];
278 }
279 else {
280 localError = [NSError errorWithDomain:NSOSStatusErrorDomain code:errSecDecode userInfo:nil];
281 }
282 }
283
284 if (error) {
285 *error = localError;
286 }
287
288 self.keyclassUsedForAKSDecryption = keyclass;
289 if (decryptedData && decryptedData.length <= unwrappedKey.length) {
290 memcpy(unwrappedKey.mutableBytes, decryptedData.bytes, decryptedData.length);
291 unwrappedKey.length = decryptedData.length;
292 self.didAKSDecrypt = YES;
293 return true;
294 }
295 else {
296 return false;
297 }
298 }
299
300 - (NSData*)getDatabaseKeyDataithError:(NSError**)error
301 {
302 if (_lockState == LockStateUnlocked) {
303 return [NSData dataWithBytes:"12345678901234567890123456789012" length:32];
304 }
305 else {
306 if (error) {
307 // <rdar://problem/38972671> add SFKeychainErrorDeviceLocked
308 *error = [NSError errorWithDomain:SFKeychainErrorDomain code:SFKeychainErrorFailedToCommunicateWithServer userInfo:nil];
309 }
310 return nil;
311 }
312 }
313
314 // Mock SecTask entitlement retrieval API, so that we can test access group entitlement parsing code in SecTaskCopyAccessGroups()
315 static NSDictionary *currentEntitlements = nil;
316 static BOOL currentEntitlementsValidated = false;
317 static NSArray *currentAccessGroups = nil;
318
319 CFTypeRef SecTaskCopyValueForEntitlement(SecTaskRef task, CFStringRef entitlement, CFErrorRef *error) {
320 id value = currentEntitlements[(__bridge id)entitlement];
321 if (value == nil && error != NULL) {
322 *error = (CFErrorRef)CFBridgingRetain([NSError errorWithDomain:NSPOSIXErrorDomain code:EINVAL userInfo:nil]);
323 }
324 return CFBridgingRetain(value);
325 }
326
327 Boolean SecTaskEntitlementsValidated(SecTaskRef task) {
328 return currentEntitlementsValidated;
329 }
330
331 - (void)setEntitlements:(NSDictionary<NSString *, id> *)entitlements validated:(BOOL)validated {
332 currentEntitlements = entitlements;
333 currentEntitlementsValidated = validated;
334 id task = CFBridgingRelease(SecTaskCreateFromSelf(kCFAllocatorDefault));
335 currentAccessGroups = CFBridgingRelease(SecTaskCopyAccessGroups((__bridge SecTaskRef)task));
336 SecAccessGroupsSetCurrent((__bridge CFArrayRef)currentAccessGroups);
337 }
338
339 @end
340
341 #endif