@@ -26,11 +26,13 @@ import Security
26
26
27
27
let appleDistributionIOSMarker = " 1.2.840.113635.100.6.1.4 "
28
28
let appleDistributionMacOSMarker = " 1.2.840.113635.100.6.1.7 "
29
+ let appleSwiftPackageCollectionMarker = " 1.2.840.113635.100.6.1.35 "
29
30
let appleIntermediateMarker = " 1.2.840.113635.100.6.2.1 "
30
31
31
32
// For BoringSSL only - the Security framework recognizes these marker extensions
32
33
#if os(Linux) || os(Windows) || os(Android)
33
- let supportedCriticalExtensions : Set < String > = [ appleDistributionIOSMarker, appleDistributionMacOSMarker,
34
+ let supportedCriticalExtensions : Set < String > = [ appleSwiftPackageCollectionMarker, // This isn't a critical extension but including it just in case,
35
+ appleDistributionIOSMarker, appleDistributionMacOSMarker,
34
36
// Support "Apple Development" cert markers--they are valid code signing certs after all and satisfy DefaultCertificatePolicy
35
37
" 1.2.840.113635.100.6.1.2 " , " 1.2.840.113635.100.6.1.12 " ]
36
38
#endif
@@ -659,11 +661,114 @@ struct DefaultCertificatePolicy: CertificatePolicy {
659
661
}
660
662
}
661
663
662
- /// Policy for validating developer.apple.com certificates.
664
+ /// Policy for validating developer.apple.com Swift Package Collection certificates.
665
+ ///
666
+ /// This has the same requirements as `DefaultCertificatePolicy` plus additional
667
+ /// marker extensions for Swift Package Collection certifiications.
668
+ struct AppleSwiftPackageCollectionCertificatePolicy : CertificatePolicy {
669
+ private static let expectedCertChainLength = 3
670
+
671
+ let trustedRoots : [ Certificate ] ?
672
+ let expectedSubjectUserID : String ?
673
+
674
+ private let callbackQueue : DispatchQueue
675
+ private let diagnosticsEngine : DiagnosticsEngine
676
+
677
+ #if os(Linux) || os(Windows) || os(Android)
678
+ private let httpClient : HTTPClient
679
+ #endif
680
+
681
+ /// Initializes a `AppleSwiftPackageCollectionCertificatePolicy`.
682
+ /// - Parameters:
683
+ /// - trustedRootCertsDir: On Apple platforms, all root certificates that come preinstalled with the OS are automatically trusted.
684
+ /// Users may specify additional certificates to trust by placing them in `trustedRootCertsDir` and
685
+ /// configure the signing tool or SwiftPM to use it. On non-Apple platforms, only trust root certificates in
686
+ /// `trustedRootCertsDir` are trusted.
687
+ /// - additionalTrustedRootCerts: Root certificates to be trusted in addition to those in `trustedRootCertsDir`. The difference
688
+ /// between this and `trustedRootCertsDir` is that the latter is user configured and dynamic,
689
+ /// while this is configured by SwiftPM and static.
690
+ /// - expectedSubjectUserID: The subject user ID that must match if specified.
691
+ /// - callbackQueue: The `DispatchQueue` to use for callbacks
692
+ /// - diagnosticsEngine: The `DiagnosticsEngine` for emitting warnings and errors.
693
+ init ( trustedRootCertsDir: URL ? , additionalTrustedRootCerts: [ Certificate ] ? , expectedSubjectUserID: String ? = nil , callbackQueue: DispatchQueue , diagnosticsEngine: DiagnosticsEngine ) {
694
+ #if !(os(macOS) || os(Linux) || os(Windows) || os(Android))
695
+ fatalError ( " Unsupported: \( #function) " )
696
+ #else
697
+ var trustedRoots = [ Certificate] ( )
698
+ if let trustedRootCertsDir = trustedRootCertsDir {
699
+ trustedRoots. append ( contentsOf: Self . loadCerts ( at: trustedRootCertsDir, diagnosticsEngine: diagnosticsEngine) )
700
+ }
701
+ if let additionalTrustedRootCerts = additionalTrustedRootCerts {
702
+ trustedRoots. append ( contentsOf: additionalTrustedRootCerts)
703
+ }
704
+ self . trustedRoots = trustedRoots. isEmpty ? nil : trustedRoots
705
+ self . expectedSubjectUserID = expectedSubjectUserID
706
+ self . callbackQueue = callbackQueue
707
+ self . diagnosticsEngine = diagnosticsEngine
708
+
709
+ #if os(Linux) || os(Windows) || os(Android)
710
+ self . httpClient = HTTPClient . makeDefault ( callbackQueue: callbackQueue)
711
+ #endif
712
+ #endif
713
+ }
714
+
715
+ func validate( certChain: [ Certificate ] , callback: @escaping ( Result < Void , Error > ) -> Void ) {
716
+ #if !(os(macOS) || os(Linux) || os(Windows) || os(Android))
717
+ fatalError ( " Unsupported: \( #function) " )
718
+ #else
719
+ let wrappedCallback : ( Result < Void , Error > ) -> Void = { result in self . callbackQueue. async { callback ( result) } }
720
+
721
+ guard !certChain. isEmpty else {
722
+ return wrappedCallback ( . failure( CertificatePolicyError . emptyCertChain) )
723
+ }
724
+ // developer.apple.com cert chain is always 3-long
725
+ guard certChain. count == Self . expectedCertChainLength else {
726
+ return wrappedCallback ( . failure( CertificatePolicyError . unexpectedCertChainLength) )
727
+ }
728
+
729
+ do {
730
+ // Check if subject user ID matches
731
+ if let expectedSubjectUserID = self . expectedSubjectUserID {
732
+ guard try certChain [ 0 ] . subject ( ) . userID == expectedSubjectUserID else {
733
+ return wrappedCallback ( . failure( CertificatePolicyError . subjectUserIDMismatch) )
734
+ }
735
+ }
736
+
737
+ // Check marker extension
738
+ guard try self . hasExtension ( oid: appleSwiftPackageCollectionMarker, in: certChain [ 0 ] ) else {
739
+ return wrappedCallback ( . failure( CertificatePolicyError . missingRequiredExtension) )
740
+ }
741
+ guard try self . hasExtension ( oid: appleIntermediateMarker, in: certChain [ 1 ] ) else {
742
+ return wrappedCallback ( . failure( CertificatePolicyError . missingRequiredExtension) )
743
+ }
744
+
745
+ // Must be a code signing certificate
746
+ guard try self . hasExtendedKeyUsage ( . codeSigning, in: certChain [ 0 ] ) else {
747
+ return wrappedCallback ( . failure( CertificatePolicyError . codeSigningCertRequired) )
748
+ }
749
+ // Must support OCSP
750
+ guard try self . supportsOCSP ( certificate: certChain [ 0 ] ) else {
751
+ return wrappedCallback ( . failure( CertificatePolicyError . ocspSupportRequired) )
752
+ }
753
+
754
+ // Verify the cert chain - if it is trusted then cert chain is valid
755
+ #if os(macOS)
756
+ self . verify ( certChain: certChain, anchorCerts: self . trustedRoots, diagnosticsEngine: self . diagnosticsEngine, callbackQueue: self . callbackQueue, callback: callback)
757
+ #elseif os(Linux) || os(Windows) || os(Android)
758
+ self . verify ( certChain: certChain, anchorCerts: self . trustedRoots, httpClient: self . httpClient, diagnosticsEngine: self . diagnosticsEngine, callbackQueue: self . callbackQueue, callback: callback)
759
+ #endif
760
+ } catch {
761
+ return wrappedCallback ( . failure( error) )
762
+ }
763
+ #endif
764
+ }
765
+ }
766
+
767
+ /// Policy for validating developer.apple.com Apple Distribution certificates.
663
768
///
664
769
/// This has the same requirements as `DefaultCertificatePolicy` plus additional
665
770
/// marker extensions for Apple Distribution certifiications.
666
- struct AppleDeveloperCertificatePolicy : CertificatePolicy {
771
+ struct AppleDistributionCertificatePolicy : CertificatePolicy {
667
772
private static let expectedCertChainLength = 3
668
773
669
774
let trustedRoots : [ Certificate ] ?
@@ -676,7 +781,7 @@ struct AppleDeveloperCertificatePolicy: CertificatePolicy {
676
781
private let httpClient : HTTPClient
677
782
#endif
678
783
679
- /// Initializes a `AppleDeveloperCertificatePolicy `.
784
+ /// Initializes a `AppleDistributionCertificatePolicy `.
680
785
/// - Parameters:
681
786
/// - trustedRootCertsDir: On Apple platforms, all root certificates that come preinstalled with the OS are automatically trusted.
682
787
/// Users may specify additional certificates to trust by placing them in `trustedRootCertsDir` and
@@ -764,11 +869,13 @@ struct AppleDeveloperCertificatePolicy: CertificatePolicy {
764
869
765
870
public enum CertificatePolicyKey : Hashable {
766
871
case `default`( subjectUserID: String ? )
872
+ case appleSwiftPackageCollection( subjectUserID: String ? )
767
873
case appleDistribution( subjectUserID: String ? )
768
874
769
875
/// For internal-use only
770
876
case custom
771
877
772
878
public static let `default` = CertificatePolicyKey . default ( subjectUserID: nil )
879
+ public static let appleSwiftPackageCollection = CertificatePolicyKey . appleSwiftPackageCollection ( subjectUserID: nil )
773
880
public static let appleDistribution = CertificatePolicyKey . appleDistribution ( subjectUserID: nil )
774
881
}
0 commit comments