diff --git a/Package.swift b/Package.swift index 207eb18e9b2..88308ebd759 100644 --- a/Package.swift +++ b/Package.swift @@ -726,7 +726,7 @@ if ProcessInfo.processInfo.environment["SWIFTCI_USE_LOCAL_DEPS"] == nil { .package(url: "https://github.com/apple/swift-crypto.git", .upToNextMinor(from: "2.5.0")), .package(url: "https://github.com/apple/swift-system.git", .upToNextMinor(from: "1.1.1")), .package(url: "https://github.com/apple/swift-collections.git", .upToNextMinor(from: "1.0.1")), - .package(url: "https://github.com/apple/swift-certificates.git", .upToNextMinor(from: "0.4.1")), + .package(url: "https://github.com/apple/swift-certificates.git", .upToNextMinor(from: "0.6.0")), ] } else { package.dependencies += [ diff --git a/Sources/PackageCollectionsSigning/CertificatePolicy.swift b/Sources/PackageCollectionsSigning/CertificatePolicy.swift index 5de918f07b9..c7e4eceb45e 100644 --- a/Sources/PackageCollectionsSigning/CertificatePolicy.swift +++ b/Sources/PackageCollectionsSigning/CertificatePolicy.swift @@ -77,7 +77,7 @@ extension CertificatePolicy { func verify( certChain: [Certificate], trustedRoots: [Certificate]?, - policies: [VerifierPolicy], + @PolicyBuilder policies: () -> some VerifierPolicy, observabilityScope: ObservabilityScope, callbackQueue: DispatchQueue, callback: @escaping (Result) -> Void @@ -87,7 +87,7 @@ extension CertificatePolicy { guard !certChain.isEmpty else { return wrappedCallback(.failure(CertificatePolicyError.emptyCertChain)) } - + let policies = policies() Task { var trustStore = CertificateStores.defaultTrustRoots if let trustedRoots { @@ -98,9 +98,9 @@ extension CertificatePolicy { return wrappedCallback(.failure(CertificatePolicyError.noTrustedRootCertsConfigured)) } - let policySet = PolicySet(policies: policies) - - var verifier = Verifier(rootCertificates: CertificateStore(trustStore), policy: policySet) + var verifier = Verifier(rootCertificates: CertificateStore(trustStore)) { + policies + } let result = await verifier.validate( leafCertificate: certChain[0], intermediates: CertificateStore(certChain) @@ -182,31 +182,26 @@ struct DefaultCertificatePolicy: CertificatePolicy { return wrappedCallback(.failure(CertificatePolicyError.emptyCertChain)) } - var policies = [VerifierPolicy]() - policies.append(_ADPCertificatePolicy()) // included for testing - // Check if subject name matches - policies.append( - _SubjectNamePolicy( - expectedUserID: self.expectedSubjectUserID, - expectedOrganizationalUnit: self.expectedSubjectOrganizationalUnit - ) - ) - // Must be a code signing certificate - policies.append(_CodeSigningPolicy()) - // Basic validations including expiry check - policies.append(RFC5280Policy(validationTime: validationTime)) - // Must support OCSP - policies.append( - _OCSPVerifierPolicy( - httpClient: self.httpClient, - validationTime: validationTime - ) - ) - self.verify( certChain: certChain, trustedRoots: self.trustedRoots, - policies: policies, + policies: { + _ADPCertificatePolicy() // included for testing + // Check if subject name matches + _SubjectNamePolicy( + expectedUserID: self.expectedSubjectUserID, + expectedOrganizationalUnit: self.expectedSubjectOrganizationalUnit + ) + // Must be a code signing certificate + _CodeSigningPolicy() + // Basic validations including expiry check + RFC5280Policy(validationTime: validationTime) + // Must support OCSP + _OCSPVerifierPolicy( + httpClient: self.httpClient, + validationTime: validationTime + ) + }, observabilityScope: self.observabilityScope, callbackQueue: self.callbackQueue, callback: callback @@ -269,33 +264,28 @@ struct ADPSwiftPackageCollectionCertificatePolicy: CertificatePolicy { return wrappedCallback(.failure(CertificatePolicyError.emptyCertChain)) } - var policies = [VerifierPolicy]() - // Check for specific markers - policies.append(_ADPSwiftPackageCertificatePolicy()) - policies.append(_ADPCertificatePolicy()) // included for testing - // Check if subject name matches - policies.append( - _SubjectNamePolicy( - expectedUserID: self.expectedSubjectUserID, - expectedOrganizationalUnit: self.expectedSubjectOrganizationalUnit - ) - ) - // Must be a code signing certificate - policies.append(_CodeSigningPolicy()) - // Basic validations including expiry check - policies.append(RFC5280Policy(validationTime: validationTime)) - // Must support OCSP - policies.append( - _OCSPVerifierPolicy( - httpClient: self.httpClient, - validationTime: validationTime - ) - ) - self.verify( certChain: certChain, trustedRoots: self.trustedRoots, - policies: policies, + policies: { + // Check for specific markers + _ADPSwiftPackageCertificatePolicy() + _ADPCertificatePolicy() // included for testing + // Check if subject name matches + _SubjectNamePolicy( + expectedUserID: self.expectedSubjectUserID, + expectedOrganizationalUnit: self.expectedSubjectOrganizationalUnit + ) + // Must be a code signing certificate + _CodeSigningPolicy() + // Basic validations including expiry check + RFC5280Policy(validationTime: validationTime) + // Must support OCSP + _OCSPVerifierPolicy( + httpClient: self.httpClient, + validationTime: validationTime + ) + }, observabilityScope: self.observabilityScope, callbackQueue: self.callbackQueue, callback: callback @@ -358,33 +348,28 @@ struct ADPAppleDistributionCertificatePolicy: CertificatePolicy { return wrappedCallback(.failure(CertificatePolicyError.emptyCertChain)) } - var policies = [VerifierPolicy]() - // Check for specific markers - policies.append(_ADPAppleDistributionCertificatePolicy()) - policies.append(_ADPCertificatePolicy()) // included for testing - // Check if subject name matches - policies.append( - _SubjectNamePolicy( - expectedUserID: self.expectedSubjectUserID, - expectedOrganizationalUnit: self.expectedSubjectOrganizationalUnit - ) - ) - // Must be a code signing certificate - policies.append(_CodeSigningPolicy()) - // Basic validations including expiry check - policies.append(RFC5280Policy(validationTime: validationTime)) - // Must support OCSP - policies.append( - _OCSPVerifierPolicy( - httpClient: self.httpClient, - validationTime: validationTime - ) - ) - self.verify( certChain: certChain, trustedRoots: self.trustedRoots, - policies: policies, + policies: { + // Check for specific markers + _ADPAppleDistributionCertificatePolicy() + _ADPCertificatePolicy() // included for testing + // Check if subject name matches + _SubjectNamePolicy( + expectedUserID: self.expectedSubjectUserID, + expectedOrganizationalUnit: self.expectedSubjectOrganizationalUnit + ) + // Must be a code signing certificate + _CodeSigningPolicy() + // Basic validations including expiry check + RFC5280Policy(validationTime: validationTime) + // Must support OCSP + _OCSPVerifierPolicy( + httpClient: self.httpClient, + validationTime: validationTime + ) + }, observabilityScope: self.observabilityScope, callbackQueue: self.callbackQueue, callback: callback diff --git a/Sources/PackageSigning/SignatureProvider.swift b/Sources/PackageSigning/SignatureProvider.swift index fe34a90d8f5..1605b14550d 100644 --- a/Sources/PackageSigning/SignatureProvider.swift +++ b/Sources/PackageSigning/SignatureProvider.swift @@ -280,9 +280,11 @@ struct CMSSignatureProvider: SignatureProviderProtocol { // the WWDR roots are in the trust store or not, which by default // they are but user may disable that through configuration. additionalIntermediateCertificates: Certificates.wwdrIntermediates, - trustRoots: CertificateStore(trustRoots), - policy: self.buildPolicySet(configuration: verifierConfiguration, httpClient: self.httpClient) - ) + trustRoots: CertificateStore(trustRoots) + ) { + self.buildPolicySet(configuration: verifierConfiguration, httpClient: self.httpClient) + } + switch result { case .success(let valid): @@ -341,9 +343,9 @@ struct CMSSignatureProvider: SignatureProviderProtocol { // For self-signed certificate, the signature should include intermediate(s). untrustedIntermediates.append(contentsOf: cmsSignature.certificates) - let policySet = self.buildPolicySet(configuration: verifierConfiguration, httpClient: self.httpClient) - - var verifier = Verifier(rootCertificates: CertificateStore(trustRoots), policy: policySet) + var verifier = Verifier(rootCertificates: CertificateStore(trustRoots)) { + self.buildPolicySet(configuration: verifierConfiguration, httpClient: self.httpClient) + } let result = await verifier.validate( leafCertificate: signingCertificate, intermediates: CertificateStore(untrustedIntermediates) diff --git a/Sources/PackageSigning/VerifierPolicies.swift b/Sources/PackageSigning/VerifierPolicies.swift index 1fe11e706d3..a962be9df47 100644 --- a/Sources/PackageSigning/VerifierPolicies.swift +++ b/Sources/PackageSigning/VerifierPolicies.swift @@ -20,56 +20,49 @@ import Basics @_implementationOnly @_spi(DisableValidityCheck) import X509 extension SignatureProviderProtocol { - func buildPolicySet(configuration: VerifierConfiguration, httpClient: HTTPClient) -> PolicySet { - var policies: [VerifierPolicy] = [ - _CodeSigningPolicy(), - _ADPCertificatePolicy(), - ] - + @PolicyBuilder + func buildPolicySet(configuration: VerifierConfiguration, httpClient: HTTPClient) -> some VerifierPolicy { + _CodeSigningPolicy() + _ADPCertificatePolicy() + let now = Date() switch (configuration.certificateExpiration, configuration.certificateRevocation) { case (.enabled(let expiryValidationTime), .strict(let revocationValidationTime)): - policies.append(RFC5280Policy(validationTime: expiryValidationTime ?? now)) - policies - .append(_OCSPVerifierPolicy( - failureMode: .hard, - httpClient: httpClient, - validationTime: revocationValidationTime ?? now - )) + RFC5280Policy(validationTime: expiryValidationTime ?? now) + _OCSPVerifierPolicy( + failureMode: .hard, + httpClient: httpClient, + validationTime: revocationValidationTime ?? now + ) case (.enabled(let expiryValidationTime), .allowSoftFail(let revocationValidationTime)): - policies.append(RFC5280Policy(validationTime: expiryValidationTime ?? now)) - policies - .append(_OCSPVerifierPolicy( - failureMode: .soft, - httpClient: httpClient, - validationTime: revocationValidationTime ?? now - )) + RFC5280Policy(validationTime: expiryValidationTime ?? now) + _OCSPVerifierPolicy( + failureMode: .soft, + httpClient: httpClient, + validationTime: revocationValidationTime ?? now + ) case (.enabled(let expiryValidationTime), .disabled): - policies.append(RFC5280Policy(validationTime: expiryValidationTime ?? now)) + RFC5280Policy(validationTime: expiryValidationTime ?? now) case (.disabled, .strict(let revocationValidationTime)): // Always do expiry check (and before) if revocation check is enabled - policies.append(RFC5280Policy(validationTime: revocationValidationTime ?? now)) - policies - .append(_OCSPVerifierPolicy( - failureMode: .hard, - httpClient: httpClient, - validationTime: revocationValidationTime ?? now - )) + RFC5280Policy(validationTime: revocationValidationTime ?? now) + _OCSPVerifierPolicy( + failureMode: .hard, + httpClient: httpClient, + validationTime: revocationValidationTime ?? now + ) case (.disabled, .allowSoftFail(let revocationValidationTime)): // Always do expiry check (and before) if revocation check is enabled - policies.append(RFC5280Policy(validationTime: revocationValidationTime ?? now)) - policies - .append(_OCSPVerifierPolicy( - failureMode: .soft, - httpClient: httpClient, - validationTime: revocationValidationTime ?? now - )) + RFC5280Policy(validationTime: revocationValidationTime ?? now) + _OCSPVerifierPolicy( + failureMode: .soft, + httpClient: httpClient, + validationTime: revocationValidationTime ?? now + ) case (.disabled, .disabled): // We should still do basic certificate validations even if expiry check is disabled - policies.append(RFC5280Policy.withValidityCheckDisabled()) + RFC5280Policy.withValidityCheckDisabled() } - - return PolicySet(policies: policies) } } diff --git a/Tests/PackageCollectionsSigningTests/Utilities.swift b/Tests/PackageCollectionsSigningTests/Utilities.swift index 42f81fc9804..8fa888c2ef5 100644 --- a/Tests/PackageCollectionsSigningTests/Utilities.swift +++ b/Tests/PackageCollectionsSigningTests/Utilities.swift @@ -56,17 +56,16 @@ struct TestCertificatePolicy: CertificatePolicy { validationTime: Date, callback: @escaping (Result) -> Void ) { - var policies = [VerifierPolicy]() - // Must be a code signing certificate - policies.append(_CodeSigningPolicy()) - // Basic validations including expiry check - policies.append(RFC5280Policy(validationTime: validationTime)) - // Doesn't require OCSP - self.verify( certChain: certChain, trustedRoots: self.trustedRoots, - policies: policies, + policies: { + // Must be a code signing certificate + _CodeSigningPolicy() + // Basic validations including expiry check + RFC5280Policy(validationTime: validationTime) + // Doesn't require OCSP + }, observabilityScope: ObservabilitySystem.NOOP, callbackQueue: callbackQueue, callback: callback diff --git a/Tests/PackageSigningTests/SigningTests.swift b/Tests/PackageSigningTests/SigningTests.swift index a0f8351e367..e71fddc61a5 100644 --- a/Tests/PackageSigningTests/SigningTests.swift +++ b/Tests/PackageSigningTests/SigningTests.swift @@ -1241,7 +1241,10 @@ extension BasicOCSPResponse { responses: [OCSPSingleResponse], privateKey: P256.Signing.PrivateKey, certs: [Certificate]? = [], - @ExtensionsBuilder responseExtensions: () -> Certificate.Extensions = { .init() } + @ExtensionsBuilder responseExtensions: () throws -> Result = { + // workaround for rdar://108897294 + Result.success(Certificate.Extensions()) + } ) throws -> Self { try .signed( responseData: .init( @@ -1249,7 +1252,7 @@ extension BasicOCSPResponse { responderID: responderID, producedAt: producedAt, responses: responses, - responseExtensions: responseExtensions() + responseExtensions: try .init(builder: responseExtensions) ), privateKey: privateKey, certs: certs