]> git.saurik.com Git - apple/security.git/blob - OSX/sec/Security/Regressions/secitem/si-29-cms-chain-mode.m
Security-59306.41.2.tar.gz
[apple/security.git] / OSX / sec / Security / Regressions / secitem / si-29-cms-chain-mode.m
1 /*
2 * Copyright (c) 2019 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 #include <AssertMacros.h>
25 #import <Foundation/Foundation.h>
26
27 #include <Security/SecCertificatePriv.h>
28 #include <Security/SecIdentity.h>
29 #include <Security/SecCMS.h>
30 #include <Security/CMSEncoder.h>
31 #include <Security/CMSDecoder.h>
32 #include <Security/SecImportExport.h>
33 #include <Security/SecCmsBase.h>
34
35 #include <utilities/SecCFWrappers.h>
36
37 #if TARGET_OS_OSX
38 #include <Security/SecKeychain.h>
39 #define kSystemLoginKeychainPath "/Library/Keychains/System.keychain"
40 #endif
41
42 #include "shared_regressions.h"
43
44 #include "si-29-cms-chain-mode.h"
45
46 static NSData* CMSEncoder_encode_for_chain_mode(SecIdentityRef identity, CMSCertificateChainMode chainMode) {
47 CMSEncoderRef encoder = NULL;
48 CFDataRef message = NULL;
49
50 /* Create encoder */
51 require_noerr_action(CMSEncoderCreate(&encoder), exit, fail("Failed to create CMS encoder"));
52 require_noerr_action(CMSEncoderSetSignerAlgorithm(encoder, kCMSEncoderDigestAlgorithmSHA256), exit,
53 fail("Failed to set digest algorithm to SHA256"));
54
55 /* Set identity as signer */
56 require_noerr_action(CMSEncoderAddSigners(encoder, identity), exit, fail("Failed to add signer identity"));
57
58 /* Set chain mode */
59 require_noerr_action(CMSEncoderSetCertificateChainMode(encoder, chainMode), exit, fail("Failed to set chain mode"));
60
61 /* Load content */
62 CMSEncoderUpdateContent(encoder, _chain_mode_content, sizeof(_chain_mode_content));
63
64 /* output cms message */
65 CMSEncoderCopyEncodedContent(encoder, &message);
66
67 exit:
68 CFReleaseNull(encoder);
69 return CFBridgingRelease(message);
70 }
71
72 static NSData* SecCMS_encode_for_chain_mode(SecIdentityRef identity, SecCmsCertChainMode chainMode) {
73 NSMutableData *data = [NSMutableData data];
74 NSData *content = [NSData dataWithBytes:_chain_mode_content length:sizeof(_chain_mode_content)];
75 NSDictionary *parameters = @{
76 (__bridge NSString*)kSecCMSSignHashAlgorithm : (__bridge NSString*)kSecCMSHashingAlgorithmSHA256,
77 (__bridge NSString*)kSecCMSCertChainMode : [NSString stringWithFormat:@"%d", chainMode],
78 };
79 SecCMSCreateSignedData(identity, (__bridge CFDataRef)content, (__bridge CFDictionaryRef)parameters, nil, (__bridge CFMutableDataRef)data);
80
81 if (data.length > 0) {
82 return data;
83 }
84 return nil;
85 }
86
87 static NSArray* CMSDecoder_copy_certs(NSData *cms_message) {
88 CMSDecoderRef decoder = NULL;
89 CFArrayRef certs = NULL;
90
91 if (!cms_message) {
92 return nil;
93 }
94
95 require_noerr_action(CMSDecoderCreate(&decoder), exit, fail("Failed to create CMS decoder"));
96 require_noerr_action(CMSDecoderUpdateMessage(decoder, cms_message.bytes, cms_message.length), exit,
97 fail("Failed to update decoder with CMS message"));
98 require_noerr_action(CMSDecoderFinalizeMessage(decoder), exit, fail("Failed to finalize decoder"));
99 ok_status(CMSDecoderCopyAllCerts(decoder, &certs), "Failed to get certs from cms message");
100
101 exit:
102 CFReleaseNull(decoder);
103 return CFBridgingRelease(certs);
104 }
105
106 static void SecCMS_root_unavailable_tests(SecIdentityRef identity) {
107 /* Chain Mode None */
108 NSData *cms_message = SecCMS_encode_for_chain_mode(identity, SecCmsCMNone);
109 NSArray *certs = CMSDecoder_copy_certs(cms_message);
110 is(0, certs.count, "Expected 0 certs, got %lu", (unsigned long)certs.count);
111
112 /* Chain Mode: Signer */
113 cms_message = SecCMS_encode_for_chain_mode(identity, SecCmsCMCertOnly);
114 certs = CMSDecoder_copy_certs(cms_message);
115 is(certs.count, 1, "Expected 1 certs, got %lu", (unsigned long)certs.count);
116
117 /* Chain Mode: Chain */
118 cms_message = SecCMS_encode_for_chain_mode(identity, SecCmsCMCertChain);
119 certs = CMSDecoder_copy_certs(cms_message);
120 is(certs.count, 2, "Expected 2 certs, got %lu", (unsigned long)certs.count);
121
122 /* Chain Mode: Chain With Root */
123 cms_message = SecCMS_encode_for_chain_mode(identity, SecCmsCMCertChainWithRoot);
124 certs = CMSDecoder_copy_certs(cms_message);
125 is(certs.count, 2, "Expected 2 certs, got %lu", (unsigned long)certs.count);
126
127 /* Chain Mode: Chain With Root or Fail */
128 /* We shouldn't be able to find the root, so we shouldn't be able to make the CMS message */
129 cms_message = SecCMS_encode_for_chain_mode(identity, SecCmsCMCertChainWithRootOrFail);
130 ok(cms_message == nil, "Expected to fail to encode CMS message without root in keychain");
131 }
132
133 static void CMSEncoder_root_unavailable_tests(SecIdentityRef identity) {
134 /* Chain Mode None */
135 NSData *cms_message = CMSEncoder_encode_for_chain_mode(identity, kCMSCertificateNone);
136 NSArray *certs = CMSDecoder_copy_certs(cms_message);
137 is(certs.count, 0, "Expected 0 certs, got %lu", (unsigned long)certs.count);
138
139 /* Chain Mode: Signer */
140 cms_message = CMSEncoder_encode_for_chain_mode(identity, kCMSCertificateSignerOnly);
141 certs = CMSDecoder_copy_certs(cms_message);
142 is(certs.count, 1, "Expected 1 certs, got %lu", (unsigned long)certs.count);
143
144 /* Chain Mode: Chain */
145 cms_message = CMSEncoder_encode_for_chain_mode(identity, kCMSCertificateChain);
146 certs = CMSDecoder_copy_certs(cms_message);
147 is(certs.count, 2, "Expected 2 certs, got %lu", (unsigned long)certs.count);
148
149 /* Chain Mode: Chain With Root */
150 cms_message = CMSEncoder_encode_for_chain_mode(identity, kCMSCertificateChainWithRoot);
151 certs = CMSDecoder_copy_certs(cms_message);
152 is(certs.count, 2, "Expected 2 certs, got %lu", (unsigned long)certs.count);
153
154 /* Chain Mode: Chain With Root or Fail */
155 /* We shouldn't be able to find the root, so we shouldn't be able to make the CMS message */
156 cms_message = CMSEncoder_encode_for_chain_mode(identity, kCMSCertificateChainWithRootOrFail);
157 ok(cms_message == nil, "Expected to fail to encode CMS message without root in keychain");
158 }
159
160 static void SecCMS_root_available_tests(SecIdentityRef identity) {
161 /* Chain Mode None */
162 NSData *cms_message = SecCMS_encode_for_chain_mode(identity, SecCmsCMNone);
163 NSArray *certs = CMSDecoder_copy_certs(cms_message);
164 is(certs.count, 0, "Expected 0 certs, got %lu", (unsigned long)certs.count);
165
166 /* Chain Mode: Signer */
167 cms_message = SecCMS_encode_for_chain_mode(identity, SecCmsCMCertOnly);
168 certs = CMSDecoder_copy_certs(cms_message);
169 is(certs.count, 1, "Expected 1 certs, got %lu", (unsigned long)certs.count);
170
171 /* Chain Mode: Chain */
172 cms_message = SecCMS_encode_for_chain_mode(identity, SecCmsCMCertChain);
173 certs = CMSDecoder_copy_certs(cms_message);
174 is(certs.count, 2, "Expected 2 certs, got %lu", (unsigned long)certs.count);
175
176 /* Chain Mode: Chain With Root */
177 cms_message = SecCMS_encode_for_chain_mode(identity, SecCmsCMCertChainWithRoot);
178 certs = CMSDecoder_copy_certs(cms_message);
179 is(certs.count, 3, "Expected 3 certs, got %lu", (unsigned long)certs.count);
180
181 /* Chain Mode: Chain With Root or Fail */
182 cms_message = SecCMS_encode_for_chain_mode(identity, SecCmsCMCertChainWithRootOrFail);
183 certs = CMSDecoder_copy_certs(cms_message);
184 is(certs.count, 3, "Expected 3 certs, got %lu", (unsigned long)certs.count);
185 }
186
187 static void CMSEncoder_root_available_tests(SecIdentityRef identity) {
188 /* Chain Mode None */
189 NSData *cms_message = CMSEncoder_encode_for_chain_mode(identity, kCMSCertificateNone);
190 NSArray *certs = CMSDecoder_copy_certs(cms_message);
191 is(certs.count, 0, "Expected 0 certs, got %lu", (unsigned long)certs.count);
192
193 /* Chain Mode: Signer */
194 cms_message = CMSEncoder_encode_for_chain_mode(identity, kCMSCertificateSignerOnly);
195 certs = CMSDecoder_copy_certs(cms_message);
196 is(certs.count, 1, "Expected 1 certs, got %lu", (unsigned long)certs.count);
197
198 /* Chain Mode: Chain */
199 cms_message = CMSEncoder_encode_for_chain_mode(identity, kCMSCertificateChain);
200 certs = CMSDecoder_copy_certs(cms_message);
201 is(certs.count, 2, "Expected 2 certs, got %lu", (unsigned long)certs.count);
202
203 /* Chain Mode: Chain With Root */
204 cms_message = CMSEncoder_encode_for_chain_mode(identity, kCMSCertificateChainWithRoot);
205 certs = CMSDecoder_copy_certs(cms_message);
206 is(certs.count, 3, "Expected 3 certs, got %lu", (unsigned long)certs.count);
207
208 /* Chain Mode: Chain With Root or Fail */
209 cms_message = CMSEncoder_encode_for_chain_mode(identity, kCMSCertificateChainWithRootOrFail);
210 certs = CMSDecoder_copy_certs(cms_message);
211 is(certs.count, 3, "Expected 3 certs, got %lu", (unsigned long)certs.count);
212 }
213
214 static bool setup_keychain(const uint8_t *p12, size_t p12_len, SecIdentityRef *identity) {
215 CFArrayRef tmp_imported_items = NULL;
216 NSArray *imported_items = nil;
217
218 NSDictionary *options = @{ (__bridge NSString *)kSecImportExportPassphrase : @"password" };
219 NSData *p12Data = [NSData dataWithBytes:p12 length:p12_len];
220 require_noerr_action(SecPKCS12Import((__bridge CFDataRef)p12Data, (__bridge CFDictionaryRef)options,
221 &tmp_imported_items), exit,
222 fail("Failed to import identity"));
223 imported_items = CFBridgingRelease(tmp_imported_items);
224 require_noerr_action([imported_items count] == 0 &&
225 [imported_items[0] isKindOfClass:[NSDictionary class]], exit,
226 fail("Wrong imported items output"));
227 *identity = (SecIdentityRef)CFBridgingRetain(imported_items[0][(__bridge NSString*)kSecImportItemIdentity]);
228 require_action(*identity, exit, fail("Failed to get identity"));
229
230 return true;
231
232 exit:
233 return false;
234 }
235
236 static void cleanup_keychain(SecIdentityRef identity, NSArray *certs) {
237 #if TARGET_OS_OSX
238 // SecPKCS12Import adds the items to the keychain on macOS
239 NSDictionary *query = @{ (__bridge NSString*)kSecValueRef : (__bridge id)identity };
240 ok_status(SecItemDelete((__bridge CFDictionaryRef)query), "failed to remove identity from keychain");
241 #else
242 pass("skip test on iOS");
243 #endif
244 for(id cert in certs) {
245 NSDictionary *cert_query = @{ (__bridge NSString*)kSecValueRef : cert };
246 ok_status(SecItemDelete((__bridge CFDictionaryRef)cert_query), "failed to remove cert from keychain");
247 }
248 }
249
250 static void add_cert_to_keychain(SecCertificateRef cert) {
251 #if TARGET_OS_OSX
252 SecKeychainRef kcRef = NULL;
253 SecKeychainOpen(kSystemLoginKeychainPath, &kcRef);
254 if (!kcRef) {
255 fail("failed to open system keychain");
256 return;
257 }
258 NSDictionary *query = @{
259 (__bridge NSString*)kSecValueRef : (__bridge id)cert,
260 (__bridge NSString*)kSecUseKeychain: (__bridge id)kcRef,
261 };
262 #else
263 NSDictionary *query = @{ (__bridge NSString*)kSecValueRef : (__bridge id)cert};
264 #endif
265 ok_status(SecItemAdd((__bridge CFDictionaryRef)query, NULL), "failed to add cert to keychain. following tests may fail.");
266 }
267
268 int si_29_cms_chain_mode(int argc, char *const *argv) {
269 plan_tests(43);
270
271 SecIdentityRef identity = NULL;
272 SecCertificateRef root = SecCertificateCreateWithBytes(NULL, _chain_mode_root, sizeof(_chain_mode_root));
273 SecCertificateRef intermediate = SecCertificateCreateWithBytes(NULL, _chain_mode_subca, sizeof(_chain_mode_subca));
274
275 if (setup_keychain(_chain_mode_leaf_p12 , sizeof(_chain_mode_leaf_p12), &identity)) {
276 add_cert_to_keychain(intermediate);
277
278 SecCMS_root_unavailable_tests(identity);
279 CMSEncoder_root_unavailable_tests(identity);
280
281 add_cert_to_keychain(root);
282
283 SecCMS_root_available_tests(identity);
284 CMSEncoder_root_available_tests(identity);
285
286 cleanup_keychain(identity, @[(__bridge id) intermediate, (__bridge id)root]);
287 }
288
289 CFReleaseNull(identity);
290 CFReleaseNull(root);
291 CFReleaseNull(intermediate);
292
293 return 0;
294 }