2 * Copyright (c) 2020 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 #include <libDER/DER_CertCrl.h>
26 #include <libDER/DER_Encode.h>
27 #include <libDER/asn1Types.h>
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>
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"
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");
51 kSecCTEntryTypeCert = 0,
52 kSecCTEntryTypePreCert = 1,
58 Version sct_version; // 1 byte
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
64 SignatureType signature_type = certificate_timestamp;
66 LogEntryType entry_type;
68 case x509_entry: ASN.1Cert;
69 case precert_entry: PreCert;
71 CtExtensions extensions;
73 } SignedCertificateTimestamp;
78 SecAsn1Oid *oidForSigAlg(SSL_HashAlgorithm hash, SSL_SignatureAlgorithm alg)
81 case SSL_SignatureAlgorithmRSA:
83 case SSL_HashAlgorithmSHA1:
84 return &CSSMOID_SHA1WithRSA;
85 case SSL_HashAlgorithmSHA256:
86 return &CSSMOID_SHA256WithRSA;
87 case SSL_HashAlgorithmSHA384:
88 return &CSSMOID_SHA384WithRSA;
92 case SSL_SignatureAlgorithmECDSA:
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;
111 static size_t SSLDecodeUint16(const uint8_t *p)
113 return (p[0]<<8 | p[1]);
116 static uint8_t *SSLEncodeUint16(uint8_t *p, size_t len)
118 p[0] = (len >> 8)&0xff;
123 static uint8_t *SSLEncodeUint24(uint8_t *p, size_t len)
125 p[0] = (len >> 16)&0xff;
126 p[1] = (len >> 8)&0xff;
133 uint64_t SSLDecodeUint64(const uint8_t *p)
136 for(int i=0; i<8; i++) {
144 static CFDataRef copy_x509_entry_from_chain(SecPVCRef pvc)
146 SecCertificateRef leafCert = SecPVCGetCertificateAtIndex(pvc, 0);
148 CFMutableDataRef data = CFDataCreateMutable(kCFAllocatorDefault, 3+SecCertificateGetLength(leafCert));
150 CFDataSetLength(data, 3+SecCertificateGetLength(leafCert));
152 uint8_t *q = CFDataGetMutableBytePtr(data);
153 q = SSLEncodeUint24(q, SecCertificateGetLength(leafCert));
154 memcpy(q, SecCertificateGetBytePtr(leafCert), SecCertificateGetLength(leafCert));
160 static CFDataRef copy_precert_entry_from_chain(SecPVCRef pvc)
162 SecCertificateRef leafCert = NULL;
163 SecCertificateRef issuer = NULL;
164 CFDataRef issuerKeyHash = NULL;
165 CFDataRef tbs_precert = NULL;
166 CFMutableDataRef data= NULL;
168 require_quiet(SecPVCGetCertificateCount(pvc)>=2, out); //we need the issuer key for precerts.
169 leafCert = SecPVCGetCertificateAtIndex(pvc, 0);
170 issuer = SecPVCGetCertificateAtIndex(pvc, 1);
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);
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));
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));
188 CFReleaseSafe(issuerKeyHash);
189 CFReleaseSafe(tbs_precert);
194 CFAbsoluteTime TimestampToCFAbsoluteTime(uint64_t ts)
196 return (ts / 1000) - kCFAbsoluteTimeIntervalSince1970;
200 uint64_t TimestampFromCFAbsoluteTime(CFAbsoluteTime at)
202 return (uint64_t)(at + kCFAbsoluteTimeIntervalSince1970) * 1000;
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)) {
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);
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))) {
224 if (end_exclusive && (cert_expiry_date >= CFDateGetAbsoluteTime(end_exclusive))) {
233 If the 'sct' is valid, add it to the validatingLogs dictionary.
236 - validatingLogs: mutable dictionary to which to add the log that validate this SCT.
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.
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.
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.
254 static CFDictionaryRef getSCTValidatingLog(CFDataRef sct, int entry_type, CFDataRef entry, uint64_t vt, CFAbsoluteTime cert_expiry_date, CFDictionaryRef trustedLogs, CFAbsoluteTime *sct_at)
257 const uint8_t *logID;
258 const uint8_t *timestampData;
260 size_t extensionsLen;
261 const uint8_t *extensionsData;
265 const uint8_t *signatureData;
266 SecKeyRef pubKey = NULL;
267 uint8_t *signed_data = NULL;
268 const SecAsn1Oid *oid = NULL;
270 CFDataRef logIDData = NULL;
271 CFDictionaryRef result = 0;
273 const uint8_t *p = CFDataGetBytePtr(sct);
274 size_t len = CFDataGetLength(sct);
276 require(len>=43, out);
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;
283 require(len>=extensionsLen, out);
284 extensionsData = p; p+=extensionsLen; len-=extensionsLen;
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 */
293 /* verify version: only v1(0) is supported */
295 secerror("SCT version unsupported: %d\n", version);
299 /* verify timestamp not in the future */
300 timestamp = SSLDecodeUint64(timestampData);
302 secerror("SCT is in the future: %llu > %llu\n", timestamp, vt);
309 size_t signed_data_len = 12 + CFDataGetLength(entry) + 2 + extensionsLen ;
310 signed_data = malloc(signed_data_len);
311 require(signed_data, out);
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);
321 logIDData = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, logID, 32, kCFAllocatorNull);
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);
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);
332 oid = oidForSigAlg(hashAlg, sigAlg);
335 algId.algorithm = *oid;
336 algId.parameters.Data = NULL;
337 algId.parameters.Length = 0;
339 if(SecKeyDigestAndVerify(pubKey, &algId, signed_data, signed_data_len, signatureData, signatureLen)==0) {
343 secerror("SCT signature failed (log=%@)\n", logData);
347 CFReleaseSafe(logIDData);
348 CFReleaseSafe(pubKey);
354 static void addValidatingLog(CFMutableDictionaryRef validatingLogs, CFDictionaryRef log, CFAbsoluteTime sct_at)
356 CFDateRef validated_time = CFDictionaryGetValue(validatingLogs, log);
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);
365 static CFArrayRef copy_ocsp_scts(SecPVCRef pvc)
367 CFMutableArrayRef SCTs = NULL;
368 SecCertificateRef leafCert = NULL;
369 SecCertificateRef issuer = NULL;
370 CFArrayRef ocspResponsesData = NULL;
371 SecOCSPRequestRef ocspRequest = NULL;
373 ocspResponsesData = SecPathBuilderCopyOCSPResponses(pvc->builder);
374 require_quiet(ocspResponsesData, out);
376 require_quiet(SecPVCGetCertificateCount(pvc)>=2, out); //we need the issuer key for precerts.
377 leafCert = SecPVCGetCertificateAtIndex(pvc, 0);
378 issuer = SecPVCGetCertificateAtIndex(pvc, 1);
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);
384 SCTs = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
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);
398 SecOCSPSingleResponseDestroy(ocspSingleResponse);
401 if(ocspResponse) SecOCSPResponseFinalize(ocspResponse);
404 if(CFArrayGetCount(SCTs)==0) {
409 CFReleaseSafe(ocspResponsesData);
411 SecOCSPRequestFinalize(ocspRequest);
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);
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);
434 uint64_t vt = TimestampFromCFAbsoluteTime(SecPVCGetVerifyTime(pvc));
436 __block bool at_least_one_currently_valid_external = 0;
437 __block bool at_least_one_currently_valid_embedded = 0;
439 require(logsValidatingEmbeddedScts, out);
440 require(currentLogsValidatingScts, out);
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);
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);
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;
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);
468 addValidatingLog(currentLogsValidatingScts, log, sct_at);
469 at_least_one_currently_valid_external = true;
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);
479 addValidatingLog(currentLogsValidatingScts, log, sct_at);
480 at_least_one_currently_valid_external = true;
486 if (CFDictionaryGetCount(currentLogsValidatingScts) > 0) {
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;
495 CFReleaseSafe(logsValidatingEmbeddedScts);
496 CFReleaseSafe(currentLogsValidatingScts);
497 CFReleaseSafe(builderScts);
498 CFReleaseSafe(embeddedScts);
499 CFReleaseSafe(ocspScts);
500 CFReleaseSafe(precertEntry);
501 CFReleaseSafe(x509Entry);
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)
509 if (!logsValidatingEmbeddedScts || !currentLogsValidatingScts) {
513 __block CFAbsoluteTime issuanceTime = SecPVCGetVerifyTime(pvc);
514 SecCertificateRef leafCert = SecPVCGetCertificateAtIndex(pvc, 0);
515 __block CFIndex trustedSCTCount = CFDictionaryGetCount(currentLogsValidatingScts);
517 /* We now have 2 sets of logs that validated those SCTS, count them and make a final decision.
520 is_ct = (A1 AND A2) OR (B1 AND B2).
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.
525 B1: SCTs from 2 currently valid logs (from any source)
526 B2: At least 1 external SCT from a currently valid log.
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);
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;
548 SecCertificatePathVCSetIssuanceTime(path, issuanceTime);
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. */
555 } else if (hasValidEmbeddedSCT) {
556 __block int lifetime; // in Months
557 __block unsigned once_or_current_qualified_embedded = 0;
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++;
572 failed_once_check = true;
576 SecCFCalendarDoWithZuluCalendar(^(CFCalendarRef zuluCalendar) {
578 CFCalendarGetComponentDifference(zuluCalendar,
579 SecCertificateNotValidBefore(leafCert),
580 SecCertificateNotValidAfter(leafCert),
582 lifetime = _lifetime;
585 unsigned requiredEmbeddedSctsCount;
588 requiredEmbeddedSctsCount = 2;
589 } else if (lifetime <= 27) {
590 requiredEmbeddedSctsCount = 3;
591 } else if (lifetime <= 39) {
592 requiredEmbeddedSctsCount = 4;
594 requiredEmbeddedSctsCount = 5;
597 if(once_or_current_qualified_embedded >= requiredEmbeddedSctsCount){
602 *outTrustedSCTCount = trustedSCTCount;
606 static void report_ct_analytics(SecPVCRef pvc, CFDictionaryRef currentLogsValidatingScts, CFIndex trustedSCTCount)
608 /* Record analytics data for CT */
609 TrustAnalyticsBuilder *analytics = SecPathBuilderGetAnalyticsData(pvc->builder);
614 SecCertificateRef leafCert = SecPVCGetCertificateAtIndex(pvc, 0);
615 CFArrayRef embeddedScts = SecCertificateCopySignedCertificateTimestamps(leafCert);
616 CFArrayRef builderScts = SecPathBuilderCopySignedCertificateTimestamps(pvc->builder);
617 CFArrayRef ocspScts = copy_ocsp_scts(pvc);
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);
625 if (builderScts && CFArrayGetCount(builderScts) > 0) {
626 analytics->sct_sources |= TA_SCT_TLS;
627 sctCount += CFArrayGetCount(builderScts);
629 if (ocspScts && CFArrayGetCount(ocspScts) > 0) {
630 analytics->sct_sources |= TA_SCT_OCSP;
631 sctCount += CFArrayGetCount(ocspScts);
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;
642 CFReleaseNull(embeddedScts);
643 CFReleaseNull(builderScts);
644 CFReleaseNull(ocspScts);
647 void SecPolicyCheckCT(SecPVCRef pvc)
649 CFDictionaryRef trustedLogs = SecPathBuilderCopyTrustedLogs(pvc->builder);
651 SecOTAPKIRef otapkiref = SecOTAPKICopyCurrentOTAPKIRef();
652 trustedLogs = SecOTAPKICopyTrustedCTLogs(otapkiref);
653 CFReleaseSafe(otapkiref);
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);
663 SecCertificatePathVCSetIsCT(path, false);
664 if (find_validating_logs(pvc, trustedLogs, ¤tLogsValidatingScts, &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);
668 report_ct_analytics(pvc, currentLogsValidatingScts, trustedSCTCount);
671 CFReleaseNull(currentLogsValidatingScts);
672 CFReleaseNull(logsValidatingEmbeddedScts);
673 CFReleaseNull(trustedLogs);
676 bool SecPolicyCheckNonTlsCT(SecPVCRef pvc)
678 CFDictionaryRef trustedLogs = SecPathBuilderCopyTrustedLogs(pvc->builder);
680 SecOTAPKIRef otapkiref = SecOTAPKICopyCurrentOTAPKIRef();
681 trustedLogs = SecOTAPKICopyNonTlsTrustedCTLogs(otapkiref);
682 CFReleaseSafe(otapkiref);
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);
693 SecCertificatePathVCSetIsCT(path, false);
694 if (find_validating_logs(pvc, trustedLogs, ¤tLogsValidatingScts, &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)) {
698 report_ct_analytics(pvc, currentLogsValidatingScts, trustedSCTCount);
701 CFReleaseNull(currentLogsValidatingScts);
702 CFReleaseNull(logsValidatingEmbeddedScts);
703 CFReleaseNull(trustedLogs);