]> git.saurik.com Git - apple/security.git/blob - trust/trustd/CertificateTransparency.m
Security-59754.80.3.tar.gz
[apple/security.git] / trust / trustd / CertificateTransparency.m
1 /*
2 * Copyright (c) 2020 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 #include <libDER/DER_CertCrl.h>
26 #include <libDER/DER_Encode.h>
27 #include <libDER/asn1Types.h>
28
29 #include <security_asn1/SecAsn1Coder.h>
30 #include <security_asn1/oidsalg.h>
31 #include <utilities/SecCFWrappers.h>
32 #include <Security/SecCertificatePriv.h>
33 #include <Security/SecureTransportPriv.h>
34 #include <Security/SecKeyPriv.h>
35 #include <Security/SecPolicyPriv.h>
36
37 #include "trust/trustd/SecTrustServer.h"
38 #include "trust/trustd/SecPolicyServer.h"
39 #include "trust/trustd/SecOCSPResponse.h"
40 #include "trust/trustd/OTATrustUtilities.h"
41 #include "trust/trustd/SecCertificateServer.h"
42 #include "trust/trustd/CertificateTransparency.h"
43
44 const CFStringRef kSecCTRetirementDateKey = CFSTR("expiry"); // For backwards compatibility, retirement date is represented with the "expiry" key
45 const CFStringRef kSecCTReadOnlyDateKey = CFSTR("frozen"); // For backwards compatibility, read-only date is represented with the "frozen" key
46 const CFStringRef kSecCTShardStartDateKey = CFSTR("start_inclusive");
47 const CFStringRef kSecCTShardEndDateKey = CFSTR("end_exclusive");
48 const CFStringRef kSecCTPublicKeyKey = CFSTR("key");
49
50 enum {
51 kSecCTEntryTypeCert = 0,
52 kSecCTEntryTypePreCert = 1,
53 };
54
55 /***
56
57 struct {
58 Version sct_version; // 1 byte
59 LogID id; // 32 bytes
60 uint64 timestamp; // 8 bytes
61 CtExtensions extensions; // 2 bytes len field, + n bytes data
62 digitally-signed struct { // 1 byte hash alg, 1 byte sig alg, n bytes signature
63 Version sct_version;
64 SignatureType signature_type = certificate_timestamp;
65 uint64 timestamp;
66 LogEntryType entry_type;
67 select(entry_type) {
68 case x509_entry: ASN.1Cert;
69 case precert_entry: PreCert;
70 } signed_entry;
71 CtExtensions extensions;
72 };
73 } SignedCertificateTimestamp;
74
75 ***/
76
77 static const
78 SecAsn1Oid *oidForSigAlg(SSL_HashAlgorithm hash, SSL_SignatureAlgorithm alg)
79 {
80 switch(alg) {
81 case SSL_SignatureAlgorithmRSA:
82 switch (hash) {
83 case SSL_HashAlgorithmSHA1:
84 return &CSSMOID_SHA1WithRSA;
85 case SSL_HashAlgorithmSHA256:
86 return &CSSMOID_SHA256WithRSA;
87 case SSL_HashAlgorithmSHA384:
88 return &CSSMOID_SHA384WithRSA;
89 default:
90 break;
91 }
92 case SSL_SignatureAlgorithmECDSA:
93 switch (hash) {
94 case SSL_HashAlgorithmSHA1:
95 return &CSSMOID_ECDSA_WithSHA1;
96 case SSL_HashAlgorithmSHA256:
97 return &CSSMOID_ECDSA_WithSHA256;
98 case SSL_HashAlgorithmSHA384:
99 return &CSSMOID_ECDSA_WithSHA384;
100 default:
101 break;
102 }
103 default:
104 break;
105 }
106
107 return NULL;
108 }
109
110
111 static size_t SSLDecodeUint16(const uint8_t *p)
112 {
113 return (p[0]<<8 | p[1]);
114 }
115
116 static uint8_t *SSLEncodeUint16(uint8_t *p, size_t len)
117 {
118 p[0] = (len >> 8)&0xff;
119 p[1] = (len & 0xff);
120 return p+2;
121 }
122
123 static uint8_t *SSLEncodeUint24(uint8_t *p, size_t len)
124 {
125 p[0] = (len >> 16)&0xff;
126 p[1] = (len >> 8)&0xff;
127 p[2] = (len & 0xff);
128 return p+3;
129 }
130
131
132 static
133 uint64_t SSLDecodeUint64(const uint8_t *p)
134 {
135 uint64_t u = 0;
136 for(int i=0; i<8; i++) {
137 u=(u<<8)|p[0];
138 p++;
139 }
140 return u;
141 }
142
143
144 static CFDataRef copy_x509_entry_from_chain(SecPVCRef pvc)
145 {
146 SecCertificateRef leafCert = SecPVCGetCertificateAtIndex(pvc, 0);
147
148 CFMutableDataRef data = CFDataCreateMutable(kCFAllocatorDefault, 3+SecCertificateGetLength(leafCert));
149
150 CFDataSetLength(data, 3+SecCertificateGetLength(leafCert));
151
152 uint8_t *q = CFDataGetMutableBytePtr(data);
153 q = SSLEncodeUint24(q, SecCertificateGetLength(leafCert));
154 memcpy(q, SecCertificateGetBytePtr(leafCert), SecCertificateGetLength(leafCert));
155
156 return data;
157 }
158
159
160 static CFDataRef copy_precert_entry_from_chain(SecPVCRef pvc)
161 {
162 SecCertificateRef leafCert = NULL;
163 SecCertificateRef issuer = NULL;
164 CFDataRef issuerKeyHash = NULL;
165 CFDataRef tbs_precert = NULL;
166 CFMutableDataRef data= NULL;
167
168 require_quiet(SecPVCGetCertificateCount(pvc)>=2, out); //we need the issuer key for precerts.
169 leafCert = SecPVCGetCertificateAtIndex(pvc, 0);
170 issuer = SecPVCGetCertificateAtIndex(pvc, 1);
171
172 require(leafCert, out);
173 require(issuer, out); // Those two would likely indicate an internal error, since we already checked the chain length above.
174 issuerKeyHash = SecCertificateCopySubjectPublicKeyInfoSHA256Digest(issuer);
175 tbs_precert = SecCertificateCopyPrecertTBS(leafCert);
176
177 require(issuerKeyHash, out);
178 require(tbs_precert, out);
179 data = CFDataCreateMutable(kCFAllocatorDefault, CFDataGetLength(issuerKeyHash) + 3 + CFDataGetLength(tbs_precert));
180 CFDataSetLength(data, CFDataGetLength(issuerKeyHash) + 3 + CFDataGetLength(tbs_precert));
181
182 uint8_t *q = CFDataGetMutableBytePtr(data);
183 memcpy(q, CFDataGetBytePtr(issuerKeyHash), CFDataGetLength(issuerKeyHash)); q += CFDataGetLength(issuerKeyHash); // issuer key hash
184 q = SSLEncodeUint24(q, CFDataGetLength(tbs_precert));
185 memcpy(q, CFDataGetBytePtr(tbs_precert), CFDataGetLength(tbs_precert));
186
187 out:
188 CFReleaseSafe(issuerKeyHash);
189 CFReleaseSafe(tbs_precert);
190 return data;
191 }
192
193 static
194 CFAbsoluteTime TimestampToCFAbsoluteTime(uint64_t ts)
195 {
196 return (ts / 1000) - kCFAbsoluteTimeIntervalSince1970;
197 }
198
199 static
200 uint64_t TimestampFromCFAbsoluteTime(CFAbsoluteTime at)
201 {
202 return (uint64_t)(at + kCFAbsoluteTimeIntervalSince1970) * 1000;
203 }
204
205 static bool isSCTValidForLogData(CFDictionaryRef logData, int entry_type, CFAbsoluteTime sct_time, CFAbsoluteTime cert_expiry_date) {
206 /* only embedded SCTs can be used from retired logs. */
207 if(entry_type==kSecCTEntryTypeCert && CFDictionaryContainsKey(logData, kSecCTRetirementDateKey)) {
208 return false;
209 }
210
211 /* SCTs from after the transition to read-only are not valid (and indicate a operator failure) */
212 CFDateRef frozen_date = CFDictionaryGetValue(logData, kSecCTReadOnlyDateKey);
213 if (frozen_date && (sct_time > CFDateGetAbsoluteTime(frozen_date))) {
214 secerror("Frozen CT log issued SCT after freezing (log=%@)\n", logData);
215 return false;
216 }
217
218 /* If the log is temporally sharded, the certificate expiry date must be within the temporal shard window */
219 CFDateRef start_inclusive = CFDictionaryGetValue(logData, kSecCTShardStartDateKey);
220 CFDateRef end_exclusive = CFDictionaryGetValue(logData, kSecCTShardEndDateKey);
221 if (start_inclusive && (cert_expiry_date < CFDateGetAbsoluteTime(start_inclusive))) {
222 return false;
223 }
224 if (end_exclusive && (cert_expiry_date >= CFDateGetAbsoluteTime(end_exclusive))) {
225 return false;
226 }
227
228 return true;
229 }
230
231
232 /*
233 If the 'sct' is valid, add it to the validatingLogs dictionary.
234
235 Inputs:
236 - validatingLogs: mutable dictionary to which to add the log that validate this SCT.
237 - sct: the SCT date
238 - entry_type: 0 for x509 cert, 1 for precert.
239 - entry: the cert or precert data.
240 - vt: verification time timestamp (as used in SCTs: ms since 1970 Epoch)
241 - trustedLog: Dictionary contain the Trusted Logs.
242
243 The SCT is valid if:
244 - It decodes properly.
245 - Its timestamp is less than 'verifyTime'.
246 - It is signed by a log in 'trustedLogs'.
247 - If entry_type = 0, the log must be currently qualified.
248 - If entry_type = 1, the log may be expired.
249
250 If the SCT is valid, it's added to the validatinLogs dictionary using the log dictionary as the key, and the timestamp as value.
251 If an entry for the same log already existing in the dictionary, the entry is replaced only if the timestamp of this SCT is earlier.
252
253 */
254 static CFDictionaryRef getSCTValidatingLog(CFDataRef sct, int entry_type, CFDataRef entry, uint64_t vt, CFAbsoluteTime cert_expiry_date, CFDictionaryRef trustedLogs, CFAbsoluteTime *sct_at)
255 {
256 uint8_t version;
257 const uint8_t *logID;
258 const uint8_t *timestampData;
259 uint64_t timestamp;
260 size_t extensionsLen;
261 const uint8_t *extensionsData;
262 uint8_t hashAlg;
263 uint8_t sigAlg;
264 size_t signatureLen;
265 const uint8_t *signatureData;
266 SecKeyRef pubKey = NULL;
267 uint8_t *signed_data = NULL;
268 const SecAsn1Oid *oid = NULL;
269 SecAsn1AlgId algId;
270 CFDataRef logIDData = NULL;
271 CFDictionaryRef result = 0;
272
273 const uint8_t *p = CFDataGetBytePtr(sct);
274 size_t len = CFDataGetLength(sct);
275
276 require(len>=43, out);
277
278 version = p[0]; p++; len--;
279 logID = p; p+=32; len-=32;
280 timestampData = p; p+=8; len-=8;
281 extensionsLen = SSLDecodeUint16(p); p+=2; len-=2;
282
283 require(len>=extensionsLen, out);
284 extensionsData = p; p+=extensionsLen; len-=extensionsLen;
285
286 require(len>=4, out);
287 hashAlg=p[0]; p++; len--;
288 sigAlg=p[0]; p++; len--;
289 signatureLen = SSLDecodeUint16(p); p+=2; len-=2;
290 require(len==signatureLen, out); /* We do not tolerate any extra data after the signature */
291 signatureData = p;
292
293 /* verify version: only v1(0) is supported */
294 if(version!=0) {
295 secerror("SCT version unsupported: %d\n", version);
296 goto out;
297 }
298
299 /* verify timestamp not in the future */
300 timestamp = SSLDecodeUint64(timestampData);
301 if(timestamp > vt) {
302 secerror("SCT is in the future: %llu > %llu\n", timestamp, vt);
303 goto out;
304 }
305
306 uint8_t *q;
307
308 /* signed entry */
309 size_t signed_data_len = 12 + CFDataGetLength(entry) + 2 + extensionsLen ;
310 signed_data = malloc(signed_data_len);
311 require(signed_data, out);
312 q = signed_data;
313 *q++ = version;
314 *q++ = 0; // certificate_timestamp
315 memcpy(q, timestampData, 8); q+=8;
316 q = SSLEncodeUint16(q, entry_type); // logentry type: 0=cert 1=precert
317 memcpy(q, CFDataGetBytePtr(entry), CFDataGetLength(entry)); q += CFDataGetLength(entry);
318 q = SSLEncodeUint16(q, extensionsLen);
319 memcpy(q, extensionsData, extensionsLen);
320
321 logIDData = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, logID, 32, kCFAllocatorNull);
322
323 CFDictionaryRef logData = CFDictionaryGetValue(trustedLogs, logIDData);
324 CFAbsoluteTime sct_time = TimestampToCFAbsoluteTime(timestamp);
325 require(logData && isSCTValidForLogData(logData, entry_type, sct_time, cert_expiry_date), out);
326
327 CFDataRef logKeyData = CFDictionaryGetValue(logData, kSecCTPublicKeyKey);
328 require(logKeyData, out); // This failing would be an internal logic error
329 pubKey = SecKeyCreateFromSubjectPublicKeyInfoData(kCFAllocatorDefault, logKeyData);
330 require(pubKey, out);
331
332 oid = oidForSigAlg(hashAlg, sigAlg);
333 require(oid, out);
334
335 algId.algorithm = *oid;
336 algId.parameters.Data = NULL;
337 algId.parameters.Length = 0;
338
339 if(SecKeyDigestAndVerify(pubKey, &algId, signed_data, signed_data_len, signatureData, signatureLen)==0) {
340 *sct_at = sct_time;
341 result = logData;
342 } else {
343 secerror("SCT signature failed (log=%@)\n", logData);
344 }
345
346 out:
347 CFReleaseSafe(logIDData);
348 CFReleaseSafe(pubKey);
349 free(signed_data);
350 return result;
351 }
352
353
354 static void addValidatingLog(CFMutableDictionaryRef validatingLogs, CFDictionaryRef log, CFAbsoluteTime sct_at)
355 {
356 CFDateRef validated_time = CFDictionaryGetValue(validatingLogs, log);
357
358 if(validated_time==NULL || (sct_at < CFDateGetAbsoluteTime(validated_time))) {
359 CFDateRef sct_time = CFDateCreate(kCFAllocatorDefault, sct_at);
360 CFDictionarySetValue(validatingLogs, log, sct_time);
361 CFReleaseSafe(sct_time);
362 }
363 }
364
365 static CFArrayRef copy_ocsp_scts(SecPVCRef pvc)
366 {
367 CFMutableArrayRef SCTs = NULL;
368 SecCertificateRef leafCert = NULL;
369 SecCertificateRef issuer = NULL;
370 CFArrayRef ocspResponsesData = NULL;
371 SecOCSPRequestRef ocspRequest = NULL;
372
373 ocspResponsesData = SecPathBuilderCopyOCSPResponses(pvc->builder);
374 require_quiet(ocspResponsesData, out);
375
376 require_quiet(SecPVCGetCertificateCount(pvc)>=2, out); //we need the issuer key for precerts.
377 leafCert = SecPVCGetCertificateAtIndex(pvc, 0);
378 issuer = SecPVCGetCertificateAtIndex(pvc, 1);
379
380 require(leafCert, out);
381 require(issuer, out); // not quiet: Those two would likely indicate an internal error, since we already checked the chain length above.
382 ocspRequest = SecOCSPRequestCreate(leafCert, issuer);
383
384 SCTs = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
385 require(SCTs, out);
386
387 CFArrayForEach(ocspResponsesData, ^(const void *value) {
388 /* TODO: Should the builder already have the appropriate SecOCSPResponseRef ? */
389 SecOCSPResponseRef ocspResponse = SecOCSPResponseCreate(value);
390 if(ocspResponse && SecOCSPGetResponseStatus(ocspResponse)==kSecOCSPSuccess) {
391 SecOCSPSingleResponseRef ocspSingleResponse = SecOCSPResponseCopySingleResponse(ocspResponse, ocspRequest);
392 if(ocspSingleResponse) {
393 CFArrayRef singleResponseSCTs = SecOCSPSingleResponseCopySCTs(ocspSingleResponse);
394 if(singleResponseSCTs) {
395 CFArrayAppendArray(SCTs, singleResponseSCTs, CFRangeMake(0, CFArrayGetCount(singleResponseSCTs)));
396 CFRelease(singleResponseSCTs);
397 }
398 SecOCSPSingleResponseDestroy(ocspSingleResponse);
399 }
400 }
401 if(ocspResponse) SecOCSPResponseFinalize(ocspResponse);
402 });
403
404 if(CFArrayGetCount(SCTs)==0) {
405 CFReleaseNull(SCTs);
406 }
407
408 out:
409 CFReleaseSafe(ocspResponsesData);
410 if(ocspRequest)
411 SecOCSPRequestFinalize(ocspRequest);
412
413 return SCTs;
414 }
415
416 static bool find_validating_logs(SecPVCRef pvc, CFDictionaryRef trustedLogs,
417 CFDictionaryRef CF_RETURNS_RETAINED * _Nonnull outCurrentLogsValidatingScts,
418 CFDictionaryRef CF_RETURNS_RETAINED * _Nonnull outLogsValidatingEmbeddedScts,
419 bool * _Nonnull out_at_least_one_currently_valid_external,
420 bool * _Nonnull out_at_least_one_currently_valid_embedded) {
421 SecCertificateRef leafCert = SecPVCGetCertificateAtIndex(pvc, 0);
422 CFArrayRef embeddedScts = SecCertificateCopySignedCertificateTimestamps(leafCert);
423 CFArrayRef builderScts = SecPathBuilderCopySignedCertificateTimestamps(pvc->builder);
424 CFArrayRef ocspScts = copy_ocsp_scts(pvc);
425 CFDataRef precertEntry = copy_precert_entry_from_chain(pvc);
426 CFDataRef x509Entry = copy_x509_entry_from_chain(pvc);
427 __block CFAbsoluteTime certExpiry = SecCertificateNotValidAfter(leafCert);
428 bool result = NO;
429
430 // This eventually contain list of logs who validated the SCT.
431 CFMutableDictionaryRef currentLogsValidatingScts = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
432 CFMutableDictionaryRef logsValidatingEmbeddedScts = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
433
434 uint64_t vt = TimestampFromCFAbsoluteTime(SecPVCGetVerifyTime(pvc));
435
436 __block bool at_least_one_currently_valid_external = 0;
437 __block bool at_least_one_currently_valid_embedded = 0;
438
439 require(logsValidatingEmbeddedScts, out);
440 require(currentLogsValidatingScts, out);
441
442 /* Skip if there are no SCTs. */
443 bool no_scts = (embeddedScts && CFArrayGetCount(embeddedScts) > 0) ||
444 (builderScts && CFArrayGetCount(builderScts) > 0) ||
445 (ocspScts && CFArrayGetCount(ocspScts) > 0);
446 require_quiet(no_scts, out);
447
448 if(trustedLogs && CFDictionaryGetCount(trustedLogs) > 0) { // Don't bother trying to validate SCTs if we don't have any trusted logs.
449 if(embeddedScts && precertEntry) { // Don't bother if we could not get the precert.
450 CFArrayForEach(embeddedScts, ^(const void *value){
451 CFAbsoluteTime sct_at;
452 CFDictionaryRef log = getSCTValidatingLog(value, 1, precertEntry, vt, certExpiry, trustedLogs, &sct_at);
453 if(log) {
454 addValidatingLog(logsValidatingEmbeddedScts, log, sct_at);
455 if(!CFDictionaryContainsKey(log, kSecCTRetirementDateKey)) {
456 addValidatingLog(currentLogsValidatingScts, log, sct_at);
457 at_least_one_currently_valid_embedded = true;
458 }
459 }
460 });
461 }
462
463 if(builderScts && x509Entry) { // Don't bother if we could not get the cert.
464 CFArrayForEach(builderScts, ^(const void *value){
465 CFAbsoluteTime sct_at;
466 CFDictionaryRef log = getSCTValidatingLog(value, 0, x509Entry, vt, certExpiry, trustedLogs, &sct_at);
467 if(log) {
468 addValidatingLog(currentLogsValidatingScts, log, sct_at);
469 at_least_one_currently_valid_external = true;
470 }
471 });
472 }
473
474 if(ocspScts && x509Entry) {
475 CFArrayForEach(ocspScts, ^(const void *value){
476 CFAbsoluteTime sct_at;
477 CFDictionaryRef log = getSCTValidatingLog(value, 0, x509Entry, vt, certExpiry, trustedLogs, &sct_at);
478 if(log) {
479 addValidatingLog(currentLogsValidatingScts, log, sct_at);
480 at_least_one_currently_valid_external = true;
481 }
482 });
483 }
484 }
485
486 if (CFDictionaryGetCount(currentLogsValidatingScts) > 0) {
487 result = true;
488 *outCurrentLogsValidatingScts = CFRetainSafe(currentLogsValidatingScts);
489 *outLogsValidatingEmbeddedScts = CFRetainSafe(logsValidatingEmbeddedScts);
490 *out_at_least_one_currently_valid_embedded = at_least_one_currently_valid_embedded;
491 *out_at_least_one_currently_valid_external = at_least_one_currently_valid_external;
492 }
493
494 out:
495 CFReleaseSafe(logsValidatingEmbeddedScts);
496 CFReleaseSafe(currentLogsValidatingScts);
497 CFReleaseSafe(builderScts);
498 CFReleaseSafe(embeddedScts);
499 CFReleaseSafe(ocspScts);
500 CFReleaseSafe(precertEntry);
501 CFReleaseSafe(x509Entry);
502 return result;
503 }
504
505 static bool verify_tls_ct_policy(SecPVCRef pvc, CFDictionaryRef currentLogsValidatingScts, CFDictionaryRef logsValidatingEmbeddedScts,
506 bool at_least_one_currently_valid_external, bool at_least_one_currently_valid_embedded,
507 CFIndex * _Nonnull outTrustedSCTCount)
508 {
509 if (!logsValidatingEmbeddedScts || !currentLogsValidatingScts) {
510 return false;
511 }
512
513 __block CFAbsoluteTime issuanceTime = SecPVCGetVerifyTime(pvc);
514 SecCertificateRef leafCert = SecPVCGetCertificateAtIndex(pvc, 0);
515 __block CFIndex trustedSCTCount = CFDictionaryGetCount(currentLogsValidatingScts);
516
517 /* We now have 2 sets of logs that validated those SCTS, count them and make a final decision.
518
519 Current Policy:
520 is_ct = (A1 AND A2) OR (B1 AND B2).
521
522 A1: embedded SCTs from 2+ to 5+ logs valid at issuance time
523 A2: At least one embedded SCT from a currently valid log.
524
525 B1: SCTs from 2 currently valid logs (from any source)
526 B2: At least 1 external SCT from a currently valid log.
527
528 */
529
530 bool hasValidExternalSCT = (at_least_one_currently_valid_external && CFDictionaryGetCount(currentLogsValidatingScts)>=2);
531 bool hasValidEmbeddedSCT = (at_least_one_currently_valid_embedded);
532 SecCertificatePathVCRef path = SecPathBuilderGetPath(pvc->builder);
533 bool result = false;
534
535 if (hasValidEmbeddedSCT) {
536 /* Calculate issuance time based on timestamp of SCTs from current logs */
537 CFDictionaryForEach(currentLogsValidatingScts, ^(const void *key, const void *value) {
538 CFDictionaryRef log = key;
539 if(!CFDictionaryContainsKey(log, kSecCTRetirementDateKey)) {
540 // Log is still qualified
541 CFDateRef ts = (CFDateRef) value;
542 CFAbsoluteTime timestamp = CFDateGetAbsoluteTime(ts);
543 if(timestamp < issuanceTime) {
544 issuanceTime = timestamp;
545 }
546 }
547 });
548 SecCertificatePathVCSetIssuanceTime(path, issuanceTime);
549 }
550 if (hasValidExternalSCT) {
551 /* Note: since external SCT validates this cert, we do not need to
552 override issuance time here. If the cert also has a valid embedded
553 SCT, issuanceTime will be calculated and set in the block above. */
554 result = true;
555 } else if (hasValidEmbeddedSCT) {
556 __block int lifetime; // in Months
557 __block unsigned once_or_current_qualified_embedded = 0;
558
559 /* Count Logs */
560 __block bool failed_once_check = false;
561 CFDictionaryForEach(logsValidatingEmbeddedScts, ^(const void *key, const void *value) {
562 CFDictionaryRef log = key;
563 CFDateRef ts = value;
564 CFDateRef expiry = CFDictionaryGetValue(log, kSecCTRetirementDateKey);
565 if (expiry == NULL) { // Currently qualified OR
566 once_or_current_qualified_embedded++;
567 } else if (CFDateCompare(ts, expiry, NULL) == kCFCompareLessThan && // Once qualified. That is, qualified at the time of SCT AND
568 issuanceTime < CFDateGetAbsoluteTime(expiry)) { // at the time of issuance.)
569 once_or_current_qualified_embedded++;
570 trustedSCTCount++;
571 } else {
572 failed_once_check = true;
573 }
574 });
575
576 SecCFCalendarDoWithZuluCalendar(^(CFCalendarRef zuluCalendar) {
577 int _lifetime;
578 CFCalendarGetComponentDifference(zuluCalendar,
579 SecCertificateNotValidBefore(leafCert),
580 SecCertificateNotValidAfter(leafCert),
581 0, "M", &_lifetime);
582 lifetime = _lifetime;
583 });
584
585 unsigned requiredEmbeddedSctsCount;
586
587 if (lifetime < 15) {
588 requiredEmbeddedSctsCount = 2;
589 } else if (lifetime <= 27) {
590 requiredEmbeddedSctsCount = 3;
591 } else if (lifetime <= 39) {
592 requiredEmbeddedSctsCount = 4;
593 } else {
594 requiredEmbeddedSctsCount = 5;
595 }
596
597 if(once_or_current_qualified_embedded >= requiredEmbeddedSctsCount){
598 result = true;
599 }
600 }
601
602 *outTrustedSCTCount = trustedSCTCount;
603 return result;
604 }
605
606 static void report_ct_analytics(SecPVCRef pvc, CFDictionaryRef currentLogsValidatingScts, CFIndex trustedSCTCount)
607 {
608 /* Record analytics data for CT */
609 TrustAnalyticsBuilder *analytics = SecPathBuilderGetAnalyticsData(pvc->builder);
610 if (!analytics) {
611 return;
612 }
613
614 SecCertificateRef leafCert = SecPVCGetCertificateAtIndex(pvc, 0);
615 CFArrayRef embeddedScts = SecCertificateCopySignedCertificateTimestamps(leafCert);
616 CFArrayRef builderScts = SecPathBuilderCopySignedCertificateTimestamps(pvc->builder);
617 CFArrayRef ocspScts = copy_ocsp_scts(pvc);
618
619 uint32_t sctCount = 0;
620 /* Count the total number of SCTs we found and report where we got them */
621 if (embeddedScts && CFArrayGetCount(embeddedScts) > 0) {
622 analytics->sct_sources |= TA_SCTEmbedded;
623 sctCount += CFArrayGetCount(embeddedScts);
624 }
625 if (builderScts && CFArrayGetCount(builderScts) > 0) {
626 analytics->sct_sources |= TA_SCT_TLS;
627 sctCount += CFArrayGetCount(builderScts);
628 }
629 if (ocspScts && CFArrayGetCount(ocspScts) > 0) {
630 analytics->sct_sources |= TA_SCT_OCSP;
631 sctCount += CFArrayGetCount(ocspScts);
632 }
633 /* Report how many of those SCTs were once or currently qualified */
634 analytics->number_trusted_scts = (uint32_t)trustedSCTCount;
635 /* Report how many SCTs we got */
636 analytics->number_scts = sctCount;
637 /* Only one current SCT -- close to failure */
638 if (CFDictionaryGetCount(currentLogsValidatingScts) == 1) {
639 analytics->ct_one_current = true;
640 }
641
642 CFReleaseNull(embeddedScts);
643 CFReleaseNull(builderScts);
644 CFReleaseNull(ocspScts);
645 }
646
647 void SecPolicyCheckCT(SecPVCRef pvc)
648 {
649 CFDictionaryRef trustedLogs = SecPathBuilderCopyTrustedLogs(pvc->builder);
650 if (!trustedLogs) {
651 SecOTAPKIRef otapkiref = SecOTAPKICopyCurrentOTAPKIRef();
652 trustedLogs = SecOTAPKICopyTrustedCTLogs(otapkiref);
653 CFReleaseSafe(otapkiref);
654 }
655
656 CFDictionaryRef currentLogsValidatingScts = NULL;
657 CFDictionaryRef logsValidatingEmbeddedScts = NULL;
658 bool at_least_one_currently_valid_external = false;
659 bool at_least_one_currently_valid_embedded = false;
660 CFIndex trustedSCTCount = 0;
661 SecCertificatePathVCRef path = SecPathBuilderGetPath(pvc->builder);
662
663 SecCertificatePathVCSetIsCT(path, false);
664 if (find_validating_logs(pvc, trustedLogs, &currentLogsValidatingScts, &logsValidatingEmbeddedScts, &at_least_one_currently_valid_external, &at_least_one_currently_valid_embedded)) {
665 if (verify_tls_ct_policy(pvc, currentLogsValidatingScts, logsValidatingEmbeddedScts, at_least_one_currently_valid_external, at_least_one_currently_valid_embedded, &trustedSCTCount)) {
666 SecCertificatePathVCSetIsCT(path, true);
667 }
668 report_ct_analytics(pvc, currentLogsValidatingScts, trustedSCTCount);
669 }
670
671 CFReleaseNull(currentLogsValidatingScts);
672 CFReleaseNull(logsValidatingEmbeddedScts);
673 CFReleaseNull(trustedLogs);
674 }
675
676 bool SecPolicyCheckNonTlsCT(SecPVCRef pvc)
677 {
678 CFDictionaryRef trustedLogs = SecPathBuilderCopyTrustedLogs(pvc->builder);
679 if (!trustedLogs) {
680 SecOTAPKIRef otapkiref = SecOTAPKICopyCurrentOTAPKIRef();
681 trustedLogs = SecOTAPKICopyNonTlsTrustedCTLogs(otapkiref);
682 CFReleaseSafe(otapkiref);
683 }
684
685 CFDictionaryRef currentLogsValidatingScts = NULL;
686 CFDictionaryRef logsValidatingEmbeddedScts = NULL;
687 bool at_least_one_currently_valid_external = false;
688 bool at_least_one_currently_valid_embedded = false;
689 CFIndex trustedSCTCount = 0;
690 SecCertificatePathVCRef path = SecPathBuilderGetPath(pvc->builder);
691 bool result = false;
692
693 SecCertificatePathVCSetIsCT(path, false);
694 if (find_validating_logs(pvc, trustedLogs, &currentLogsValidatingScts, &logsValidatingEmbeddedScts, &at_least_one_currently_valid_external, &at_least_one_currently_valid_embedded)) {
695 if (verify_tls_ct_policy(pvc, currentLogsValidatingScts, logsValidatingEmbeddedScts, at_least_one_currently_valid_external, at_least_one_currently_valid_embedded, &trustedSCTCount)) {
696 result = true;
697 }
698 report_ct_analytics(pvc, currentLogsValidatingScts, trustedSCTCount);
699 }
700
701 CFReleaseNull(currentLogsValidatingScts);
702 CFReleaseNull(logsValidatingEmbeddedScts);
703 CFReleaseNull(trustedLogs);
704 return result;
705 }