2 * Copyright (c) 2018 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@
24 #import "KeychainXCTest.h"
25 #import "SecDbKeychainItem.h"
26 #import "SecdTestKeychainUtilities.h"
28 #import "SecDbKeychainItemV7.h"
29 #import "SecItemPriv.h"
30 #import "SecItemServer.h"
32 #import "SecDbKeychainSerializedItemV7.h"
33 #import "SecDbKeychainSerializedMetadata.h"
34 #import "SecDbKeychainSerializedSecretData.h"
35 #import "SecDbKeychainSerializedAKSWrappedKey.h"
36 #import <utilities/SecCFWrappers.h>
37 #import <SecurityFoundation/SFEncryptionOperation.h>
38 #import <SecurityFoundation/SFCryptoServicesErrors.h>
39 #import <XCTest/XCTest.h>
40 #import <OCMock/OCMock.h>
44 @interface SecDbKeychainItemV7 ()
46 + (SFAESKeySpecifier*)keySpecifier;
50 @implementation KeychainXCTest
64 self.lockState = LockStateUnlocked;
65 self.allowDecryption = true;
66 self.didAKSDecrypt = NO;
67 self.simulateRolledAKSKey = NO;
70 self.keyclassUsedForAKSDecryption = 0;
72 self.keySpecifier = [[SFAESKeySpecifier alloc] initWithBitSize:SFAESKeyBitSize256];
73 [self setNewFakeAKSKey:[NSData dataWithBytes:"1234567890123456789012" length:32]];
75 [SecDbKeychainMetadataKeyStore resetSharedStore];
77 self.mockSecDbKeychainItemV7 = OCMClassMock([SecDbKeychainItemV7 class]);
78 [[[[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];
79 [[[[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];
80 [[[self.mockSecDbKeychainItemV7 stub] andCall:@selector(decryptionOperation) onObject:self] decryptionOperation];
81 [[[self.mockSecDbKeychainItemV7 stub] andCall:@selector(isKeychainUnlocked) onObject:self] isKeychainUnlocked];
83 NSArray* partsOfName = [self.name componentsSeparatedByCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:@" ]"]];
84 secd_test_setup_temp_keychain([partsOfName[1] UTF8String], NULL);
89 [self.mockSecDbKeychainItemV7 stopMocking];
93 - (bool)isKeychainUnlocked
95 return self.lockState == LockStateUnlocked;
98 - (id)decryptionOperation
100 return self.allowDecryption ? [[SFAuthenticatedEncryptionOperation alloc] initWithKeySpecifier:[SecDbKeychainItemV7 keySpecifier]] : nil;
103 - (bool)setNewFakeAKSKey:(NSData*)newKeyData
105 NSError* error = nil;
106 self.fakeAKSKey = [[SFAESKey alloc] initWithData:newKeyData specifier:self.keySpecifier error:&error];
107 XCTAssertNil(error, "Should be no error making a fake AKS key");
108 XCTAssertNotNil(self.fakeAKSKey, "Should have received a fake AKS key");
112 - (bool)fakeAKSEncryptWithKeybag:(keybag_handle_t)keybag
113 keyclass:(keyclass_t)keyclass
114 keyData:(NSData*)keyData
115 outKeyclass:(keyclass_t*)outKeyclass
116 wrappedKey:(NSMutableData*)wrappedKey
117 error:(NSError**)error
119 if (self.lockState == LockStateLockedAndDisallowAKS) {
121 *error = [NSError errorWithDomain:(__bridge NSString *)kSecErrorDomain code:errSecInteractionNotAllowed userInfo:NULL];
126 uint32_t keyLength = (uint32_t)keyData.length;
127 const uint8_t* keyBytes = keyData.bytes;
129 NSData* dataToEncrypt = [NSData dataWithBytes:keyBytes length:keyLength];
130 NSError* localError = nil;
132 SFAuthenticatedEncryptionOperation* encryptionOperation = [[SFAuthenticatedEncryptionOperation alloc] initWithKeySpecifier:self.keySpecifier];
133 encryptionOperation.authenticationCodeLength = 8;
134 SFAuthenticatedCiphertext* ciphertext = [encryptionOperation encrypt:dataToEncrypt withKey:self.fakeAKSKey error:&localError];
141 void* wrappedKeyMutableBytes = wrappedKey.mutableBytes;
142 memcpy(wrappedKeyMutableBytes, ciphertext.ciphertext.bytes, 32);
143 memcpy(wrappedKeyMutableBytes + 32, ciphertext.initializationVector.bytes, 32);
144 memcpy(wrappedKeyMutableBytes + 64, ciphertext.authenticationCode.bytes, 8);
146 if (self.simulateRolledAKSKey && outKeyclass) {
147 *outKeyclass = keyclass | (key_class_last + 1);
157 - (bool)fakeAKSDecryptWithKeybag:(keybag_handle_t)keybag
158 keyclass:(keyclass_t)keyclass
159 wrappedKeyData:(NSData*)wrappedKeyData
160 outKeyclass:(keyclass_t*)outKeyclass
161 unwrappedKey:(NSMutableData*)unwrappedKey
162 error:(NSError**)error
164 if (self.lockState == LockStateLockedAndDisallowAKS) {
166 *error = [NSError errorWithDomain:(__bridge NSString *)kSecErrorDomain code:errSecInteractionNotAllowed userInfo:NULL];
171 if (self.simulateRolledAKSKey && keyclass < key_class_last) {
172 // 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
176 const uint8_t* wrappedKeyBytes = wrappedKeyData.bytes;
178 NSData* ciphertextData = [NSData dataWithBytes:wrappedKeyBytes length:32];
179 NSData* ivData = [NSData dataWithBytes:wrappedKeyBytes + 32 length:32];
180 NSData* authCodeData = [NSData dataWithBytes:wrappedKeyBytes + 64 length:8];
181 SFAuthenticatedCiphertext* ciphertext = [[SFAuthenticatedCiphertext alloc] initWithCiphertext:ciphertextData authenticationCode:authCodeData initializationVector:ivData];
183 NSError* localError = nil;
185 SFAuthenticatedEncryptionOperation* encryptionOperation = [[SFAuthenticatedEncryptionOperation alloc] initWithKeySpecifier:self.keySpecifier];
186 encryptionOperation.authenticationCodeLength = 8;
187 NSData* decryptedData = [encryptionOperation decrypt:ciphertext withKey:self.fakeAKSKey error:&localError];
189 // in real securityd, we go through AKS rather than SFCryptoServices
190 // we need to translate the error for proper handling
191 if ([localError.domain isEqualToString:SFCryptoServicesErrorDomain] && localError.code == SFCryptoServicesErrorDecryptionFailed) {
192 if (!self.simulateRolledAKSKey && keyclass > key_class_last) {
193 // 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
194 localError = [NSError errorWithDomain:NSOSStatusErrorDomain code:errSecInteractionNotAllowed userInfo:nil];
197 localError = [NSError errorWithDomain:NSOSStatusErrorDomain code:errSecDecode userInfo:nil];
205 self.keyclassUsedForAKSDecryption = keyclass;
206 if (decryptedData && decryptedData.length <= unwrappedKey.length) {
207 memcpy(unwrappedKey.mutableBytes, decryptedData.bytes, decryptedData.length);
208 unwrappedKey.length = decryptedData.length;
209 self.didAKSDecrypt = YES;