]> git.saurik.com Git - apple/security.git/blob - OSX/libsecurity_keychain/regressions/kc-15-key-update-valueref.c
Security-59306.11.20.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 #include <utilities/SecCFRelease.h>
39
40 #include <stdlib.h>
41 #include <string.h>
42 #include <syslog.h>
43 #include <unistd.h>
44 #include <time.h>
45 #include <sys/param.h>
46
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 g15Prefix = CFSTR("Test Key");
82 const CFStringRef g15Label = CFSTR("Test AES Encryption Key");
83 const CFStringRef g15UUID = 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(g15Label, 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 ); CFRelease(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 g15Prefix, (int) (curGDate.year), (int) (curGDate.month), (int) (curGDate.day));
184 CFStringRef curAppTag = CFSTR("SecItemUpdate");
185
186 status = CreateSymmetricKey(keychain, curDateLabel, g15UUID, curAppTag, noErr);
187 CFReleaseNull(curDateLabel);
188 if (status && status != errSecDuplicateItem)
189 ++result;
190
191 CFStringRef keyLabel = CFSTR("iMessage test key");
192 CFStringRef newLabel = CFSTR("iMessage test PRIVATE key");
193
194 // create a new 1024-bit RSA key pair
195 SecKeyRef publicKey = NULL;
196 SecKeyRef privateKey = NULL;
197 CFMutableDictionaryRef params = CFDictionaryCreateMutable(NULL, 0,
198 &kCFTypeDictionaryKeyCallBacks,
199 &kCFTypeDictionaryValueCallBacks);
200 int keySizeValue = 1024;
201 CFNumberRef keySize = CFNumberCreate(NULL, kCFNumberIntType, &keySizeValue);
202
203 CFDictionaryAddValue( params, kSecAttrKeyType, kSecAttrKeyTypeRSA );
204 CFDictionaryAddValue( params, kSecAttrKeySizeInBits, keySize ); CFReleaseNull(keySize);
205 CFDictionaryAddValue( params, kSecAttrLabel, keyLabel );
206 // CFDictionaryAddValue( params, kSecAttrAccess, access );
207 // %%% note that SecKeyGeneratePair will create the key pair in the default keychain
208 // if a keychain is not given via the kSecUseKeychain parameter.
209 CFDictionaryAddValue( params, kSecUseKeychain, keychain );
210
211 status = SecKeyGeneratePair(params, &publicKey, &privateKey);
212 ok_status(status, "%s: SecKeyGeneratePair", testName);
213 if (status != noErr) {
214 ++result;
215 }
216 PrintTestResult("TestUpdateItems: generating key pair", status, noErr);
217
218 // Make sure we have the key of interest
219 checkN(testName, createQueryKeyDictionaryWithLabel(keychain, kSecAttrKeyClassPrivate, keyLabel), 1);
220 checkN(testName, createQueryKeyDictionaryWithLabel(keychain, kSecAttrKeyClassPrivate, newLabel), 0);
221
222 // create a query which will match just the private key item (based on its known reference)
223 CFMutableDictionaryRef query = CFDictionaryCreateMutable(NULL, 0,
224 &kCFTypeDictionaryKeyCallBacks,
225 &kCFTypeDictionaryValueCallBacks);
226 // CFArrayRef itemList = CFArrayCreate(NULL, (const void**) &privateKey, 1, &kCFTypeArrayCallBacks);
227 // %%% note that kSecClass seems to be a required query parameter even though
228 // kSecMatchItemList is provided; that looks like it could be a bug...
229 CFDictionaryAddValue( query, kSecClass, kSecClassKey );
230 // CFDictionaryAddValue( query, kSecAttrKeyClass, kSecAttrKeyClassPrivate );
231
232 // %%% pass the private key ref, instead of the item list, to test <rdar://problem/10358577>
233 // CFDictionaryAddValue( query, kSecMatchItemList, itemList );
234 CFDictionaryAddValue( query, kSecValueRef, privateKey );
235
236 // create dictionary of changed attributes for the private key
237 CFMutableDictionaryRef attrs = CFDictionaryCreateMutable(NULL, 0,
238 &kCFTypeDictionaryKeyCallBacks,
239 &kCFTypeDictionaryValueCallBacks);
240 SecAccessRef access = NULL;
241
242 status = SecAccessCreate(newLabel, NULL, &access);
243 ok_status(status, "%s: SecAccessCreate", testName);
244 if (status != noErr) {
245 ++result;
246 }
247 PrintTestResult("TestUpdateItems: creating access", status, noErr);
248
249 //%%% note that changing the access for this key causes a dialog,
250 // so leave this out for the moment (uncomment to test that access change works).
251 // Normally the desired access should be passed into the SecKeyGeneratePair function.
252 // so there is no need for a dialog later.
253 // CFDictionaryAddValue( attrs, kSecAttrAccess, access );
254 CFDictionaryAddValue( attrs, kSecAttrLabel, newLabel );
255
256 // update the private key with the new attributes
257 status = SecItemUpdate( query, attrs );
258 ok_status(status, "%s: SecItemUpdate", testName);
259
260 if (status != noErr) {
261 ++result;
262 }
263 PrintTestResult("TestUpdateItems: updating item", status, noErr);
264
265 // Make sure label changed
266 checkN(testName, createQueryKeyDictionaryWithLabel(keychain, kSecAttrKeyClassPrivate, keyLabel), 0);
267 checkN(testName, createQueryKeyDictionaryWithLabel(keychain, kSecAttrKeyClassPrivate, newLabel), 1);
268
269 if (publicKey)
270 CFRelease(publicKey);
271 if (privateKey)
272 CFRelease(privateKey);
273 if (access)
274 CFRelease(access);
275
276 if (params)
277 CFRelease(params);
278 if (query)
279 CFRelease(query);
280 if (attrs)
281 CFRelease(attrs);
282
283 return result;
284 }
285
286 int kc_15_key_update_valueref(int argc, char *const *argv)
287 {
288 plan_tests(20);
289 initializeKeychainTests(__FUNCTION__);
290
291 SecKeychainRef keychain = getPopulatedTestKeychain();
292
293 TestUpdateItems(keychain);
294
295 checkPrompts(0, "no prompts during test");
296
297 ok_status(SecKeychainDelete(keychain), "%s: SecKeychainDelete", testName);
298 CFRelease(keychain);
299
300 deleteTestFiles();
301 return 0;
302 }