2 * Copyright (c) 2016 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 xLicense.
21 * @APPLE_LICENSE_HEADER_END@
24 #import <Security/Security.h>
25 #import <Security/SecCertificatePriv.h>
27 #include "keychain_regressions.h"
28 #include "kc-helpers.h"
31 #import <Foundation/Foundation.h>
33 static NSString * const EncryptionKeyLabel = @"Test Encryption Key";
36 // This function allows finer-grained access control settings;
37 // the given application list is trusted only for one specific authorization
38 // (e.g. kSecACLAuthorizationDecrypt). Note that if trustedApplications
39 // is NULL, this means "allow any application", while an empty (zero-length)
40 // list means "no applications have access".
41 // Returns true if the ACL was modified successfully, otherwise false.
43 static bool setTrustedApplicationsForACLAuthorization(SecAccessRef access,
44 CFTypeRef authorizationTag, NSArray* trustedApplications)
46 // get the access control list for this authorization tag)
47 CFArrayRef aclList = SecAccessCopyMatchingACLList(access, authorizationTag);
51 // get the first entry in the access control list
52 SecACLRef aclRef=(SecACLRef)CFArrayGetValueAtIndex(aclList, 0);
53 CFArrayRef appList=nil;
54 CFStringRef promptDescription=nil;
55 SecKeychainPromptSelector promptSelector;
56 OSStatus err = SecACLCopyContents(aclRef, &appList, &promptDescription, &promptSelector);
57 ok_status(err, "%s: SecACLCopyContents", testName);
59 if (!trustedApplications) // "allow all applications to access this item"
61 // change the ACL to not require the passphrase, and have a nil application list.
62 promptSelector &= ~kSecKeychainPromptRequirePassphase;
63 err = SecACLSetContents(aclRef, NULL, promptDescription, promptSelector);
64 ok_status(err, "%s: SecACLSetContents (allow all)", testName);
66 else // "allow access by these applications"
68 // modify the application list
69 err = SecACLSetContents(aclRef, (CFArrayRef)trustedApplications, promptDescription, promptSelector);
70 ok_status(err, "%s: SecACLSetContents", testName);
73 if (appList) CFRelease(appList);
74 if (promptDescription) CFRelease(promptDescription);
81 // This function returns a SecAccessRef, which the caller owns and must release.
82 // Note that if the provided item is not NULL, its existing access reference is returned,
83 // otherwise a new access reference is created.
85 static SecAccessRef createAccess(SecKeychainItemRef item, NSString *accessLabel, BOOL allowAny)
88 SecAccessRef access=nil;
89 NSArray *trustedApplications=nil;
91 if (!allowAny) // use default access ("confirm access")
93 // make an exception list of applications you want to trust,
94 // which are allowed to access the item without requiring user confirmation
95 SecTrustedApplicationRef myself, someOther;
96 err = SecTrustedApplicationCreateFromPath(NULL, &myself);
97 ok_status(err, "%s: SecTrustedApplicationCreateFromPath (1)", testName);
98 err = SecTrustedApplicationCreateFromPath("/Applications/Safari.app", &someOther);
99 ok_status(err, "%s: SecTrustedApplicationCreateFromPath (2)", testName);
100 trustedApplications = [NSArray arrayWithObjects:(id)myself, (id)someOther, nil];
103 // If the keychain item already exists, use its access reference; otherwise, create a new one
105 err = SecKeychainItemCopyAccess(item, &access);
106 ok_status(err, "%s: SecKeychainItemCopyAccess", testName);
108 err = SecAccessCreate((CFStringRef)accessLabel, (CFArrayRef)trustedApplications, &access);
109 ok_status(err, "%s: SecAccessCreate", testName);
114 // At this point we have a SecAccessRef which permits "decrypt" access to the item
115 // only by apps in our trustedApplications list. We could return at this point.
117 // To set up other types of access, we need to do more work.
118 // In this example, we'll explicitly set the access control for decrypt and encrypt operations.
120 setTrustedApplicationsForACLAuthorization(access, kSecACLAuthorizationEncrypt, (allowAny) ? NULL : trustedApplications);
121 setTrustedApplicationsForACLAuthorization(access, kSecACLAuthorizationDecrypt, (allowAny) ? NULL : trustedApplications);
126 static SecKeyRef findExistingEncryptionKey(SecKeychainRef kc)
129 NSArray* searchList = @[(__bridge id) kc];
130 NSDictionary *query = @{ (__bridge id)kSecMatchSearchList: searchList,
131 (__bridge id)kSecClass: (__bridge id)kSecClassKey,
132 (__bridge id)kSecAttrApplicationLabel: EncryptionKeyLabel,
133 (__bridge id)kSecReturnRef: @YES };
134 if (!SecItemCopyMatching((__bridge CFDictionaryRef)query, (CFTypeRef*)&key))
140 static SecKeyRef generateEncryptionKey(SecKeychainRef kc)
142 SecAccessRef access = createAccess(nil, EncryptionKeyLabel, false);
144 NSLog(@"Creating an access object failed.");
148 CFErrorRef error = NULL;
149 NSDictionary *keyParameters = @{ (__bridge id)kSecUseKeychain: (__bridge id)kc,
150 (__bridge id)kSecAttrKeyType: (__bridge id)kSecAttrKeyTypeAES,
151 (__bridge id)kSecAttrKeySizeInBits: @(256),
152 (__bridge id)kSecAttrCanEncrypt: @YES,
153 (__bridge id)kSecAttrCanDecrypt: @YES,
154 (__bridge id)kSecAttrIsPermanent: @YES,
155 (__bridge id)kSecAttrAccess: (__bridge id)access,
156 (__bridge id)kSecAttrLabel: EncryptionKeyLabel,
157 (__bridge id)kSecAttrApplicationLabel: EncryptionKeyLabel };
158 SecKeyRef key = SecKeyGenerateSymmetric((__bridge CFDictionaryRef)keyParameters, &error);
160 is(error, NULL, "%s: SecKeyGenerateSymmetric", testName);
161 ok(key, "%s: SecKeyGenerateSymmetric returned a key", testName);
164 NSLog(@"Creating encryption key failed: %@", error);
170 static SecKeyRef findOrGenerateEncryptionKey(SecKeychainRef kc)
172 SecKeyRef key = findExistingEncryptionKey(kc);
176 return generateEncryptionKey(kc);
179 static SecKeyRef encryptionKey(SecKeychainRef kc)
181 static SecKeyRef key = NULL;
183 key = findOrGenerateEncryptionKey(kc);
188 static NSData *encryptData(SecKeychainRef kc, NSData *plainTextData)
190 SecTransformRef transform = SecEncryptTransformCreate(encryptionKey(kc), nullptr);
191 SecTransformSetAttribute(transform, kSecPaddingKey, kSecPaddingPKCS7Key, nullptr);
192 SecTransformSetAttribute(transform, kSecEncryptionMode, kSecModeCBCKey, nullptr);
193 SecTransformSetAttribute(transform, kSecTransformInputAttributeName, (__bridge CFDataRef)plainTextData, nullptr);
195 CFErrorRef error = 0;
196 NSData *result = CFBridgingRelease(SecTransformExecute(transform, &error));
197 CFRelease(transform);
198 is(error, NULL, "%s: SecTransformExecute (encrypt)", testName);
201 NSLog(@"Encrypting data failed: %@", error);
209 static NSData *decryptData(SecKeychainRef kc, NSData *cipherTextData)
211 SecTransformRef transform = SecDecryptTransformCreate(encryptionKey(kc), nullptr);
212 SecTransformSetAttribute(transform, kSecPaddingKey, kSecPaddingPKCS7Key, nullptr);
213 SecTransformSetAttribute(transform, kSecEncryptionMode, kSecModeCBCKey, nullptr);
214 SecTransformSetAttribute(transform, kSecTransformInputAttributeName, (__bridge CFDataRef)cipherTextData, nullptr);
216 CFErrorRef error = 0;
217 NSData *result = CFBridgingRelease(SecTransformExecute(transform, &error));
218 is(error, NULL, "%s: SecTransformExecute (decrypt)", testName);
220 CFRelease(transform);
222 NSLog(@"Decrypting data failed: %@", error);
230 int kc_12_key_create_symmetric_and_use(int argc, char *const *argv)
233 initializeKeychainTests(__FUNCTION__);
235 SecKeychainRef kc = getPopulatedTestKeychain();
238 NSData *data = [@"Hello, world!" dataUsingEncoding:NSUTF8StringEncoding];
239 NSLog(@" Original: %@", data);
240 NSData *encryptedData = encryptData(kc, data);
241 NSLog(@"Encrypted: %@", encryptedData);
243 NSData *roundtrippedData = decryptData(kc, encryptedData);
245 eq_cf(roundtrippedData, data, "%s: Round-tripped data does not match original data", testName);
246 NSLog(@"Decrypted: %@", roundtrippedData);
250 checkPrompts(0, "no prompts during test");
252 ok_status(SecKeychainDelete(kc), "%s: SecKeychainDelete", testName);