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 #import <Security/SecCertificatePriv.h>
27 #include "keychain_regressions.h"
28 #include "kc-helpers.h"
31 // Test for <rdar://9251635>
37 #include <CoreFoundation/CoreFoundation.h>
38 #include <Security/Security.h>
39 #include <Security/SecItemPriv.h>
41 #pragma clang diagnostic ignored "-Wdeprecated-declarations"
44 Note: the following will show all private keys with their label
45 and extractable attribute value (in attribute 0x00000010):
47 $ security dump | grep -A 16 "0x00000000 <uint32>=0x00000010" | grep -e ^\ *0x000000[01][01] -e --
51 static OSStatus
GenerateRSAKeyPair(
52 SecKeychainRef keychain
,
56 SecKeyRef
*publicKeyRef
,
57 SecKeyRef
*privateKeyRef
)
60 CFNumberRef keySize
= CFNumberCreate(NULL
, kCFNumberIntType
, &keySizeValue
);
62 // create a SecAccessRef to set up the initial access control settings for this key
63 // (this step is optional; if omitted, the creating application has access to the key)
64 // note: the access descriptor should be the same string as will be used for the item's label,
65 // since it's the string that is displayed by the access confirmation dialog to describe the item.
66 SecAccessRef access
= NULL
;
67 status
= SecAccessCreate(keyLabel
, NULL
, &access
);
69 // create a dictionary of parameters describing the key we want to create
70 CFMutableDictionaryRef params
= CFDictionaryCreateMutable(NULL
, 0,
71 &kCFTypeDictionaryKeyCallBacks
,
72 &kCFTypeDictionaryValueCallBacks
);
74 From the header doc for SecKeyGeneratePair (seems to be incomplete...):
75 * kSecAttrLabel default NULL
76 * kSecAttrIsPermanent if this key is present and has a Boolean
77 value of true, the key or key pair will be added to the default
79 * kSecAttrApplicationTag default NULL
80 * kSecAttrEffectiveKeySize default NULL same as kSecAttrKeySizeInBits
81 * kSecAttrCanEncrypt default false for private keys, true for public keys
82 * kSecAttrCanDecrypt default true for private keys, false for public keys
83 * kSecAttrCanDerive default true
84 * kSecAttrCanSign default true for private keys, false for public keys
85 * kSecAttrCanVerify default false for private keys, true for public keys
86 * kSecAttrCanWrap default false for private keys, true for public keys
87 * kSecAttrCanUnwrap default true for private keys, false for public keys
89 CFDictionaryAddValue( params
, kSecUseKeychain
, keychain
);
90 CFDictionaryAddValue( params
, kSecAttrAccess
, access
);
91 CFDictionaryAddValue( params
, kSecAttrKeyType
, kSecAttrKeyTypeRSA
);
92 CFDictionaryAddValue( params
, kSecAttrKeySizeInBits
, keySize
);
93 CFDictionaryAddValue( params
, kSecAttrIsPermanent
, kCFBooleanTrue
);
96 CFDictionaryAddValue( params
, kSecAttrIsExtractable
, (*extractable
) ? kCFBooleanTrue
: kCFBooleanFalse
);
98 CFDictionaryAddValue( params
, kSecAttrLabel
, keyLabel
);
101 status
= SecKeyGeneratePair(params
, publicKeyRef
, privateKeyRef
);
102 ok_status(status
, "%s: SecKeyGeneratePair", testName
);
104 if (params
) CFRelease(params
);
105 if (access
) CFRelease(access
);
110 static int testExtractable(
111 SecKeychainRef keychain
,
116 SecKeyRef publicKeyRef
= NULL
;
117 SecKeyRef privateKeyRef
= NULL
;
118 CFStringRef label
= (extractable
) ? CFSTR("test-extractable-YES") : CFSTR("test-extractable-NO");
119 Boolean
*extractablePtr
= (explicit) ? &extractable
: NULL
;
121 status
= GenerateRSAKeyPair(keychain
,
128 if (status
!= noErr
) {
129 //errx(EXIT_FAILURE, "Unable to get key pair (err = %d)", status);
133 // check that the attributes of the generated private key are what we think they are
134 const CSSM_KEY
*cssmPrivKey
;
135 status
= SecKeyGetCSSMKey(privateKeyRef
, &cssmPrivKey
);
136 ok_status(status
, "%s: SecKeyGetCSSMKey", testName
);
138 if (status
!= noErr
) {
139 //errx(EXIT_FAILURE, "Unable to get CSSM reference key (err = %d)", status);
143 ok(cssmPrivKey
->KeyHeader
.KeyAttr
& CSSM_KEYATTR_EXTRACTABLE
, "%s: check private key marked as extractable (as requested)", testName
);
144 if (!(cssmPrivKey
->KeyHeader
.KeyAttr
& CSSM_KEYATTR_EXTRACTABLE
)) {
145 //errx(EXIT_FAILURE, "Oops! the private key was not marked as extractable!");
150 ok(!(cssmPrivKey
->KeyHeader
.KeyAttr
& CSSM_KEYATTR_EXTRACTABLE
), "%s: check private key marked as non-extractable (as requested)", testName
);
151 if (cssmPrivKey
->KeyHeader
.KeyAttr
& CSSM_KEYATTR_EXTRACTABLE
) {
152 //errx(EXIT_FAILURE, "Oops! the private key was marked as extractable!");
157 SecKeyImportExportParameters keyParams
;
158 memset(&keyParams
, 0, sizeof(keyParams
));
159 keyParams
.version
= SEC_KEY_IMPORT_EXPORT_PARAMS_VERSION
;
160 keyParams
.passphrase
= CFSTR("borken");
162 CFDataRef exportedData
= NULL
;
164 status
= SecKeychainItemExport(privateKeyRef
, kSecFormatWrappedPKCS8
, 0, &keyParams
, &exportedData
);
166 ok_status(status
, "%s: SecKeychainItemExport (PKCS8) (and we expected it to succeed)", testName
);
168 is(status
, errSecDataNotAvailable
, "%s: SecKeychainItemExport (PKCS8) (and we expected this to fail with errSecDataNotAvailable)", testName
);
171 status
= SecKeychainItemExport(privateKeyRef
, kSecFormatPKCS12
, 0, &keyParams
, &exportedData
);
173 ok_status(status
, "%s: SecKeychainItemExport(and we expected it to succeed)", testName
);
175 is(status
, errSecDataNotAvailable
, "%s: SecKeychainItemExport (PKCS12) (and we expected this to fail with errSecDataNotAvailable)", testName
);
178 if (status
!= noErr
) {
180 //errx(EXIT_FAILURE, "Unable to export extractable key! (err = %d)", status);
184 status
= 0; // wasn't extractable, so this is the expected result
187 else if (status
== noErr
&& !extractable
) {
188 //errx(EXIT_FAILURE, "Was able to export non-extractable key! (err = %d)", status);
192 status
= SecKeychainItemDelete((SecKeychainItemRef
)publicKeyRef
);
193 ok_status(status
, "%s: SecKeychainItemDelete", testName
);
194 if (status
!= noErr
) {
195 warnx("Unable to delete created public key from keychain (err = %d)", (int)status
);
198 status
= SecKeychainItemDelete((SecKeychainItemRef
)privateKeyRef
);
199 ok_status(status
, "%s: SecKeychainItemDelete", testName
);
200 if (status
!= noErr
) {
201 warnx("Unable to delete created private key from keychain (err = %d)", (int)status
);
204 CFRelease(publicKeyRef
);
205 CFRelease(privateKeyRef
);
211 int kc_27_key_non_extractable(int argc
, char *const *argv
)
214 initializeKeychainTests(__FUNCTION__
);
216 SecKeychainRef kc
= getPopulatedTestKeychain();
218 // test case 1: extractable
219 startTest("Extract extractable key");
220 testExtractable(kc
, TRUE
, TRUE
);
222 // test case 2: non-extractable
223 startTest("Extract non-extractable key");
224 testExtractable(kc
, FALSE
, TRUE
);
226 // test case 3: extractable (when not explicitly specified)
227 startTest("Extract implicitly extractable key");
228 testExtractable(kc
, TRUE
, FALSE
);
230 ok_status(SecKeychainDelete(kc
), "%s: SecKeychainDelete", testName
);