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@
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>).
30 #include "keychain_regressions.h"
31 #include "kc-helpers.h"
32 #include "kc-item-helpers.h"
33 #include "kc-key-helpers.h"
35 #include <CoreFoundation/CoreFoundation.h>
36 #include <CoreServices/CoreServices.h>
37 #include <Security/Security.h>
38 #include <utilities/SecCFRelease.h>
45 #include <sys/param.h>
48 static int verbose
= 1;
50 #define MAXNAMELEN MAXPATHLEN
51 #define MAXITEMS INT32_MAX
53 #pragma mark -- Utility Functions --
56 static void PrintTestResult(char *testStr
, OSStatus status
, OSStatus expected
)
59 fprintf(stdout
, "%s: %s (result=%d, expected=%d)\n", testStr
,
60 (status
==expected
) ? "OK" : "FAILED",
61 (int)status
, (int)expected
);
64 fprintf(stdout
, "\n");
69 static void PrintCFStringWithFormat(const char *formatStr
, CFStringRef inStr
)
71 char *buf
= (char*)malloc(MAXNAMELEN
);
73 if (CFStringGetCString(inStr
, buf
, (CFIndex
)MAXNAMELEN
, kCFStringEncodingUTF8
)) {
74 fprintf(stdout
, formatStr
, buf
);
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");
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.
94 static int CreateSymmetricKey(
95 SecKeychainRef inKeychain
,
97 CFStringRef keyAppLabel
,
98 CFStringRef keyAppTag
,
102 int keySizeValue
= 128;
103 CFNumberRef keySize
= CFNumberCreate(NULL
, kCFNumberIntType
, &keySizeValue
);
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
);
111 keychain
= (SecKeychainRef
) CFRetain(inKeychain
);
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
);
120 // create a dictionary of parameters describing the key we want to create
121 CFMutableDictionaryRef params
= CFDictionaryCreateMutable(NULL
, 0,
122 &kCFTypeDictionaryKeyCallBacks
,
123 &kCFTypeDictionaryValueCallBacks
);
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
);
137 CFDictionaryAddValue( params
, kSecAttrLabel
, keyLabel
);
139 CFDictionaryAddValue( params
, kSecAttrApplicationLabel
, keyAppLabel
);
141 CFDictionaryAddValue( params
, kSecAttrApplicationTag
, keyAppTag
);
144 CFErrorRef error
= NULL
;
145 SecKeyRef key
= SecKeyGenerateSymmetric(params
, &error
);
147 // print result and clean up
150 CFStringRef desc
= (error
) ? CFErrorCopyDescription(error
) : CFRetain(CFSTR("(no result!"));
151 PrintCFStringWithFormat("SecKeyGenerateSymmetric failed: %s\n", desc
);
155 CFStringRef desc
= CFCopyDescription(key
);
156 PrintCFStringWithFormat("SecKeyGenerateSymmetric succeeded: %s\n", desc
);
160 status
= (error
) ? (OSStatus
) CFErrorGetCode(error
) : noErr
;
161 // if (status == errSecDuplicateItem)
162 // status = noErr; // it's OK if the key already exists
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
);
170 PrintTestResult("CreateSymmetricKey", status
, expected
);
175 static int TestUpdateItems(SecKeychainRef keychain
)
178 OSStatus status
= errSecSuccess
;
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");
186 status
= CreateSymmetricKey(keychain
, curDateLabel
, g15UUID
, curAppTag
, noErr
);
187 CFReleaseNull(curDateLabel
);
188 if (status
&& status
!= errSecDuplicateItem
)
191 CFStringRef keyLabel
= CFSTR("iMessage test key");
192 CFStringRef newLabel
= CFSTR("iMessage test PRIVATE key");
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
);
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
);
211 status
= SecKeyGeneratePair(params
, &publicKey
, &privateKey
);
212 ok_status(status
, "%s: SecKeyGeneratePair", testName
);
213 if (status
!= noErr
) {
216 PrintTestResult("TestUpdateItems: generating key pair", status
, noErr
);
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);
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 );
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
);
236 // create dictionary of changed attributes for the private key
237 CFMutableDictionaryRef attrs
= CFDictionaryCreateMutable(NULL
, 0,
238 &kCFTypeDictionaryKeyCallBacks
,
239 &kCFTypeDictionaryValueCallBacks
);
240 SecAccessRef access
= NULL
;
242 status
= SecAccessCreate(newLabel
, NULL
, &access
);
243 ok_status(status
, "%s: SecAccessCreate", testName
);
244 if (status
!= noErr
) {
247 PrintTestResult("TestUpdateItems: creating access", status
, noErr
);
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
);
256 // update the private key with the new attributes
257 status
= SecItemUpdate( query
, attrs
);
258 ok_status(status
, "%s: SecItemUpdate", testName
);
260 if (status
!= noErr
) {
263 PrintTestResult("TestUpdateItems: updating item", status
, noErr
);
265 // Make sure label changed
266 checkN(testName
, createQueryKeyDictionaryWithLabel(keychain
, kSecAttrKeyClassPrivate
, keyLabel
), 0);
267 checkN(testName
, createQueryKeyDictionaryWithLabel(keychain
, kSecAttrKeyClassPrivate
, newLabel
), 1);
270 CFRelease(publicKey
);
272 CFRelease(privateKey
);
286 int kc_15_key_update_valueref(int argc
, char *const *argv
)
289 initializeKeychainTests(__FUNCTION__
);
291 SecKeychainRef keychain
= getPopulatedTestKeychain();
293 TestUpdateItems(keychain
);
295 checkPrompts(0, "no prompts during test");
297 ok_status(SecKeychainDelete(keychain
), "%s: SecKeychainDelete", testName
);