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