]> git.saurik.com Git - apple/security.git/blob - OSX/libsecurity_keychain/regressions/kc-15-key-update-valueref.c
Security-57740.31.2.tar.gz
[apple/security.git] / OSX / libsecurity_keychain / regressions / kc-15-key-update-valueref.c
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 //
25 // Tests the SecItemUpdate function.
26 // Currently this is a simple test to determine whether the correct item
27 // is updated when specified by a kSecValueRef (see <rdar://10358577>).
28 //
29
30 #include "keychain_regressions.h"
31 #include "kc-helpers.h"
32 #include "kc-item-helpers.h"
33 #include "kc-key-helpers.h"
34
35 #include <CoreFoundation/CoreFoundation.h>
36 #include <CoreServices/CoreServices.h>
37 #include <Security/Security.h>
38
39 #include <stdlib.h>
40 #include <string.h>
41 #include <syslog.h>
42 #include <unistd.h>
43 #include <time.h>
44 #include <sys/param.h>
45
46 static int quiet = 0;
47 static int debug = 1;
48 static int verbose = 1;
49
50 #define MAXNAMELEN MAXPATHLEN
51 #define MAXITEMS INT32_MAX
52
53 #pragma mark -- Utility Functions --
54
55
56 static void PrintTestResult(char *testStr, OSStatus status, OSStatus expected)
57 {
58 if (verbose) {
59 fprintf(stdout, "%s: %s (result=%d, expected=%d)\n", testStr,
60 (status==expected) ? "OK" : "FAILED",
61 (int)status, (int)expected);
62 }
63 if (debug) {
64 fprintf(stdout, "\n");
65 }
66 fflush(stdout);
67 }
68
69 static void PrintCFStringWithFormat(const char *formatStr, CFStringRef inStr)
70 {
71 char *buf = (char*)malloc(MAXNAMELEN);
72 if (buf) {
73 if (CFStringGetCString(inStr, buf, (CFIndex)MAXNAMELEN, kCFStringEncodingUTF8)) {
74 fprintf(stdout, formatStr, buf);
75 fflush(stdout);
76 }
77 free(buf);
78 }
79 }
80
81 const CFStringRef gPrefix = CFSTR("Test Key");
82 const CFStringRef gLabel = CFSTR("Test AES Encryption Key");
83 const CFStringRef gUUID = CFSTR("550e8400-e29b-41d4-a716-446655441234");
84
85 // CreateSymmetricKey will create a new AES-128 symmetric encryption key
86 // with the provided label, application label, and application tag.
87 // Each of those attributes is optional, but only the latter two
88 // (application label and application tag) are considered part of the
89 // key's "unique" attribute set. Previously, if you attempted to create a
90 // key which differs only in the label attribute (but not in the other two)
91 // then the attempt would fail and leave a "turd" key with no label in your
92 // keychain: <rdar://8289559>, fixed in 11A268a.
93
94 static int CreateSymmetricKey(
95 SecKeychainRef inKeychain,
96 CFStringRef keyLabel,
97 CFStringRef keyAppLabel,
98 CFStringRef keyAppTag,
99 OSStatus expected)
100 {
101 OSStatus status;
102 int keySizeValue = 128;
103 CFNumberRef keySize = CFNumberCreate(NULL, kCFNumberIntType, &keySizeValue);
104
105 // get a SecKeychainRef for the keychain in which we want the key to be created
106 // (this step is optional, but if omitted, the key is NOT saved in any keychain!)
107 SecKeychainRef keychain = NULL;
108 if (inKeychain == NULL)
109 status = SecKeychainCopyDefault(&keychain);
110 else
111 keychain = (SecKeychainRef) CFRetain(inKeychain);
112
113 // create a SecAccessRef to set up the initial access control settings for this key
114 // (this step is optional; if omitted, the creating application has access to the key)
115 // note: the access descriptor should be the same string as will be used for the item's label,
116 // since it's the string that is displayed by the access confirmation dialog to describe the item.
117 SecAccessRef access = NULL;
118 status = SecAccessCreate(gLabel, NULL, &access);
119
120 // create a dictionary of parameters describing the key we want to create
121 CFMutableDictionaryRef params = CFDictionaryCreateMutable(NULL, 0,
122 &kCFTypeDictionaryKeyCallBacks,
123 &kCFTypeDictionaryValueCallBacks);
124
125 CFDictionaryAddValue( params, kSecClass, kSecClassKey );
126 CFDictionaryAddValue( params, kSecUseKeychain, keychain );
127 CFDictionaryAddValue( params, kSecAttrAccess, access );
128 CFDictionaryAddValue( params, kSecAttrKeyClass, kSecAttrKeyClassSymmetric );
129 CFDictionaryAddValue( params, kSecAttrKeyType, kSecAttrKeyTypeAES );
130 CFDictionaryAddValue( params, kSecAttrKeySizeInBits, keySize );
131 CFDictionaryAddValue( params, kSecAttrIsPermanent, kCFBooleanTrue );
132 CFDictionaryAddValue( params, kSecAttrCanEncrypt, kCFBooleanTrue );
133 CFDictionaryAddValue( params, kSecAttrCanDecrypt, kCFBooleanTrue );
134 CFDictionaryAddValue( params, kSecAttrCanWrap, kCFBooleanFalse );
135 CFDictionaryAddValue( params, kSecAttrCanUnwrap, kCFBooleanFalse );
136 if (keyLabel)
137 CFDictionaryAddValue( params, kSecAttrLabel, keyLabel );
138 if (keyAppLabel)
139 CFDictionaryAddValue( params, kSecAttrApplicationLabel, keyAppLabel );
140 if (keyAppTag)
141 CFDictionaryAddValue( params, kSecAttrApplicationTag, keyAppTag );
142
143 // generate the key
144 CFErrorRef error = NULL;
145 SecKeyRef key = SecKeyGenerateSymmetric(params, &error);
146
147 // print result and clean up
148 if (debug) {
149 if (key == NULL) {
150 CFStringRef desc = (error) ? CFErrorCopyDescription(error) : CFRetain(CFSTR("(no result!"));
151 PrintCFStringWithFormat("SecKeyGenerateSymmetric failed: %s\n", desc);
152 CFRelease(desc);
153 }
154 else {
155 CFStringRef desc = CFCopyDescription(key);
156 PrintCFStringWithFormat("SecKeyGenerateSymmetric succeeded: %s\n", desc);
157 CFRelease(desc);
158 }
159 }
160 status = (error) ? (OSStatus) CFErrorGetCode(error) : noErr;
161 // if (status == errSecDuplicateItem)
162 // status = noErr; // it's OK if the key already exists
163
164 if (key) CFRelease(key);
165 if (error) CFRelease(error);
166 if (params) CFRelease(params);
167 if (keychain) CFRelease(keychain);
168 if (access) CFRelease(access);
169
170 PrintTestResult("CreateSymmetricKey", status, expected);
171
172 return status;
173 }
174
175 static int TestUpdateItems(SecKeychainRef keychain)
176 {
177 int result = 0;
178 OSStatus status = errSecSuccess;
179
180 // first, create a symmetric key
181 CFGregorianDate curGDate = CFAbsoluteTimeGetGregorianDate(CFAbsoluteTimeGetCurrent(), NULL);
182 CFStringRef curDateLabel = CFStringCreateWithFormat(NULL, NULL, CFSTR("%@ (%4d-%02d-%02d)"),
183 gPrefix, (int) (curGDate.year), (int) (curGDate.month), (int) (curGDate.day));
184 CFStringRef curAppTag = CFSTR("SecItemUpdate");
185
186 status = CreateSymmetricKey(keychain, curDateLabel, gUUID, curAppTag, noErr);
187 if (status && status != errSecDuplicateItem)
188 ++result;
189
190 CFStringRef keyLabel = CFSTR("iMessage test key");
191 CFStringRef newLabel = CFSTR("iMessage test PRIVATE key");
192
193 // create a new 1024-bit RSA key pair
194 SecKeyRef publicKey = NULL;
195 SecKeyRef privateKey = NULL;
196 CFMutableDictionaryRef params = CFDictionaryCreateMutable(NULL, 0,
197 &kCFTypeDictionaryKeyCallBacks,
198 &kCFTypeDictionaryValueCallBacks);
199 int keySizeValue = 1024;
200 CFNumberRef keySize = CFNumberCreate(NULL, kCFNumberIntType, &keySizeValue);
201
202 CFDictionaryAddValue( params, kSecAttrKeyType, kSecAttrKeyTypeRSA );
203 CFDictionaryAddValue( params, kSecAttrKeySizeInBits, keySize );
204 CFDictionaryAddValue( params, kSecAttrLabel, keyLabel );
205 // CFDictionaryAddValue( params, kSecAttrAccess, access );
206 // %%% note that SecKeyGeneratePair will create the key pair in the default keychain
207 // if a keychain is not given via the kSecUseKeychain parameter.
208 CFDictionaryAddValue( params, kSecUseKeychain, keychain );
209
210 status = SecKeyGeneratePair(params, &publicKey, &privateKey);
211 ok_status(status, "%s: SecKeyGeneratePair", testName);
212 if (status != noErr) {
213 ++result;
214 }
215 PrintTestResult("TestUpdateItems: generating key pair", status, noErr);
216
217 // Make sure we have the key of interest
218 checkN(testName, makeQueryKeyDictionaryWithLabel(keychain, kSecAttrKeyClassPrivate, keyLabel), 1);
219 checkN(testName, makeQueryKeyDictionaryWithLabel(keychain, kSecAttrKeyClassPrivate, newLabel), 0);
220
221 // create a query which will match just the private key item (based on its known reference)
222 CFMutableDictionaryRef query = CFDictionaryCreateMutable(NULL, 0,
223 &kCFTypeDictionaryKeyCallBacks,
224 &kCFTypeDictionaryValueCallBacks);
225 // CFArrayRef itemList = CFArrayCreate(NULL, (const void**) &privateKey, 1, &kCFTypeArrayCallBacks);
226 // %%% note that kSecClass seems to be a required query parameter even though
227 // kSecMatchItemList is provided; that looks like it could be a bug...
228 CFDictionaryAddValue( query, kSecClass, kSecClassKey );
229 // CFDictionaryAddValue( query, kSecAttrKeyClass, kSecAttrKeyClassPrivate );
230
231 // %%% pass the private key ref, instead of the item list, to test <rdar://problem/10358577>
232 // CFDictionaryAddValue( query, kSecMatchItemList, itemList );
233 CFDictionaryAddValue( query, kSecValueRef, privateKey );
234
235 // create dictionary of changed attributes for the private key
236 CFMutableDictionaryRef attrs = CFDictionaryCreateMutable(NULL, 0,
237 &kCFTypeDictionaryKeyCallBacks,
238 &kCFTypeDictionaryValueCallBacks);
239 SecAccessRef access = NULL;
240
241 status = SecAccessCreate(newLabel, NULL, &access);
242 ok_status(status, "%s: SecAccessCreate", testName);
243 if (status != noErr) {
244 ++result;
245 }
246 PrintTestResult("TestUpdateItems: creating access", status, noErr);
247
248 //%%% note that changing the access for this key causes a dialog,
249 // so leave this out for the moment (uncomment to test that access change works).
250 // Normally the desired access should be passed into the SecKeyGeneratePair function.
251 // so there is no need for a dialog later.
252 // CFDictionaryAddValue( attrs, kSecAttrAccess, access );
253 CFDictionaryAddValue( attrs, kSecAttrLabel, newLabel );
254
255 // update the private key with the new attributes
256 status = SecItemUpdate( query, attrs );
257 ok_status(status, "%s: SecItemUpdate", testName);
258
259 if (status != noErr) {
260 ++result;
261 }
262 PrintTestResult("TestUpdateItems: updating item", status, noErr);
263
264 // Make sure label changed
265 checkN(testName, makeQueryKeyDictionaryWithLabel(keychain, kSecAttrKeyClassPrivate, keyLabel), 0);
266 checkN(testName, makeQueryKeyDictionaryWithLabel(keychain, kSecAttrKeyClassPrivate, newLabel), 1);
267
268 if (publicKey)
269 CFRelease(publicKey);
270 if (privateKey)
271 CFRelease(privateKey);
272 if (access)
273 CFRelease(access);
274
275 if (params)
276 CFRelease(params);
277 if (query)
278 CFRelease(query);
279 if (attrs)
280 CFRelease(attrs);
281
282 return result;
283 }
284
285 int kc_15_key_update_valueref(int argc, char *const *argv)
286 {
287 plan_tests(20);
288 initializeKeychainTests(__FUNCTION__);
289
290 SecKeychainRef keychain = getPopulatedTestKeychain();
291
292 TestUpdateItems(keychain);
293
294 checkPrompts(0, "no prompts during test");
295
296 ok_status(SecKeychainDelete(keychain), "%s: SecKeychainDelete", testName);
297 CFRelease(keychain);
298
299 deleteTestFiles();
300 return 0;
301 }