2 * Copyright (c) 2018 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@
24 #include <AssertMacros.h>
25 #import <Foundation/Foundation.h>
27 #include <Security/SecIdentity.h>
28 #include <Security/SecCMS.h>
29 #include <Security/CMSEncoder.h>
30 #include <Security/CMSDecoder.h>
31 #include <Security/CMSPrivate.h>
33 #include <utilities/SecCFWrappers.h>
36 #include <Security/SecKeychain.h>
37 #include <Security/SecImportExport.h>
38 #include <Security/CMSPrivate.h>
41 #include "shared_regressions.h"
43 #include "si-35-cms-expiration-time.h"
45 /* MARK: SecCMS tests */
46 static void SecCMS_positive_tests(void) {
47 SecPolicyRef policy = NULL;
48 SecTrustRef trust = NULL;
49 CFDictionaryRef tmpAttrs = NULL;
50 NSDictionary* attrs = nil;
51 NSData *expirationDateOid = nil, *unparsedExpirationDate = nil;
52 NSArray *attrValues = nil;
53 NSDate *expirationDate = nil, *expectedDate = [NSDate dateWithTimeIntervalSinceReferenceDate: 599400000.0];
55 NSData *message = [NSData dataWithBytes:_css_gen_expiration_time length:sizeof(_css_gen_expiration_time)];
56 NSData *content = [NSData dataWithBytes:_css_content length:sizeof(_css_content)];
57 policy = SecPolicyCreateBasicX509();
59 /* verify a valid message and copy out attributes */
60 ok_status(SecCMSVerifyCopyDataAndAttributes((__bridge CFDataRef)message, (__bridge CFDataRef)content, policy, &trust, NULL, &tmpAttrs),
61 "Failed to verify valid CMS message and get out attributes");
62 require_action(attrs = CFBridgingRelease(tmpAttrs), exit, fail("Failed to copy attributes"));
64 /* verify we can get the parsed expiration date attribute out */
65 uint8_t appleExpirationDateOid[] = { 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x63, 0x64, 0x9, 0x3 };
66 expirationDateOid = [NSData dataWithBytes:appleExpirationDateOid length:sizeof(appleExpirationDateOid)];
67 attrValues = attrs[expirationDateOid];
68 is([attrValues count], (size_t)1, "Wrong number of attribute values");
69 require_action(unparsedExpirationDate = attrValues[0], exit, fail("Failed to get expiration date attribute value"));
70 uint8_t expectedUTCData[] = { 0x31, 0x39, 0x31, 0x32, 0x33, 0x30, 0x31, 0x32, 0x30, 0x30, 0x30, 0x30, 0x5a };
71 is([unparsedExpirationDate isEqualToData:[NSData dataWithBytes:expectedUTCData length:sizeof(expectedUTCData)]], true, "Failed to get correct expiration date");
73 /* verify we can get the "cooked" expiration data out */
74 ok(expirationDate = attrs[(__bridge NSString*)kSecCMSExpirationDate], "Failed to get pre-parsed expiration date from attributes");
75 is([expirationDate isEqualToDate:expectedDate], true, "Failed to get correct expiration date");
78 CFReleaseNull(policy);
82 static void SecCMS_negative_date_changed(void) {
83 SecPolicyRef policy = NULL;
84 SecTrustRef trust = NULL;
86 NSMutableData *invalid_message = [NSMutableData dataWithBytes:_css_gen_expiration_time length:sizeof(_css_gen_expiration_time)];
87 [invalid_message resetBytesInRange:NSMakeRange(3980, 1)]; // reset byte in expiration date attribute of _css_gen_expiration_time
88 NSData *content = [NSData dataWithBytes:_css_content length:sizeof(_css_content)];
89 policy = SecPolicyCreateBasicX509();
91 /* Verify message with expiration date changed fails*/
92 is(SecCMSVerifyCopyDataAndAttributes((__bridge CFDataRef)invalid_message, (__bridge CFDataRef)content, policy, &trust, NULL, NULL),
93 errSecAuthFailed, "Failed to verify valid CMS message and get out attributes");
95 CFReleaseNull(policy);
99 static void SecCMS_negative_missing_date(void) {
100 SecPolicyRef policy = NULL;
101 SecTrustRef trust = NULL;
102 CFDictionaryRef tmpAttrs = NULL;
103 NSDictionary *attrs = nil;
104 NSData *expirationDateOid = nil;
106 NSData *message = [NSData dataWithBytes:_no_expiration_attr length:sizeof(_no_expiration_attr)];
107 policy = SecPolicyCreateBasicX509();
109 /* verify a message with no expiration date */
110 ok_status(SecCMSVerifyCopyDataAndAttributes((__bridge CFDataRef)message, NULL, policy, &trust, NULL, &tmpAttrs),
111 "Failed to verify valid CMS message and get out attributes");
112 require_action(attrs = CFBridgingRelease(tmpAttrs), exit, fail("Failed to copy attributes"));
114 /* verify we can't get the expiration date out */
115 uint8_t appleExpirationDateOid[] = { 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x63, 0x64, 0x9, 0x3 };
116 expirationDateOid = [NSData dataWithBytes:appleExpirationDateOid length:sizeof(appleExpirationDateOid)];
117 is(attrs[expirationDateOid], NULL, "Got an expiration date attribute from message with no expiration date");
118 is(attrs[(__bridge NSString*)kSecCMSExpirationDate], NULL, "Got an expiration date attribute from message with no expiration date");
121 CFReleaseNull(policy);
122 CFReleaseNull(trust);
125 static void SecCMS_tests(void) {
126 SecCMS_positive_tests();
127 SecCMS_negative_date_changed();
128 SecCMS_negative_missing_date();
131 /* MARK: CMSEncoder tests */
132 static void CMSEncoder_tests(SecIdentityRef identity) {
133 CMSEncoderRef encoder = NULL;
134 CMSDecoderRef decoder = NULL;
135 CFDataRef message = NULL;
138 require_noerr_action(CMSEncoderCreate(&encoder), exit, fail("Failed to create CMS encoder"));
139 require_noerr_action(CMSEncoderSetSignerAlgorithm(encoder, kCMSEncoderDigestAlgorithmSHA256), exit,
140 fail("Failed to set digest algorithm to SHA256"));
142 /* Set identity as signer */
143 require_noerr_action(CMSEncoderAddSigners(encoder, identity), exit, fail("Failed to add signer identity"));
145 /* Add signing time attribute for 6 June 2018 */
146 require_noerr_action(CMSEncoderAddSignedAttributes(encoder, kCMSAttrSigningTime), exit,
147 fail("Failed to set signing time flag"));
148 require_noerr_action(CMSEncoderSetSigningTime(encoder, 550000000.0), exit, fail("Failed to set signing time"));
150 /* Add expiration date attribute for 30 September 2018 */
151 ok_status(CMSEncoderAddSignedAttributes(encoder, kCMSAttrAppleExpirationTime),
152 "Set expiration date flag");
153 ok_status(CMSEncoderSetAppleExpirationTime(encoder, 560000000.0), "Set Expiration time");
156 require_noerr_action(CMSEncoderSetHasDetachedContent(encoder, true), exit, fail("Failed to set detached content"));
157 require_noerr_action(CMSEncoderUpdateContent(encoder, _css_content, sizeof(_css_content)), exit, fail("Failed to set content"));
159 /* output cms message */
160 ok_status(CMSEncoderCopyEncodedContent(encoder, &message), "Finish encoding and output message");
161 isnt(message, NULL, "Encoded message exists");
164 require_noerr_action(CMSDecoderCreate(&decoder), exit, fail("Create CMS decoder"));
165 require_noerr_action(CMSDecoderUpdateMessage(decoder, CFDataGetBytePtr(message),
166 CFDataGetLength(message)), exit,
167 fail("Update decoder with CMS message"));
168 require_noerr_action(CMSDecoderSetDetachedContent(decoder, (__bridge CFDataRef)[NSData dataWithBytes:_css_content
169 length:sizeof(_css_content)]),
170 exit, fail("Set detached content"));
171 ok_status(CMSDecoderFinalizeMessage(decoder), "Finalize decoder");
174 CFReleaseNull(encoder);
175 CFReleaseNull(message);
176 CFReleaseNull(decoder);
179 /* MARK: CMSDecoder tests */
180 static void CMSDecoder_positive_tests(void) {
181 CMSDecoderRef decoder = NULL;
182 SecPolicyRef policy = NULL;
183 SecTrustRef trust = NULL;
184 CMSSignerStatus signerStatus;
185 NSData *_css_contentData = nil;
186 CFAbsoluteTime expirationTime = 0;
187 NSDate *expirationDate = nil, *expectedDate = [NSDate dateWithTimeIntervalSinceReferenceDate: 599400000.0];
189 /* Create decoder and decode */
190 require_noerr_action(CMSDecoderCreate(&decoder), exit, fail("Failed to create CMS decoder"));
191 require_noerr_action(CMSDecoderUpdateMessage(decoder, _css_gen_expiration_time, sizeof(_css_gen_expiration_time)), exit,
192 fail("Failed to update decoder with CMS message"));
193 _css_contentData = [NSData dataWithBytes:_css_content length:sizeof(_css_content)];
194 require_noerr_action(CMSDecoderSetDetachedContent(decoder, (__bridge CFDataRef)_css_contentData), exit,
195 fail("Failed to set detached _css_content"));
196 ok_status(CMSDecoderFinalizeMessage(decoder), "Finalize decoder");
198 /* Get signer status */
199 require_action(policy = SecPolicyCreateBasicX509(), exit, fail("Failed to Create policy"));
200 ok_status(CMSDecoderCopySignerStatus(decoder, 0, policy, false, &signerStatus, &trust, NULL),
201 "Copy Signer status");
202 is(signerStatus, kCMSSignerValid, "Valid signature");
204 /* Get Expiration Time Attribute value */
205 ok_status(CMSDecoderCopySignerAppleExpirationTime(decoder, 0, &expirationTime),
206 "Got expiration time from message with no expiration attribute");
207 expirationDate = [NSDate dateWithTimeIntervalSinceReferenceDate:expirationTime];
208 is([expirationDate isEqualToDate:expectedDate], true, "Got wrong expiration time"); // 31 December 2019 12:00:00 Zulu
211 CFReleaseNull(decoder);
212 CFReleaseNull(policy);
213 CFReleaseNull(trust);
216 static void CMSDecoder_negative_date_changed(void) {
217 CMSDecoderRef decoder = NULL;
218 SecPolicyRef policy = NULL;
219 SecTrustRef trust = NULL;
220 CMSSignerStatus signerStatus;
221 NSData *_css_contentData = nil;
222 NSMutableData *invalid_message = nil;
224 /* Create decoder and decode */
225 invalid_message = [NSMutableData dataWithBytes:_css_gen_expiration_time length:sizeof(_css_gen_expiration_time)];
226 [invalid_message resetBytesInRange:NSMakeRange(3980, 1)]; // reset byte in expiration date attribute of _css_gen_expiration_time
227 require_noerr_action(CMSDecoderCreate(&decoder), exit, fail("Failed to create CMS decoder"));
228 require_noerr_action(CMSDecoderUpdateMessage(decoder, [invalid_message bytes], [invalid_message length]), exit,
229 fail("Failed to update decoder with CMS message"));
230 _css_contentData = [NSData dataWithBytes:_css_content length:sizeof(_css_content)];
231 require_noerr_action(CMSDecoderSetDetachedContent(decoder, (__bridge CFDataRef)_css_contentData), exit,
232 fail("Failed to set detached _css_content"));
233 ok_status(CMSDecoderFinalizeMessage(decoder), "Finalize decoder");
235 /* Get signer status */
236 require_action(policy = SecPolicyCreateBasicX509(), exit, fail("Failed to Create policy"));
237 ok_status(CMSDecoderCopySignerStatus(decoder, 0, policy, false, &signerStatus, &trust, NULL),
238 "Copy Signer status");
239 is(signerStatus, kCMSSignerInvalidSignature, "Valid signature");
242 CFReleaseNull(decoder);
243 CFReleaseNull(policy);
244 CFReleaseNull(trust);
247 static void CMSDecoder_negative_missing_date(void) {
248 CMSDecoderRef decoder = NULL;
249 SecPolicyRef policy = NULL;
250 SecTrustRef trust = NULL;
251 CMSSignerStatus signerStatus;
252 CFAbsoluteTime expirationTime = 0.0;
254 /* Create decoder and decode */
255 require_noerr_action(CMSDecoderCreate(&decoder), exit, fail("Failed to create CMS decoder"));
256 require_noerr_action(CMSDecoderUpdateMessage(decoder, _no_expiration_attr, sizeof(_no_expiration_attr)), exit,
257 fail("Failed to update decoder with CMS message"));
258 ok_status(CMSDecoderFinalizeMessage(decoder), "Finalize decoder");
260 /* Get signer status */
261 require_action(policy = SecPolicyCreateBasicX509(), exit, fail("Failed to Create policy"));
262 ok_status(CMSDecoderCopySignerStatus(decoder, 0, policy, false, &signerStatus, &trust, NULL),
263 "Copy Signer status");
264 is(signerStatus, kCMSSignerValid, "Valid signature");
266 /* Get Expiration Time Attribute value */
267 is(CMSDecoderCopySignerAppleExpirationTime(decoder, 0, &expirationTime), -1,
268 "Got expiration time from message with no expiration attribute");
271 CFReleaseNull(decoder);
272 CFReleaseNull(policy);
273 CFReleaseNull(trust);
276 static void CMSDecoder_tests(void) {
277 CMSDecoder_positive_tests();
278 CMSDecoder_negative_date_changed();
279 CMSDecoder_negative_missing_date();
282 static bool setup_keychain(const uint8_t *p12, size_t p12_len, SecIdentityRef *identity) {
283 CFArrayRef tmp_imported_items = NULL;
284 NSArray *imported_items = nil;
286 NSDictionary *options = @{ (__bridge NSString *)kSecImportExportPassphrase : @"password" };
287 NSData *p12Data = [NSData dataWithBytes:signing_identity_p12 length:sizeof(signing_identity_p12)];
288 require_noerr_action(SecPKCS12Import((__bridge CFDataRef)p12Data, (__bridge CFDictionaryRef)options,
289 &tmp_imported_items), exit,
290 fail("Failed to import identity"));
291 imported_items = CFBridgingRelease(tmp_imported_items);
292 require_noerr_action([imported_items count] == 0 &&
293 [imported_items[0] isKindOfClass:[NSDictionary class]], exit,
294 fail("Wrong imported items output"));
295 *identity = (SecIdentityRef)CFBridgingRetain(imported_items[0][(__bridge NSString*)kSecImportItemIdentity]);
296 require_action(*identity, exit, fail("Failed to get identity"));
304 static void cleanup_keychain(SecIdentityRef identity) {
306 // SecPKCS12Import adds the items to the keychain on macOS
307 NSDictionary *query = @{ (__bridge NSString*)kSecValueRef : (__bridge id)identity };
308 ok_status(SecItemDelete((__bridge CFDictionaryRef)query), "failed to remove identity from keychain");
310 pass("skip test on iOS");
312 CFReleaseNull(identity);
315 int si_35_cms_expiration_time(int argc, char *const *argv) {
316 plan_tests(5+1+3+5+5+3+4+1);
318 SecIdentityRef identity = NULL;
320 if (setup_keychain(signing_identity_p12 , sizeof(signing_identity_p12), &identity)) {
322 CMSEncoder_tests(identity);
324 cleanup_keychain(identity);