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 License.
21 * @APPLE_LICENSE_HEADER_END@
24 #ifndef kc_key_helpers_h
25 #define kc_key_helpers_h
27 #include "kc-helpers.h"
28 #include "utilities/SecCFRelease.h"
32 #pragma clang diagnostic push
33 #pragma clang diagnostic ignored "-Wunused-variable"
34 #pragma clang diagnostic ignored "-Wunused-function"
36 static CFMutableDictionaryRef
makeBaseKeyDictionary() {
37 CFMutableDictionaryRef query
= CFDictionaryCreateMutable(NULL
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
38 CFDictionarySetValue(query
, kSecClass
, kSecClassKey
);
42 static CFMutableDictionaryRef
makeQueryKeyDictionary(SecKeychainRef kc
, CFStringRef keyClass
) {
43 CFMutableDictionaryRef query
= makeBaseKeyDictionary();
45 CFMutableArrayRef searchList
= (CFMutableArrayRef
) CFArrayCreateMutable(kCFAllocatorDefault
, 1, &kCFTypeArrayCallBacks
);
46 CFArrayAppendValue((CFMutableArrayRef
)searchList
, kc
);
47 CFDictionarySetValue(query
, kSecMatchSearchList
, searchList
);
49 CFDictionarySetValue(query
, kSecAttrKeyClass
, keyClass
);
51 CFDictionarySetValue(query
, kSecMatchLimit
, kSecMatchLimitAll
);
55 static CFMutableDictionaryRef
makeQueryKeyDictionaryWithLabel(SecKeychainRef kc
, CFStringRef keyClass
, CFStringRef label
) {
56 CFMutableDictionaryRef query
= makeQueryKeyDictionary(kc
, keyClass
);
57 CFDictionarySetValue(query
, kSecAttrLabel
, label
);
61 static CFMutableDictionaryRef
makeAddKeyDictionaryWithApplicationLabel(SecKeychainRef kc
, CFStringRef keyClass
, CFStringRef label
, CFStringRef applicationLabel
) {
62 CFMutableDictionaryRef query
= makeBaseKeyDictionary();
63 CFDictionaryAddValue(query
, kSecUseKeychain
, kc
);
65 CFDictionarySetValue(query
, kSecAttrLabel
, label
);
66 if(applicationLabel
) {
67 CFDictionarySetValue(query
, kSecAttrApplicationLabel
, applicationLabel
);
69 CFDictionarySetValue(query
, kSecAttrApplicationLabel
, CFSTR("test_application")); // without setting this, it uses the current datetime.
73 if(CFEqual(keyClass
, kSecAttrKeyClassSymmetric
)) {
74 CFDictionarySetValue(query
, kSecAttrKeyType
, kSecAttrKeyTypeAES
);
76 } else if(CFEqual(keyClass
, kSecAttrKeyClassPublic
) ||
77 CFEqual(keyClass
, kSecAttrKeyClassPrivate
)) {
78 CFDictionarySetValue(query
, kSecAttrKeyType
, kSecAttrKeyTypeRSA
);
81 CFNumberRef num
= CFNumberCreate(kCFAllocatorDefault
, kCFNumberSInt32Type
, &n
);
82 CFDictionarySetValue(query
, kSecAttrKeySizeInBits
, num
);
86 static CFMutableDictionaryRef
makeAddKeyDictionary(SecKeychainRef kc
, CFStringRef keyClass
, CFStringRef label
) {
87 return makeAddKeyDictionaryWithApplicationLabel(kc
, keyClass
, label
, NULL
);
90 static SecKeyRef
makeCustomKeyWithApplicationLabel(const char* name
, SecKeychainRef kc
, CFStringRef label
, CFStringRef applicationLabel
) {
91 CFMutableDictionaryRef query
= makeAddKeyDictionaryWithApplicationLabel(kc
, kSecAttrKeyClassSymmetric
, label
, applicationLabel
);
93 CFErrorRef error
= NULL
;
94 SecKeyRef item
= SecKeyGenerateSymmetric(query
, &error
);
95 ok(item
!= NULL
, "%s: SecKeyGenerateSymmetric: %ld", name
, error
? CFErrorGetCode(error
) : 0);
100 #define makeCustomKeyWithApplicationLabelTests 1
102 static SecKeyRef
makeCustomKey(const char* name
, SecKeychainRef kc
, CFStringRef label
) {
103 return makeCustomKeyWithApplicationLabel(name
, kc
, label
, NULL
);
105 #define makeCustomKeyTests makeCustomKeyWithApplicationLabelTests
107 static SecKeyRef
makeKey(const char* name
, SecKeychainRef kc
) {
108 return makeCustomKey(name
, kc
, CFSTR("test_key"));
110 #define makeKeyTests makeCustomKeyTests
112 static void makeCustomKeyPair(const char* name
, SecKeychainRef kc
, CFStringRef label
, SecKeyRef
* aPub
, SecKeyRef
* aPriv
) {
113 CFMutableDictionaryRef query
= makeAddKeyDictionary(kc
, kSecAttrKeyClassPublic
, label
);
117 ok_status(SecKeyGeneratePair(query
, &pub
, &priv
), "%s: SecKeyGeneratePair returned a result", name
);
126 CFReleaseNull(query
);
128 #define makeCustomKeyPairTests 1
130 static void makeKeyPair(const char* name
, SecKeychainRef kc
, SecKeyRef
* aPub
, SecKeyRef
* aPriv
) {
131 makeCustomKeyPair(name
, kc
, CFSTR("test_key"), aPub
, aPriv
);
133 #define makeKeyPairTests makeCustomKeyPairTests
135 // This only works for symmetric keys; key pairs cannot ever generate a duplicate (due to setting kSecKeyLabel to the hash of the public key)
136 static void makeCustomDuplicateKey(const char* name
, SecKeychainRef kc
, CFStringRef label
) {
137 CFMutableDictionaryRef query
;
139 query
= makeAddKeyDictionary(kc
, kSecAttrKeyClassSymmetric
, label
);
140 CFErrorRef error
= NULL
;
141 CFReleaseSafe(SecKeyGenerateSymmetric(query
, &error
));
142 is(CFErrorGetCode(error
), errSecDuplicateItem
, "%s: SecKeyGenerateSymmetric (duplicate) errored: %ld", name
, error
? CFErrorGetCode(error
) : -1);
144 CFReleaseNull(query
);
146 #define makeCustomDuplicateKeyTests 1
148 static void makeDuplicateKey(const char* name
, SecKeychainRef kc
) {
149 makeCustomDuplicateKey(name
, kc
, CFSTR("test_key"));
151 #define makeDuplicateKeyTests makeCustomDuplicateKeyTests
153 static SecKeyRef
makeCustomFreeKey(const char* name
, SecKeychainRef kc
, CFStringRef label
) {
156 ok_status(SecKeyGenerate(
159 0, /* contextHandle */
160 CSSM_KEYUSE_ENCRYPT
| CSSM_KEYUSE_DECRYPT
,
161 CSSM_KEYATTR_EXTRACTABLE
,
162 NULL
, /* initialAccess */
163 &symkey
), "%s: SecKeyGenerate", name
);;
165 CFMutableDictionaryRef query
= makeAddKeyDictionary(kc
, kSecAttrKeyClassSymmetric
, label
);
167 CFMutableArrayRef itemList
= (CFMutableArrayRef
) CFArrayCreateMutable(kCFAllocatorDefault
, 1, &kCFTypeArrayCallBacks
);
168 CFArrayAppendValue((CFMutableArrayRef
)itemList
, symkey
);
170 CFDictionarySetValue(query
, kSecUseItemList
, itemList
);
172 CFTypeRef result
= NULL
;
173 ok_status(SecItemAdd(query
, &result
), "%s: SecItemAdd", name
);
174 ok(result
!= NULL
, "%s: SecItemAdd returned a result", name
);
175 CFReleaseNull(symkey
);
176 return (SecKeyRef
) result
;
178 #define makeCustomFreeKeyTests 3
180 static SecKeyRef
makeFreeKey(const char* name
, SecKeychainRef kc
) {
181 return makeCustomFreeKey(name
, kc
, CFSTR("test_free_key"));
183 #define makeFreeKeyTests makeCustomFreeKeyTests
185 static SecKeyRef
makeCustomDuplicateFreeKey(const char* name
, SecKeychainRef kc
, CFStringRef label
) {
188 ok_status(SecKeyGenerate(
191 0, /* contextHandle */
192 CSSM_KEYUSE_ENCRYPT
| CSSM_KEYUSE_DECRYPT
,
193 CSSM_KEYATTR_EXTRACTABLE
,
194 NULL
, /* initialAccess */
195 &symkey
), "%s: SecKeyGenerate", name
);;
197 CFMutableDictionaryRef query
= makeAddKeyDictionary(kc
, kSecAttrKeyClassSymmetric
, label
);
199 CFMutableArrayRef itemList
= (CFMutableArrayRef
) CFArrayCreateMutable(kCFAllocatorDefault
, 1, &kCFTypeArrayCallBacks
);
200 CFArrayAppendValue((CFMutableArrayRef
)itemList
, symkey
);
202 CFDictionarySetValue(query
, kSecUseItemList
, itemList
);
204 CFTypeRef result
= NULL
;
205 is(SecItemAdd(query
, &result
), errSecDuplicateItem
, "%s: SecItemAdd (duplicate)", name
);
206 CFReleaseNull(symkey
);
207 return (SecKeyRef
) result
;
209 #define makeCustomDuplicateFreeKeyTests 2
211 static SecKeyRef
makeDuplicateFreeKey(const char* name
, SecKeychainRef kc
) {
212 return makeCustomFreeKey(name
, kc
, CFSTR("test_free_key"));
214 #define makeDuplicateFreeKeyTests makeCustomDuplicateFreeKeyTests
216 #define checkKeyUseTests 4
217 static void checkKeyUse(SecKeyRef key
, OSStatus expectedStatus
) {
218 CFStringRef plaintext
= CFSTR("A short story: the string goes into the encryptor, and returns unrecognizable. The decryptor reverses.");
219 CFDataRef plaintextData
= CFDataCreate(NULL
, (uint8_t*) CFStringGetCStringPtr(plaintext
, kCFStringEncodingUTF8
), CFStringGetLength(plaintext
));
222 SecTransformRef transform
= SecEncryptTransformCreate(key
, NULL
);
223 SecTransformSetAttribute(transform
, kSecPaddingKey
, kSecPaddingPKCS7Key
, NULL
);
224 SecTransformSetAttribute(transform
, kSecEncryptionMode
, kSecModeCBCKey
, NULL
);
225 SecTransformSetAttribute(transform
, kSecTransformInputAttributeName
, plaintextData
, NULL
);
227 CFErrorRef error
= NULL
;
228 CFDataRef ciphertextData
= SecTransformExecute(transform
, &error
);
231 is(CFErrorGetCode(error
), expectedStatus
, "%s: Encrypting data failed: %d %s (and expected %d)", testName
, (int) CFErrorGetCode(error
), CFStringGetCStringPtr(CFErrorCopyDescription(error
), kCFStringEncodingUTF8
), (int) expectedStatus
);
233 if(expectedStatus
!= errSecSuccess
) {
234 // make test numbers match and quit
235 for(int i
= 1; i
< checkKeyUseTests
; i
++) {
236 pass("test numbers match");
242 pass("%s: transform executed", testName
);
245 CFReleaseSafe(transform
);
247 /* and now decrypt */
248 transform
= SecDecryptTransformCreate(key
, NULL
);
249 SecTransformSetAttribute(transform
, kSecPaddingKey
, kSecPaddingPKCS7Key
, NULL
);
250 SecTransformSetAttribute(transform
, kSecEncryptionMode
, kSecModeCBCKey
, NULL
);
251 SecTransformSetAttribute(transform
, kSecTransformInputAttributeName
, ciphertextData
, NULL
);
253 CFDataRef roundtripData
= SecTransformExecute(transform
, &error
);
254 is(error
, NULL
, "%s: checkKeyUse: SecTransformExecute (decrypt)", testName
);
257 CFStringRef errorStr
= CFErrorCopyDescription(error
);
258 fail("%s: Decrypting data failed: %d %s", testName
, (int) CFErrorGetCode(error
), CFStringGetCStringPtr(errorStr
, kCFStringEncodingUTF8
));
261 pass("%s: make test numbers match", testName
);
264 CFReleaseSafe(transform
);
266 eq_cf(plaintextData
, roundtripData
, "%s: checkKeyUse: roundtripped data is input data", testName
);
268 CFReleaseSafe(plaintext
);
269 CFReleaseSafe(plaintextData
);
270 CFReleaseSafe(ciphertextData
);
271 CFReleaseSafe(roundtripData
);
276 #pragma clang diagnostic pop
280 #endif /* TARGET_OS_MAC */
283 #endif /* kc_key_helpers_h */