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@
25 #import <Foundation/Foundation.h>
27 #include "shared_regressions.h"
29 static void create_random_key_worker(id keyType, int keySize, bool permPub, bool permPriv) {
30 NSDictionary *params = nil;
34 (id)kSecAttrKeyType: keyType,
35 (id)kSecAttrKeySizeInBits: @(keySize),
36 (id)kSecAttrLabel: @"si-44-seckey-gen:0",
37 (id)kSecPublicKeyAttrs: @{
38 (id)kSecAttrIsPermanent: @(permPub),
40 (id)kSecPrivateKeyAttrs: @{
41 (id)kSecAttrIsPermanent: @(permPriv),
45 id privateKey = CFBridgingRelease(SecKeyCreateRandomKey((CFDictionaryRef)params, (void *)&error));
46 ok(privateKey != nil, "generating key (type:%@, size:%d, permPub:%d, permPriv:%d) : %@", keyType, keySize, (int)permPub, (int)permPriv, error);
48 id publicKey = CFBridgingRelease(SecKeyCopyPublicKey((SecKeyRef)privateKey));
49 ok(publicKey != nil, "got public key from generated private key");
52 NSData *pubKeyData = CFBridgingRelease(SecKeyCopyExternalRepresentation((SecKeyRef)publicKey, (void *)&error));
53 ok(pubKeyData != nil, "get public key data, error %@", error);
56 (id)kSecClass: (id)kSecClassKey,
57 (id)kSecAttrKeyType: keyType,
58 (id)kSecAttrKeySizeInBits: @(keySize),
59 (id)kSecAttrLabel: @"si-44-seckey-gen:0",
60 (id)kSecMatchLimit: (id)kSecMatchLimitAll,
61 (id)kSecReturnAttributes: @YES,
64 OSStatus expected = (permPub || permPriv) ? errSecSuccess : errSecItemNotFound;
65 is_status(SecItemCopyMatching((CFDictionaryRef)params, (void *)&items), expected, "keychain query for generated keys");
66 is((int)items.count, (permPub ? 1 : 0) + (permPriv ? 1 : 0), "found keys in the keychain");
69 // Store public key into keychain explicitly.
70 NSDictionary *add = @{
71 (id)kSecValueRef: (id)publicKey,
72 (id)kSecAttrLabel: @"si-44-seckey-gen:0",
73 (id)kSecReturnPersistentRef: @YES,
76 ok_status(SecItemAdd((CFDictionaryRef)add, (void *)&pRef), "Add public key");
77 ok([pRef isKindOfClass:NSData.class] || [pRef isKindOfClass:NSArray.class] , "got persistent ref");
84 // Store public key into keychain explicitly.
85 NSDictionary *add = @{
86 (id)kSecValueRef: (id)privateKey,
87 (id)kSecAttrLabel: @"si-44-seckey-gen:0",
88 (id)kSecReturnPersistentRef: @YES,
91 ok_status(SecItemAdd((CFDictionaryRef)add, (void *)&pRef), "Add private key");
92 ok([pRef isKindOfClass:NSData.class] || [pRef isKindOfClass:NSArray.class] , "got persistent ref");
99 ok_status(SecItemCopyMatching((CFDictionaryRef)params, (void *)&items), "keychain query for generated keys");
100 is((int)items.count, 2, "keypair is in the keychain");
103 (id)kSecClass: (id)kSecClassKey,
104 (id)kSecAttrKeyType: keyType,
105 (id)kSecAttrKeySizeInBits: @(keySize),
107 // Despite headerdoc and other docs, SecItemDelete on macOS deletes only first found item, we need to persuade
108 // it to delete everything passing the query. On the other hand, iOS implementation errs out when
109 // kSecMatchLimit is given, so we need to add it only for macOS.
110 (id)kSecMatchLimit: (id)kSecMatchLimitAll,
112 (id)kSecAttrLabel: @"si-44-seckey-gen:0",
114 ok_status(SecItemDelete((CFDictionaryRef)params), "clear generated pair from keychain");
117 static void test_create_random_key() {
118 create_random_key_worker((id)kSecAttrKeyTypeRSA, 1024, false, false);
119 create_random_key_worker((id)kSecAttrKeyTypeRSA, 1024, false, true);
120 create_random_key_worker((id)kSecAttrKeyTypeRSA, 1024, true, true);
121 create_random_key_worker((id)kSecAttrKeyTypeECSECPrimeRandom, 256, false, false);
122 create_random_key_worker((id)kSecAttrKeyTypeECSECPrimeRandom, 256, false, true);
123 create_random_key_worker((id)kSecAttrKeyTypeECSECPrimeRandom, 256, true, true);
125 // Create pair with pub-permanent&priv-nonpermanent currently does not work well, created
126 // pub key has some strange ACL which prevents it from sensible use. However, this combination
127 // also does not make sense at all from usage point of view.
128 // create_random_key_worker((id)kSecAttrKeyTypeRSA, 1024, true, false);
129 // create_random_key_worker((id)kSecAttrKeyTypeECSECPrimeRandom, 256, true, false);
131 static const int TestCountCreateRandomKey = (3 * 12) * 2;
133 static void test_create_key_with_attrs_run(NSDictionary *attrs) {
134 NSMutableDictionary *params = attrs.mutableCopy;
135 params[(id)kSecAttrKeyType] = (id)kSecAttrKeyTypeRSA;
136 params[(id)kSecAttrKeySizeInBits] = @1024;
137 params[(id)kSecAttrIsPermanent] = @YES;
140 id privKey = CFBridgingRelease(SecKeyCreateRandomKey((CFDictionaryRef)params, (void *)&error));
141 ok(privKey != nil, "generatekey: %@", privKey);
143 id pubKey = CFBridgingRelease(SecKeyCopyPublicKey((SecKeyRef)privKey));
144 ok(pubKey != nil, "get pub key");
146 NSDictionary *result;
147 ok_status(SecItemCopyMatching((CFDictionaryRef)@{(id)kSecValueRef: privKey, (id)kSecReturnAttributes: @YES}, (void *)&result), "copymatching create privkey");
148 __block BOOL failed = NO;
149 [attrs enumerateKeysAndObjectsUsingBlock:^(id _Nonnull key, id _Nonnull obj, BOOL * _Nonnull stop) {
150 failed = failed || ![result[key] isEqual:obj];
152 ok(!failed, "retrieved attributes differ (privkey): expected: %@ - got: %@", attrs, result);
155 ok_status(SecItemCopyMatching((CFDictionaryRef)@{(id)kSecValueRef: pubKey, (id)kSecReturnAttributes: @YES}, (void *)&result), "copymatching create privkey");
157 [attrs enumerateKeysAndObjectsUsingBlock:^(id _Nonnull key, id _Nonnull obj, BOOL * _Nonnull stop) {
158 failed = failed || ![result[key] isEqual:obj];
160 ok(!failed, "retrieved attributes differ (pubkey): expected: %@ - got: %@", attrs, result);
162 // Cleanup of the keys.
163 ok_status(SecItemDelete((CFDictionaryRef)@{(id)kSecValueRef: privKey}));
164 ok_status(SecItemDelete((CFDictionaryRef)@{(id)kSecValueRef: pubKey}));
166 static const int TestCountCreateKeyWithAttrsRun = 8;
168 static void test_create_key_with_attrs() {
169 test_create_key_with_attrs_run(@{(id)kSecAttrLabel:@"Label"});
170 test_create_key_with_attrs_run(@{(id)kSecAttrApplicationTag:[@"AppTag" dataUsingEncoding:NSUTF8StringEncoding]});
171 test_create_key_with_attrs_run(@{(id)kSecAttrLabel:@"Label",
172 (id)kSecAttrApplicationTag:[@"AppTag" dataUsingEncoding:NSUTF8StringEncoding]});
174 static const int TestCountCreateKeyWithAttrs = 3 * TestCountCreateKeyWithAttrsRun;
176 static const int TestCount =
177 TestCountCreateRandomKey +
178 TestCountCreateKeyWithAttrs
181 int si_44_seckey_gen(int argc, char *const *argv) {
182 plan_tests(TestCount);
185 test_create_random_key();
186 test_create_key_with_attrs();