@@ -16,149 +16,99 @@ import struct Foundation.Date
16
16
import struct Foundation. URL
17
17
18
18
import Basics
19
- @_implementationOnly import SwiftASN1
20
- @_implementationOnly @_spi ( DisableValidityCheck) import X509
19
+ @_implementationOnly import X509
21
20
22
21
extension SignatureProviderProtocol {
23
22
func buildPolicySet( configuration: VerifierConfiguration , httpClient: HTTPClient ) -> PolicySet {
24
- var policies : [ VerifierPolicy ] = [
25
- _CodeSigningPolicy ( ) ,
26
- _ADPCertificatePolicy ( ) ,
27
- ]
28
-
29
- let now = Date ( )
30
- switch ( configuration. certificateExpiration, configuration. certificateRevocation) {
31
- case ( . enabled( let expiryValidationTime) , . strict( let revocationValidationTime) ) :
32
- policies. append ( RFC5280Policy ( validationTime: expiryValidationTime ?? now) )
33
- policies
34
- . append ( _OCSPVerifierPolicy (
35
- failureMode: . hard,
36
- httpClient: httpClient,
37
- validationTime: revocationValidationTime ?? now
38
- ) )
39
- case ( . enabled( let expiryValidationTime) , . allowSoftFail( let revocationValidationTime) ) :
40
- policies. append ( RFC5280Policy ( validationTime: expiryValidationTime ?? now) )
41
- policies
42
- . append ( _OCSPVerifierPolicy (
43
- failureMode: . soft,
44
- httpClient: httpClient,
45
- validationTime: revocationValidationTime ?? now
46
- ) )
47
- case ( . enabled( let expiryValidationTime) , . disabled) :
48
- policies. append ( RFC5280Policy ( validationTime: expiryValidationTime ?? now) )
49
- case ( . disabled, . strict( let revocationValidationTime) ) :
50
- // Always do expiry check (and before) if revocation check is enabled
51
- policies. append ( RFC5280Policy ( validationTime: revocationValidationTime ?? now) )
52
- policies
53
- . append ( _OCSPVerifierPolicy (
54
- failureMode: . hard,
55
- httpClient: httpClient,
56
- validationTime: revocationValidationTime ?? now
57
- ) )
58
- case ( . disabled, . allowSoftFail( let revocationValidationTime) ) :
59
- // Always do expiry check (and before) if revocation check is enabled
60
- policies. append ( RFC5280Policy ( validationTime: revocationValidationTime ?? now) )
23
+ var policies = [ VerifierPolicy] ( )
24
+
25
+ if case . enabled( let validationTime) = configuration. certificateExpiration {
26
+ policies. append ( RFC5280Policy ( validationTime: validationTime ?? Date ( ) ) )
27
+ }
28
+
29
+ switch configuration. certificateRevocation {
30
+ case . strict( let validationTime) :
31
+ policies. append ( _OCSPVerifierPolicy ( httpClient: httpClient, mode: . strict, validationTime: validationTime) )
32
+ case . allowSoftFail( let validationTime) :
61
33
policies
62
34
. append ( _OCSPVerifierPolicy (
63
- failureMode: . soft,
64
35
httpClient: httpClient,
65
- validationTime: revocationValidationTime ?? now
36
+ mode: . allowSoftFail,
37
+ validationTime: validationTime
66
38
) )
67
- case ( . disabled, . disabled) :
68
- // We should still do basic certificate validations even if expiry check is disabled
69
- policies. append ( RFC5280Policy . withValidityCheckDisabled ( ) )
39
+ case . disabled:
40
+ ( )
70
41
}
71
42
72
43
return PolicySet ( policies: policies)
73
44
}
74
45
}
75
46
76
- /// Policy for code signing certificates.
77
- struct _CodeSigningPolicy : VerifierPolicy {
78
- let verifyingCriticalExtensions : [ ASN1ObjectIdentifier ] = [
79
- ASN1ObjectIdentifier . X509ExtensionID. keyUsage,
80
- ASN1ObjectIdentifier . X509ExtensionID. extendedKeyUsage,
81
- ]
82
-
83
- func chainMeetsPolicyRequirements( chain: UnverifiedCertificateChain ) async -> PolicyEvaluationResult {
84
- let isCodeSigning = (
85
- try ? chain. leaf. extensions. extendedKeyUsage? . contains ( ExtendedKeyUsage . Usage. codeSigning)
86
- ) ??
87
- false
88
- guard isCodeSigning else {
89
- return . failsToMeetPolicy( reason: " Certificate \( chain. leaf) does not have code signing extended key usage " )
90
- }
91
- return . meetsPolicy
92
- }
93
- }
94
-
95
- /// Policy for ADP certificates.
96
- struct _ADPCertificatePolicy : VerifierPolicy {
97
- /// Include custom marker extensions (which can be critical) so they would not
98
- /// be considered unhandled and cause certificate chain validation to fail.
99
- let verifyingCriticalExtensions : [ ASN1ObjectIdentifier ] = Self . swiftPackageMarkers
100
- + Self. developmentMarkers
101
-
102
- // Marker extensions for Swift Package certificate
103
- private static let swiftPackageMarkers : [ ASN1ObjectIdentifier ] = [
104
- // This is not a critical extension but including it just in case
105
- ASN1ObjectIdentifier . NameAttributes. adpSwiftPackageMarker,
106
- ]
107
-
108
- // Marker extensions for Development certificate (included for testing)
109
- private static let developmentMarkers : [ ASN1ObjectIdentifier ] = [
110
- [ 1 , 2 , 840 , 113_635 , 100 , 6 , 1 , 2 ] ,
111
- [ 1 , 2 , 840 , 113_635 , 100 , 6 , 1 , 12 ] ,
112
- ]
113
-
114
- func chainMeetsPolicyRequirements( chain: UnverifiedCertificateChain ) async -> PolicyEvaluationResult {
115
- // Not policing anything here. This policy is mainly for
116
- // listing marker extensions to prevent chain validation
117
- // from failing prematurely.
118
- . meetsPolicy
119
- }
120
- }
121
-
122
47
struct _OCSPVerifierPolicy : VerifierPolicy {
123
48
private static let cacheTTL : DispatchTimeInterval = . seconds( 5 * 60 )
124
49
private let cache = ThreadSafeKeyValueStore <
125
50
UnverifiedCertificateChain ,
126
51
( result: PolicyEvaluationResult , expires: DispatchTime )
127
52
> ( )
128
53
129
- private var underlying : OCSPVerifierPolicy < _OCSPRequester >
130
-
131
- let verifyingCriticalExtensions : [ ASN1ObjectIdentifier ] = [ ]
132
-
133
- /// Initializes an `_OCSPVerifierPolicy` that caches its results.
134
- ///
135
- /// - Parameters:
136
- /// - failureMode: `OCSPFailureMode` that defines policy failure in event of failure.
137
- /// Possible values are `hard` (OCSP request failure and unknown status
138
- /// not allowed) or `soft` (OCSP request failure and unknown status allowed).
139
- /// - httpClient: `HTTPClient` that backs`_OCSPRequester` for making OCSP requests.
140
- /// - validationTime: The time used to decide if the OCSP request is relatively recent. It is
141
- /// considered a failure if the request is too old.
142
- init ( failureMode: OCSPFailureMode , httpClient: HTTPClient , validationTime: Date ) {
143
- self . underlying = OCSPVerifierPolicy (
144
- failureMode: failureMode,
145
- requester: _OCSPRequester ( httpClient: httpClient) ,
146
- validationTime: validationTime
147
- )
54
+ private let underlying : OCSPVerifierPolicy < _OCSPRequester >
55
+ private let mode : Mode
56
+ private let validationTime : Date
57
+
58
+ init ( httpClient: HTTPClient , mode: Mode , validationTime: Date ? ) {
59
+ self . underlying = OCSPVerifierPolicy ( requester: _OCSPRequester ( httpClient: httpClient) )
60
+ self . mode = mode
61
+ self . validationTime = validationTime ?? Date ( )
148
62
}
149
63
150
- mutating func chainMeetsPolicyRequirements( chain: UnverifiedCertificateChain ) async -> PolicyEvaluationResult {
64
+ func chainMeetsPolicyRequirements( chain: UnverifiedCertificateChain ) async -> PolicyEvaluationResult {
65
+ // Check for expiration of the leaf before revocation
66
+ let leaf = chain. leaf
67
+ if leaf. notValidBefore > leaf. notValidAfter {
68
+ return . failsToMeetPolicy(
69
+ reason: " OCSPVerifierPolicy: leaf certificate \( leaf) has invalid expiry, notValidAfter is earlier than notValidBefore "
70
+ )
71
+ }
72
+ if self . validationTime < leaf. notValidBefore {
73
+ return . failsToMeetPolicy( reason: " OCSPVerifierPolicy: leaf certificate \( leaf) is not yet valid " )
74
+ }
75
+ if self . validationTime > leaf. notValidAfter {
76
+ return . failsToMeetPolicy( reason: " OCSPVerifierPolicy: leaf certificate \( leaf) has expired " )
77
+ }
78
+
151
79
// Look for cached result
152
80
if let cached = self . cache [ chain] , cached. expires < . now( ) {
153
81
return cached. result
154
82
}
155
83
156
84
// This makes HTTP requests
157
85
let result = await self . underlying. chainMeetsPolicyRequirements ( chain: chain)
86
+ let actualResult : PolicyEvaluationResult
87
+ switch result {
88
+ case . meetsPolicy:
89
+ actualResult = result
90
+ case . failsToMeetPolicy( let reason) :
91
+ switch self . mode {
92
+ case . strict:
93
+ actualResult = result
94
+ case . allowSoftFail:
95
+ // Allow 'unknown' status and failed OCSP request in this mode
96
+ if reason. lowercased ( ) . contains ( " revoked through ocsp " ) {
97
+ actualResult = result
98
+ } else {
99
+ actualResult = . meetsPolicy
100
+ }
101
+ }
102
+ }
158
103
159
104
// Save result to cache
160
- self . cache [ chain] = ( result: result, expires: . now( ) + Self. cacheTTL)
161
- return result
105
+ self . cache [ chain] = ( result: actualResult, expires: . now( ) + Self. cacheTTL)
106
+ return actualResult
107
+ }
108
+
109
+ enum Mode {
110
+ case strict
111
+ case allowSoftFail
162
112
}
163
113
}
164
114
0 commit comments