@@ -16,14 +16,21 @@ import struct Foundation.Date
16
16
import struct Foundation. URL
17
17
18
18
import Basics
19
+ @_implementationOnly import SwiftASN1
19
20
@_implementationOnly import X509
20
21
21
22
extension SignatureProviderProtocol {
22
23
func buildPolicySet( configuration: VerifierConfiguration , httpClient: HTTPClient ) -> PolicySet {
23
- var policies = [ VerifierPolicy] ( )
24
-
25
- if case . enabled( let validationTime) = configuration. certificateExpiration {
26
- policies. append ( RFC5280Policy ( validationTime: validationTime ?? Date ( ) ) )
24
+ var policies : [ VerifierPolicy ] = [
25
+ _CodeSigningPolicy ( ) ,
26
+ _ADPCertificatePolicy ( ) ,
27
+ ]
28
+
29
+ switch configuration. certificateExpiration {
30
+ case . enabled( let validationTime) :
31
+ policies. append ( _RFC5280Policy ( validationTime: validationTime, enableExpiryCheck: true ) )
32
+ case . disabled:
33
+ policies. append ( _RFC5280Policy ( validationTime: . none, enableExpiryCheck: false ) )
27
34
}
28
35
29
36
switch configuration. certificateRevocation {
@@ -44,38 +51,99 @@ extension SignatureProviderProtocol {
44
51
}
45
52
}
46
53
54
+ /// Policy for code signing certificates.
55
+ struct _CodeSigningPolicy : VerifierPolicy {
56
+ let verifyingCriticalExtensions : [ ASN1ObjectIdentifier ] = [
57
+ ASN1ObjectIdentifier . X509ExtensionID. keyUsage,
58
+ ASN1ObjectIdentifier . X509ExtensionID. extendedKeyUsage,
59
+ ]
60
+
61
+ func chainMeetsPolicyRequirements( chain: UnverifiedCertificateChain ) async -> PolicyEvaluationResult {
62
+ let isCodeSigning = (
63
+ try ? chain. leaf. extensions. extendedKeyUsage? . contains ( ExtendedKeyUsage . Usage. codeSigning)
64
+ ) ??
65
+ false
66
+ guard isCodeSigning else {
67
+ return . failsToMeetPolicy( reason: " Certificate \( chain. leaf) does not have code signing extended key usage " )
68
+ }
69
+ return . meetsPolicy
70
+ }
71
+ }
72
+
73
+ /// Policy for ADP certificates.
74
+ struct _ADPCertificatePolicy : VerifierPolicy {
75
+ /// Include custom marker extensions (which can be critical) so they would not
76
+ /// be considered unhandled and cause certificate chain validation to fail.
77
+ let verifyingCriticalExtensions : [ ASN1ObjectIdentifier ] = Self . swiftPackageMarkers
78
+ + Self. developmentMarkers
79
+
80
+ // Marker extensions for Swift Package certificate
81
+ private static let swiftPackageMarkers : [ ASN1ObjectIdentifier ] = [
82
+ // This is not a critical extension but including it just in case
83
+ ASN1ObjectIdentifier . NameAttributes. adpSwiftPackageMarker,
84
+ ]
85
+
86
+ // Marker extensions for Development certificate (included for testing)
87
+ private static let developmentMarkers : [ ASN1ObjectIdentifier ] = [
88
+ [ 1 , 2 , 840 , 113_635 , 100 , 6 , 1 , 2 ] ,
89
+ [ 1 , 2 , 840 , 113_635 , 100 , 6 , 1 , 12 ] ,
90
+ ]
91
+
92
+ func chainMeetsPolicyRequirements( chain: UnverifiedCertificateChain ) async -> PolicyEvaluationResult {
93
+ // Not policing anything here. This policy is mainly for
94
+ // listing marker extensions to prevent chain validation
95
+ // from failing prematurely.
96
+ . meetsPolicy
97
+ }
98
+ }
99
+
100
+ struct _RFC5280Policy : VerifierPolicy {
101
+ /// See RFC5280Policy
102
+ public let verifyingCriticalExtensions : [ ASN1ObjectIdentifier ] = [
103
+ . X509ExtensionID. basicConstraints,
104
+ . X509ExtensionID. nameConstraints,
105
+ . X509ExtensionID. keyUsage,
106
+ ]
107
+
108
+ let validationTime : Date ?
109
+ let enableExpiryCheck : Bool
110
+
111
+ func chainMeetsPolicyRequirements( chain: UnverifiedCertificateChain ) async -> PolicyEvaluationResult {
112
+ let policy : RFC5280Policy
113
+ if self . enableExpiryCheck {
114
+ policy = RFC5280Policy ( validationTime: self . validationTime ?? Date ( ) )
115
+ } else {
116
+ // Use leaf.notValidBefore as validationTime to ensure the leaf
117
+ // will pass expiry check. This should work for the chain if the
118
+ // chain follows validity nesting, but if a parent expires before
119
+ // a child does then the expiry check will fail.
120
+ policy = RFC5280Policy ( validationTime: chain. leaf. notValidBefore)
121
+ }
122
+ return policy. chainMeetsPolicyRequirements ( chain: chain)
123
+ }
124
+ }
125
+
47
126
struct _OCSPVerifierPolicy : VerifierPolicy {
48
127
private static let cacheTTL : DispatchTimeInterval = . seconds( 5 * 60 )
49
128
private let cache = ThreadSafeKeyValueStore <
50
129
UnverifiedCertificateChain ,
51
130
( result: PolicyEvaluationResult , expires: DispatchTime )
52
131
> ( )
53
132
54
- private let underlying : OCSPVerifierPolicy < _OCSPRequester >
133
+ private var underlying : OCSPVerifierPolicy < _OCSPRequester >
55
134
private let mode : Mode
56
- private let validationTime : Date
135
+
136
+ let verifyingCriticalExtensions : [ ASN1ObjectIdentifier ] = [ ]
57
137
58
138
init ( httpClient: HTTPClient , mode: Mode , validationTime: Date ? ) {
59
- self . underlying = OCSPVerifierPolicy ( requester: _OCSPRequester ( httpClient: httpClient) )
139
+ self . underlying = OCSPVerifierPolicy (
140
+ requester: _OCSPRequester ( httpClient: httpClient) ,
141
+ validationTime: validationTime ?? Date ( )
142
+ )
60
143
self . mode = mode
61
- self . validationTime = validationTime ?? Date ( )
62
144
}
63
145
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
-
146
+ mutating func chainMeetsPolicyRequirements( chain: UnverifiedCertificateChain ) async -> PolicyEvaluationResult {
79
147
// Look for cached result
80
148
if let cached = self . cache [ chain] , cached. expires < . now( ) {
81
149
return cached. result
0 commit comments