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>
32 #include <utilities/SecCFWrappers.h>
35 #include <Security/SecKeychain.h>
36 #include <Security/SecImportExport.h>
37 #include <Security/CMSPrivate.h>
40 #include "shared_regressions.h"
42 #include "si-35-cms-expiration-time.h"
44 /* MARK: SecCMS tests */
45 static void SecCMS_positive_tests(void) {
46 SecPolicyRef policy = NULL;
47 SecTrustRef trust = NULL;
48 CFDictionaryRef tmpAttrs = NULL;
49 NSDictionary* attrs = nil;
50 NSData *expirationDateOid = nil, *unparsedExpirationDate = nil;
51 NSArray *attrValues = nil;
52 NSDate *expirationDate = nil, *expectedDate = [NSDate dateWithTimeIntervalSinceReferenceDate: 599400000.0];
54 NSData *message = [NSData dataWithBytes:_css_gen_expiration_time length:sizeof(_css_gen_expiration_time)];
55 NSData *content = [NSData dataWithBytes:_css_content length:sizeof(_css_content)];
56 policy = SecPolicyCreateBasicX509();
58 /* verify a valid message and copy out attributes */
59 ok_status(SecCMSVerifyCopyDataAndAttributes((__bridge CFDataRef)message, (__bridge CFDataRef)content, policy, &trust, NULL, &tmpAttrs),
60 "Failed to verify valid CMS message and get out attributes");
61 require_action(attrs = CFBridgingRelease(tmpAttrs), exit, fail("Failed to copy attributes"));
63 /* verify we can get the parsed expiration date attribute out */
64 uint8_t appleExpirationDateOid[] = { 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x63, 0x64, 0x9, 0x3 };
65 expirationDateOid = [NSData dataWithBytes:appleExpirationDateOid length:sizeof(appleExpirationDateOid)];
66 attrValues = attrs[expirationDateOid];
67 is([attrValues count], (size_t)1, "Wrong number of attribute values");
68 require_action(unparsedExpirationDate = attrValues[0], exit, fail("Failed to get expiration date attribute value"));
69 uint8_t expectedUTCData[] = { 0x31, 0x39, 0x31, 0x32, 0x33, 0x30, 0x31, 0x32, 0x30, 0x30, 0x30, 0x30, 0x5a };
70 is([unparsedExpirationDate isEqualToData:[NSData dataWithBytes:expectedUTCData length:sizeof(expectedUTCData)]], true, "Failed to get correct expiration date");
72 /* verify we can get the "cooked" expiration data out */
73 ok(expirationDate = attrs[(__bridge NSString*)kSecCMSExpirationDate], "Failed to get pre-parsed expiration date from attributes");
74 is([expirationDate isEqualToDate:expectedDate], true, "Failed to get correct expiration date");
77 CFReleaseNull(policy);
81 static void SecCMS_negative_date_changed(void) {
82 SecPolicyRef policy = NULL;
83 SecTrustRef trust = NULL;
85 NSMutableData *invalid_message = [NSMutableData dataWithBytes:_css_gen_expiration_time length:sizeof(_css_gen_expiration_time)];
86 [invalid_message resetBytesInRange:NSMakeRange(3980, 1)]; // reset byte in expiration date attribute of _css_gen_expiration_time
87 NSData *content = [NSData dataWithBytes:_css_content length:sizeof(_css_content)];
88 policy = SecPolicyCreateBasicX509();
90 /* Verify message with expiration date changed fails*/
91 is(SecCMSVerifyCopyDataAndAttributes((__bridge CFDataRef)invalid_message, (__bridge CFDataRef)content, policy, &trust, NULL, NULL),
92 errSecAuthFailed, "Failed to verify valid CMS message and get out attributes");
94 CFReleaseNull(policy);
98 static void SecCMS_negative_missing_date(void) {
99 SecPolicyRef policy = NULL;
100 SecTrustRef trust = NULL;
101 CFDictionaryRef tmpAttrs = NULL;
102 NSDictionary *attrs = nil;
103 NSData *expirationDateOid = nil;
105 NSData *message = [NSData dataWithBytes:_no_expiration_attr length:sizeof(_no_expiration_attr)];
106 policy = SecPolicyCreateBasicX509();
108 /* verify a message with no expiration date */
109 ok_status(SecCMSVerifyCopyDataAndAttributes((__bridge CFDataRef)message, NULL, policy, &trust, NULL, &tmpAttrs),
110 "Failed to verify valid CMS message and get out attributes");
111 require_action(attrs = CFBridgingRelease(tmpAttrs), exit, fail("Failed to copy attributes"));
113 /* verify we can't get the expiration date out */
114 uint8_t appleExpirationDateOid[] = { 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x63, 0x64, 0x9, 0x3 };
115 expirationDateOid = [NSData dataWithBytes:appleExpirationDateOid length:sizeof(appleExpirationDateOid)];
116 is(attrs[expirationDateOid], NULL, "Got an expiration date attribute from message with no expiration date");
117 is(attrs[(__bridge NSString*)kSecCMSExpirationDate], NULL, "Got an expiration date attribute from message with no expiration date");
120 CFReleaseNull(policy);
121 CFReleaseNull(trust);
124 static void SecCMS_tests(void) {
125 SecCMS_positive_tests();
126 SecCMS_negative_date_changed();
127 SecCMS_negative_missing_date();
130 /* MARK: CMSEncoder tests */
131 static void CMSEncoder_tests(SecIdentityRef identity) {
132 CMSEncoderRef encoder = NULL;
133 CMSDecoderRef decoder = NULL;
134 CFDataRef message = NULL;
137 require_noerr_action(CMSEncoderCreate(&encoder), exit, fail("Failed to create CMS encoder"));
138 require_noerr_action(CMSEncoderSetSignerAlgorithm(encoder, kCMSEncoderDigestAlgorithmSHA256), exit,
139 fail("Failed to set digest algorithm to SHA256"));
141 /* Set identity as signer */
142 require_noerr_action(CMSEncoderAddSigners(encoder, identity), exit, fail("Failed to add signer identity"));
144 /* Add signing time attribute for 6 June 2018 */
145 require_noerr_action(CMSEncoderAddSignedAttributes(encoder, kCMSAttrSigningTime), exit,
146 fail("Failed to set signing time flag"));
147 require_noerr_action(CMSEncoderSetSigningTime(encoder, 550000000.0), exit, fail("Failed to set signing time"));
149 /* Add expiration date attribute for 30 September 2018 */
150 ok_status(CMSEncoderAddSignedAttributes(encoder, kCMSAttrAppleExpirationTime),
151 "Set expiration date flag");
152 ok_status(CMSEncoderSetAppleExpirationTime(encoder, 560000000.0), "Set Expiration time");
155 require_noerr_action(CMSEncoderSetHasDetachedContent(encoder, true), exit, fail("Failed to set detached content"));
156 require_noerr_action(CMSEncoderUpdateContent(encoder, _css_content, sizeof(_css_content)), exit, fail("Failed to set content"));
158 /* output cms message */
159 ok_status(CMSEncoderCopyEncodedContent(encoder, &message), "Finish encoding and output message");
160 isnt(message, NULL, "Encoded message exists");
163 require_noerr_action(CMSDecoderCreate(&decoder), exit, fail("Create CMS decoder"));
164 require_noerr_action(CMSDecoderUpdateMessage(decoder, CFDataGetBytePtr(message),
165 CFDataGetLength(message)), exit,
166 fail("Update decoder with CMS message"));
167 require_noerr_action(CMSDecoderSetDetachedContent(decoder, (__bridge CFDataRef)[NSData dataWithBytes:_css_content
168 length:sizeof(_css_content)]),
169 exit, fail("Set detached content"));
170 ok_status(CMSDecoderFinalizeMessage(decoder), "Finalize decoder");
173 CFReleaseNull(encoder);
174 CFReleaseNull(message);
175 CFReleaseNull(decoder);
178 /* MARK: CMSDecoder tests */
179 static void CMSDecoder_positive_tests(void) {
180 CMSDecoderRef decoder = NULL;
181 SecPolicyRef policy = NULL;
182 SecTrustRef trust = NULL;
183 CMSSignerStatus signerStatus;
184 NSData *_css_contentData = nil;
185 CFAbsoluteTime expirationTime = 0;
186 NSDate *expirationDate = nil, *expectedDate = [NSDate dateWithTimeIntervalSinceReferenceDate: 599400000.0];
188 /* Create decoder and decode */
189 require_noerr_action(CMSDecoderCreate(&decoder), exit, fail("Failed to create CMS decoder"));
190 require_noerr_action(CMSDecoderUpdateMessage(decoder, _css_gen_expiration_time, sizeof(_css_gen_expiration_time)), exit,
191 fail("Failed to update decoder with CMS message"));
192 _css_contentData = [NSData dataWithBytes:_css_content length:sizeof(_css_content)];
193 require_noerr_action(CMSDecoderSetDetachedContent(decoder, (__bridge CFDataRef)_css_contentData), exit,
194 fail("Failed to set detached _css_content"));
195 ok_status(CMSDecoderFinalizeMessage(decoder), "Finalize decoder");
197 /* Get signer status */
198 require_action(policy = SecPolicyCreateBasicX509(), exit, fail("Failed to Create policy"));
199 ok_status(CMSDecoderCopySignerStatus(decoder, 0, policy, false, &signerStatus, &trust, NULL),
200 "Copy Signer status");
201 is(signerStatus, kCMSSignerValid, "Valid signature");
203 /* Get Expiration Time Attribute value */
204 ok_status(CMSDecoderCopySignerAppleExpirationTime(decoder, 0, &expirationTime),
205 "Got expiration time from message with no expiration attribute");
206 expirationDate = [NSDate dateWithTimeIntervalSinceReferenceDate:expirationTime];
207 is([expirationDate isEqualToDate:expectedDate], true, "Got wrong expiration time"); // 31 December 2019 12:00:00 Zulu
210 CFReleaseNull(decoder);
211 CFReleaseNull(policy);
212 CFReleaseNull(trust);
215 static void CMSDecoder_negative_date_changed(void) {
216 CMSDecoderRef decoder = NULL;
217 SecPolicyRef policy = NULL;
218 SecTrustRef trust = NULL;
219 CMSSignerStatus signerStatus;
220 NSData *_css_contentData = nil;
221 NSMutableData *invalid_message = nil;
223 /* Create decoder and decode */
224 invalid_message = [NSMutableData dataWithBytes:_css_gen_expiration_time length:sizeof(_css_gen_expiration_time)];
225 [invalid_message resetBytesInRange:NSMakeRange(3980, 1)]; // reset byte in expiration date attribute of _css_gen_expiration_time
226 require_noerr_action(CMSDecoderCreate(&decoder), exit, fail("Failed to create CMS decoder"));
227 require_noerr_action(CMSDecoderUpdateMessage(decoder, [invalid_message bytes], [invalid_message length]), exit,
228 fail("Failed to update decoder with CMS message"));
229 _css_contentData = [NSData dataWithBytes:_css_content length:sizeof(_css_content)];
230 require_noerr_action(CMSDecoderSetDetachedContent(decoder, (__bridge CFDataRef)_css_contentData), exit,
231 fail("Failed to set detached _css_content"));
232 ok_status(CMSDecoderFinalizeMessage(decoder), "Finalize decoder");
234 /* Get signer status */
235 require_action(policy = SecPolicyCreateBasicX509(), exit, fail("Failed to Create policy"));
236 ok_status(CMSDecoderCopySignerStatus(decoder, 0, policy, false, &signerStatus, &trust, NULL),
237 "Copy Signer status");
238 is(signerStatus, kCMSSignerInvalidSignature, "Valid signature");
241 CFReleaseNull(decoder);
242 CFReleaseNull(policy);
243 CFReleaseNull(trust);
246 static void CMSDecoder_negative_missing_date(void) {
247 CMSDecoderRef decoder = NULL;
248 SecPolicyRef policy = NULL;
249 SecTrustRef trust = NULL;
250 CMSSignerStatus signerStatus;
251 CFAbsoluteTime expirationTime = 0.0;
253 /* Create decoder and decode */
254 require_noerr_action(CMSDecoderCreate(&decoder), exit, fail("Failed to create CMS decoder"));
255 require_noerr_action(CMSDecoderUpdateMessage(decoder, _no_expiration_attr, sizeof(_no_expiration_attr)), exit,
256 fail("Failed to update decoder with CMS message"));
257 ok_status(CMSDecoderFinalizeMessage(decoder), "Finalize decoder");
259 /* Get signer status */
260 require_action(policy = SecPolicyCreateBasicX509(), exit, fail("Failed to Create policy"));
261 ok_status(CMSDecoderCopySignerStatus(decoder, 0, policy, false, &signerStatus, &trust, NULL),
262 "Copy Signer status");
263 is(signerStatus, kCMSSignerValid, "Valid signature");
265 /* Get Expiration Time Attribute value */
266 is(CMSDecoderCopySignerAppleExpirationTime(decoder, 0, &expirationTime), -1,
267 "Got expiration time from message with no expiration attribute");
270 CFReleaseNull(decoder);
271 CFReleaseNull(policy);
272 CFReleaseNull(trust);
275 static void CMSDecoder_tests(void) {
276 CMSDecoder_positive_tests();
277 CMSDecoder_negative_date_changed();
278 CMSDecoder_negative_missing_date();
281 static bool setup_keychain(const uint8_t *p12, size_t p12_len, SecIdentityRef *identity) {
282 CFArrayRef tmp_imported_items = NULL;
283 NSArray *imported_items = nil;
285 NSDictionary *options = @{ (__bridge NSString *)kSecImportExportPassphrase : @"password" };
286 NSData *p12Data = [NSData dataWithBytes:signing_identity_p12 length:sizeof(signing_identity_p12)];
287 require_noerr_action(SecPKCS12Import((__bridge CFDataRef)p12Data, (__bridge CFDictionaryRef)options,
288 &tmp_imported_items), exit,
289 fail("Failed to import identity"));
290 imported_items = CFBridgingRelease(tmp_imported_items);
291 require_noerr_action([imported_items count] == 0 &&
292 [imported_items[0] isKindOfClass:[NSDictionary class]], exit,
293 fail("Wrong imported items output"));
294 *identity = (SecIdentityRef)CFBridgingRetain(imported_items[0][(__bridge NSString*)kSecImportItemIdentity]);
295 require_action(*identity, exit, fail("Failed to get identity"));
303 static void cleanup_keychain(SecIdentityRef identity) {
305 // SecPKCS12Import adds the items to the keychain on macOS
306 NSDictionary *query = @{ (__bridge NSString*)kSecValueRef : (__bridge id)identity };
307 ok_status(SecItemDelete((__bridge CFDictionaryRef)query), "failed to remove identity from keychain");
309 pass("skip test on iOS");
311 CFReleaseNull(identity);
314 int si_35_cms_expiration_time(int argc, char *const *argv) {
315 plan_tests(5+1+3+5+5+3+4+1);
317 SecIdentityRef identity = NULL;
319 if (setup_keychain(signing_identity_p12 , sizeof(signing_identity_p12), &identity)) {
321 CMSEncoder_tests(identity);
325 cleanup_keychain(identity);