]> git.saurik.com Git - apple/security.git/commitdiff
Security-59754.80.3.tar.gz master macos-112 v59754.80.3
authorApple <opensource@apple.com>
Tue, 26 Jan 2021 19:59:20 +0000 (19:59 +0000)
committerApple <opensource@apple.com>
Tue, 26 Jan 2021 19:59:20 +0000 (19:59 +0000)
16 files changed:
Analytics/SQLite/SFSQLite.m
OSX/sec/Security/SecCertificate.c
OSX/sec/Security/SecFrameworkStrings.h
OSX/sec/Security/SecPolicy.c
OSX/sec/Security/SecPolicy.list
OSX/sec/Security/SecPolicyChecks.list
Security.xcodeproj/project.pbxproj
tests/TrustTests/EvaluationTests/CTTests.m
trust/headers/SecPolicyPriv.h
trust/trustd/CertificateTransparency.h [new file with mode: 0644]
trust/trustd/CertificateTransparency.m [new file with mode: 0644]
trust/trustd/OTATrustUtilities.h
trust/trustd/OTATrustUtilities.m
trust/trustd/SecPolicyServer.c
trust/trustd/SecPolicyServer.h
trust/trustd/SecTrustServer.h

index e0f1a8ddee6f9121ad596d68c041c55e2d3efc31..d3d9acd3fec61704d5de1ce0ad17dc0a2e0e3bfb 100644 (file)
@@ -301,7 +301,6 @@ allDone:
 */
 - (void)attemptProperDatabasePermissions
 {
-#if TARGET_OS_IPHONE
     NSFileManager* fm = [NSFileManager defaultManager];
     [fm setAttributes:@{NSFilePosixPermissions : [NSNumber numberWithShort:0666]}
          ofItemAtPath:_path
@@ -312,7 +311,6 @@ allDone:
     [fm setAttributes:@{NSFilePosixPermissions : [NSNumber numberWithShort:0666]}
          ofItemAtPath:[NSString stringWithFormat:@"%@-shm",_path]
                 error:nil];
-#endif
 }
 
 - (BOOL)openWithError:(NSError **)error {
index dd5f50d4fe4a3a47645943220462720f629398bf..d9571455702e27b96edad72cc6b26e9f39a22b59 100644 (file)
@@ -1249,13 +1249,15 @@ static bool isAppleExtensionOID(const DERItem *extnID)
     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)
index 0d6db54a8a6f7ea35a49819ce96141329f03bf4b..62286360806caa4a1050e7c1d2ccdc43ccb21dee 100644 (file)
@@ -331,6 +331,7 @@ __BEGIN_DECLS
 #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
 
index 5e32b6eb1fb337d3573572ca96daeca2376bed35..2945918eccbe1190e7c0620cbe89d0ef0e769b41 100644 (file)
@@ -355,6 +355,8 @@ SecPolicyRef SecPolicyCreateWithProperties(CFTypeRef policyIdentifier,
         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. */
@@ -4516,3 +4518,49 @@ errOut:
     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;
+}
index bd0ddbc41ff3cfddd96063ce22fabb02afd1a51f..641f5d46592a9b1b2019bf6598f864aefe4a122d 100644 (file)
@@ -105,3 +105,4 @@ POLICYMACRO(AccessoryUpdateSigning,                 99, E, AccessoryUpdateSignin
 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)
index 91f55d0f1fa8a37bf10a173a844124685c716e8b..8711e54e97e11bfdf1eeef365ae5d79445507851 100644 (file)
@@ -95,6 +95,7 @@ POLICYCHECKMACRO(IssuerNameConstraints,          F, B,  ,  ,  , 0x8001211F, errS
 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 *********************
index 05c364386b750e781b20202cbf2eff65e1be9d48..6ff0fdc5db7541f93f7a795fe4881bb98fbff23f 100644 (file)
                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 */,
index 40fdd7c43f1631991cd422bff3d9dddc5e23d0ac..b95fddaafd0d98bd551529b9450ef414f8c378d5 100644 (file)
@@ -1944,3 +1944,199 @@ errOut:
 }
 
 @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
index 85f6237f021e5347898f0eb4011bef0ac625db15..7caac7ee1c7480dddbcfc114ec803605517fd988 100644 (file)
@@ -197,6 +197,8 @@ extern const CFStringRef kSecPolicyApplePCSEscrowServiceIdKeySigning
     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));
 
 
 /*!
@@ -1852,6 +1854,28 @@ __nullable CF_RETURNS_RETAINED
 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)
  */
@@ -1944,6 +1968,7 @@ extern const CFStringRef kSecPolicyCheckMissingIntermediate;
 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;
diff --git a/trust/trustd/CertificateTransparency.h b/trust/trustd/CertificateTransparency.h
new file mode 100644 (file)
index 0000000..b51c32e
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+ * 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_ */
diff --git a/trust/trustd/CertificateTransparency.m b/trust/trustd/CertificateTransparency.m
new file mode 100644 (file)
index 0000000..a10647e
--- /dev/null
@@ -0,0 +1,705 @@
+/*
+ * 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, &currentLogsValidatingScts, &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, &currentLogsValidatingScts, &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;
+}
index 4b770f9d9d758800f82eaa3d3105366dd32a8f89..5d3d1bb719ae19ebde5fae4b1c4af2d408924a0e 100644 (file)
@@ -154,16 +154,21 @@ NSNumber *SecOTAPKIGetSamplingRateForEvent(SecOTAPKIRef otapkiRef, NSString *eve
 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);
index 4ac7be77d67d0b0212c190b4c8673586a3891ddd..84d1344e1733b7a6e6fe4d49d3e560fc3be0f770 100644 (file)
@@ -206,11 +206,16 @@ NSString *kOTATrustContentVersionKey = @"MobileAssetContentVersion";
 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";
@@ -446,6 +451,7 @@ static void DeleteOldAssetData(void) {
     if (SecOTAPKIIsSystemTrustd()) {
         /* Delete the asset files, but keep the check-in time and version */
         DeleteFileWithName(kOTATrustTrustedCTLogsFilename);
+        DeleteFileWithName(kOTATrustTrustedCTLogsNonTLSFilename);
         DeleteFileWithName(kOTATrustAnalyticsSamplingRatesFilename);
         DeleteFileWithName(kOTATrustAppleCertifcateAuthoritiesFilename);
     }
@@ -506,6 +512,11 @@ static BOOL CopyFileToDisk(NSString *filename, NSURL *localURL, NSError **error)
     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;
 
@@ -519,6 +530,16 @@ static void GetKillSwitchAttributes(NSDictionary *attributes) {
         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
@@ -846,6 +867,7 @@ static void InitializeOTATrustAsset(dispatch_queue_t queue) {
         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));
         });
     }
 }
@@ -1054,13 +1076,13 @@ CFDictionaryRef SecOTAPKICreateTrustedCTLogsDictionaryFromArray(CFArrayRef trust
     }
 }
 
-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);
@@ -1071,7 +1093,7 @@ static CF_RETURNS_RETAINED CFDictionaryRef InitializeTrustedCTLogs() {
         }
 #endif
         if (!isNSArray(trustedCTLogs)) {
-            trustedCTLogs = [NSArray arrayWithContentsOfURL:SecSystemTrustStoreCopyResourceNSURL(kOTATrustTrustedCTLogsFilename)];
+            trustedCTLogs = [NSArray arrayWithContentsOfURL:SecSystemTrustStoreCopyResourceNSURL(filename)];
         }
         if (isNSArray(trustedCTLogs)) {
             return CFBridgingRetain(ConvertTrustedCTLogsArrayToDictionary(trustedCTLogs));
@@ -1456,6 +1478,7 @@ struct _OpaqueSecOTAPKI {
     CFSetRef            _grayListSet;
     CFDictionaryRef     _allowList;
     CFDictionaryRef     _trustedCTLogs;
+    CFDictionaryRef     _nonTlsTrustedCTLogs;
     CFURLRef            _pinningList;
     CFArrayRef          _escrowCertificates;
     CFArrayRef          _escrowPCSCertificates;
@@ -1473,6 +1496,7 @@ struct _OpaqueSecOTAPKI {
     CFDictionaryRef     _secExperimentConfig;
     uint64_t            _secExperimentAssetVersion;
     bool                _ctKillSwitch;
+    bool                _nonTlsCtKillSwitch;
 };
 
 CFGiblisFor(SecOTAPKI)
@@ -1495,6 +1519,7 @@ static void SecOTAPKIDestroy(CFTypeRef cf) {
     CFReleaseNull(otapkiref->_anchorLookupTable);
 
     CFReleaseNull(otapkiref->_trustedCTLogs);
+    CFReleaseNull(otapkiref->_nonTlsTrustedCTLogs);
     CFReleaseNull(otapkiref->_pinningList);
     CFReleaseNull(otapkiref->_eventSamplingRates);
     CFReleaseNull(otapkiref->_appleCAs);
@@ -1547,6 +1572,7 @@ static uint64_t GetAssetVersion(CFErrorRef *error) {
                 *error = CFRetainSafe((__bridge CFErrorRef)nserror);
             }
             DeleteOldAssetData();
+            DisableKillSwitches();
         }
 #endif
         return version;
@@ -1610,7 +1636,8 @@ static SecOTAPKIRef SecOTACreate() {
     // (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();
@@ -1677,9 +1704,11 @@ static SecOTAPKIRef SecOTACreate() {
     /* 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;
@@ -1738,6 +1767,8 @@ void UpdateKillSwitch(NSString *key, bool value) {
     dispatch_sync(kOTAQueue, ^{
         if ([key isEqualToString:(__bridge NSString*)kOTAPKIKillSwitchCT]) {
             kCurrentOTAPKIRef->_ctKillSwitch = value;
+        } else if ([key isEqualToString:(__bridge NSString*)kOTAPKIKillSwitchNonTLSCT]) {
+            kCurrentOTAPKIRef->_nonTlsCtKillSwitch = value;
         }
     });
 }
@@ -1749,6 +1780,7 @@ static BOOL UpdateFromAsset(NSURL *localURL, NSNumber *asset_version, NSError **
         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;
@@ -1762,6 +1794,15 @@ static BOOL UpdateFromAsset(NSURL *localURL, NSNumber *asset_version, NSError **
         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];
@@ -1784,14 +1825,21 @@ static BOOL UpdateFromAsset(NSURL *localURL, NSNumber *asset_version, NSError **
     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)
@@ -1916,6 +1964,23 @@ CFDictionaryRef SecOTAPKICopyTrustedCTLogs(SecOTAPKIRef otapkiRef) {
     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;
@@ -2107,6 +2172,8 @@ bool SecOTAPKIKillSwitchEnabled(SecOTAPKIRef otapkiRef, CFStringRef key) {
     }
     if (CFEqualSafe(key, kOTAPKIKillSwitchCT)) {
         return otapkiRef->_ctKillSwitch;
+    } else if (CFEqualSafe(key, kOTAPKIKillSwitchNonTLSCT)) {
+        return otapkiRef->_nonTlsCtKillSwitch;
     }
     return false;
 }
@@ -2166,14 +2233,24 @@ CFDictionaryRef SecOTAPKICopyCTLogForKeyID(CFDataRef keyID, CFErrorRef* error) {
         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;
 }
 
index e0abcb940787357aabb5bb6959bdf651a84319e2..063b0e1b818aae8beaee510cbce90c4c8cf99852 100644 (file)
  * 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>
@@ -95,9 +101,6 @@ static void secdumpdata(CFDataRef data, const char *name) {
  ****************** 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;
@@ -1328,633 +1331,6 @@ certificatePolicies or extendedKeyUsage extensions.
 */
 }
 
-
-/*
- * 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;
@@ -2622,6 +1998,21 @@ static void SecPolicyCheckNotCA(SecPVCRef pvc, CFStringRef key) {
     }
 }
 
+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);
@@ -2712,15 +2103,15 @@ SecPolicyRef SecPVCGetPolicy(SecPVCRef pvc) {
        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);
 }
 
index 44811a01d20cb67ce57f734a4d6a7f0be471a189..ab22155df6ea01f01013da9126bae1dad1e8c9d0 100644 (file)
@@ -45,6 +45,9 @@ void SecPVCInit(SecPVCRef pvc, SecPathBuilderRef builder, CFArrayRef policies);
 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
index 50233ad5d25503236bf95f30736a99019c945f5b..10bcf29c353122ab62b5de81519c5c65a1d4d18b 100644 (file)
@@ -152,20 +152,6 @@ typedef CF_OPTIONS(uint8_t, TA_SCTSource) {
     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,
@@ -185,7 +171,6 @@ typedef struct {
     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;