@@ -179,7 +179,8 @@ extension CertificatePolicy {
179
179
180
180
if certChain. count >= 1 , let httpClient = httpClient {
181
181
// Whether cert chain can be trusted depends on OCSP result
182
- self . BoringSSL_OCSP_isGood ( certificate: certChain [ 0 ] , issuer: certChain [ 1 ] , httpClient: httpClient, callbackQueue: callbackQueue, callback: callback)
182
+ self . BoringSSL_OCSP_isGood ( certificate: certChain [ 0 ] , issuer: certChain [ 1 ] , httpClient: httpClient,
183
+ diagnosticsEngine: diagnosticsEngine, callbackQueue: callbackQueue, callback: callback)
183
184
} else {
184
185
wrappedCallback ( . success( ( ) ) )
185
186
}
@@ -188,6 +189,7 @@ extension CertificatePolicy {
188
189
private func BoringSSL_OCSP_isGood( certificate: Certificate ,
189
190
issuer: Certificate ,
190
191
httpClient: HTTPClient ,
192
+ diagnosticsEngine: DiagnosticsEngine ,
191
193
callbackQueue: DispatchQueue ,
192
194
callback: @escaping ( Result < Void , Error > ) -> Void ) {
193
195
let wrappedCallback : ( Result < Void , Error > ) -> Void = { result in callbackQueue. async { callback ( result) } }
@@ -224,13 +226,14 @@ extension CertificatePolicy {
224
226
225
227
let requestData = Data ( UnsafeBufferPointer ( start: out. pointee, count: count) )
226
228
227
- let results = ThreadSafeArrayStore < Bool > ( )
229
+ let results = ThreadSafeArrayStore < Result < Bool , Error > > ( )
228
230
let group = DispatchGroup ( )
229
231
230
232
// Query each OCSP responder and record result
231
233
for index in 0 ..< ocspURLCount {
232
234
guard let urlStr = CCryptoBoringSSL_sk_OPENSSL_STRING_value ( ocspURLs, numericCast ( index) ) ,
233
235
let url = String ( validatingUTF8: urlStr) . flatMap ( { URL ( string: $0) } ) else {
236
+ results. append ( . failure( OCSPError . badURL) )
234
237
continue
235
238
}
236
239
@@ -246,16 +249,20 @@ extension CertificatePolicy {
246
249
defer { group. leave ( ) }
247
250
248
251
switch result {
249
- case . failure:
250
- return
252
+ case . failure( let error ) :
253
+ results . append ( . failure ( error ) )
251
254
case . success( let response) :
252
- guard let responseData = response. body else { return }
255
+ guard let responseData = response. body else {
256
+ results. append ( . failure( OCSPError . emptyResponseBody) )
257
+ return
258
+ }
253
259
254
260
let bytes = responseData. copyBytes ( )
255
261
// Convert response to bio then OCSP response
256
262
guard let bio = CCryptoBoringSSL_BIO_new ( CCryptoBoringSSL_BIO_s_mem ( ) ) ,
257
263
CCryptoBoringSSL_BIO_write ( bio, bytes, numericCast ( bytes. count) ) > 0 ,
258
264
let response = d2i_OCSP_RESPONSE_bio ( bio, nil ) else {
265
+ results. append ( . failure( OCSPError . responseConversionFailure) )
259
266
return
260
267
}
261
268
defer { CCryptoBoringSSL_BIO_free ( bio) }
@@ -266,6 +273,7 @@ extension CertificatePolicy {
266
273
CCryptoBoringSSL_OBJ_obj2nid ( response. pointee. responseBytes. pointee. responseType) == NID_id_pkix_OCSP_basic,
267
274
let basicResp = OCSP_response_get1_basic ( response) ,
268
275
let basicRespData = basicResp. pointee. tbsResponseData? . pointee else {
276
+ results. append ( . failure( OCSPError . badResponse) )
269
277
return
270
278
}
271
279
defer { OCSP_BASICRESP_free ( basicResp) }
@@ -274,11 +282,12 @@ extension CertificatePolicy {
274
282
for i in 0 ..< sk_OCSP_SINGLERESP_num ( basicRespData. responses) {
275
283
guard let singleResp = sk_OCSP_SINGLERESP_value ( basicRespData. responses, numericCast ( i) ) ,
276
284
let certStatus = singleResp. pointee. certStatus else {
277
- continue
285
+ results. append ( . failure( OCSPError . badResponse) )
286
+ return
278
287
}
279
288
280
289
// Is the certificate in good status?
281
- results. append ( certStatus. pointee. type == V_OCSP_CERTSTATUS_GOOD)
290
+ results. append ( . success ( certStatus. pointee. type == V_OCSP_CERTSTATUS_GOOD) )
282
291
break
283
292
}
284
293
}
@@ -287,11 +296,12 @@ extension CertificatePolicy {
287
296
288
297
group. notify ( queue: callbackQueue) {
289
298
// If there's no result then something must have gone wrong
290
- guard !results. isEmpty else {
299
+ guard !results. isEmpty, results. compactMap ( { $0. failure } ) . isEmpty else {
300
+ diagnosticsEngine. emit ( error: " OCSP failed. All results: \( results) " )
291
301
return wrappedCallback ( . failure( CertificatePolicyError . ocspFailure) )
292
302
}
293
303
// Is there response "bad status" response?
294
- guard results. get ( ) . first ( where: { !$0 } ) == nil else {
304
+ guard results. compactMap ( { $0 . success } ) . first ( where: { !$0 } ) == nil else {
295
305
return wrappedCallback ( . failure( CertificatePolicyError . invalidCertChain) )
296
306
}
297
307
wrappedCallback ( . success( ( ) ) )
@@ -411,6 +421,13 @@ enum CertificatePolicyError: Error, Equatable {
411
421
case ocspFailure
412
422
}
413
423
424
+ private enum OCSPError : Error {
425
+ case badURL
426
+ case emptyResponseBody
427
+ case responseConversionFailure
428
+ case badResponse
429
+ }
430
+
414
431
// MARK: - Certificate policies
415
432
416
433
/// Default policy for validating certificates used to sign package collections.
@@ -459,9 +476,7 @@ struct DefaultCertificatePolicy: CertificatePolicy {
459
476
self . diagnosticsEngine = diagnosticsEngine
460
477
461
478
#if !os(macOS)
462
- var httpClientConfig = HTTPClientConfiguration ( )
463
- httpClientConfig. callbackQueue = callbackQueue
464
- self . httpClient = HTTPClient ( configuration: httpClientConfig)
479
+ self . httpClient = HTTPClient . makeDefault ( callbackQueue: callbackQueue)
465
480
#endif
466
481
}
467
482
@@ -547,9 +562,7 @@ struct AppleDeveloperCertificatePolicy: CertificatePolicy {
547
562
self . diagnosticsEngine = diagnosticsEngine
548
563
549
564
#if !os(macOS)
550
- var httpClientConfig = HTTPClientConfiguration ( )
551
- httpClientConfig. callbackQueue = callbackQueue
552
- self . httpClient = HTTPClient ( configuration: httpClientConfig)
565
+ self . httpClient = HTTPClient . makeDefault ( callbackQueue: callbackQueue)
553
566
#endif
554
567
}
555
568
@@ -611,3 +624,34 @@ public enum CertificatePolicyKey: Equatable, Hashable {
611
624
public static let `default` = CertificatePolicyKey . default ( subjectUserID: nil )
612
625
public static let appleDistribution = CertificatePolicyKey . appleDistribution ( subjectUserID: nil )
613
626
}
627
+
628
+ // MARK: - Utilities
629
+
630
+ private extension Result {
631
+ var failure : Failure ? {
632
+ switch self {
633
+ case . failure( let failure) :
634
+ return failure
635
+ case . success:
636
+ return nil
637
+ }
638
+ }
639
+
640
+ var success : Success ? {
641
+ switch self {
642
+ case . failure:
643
+ return nil
644
+ case . success( let value) :
645
+ return value
646
+ }
647
+ }
648
+ }
649
+
650
+ private extension HTTPClient {
651
+ static func makeDefault( callbackQueue: DispatchQueue ) -> HTTPClient {
652
+ var httpClientConfig = HTTPClientConfiguration ( )
653
+ httpClientConfig. callbackQueue = callbackQueue
654
+ httpClientConfig. requestTimeout = . seconds( 1 )
655
+ return HTTPClient ( configuration: httpClientConfig)
656
+ }
657
+ }
0 commit comments