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@
24 #import <Security/Security.h>
25 #include "keychain_regressions.h"
26 #include "kc-helpers.h"
27 #include "kc-key-helpers.h"
28 #include "kc-keychain-file-helpers.h"
33 #include <CoreFoundation/CoreFoundation.h>
34 #include <Security/Security.h>
36 /****************************************************************/
38 static OSStatus
GenerateRSAKeyPair(
39 SecKeychainRef keychain
,
43 SecKeyRef
*publicKeyRef
,
44 SecKeyRef
*privateKeyRef
)
47 CFNumberRef keySize
= CFNumberCreate(NULL
, kCFNumberIntType
, &keySizeValue
);
49 // create a SecAccessRef to set up the initial access control settings for this key
50 // (this step is optional; if omitted, the creating application has access to the key)
51 // note: the access descriptor should be the same string as will be used for the item's label,
52 // since it's the string that is displayed by the access confirmation dialog to describe the item.
53 SecAccessRef access
= NULL
;
54 status
= SecAccessCreate(keyLabel
, NULL
, &access
);
56 // create a dictionary of parameters describing the key we want to create
57 CFMutableDictionaryRef params
= CFDictionaryCreateMutable(NULL
, 0,
58 &kCFTypeDictionaryKeyCallBacks
,
59 &kCFTypeDictionaryValueCallBacks
);
61 From the header doc for SecKeyGeneratePair (seems to be incomplete...):
62 * kSecAttrLabel default NULL
63 * kSecAttrIsPermanent if this key is present and has a Boolean
64 value of true, the key or key pair will be added to the default
66 * kSecAttrApplicationTag default NULL
67 * kSecAttrEffectiveKeySize default NULL same as kSecAttrKeySizeInBits
68 * kSecAttrCanEncrypt default false for private keys, true for public keys
69 * kSecAttrCanDecrypt default true for private keys, false for public keys
70 * kSecAttrCanDerive default true
71 * kSecAttrCanSign default true for private keys, false for public keys
72 * kSecAttrCanVerify default false for private keys, true for public keys
73 * kSecAttrCanWrap default false for private keys, true for public keys
74 * kSecAttrCanUnwrap default true for private keys, false for public keys
76 CFDictionaryAddValue( params
, kSecUseKeychain
, keychain
);
77 CFDictionaryAddValue( params
, kSecAttrAccess
, access
);
78 CFDictionaryAddValue( params
, kSecAttrKeyType
, kSecAttrKeyTypeRSA
);
79 CFDictionaryAddValue( params
, kSecAttrKeySizeInBits
, keySize
);
80 CFDictionaryAddValue( params
, kSecAttrIsPermanent
, kCFBooleanTrue
);
83 CFDictionaryAddValue( params
, kSecAttrIsExtractable
, (*extractable
) ? kCFBooleanTrue
: kCFBooleanFalse
);
85 CFDictionaryAddValue( params
, kSecAttrLabel
, keyLabel
);
88 status
= SecKeyGeneratePair(params
, publicKeyRef
, privateKeyRef
);
90 ok_status(status
, "%s: SecKeyGeneratePair", testName
);
92 if (params
) CFRelease(params
);
93 if (keychain
) CFRelease(keychain
);
94 if (access
) CFRelease(access
);
99 static SecAccessRef
MakeNewAccess(SecKeychainItemRef item
, CFStringRef accessLabel
, Boolean allowAny
)
102 SecAccessRef access
= NULL
;
103 CFMutableArrayRef trustedApplications
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
105 if (!allowAny
) // use default access control ("confirm access")
107 // Make an exception list of applications you want to trust,
108 // which are allowed to access the item without requiring user confirmation.
109 // In this example, the calling app and Mail will have access.
110 SecTrustedApplicationRef myself
= NULL
, someOther
= NULL
;
111 status
= SecTrustedApplicationCreateFromPath(NULL
, &myself
);
112 ok_status(status
, "%s: MakeNewAccess: SecTrustedApplicationCreateFromPath (self)", testName
);
114 if (!status
&& myself
) {
115 CFArrayAppendValue(trustedApplications
, myself
);
118 status
= SecTrustedApplicationCreateFromPath("/Applications/Mail.app", &someOther
);
119 ok_status(status
, "%s: MakeNewAccess: SecTrustedApplicationCreateFromPath (Mail.app)", testName
);
121 if (!status
&& someOther
) {
122 CFArrayAppendValue(trustedApplications
, someOther
);
123 CFRelease(someOther
);
127 // If the keychain item already exists, use its access reference; otherwise, create a new one
129 status
= SecKeychainItemCopyAccess(item
, &access
);
130 ok_status(status
, "%s: MakeNewAccess: SecKeychainItemCopyAccess", testName
);
132 status
= SecAccessCreate(accessLabel
, trustedApplications
, &access
);
133 ok_status(status
, "%s: MakeNewAccess: SecAccessCreate", testName
);
135 if (status
) return NULL
;
137 // get the access control list for decryption operations (this controls access to an item's data)
138 CFArrayRef aclList
= NULL
;
139 status
= SecAccessCopySelectedACLList(access
, CSSM_ACL_AUTHORIZATION_DECRYPT
, &aclList
);
140 ok_status(status
, "%s: MakeNewAccess: SecAccessCopySelectedACLList", testName
);
143 // get the first entry in the access control list
144 SecACLRef aclRef
= (SecACLRef
)CFArrayGetValueAtIndex(aclList
, 0);
145 CFArrayRef appList
= NULL
;
146 CFStringRef promptDescription
= NULL
;
147 CSSM_ACL_KEYCHAIN_PROMPT_SELECTOR promptSelector
;
148 status
= SecACLCopySimpleContents(aclRef
, &appList
, &promptDescription
, &promptSelector
);
149 ok_status(status
, "%s: MakeNewAccess: SecAccessCopySimpleContents", testName
);
151 if (allowAny
) // "allow all applications to access this item"
153 // change the decryption ACL to not require the passphrase, and have a NULL application list.
154 promptSelector
.flags
&= ~CSSM_ACL_KEYCHAIN_PROMPT_REQUIRE_PASSPHRASE
;
155 status
= SecACLSetSimpleContents(aclRef
, NULL
, promptDescription
, &promptSelector
);
156 ok_status(status
, "%s: MakeNewAccess: SecACLSetSimpleContents", testName
);
158 else // "allow access by these applications"
160 // modify the application list
161 status
= SecACLSetSimpleContents(aclRef
, trustedApplications
, promptDescription
, &promptSelector
);
162 ok_status(status
, "%s: MakeNewAccess: SecACLSetSimpleContents", testName
);
165 if (appList
) CFRelease(appList
);
166 if (promptDescription
) CFRelease(promptDescription
);
168 if (aclList
) CFRelease(aclList
);
169 if (trustedApplications
) CFRelease(trustedApplications
);
174 static int testCopyKey(SecKeychainRef userKeychain
, SecKeychainRef tempKeychain
)
177 SecAccessRef access
= NULL
;
178 SecKeyRef publicKeyRef
= NULL
;
179 SecKeyRef privateKeyRef
= NULL
;
180 CFStringRef label
= CFSTR("Test Key Copied To Keychain");
183 warnc(EXIT_FAILURE
, "Failed to make a new temporary keychain!");
186 // generate key pair in temporary keychain
187 status
= GenerateRSAKeyPair(tempKeychain
,
190 NULL
, // implicitly extractable
194 if (status
!= errSecSuccess
) {
195 warnc(EXIT_FAILURE
, "Unable to get key pair (error %d)", (int)status
);
198 // export private key from temp keychain to a wrapped data blob
199 CFDataRef exportedData
= NULL
;
200 CFStringRef tempPassword
= CFSTR("MY_TEMPORARY_PASSWORD");
202 SecItemImportExportKeyParameters keyParams
;
203 memset(&keyParams
, 0, sizeof(keyParams
));
204 keyParams
.version
= SEC_KEY_IMPORT_EXPORT_PARAMS_VERSION
;
205 keyParams
.passphrase
= tempPassword
;
207 status
= SecItemExport(privateKeyRef
, kSecFormatWrappedPKCS8
, 0, &keyParams
, &exportedData
);
208 ok_status(status
, "%s: SecItemExport", testName
);
210 if (!exportedData
|| status
!= noErr
) {
211 warnc(EXIT_FAILURE
, "Unable to export key! (error %d)", (int)status
);
214 // set up an explicit access control instance for the imported key
215 // (this example allows unrestricted access to any application)
216 access
= MakeNewAccess(NULL
, label
, true);
217 keyParams
.accessRef
= access
;
219 // import wrapped data blob to user keychain
220 SecExternalFormat format
= kSecFormatWrappedPKCS8
;
221 SecExternalItemType itemType
= kSecItemTypePrivateKey
;
223 CFArrayRef importedItems
= NULL
;
224 status
= SecItemImport(exportedData
, NULL
, &format
, &itemType
, 0, &keyParams
, userKeychain
, &importedItems
);
225 ok_status(status
, "%s: SecItemImport", testName
);
227 if (status
!= noErr
) {
228 warnc(EXIT_FAILURE
, "Unable to import key! (error %d)", (int)status
);
232 // make sure to set a label on our newly imported key, since a label is not part of the PKCS8 format.
233 SecKeyRef importedKey
= (SecKeyRef
) CFArrayGetValueAtIndex(importedItems
, 0);
234 if (CFGetTypeID(importedKey
) == SecKeyGetTypeID()) {
235 // set up a query defining the item(s) to be operated on, in this case, one item uniquely identified by reference
236 CFMutableDictionaryRef query
= CFDictionaryCreateMutable(NULL
, 0, &kCFTypeDictionaryKeyCallBacks
,
237 &kCFTypeDictionaryValueCallBacks
);
238 CFDictionaryAddValue( query
, kSecClass
, kSecClassKey
); // item class is a required attribute in any query
239 CFDictionaryAddValue( query
, kSecValueRef
, importedKey
);
241 // define the attributes to be updated, in this case the label
242 CFMutableDictionaryRef attrs
= CFDictionaryCreateMutable(NULL
, 0, &kCFTypeDictionaryKeyCallBacks
,
243 &kCFTypeDictionaryValueCallBacks
);
244 CFDictionaryAddValue( attrs
, kSecAttrLabel
, label
);
247 status
= SecItemUpdate( query
, attrs
);
248 ok_status(status
, "%s: SecItemUpdate", testName
);
250 if (status
!= errSecSuccess
) {
251 warnc(EXIT_FAILURE
, "Failed to update label of imported key! (error %d)", (int)status
);
257 CFRelease(importedItems
);
260 // ensure that key was copied, and its label changed
261 checkN(testName
, makeQueryKeyDictionaryWithLabel(userKeychain
, kSecAttrKeyClassPrivate
, label
), 1);
263 if (access
) CFRelease(access
);
269 int kc_24_key_copy_keychain(int argc
, char *const *argv
)
272 initializeKeychainTests(__FUNCTION__
);
274 SecKeychainRef keychain
= getPopulatedTestKeychain();
275 SecKeychainRef blankKeychain
= createNewKeychain("forKeys", "password");
277 testCopyKey(keychain
, blankKeychain
);
279 ok_status(SecKeychainDelete(keychain
), "%s: SecKeychainDelete", testName
);
280 ok_status(SecKeychainDelete(blankKeychain
), "%s: SecKeychainDelete", testName
);
282 CFReleaseNull(keychain
);
283 CFReleaseNull(blankKeychain
);
285 checkPrompts(0, "No prompts while importing items");