]> git.saurik.com Git - apple/security.git/blob - OSX/libsecurity_keychain/regressions/kc-12-key-create-symmetric-and-use.m
Security-58286.200.222.tar.gz
[apple/security.git] / OSX / libsecurity_keychain / regressions / kc-12-key-create-symmetric-and-use.m
1 /*
2 * Copyright (c) 2016 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 xLicense.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24 #import <Security/Security.h>
25 #import <Security/SecCertificatePriv.h>
26
27 #include "keychain_regressions.h"
28 #include "kc-helpers.h"
29 #define nullptr NULL
30
31 #import <Foundation/Foundation.h>
32
33 static NSString * const EncryptionKeyLabel = @"Test Encryption Key";
34
35 //
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.
42 //
43 static bool setTrustedApplicationsForACLAuthorization(SecAccessRef access,
44 CFTypeRef authorizationTag, NSArray* trustedApplications)
45 {
46 // get the access control list for this authorization tag)
47 CFArrayRef aclList = SecAccessCopyMatchingACLList(access, authorizationTag);
48 if (!aclList)
49 return false;
50
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);
58
59 if (!trustedApplications) // "allow all applications to access this item"
60 {
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);
65 }
66 else // "allow access by these applications"
67 {
68 // modify the application list
69 err = SecACLSetContents(aclRef, (CFArrayRef)trustedApplications, promptDescription, promptSelector);
70 ok_status(err, "%s: SecACLSetContents", testName);
71 }
72
73 if (appList) CFRelease(appList);
74 if (promptDescription) CFRelease(promptDescription);
75
76 CFRelease(aclList);
77 return (!err);
78 }
79
80 //
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.
84 //
85 static SecAccessRef createAccess(SecKeychainItemRef item, NSString *accessLabel, BOOL allowAny)
86 {
87 OSStatus err;
88 SecAccessRef access=nil;
89 NSArray *trustedApplications=nil;
90
91 if (!allowAny) // use default access ("confirm access")
92 {
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:(__bridge_transfer id)myself, (__bridge_transfer id)someOther, nil];
101 }
102
103 // If the keychain item already exists, use its access reference; otherwise, create a new one
104 if (item) {
105 err = SecKeychainItemCopyAccess(item, &access);
106 ok_status(err, "%s: SecKeychainItemCopyAccess", testName);
107 } else {
108 err = SecAccessCreate((CFStringRef)accessLabel, (CFArrayRef)trustedApplications, &access);
109 ok_status(err, "%s: SecAccessCreate", testName);
110 }
111
112 if (err) return nil;
113
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.
116 //
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.
119 //
120 setTrustedApplicationsForACLAuthorization(access, kSecACLAuthorizationEncrypt, (allowAny) ? NULL : trustedApplications);
121 setTrustedApplicationsForACLAuthorization(access, kSecACLAuthorizationDecrypt, (allowAny) ? NULL : trustedApplications);
122
123 return access;
124 }
125
126 static SecKeyRef findExistingEncryptionKey(SecKeychainRef kc)
127 {
128 SecKeyRef key;
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))
135 return key;
136
137 return nullptr;
138 }
139
140 static CF_RETURNS_RETAINED SecKeyRef generateEncryptionKey(SecKeychainRef kc)
141 {
142 SecAccessRef access = createAccess(nil, EncryptionKeyLabel, false);
143 if (!access) {
144 NSLog(@"Creating an access object failed.");
145 return nullptr;
146 }
147
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);
159
160 is(error, NULL, "%s: SecKeyGenerateSymmetric", testName);
161 ok(key, "%s: SecKeyGenerateSymmetric returned a key", testName);
162
163 if (!key)
164 NSLog(@"Creating encryption key failed: %@", error);
165
166 CFRelease(access);
167 return key;
168 }
169
170 static SecKeyRef findOrGenerateEncryptionKey(SecKeychainRef kc)
171 {
172 SecKeyRef key = findExistingEncryptionKey(kc);
173 if (key)
174 return key;
175
176 return generateEncryptionKey(kc);
177 }
178
179 static SecKeyRef encryptionKey(SecKeychainRef kc)
180 {
181 static SecKeyRef key = NULL;
182 if (!key) {
183 key = findOrGenerateEncryptionKey(kc);
184 }
185 return key;
186 }
187
188 static NSData *encryptData(SecKeychainRef kc, NSData *plainTextData)
189 {
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);
194
195 CFErrorRef error = 0;
196 NSData *result = CFBridgingRelease(SecTransformExecute(transform, &error));
197 CFRelease(transform);
198 is(error, NULL, "%s: SecTransformExecute (encrypt)", testName);
199
200 if (!result) {
201 NSLog(@"Encrypting data failed: %@", error);
202 CFRelease(error);
203 return nil;
204 }
205
206 return result;
207 }
208
209 static NSData *decryptData(SecKeychainRef kc, NSData *cipherTextData)
210 {
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);
215
216 CFErrorRef error = 0;
217 NSData *result = CFBridgingRelease(SecTransformExecute(transform, &error));
218 is(error, NULL, "%s: SecTransformExecute (decrypt)", testName);
219
220 CFRelease(transform);
221 if (!result) {
222 NSLog(@"Decrypting data failed: %@", error);
223 CFRelease(error);
224 return nil;
225 }
226
227 return result;
228 }
229
230 int kc_12_key_create_symmetric_and_use(int argc, char *const *argv)
231 {
232 plan_tests(17);
233 initializeKeychainTests(__FUNCTION__);
234
235 SecKeychainRef kc = getPopulatedTestKeychain();
236
237 @autoreleasepool {
238 NSData *data = [@"Hello, world!" dataUsingEncoding:NSUTF8StringEncoding];
239 NSLog(@" Original: %@", data);
240 NSData *encryptedData = encryptData(kc, data);
241 NSLog(@"Encrypted: %@", encryptedData);
242 if (encryptedData) {
243 NSData *roundtrippedData = decryptData(kc, encryptedData);
244
245 eq_cf((__bridge CFTypeRef) roundtrippedData, (__bridge CFTypeRef) data, "%s: Round-tripped data does not match original data", testName);
246 NSLog(@"Decrypted: %@", roundtrippedData);
247 }
248 }
249
250 checkPrompts(0, "no prompts during test");
251
252 ok_status(SecKeychainDelete(kc), "%s: SecKeychainDelete", testName);
253 CFReleaseNull(kc);
254
255 deleteTestFiles();
256 return 0;
257 }
258