]>
Commit | Line | Data |
---|---|---|
fa7225c8 A |
1 | /* |
2 | * Copyright (c) 2016 Apple Inc. All Rights Reserved. | |
3 | * | |
4 | * @APPLE_LICENSE_HEADER_START@ | |
5 | * | |
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 | |
11 | * file. | |
12 | * | |
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. | |
20 | * | |
21 | * @APPLE_LICENSE_HEADER_END@ | |
22 | */ | |
23 | ||
24 | ||
25 | #import <Foundation/Foundation.h> | |
26 | ||
27 | #include "shared_regressions.h" | |
28 | ||
29 | static void create_random_key_worker(id keyType, int keySize, bool permPub, bool permPriv) { | |
30 | NSDictionary *params = nil; | |
31 | NSError *error = nil; | |
32 | ||
33 | params = @{ | |
34 | (id)kSecAttrKeyType: keyType, | |
35 | (id)kSecAttrKeySizeInBits: @(keySize), | |
36 | (id)kSecAttrLabel: @"si-44-seckey-gen:0", | |
37 | (id)kSecPublicKeyAttrs: @{ | |
38 | (id)kSecAttrIsPermanent: @(permPub), | |
39 | }, | |
40 | (id)kSecPrivateKeyAttrs: @{ | |
41 | (id)kSecAttrIsPermanent: @(permPriv), | |
42 | }, | |
43 | }; | |
44 | ||
45 | id privateKey = CFBridgingRelease(SecKeyCreateRandomKey((CFDictionaryRef)params, (void *)&error)); | |
6b200bc3 A |
46 | ok(privateKey != nil, "generating key (type:%@, size:%d, permPub:%d, permPriv:%d) : %@", keyType, keySize, (int)permPub, (int)permPriv, error); |
47 | ||
48 | id publicKey = CFBridgingRelease(SecKeyCopyPublicKey((SecKeyRef)privateKey)); | |
49 | ok(publicKey != nil, "got public key from generated private key"); | |
fa7225c8 | 50 | |
866f8763 A |
51 | error = nil; |
52 | NSData *pubKeyData = CFBridgingRelease(SecKeyCopyExternalRepresentation((SecKeyRef)publicKey, (void *)&error)); | |
53 | ok(pubKeyData != nil, "get public key data, error %@", error); | |
54 | ||
fa7225c8 A |
55 | params = @{ |
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, | |
62 | }; | |
63 | NSArray *items = nil; | |
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"); | |
67 | ||
866f8763 A |
68 | if (!permPub) { |
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, | |
74 | }; | |
75 | NSData *pRef; | |
76 | ok_status(SecItemAdd((CFDictionaryRef)add, (void *)&pRef), "Add public key"); | |
77 | ok([pRef isKindOfClass:NSData.class] || [pRef isKindOfClass:NSArray.class] , "got persistent ref"); | |
78 | } else { | |
79 | ok(true); | |
80 | ok(true); | |
81 | } | |
82 | ||
83 | if (!permPriv) { | |
84 | // Store public key into keychain explicitly. | |
85 | NSDictionary *add = @{ | |
86 | (id)kSecValueRef: (id)privateKey, | |
fa7225c8 | 87 | (id)kSecAttrLabel: @"si-44-seckey-gen:0", |
866f8763 | 88 | (id)kSecReturnPersistentRef: @YES, |
fa7225c8 | 89 | }; |
866f8763 A |
90 | NSData *pRef; |
91 | ok_status(SecItemAdd((CFDictionaryRef)add, (void *)&pRef), "Add private key"); | |
92 | ok([pRef isKindOfClass:NSData.class] || [pRef isKindOfClass:NSArray.class] , "got persistent ref"); | |
93 | } else { | |
94 | ok(true); | |
95 | ok(true); | |
fa7225c8 | 96 | } |
866f8763 A |
97 | |
98 | items = nil; | |
99 | ok_status(SecItemCopyMatching((CFDictionaryRef)params, (void *)&items), "keychain query for generated keys"); | |
100 | is((int)items.count, 2, "keypair is in the keychain"); | |
101 | ||
102 | params = @{ | |
103 | (id)kSecClass: (id)kSecClassKey, | |
104 | (id)kSecAttrKeyType: keyType, | |
105 | (id)kSecAttrKeySizeInBits: @(keySize), | |
106 | #if TARGET_OS_OSX | |
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, | |
111 | #endif | |
112 | (id)kSecAttrLabel: @"si-44-seckey-gen:0", | |
113 | }; | |
114 | ok_status(SecItemDelete((CFDictionaryRef)params), "clear generated pair from keychain"); | |
fa7225c8 A |
115 | } |
116 | ||
117 | static void test_create_random_key() { | |
118 | create_random_key_worker((id)kSecAttrKeyTypeRSA, 1024, false, false); | |
fa7225c8 A |
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); | |
fa7225c8 A |
122 | create_random_key_worker((id)kSecAttrKeyTypeECSECPrimeRandom, 256, false, true); |
123 | create_random_key_worker((id)kSecAttrKeyTypeECSECPrimeRandom, 256, true, true); | |
866f8763 A |
124 | |
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); | |
130 | } | |
131 | static const int TestCountCreateRandomKey = (3 * 12) * 2; | |
132 | ||
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; | |
138 | ||
139 | NSError *error; | |
140 | id privKey = CFBridgingRelease(SecKeyCreateRandomKey((CFDictionaryRef)params, (void *)&error)); | |
141 | ok(privKey != nil, "generatekey: %@", privKey); | |
142 | ||
143 | id pubKey = CFBridgingRelease(SecKeyCopyPublicKey((SecKeyRef)privKey)); | |
144 | ok(pubKey != nil, "get pub key"); | |
145 | ||
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]; | |
151 | }]; | |
152 | ok(!failed, "retrieved attributes differ (privkey): expected: %@ - got: %@", attrs, result); | |
153 | ||
154 | result = nil; | |
155 | ok_status(SecItemCopyMatching((CFDictionaryRef)@{(id)kSecValueRef: pubKey, (id)kSecReturnAttributes: @YES}, (void *)&result), "copymatching create privkey"); | |
156 | failed = NO; | |
157 | [attrs enumerateKeysAndObjectsUsingBlock:^(id _Nonnull key, id _Nonnull obj, BOOL * _Nonnull stop) { | |
158 | failed = failed || ![result[key] isEqual:obj]; | |
159 | }]; | |
160 | ok(!failed, "retrieved attributes differ (pubkey): expected: %@ - got: %@", attrs, result); | |
161 | ||
162 | // Cleanup of the keys. | |
163 | ok_status(SecItemDelete((CFDictionaryRef)@{(id)kSecValueRef: privKey})); | |
164 | ok_status(SecItemDelete((CFDictionaryRef)@{(id)kSecValueRef: pubKey})); | |
165 | } | |
166 | static const int TestCountCreateKeyWithAttrsRun = 8; | |
167 | ||
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]}); | |
fa7225c8 | 173 | } |
866f8763 | 174 | static const int TestCountCreateKeyWithAttrs = 3 * TestCountCreateKeyWithAttrsRun; |
fa7225c8 | 175 | |
866f8763 A |
176 | static const int TestCount = |
177 | TestCountCreateRandomKey + | |
178 | TestCountCreateKeyWithAttrs | |
179 | ; | |
fa7225c8 A |
180 | |
181 | int si_44_seckey_gen(int argc, char *const *argv) { | |
182 | plan_tests(TestCount); | |
183 | ||
184 | @autoreleasepool { | |
185 | test_create_random_key(); | |
866f8763 | 186 | test_create_key_with_attrs(); |
fa7225c8 A |
187 | } |
188 | ||
189 | return 0; | |
190 | } |