*/
- (void)attemptProperDatabasePermissions
{
-#if TARGET_OS_IPHONE
NSFileManager* fm = [NSFileManager defaultManager];
[fm setAttributes:@{NSFilePosixPermissions : [NSNumber numberWithShort:0666]}
ofItemAtPath:_path
[fm setAttributes:@{NSFilePosixPermissions : [NSNumber numberWithShort:0666]}
ofItemAtPath:[NSString stringWithFormat:@"%@-shm",_path]
error:nil];
-#endif
}
- (BOOL)openWithError:(NSError **)error {
static const uint8_t appleComponentExtensionArc[8] = { 0x2a,0x86,0x48,0x86,0xf7,0x63,0x64,0x0b };
static const uint8_t appleSigningExtensionArc[8] = { 0x2a,0x86,0x48,0x86,0xf7,0x63,0x64,0x0c };
static const uint8_t appleEncryptionExtensionArc[8] = { 0x2a,0x86,0x48,0x86,0xf7,0x63,0x64,0x0d };
+ static const uint8_t appleExternalEncryptionExtensionArc[8] = { 0x2a,0x86,0x48,0x86,0xf7,0x63,0x64,0x0f };
if (!extnID || !extnID->data || (extnID->length <= sizeof(appleExtensionArc))) {
return false;
}
return (!memcmp(extnID->data, appleExtensionArc, sizeof(appleExtensionArc)) ||
!memcmp(extnID->data, appleComponentExtensionArc, sizeof(appleComponentExtensionArc)) ||
!memcmp(extnID->data, appleSigningExtensionArc, sizeof(appleSigningExtensionArc)) ||
- !memcmp(extnID->data, appleEncryptionExtensionArc, sizeof(appleEncryptionExtensionArc)));
+ !memcmp(extnID->data, appleEncryptionExtensionArc, sizeof(appleEncryptionExtensionArc)) ||
+ !memcmp(extnID->data, appleExternalEncryptionExtensionArc, sizeof(appleExternalEncryptionExtensionArc)));
}
static bool isCCCExtensionOID(const DERItem *extnID)
#define SEC_TRUST_ERROR_ValidityPeriodMaximums SecStringWithDefaultValue("Certificate exceeds maximum temporal validity period", "Trust", 0, "Certificate exceeds maximum temporal validity period", "Error for certificates that exceed the system's maximum temporal validity")
#define SEC_TRUST_ERROR_ServerAuthEKU SecStringWithDefaultValue("Extended key usage does not match certificate usage", "Trust", 0, "Extended key usage does not match certificate usage", "Error for extended key usage mismatch")
#define SEC_TRUST_ERROR_UnparseableExtension SecStringWithDefaultValue("Unable to parse known extension", "Trust", 0, "Unable to parse known extension", "Error for unparseable known extensions")
+#define SEC_TRUST_ERROR_NonTlsCTRequired SecStringWithDefaultValue("Certificate Transparency validation required but missing", "Trust", 0, "Certificate Transparency validation required but missing", "Error for missing Certificate Transparency validation")
__END_DECLS
policy = SecPolicyCreateAppleComponentCertificate(rootDigest);
} else if (CFEqual(policyIdentifier, kSecPolicyAppleAggregateMetricTransparency)) {
policy = SecPolicyCreateAggregateMetricTransparency(!client);
+ } else if (CFEqual(policyIdentifier, kSecPolicyAppleAggregateMetricEncryption)) {
+ policy = SecPolicyCreateAggregateMetricEncryption(!client);
}
/* For a couple of common patterns we use the macro, but some of the
* policies are deprecated (or not yet available), so we need to ignore the warning. */
CFReleaseSafe(options);
return result;
}
+
+SecPolicyRef SecPolicyCreateAggregateMetricEncryption(bool facilitator)
+{
+ CFMutableDictionaryRef options = NULL;
+ SecPolicyRef result = NULL;
+
+ require(options = CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
+ &kCFTypeDictionaryKeyCallBacks,
+ &kCFTypeDictionaryValueCallBacks), errOut);
+
+ SecPolicyAddBasicX509Options(options);
+
+ /* Anchored to the Apple Roots */
+ require(SecPolicyAddAppleAnchorOptions(options, kSecPolicyNameAggregateMetricEncryption), errOut);
+
+ /* Exactly 3 certs in the chain */
+ require(SecPolicyAddChainLengthOptions(options, 3), errOut);
+
+ /* Intermediate marker OID matches AAICA 6 */
+ add_element(options, kSecPolicyCheckIntermediateMarkerOid, CFSTR("1.2.840.113635.100.6.2.26"));
+
+ /* Leaf marker OID matches expected OID for either Facilitator or Partner */
+ if (facilitator) {
+ add_leaf_marker_string(options, CFSTR("1.2.840.113635.100.15.2"));
+ } else {
+ add_leaf_marker_string(options, CFSTR("1.2.840.113635.100.15.3"));
+ }
+
+ /* Check revocation using any available method */
+ add_element(options, kSecPolicyCheckRevocation, kSecPolicyCheckRevocationAny);
+
+ /* RSA key sizes are 2048-bit or larger. EC key sizes are P-256 or larger. */
+ require(SecPolicyAddStrongKeySizeOptions(options), errOut);
+
+ /* Require CT */
+ if (!SecIsInternalRelease() || !isCFPreferenceInSecurityDomain(CFSTR("disableAggregateMetricsCTCheck"))) {
+ add_element(options, kSecPolicyCheckNonTlsCTRequired, kCFBooleanTrue);
+ }
+
+ require(result = SecPolicyCreate(kSecPolicyAppleAggregateMetricEncryption,
+ kSecPolicyNameAggregateMetricEncryption, options), errOut);
+
+errOut:
+ CFReleaseSafe(options);
+ return result;
+}
POLICYMACRO(EscrowServiceIdKeySigning, 100, E, AppleEscrowServiceIdKeySigning, , Y, EscrowServiceIdKeySigning)
POLICYMACRO(PCSEscrowServiceIdKeySigning, 101, E, ApplePCSEscrowServiceIdKeySigning, , Y, PCSEscrowServiceIdKeySigning)
POLICYMACRO(AggregateMetricTransparency, 102, E, AggregateMetricTransparency, , , AggregateMetricTransparency)
+POLICYMACRO(AggregateMetricEncryption, 103, E, AggregateMetricEncryption, , , AggregateMetricEncryption)
POLICYCHECKMACRO(ValidityPeriodMaximums, R, C, , A, , 0x8001210D, errSecCertificateValidityPeriodTooLong) //CSSMERR_TP_CERT_SUSPENDED
POLICYCHECKMACRO(ServerAuthEKU, R, U, , A, , 0x80012407, errSecInvalidExtendedKeyUsage) //CSSMERR_APPLETP_INVALID_EXTENDED_KEY_USAGE
POLICYCHECKMACRO(UnparseableExtension, R, C, , , O, 0x80012410, errSecUnknownCertExtension) //CSSMERR_APPLETP_UNKNOWN_CERT_EXTEN
+POLICYCHECKMACRO(NonTlsCTRequired, R, T, , A, , 0x80012114, errSecVerifyActionFailed) //CSSMERR_TP_VERIFY_ACTION_FAILED
/********************************************************
******************* Feature Toggles *********************
D47AB2D12356B2FE005A3801 /* Network.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D47AB2CA2356AD72005A3801 /* Network.framework */; };
D47AB2D22356B325005A3801 /* Network.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D47AB2CA2356AD72005A3801 /* Network.framework */; };
D47AB2D62357955F005A3801 /* Network.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D47AB2CA2356AD72005A3801 /* Network.framework */; };
+ D47C457A255244AD00460750 /* CertificateTransparency.m in Sources */ = {isa = PBXBuildFile; fileRef = D47C4579255244AD00460750 /* CertificateTransparency.m */; };
D47CA65D1EB036450038E2BB /* libMobileGestalt.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = D47CA65C1EB036450038E2BB /* libMobileGestalt.dylib */; };
D47DCCB523427C7D00B80E37 /* md.m in Sources */ = {isa = PBXBuildFile; fileRef = D47DCCB423427C7D00B80E37 /* md.m */; };
D47E69401E92F75D002C8CF6 /* si-61-pkcs12.c in Sources */ = {isa = PBXBuildFile; fileRef = DCC78DD91D8085FC00865A7C /* si-61-pkcs12.c */; };
D47A085B2486EC1A000F2C49 /* AppleExternalRootCertificates.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppleExternalRootCertificates.h; sourceTree = "<group>"; };
D47A55892466100A0039285D /* MSUDataAccessor.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MSUDataAccessor.framework; path = System/Library/PrivateFrameworks/MSUDataAccessor.framework; sourceTree = SDKROOT; };
D47AB2CA2356AD72005A3801 /* Network.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Network.framework; path = System/Library/Frameworks/Network.framework; sourceTree = SDKROOT; };
+ D47C4579255244AD00460750 /* CertificateTransparency.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CertificateTransparency.m; sourceTree = "<group>"; };
+ D47C4730255246FC00460750 /* CertificateTransparency.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CertificateTransparency.h; sourceTree = "<group>"; };
D47C56AB1DCA831C00E18518 /* lib_ios_x64.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = lib_ios_x64.xcconfig; path = xcconfig/lib_ios_x64.xcconfig; sourceTree = "<group>"; };
D47C56AF1DCA841D00E18518 /* lib_ios_x64_shim.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = lib_ios_x64_shim.xcconfig; path = xcconfig/lib_ios_x64_shim.xcconfig; sourceTree = "<group>"; };
D47CA65C1EB036450038E2BB /* libMobileGestalt.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libMobileGestalt.dylib; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS11.0.Internal.sdk/usr/lib/libMobileGestalt.dylib; sourceTree = DEVELOPER_DIR; };
D4B68C65211A8186009FED69 /* trustd_spi.c */,
D47DCCB423427C7D00B80E37 /* md.m */,
D47DCCB723427C8D00B80E37 /* md.h */,
+ D47C4579255244AD00460750 /* CertificateTransparency.m */,
+ D47C4730255246FC00460750 /* CertificateTransparency.h */,
D43DBED71E99D17100C04AEA /* nameconstraints.c */,
D43DBED81E99D17100C04AEA /* nameconstraints.h */,
D43DBED91E99D17100C04AEA /* OTATrustUtilities.m */,
5F84950222DFB505008B3EFB /* SecTrustExceptionResetCount.m in Sources */,
D43DBF041E99D1CA00C04AEA /* SecOCSPCache.c in Sources */,
D43DBF051E99D1CA00C04AEA /* SecOCSPRequest.c in Sources */,
+ D47C457A255244AD00460750 /* CertificateTransparency.m in Sources */,
D4B68C68211A827C009FED69 /* trustd_spi.c in Sources */,
D43761671EB2996C00954447 /* SecRevocationNetworking.m in Sources */,
D4EF3221215F0F7F000A31A5 /* SecTrustStoreServer.m in Sources */,
}
@end
+
+// MARK: -
+// MARK: Non-TLS CT tests
+
+@interface NonTlsCTTests : TrustEvaluationTestCase
+@end
+
+@implementation NonTlsCTTests
++ (void)setUp {
+ [super setUp];
+ NSURL *trustedLogsURL = [[NSBundle bundleForClass:[self class]] URLForResource:@"CTlogs"
+ withExtension:@"plist"
+ subdirectory:@"si-82-sectrust-ct-data"];
+ trustedCTLogs = [NSArray arrayWithContentsOfURL:trustedLogsURL];
+}
+
+- (SecPolicyRef)nonTlsCTRequiredPolicy
+{
+ SecPolicyRef policy = SecPolicyCreateBasicX509();
+ SecPolicySetOptionsValue(policy, kSecPolicyCheckNonTlsCTRequired, kCFBooleanTrue);
+ return policy;
+}
+
+#if !TARGET_OS_BRIDGE
+/* Skip tests on bridgeOS where we don't do MobileAsset updates */
+- (void)testNoMACheckIn {
+ SecCertificateRef system_root = NULL, system_server_after = NULL;
+ SecTrustRef trust = NULL;
+ SecPolicyRef policy = [self nonTlsCTRequiredPolicy];
+ NSArray *enforce_anchors = nil;
+ NSDate *date = [NSDate dateWithTimeIntervalSinceReferenceDate:562340800.0]; // October 27, 2018 at 6:46:40 AM PDT
+
+ /* Mock a failing MobileAsset so we don't enforce via MobileAsset */
+ id mockFailedMA = OCMClassMock([MAAsset class]);
+ OCMStub([mockFailedMA startCatalogDownload:[OCMArg any]
+ options:[OCMArg any]
+ then:([OCMArg invokeBlockWithArgs:OCMOCK_VALUE((NSInteger){MADownloadFailed}), nil])]);
+ SecOTAPKIResetCurrentAssetVersion(NULL);
+
+ require_action(system_root = (__bridge SecCertificateRef)[CTTests SecCertificateCreateFromResource:@"enforcement_system_root"],
+ errOut, fail("failed to create system root"));
+ require_action(system_server_after = (__bridge SecCertificateRef)[CTTests SecCertificateCreateFromResource:@"enforcement_system_server_after"],
+ errOut, fail("failed to create server cert"));
+
+ enforce_anchors = @[ (__bridge id)system_root ];
+ require_noerr_action(SecTrustCreateWithCertificates(system_server_after, policy, &trust), errOut, fail("failed to create trust"));
+ require_noerr_action(SecTrustSetAnchorCertificates(trust, (__bridge CFArrayRef)enforce_anchors), errOut, fail("failed to set anchors"));
+ require_noerr_action(SecTrustSetVerifyDate(trust, (__bridge CFDateRef)date), errOut, fail("failed to set verify date"));
+
+ // Out-of-date asset, test cert without CT passes
+ ok(SecTrustEvaluateWithError(trust, NULL), "non-CT cert failed with out-of-date asset");
+
+errOut:
+ CFReleaseNull(system_root);
+ CFReleaseNull(system_server_after);
+ CFReleaseNull(policy);
+ CFReleaseNull(trust);
+}
+
+- (void)testKillSwitch {
+ SecCertificateRef system_root = NULL, system_server_after = NULL;
+ SecTrustRef trust = NULL;
+ SecPolicyRef policy = [self nonTlsCTRequiredPolicy];
+ NSArray *enforce_anchors = nil;
+ NSDate *date = [NSDate dateWithTimeIntervalSinceReferenceDate:562340800.0]; // October 27, 2018 at 6:46:40 AM PDT
+
+ /* Mock setting a kill switch */
+ UpdateKillSwitch((__bridge NSString *)kOTAPKIKillSwitchNonTLSCT, true);
+
+ require_action(system_root = (__bridge SecCertificateRef)[CTTests SecCertificateCreateFromResource:@"enforcement_system_root"],
+ errOut, fail("failed to create system root"));
+ require_action(system_server_after = (__bridge SecCertificateRef)[CTTests SecCertificateCreateFromResource:@"enforcement_system_server_after"],
+ errOut, fail("failed to create system server cert"));
+
+ enforce_anchors = @[ (__bridge id)system_root ];
+ require_noerr_action(SecTrustCreateWithCertificates(system_server_after, policy, &trust), errOut, fail("failed to create trust"));
+ require_noerr_action(SecTrustSetAnchorCertificates(trust, (__bridge CFArrayRef)enforce_anchors), errOut, fail("failed to set anchors"));
+ require_noerr_action(SecTrustSetVerifyDate(trust, (__bridge CFDateRef)date), errOut, fail("failed to set verify date"));
+
+ // CT kill switch enabled so test cert without CT passes
+ ok(SecTrustEvaluateWithError(trust, NULL), "non-CT cert failed with kill switch enabled");
+
+ /* Remove the kill switch */
+ UpdateKillSwitch((__bridge NSString *)kOTAPKIKillSwitchNonTLSCT, false);
+
+errOut:
+ CFReleaseNull(system_root);
+ CFReleaseNull(system_server_after);
+ CFReleaseNull(policy);
+ CFReleaseNull(trust);
+}
+
+- (void) testWithMACheckIn {
+ SecCertificateRef system_root = NULL, system_server_after = NULL;
+ SecTrustRef trust = NULL;
+ SecPolicyRef policy = [self nonTlsCTRequiredPolicy];
+ NSArray *enforce_anchors = nil;
+ NSDate *date = [NSDate dateWithTimeIntervalSinceReferenceDate:562340800.0]; // October 27, 2018 at 6:46:40 AM PDT
+ CFErrorRef error = nil;
+
+ /* Mock a successful mobile asset check-in so that we enforce CT */
+ XCTAssertTrue(UpdateOTACheckInDate(), "failed to set check-in date as now");
+
+ require_action(system_root = (__bridge SecCertificateRef)[CTTests SecCertificateCreateFromResource:@"enforcement_system_root"],
+ errOut, fail("failed to create system root"));
+ require_action(system_server_after = (__bridge SecCertificateRef)[CTTests SecCertificateCreateFromResource:@"enforcement_system_server_after"],
+ errOut, fail("failed to create system server cert"));
+
+ enforce_anchors = @[ (__bridge id)system_root ];
+ require_noerr_action(SecTrustCreateWithCertificates(system_server_after, policy, &trust), errOut, fail("failed to create trust"));
+ require_noerr_action(SecTrustSetAnchorCertificates(trust, (__bridge CFArrayRef)enforce_anchors), errOut, fail("failed to set anchors"));
+ require_noerr_action(SecTrustSetVerifyDate(trust, (__bridge CFDateRef)date), errOut, fail("failed to set verify date"));
+
+ // test system cert after date without CT fails (with check-in)
+ is(SecTrustEvaluateWithError(trust, &error), false, "non-CT cert with in-date asset succeeded");
+ if (error) {
+ is(CFErrorGetCode(error), errSecVerifyActionFailed, "got wrong error code for non-ct cert, got %ld, expected %d",
+ (long)CFErrorGetCode(error), (int)errSecVerifyActionFailed);
+ } else {
+ fail("expected trust evaluation to fail and it did not.");
+ }
+
+errOut:
+ CFReleaseNull(system_root);
+ CFReleaseNull(system_server_after);
+ CFReleaseNull(policy);
+ CFReleaseNull(trust);
+ CFReleaseNull(error);
+}
+#endif // !TARGET_OS_BRIDGE
+
+- (void)testWithTrustedLogs {
+ SecCertificateRef system_root = NULL, system_server_after = NULL;
+ SecTrustRef trust = NULL;
+ SecPolicyRef policy = [self nonTlsCTRequiredPolicy];
+ NSArray *enforce_anchors = nil;
+ NSDate *date = [NSDate dateWithTimeIntervalSinceReferenceDate:562340800.0]; // October 27, 2018 at 6:46:40 AM PDT
+ CFErrorRef error = nil;
+
+ require_action(system_root = (__bridge SecCertificateRef)[CTTests SecCertificateCreateFromResource:@"enforcement_system_root"],
+ errOut, fail("failed to create system root"));
+ require_action(system_server_after = (__bridge SecCertificateRef)[CTTests SecCertificateCreateFromResource:@"enforcement_system_server_after"],
+ errOut, fail("failed to create system server cert"));
+
+ enforce_anchors = @[ (__bridge id)system_root ];
+ require_noerr_action(SecTrustCreateWithCertificates(system_server_after, policy, &trust), errOut, fail("failed to create trust"));
+ require_noerr_action(SecTrustSetAnchorCertificates(trust, (__bridge CFArrayRef)enforce_anchors), errOut, fail("failed to set anchors"));
+ require_noerr_action(SecTrustSetVerifyDate(trust, (__bridge CFDateRef)date), errOut, fail("failed to set verify date"));
+
+ // set trusted logs to trigger enforcing behavior
+ require_noerr_action(SecTrustSetTrustedLogs(trust, (__bridge CFArrayRef)trustedCTLogs), errOut, fail("failed to set trusted logs"));
+
+ // test system cert without CT fails (with trusted logs)
+ is(SecTrustEvaluateWithError(trust, &error), false, "non-CT cert with trusted logs succeeded");
+ if (error) {
+ is(CFErrorGetCode(error), errSecVerifyActionFailed, "got wrong error code for non-ct cert, got %ld, expected %d",
+ (long)CFErrorGetCode(error), (int)errSecVerifyActionFailed);
+ } else {
+ fail("expected trust evaluation to fail and it did not.");
+ }
+
+errOut:
+ CFReleaseNull(system_root);
+ CFReleaseNull(system_server_after);
+ CFReleaseNull(policy);
+ CFReleaseNull(trust);
+ CFReleaseNull(error);
+}
+
+- (void) testSuccess {
+ SecCertificateRef system_root = NULL, leaf = NULL;
+ SecTrustRef trust = NULL;
+ SecPolicyRef policy = [self nonTlsCTRequiredPolicy];
+ NSArray *enforce_anchors = nil;
+ NSDate *date = [NSDate dateWithTimeIntervalSinceReferenceDate:562340800.0]; // October 27, 2018 at 6:46:40 AM PDT
+
+ require_action(system_root = (__bridge SecCertificateRef)[CTTests SecCertificateCreateFromResource:@"enforcement_system_root"],
+ errOut, fail("failed to create system root"));
+ require_action(leaf = (__bridge SecCertificateRef)[CTTests SecCertificateCreateFromResource:@"enforcement_system_server_after_scts"],
+ errOut, fail("failed to create system server cert"));
+
+ enforce_anchors = @[ (__bridge id)system_root ];
+ require_noerr_action(SecTrustCreateWithCertificates(leaf, policy, &trust), errOut, fail("failed to create trust"));
+ require_noerr_action(SecTrustSetAnchorCertificates(trust, (__bridge CFArrayRef)enforce_anchors), errOut, fail("failed to set anchors"));
+ require_noerr_action(SecTrustSetVerifyDate(trust, (__bridge CFDateRef)date), errOut, fail("failed to set verify date"));
+ require_noerr_action(SecTrustSetTrustedLogs(trust, (__bridge CFArrayRef)trustedCTLogs), errOut, fail("failed to set trusted logs"));
+ ok(SecTrustEvaluateWithError(trust, NULL), "CT cert failed");
+
+errOut:
+ CFReleaseNull(system_root);
+ CFReleaseNull(leaf);
+ CFReleaseNull(policy);
+ CFReleaseNull(trust);
+}
+
+@end
API_AVAILABLE(macos(10.15.6), ios(13.5.5));
extern const CFStringRef kSecPolicyAppleAggregateMetricTransparency
API_AVAILABLE(macos(10.15.6), ios(13.6), watchos(6.2), tvos(13.4));
+extern const CFStringRef kSecPolicyAppleAggregateMetricEncryption
+ API_AVAILABLE(macos(11.1), ios(14.3), watchos(7.2), tvos(14.3));
/*!
SecPolicyRef SecPolicyCreateAggregateMetricTransparency(bool facilitator)
API_AVAILABLE(macos(10.15.6), ios(13.6), watchos(6.2), tvos(13.4));
+/*!
+ @function SecPolicyCreateAggregateMetricEncryption
+ @abstract Returns a policy object for verifying Aggregate Metric Encryption certificates
+ @param facilitator A boolean to indicate whether the facilitator or partner encryption
+ certificate is being checked.
+ @discussion The resulting policy uses the Basic X.509 policy with validity check and
+ pinning options:
+ * The chain is anchored to any of the Apple Root CAs.
+ * There are exactly 3 certs in the chain.
+ * The intermediate has a marker extension with OID 1.2.840.113635.100.6.2.26.
+ * The leaf has a marker extension with OID 1.2.840.113635.100.15.2 if facilitator is true or
+ 1.2.840.113635.100.15.3 if facilitator is false.
+ * Revocation is checked via any available method.
+ * RSA key sizes are 2048-bit or larger. EC key sizes are P-256 or larger.
+ * Require a positive CT verification result using the non-TLS CT log list
+ @result A policy object. The caller is responsible for calling CFRelease on this when
+ it is no longer needed.
+ */
+__nullable CF_RETURNS_RETAINED
+SecPolicyRef SecPolicyCreateAggregateMetricEncryption(bool facilitator)
+ API_AVAILABLE(macos(11.1), ios(14.3), watchos(7.2), tvos(14.3));
+
/*
* Legacy functions (OS X only)
*/
extern const CFStringRef kSecPolicyCheckNameConstraints;
extern const CFStringRef kSecPolicyCheckNoNetworkAccess;
extern const CFStringRef kSecPolicyCheckNonEmptySubject;
+extern const CFStringRef kSecPolicyCheckNonTlsCTRequired;
extern const CFStringRef kSecPolicyCheckNotCA;
extern const CFStringRef kSecPolicyCheckNotValidBefore;
extern const CFStringRef kSecPolicyCheckPinningRequired;
--- /dev/null
+/*
+ * Copyright (c) 2020 Apple Inc. All Rights Reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+#ifndef _SECURITY_CERTIFICATE_TRANSPARENCY_H_
+#define _SECURITY_CERTIFICATE_TRANSPARENCY_H_
+
+#include "trust/trustd/SecPolicyServer.h"
+
+void SecPolicyCheckCT(SecPVCRef pvc);
+bool SecPolicyCheckNonTlsCT(SecPVCRef pvc);
+
+#endif /* _SECURITY_CERTIFICATE_TRANSPARENCY_H_ */
--- /dev/null
+/*
+ * Copyright (c) 2020 Apple Inc. All Rights Reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+#include <AssertMacros.h>
+#include <libDER/DER_CertCrl.h>
+#include <libDER/DER_Encode.h>
+#include <libDER/asn1Types.h>
+
+#include <security_asn1/SecAsn1Coder.h>
+#include <security_asn1/oidsalg.h>
+#include <utilities/SecCFWrappers.h>
+#include <Security/SecCertificatePriv.h>
+#include <Security/SecureTransportPriv.h>
+#include <Security/SecKeyPriv.h>
+#include <Security/SecPolicyPriv.h>
+
+#include "trust/trustd/SecTrustServer.h"
+#include "trust/trustd/SecPolicyServer.h"
+#include "trust/trustd/SecOCSPResponse.h"
+#include "trust/trustd/OTATrustUtilities.h"
+#include "trust/trustd/SecCertificateServer.h"
+#include "trust/trustd/CertificateTransparency.h"
+
+const CFStringRef kSecCTRetirementDateKey = CFSTR("expiry"); // For backwards compatibility, retirement date is represented with the "expiry" key
+const CFStringRef kSecCTReadOnlyDateKey = CFSTR("frozen"); // For backwards compatibility, read-only date is represented with the "frozen" key
+const CFStringRef kSecCTShardStartDateKey = CFSTR("start_inclusive");
+const CFStringRef kSecCTShardEndDateKey = CFSTR("end_exclusive");
+const CFStringRef kSecCTPublicKeyKey = CFSTR("key");
+
+enum {
+ kSecCTEntryTypeCert = 0,
+ kSecCTEntryTypePreCert = 1,
+};
+
+/***
+
+struct {
+ Version sct_version; // 1 byte
+ LogID id; // 32 bytes
+ uint64 timestamp; // 8 bytes
+ CtExtensions extensions; // 2 bytes len field, + n bytes data
+ digitally-signed struct { // 1 byte hash alg, 1 byte sig alg, n bytes signature
+ Version sct_version;
+ SignatureType signature_type = certificate_timestamp;
+ uint64 timestamp;
+ LogEntryType entry_type;
+ select(entry_type) {
+ case x509_entry: ASN.1Cert;
+ case precert_entry: PreCert;
+ } signed_entry;
+ CtExtensions extensions;
+ };
+} SignedCertificateTimestamp;
+
+***/
+
+static const
+SecAsn1Oid *oidForSigAlg(SSL_HashAlgorithm hash, SSL_SignatureAlgorithm alg)
+{
+ switch(alg) {
+ case SSL_SignatureAlgorithmRSA:
+ switch (hash) {
+ case SSL_HashAlgorithmSHA1:
+ return &CSSMOID_SHA1WithRSA;
+ case SSL_HashAlgorithmSHA256:
+ return &CSSMOID_SHA256WithRSA;
+ case SSL_HashAlgorithmSHA384:
+ return &CSSMOID_SHA384WithRSA;
+ default:
+ break;
+ }
+ case SSL_SignatureAlgorithmECDSA:
+ switch (hash) {
+ case SSL_HashAlgorithmSHA1:
+ return &CSSMOID_ECDSA_WithSHA1;
+ case SSL_HashAlgorithmSHA256:
+ return &CSSMOID_ECDSA_WithSHA256;
+ case SSL_HashAlgorithmSHA384:
+ return &CSSMOID_ECDSA_WithSHA384;
+ default:
+ break;
+ }
+ default:
+ break;
+ }
+
+ return NULL;
+}
+
+
+static size_t SSLDecodeUint16(const uint8_t *p)
+{
+ return (p[0]<<8 | p[1]);
+}
+
+static uint8_t *SSLEncodeUint16(uint8_t *p, size_t len)
+{
+ p[0] = (len >> 8)&0xff;
+ p[1] = (len & 0xff);
+ return p+2;
+}
+
+static uint8_t *SSLEncodeUint24(uint8_t *p, size_t len)
+{
+ p[0] = (len >> 16)&0xff;
+ p[1] = (len >> 8)&0xff;
+ p[2] = (len & 0xff);
+ return p+3;
+}
+
+
+static
+uint64_t SSLDecodeUint64(const uint8_t *p)
+{
+ uint64_t u = 0;
+ for(int i=0; i<8; i++) {
+ u=(u<<8)|p[0];
+ p++;
+ }
+ return u;
+}
+
+
+static CFDataRef copy_x509_entry_from_chain(SecPVCRef pvc)
+{
+ SecCertificateRef leafCert = SecPVCGetCertificateAtIndex(pvc, 0);
+
+ CFMutableDataRef data = CFDataCreateMutable(kCFAllocatorDefault, 3+SecCertificateGetLength(leafCert));
+
+ CFDataSetLength(data, 3+SecCertificateGetLength(leafCert));
+
+ uint8_t *q = CFDataGetMutableBytePtr(data);
+ q = SSLEncodeUint24(q, SecCertificateGetLength(leafCert));
+ memcpy(q, SecCertificateGetBytePtr(leafCert), SecCertificateGetLength(leafCert));
+
+ return data;
+}
+
+
+static CFDataRef copy_precert_entry_from_chain(SecPVCRef pvc)
+{
+ SecCertificateRef leafCert = NULL;
+ SecCertificateRef issuer = NULL;
+ CFDataRef issuerKeyHash = NULL;
+ CFDataRef tbs_precert = NULL;
+ CFMutableDataRef data= NULL;
+
+ require_quiet(SecPVCGetCertificateCount(pvc)>=2, out); //we need the issuer key for precerts.
+ leafCert = SecPVCGetCertificateAtIndex(pvc, 0);
+ issuer = SecPVCGetCertificateAtIndex(pvc, 1);
+
+ require(leafCert, out);
+ require(issuer, out); // Those two would likely indicate an internal error, since we already checked the chain length above.
+ issuerKeyHash = SecCertificateCopySubjectPublicKeyInfoSHA256Digest(issuer);
+ tbs_precert = SecCertificateCopyPrecertTBS(leafCert);
+
+ require(issuerKeyHash, out);
+ require(tbs_precert, out);
+ data = CFDataCreateMutable(kCFAllocatorDefault, CFDataGetLength(issuerKeyHash) + 3 + CFDataGetLength(tbs_precert));
+ CFDataSetLength(data, CFDataGetLength(issuerKeyHash) + 3 + CFDataGetLength(tbs_precert));
+
+ uint8_t *q = CFDataGetMutableBytePtr(data);
+ memcpy(q, CFDataGetBytePtr(issuerKeyHash), CFDataGetLength(issuerKeyHash)); q += CFDataGetLength(issuerKeyHash); // issuer key hash
+ q = SSLEncodeUint24(q, CFDataGetLength(tbs_precert));
+ memcpy(q, CFDataGetBytePtr(tbs_precert), CFDataGetLength(tbs_precert));
+
+out:
+ CFReleaseSafe(issuerKeyHash);
+ CFReleaseSafe(tbs_precert);
+ return data;
+}
+
+static
+CFAbsoluteTime TimestampToCFAbsoluteTime(uint64_t ts)
+{
+ return (ts / 1000) - kCFAbsoluteTimeIntervalSince1970;
+}
+
+static
+uint64_t TimestampFromCFAbsoluteTime(CFAbsoluteTime at)
+{
+ return (uint64_t)(at + kCFAbsoluteTimeIntervalSince1970) * 1000;
+}
+
+static bool isSCTValidForLogData(CFDictionaryRef logData, int entry_type, CFAbsoluteTime sct_time, CFAbsoluteTime cert_expiry_date) {
+ /* only embedded SCTs can be used from retired logs. */
+ if(entry_type==kSecCTEntryTypeCert && CFDictionaryContainsKey(logData, kSecCTRetirementDateKey)) {
+ return false;
+ }
+
+ /* SCTs from after the transition to read-only are not valid (and indicate a operator failure) */
+ CFDateRef frozen_date = CFDictionaryGetValue(logData, kSecCTReadOnlyDateKey);
+ if (frozen_date && (sct_time > CFDateGetAbsoluteTime(frozen_date))) {
+ secerror("Frozen CT log issued SCT after freezing (log=%@)\n", logData);
+ return false;
+ }
+
+ /* If the log is temporally sharded, the certificate expiry date must be within the temporal shard window */
+ CFDateRef start_inclusive = CFDictionaryGetValue(logData, kSecCTShardStartDateKey);
+ CFDateRef end_exclusive = CFDictionaryGetValue(logData, kSecCTShardEndDateKey);
+ if (start_inclusive && (cert_expiry_date < CFDateGetAbsoluteTime(start_inclusive))) {
+ return false;
+ }
+ if (end_exclusive && (cert_expiry_date >= CFDateGetAbsoluteTime(end_exclusive))) {
+ return false;
+ }
+
+ return true;
+}
+
+
+/*
+ If the 'sct' is valid, add it to the validatingLogs dictionary.
+
+ Inputs:
+ - validatingLogs: mutable dictionary to which to add the log that validate this SCT.
+ - sct: the SCT date
+ - entry_type: 0 for x509 cert, 1 for precert.
+ - entry: the cert or precert data.
+ - vt: verification time timestamp (as used in SCTs: ms since 1970 Epoch)
+ - trustedLog: Dictionary contain the Trusted Logs.
+
+ The SCT is valid if:
+ - It decodes properly.
+ - Its timestamp is less than 'verifyTime'.
+ - It is signed by a log in 'trustedLogs'.
+ - If entry_type = 0, the log must be currently qualified.
+ - If entry_type = 1, the log may be expired.
+
+ If the SCT is valid, it's added to the validatinLogs dictionary using the log dictionary as the key, and the timestamp as value.
+ 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.
+
+ */
+static CFDictionaryRef getSCTValidatingLog(CFDataRef sct, int entry_type, CFDataRef entry, uint64_t vt, CFAbsoluteTime cert_expiry_date, CFDictionaryRef trustedLogs, CFAbsoluteTime *sct_at)
+{
+ uint8_t version;
+ const uint8_t *logID;
+ const uint8_t *timestampData;
+ uint64_t timestamp;
+ size_t extensionsLen;
+ const uint8_t *extensionsData;
+ uint8_t hashAlg;
+ uint8_t sigAlg;
+ size_t signatureLen;
+ const uint8_t *signatureData;
+ SecKeyRef pubKey = NULL;
+ uint8_t *signed_data = NULL;
+ const SecAsn1Oid *oid = NULL;
+ SecAsn1AlgId algId;
+ CFDataRef logIDData = NULL;
+ CFDictionaryRef result = 0;
+
+ const uint8_t *p = CFDataGetBytePtr(sct);
+ size_t len = CFDataGetLength(sct);
+
+ require(len>=43, out);
+
+ version = p[0]; p++; len--;
+ logID = p; p+=32; len-=32;
+ timestampData = p; p+=8; len-=8;
+ extensionsLen = SSLDecodeUint16(p); p+=2; len-=2;
+
+ require(len>=extensionsLen, out);
+ extensionsData = p; p+=extensionsLen; len-=extensionsLen;
+
+ require(len>=4, out);
+ hashAlg=p[0]; p++; len--;
+ sigAlg=p[0]; p++; len--;
+ signatureLen = SSLDecodeUint16(p); p+=2; len-=2;
+ require(len==signatureLen, out); /* We do not tolerate any extra data after the signature */
+ signatureData = p;
+
+ /* verify version: only v1(0) is supported */
+ if(version!=0) {
+ secerror("SCT version unsupported: %d\n", version);
+ goto out;
+ }
+
+ /* verify timestamp not in the future */
+ timestamp = SSLDecodeUint64(timestampData);
+ if(timestamp > vt) {
+ secerror("SCT is in the future: %llu > %llu\n", timestamp, vt);
+ goto out;
+ }
+
+ uint8_t *q;
+
+ /* signed entry */
+ size_t signed_data_len = 12 + CFDataGetLength(entry) + 2 + extensionsLen ;
+ signed_data = malloc(signed_data_len);
+ require(signed_data, out);
+ q = signed_data;
+ *q++ = version;
+ *q++ = 0; // certificate_timestamp
+ memcpy(q, timestampData, 8); q+=8;
+ q = SSLEncodeUint16(q, entry_type); // logentry type: 0=cert 1=precert
+ memcpy(q, CFDataGetBytePtr(entry), CFDataGetLength(entry)); q += CFDataGetLength(entry);
+ q = SSLEncodeUint16(q, extensionsLen);
+ memcpy(q, extensionsData, extensionsLen);
+
+ logIDData = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, logID, 32, kCFAllocatorNull);
+
+ CFDictionaryRef logData = CFDictionaryGetValue(trustedLogs, logIDData);
+ CFAbsoluteTime sct_time = TimestampToCFAbsoluteTime(timestamp);
+ require(logData && isSCTValidForLogData(logData, entry_type, sct_time, cert_expiry_date), out);
+
+ CFDataRef logKeyData = CFDictionaryGetValue(logData, kSecCTPublicKeyKey);
+ require(logKeyData, out); // This failing would be an internal logic error
+ pubKey = SecKeyCreateFromSubjectPublicKeyInfoData(kCFAllocatorDefault, logKeyData);
+ require(pubKey, out);
+
+ oid = oidForSigAlg(hashAlg, sigAlg);
+ require(oid, out);
+
+ algId.algorithm = *oid;
+ algId.parameters.Data = NULL;
+ algId.parameters.Length = 0;
+
+ if(SecKeyDigestAndVerify(pubKey, &algId, signed_data, signed_data_len, signatureData, signatureLen)==0) {
+ *sct_at = sct_time;
+ result = logData;
+ } else {
+ secerror("SCT signature failed (log=%@)\n", logData);
+ }
+
+out:
+ CFReleaseSafe(logIDData);
+ CFReleaseSafe(pubKey);
+ free(signed_data);
+ return result;
+}
+
+
+static void addValidatingLog(CFMutableDictionaryRef validatingLogs, CFDictionaryRef log, CFAbsoluteTime sct_at)
+{
+ CFDateRef validated_time = CFDictionaryGetValue(validatingLogs, log);
+
+ if(validated_time==NULL || (sct_at < CFDateGetAbsoluteTime(validated_time))) {
+ CFDateRef sct_time = CFDateCreate(kCFAllocatorDefault, sct_at);
+ CFDictionarySetValue(validatingLogs, log, sct_time);
+ CFReleaseSafe(sct_time);
+ }
+}
+
+static CFArrayRef copy_ocsp_scts(SecPVCRef pvc)
+{
+ CFMutableArrayRef SCTs = NULL;
+ SecCertificateRef leafCert = NULL;
+ SecCertificateRef issuer = NULL;
+ CFArrayRef ocspResponsesData = NULL;
+ SecOCSPRequestRef ocspRequest = NULL;
+
+ ocspResponsesData = SecPathBuilderCopyOCSPResponses(pvc->builder);
+ require_quiet(ocspResponsesData, out);
+
+ require_quiet(SecPVCGetCertificateCount(pvc)>=2, out); //we need the issuer key for precerts.
+ leafCert = SecPVCGetCertificateAtIndex(pvc, 0);
+ issuer = SecPVCGetCertificateAtIndex(pvc, 1);
+
+ require(leafCert, out);
+ require(issuer, out); // not quiet: Those two would likely indicate an internal error, since we already checked the chain length above.
+ ocspRequest = SecOCSPRequestCreate(leafCert, issuer);
+
+ SCTs = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
+ require(SCTs, out);
+
+ CFArrayForEach(ocspResponsesData, ^(const void *value) {
+ /* TODO: Should the builder already have the appropriate SecOCSPResponseRef ? */
+ SecOCSPResponseRef ocspResponse = SecOCSPResponseCreate(value);
+ if(ocspResponse && SecOCSPGetResponseStatus(ocspResponse)==kSecOCSPSuccess) {
+ SecOCSPSingleResponseRef ocspSingleResponse = SecOCSPResponseCopySingleResponse(ocspResponse, ocspRequest);
+ if(ocspSingleResponse) {
+ CFArrayRef singleResponseSCTs = SecOCSPSingleResponseCopySCTs(ocspSingleResponse);
+ if(singleResponseSCTs) {
+ CFArrayAppendArray(SCTs, singleResponseSCTs, CFRangeMake(0, CFArrayGetCount(singleResponseSCTs)));
+ CFRelease(singleResponseSCTs);
+ }
+ SecOCSPSingleResponseDestroy(ocspSingleResponse);
+ }
+ }
+ if(ocspResponse) SecOCSPResponseFinalize(ocspResponse);
+ });
+
+ if(CFArrayGetCount(SCTs)==0) {
+ CFReleaseNull(SCTs);
+ }
+
+out:
+ CFReleaseSafe(ocspResponsesData);
+ if(ocspRequest)
+ SecOCSPRequestFinalize(ocspRequest);
+
+ return SCTs;
+}
+
+static bool find_validating_logs(SecPVCRef pvc, CFDictionaryRef trustedLogs,
+ CFDictionaryRef CF_RETURNS_RETAINED * _Nonnull outCurrentLogsValidatingScts,
+ CFDictionaryRef CF_RETURNS_RETAINED * _Nonnull outLogsValidatingEmbeddedScts,
+ bool * _Nonnull out_at_least_one_currently_valid_external,
+ bool * _Nonnull out_at_least_one_currently_valid_embedded) {
+ SecCertificateRef leafCert = SecPVCGetCertificateAtIndex(pvc, 0);
+ CFArrayRef embeddedScts = SecCertificateCopySignedCertificateTimestamps(leafCert);
+ CFArrayRef builderScts = SecPathBuilderCopySignedCertificateTimestamps(pvc->builder);
+ CFArrayRef ocspScts = copy_ocsp_scts(pvc);
+ CFDataRef precertEntry = copy_precert_entry_from_chain(pvc);
+ CFDataRef x509Entry = copy_x509_entry_from_chain(pvc);
+ __block CFAbsoluteTime certExpiry = SecCertificateNotValidAfter(leafCert);
+ bool result = NO;
+
+ // This eventually contain list of logs who validated the SCT.
+ CFMutableDictionaryRef currentLogsValidatingScts = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
+ CFMutableDictionaryRef logsValidatingEmbeddedScts = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
+
+ uint64_t vt = TimestampFromCFAbsoluteTime(SecPVCGetVerifyTime(pvc));
+
+ __block bool at_least_one_currently_valid_external = 0;
+ __block bool at_least_one_currently_valid_embedded = 0;
+
+ require(logsValidatingEmbeddedScts, out);
+ require(currentLogsValidatingScts, out);
+
+ /* Skip if there are no SCTs. */
+ bool no_scts = (embeddedScts && CFArrayGetCount(embeddedScts) > 0) ||
+ (builderScts && CFArrayGetCount(builderScts) > 0) ||
+ (ocspScts && CFArrayGetCount(ocspScts) > 0);
+ require_quiet(no_scts, out);
+
+ if(trustedLogs && CFDictionaryGetCount(trustedLogs) > 0) { // Don't bother trying to validate SCTs if we don't have any trusted logs.
+ if(embeddedScts && precertEntry) { // Don't bother if we could not get the precert.
+ CFArrayForEach(embeddedScts, ^(const void *value){
+ CFAbsoluteTime sct_at;
+ CFDictionaryRef log = getSCTValidatingLog(value, 1, precertEntry, vt, certExpiry, trustedLogs, &sct_at);
+ if(log) {
+ addValidatingLog(logsValidatingEmbeddedScts, log, sct_at);
+ if(!CFDictionaryContainsKey(log, kSecCTRetirementDateKey)) {
+ addValidatingLog(currentLogsValidatingScts, log, sct_at);
+ at_least_one_currently_valid_embedded = true;
+ }
+ }
+ });
+ }
+
+ if(builderScts && x509Entry) { // Don't bother if we could not get the cert.
+ CFArrayForEach(builderScts, ^(const void *value){
+ CFAbsoluteTime sct_at;
+ CFDictionaryRef log = getSCTValidatingLog(value, 0, x509Entry, vt, certExpiry, trustedLogs, &sct_at);
+ if(log) {
+ addValidatingLog(currentLogsValidatingScts, log, sct_at);
+ at_least_one_currently_valid_external = true;
+ }
+ });
+ }
+
+ if(ocspScts && x509Entry) {
+ CFArrayForEach(ocspScts, ^(const void *value){
+ CFAbsoluteTime sct_at;
+ CFDictionaryRef log = getSCTValidatingLog(value, 0, x509Entry, vt, certExpiry, trustedLogs, &sct_at);
+ if(log) {
+ addValidatingLog(currentLogsValidatingScts, log, sct_at);
+ at_least_one_currently_valid_external = true;
+ }
+ });
+ }
+ }
+
+ if (CFDictionaryGetCount(currentLogsValidatingScts) > 0) {
+ result = true;
+ *outCurrentLogsValidatingScts = CFRetainSafe(currentLogsValidatingScts);
+ *outLogsValidatingEmbeddedScts = CFRetainSafe(logsValidatingEmbeddedScts);
+ *out_at_least_one_currently_valid_embedded = at_least_one_currently_valid_embedded;
+ *out_at_least_one_currently_valid_external = at_least_one_currently_valid_external;
+ }
+
+out:
+ CFReleaseSafe(logsValidatingEmbeddedScts);
+ CFReleaseSafe(currentLogsValidatingScts);
+ CFReleaseSafe(builderScts);
+ CFReleaseSafe(embeddedScts);
+ CFReleaseSafe(ocspScts);
+ CFReleaseSafe(precertEntry);
+ CFReleaseSafe(x509Entry);
+ return result;
+}
+
+static bool verify_tls_ct_policy(SecPVCRef pvc, CFDictionaryRef currentLogsValidatingScts, CFDictionaryRef logsValidatingEmbeddedScts,
+ bool at_least_one_currently_valid_external, bool at_least_one_currently_valid_embedded,
+ CFIndex * _Nonnull outTrustedSCTCount)
+{
+ if (!logsValidatingEmbeddedScts || !currentLogsValidatingScts) {
+ return false;
+ }
+
+ __block CFAbsoluteTime issuanceTime = SecPVCGetVerifyTime(pvc);
+ SecCertificateRef leafCert = SecPVCGetCertificateAtIndex(pvc, 0);
+ __block CFIndex trustedSCTCount = CFDictionaryGetCount(currentLogsValidatingScts);
+
+ /* We now have 2 sets of logs that validated those SCTS, count them and make a final decision.
+
+ Current Policy:
+ is_ct = (A1 AND A2) OR (B1 AND B2).
+
+ A1: embedded SCTs from 2+ to 5+ logs valid at issuance time
+ A2: At least one embedded SCT from a currently valid log.
+
+ B1: SCTs from 2 currently valid logs (from any source)
+ B2: At least 1 external SCT from a currently valid log.
+
+ */
+
+ bool hasValidExternalSCT = (at_least_one_currently_valid_external && CFDictionaryGetCount(currentLogsValidatingScts)>=2);
+ bool hasValidEmbeddedSCT = (at_least_one_currently_valid_embedded);
+ SecCertificatePathVCRef path = SecPathBuilderGetPath(pvc->builder);
+ bool result = false;
+
+ if (hasValidEmbeddedSCT) {
+ /* Calculate issuance time based on timestamp of SCTs from current logs */
+ CFDictionaryForEach(currentLogsValidatingScts, ^(const void *key, const void *value) {
+ CFDictionaryRef log = key;
+ if(!CFDictionaryContainsKey(log, kSecCTRetirementDateKey)) {
+ // Log is still qualified
+ CFDateRef ts = (CFDateRef) value;
+ CFAbsoluteTime timestamp = CFDateGetAbsoluteTime(ts);
+ if(timestamp < issuanceTime) {
+ issuanceTime = timestamp;
+ }
+ }
+ });
+ SecCertificatePathVCSetIssuanceTime(path, issuanceTime);
+ }
+ if (hasValidExternalSCT) {
+ /* Note: since external SCT validates this cert, we do not need to
+ override issuance time here. If the cert also has a valid embedded
+ SCT, issuanceTime will be calculated and set in the block above. */
+ result = true;
+ } else if (hasValidEmbeddedSCT) {
+ __block int lifetime; // in Months
+ __block unsigned once_or_current_qualified_embedded = 0;
+
+ /* Count Logs */
+ __block bool failed_once_check = false;
+ CFDictionaryForEach(logsValidatingEmbeddedScts, ^(const void *key, const void *value) {
+ CFDictionaryRef log = key;
+ CFDateRef ts = value;
+ CFDateRef expiry = CFDictionaryGetValue(log, kSecCTRetirementDateKey);
+ if (expiry == NULL) { // Currently qualified OR
+ once_or_current_qualified_embedded++;
+ } else if (CFDateCompare(ts, expiry, NULL) == kCFCompareLessThan && // Once qualified. That is, qualified at the time of SCT AND
+ issuanceTime < CFDateGetAbsoluteTime(expiry)) { // at the time of issuance.)
+ once_or_current_qualified_embedded++;
+ trustedSCTCount++;
+ } else {
+ failed_once_check = true;
+ }
+ });
+
+ SecCFCalendarDoWithZuluCalendar(^(CFCalendarRef zuluCalendar) {
+ int _lifetime;
+ CFCalendarGetComponentDifference(zuluCalendar,
+ SecCertificateNotValidBefore(leafCert),
+ SecCertificateNotValidAfter(leafCert),
+ 0, "M", &_lifetime);
+ lifetime = _lifetime;
+ });
+
+ unsigned requiredEmbeddedSctsCount;
+
+ if (lifetime < 15) {
+ requiredEmbeddedSctsCount = 2;
+ } else if (lifetime <= 27) {
+ requiredEmbeddedSctsCount = 3;
+ } else if (lifetime <= 39) {
+ requiredEmbeddedSctsCount = 4;
+ } else {
+ requiredEmbeddedSctsCount = 5;
+ }
+
+ if(once_or_current_qualified_embedded >= requiredEmbeddedSctsCount){
+ result = true;
+ }
+ }
+
+ *outTrustedSCTCount = trustedSCTCount;
+ return result;
+}
+
+static void report_ct_analytics(SecPVCRef pvc, CFDictionaryRef currentLogsValidatingScts, CFIndex trustedSCTCount)
+{
+ /* Record analytics data for CT */
+ TrustAnalyticsBuilder *analytics = SecPathBuilderGetAnalyticsData(pvc->builder);
+ if (!analytics) {
+ return;
+ }
+
+ SecCertificateRef leafCert = SecPVCGetCertificateAtIndex(pvc, 0);
+ CFArrayRef embeddedScts = SecCertificateCopySignedCertificateTimestamps(leafCert);
+ CFArrayRef builderScts = SecPathBuilderCopySignedCertificateTimestamps(pvc->builder);
+ CFArrayRef ocspScts = copy_ocsp_scts(pvc);
+
+ uint32_t sctCount = 0;
+ /* Count the total number of SCTs we found and report where we got them */
+ if (embeddedScts && CFArrayGetCount(embeddedScts) > 0) {
+ analytics->sct_sources |= TA_SCTEmbedded;
+ sctCount += CFArrayGetCount(embeddedScts);
+ }
+ if (builderScts && CFArrayGetCount(builderScts) > 0) {
+ analytics->sct_sources |= TA_SCT_TLS;
+ sctCount += CFArrayGetCount(builderScts);
+ }
+ if (ocspScts && CFArrayGetCount(ocspScts) > 0) {
+ analytics->sct_sources |= TA_SCT_OCSP;
+ sctCount += CFArrayGetCount(ocspScts);
+ }
+ /* Report how many of those SCTs were once or currently qualified */
+ analytics->number_trusted_scts = (uint32_t)trustedSCTCount;
+ /* Report how many SCTs we got */
+ analytics->number_scts = sctCount;
+ /* Only one current SCT -- close to failure */
+ if (CFDictionaryGetCount(currentLogsValidatingScts) == 1) {
+ analytics->ct_one_current = true;
+ }
+
+ CFReleaseNull(embeddedScts);
+ CFReleaseNull(builderScts);
+ CFReleaseNull(ocspScts);
+}
+
+void SecPolicyCheckCT(SecPVCRef pvc)
+{
+ CFDictionaryRef trustedLogs = SecPathBuilderCopyTrustedLogs(pvc->builder);
+ if (!trustedLogs) {
+ SecOTAPKIRef otapkiref = SecOTAPKICopyCurrentOTAPKIRef();
+ trustedLogs = SecOTAPKICopyTrustedCTLogs(otapkiref);
+ CFReleaseSafe(otapkiref);
+ }
+
+ CFDictionaryRef currentLogsValidatingScts = NULL;
+ CFDictionaryRef logsValidatingEmbeddedScts = NULL;
+ bool at_least_one_currently_valid_external = false;
+ bool at_least_one_currently_valid_embedded = false;
+ CFIndex trustedSCTCount = 0;
+ SecCertificatePathVCRef path = SecPathBuilderGetPath(pvc->builder);
+
+ SecCertificatePathVCSetIsCT(path, false);
+ if (find_validating_logs(pvc, trustedLogs, ¤tLogsValidatingScts, &logsValidatingEmbeddedScts, &at_least_one_currently_valid_external, &at_least_one_currently_valid_embedded)) {
+ if (verify_tls_ct_policy(pvc, currentLogsValidatingScts, logsValidatingEmbeddedScts, at_least_one_currently_valid_external, at_least_one_currently_valid_embedded, &trustedSCTCount)) {
+ SecCertificatePathVCSetIsCT(path, true);
+ }
+ report_ct_analytics(pvc, currentLogsValidatingScts, trustedSCTCount);
+ }
+
+ CFReleaseNull(currentLogsValidatingScts);
+ CFReleaseNull(logsValidatingEmbeddedScts);
+ CFReleaseNull(trustedLogs);
+}
+
+bool SecPolicyCheckNonTlsCT(SecPVCRef pvc)
+{
+ CFDictionaryRef trustedLogs = SecPathBuilderCopyTrustedLogs(pvc->builder);
+ if (!trustedLogs) {
+ SecOTAPKIRef otapkiref = SecOTAPKICopyCurrentOTAPKIRef();
+ trustedLogs = SecOTAPKICopyNonTlsTrustedCTLogs(otapkiref);
+ CFReleaseSafe(otapkiref);
+ }
+
+ CFDictionaryRef currentLogsValidatingScts = NULL;
+ CFDictionaryRef logsValidatingEmbeddedScts = NULL;
+ bool at_least_one_currently_valid_external = false;
+ bool at_least_one_currently_valid_embedded = false;
+ CFIndex trustedSCTCount = 0;
+ SecCertificatePathVCRef path = SecPathBuilderGetPath(pvc->builder);
+ bool result = false;
+
+ SecCertificatePathVCSetIsCT(path, false);
+ if (find_validating_logs(pvc, trustedLogs, ¤tLogsValidatingScts, &logsValidatingEmbeddedScts, &at_least_one_currently_valid_external, &at_least_one_currently_valid_embedded)) {
+ if (verify_tls_ct_policy(pvc, currentLogsValidatingScts, logsValidatingEmbeddedScts, at_least_one_currently_valid_external, at_least_one_currently_valid_embedded, &trustedSCTCount)) {
+ result = true;
+ }
+ report_ct_analytics(pvc, currentLogsValidatingScts, trustedSCTCount);
+ }
+
+ CFReleaseNull(currentLogsValidatingScts);
+ CFReleaseNull(logsValidatingEmbeddedScts);
+ CFReleaseNull(trustedLogs);
+ return result;
+}
CFArrayRef SecOTAPKICopyAppleCertificateAuthorities(SecOTAPKIRef otapkiRef);
extern const CFStringRef kOTAPKIKillSwitchCT;
+extern const CFStringRef kOTAPKIKillSwitchNonTLSCT;
bool SecOTAPKIKillSwitchEnabled(SecOTAPKIRef otapkiRef, CFStringRef switchKey);
// SPI to return the array of currently trusted Escrow certificates
CF_EXPORT
CFArrayRef SecOTAPKICopyCurrentEscrowCertificates(uint32_t escrowRootType, CFErrorRef* error);
-// SPI to return the array of currently trusted CT logs
+// SPI to return the array of currently (TLS) trusted CT logs
CF_EXPORT
CFDictionaryRef SecOTAPKICopyCurrentTrustedCTLogs(CFErrorRef* error);
+// SPI to return the array of currently non-TLS trusted CT logs
+CF_EXPORT
+CFDictionaryRef SecOTAPKICopyNonTlsTrustedCTLogs(SecOTAPKIRef otapkiRef);
+
// SPI to return dictionary of CT log matching specified key id */
CF_EXPORT
CFDictionaryRef SecOTAPKICopyCTLogForKeyID(CFDataRef keyID, CFErrorRef* error);
NSString *kOTATrustLastCheckInKey = @"MobileAssetLastCheckIn";
NSString *kOTATrustContextFilename = @"OTAPKIContext.plist";
NSString *kOTATrustTrustedCTLogsFilename = @"TrustedCTLogs.plist";
+NSString *kOTATrustTrustedCTLogsNonTLSFilename = @"TrustedCTLogs_nonTLS.plist";
NSString *kOTATrustAnalyticsSamplingRatesFilename = @"AnalyticsSamplingRates.plist";
NSString *kOTATrustAppleCertifcateAuthoritiesFilename = @"AppleCertificateAuthorities.plist";
NSString *kOTASecExperimentConfigFilename = @"SecExperimentAssets.plist";
+/* A device will honor a kill switch until it gets a new asset xml that sets the kill switch value to 0/false
+ * OR the asset is (completely) reset to shipping version. Such resets can happen if asset files cannot be
+ * read properly or if the OS is updated and contains a newer asset version or pinning DB version. */
const CFStringRef kOTAPKIKillSwitchCT = CFSTR("CTKillSwitch");
+const CFStringRef kOTAPKIKillSwitchNonTLSCT = CFSTR("CTKillSwitch_nonTLS");
#if !TARGET_OS_BRIDGE
NSString *OTATrustMobileAssetType = @"com.apple.MobileAsset.PKITrustSupplementals";
if (SecOTAPKIIsSystemTrustd()) {
/* Delete the asset files, but keep the check-in time and version */
DeleteFileWithName(kOTATrustTrustedCTLogsFilename);
+ DeleteFileWithName(kOTATrustTrustedCTLogsNonTLSFilename);
DeleteFileWithName(kOTATrustAnalyticsSamplingRatesFilename);
DeleteFileWithName(kOTATrustAppleCertifcateAuthoritiesFilename);
}
return NO;
}
+static void DisableKillSwitches() {
+ UpdateOTAContextOnDisk((__bridge NSString*)kOTAPKIKillSwitchCT, @0, nil);
+ UpdateOTAContextOnDisk((__bridge NSString*)kOTAPKIKillSwitchNonTLSCT, @0, nil);
+}
+
static void GetKillSwitchAttributes(NSDictionary *attributes) {
bool killSwitchEnabled = false;
killSwitchEnabled = true;
}
+ // Non-TLS CT Kill Switch
+ ctKillSwitch = [attributes objectForKey:(__bridge NSString*)kOTAPKIKillSwitchNonTLSCT];
+ if (isNSNumber(ctKillSwitch)) {
+ NSError *error = nil;
+ UpdateOTAContextOnDisk((__bridge NSString*)kOTAPKIKillSwitchNonTLSCT, ctKillSwitch, &error);
+ UpdateKillSwitch((__bridge NSString*)kOTAPKIKillSwitchNonTLSCT, [ctKillSwitch boolValue]);
+ secnotice("OTATrust", "got non-TLS CT kill switch = %d", [ctKillSwitch boolValue]);
+ killSwitchEnabled = true;
+ }
+
/* Other kill switches TBD.
* When adding one, make sure to add to the Analytics Samplers since these kill switches
* are installed before the full asset is downloaded and installed. (A device can have the
int out_token3 = 0;
notify_register_dispatch(kOTATrustKillSwitchNotification, &out_token3, queue, ^(int __unused token) {
UpdateKillSwitch((__bridge NSString*)kOTAPKIKillSwitchCT, InitializeKillSwitch((__bridge NSString*)kOTAPKIKillSwitchCT));
+ UpdateKillSwitch((__bridge NSString*)kOTAPKIKillSwitchNonTLSCT, InitializeKillSwitch((__bridge NSString*)kOTAPKIKillSwitchNonTLSCT));
});
}
}
}
}
-static CF_RETURNS_RETAINED CFDictionaryRef InitializeTrustedCTLogs() {
+static CF_RETURNS_RETAINED CFDictionaryRef InitializeTrustedCTLogs(NSString *filename) {
@autoreleasepool {
NSArray *trustedCTLogs = nil;
NSError *error = nil;
#if !TARGET_OS_BRIDGE
if (ShouldInitializeWithAsset()) {
- trustedCTLogs = [NSArray arrayWithContentsOfURL:GetAssetFileURL(kOTATrustTrustedCTLogsFilename) error:&error];
+ trustedCTLogs = [NSArray arrayWithContentsOfURL:GetAssetFileURL(filename) error:&error];
if (!isNSArray(trustedCTLogs)) {
secerror("OTATrust: failed to read CT list from asset data: %@", error);
LogRemotely(OTATrustLogLevelError, &error);
}
#endif
if (!isNSArray(trustedCTLogs)) {
- trustedCTLogs = [NSArray arrayWithContentsOfURL:SecSystemTrustStoreCopyResourceNSURL(kOTATrustTrustedCTLogsFilename)];
+ trustedCTLogs = [NSArray arrayWithContentsOfURL:SecSystemTrustStoreCopyResourceNSURL(filename)];
}
if (isNSArray(trustedCTLogs)) {
return CFBridgingRetain(ConvertTrustedCTLogsArrayToDictionary(trustedCTLogs));
CFSetRef _grayListSet;
CFDictionaryRef _allowList;
CFDictionaryRef _trustedCTLogs;
+ CFDictionaryRef _nonTlsTrustedCTLogs;
CFURLRef _pinningList;
CFArrayRef _escrowCertificates;
CFArrayRef _escrowPCSCertificates;
CFDictionaryRef _secExperimentConfig;
uint64_t _secExperimentAssetVersion;
bool _ctKillSwitch;
+ bool _nonTlsCtKillSwitch;
};
CFGiblisFor(SecOTAPKI)
CFReleaseNull(otapkiref->_anchorLookupTable);
CFReleaseNull(otapkiref->_trustedCTLogs);
+ CFReleaseNull(otapkiref->_nonTlsTrustedCTLogs);
CFReleaseNull(otapkiref->_pinningList);
CFReleaseNull(otapkiref->_eventSamplingRates);
CFReleaseNull(otapkiref->_appleCAs);
*error = CFRetainSafe((__bridge CFErrorRef)nserror);
}
DeleteOldAssetData();
+ DisableKillSwitches();
}
#endif
return version;
// (now loaded lazily in SecOTAPKICopyAllowList)
// Get the trusted Certificate Transparency Logs
- otapkiref->_trustedCTLogs = InitializeTrustedCTLogs();
+ otapkiref->_trustedCTLogs = InitializeTrustedCTLogs(kOTATrustTrustedCTLogsFilename);
+ otapkiref->_nonTlsTrustedCTLogs = InitializeTrustedCTLogs(kOTATrustTrustedCTLogsNonTLSFilename);
// Get the pinning list
otapkiref->_pinningList = InitializePinningList();
/* Initialize our update handling */
InitializeOTATrustAsset(kOTABackgroundQueue);
otapkiref->_ctKillSwitch = InitializeKillSwitch((__bridge NSString*)kOTAPKIKillSwitchCT);
+ otapkiref->_nonTlsCtKillSwitch = InitializeKillSwitch((__bridge NSString*)kOTAPKIKillSwitchNonTLSCT);
InitializeOTASecExperimentAsset(kOTABackgroundQueue);
#else // TARGET_OS_BRIDGE
otapkiref->_ctKillSwitch = true; // bridgeOS never enforces CT
+ otapkiref->_nonTlsCtKillSwitch = true;
#endif // TARGET_OS_BRIDGE
return otapkiref;
dispatch_sync(kOTAQueue, ^{
if ([key isEqualToString:(__bridge NSString*)kOTAPKIKillSwitchCT]) {
kCurrentOTAPKIRef->_ctKillSwitch = value;
+ } else if ([key isEqualToString:(__bridge NSString*)kOTAPKIKillSwitchNonTLSCT]) {
+ kCurrentOTAPKIRef->_nonTlsCtKillSwitch = value;
}
});
}
return NO;
}
__block NSArray *newTrustedCTLogs = NULL;
+ __block NSArray *newNonTlsTrustedCTLogs = NULL;
__block uint64_t version = [asset_version unsignedLongLongValue];
__block NSDictionary *newAnalyticsSamplingRates = NULL;
__block NSArray *newAppleCAs = NULL;
return NO;
}
+ NSURL *nonTLSTrustedCTLogsFileLoc = [NSURL URLWithString:kOTATrustTrustedCTLogsNonTLSFilename
+ relativeToURL:localURL];
+ newNonTlsTrustedCTLogs = [NSArray arrayWithContentsOfURL:nonTLSTrustedCTLogsFileLoc error:error];
+ if (!newNonTlsTrustedCTLogs) {
+ secerror("OTATrust: unable to create TrustedCTLogs_nonTLS from asset file: %@", error ? *error: nil);
+ LogRemotely(OTATrustLogLevelError, error);
+ return NO;
+ }
+
NSURL *AnalyticsSamplingRatesFileLoc = [NSURL URLWithString:kOTATrustAnalyticsSamplingRatesFilename
relativeToURL:localURL];
newAnalyticsSamplingRates = [NSDictionary dictionaryWithContentsOfURL:AnalyticsSamplingRatesFileLoc error:error];
dispatch_sync(kOTAQueue, ^{
secnotice("OTATrust", "updating asset version from %llu to %llu", kCurrentOTAPKIRef->_assetVersion, version);
CFRetainAssign(kCurrentOTAPKIRef->_trustedCTLogs, (__bridge CFDictionaryRef)ConvertTrustedCTLogsArrayToDictionary(newTrustedCTLogs));
+ CFRetainAssign(kCurrentOTAPKIRef->_nonTlsTrustedCTLogs, (__bridge CFDictionaryRef)ConvertTrustedCTLogsArrayToDictionary(newNonTlsTrustedCTLogs));
CFRetainAssign(kCurrentOTAPKIRef->_eventSamplingRates, (__bridge CFDictionaryRef)newAnalyticsSamplingRates);
CFRetainAssign(kCurrentOTAPKIRef->_appleCAs, (__bridge CFArrayRef)newAppleCAs);
kCurrentOTAPKIRef->_assetVersion = version;
});
- /* Write the data to disk (so that we don't have to re-download the asset on re-launch) */
- DeleteAssetFromDisk();
+ /* Reset the current files, version, and checkin so that in the case of write failures, we'll re-try
+ * to update the data. We don't call DeleteAssetFromDisk() here to preserve any kill switches. */
+ DeleteOldAssetData();
+ UpdateOTAContext(@(0), nil);
+ UpdateOTAContextOnDisk(kOTATrustLastCheckInKey, [NSDate dateWithTimeIntervalSince1970:0], nil);
+
+ /* Write the data to disk (so that we don't have to re-download the asset on re-launch). */
if (CopyFileToDisk(kOTATrustTrustedCTLogsFilename, TrustedCTLogsFileLoc, error) &&
+ CopyFileToDisk(kOTATrustTrustedCTLogsNonTLSFilename, nonTLSTrustedCTLogsFileLoc, error) &&
CopyFileToDisk(kOTATrustAnalyticsSamplingRatesFilename, AnalyticsSamplingRatesFileLoc, error) &&
CopyFileToDisk(kOTATrustAppleCertifcateAuthoritiesFilename, AppleCAsFileLoc, error) &&
UpdateOTAContext(asset_version, error)) { // Set version and check-in time last (after success)
return result;
}
+CFDictionaryRef SecOTAPKICopyNonTlsTrustedCTLogs(SecOTAPKIRef otapkiRef) {
+ CFDictionaryRef result = NULL;
+ if (NULL == otapkiRef) {
+ return result;
+ }
+
+#if !TARGET_OS_BRIDGE
+ /* Trigger periodic background MA checks in system trustd
+ * We also check on trustd launch and listen for notifications. */
+ TriggerPeriodicOTATrustAssetChecks(kOTABackgroundQueue);
+#endif
+
+ result = otapkiRef->_nonTlsTrustedCTLogs;
+ CFRetainSafe(result);
+ return result;
+}
+
CFURLRef SecOTAPKICopyPinningList(SecOTAPKIRef otapkiRef) {
if (NULL == otapkiRef) {
return NULL;
}
if (CFEqualSafe(key, kOTAPKIKillSwitchCT)) {
return otapkiRef->_ctKillSwitch;
+ } else if (CFEqualSafe(key, kOTAPKIKillSwitchNonTLSCT)) {
+ return otapkiRef->_nonTlsCtKillSwitch;
}
return false;
}
return NULL;
}
- CFDictionaryRef trustedLogs = SecOTAPKICopyTrustedCTLogs(otapkiref);
+ /* Get the log lists */
+ CFDictionaryRef trustedTlsLogs = SecOTAPKICopyTrustedCTLogs(otapkiref);
+ CFDictionaryRef trustedNonTlsLogs = SecOTAPKICopyNonTlsTrustedCTLogs(otapkiref);
CFReleaseNull(otapkiref);
- if (!trustedLogs) {
+ if (!trustedTlsLogs || !trustedNonTlsLogs) {
+ CFReleaseNull(trustedTlsLogs);
+ CFReleaseNull(trustedNonTlsLogs);
return NULL;
}
- CFDictionaryRef logDict = CFDictionaryGetValue(trustedLogs, keyID);
+
+ /* Find the log */
+ CFDictionaryRef logDict = CFDictionaryGetValue(trustedTlsLogs, keyID);
+ if (!logDict) {
+ logDict = CFDictionaryGetValue(trustedNonTlsLogs, keyID);
+ }
CFRetainSafe(logDict);
- CFReleaseSafe(trustedLogs);
+ CFReleaseNull(trustedTlsLogs);
+ CFReleaseNull(trustedNonTlsLogs);
return logDict;
}
* SecPolicyServer.c - Engine for evaluating certificate paths against trust policies.
*/
-#include "trust/trustd/SecPolicyServer.h"
-#include <Security/SecPolicyInternal.h>
-#include <Security/SecPolicyPriv.h>
-#include <Security/SecTask.h>
-#include "trust/trustd/policytree.h"
-#include "trust/trustd/nameconstraints.h"
-#include <CoreFoundation/CFTimeZone.h>
+#include <AssertMacros.h>
#include <wctype.h>
-#include <libDER/oids.h>
+#include <asl.h>
+
+#include <CommonCrypto/CommonDigest.h>
+#include <CoreFoundation/CFTimeZone.h>
#include <CoreFoundation/CFNumber.h>
-#include <Security/SecCertificateInternal.h>
-#include <AssertMacros.h>
+#include <sys/codesign.h>
+#include <libDER/DER_CertCrl.h>
+#include <libDER/DER_Encode.h>
+#include <libDER/asn1Types.h>
+#include <libDER/oids.h>
+#include <SystemConfiguration/SCDynamicStoreCopySpecific.h>
+
+#include <utilities/array_size.h>
+#include <utilities/SecCFWrappers.h>
+#include <utilities/SecAppleAnchorPriv.h>
#include <utilities/debugging.h>
#include <utilities/SecInternalReleasePriv.h>
#include <security_asn1/SecAsn1Coder.h>
#include <security_asn1/ocspTemplates.h>
#include <security_asn1/oidsalg.h>
#include <security_asn1/oidsocsp.h>
-#include <CommonCrypto/CommonDigest.h>
+#include <Security/SecPolicyInternal.h>
+#include <Security/SecPolicyPriv.h>
+#include <Security/SecTask.h>
+#include <Security/SecCertificateInternal.h>
#include <Security/SecFramework.h>
#include <Security/SecPolicyInternal.h>
#include <Security/SecTrustPriv.h>
#include <Security/SecTrustSettingsPriv.h>
#include <Security/SecInternal.h>
#include <Security/SecKeyPriv.h>
-#include <Security/SecTask.h>
-#include <SystemConfiguration/SCDynamicStoreCopySpecific.h>
-#include <asl.h>
+
+#include "trust/trustd/SecPolicyServer.h"
+#include "trust/trustd/policytree.h"
+#include "trust/trustd/nameconstraints.h"
#include "trust/trustd/SecTrustServer.h"
#include "trust/trustd/SecTrustLoggingServer.h"
#include "trust/trustd/SecRevocationServer.h"
#include "trust/trustd/SecCertificateSource.h"
#include "trust/trustd/SecOCSPResponse.h"
#include "trust/trustd/SecTrustStoreServer.h"
-#include <utilities/array_size.h>
-#include <utilities/SecCFWrappers.h>
-#include <utilities/SecAppleAnchorPriv.h>
-#include "OTATrustUtilities.h"
-#include "personalization.h"
-#include <sys/codesign.h>
+#include "trust/trustd/OTATrustUtilities.h"
+#include "trust/trustd/personalization.h"
+#include "trust/trustd/CertificateTransparency.h"
#if !TARGET_OS_IPHONE
#include <Security/SecTaskPriv.h>
****************** SecPolicy object ********************
********************************************************/
-static SecCertificateRef SecPVCGetCertificateAtIndex(SecPVCRef pvc, CFIndex ix);
-static CFIndex SecPVCGetCertificateCount(SecPVCRef pvc);
-static CFAbsoluteTime SecPVCGetVerifyTime(SecPVCRef pvc);
static SecTrustSettingsResult SecPVCGetTrustSettingsResult(SecPVCRef pvc, SecCertificateRef certificate, CFArrayRef constraints);
static CFMutableDictionaryRef gSecPolicyLeafCallbacks = NULL;
*/
}
-
-/*
- * MARK: Certificate Transparency support
- */
-const CFStringRef kSecCTRetirementDateKey = CFSTR("expiry"); // For backwards compatibility, retirement date is represented with the "expiry" key
-const CFStringRef kSecCTReadOnlyDateKey = CFSTR("frozen"); // For backwards compatibility, read-only date is represented with the "frozen" key
-const CFStringRef kSecCTShardStartDateKey = CFSTR("start_inclusive");
-const CFStringRef kSecCTShardEndDateKey = CFSTR("end_exclusive");
-const CFStringRef kSecCTPublicKeyKey = CFSTR("key");
-
-enum {
- kSecCTEntryTypeCert = 0,
- kSecCTEntryTypePreCert = 1,
-};
-
-/***
-
-struct {
- Version sct_version; // 1 byte
- LogID id; // 32 bytes
- uint64 timestamp; // 8 bytes
- CtExtensions extensions; // 2 bytes len field, + n bytes data
- digitally-signed struct { // 1 byte hash alg, 1 byte sig alg, n bytes signature
- Version sct_version;
- SignatureType signature_type = certificate_timestamp;
- uint64 timestamp;
- LogEntryType entry_type;
- select(entry_type) {
- case x509_entry: ASN.1Cert;
- case precert_entry: PreCert;
- } signed_entry;
- CtExtensions extensions;
- };
-} SignedCertificateTimestamp;
-
-***/
-
-#include <Security/SecureTransportPriv.h>
-
-static const
-SecAsn1Oid *oidForSigAlg(SSL_HashAlgorithm hash, SSL_SignatureAlgorithm alg)
-{
- switch(alg) {
- case SSL_SignatureAlgorithmRSA:
- switch (hash) {
- case SSL_HashAlgorithmSHA1:
- return &CSSMOID_SHA1WithRSA;
- case SSL_HashAlgorithmSHA256:
- return &CSSMOID_SHA256WithRSA;
- case SSL_HashAlgorithmSHA384:
- return &CSSMOID_SHA384WithRSA;
- default:
- break;
- }
- case SSL_SignatureAlgorithmECDSA:
- switch (hash) {
- case SSL_HashAlgorithmSHA1:
- return &CSSMOID_ECDSA_WithSHA1;
- case SSL_HashAlgorithmSHA256:
- return &CSSMOID_ECDSA_WithSHA256;
- case SSL_HashAlgorithmSHA384:
- return &CSSMOID_ECDSA_WithSHA384;
- default:
- break;
- }
- default:
- break;
- }
-
- return NULL;
-}
-
-
-static size_t SSLDecodeUint16(const uint8_t *p)
-{
- return (p[0]<<8 | p[1]);
-}
-
-static uint8_t *SSLEncodeUint16(uint8_t *p, size_t len)
-{
- p[0] = (len >> 8)&0xff;
- p[1] = (len & 0xff);
- return p+2;
-}
-
-static uint8_t *SSLEncodeUint24(uint8_t *p, size_t len)
-{
- p[0] = (len >> 16)&0xff;
- p[1] = (len >> 8)&0xff;
- p[2] = (len & 0xff);
- return p+3;
-}
-
-
-static
-uint64_t SSLDecodeUint64(const uint8_t *p)
-{
- uint64_t u = 0;
- for(int i=0; i<8; i++) {
- u=(u<<8)|p[0];
- p++;
- }
- return u;
-}
-
-#include <libDER/DER_CertCrl.h>
-#include <libDER/DER_Encode.h>
-#include <libDER/asn1Types.h>
-
-
-static CFDataRef copy_x509_entry_from_chain(SecPVCRef pvc)
-{
- SecCertificateRef leafCert = SecPVCGetCertificateAtIndex(pvc, 0);
-
- CFMutableDataRef data = CFDataCreateMutable(kCFAllocatorDefault, 3+SecCertificateGetLength(leafCert));
-
- CFDataSetLength(data, 3+SecCertificateGetLength(leafCert));
-
- uint8_t *q = CFDataGetMutableBytePtr(data);
- q = SSLEncodeUint24(q, SecCertificateGetLength(leafCert));
- memcpy(q, SecCertificateGetBytePtr(leafCert), SecCertificateGetLength(leafCert));
-
- return data;
-}
-
-
-static CFDataRef copy_precert_entry_from_chain(SecPVCRef pvc)
-{
- SecCertificateRef leafCert = NULL;
- SecCertificateRef issuer = NULL;
- CFDataRef issuerKeyHash = NULL;
- CFDataRef tbs_precert = NULL;
- CFMutableDataRef data= NULL;
-
- require_quiet(SecPVCGetCertificateCount(pvc)>=2, out); //we need the issuer key for precerts.
- leafCert = SecPVCGetCertificateAtIndex(pvc, 0);
- issuer = SecPVCGetCertificateAtIndex(pvc, 1);
-
- require(leafCert, out);
- require(issuer, out); // Those two would likely indicate an internal error, since we already checked the chain length above.
- issuerKeyHash = SecCertificateCopySubjectPublicKeyInfoSHA256Digest(issuer);
- tbs_precert = SecCertificateCopyPrecertTBS(leafCert);
-
- require(issuerKeyHash, out);
- require(tbs_precert, out);
- data = CFDataCreateMutable(kCFAllocatorDefault, CFDataGetLength(issuerKeyHash) + 3 + CFDataGetLength(tbs_precert));
- CFDataSetLength(data, CFDataGetLength(issuerKeyHash) + 3 + CFDataGetLength(tbs_precert));
-
- uint8_t *q = CFDataGetMutableBytePtr(data);
- memcpy(q, CFDataGetBytePtr(issuerKeyHash), CFDataGetLength(issuerKeyHash)); q += CFDataGetLength(issuerKeyHash); // issuer key hash
- q = SSLEncodeUint24(q, CFDataGetLength(tbs_precert));
- memcpy(q, CFDataGetBytePtr(tbs_precert), CFDataGetLength(tbs_precert));
-
-out:
- CFReleaseSafe(issuerKeyHash);
- CFReleaseSafe(tbs_precert);
- return data;
-}
-
-static
-CFAbsoluteTime TimestampToCFAbsoluteTime(uint64_t ts)
-{
- return (ts / 1000) - kCFAbsoluteTimeIntervalSince1970;
-}
-
-static
-uint64_t TimestampFromCFAbsoluteTime(CFAbsoluteTime at)
-{
- return (uint64_t)(at + kCFAbsoluteTimeIntervalSince1970) * 1000;
-}
-
-static bool isSCTValidForLogData(CFDictionaryRef logData, int entry_type, CFAbsoluteTime sct_time, CFAbsoluteTime cert_expiry_date) {
- /* only embedded SCTs can be used from retired logs. */
- if(entry_type==kSecCTEntryTypeCert && CFDictionaryContainsKey(logData, kSecCTRetirementDateKey)) {
- return false;
- }
-
- /* SCTs from after the transition to read-only are not valid (and indicate a operator failure) */
- CFDateRef frozen_date = CFDictionaryGetValue(logData, kSecCTReadOnlyDateKey);
- if (frozen_date && (sct_time > CFDateGetAbsoluteTime(frozen_date))) {
- secerror("Frozen CT log issued SCT after freezing (log=%@)\n", logData);
- return false;
- }
-
- /* If the log is temporally sharded, the certificate expiry date must be within the temporal shard window */
- CFDateRef start_inclusive = CFDictionaryGetValue(logData, kSecCTShardStartDateKey);
- CFDateRef end_exclusive = CFDictionaryGetValue(logData, kSecCTShardEndDateKey);
- if (start_inclusive && (cert_expiry_date < CFDateGetAbsoluteTime(start_inclusive))) {
- return false;
- }
- if (end_exclusive && (cert_expiry_date >= CFDateGetAbsoluteTime(end_exclusive))) {
- return false;
- }
-
- return true;
-}
-
-
-/*
- If the 'sct' is valid, add it to the validatingLogs dictionary.
-
- Inputs:
- - validatingLogs: mutable dictionary to which to add the log that validate this SCT.
- - sct: the SCT date
- - entry_type: 0 for x509 cert, 1 for precert.
- - entry: the cert or precert data.
- - vt: verification time timestamp (as used in SCTs: ms since 1970 Epoch)
- - trustedLog: Dictionary contain the Trusted Logs.
-
- The SCT is valid if:
- - It decodes properly.
- - Its timestamp is less than 'verifyTime'.
- - It is signed by a log in 'trustedLogs'.
- - If entry_type = 0, the log must be currently qualified.
- - If entry_type = 1, the log may be expired.
-
- If the SCT is valid, it's added to the validatinLogs dictionary using the log dictionary as the key, and the timestamp as value.
- 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.
-
- */
-static CFDictionaryRef getSCTValidatingLog(CFDataRef sct, int entry_type, CFDataRef entry, uint64_t vt, CFAbsoluteTime cert_expiry_date, CFDictionaryRef trustedLogs, CFAbsoluteTime *sct_at)
-{
- uint8_t version;
- const uint8_t *logID;
- const uint8_t *timestampData;
- uint64_t timestamp;
- size_t extensionsLen;
- const uint8_t *extensionsData;
- uint8_t hashAlg;
- uint8_t sigAlg;
- size_t signatureLen;
- const uint8_t *signatureData;
- SecKeyRef pubKey = NULL;
- uint8_t *signed_data = NULL;
- const SecAsn1Oid *oid = NULL;
- SecAsn1AlgId algId;
- CFDataRef logIDData = NULL;
- CFDictionaryRef result = 0;
-
- const uint8_t *p = CFDataGetBytePtr(sct);
- size_t len = CFDataGetLength(sct);
-
- require(len>=43, out);
-
- version = p[0]; p++; len--;
- logID = p; p+=32; len-=32;
- timestampData = p; p+=8; len-=8;
- extensionsLen = SSLDecodeUint16(p); p+=2; len-=2;
-
- require(len>=extensionsLen, out);
- extensionsData = p; p+=extensionsLen; len-=extensionsLen;
-
- require(len>=4, out);
- hashAlg=p[0]; p++; len--;
- sigAlg=p[0]; p++; len--;
- signatureLen = SSLDecodeUint16(p); p+=2; len-=2;
- require(len==signatureLen, out); /* We do not tolerate any extra data after the signature */
- signatureData = p;
-
- /* verify version: only v1(0) is supported */
- if(version!=0) {
- secerror("SCT version unsupported: %d\n", version);
- goto out;
- }
-
- /* verify timestamp not in the future */
- timestamp = SSLDecodeUint64(timestampData);
- if(timestamp > vt) {
- secerror("SCT is in the future: %llu > %llu\n", timestamp, vt);
- goto out;
- }
-
- uint8_t *q;
-
- /* signed entry */
- size_t signed_data_len = 12 + CFDataGetLength(entry) + 2 + extensionsLen ;
- signed_data = malloc(signed_data_len);
- require(signed_data, out);
- q = signed_data;
- *q++ = version;
- *q++ = 0; // certificate_timestamp
- memcpy(q, timestampData, 8); q+=8;
- q = SSLEncodeUint16(q, entry_type); // logentry type: 0=cert 1=precert
- memcpy(q, CFDataGetBytePtr(entry), CFDataGetLength(entry)); q += CFDataGetLength(entry);
- q = SSLEncodeUint16(q, extensionsLen);
- memcpy(q, extensionsData, extensionsLen);
-
- logIDData = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, logID, 32, kCFAllocatorNull);
-
- CFDictionaryRef logData = CFDictionaryGetValue(trustedLogs, logIDData);
- CFAbsoluteTime sct_time = TimestampToCFAbsoluteTime(timestamp);
- require(logData && isSCTValidForLogData(logData, entry_type, sct_time, cert_expiry_date), out);
-
- CFDataRef logKeyData = CFDictionaryGetValue(logData, kSecCTPublicKeyKey);
- require(logKeyData, out); // This failing would be an internal logic error
- pubKey = SecKeyCreateFromSubjectPublicKeyInfoData(kCFAllocatorDefault, logKeyData);
- require(pubKey, out);
-
- oid = oidForSigAlg(hashAlg, sigAlg);
- require(oid, out);
-
- algId.algorithm = *oid;
- algId.parameters.Data = NULL;
- algId.parameters.Length = 0;
-
- if(SecKeyDigestAndVerify(pubKey, &algId, signed_data, signed_data_len, signatureData, signatureLen)==0) {
- *sct_at = sct_time;
- result = logData;
- } else {
- secerror("SCT signature failed (log=%@)\n", logData);
- }
-
-out:
- CFReleaseSafe(logIDData);
- CFReleaseSafe(pubKey);
- free(signed_data);
- return result;
-}
-
-
-static void addValidatingLog(CFMutableDictionaryRef validatingLogs, CFDictionaryRef log, CFAbsoluteTime sct_at)
-{
- CFDateRef validated_time = CFDictionaryGetValue(validatingLogs, log);
-
- if(validated_time==NULL || (sct_at < CFDateGetAbsoluteTime(validated_time))) {
- CFDateRef sct_time = CFDateCreate(kCFAllocatorDefault, sct_at);
- CFDictionarySetValue(validatingLogs, log, sct_time);
- CFReleaseSafe(sct_time);
- }
-}
-
-static CFArrayRef copy_ocsp_scts(SecPVCRef pvc)
-{
- CFMutableArrayRef SCTs = NULL;
- SecCertificateRef leafCert = NULL;
- SecCertificateRef issuer = NULL;
- CFArrayRef ocspResponsesData = NULL;
- SecOCSPRequestRef ocspRequest = NULL;
-
- ocspResponsesData = SecPathBuilderCopyOCSPResponses(pvc->builder);
- require_quiet(ocspResponsesData, out);
-
- require_quiet(SecPVCGetCertificateCount(pvc)>=2, out); //we need the issuer key for precerts.
- leafCert = SecPVCGetCertificateAtIndex(pvc, 0);
- issuer = SecPVCGetCertificateAtIndex(pvc, 1);
-
- require(leafCert, out);
- require(issuer, out); // not quiet: Those two would likely indicate an internal error, since we already checked the chain length above.
- ocspRequest = SecOCSPRequestCreate(leafCert, issuer);
-
- SCTs = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
- require(SCTs, out);
-
- CFArrayForEach(ocspResponsesData, ^(const void *value) {
- /* TODO: Should the builder already have the appropriate SecOCSPResponseRef ? */
- SecOCSPResponseRef ocspResponse = SecOCSPResponseCreate(value);
- if(ocspResponse && SecOCSPGetResponseStatus(ocspResponse)==kSecOCSPSuccess) {
- SecOCSPSingleResponseRef ocspSingleResponse = SecOCSPResponseCopySingleResponse(ocspResponse, ocspRequest);
- if(ocspSingleResponse) {
- CFArrayRef singleResponseSCTs = SecOCSPSingleResponseCopySCTs(ocspSingleResponse);
- if(singleResponseSCTs) {
- CFArrayAppendArray(SCTs, singleResponseSCTs, CFRangeMake(0, CFArrayGetCount(singleResponseSCTs)));
- CFRelease(singleResponseSCTs);
- }
- SecOCSPSingleResponseDestroy(ocspSingleResponse);
- }
- }
- if(ocspResponse) SecOCSPResponseFinalize(ocspResponse);
- });
-
- if(CFArrayGetCount(SCTs)==0) {
- CFReleaseNull(SCTs);
- }
-
-out:
- CFReleaseSafe(ocspResponsesData);
- if(ocspRequest)
- SecOCSPRequestFinalize(ocspRequest);
-
- return SCTs;
-}
-
-static void SecPolicyCheckCT(SecPVCRef pvc)
-{
- SecCertificateRef leafCert = SecPVCGetCertificateAtIndex(pvc, 0);
- CFArrayRef embeddedScts = SecCertificateCopySignedCertificateTimestamps(leafCert);
- CFArrayRef builderScts = SecPathBuilderCopySignedCertificateTimestamps(pvc->builder);
- CFDictionaryRef trustedLogs = SecPathBuilderCopyTrustedLogs(pvc->builder);
- CFArrayRef ocspScts = copy_ocsp_scts(pvc);
- CFDataRef precertEntry = copy_precert_entry_from_chain(pvc);
- CFDataRef x509Entry = copy_x509_entry_from_chain(pvc);
- __block uint32_t trustedSCTCount = 0;
- __block CFAbsoluteTime issuanceTime = SecPVCGetVerifyTime(pvc);
- __block CFAbsoluteTime certExpiry = SecCertificateNotValidAfter(leafCert);
- TA_CTFailureReason failureReason = TA_CTNoFailure;
-
- if (!trustedLogs) {
- SecOTAPKIRef otapkiref = SecOTAPKICopyCurrentOTAPKIRef();
- trustedLogs = SecOTAPKICopyTrustedCTLogs(otapkiref);
- CFReleaseSafe(otapkiref);
- }
-
- // This eventually contain list of logs who validated the SCT.
- CFMutableDictionaryRef currentLogsValidatingScts = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
- CFMutableDictionaryRef logsValidatingEmbeddedScts = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
-
- uint64_t vt = TimestampFromCFAbsoluteTime(SecPVCGetVerifyTime(pvc));
-
- __block bool at_least_one_currently_valid_external = 0;
- __block bool at_least_one_currently_valid_embedded = 0;
- __block bool unknown_log = 0;
- __block bool disqualified_log = 0;
-
- require(logsValidatingEmbeddedScts, out);
- require(currentLogsValidatingScts, out);
-
- /* Skip if there are no SCTs. */
- bool no_scts = (embeddedScts && CFArrayGetCount(embeddedScts) > 0) ||
- (builderScts && CFArrayGetCount(builderScts) > 0) ||
- (ocspScts && CFArrayGetCount(ocspScts) > 0);
- require_action_quiet(no_scts, out,
- TrustAnalyticsBuilder *analytics = SecPathBuilderGetAnalyticsData(pvc->builder);
- if (analytics) {
- analytics->ct_failure_reason = TA_CTNoSCTs;
- }
- );
-
- if(trustedLogs && CFDictionaryGetCount(trustedLogs) > 0) { // Don't bother trying to validate SCTs if we don't have any trusted logs.
- if(embeddedScts && precertEntry) { // Don't bother if we could not get the precert.
- CFArrayForEach(embeddedScts, ^(const void *value){
- CFAbsoluteTime sct_at;
- CFDictionaryRef log = getSCTValidatingLog(value, 1, precertEntry, vt, certExpiry, trustedLogs, &sct_at);
- if(log) {
- addValidatingLog(logsValidatingEmbeddedScts, log, sct_at);
- if(!CFDictionaryContainsKey(log, kSecCTRetirementDateKey)) {
- addValidatingLog(currentLogsValidatingScts, log, sct_at);
- at_least_one_currently_valid_embedded = true;
- trustedSCTCount++;
- } else {
- disqualified_log = true;
- }
- } else {
- unknown_log = true;
- }
- });
- }
-
- if(builderScts && x509Entry) { // Don't bother if we could not get the cert.
- CFArrayForEach(builderScts, ^(const void *value){
- CFAbsoluteTime sct_at;
- CFDictionaryRef log = getSCTValidatingLog(value, 0, x509Entry, vt, certExpiry, trustedLogs, &sct_at);
- if(log) {
- addValidatingLog(currentLogsValidatingScts, log, sct_at);
- at_least_one_currently_valid_external = true;
- trustedSCTCount++;
- } else {
- unknown_log = true;
- }
- });
- }
-
- if(ocspScts && x509Entry) {
- CFArrayForEach(ocspScts, ^(const void *value){
- CFAbsoluteTime sct_at;
- CFDictionaryRef log = getSCTValidatingLog(value, 0, x509Entry, vt, certExpiry, trustedLogs, &sct_at);
- if(log) {
- addValidatingLog(currentLogsValidatingScts, log, sct_at);
- at_least_one_currently_valid_external = true;
- trustedSCTCount++;
- } else {
- unknown_log = true;
- }
- });
- }
- } else {
- failureReason = TA_CTMissingLogs;
- }
-
-
- /* We now have 2 sets of logs that validated those SCTS, count them and make a final decision.
-
- Current Policy:
- is_ct = (A1 AND A2) OR (B1 AND B2).
-
- A1: embedded SCTs from 2+ to 5+ logs valid at issuance time
- A2: At least one embedded SCT from a currently valid log.
-
- B1: SCTs from 2 currently valid logs (from any source)
- B2: At least 1 external SCT from a currently valid log.
-
- */
-
- bool hasValidExternalSCT = (at_least_one_currently_valid_external && CFDictionaryGetCount(currentLogsValidatingScts)>=2);
- bool hasValidEmbeddedSCT = (at_least_one_currently_valid_embedded);
- SecCertificatePathVCRef path = SecPathBuilderGetPath(pvc->builder);
- SecCertificatePathVCSetIsCT(path, false);
-
- if (hasValidEmbeddedSCT) {
- /* Calculate issuance time based on timestamp of SCTs from current logs */
- CFDictionaryForEach(currentLogsValidatingScts, ^(const void *key, const void *value) {
- CFDictionaryRef log = key;
- if(!CFDictionaryContainsKey(log, kSecCTRetirementDateKey)) {
- // Log is still qualified
- CFDateRef ts = (CFDateRef) value;
- CFAbsoluteTime timestamp = CFDateGetAbsoluteTime(ts);
- if(timestamp < issuanceTime) {
- issuanceTime = timestamp;
- }
- }
- });
- SecCertificatePathVCSetIssuanceTime(path, issuanceTime);
- }
- if (hasValidExternalSCT) {
- /* Note: since external SCT validates this cert, we do not need to
- override issuance time here. If the cert also has a valid embedded
- SCT, issuanceTime will be calculated and set in the block above. */
- SecCertificatePathVCSetIsCT(path, true);
- } else if (hasValidEmbeddedSCT) {
- __block int lifetime; // in Months
- __block unsigned once_or_current_qualified_embedded = 0;
-
- /* Count Logs */
- __block bool failed_once_check = false;
- CFDictionaryForEach(logsValidatingEmbeddedScts, ^(const void *key, const void *value) {
- CFDictionaryRef log = key;
- CFDateRef ts = value;
- CFDateRef expiry = CFDictionaryGetValue(log, kSecCTRetirementDateKey);
- if (expiry == NULL) { // Currently qualified OR
- once_or_current_qualified_embedded++;
- } else if (CFDateCompare(ts, expiry, NULL) == kCFCompareLessThan && // Once qualified. That is, qualified at the time of SCT AND
- issuanceTime < CFDateGetAbsoluteTime(expiry)) { // at the time of issuance.)
- once_or_current_qualified_embedded++;
- trustedSCTCount++;
- } else {
- failed_once_check = true;
- }
- });
-
- SecCFCalendarDoWithZuluCalendar(^(CFCalendarRef zuluCalendar) {
- int _lifetime;
- CFCalendarGetComponentDifference(zuluCalendar,
- SecCertificateNotValidBefore(leafCert),
- SecCertificateNotValidAfter(leafCert),
- 0, "M", &_lifetime);
- lifetime = _lifetime;
- });
-
- unsigned requiredEmbeddedSctsCount;
-
- if (lifetime < 15) {
- requiredEmbeddedSctsCount = 2;
- } else if (lifetime <= 27) {
- requiredEmbeddedSctsCount = 3;
- } else if (lifetime <= 39) {
- requiredEmbeddedSctsCount = 4;
- } else {
- requiredEmbeddedSctsCount = 5;
- }
-
- if(once_or_current_qualified_embedded >= requiredEmbeddedSctsCount){
- SecCertificatePathVCSetIsCT(path, true);
- } else {
- /* Not enough "once or currently qualified" SCTs */
- if (failed_once_check) {
- failureReason = TA_CTEmbeddedNotEnoughDisqualified;
- } else if (unknown_log) {
- failureReason = TA_CTEmbeddedNotEnoughUnknown;
- } else {
- failureReason = TA_CTEmbeddedNotEnough;
- }
- }
- } else if (!at_least_one_currently_valid_embedded && !at_least_one_currently_valid_external) {
- /* No currently valid SCTs */
- if (disqualified_log) {
- failureReason = TA_CTNoCurrentSCTsDisqualifiedLog;
- } else if (unknown_log) {
- failureReason = TA_CTNoCurrentSCTsUnknownLog;
- }
- } else if (at_least_one_currently_valid_external) {
- /* One presented current SCT but failed total current check */
- if (disqualified_log) {
- failureReason = TA_CTPresentedNotEnoughDisqualified;
- } else if (unknown_log) {
- failureReason = TA_CTPresentedNotEnoughUnknown;
- } else {
- failureReason = TA_CTPresentedNotEnough;
- }
- }
-
- /* Record analytics data for CT */
- TrustAnalyticsBuilder *analytics = SecPathBuilderGetAnalyticsData(pvc->builder);
- require_quiet(analytics, out);
- uint32_t sctCount = 0;
- /* Count the total number of SCTs we found and report where we got them */
- if (embeddedScts && CFArrayGetCount(embeddedScts) > 0) {
- analytics->sct_sources |= TA_SCTEmbedded;
- sctCount += CFArrayGetCount(embeddedScts);
- }
- if (builderScts && CFArrayGetCount(builderScts) > 0) {
- analytics->sct_sources |= TA_SCT_TLS;
- sctCount += CFArrayGetCount(builderScts);
- }
- if (ocspScts && CFArrayGetCount(ocspScts) > 0) {
- analytics->sct_sources |= TA_SCT_OCSP;
- sctCount += CFArrayGetCount(ocspScts);
- }
- /* Report how many of those SCTs were once or currently qualified */
- analytics->number_trusted_scts = trustedSCTCount;
- /* Report how many SCTs we got */
- analytics->number_scts = sctCount;
- /* Why we failed */
- analytics->ct_failure_reason = failureReason;
- /* Only one current SCT -- close to failure */
- if (CFDictionaryGetCount(currentLogsValidatingScts) == 1) {
- analytics->ct_one_current = true;
- }
-out:
- CFReleaseSafe(logsValidatingEmbeddedScts);
- CFReleaseSafe(currentLogsValidatingScts);
- CFReleaseSafe(builderScts);
- CFReleaseSafe(embeddedScts);
- CFReleaseSafe(ocspScts);
- CFReleaseSafe(precertEntry);
- CFReleaseSafe(trustedLogs);
- CFReleaseSafe(x509Entry);
-}
-
static bool checkPolicyOidData(SecPVCRef pvc, CFDataRef oid) {
CFIndex ix, count = SecPVCGetCertificateCount(pvc);
DERItem key_value;
}
}
+static void SecPolicyCheckNonTlsCTRequired(SecPVCRef pvc, CFStringRef key) {
+ // Skip if kill switch enabled or log list not updated
+ SecOTAPKIRef otaref = SecOTAPKICopyCurrentOTAPKIRef();
+ CFDictionaryRef trustedLogs = SecPathBuilderCopyTrustedLogs(pvc->builder);
+ if (!SecOTAPKIKillSwitchEnabled(otaref, kOTAPKIKillSwitchNonTLSCT) &&
+ (SecOTAPKIAssetStalenessLessThanSeconds(otaref, kSecOTAPKIAssetStalenessDisable) || trustedLogs)) {
+ // Check CT against the non-TLS log list
+ if (!SecPolicyCheckNonTlsCT(pvc)) {
+ SecPVCSetResult(pvc, key, 0, kCFBooleanFalse);
+ }
+ }
+ CFReleaseNull(otaref);
+ CFReleaseNull(trustedLogs);
+}
+
void SecPolicyServerInitialize(void) {
gSecPolicyLeafCallbacks = CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
&kCFTypeDictionaryKeyCallBacks, NULL);
return (SecPolicyRef)CFArrayGetValueAtIndex(pvc->policies, pvc->policyIX);
}
-static CFIndex SecPVCGetCertificateCount(SecPVCRef pvc) {
+CFIndex SecPVCGetCertificateCount(SecPVCRef pvc) {
return SecPathBuilderGetCertificateCount(pvc->builder);
}
-static SecCertificateRef SecPVCGetCertificateAtIndex(SecPVCRef pvc, CFIndex ix) {
+SecCertificateRef SecPVCGetCertificateAtIndex(SecPVCRef pvc, CFIndex ix) {
return SecPathBuilderGetCertificateAtIndex(pvc->builder, ix);
}
-static CFAbsoluteTime SecPVCGetVerifyTime(SecPVCRef pvc) {
+CFAbsoluteTime SecPVCGetVerifyTime(SecPVCRef pvc) {
return SecPathBuilderGetVerifyTime(pvc->builder);
}
void SecPVCDelete(SecPVCRef pvc);
void SecPVCSetPath(SecPVCRef pvc, SecCertificatePathVCRef path);
SecPolicyRef SecPVCGetPolicy(SecPVCRef pv);
+SecCertificateRef SecPVCGetCertificateAtIndex(SecPVCRef pvc, CFIndex ix);
+CFIndex SecPVCGetCertificateCount(SecPVCRef pvc);
+CFAbsoluteTime SecPVCGetVerifyTime(SecPVCRef pvc);
/* Set the string result as the reason for the sub policy check key
failing. The policy check function should continue processing if
TA_SCT_TLS = 1 << 2,
};
-typedef CF_ENUM(uint8_t, TA_CTFailureReason) {
- TA_CTNoFailure = 0,
- TA_CTNoSCTs = 1,
- TA_CTMissingLogs = 2,
- TA_CTNoCurrentSCTsUnknownLog = 3,
- TA_CTNoCurrentSCTsDisqualifiedLog = 4,
- TA_CTPresentedNotEnoughUnknown = 5,
- TA_CTPresentedNotEnoughDisqualified = 6,
- TA_CTPresentedNotEnough = 7,
- TA_CTEmbeddedNotEnoughUnknown = 8,
- TA_CTEmbeddedNotEnoughDisqualified = 9,
- TA_CTEmbeddedNotEnough = 10,
-};
-
typedef CF_OPTIONS(uint8_t, TAValidStatus) {
TAValidDefinitelyOK = 1 << 0,
TAValidProbablyOK = 1 << 1,
TA_SCTSource sct_sources;
uint32_t number_scts;
uint32_t number_trusted_scts;
- TA_CTFailureReason ct_failure_reason;
bool ct_one_current;
// CAIssuer
bool ca_issuer_cache_hit;