2 * Copyright (c) 2015 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 License.
21 * @APPLE_LICENSE_HEADER_END@
24 #include "kc-30-xara-helpers.h"
26 #ifndef kc_30_xara_key_helpers_h
27 #define kc_30_xara_key_helpers_h
31 static CFMutableDictionaryRef
makeBaseKeyDictionary() {
32 CFMutableDictionaryRef query
= CFDictionaryCreateMutable(NULL
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
33 CFDictionarySetValue(query
, kSecClass
, kSecClassKey
);
37 static CFMutableDictionaryRef
makeQueryKeyDictionary(SecKeychainRef kc
, CFStringRef keyClass
) {
38 CFMutableDictionaryRef query
= makeBaseKeyDictionary();
40 CFMutableArrayRef searchList
= (CFMutableArrayRef
) CFArrayCreateMutable(kCFAllocatorDefault
, 1, &kCFTypeArrayCallBacks
);
41 CFArrayAppendValue((CFMutableArrayRef
)searchList
, kc
);
42 CFDictionarySetValue(query
, kSecMatchSearchList
, searchList
);
44 CFDictionarySetValue(query
, kSecAttrKeyClass
, keyClass
);
46 CFDictionarySetValue(query
, kSecMatchLimit
, kSecMatchLimitAll
);
50 static CFMutableDictionaryRef
makeAddKeyDictionary(SecKeychainRef kc
, CFStringRef keyClass
, CFStringRef label
) {
51 CFMutableDictionaryRef query
= makeBaseKeyDictionary();
52 CFDictionaryAddValue(query
, kSecUseKeychain
, kc
);
54 CFDictionarySetValue(query
, kSecAttrLabel
, label
);
55 CFDictionarySetValue(query
, kSecAttrApplicationLabel
, CFSTR("test_application")); // without setting this, it uses the current datetime.
58 if(CFEqual(keyClass
, kSecAttrKeyClassSymmetric
)) {
59 CFDictionarySetValue(query
, kSecAttrKeyType
, kSecAttrKeyTypeAES
);
61 } else if(CFEqual(keyClass
, kSecAttrKeyClassPublic
) ||
62 CFEqual(keyClass
, kSecAttrKeyClassPrivate
)) {
63 CFDictionarySetValue(query
, kSecAttrKeyType
, kSecAttrKeyTypeRSA
);
66 CFNumberRef num
= CFNumberCreate(kCFAllocatorDefault
, kCFNumberSInt32Type
, &n
);
67 CFDictionarySetValue(query
, kSecAttrKeySizeInBits
, num
);
72 static SecKeyRef
makeCustomKey(const char* name
, SecKeychainRef kc
, CFStringRef label
) {
73 CFMutableDictionaryRef query
= makeAddKeyDictionary(kc
, kSecAttrKeyClassSymmetric
, label
);
75 CFErrorRef error
= NULL
;
76 SecKeyRef item
= SecKeyGenerateSymmetric(query
, &error
);
77 ok(item
!= NULL
, "%s: SecKeyGenerateSymmetric errored: %ld", name
, error
? CFErrorGetCode(error
) : -1);
82 #define makeCustomKeyTests 1
84 static SecKeyRef
makeKey(const char* name
, SecKeychainRef kc
) {
85 return makeCustomKey(name
, kc
, CFSTR("test_key"));
87 #define makeKeyTests makeCustomKeyTests
90 static void makeCustomKeyWithIntegrity(const char* name
, SecKeychainRef kc
, CFStringRef label
, CFStringRef expectedHash
) {
91 SecKeyRef key
= makeCustomKey(name
, kc
, label
);
92 checkIntegrityHash(name
, (SecKeychainItemRef
) key
, expectedHash
);
95 #define makeCustomKeyWithIntegrityTests (makeCustomKeyTests + checkIntegrityHashTests)
97 static void makeKeyWithIntegrity(const char* name
, SecKeychainRef kc
, CFStringRef expectedHash
) {
98 makeCustomKeyWithIntegrity(name
, kc
, CFSTR("test_key"), expectedHash
);
100 #define makeKeyWithIntegrityTests makeCustomKeyWithIntegrityTests
102 static void makeCustomKeyPair(const char* name
, SecKeychainRef kc
, CFStringRef label
, SecKeyRef
* aPub
, SecKeyRef
* aPriv
) {
103 CFMutableDictionaryRef query
= makeAddKeyDictionary(kc
, kSecAttrKeyClassPublic
, label
);
105 CFErrorRef error
= NULL
;
108 ok_status(SecKeyGeneratePair(query
, &pub
, &priv
), "%s: SecKeyGeneratePair returned a result", name
);
117 CFReleaseNull(query
);
119 #define makeCustomKeyPairTests 1
121 static void makeKeyPair(const char* name
, SecKeychainRef kc
, SecKeyRef
* aPub
, SecKeyRef
* aPriv
) {
122 makeCustomKeyPair(name
, kc
, CFSTR("test_key"), aPub
, aPriv
);
124 #define makeKeyPairTests makeCustomKeyPairTests
126 // Note that this is nearly useless, as key pairs will never have stable hashes
127 static void makeKeyPairWithIntegrity(const char* name
, SecKeychainRef kc
, CFStringRef expectedPubHash
, CFStringRef expectedPrivHash
) {
130 makeKeyPair(name
, kc
, &pub
, &priv
);
132 checkIntegrityHash(name
, (SecKeychainItemRef
) pub
, expectedPubHash
);
133 checkIntegrityHash(name
, (SecKeychainItemRef
) priv
, expectedPrivHash
);
135 #define makeKeyPairWithIntegrityTests (makeKeyTests + checkIntegrityHashTests)
137 // This only works for symmetric keys; key pairs cannot ever generate a duplicate (due to setting kSecKeyLabel to the hash of the public key)
138 static void makeCustomDuplicateKey(const char* name
, SecKeychainRef kc
, CFStringRef label
) {
139 CFMutableDictionaryRef query
;
141 query
= makeAddKeyDictionary(kc
, kSecAttrKeyClassSymmetric
, label
);
142 CFErrorRef error
= NULL
;
143 SecKeyRef item
= SecKeyGenerateSymmetric(query
, &error
);
144 is(CFErrorGetCode(error
), errSecDuplicateItem
, "%s: SecKeyGenerateSymmetric (duplicate) errored: %ld", name
, error
? CFErrorGetCode(error
) : -1);
146 CFReleaseNull(query
);
148 #define makeCustomDuplicateKeyTests 1
150 static void makeDuplicateKey(const char* name
, SecKeychainRef kc
) {
151 makeCustomDuplicateKey(name
, kc
, CFSTR("test_key"));
153 #define makeDuplicateKeyTests makeCustomDuplicateKeyTests
155 static SecKeyRef
makeCustomFreeKey(const char* name
, SecKeychainRef kc
, CFStringRef label
) {
158 ok_status(SecKeyGenerate(
161 0, /* contextHandle */
162 CSSM_KEYUSE_ENCRYPT
| CSSM_KEYUSE_DECRYPT
,
163 CSSM_KEYATTR_EXTRACTABLE
,
164 NULL
, /* initialAccess */
165 &symkey
), "%s: SecKeyGenerate", name
);;
167 CFMutableDictionaryRef query
= makeAddKeyDictionary(kc
, kSecAttrKeyClassSymmetric
, label
);
169 CFMutableArrayRef itemList
= (CFMutableArrayRef
) CFArrayCreateMutable(kCFAllocatorDefault
, 1, &kCFTypeArrayCallBacks
);
170 CFArrayAppendValue((CFMutableArrayRef
)itemList
, symkey
);
172 CFDictionarySetValue(query
, kSecUseItemList
, itemList
);
174 CFTypeRef result
= NULL
;
175 ok_status(SecItemAdd(query
, &result
), "%s: SecItemAdd", name
);
176 ok(result
!= NULL
, "%s: SecItemAdd returned a result", name
);
177 CFReleaseNull(symkey
);
178 return (SecKeyRef
) result
;
180 #define makeCustomFreeKeyTests 3
182 static SecKeyRef
makeFreeKey(const char* name
, SecKeychainRef kc
) {
183 return makeCustomFreeKey(name
, kc
, CFSTR("test_free_key"));
185 #define makeFreeKeyTests makeCustomFreeKeyTests
187 static SecKeyRef
makeCustomDuplicateFreeKey(const char* name
, SecKeychainRef kc
, CFStringRef label
) {
190 ok_status(SecKeyGenerate(
193 0, /* contextHandle */
194 CSSM_KEYUSE_ENCRYPT
| CSSM_KEYUSE_DECRYPT
,
195 CSSM_KEYATTR_EXTRACTABLE
,
196 NULL
, /* initialAccess */
197 &symkey
), "%s: SecKeyGenerate", name
);;
199 CFMutableDictionaryRef query
= makeAddKeyDictionary(kc
, kSecAttrKeyClassSymmetric
, label
);
201 CFMutableArrayRef itemList
= (CFMutableArrayRef
) CFArrayCreateMutable(kCFAllocatorDefault
, 1, &kCFTypeArrayCallBacks
);
202 CFArrayAppendValue((CFMutableArrayRef
)itemList
, symkey
);
204 CFDictionarySetValue(query
, kSecUseItemList
, itemList
);
206 CFTypeRef result
= NULL
;
207 is(SecItemAdd(query
, &result
), errSecDuplicateItem
, "%s: SecItemAdd (duplicate)", name
);
208 CFReleaseNull(symkey
);
209 return (SecKeyRef
) result
;
211 #define makeCustomDuplicateFreeKeyTests 2
213 static SecKeyRef
makeDuplicateFreeKey(const char* name
, SecKeychainRef kc
) {
214 return makeCustomFreeKey(name
, kc
, CFSTR("test_free_key"));
216 #define makeDuplicateFreeKeyTests makeCustomDuplicateFreeKeyTests
219 // And now for the actual tests
221 static void testAddKey(CFStringRef expectedHash
) {
222 char* name
= "testAddKey";
223 SecKeychainRef kc
= newKeychain(name
);
224 makeKeyWithIntegrity(name
, kc
, expectedHash
);
225 ok_status(SecKeychainDelete(kc
), "%s: SecKeychainDelete", name
);
227 #define testAddKeyTests (newKeychainTests + makeKeyWithIntegrityTests + 1)
229 static void testAddFreeKey(CFStringRef expectedHash
) {
230 // Due to <rdar://problem/8431281> SecItemAdd() will not add a generated symmetric key to the keychain
231 // we can't actually run this test. Code is included here as a reference.
232 //char* name = "testAddFreeKey";
233 //SecKeychainRef kc = newKeychain(name);
235 //SecKeyRef key = makeFreeKey(name, kc);
236 //checkIntegrityHash(name, (SecKeychainItemRef) key, expectedHash);
238 //ok_status(SecKeychainDelete(kc), "%s: SecKeychainDelete", name);
240 //#define testAddFreeKeyTests (newKeychainTests + makeFreeKeyTests + checkIntegrityHashTests + 1)
241 #define testAddFreeKeyTests 0
243 static void testCopyMatchingKey(CFStringRef expectedHash
) {
244 char* name
= "testCopyMatchingKey";
245 secdebugfunc("integrity", "************************************* %s", name
);
247 SecKeychainRef kc
= newKeychain(name
);
248 checkN(name
, makeQueryKeyDictionary(kc
, kSecAttrKeyClassSymmetric
), 0);
250 makeKeyWithIntegrity(name
, kc
, expectedHash
);
252 SecKeyRef item
= (SecKeyRef
) checkN(name
, makeQueryKeyDictionary(kc
, kSecAttrKeyClassSymmetric
), 1);
253 checkIntegrityHash(name
, (SecKeychainItemRef
) item
, expectedHash
);
254 ok_status(SecKeychainDelete(kc
), "%s: SecKeychainDelete", name
);
256 #define testCopyMatchingKeyTests (newKeychainTests + checkNTests + makeKeyWithIntegrityTests + checkNTests + checkIntegrityHashTests + 1)
259 static void testUpdateKey(CFStringRef expectedHash
, CFStringRef expectedHashAfter
) {
260 char * name
= "testUpdateKey";
261 secdebugfunc("integrity", "************************************* %s", name
);
263 SecKeychainRef kc
= newKeychain(name
);
264 makeKeyWithIntegrity(name
, kc
, expectedHash
);
265 SecKeychainItemRef item
= checkN(name
, makeQueryKeyDictionary(kc
, kSecAttrKeyClassSymmetric
), 1);
268 CFStringRef label
= CFSTR("a modified label");
270 CFMutableDictionaryRef query
= makeQueryKeyDictionary(kc
, kSecAttrKeyClassSymmetric
);
271 CFMutableDictionaryRef update
= CFDictionaryCreateMutable(NULL
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
272 CFDictionarySetValue(update
, kSecAttrLabel
, label
);
273 ok_status(SecItemUpdate(query
, update
), "%s: SecItemUpdate", name
);
275 CFReleaseNull(query
);
276 CFReleaseNull(update
);
278 // Find the item again.
279 query
= makeQueryKeyDictionary(kc
, kSecAttrKeyClassSymmetric
);
280 CFDictionarySetValue(query
, kSecAttrLabel
, label
);
281 item
= checkN(name
, query
, 1);
282 checkIntegrityHash(name
, item
, expectedHashAfter
);
283 ok_status(SecKeychainDelete(kc
), "%s: SecKeychainDelete", name
);
285 #define testUpdateKeyTests (newKeychainTests + makeKeyWithIntegrityTests + checkNTests + 1 + checkNTests + checkIntegrityHashTests + 1)
287 // Key Pairs have non-predictable hashes, since they receive an attribute kSecKeyLabel that is
288 // the hash of the public key. Since the key is generated randomly, so is the label.
290 // We can't do our normal "add", "copymatching", "update" tests here, so do
291 // something special...
293 static void testKeyPair() {
294 char* name
= "testKeyPair";
295 secdebugfunc("integrity", "************************************* %s", name
);
297 SecKeychainRef kc
= newKeychain(name
);
298 checkN(name
, makeQueryKeyDictionary(kc
, kSecAttrKeyClassPublic
), 0);
299 checkN(name
, makeQueryKeyDictionary(kc
, kSecAttrKeyClassPrivate
), 0);
303 makeKeyPair(name
, kc
, &pub
, &priv
);
305 // Now that we have the key pair, make sure we can pull the individual keys
306 // out (and the hashes match)
309 item
= (SecKeyRef
) checkN(name
, makeQueryKeyDictionary(kc
, kSecAttrKeyClassPublic
), 1);
310 checkHashesMatch(name
, (SecKeychainItemRef
)pub
, (SecKeychainItemRef
)item
);
312 item
= (SecKeyRef
) checkN(name
, makeQueryKeyDictionary(kc
, kSecAttrKeyClassPrivate
), 1);
313 checkHashesMatch(name
, (SecKeychainItemRef
)priv
, (SecKeychainItemRef
)item
);
315 // TODO: is there a way to test SecItemUpdate?
317 ok_status(SecKeychainDelete(kc
), "%s: SecKeychainDelete", name
);
319 #define testKeyPairTests (newKeychainTests + checkNTests + checkNTests + makeKeyPairTests \
320 + checkNTests + checkHashesMatchTests \
321 + checkNTests + checkHashesMatchTests + 1)
323 static void testAddDuplicateKey(CFStringRef expectedHash
) {
324 char * name
= "testAddDuplicateKey";
325 secdebugfunc("integrity", "************************************* %s", name
);
327 SecKeychainRef kc
= newKeychain(name
);
328 checkN(name
, makeQueryKeyDictionary(kc
, kSecAttrKeyClassSymmetric
), 0);
330 makeKeyWithIntegrity(name
, kc
, expectedHash
);
332 SecKeychainItemRef item
= checkN(name
, makeQueryKeyDictionary(kc
, kSecAttrKeyClassSymmetric
), 1);
333 makeDuplicateKey(name
, kc
);
334 checkN(name
, makeQueryKeyDictionary(kc
, kSecAttrKeyClassSymmetric
), 1);
336 ok_status(SecKeychainDelete(kc
), "%s: SecKeychainDelete", name
);
338 #define testAddDuplicateKeyTests (newKeychainTests + checkNTests +makeKeyWithIntegrityTests + checkNTests + makeDuplicateKeyTests + checkNTests + 1)
340 static void testAddDuplicateFreeKey(CFStringRef expectedHash
) {
341 // Due to <rdar://problem/8431281> SecItemAdd() will not add a generated symmetric key to the keychain
342 // we can't actually run this test. Code is included here as a reference.
343 //char * name = "testAddDuplicateFreeKey";
344 //secdebugfunc("integrity", "************************************* %s", name);
345 //SecKeychainRef kc = newKeychain(name);
346 //checkN(name, makeQueryKeyDictionary(kc, kSecAttrKeyClassSymmetric), 0);
348 //SecKeyRef key = makeFreeKey(name, kc);
349 //checkIntegrityHash(name, (SecKeychainItemRef) key, expectedHash);
350 //SecKeychainItemRef item = checkN(name, makeQueryKeyDictionary(kc, kSecAttrKeyClassSymmetric), 1);
352 //makeDuplicateFreeKey(name, kc);
353 //checkN(name, makeQueryKeyDictionary(kc, kSecAttrKeyClassSymmetric), 1);
355 //ok_status(SecKeychainDelete(kc), "%s: SecKeychainDelete", name);
357 //#define testAddDuplicateFreeKeyTests (newKeychainTests + checkNTests + makeFreeKeyTests + checkIntegrityHashTests + checkNTests \
358 // + makeDuplicateKeyTests + checkNTests + 1)
359 #define testAddDuplicateFreeKeyTests 0
361 // testAddDuplicateKeyPair:
363 // By use of the Sec* APIs, you will almost certainly never get an
364 // errSecDuplicateItem out of SecKeychainGeneratePair. Since it sets a primary
365 // key attribute as the hash of the public key, it just will never generate a
368 static void testExportImportKeyPair() {
369 char* name
= "testExportImportKeyPair";
370 secdebugfunc("integrity", "************************************* %s", name
);
372 SecKeychainRef kc
= newKeychain(name
);
373 checkN(name
, makeQueryKeyDictionary(kc
, kSecAttrKeyClassPublic
), 0);
374 checkN(name
, makeQueryKeyDictionary(kc
, kSecAttrKeyClassPrivate
), 0);
378 makeKeyPair(name
, kc
, &pub
, &priv
);
380 // Now that we have the key pair, make sure we can pull the individual keys out
383 item
= (SecKeyRef
) checkN(name
, makeQueryKeyDictionary(kc
, kSecAttrKeyClassPublic
), 1);
384 checkHashesMatch(name
, (SecKeychainItemRef
)pub
, (SecKeychainItemRef
)item
);
386 item
= (SecKeyRef
) checkN(name
, makeQueryKeyDictionary(kc
, kSecAttrKeyClassPrivate
), 1);
387 checkHashesMatch(name
, (SecKeychainItemRef
)priv
, (SecKeychainItemRef
)item
);
389 CFMutableArrayRef applications
= (CFMutableArrayRef
) CFArrayCreateMutable(kCFAllocatorDefault
, 1, &kCFTypeArrayCallBacks
);
390 SecTrustedApplicationRef app
= NULL
;
392 ok_status(SecTrustedApplicationCreateFromPath(NULL
, &app
), "%s: SecTrustedApplicationCreateFromPath", name
);
393 CFArrayAppendValue(applications
, app
);
395 ok_status(SecTrustedApplicationCreateFromPath("/usr/bin/codesign", &app
), "%s: SecTrustedApplicationCreateFromPath", name
);
396 CFArrayAppendValue(applications
, app
);
398 SecAccessRef accessRef
= NULL
;
399 ok_status(SecAccessCreate(CFSTR("accessDescription"), applications
, &accessRef
), "%s: SecAccessCreate", name
);
401 const SecItemImportExportKeyParameters keyParams
=
403 SEC_KEY_IMPORT_EXPORT_PARAMS_VERSION
,
410 CFArrayRef items
= NULL
;
412 CFDataRef keyData
= NULL
;
413 ok_status(SecItemExport(pub
, kSecFormatPEMSequence
, kSecItemPemArmour
, &keyParams
, &keyData
), "%s: SecItemExport", name
);
414 ok_status(SecKeychainItemDelete((SecKeychainItemRef
)pub
), "%s: SecKeychainDelete", name
);;
418 checkN(name
, makeQueryKeyDictionary(kc
, kSecAttrKeyClassPublic
), 0);
419 ok_status(SecItemImport(keyData
, NULL
, NULL
, NULL
, kSecItemPemArmour
, &keyParams
, kc
, &items
), "%s: SecItemImport", name
);
420 checkN(name
, makeQueryKeyDictionary(kc
, kSecAttrKeyClassPublic
), 1);
423 ok_status(SecItemExport(priv
, kSecFormatPEMSequence
, kSecItemPemArmour
, &keyParams
, &keyData
), "%s: SecItemExport", name
);
424 ok_status(SecKeychainItemDelete((SecKeychainItemRef
)priv
), "%s: SecKeychainDelete", name
);;
428 checkN(name
, makeQueryKeyDictionary(kc
, kSecAttrKeyClassPrivate
), 0);
430 ok_status(SecItemImport(keyData
, NULL
, NULL
, NULL
, kSecItemPemArmour
, &keyParams
, kc
, &items
), "%s: SecItemImport", name
);
432 checkN(name
, makeQueryKeyDictionary(kc
, kSecAttrKeyClassPrivate
), 1);
434 SecAccessRef newRef
= NULL
;
435 ok_status(SecKeychainItemCopyAccess((SecKeychainItemRef
) CFArrayGetValueAtIndex(items
, 0), &newRef
), "%s:SecKeychainItemCopyAccess", name
);
437 SecKeyRef importedKey
= items
&& CFArrayGetCount(items
) > 0 ? (SecKeyRef
)CFArrayGetValueAtIndex(items
, 0) : NULL
;
439 CFMutableDictionaryRef query
= CFDictionaryCreateMutable(NULL
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
440 CFDictionaryAddValue(query
, kSecClass
, kSecClassKey
);
441 CFDictionaryAddValue(query
, kSecValueRef
, importedKey
);
443 CFMutableDictionaryRef attrs
= CFDictionaryCreateMutable(NULL
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
444 CFDictionaryAddValue(attrs
, kSecAttrLabel
, CFSTR("private key custom label"));
446 ok_status( SecItemUpdate(query
, attrs
), "%s: SecItemUpdate", name
);
448 fail("%s: Didn't have an item to update", name
);
451 ok_status(SecKeychainItemCopyAccess((SecKeychainItemRef
) CFArrayGetValueAtIndex(items
, 0), &newRef
), "%s:SecKeychainItemCopyAccess", name
);
452 // TODO: should probably check this AccessRef object to make sure it's simple
454 checkN(name
, makeQueryKeyDictionary(kc
, kSecAttrKeyClassPrivate
), 1);
456 ok_status(SecKeychainDelete(kc
), "%s: SecKeychainDelete", name
);
458 #define testExportImportKeyPairTests (newKeychainTests + checkNTests + checkNTests + makeKeyPairTests \
459 + checkNTests + checkHashesMatchTests \
460 + checkNTests + checkHashesMatchTests \
461 + 5 + checkNTests + 1 + checkNTests \
462 + 2 + checkNTests + 1 + checkNTests + 1 + 1 + 1 + checkNTests\
468 #endif /* TARGET_OS_MAC */
471 #endif /* kc_30_xara_key_helpers_h */