Proyectos de Subversion Iphone Microlearning

Rev

| Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
1 efrain 1
//
2
//  ServerTrustPolicy.swift
3
//
4
//  Copyright (c) 2014-2016 Alamofire Software Foundation (http://alamofire.org/)
5
//
6
//  Permission is hereby granted, free of charge, to any person obtaining a copy
7
//  of this software and associated documentation files (the "Software"), to deal
8
//  in the Software without restriction, including without limitation the rights
9
//  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
//  copies of the Software, and to permit persons to whom the Software is
11
//  furnished to do so, subject to the following conditions:
12
//
13
//  The above copyright notice and this permission notice shall be included in
14
//  all copies or substantial portions of the Software.
15
//
16
//  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
//  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
//  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
//  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
//  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
//  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22
//  THE SOFTWARE.
23
//
24
 
25
import Foundation
26
 
27
/// Responsible for managing the mapping of `ServerTrustEvaluating` values to given hosts.
28
open class ServerTrustManager {
29
    /// Determines whether all hosts for this `ServerTrustManager` must be evaluated. `true` by default.
30
    public let allHostsMustBeEvaluated: Bool
31
 
32
    /// The dictionary of policies mapped to a particular host.
33
    public let evaluators: [String: ServerTrustEvaluating]
34
 
35
    /// Initializes the `ServerTrustManager` instance with the given evaluators.
36
    ///
37
    /// Since different servers and web services can have different leaf certificates, intermediate and even root
38
    /// certificates, it is important to have the flexibility to specify evaluation policies on a per host basis. This
39
    /// allows for scenarios such as using default evaluation for host1, certificate pinning for host2, public key
40
    /// pinning for host3 and disabling evaluation for host4.
41
    ///
42
    /// - Parameters:
43
    ///   - allHostsMustBeEvaluated: The value determining whether all hosts for this instance must be evaluated. `true`
44
    ///                              by default.
45
    ///   - evaluators:              A dictionary of evaluators mapped to hosts.
46
    public init(allHostsMustBeEvaluated: Bool = true, evaluators: [String: ServerTrustEvaluating]) {
47
        self.allHostsMustBeEvaluated = allHostsMustBeEvaluated
48
        self.evaluators = evaluators
49
    }
50
 
51
    #if !(os(Linux) || os(Windows))
52
    /// Returns the `ServerTrustEvaluating` value for the given host, if one is set.
53
    ///
54
    /// By default, this method will return the policy that perfectly matches the given host. Subclasses could override
55
    /// this method and implement more complex mapping implementations such as wildcards.
56
    ///
57
    /// - Parameter host: The host to use when searching for a matching policy.
58
    ///
59
    /// - Returns:        The `ServerTrustEvaluating` value for the given host if found, `nil` otherwise.
60
    /// - Throws:         `AFError.serverTrustEvaluationFailed` if `allHostsMustBeEvaluated` is `true` and no matching
61
    ///                   evaluators are found.
62
    open func serverTrustEvaluator(forHost host: String) throws -> ServerTrustEvaluating? {
63
        guard let evaluator = evaluators[host] else {
64
            if allHostsMustBeEvaluated {
65
                throw AFError.serverTrustEvaluationFailed(reason: .noRequiredEvaluator(host: host))
66
            }
67
 
68
            return nil
69
        }
70
 
71
        return evaluator
72
    }
73
    #endif
74
}
75
 
76
/// A protocol describing the API used to evaluate server trusts.
77
public protocol ServerTrustEvaluating {
78
    #if os(Linux) || os(Windows)
79
    // Implement this once Linux/Windows has API for evaluating server trusts.
80
    #else
81
    /// Evaluates the given `SecTrust` value for the given `host`.
82
    ///
83
    /// - Parameters:
84
    ///   - trust: The `SecTrust` value to evaluate.
85
    ///   - host:  The host for which to evaluate the `SecTrust` value.
86
    ///
87
    /// - Returns: A `Bool` indicating whether the evaluator considers the `SecTrust` value valid for `host`.
88
    func evaluate(_ trust: SecTrust, forHost host: String) throws
89
    #endif
90
}
91
 
92
// MARK: - Server Trust Evaluators
93
 
94
#if !(os(Linux) || os(Windows))
95
/// An evaluator which uses the default server trust evaluation while allowing you to control whether to validate the
96
/// host provided by the challenge. Applications are encouraged to always validate the host in production environments
97
/// to guarantee the validity of the server's certificate chain.
98
public final class DefaultTrustEvaluator: ServerTrustEvaluating {
99
    private let validateHost: Bool
100
 
101
    /// Creates a `DefaultTrustEvaluator`.
102
    ///
103
    /// - Parameter validateHost: Determines whether or not the evaluator should validate the host. `true` by default.
104
    public init(validateHost: Bool = true) {
105
        self.validateHost = validateHost
106
    }
107
 
108
    public func evaluate(_ trust: SecTrust, forHost host: String) throws {
109
        if validateHost {
110
            try trust.af.performValidation(forHost: host)
111
        }
112
 
113
        try trust.af.performDefaultValidation(forHost: host)
114
    }
115
}
116
 
117
/// An evaluator which Uses the default and revoked server trust evaluations allowing you to control whether to validate
118
/// the host provided by the challenge as well as specify the revocation flags for testing for revoked certificates.
119
/// Apple platforms did not start testing for revoked certificates automatically until iOS 10.1, macOS 10.12 and tvOS
120
/// 10.1 which is demonstrated in our TLS tests. Applications are encouraged to always validate the host in production
121
/// environments to guarantee the validity of the server's certificate chain.
122
public final class RevocationTrustEvaluator: ServerTrustEvaluating {
123
    /// Represents the options to be use when evaluating the status of a certificate.
124
    /// Only Revocation Policy Constants are valid, and can be found in [Apple's documentation](https://developer.apple.com/documentation/security/certificate_key_and_trust_services/policies/1563600-revocation_policy_constants).
125
    public struct Options: OptionSet {
126
        /// Perform revocation checking using the CRL (Certification Revocation List) method.
127
        public static let crl = Options(rawValue: kSecRevocationCRLMethod)
128
        /// Consult only locally cached replies; do not use network access.
129
        public static let networkAccessDisabled = Options(rawValue: kSecRevocationNetworkAccessDisabled)
130
        /// Perform revocation checking using OCSP (Online Certificate Status Protocol).
131
        public static let ocsp = Options(rawValue: kSecRevocationOCSPMethod)
132
        /// Prefer CRL revocation checking over OCSP; by default, OCSP is preferred.
133
        public static let preferCRL = Options(rawValue: kSecRevocationPreferCRL)
134
        /// Require a positive response to pass the policy. If the flag is not set, revocation checking is done on a
135
        /// "best attempt" basis, where failure to reach the server is not considered fatal.
136
        public static let requirePositiveResponse = Options(rawValue: kSecRevocationRequirePositiveResponse)
137
        /// Perform either OCSP or CRL checking. The checking is performed according to the method(s) specified in the
138
        /// certificate and the value of `preferCRL`.
139
        public static let any = Options(rawValue: kSecRevocationUseAnyAvailableMethod)
140
 
141
        /// The raw value of the option.
142
        public let rawValue: CFOptionFlags
143
 
144
        /// Creates an `Options` value with the given `CFOptionFlags`.
145
        ///
146
        /// - Parameter rawValue: The `CFOptionFlags` value to initialize with.
147
        public init(rawValue: CFOptionFlags) {
148
            self.rawValue = rawValue
149
        }
150
    }
151
 
152
    private let performDefaultValidation: Bool
153
    private let validateHost: Bool
154
    private let options: Options
155
 
156
    /// Creates a `RevocationTrustEvaluator` using the provided parameters.
157
    ///
158
    /// - Note: Default and host validation will fail when using this evaluator with self-signed certificates. Use
159
    ///         `PinnedCertificatesTrustEvaluator` if you need to use self-signed certificates.
160
    ///
161
    /// - Parameters:
162
    ///   - performDefaultValidation: Determines whether default validation should be performed in addition to
163
    ///                               evaluating the pinned certificates. `true` by default.
164
    ///   - validateHost:             Determines whether or not the evaluator should validate the host, in addition to
165
    ///                               performing the default evaluation, even if `performDefaultValidation` is `false`.
166
    ///                               `true` by default.
167
    ///   - options:                  The `Options` to use to check the revocation status of the certificate. `.any` by
168
    ///                               default.
169
    public init(performDefaultValidation: Bool = true, validateHost: Bool = true, options: Options = .any) {
170
        self.performDefaultValidation = performDefaultValidation
171
        self.validateHost = validateHost
172
        self.options = options
173
    }
174
 
175
    public func evaluate(_ trust: SecTrust, forHost host: String) throws {
176
        if performDefaultValidation {
177
            try trust.af.performDefaultValidation(forHost: host)
178
        }
179
 
180
        if validateHost {
181
            try trust.af.performValidation(forHost: host)
182
        }
183
 
184
        if #available(iOS 12, macOS 10.14, tvOS 12, watchOS 5, *) {
185
            try trust.af.evaluate(afterApplying: SecPolicy.af.revocation(options: options))
186
        } else {
187
            try trust.af.validate(policy: SecPolicy.af.revocation(options: options)) { status, result in
188
                AFError.serverTrustEvaluationFailed(reason: .revocationCheckFailed(output: .init(host, trust, status, result), options: options))
189
            }
190
        }
191
    }
192
}
193
 
194
#if swift(>=5.5)
195
extension ServerTrustEvaluating where Self == RevocationTrustEvaluator {
196
    /// Provides a default `RevocationTrustEvaluator` instance.
197
    public static var revocationChecking: RevocationTrustEvaluator { RevocationTrustEvaluator() }
198
 
199
    /// Creates a `RevocationTrustEvaluator` using the provided parameters.
200
    ///
201
    /// - Note: Default and host validation will fail when using this evaluator with self-signed certificates. Use
202
    ///         `PinnedCertificatesTrustEvaluator` if you need to use self-signed certificates.
203
    ///
204
    /// - Parameters:
205
    ///   - performDefaultValidation: Determines whether default validation should be performed in addition to
206
    ///                               evaluating the pinned certificates. `true` by default.
207
    ///   - validateHost:             Determines whether or not the evaluator should validate the host, in addition
208
    ///                               to performing the default evaluation, even if `performDefaultValidation` is
209
    ///                               `false`. `true` by default.
210
    ///   - options:                  The `Options` to use to check the revocation status of the certificate. `.any`
211
    ///                               by default.
212
    /// - Returns:                    The `RevocationTrustEvaluator`.
213
    public static func revocationChecking(performDefaultValidation: Bool = true,
214
                                          validateHost: Bool = true,
215
                                          options: RevocationTrustEvaluator.Options = .any) -> RevocationTrustEvaluator {
216
        RevocationTrustEvaluator(performDefaultValidation: performDefaultValidation,
217
                                 validateHost: validateHost,
218
                                 options: options)
219
    }
220
}
221
#endif
222
 
223
/// Uses the pinned certificates to validate the server trust. The server trust is considered valid if one of the pinned
224
/// certificates match one of the server certificates. By validating both the certificate chain and host, certificate
225
/// pinning provides a very secure form of server trust validation mitigating most, if not all, MITM attacks.
226
/// Applications are encouraged to always validate the host and require a valid certificate chain in production
227
/// environments.
228
public final class PinnedCertificatesTrustEvaluator: ServerTrustEvaluating {
229
    private let certificates: [SecCertificate]
230
    private let acceptSelfSignedCertificates: Bool
231
    private let performDefaultValidation: Bool
232
    private let validateHost: Bool
233
 
234
    /// Creates a `PinnedCertificatesTrustEvaluator` from the provided parameters.
235
    ///
236
    /// - Parameters:
237
    ///   - certificates:                 The certificates to use to evaluate the trust. All `cer`, `crt`, and `der`
238
    ///                                   certificates in `Bundle.main` by default.
239
    ///   - acceptSelfSignedCertificates: Adds the provided certificates as anchors for the trust evaluation, allowing
240
    ///                                   self-signed certificates to pass. `false` by default. THIS SETTING SHOULD BE
241
    ///                                   FALSE IN PRODUCTION!
242
    ///   - performDefaultValidation:     Determines whether default validation should be performed in addition to
243
    ///                                   evaluating the pinned certificates. `true` by default.
244
    ///   - validateHost:                 Determines whether or not the evaluator should validate the host, in addition
245
    ///                                   to performing the default evaluation, even if `performDefaultValidation` is
246
    ///                                   `false`. `true` by default.
247
    public init(certificates: [SecCertificate] = Bundle.main.af.certificates,
248
                acceptSelfSignedCertificates: Bool = false,
249
                performDefaultValidation: Bool = true,
250
                validateHost: Bool = true) {
251
        self.certificates = certificates
252
        self.acceptSelfSignedCertificates = acceptSelfSignedCertificates
253
        self.performDefaultValidation = performDefaultValidation
254
        self.validateHost = validateHost
255
    }
256
 
257
    public func evaluate(_ trust: SecTrust, forHost host: String) throws {
258
        guard !certificates.isEmpty else {
259
            throw AFError.serverTrustEvaluationFailed(reason: .noCertificatesFound)
260
        }
261
 
262
        if acceptSelfSignedCertificates {
263
            try trust.af.setAnchorCertificates(certificates)
264
        }
265
 
266
        if performDefaultValidation {
267
            try trust.af.performDefaultValidation(forHost: host)
268
        }
269
 
270
        if validateHost {
271
            try trust.af.performValidation(forHost: host)
272
        }
273
 
274
        let serverCertificatesData = Set(trust.af.certificateData)
275
        let pinnedCertificatesData = Set(certificates.af.data)
276
        let pinnedCertificatesInServerData = !serverCertificatesData.isDisjoint(with: pinnedCertificatesData)
277
        if !pinnedCertificatesInServerData {
278
            throw AFError.serverTrustEvaluationFailed(reason: .certificatePinningFailed(host: host,
279
                                                                                        trust: trust,
280
                                                                                        pinnedCertificates: certificates,
281
                                                                                        serverCertificates: trust.af.certificates))
282
        }
283
    }
284
}
285
 
286
#if swift(>=5.5)
287
extension ServerTrustEvaluating where Self == PinnedCertificatesTrustEvaluator {
288
    /// Provides a default `PinnedCertificatesTrustEvaluator` instance.
289
    public static var pinnedCertificates: PinnedCertificatesTrustEvaluator { PinnedCertificatesTrustEvaluator() }
290
 
291
    /// Creates a `PinnedCertificatesTrustEvaluator` using the provided parameters.
292
    ///
293
    /// - Parameters:
294
    ///   - certificates:                 The certificates to use to evaluate the trust. All `cer`, `crt`, and `der`
295
    ///                                   certificates in `Bundle.main` by default.
296
    ///   - acceptSelfSignedCertificates: Adds the provided certificates as anchors for the trust evaluation, allowing
297
    ///                                   self-signed certificates to pass. `false` by default. THIS SETTING SHOULD BE
298
    ///                                   FALSE IN PRODUCTION!
299
    ///   - performDefaultValidation:     Determines whether default validation should be performed in addition to
300
    ///                                   evaluating the pinned certificates. `true` by default.
301
    ///   - validateHost:                 Determines whether or not the evaluator should validate the host, in addition
302
    ///                                   to performing the default evaluation, even if `performDefaultValidation` is
303
    ///                                   `false`. `true` by default.
304
    public static func pinnedCertificates(certificates: [SecCertificate] = Bundle.main.af.certificates,
305
                                          acceptSelfSignedCertificates: Bool = false,
306
                                          performDefaultValidation: Bool = true,
307
                                          validateHost: Bool = true) -> PinnedCertificatesTrustEvaluator {
308
        PinnedCertificatesTrustEvaluator(certificates: certificates,
309
                                         acceptSelfSignedCertificates: acceptSelfSignedCertificates,
310
                                         performDefaultValidation: performDefaultValidation,
311
                                         validateHost: validateHost)
312
    }
313
}
314
#endif
315
 
316
/// Uses the pinned public keys to validate the server trust. The server trust is considered valid if one of the pinned
317
/// public keys match one of the server certificate public keys. By validating both the certificate chain and host,
318
/// public key pinning provides a very secure form of server trust validation mitigating most, if not all, MITM attacks.
319
/// Applications are encouraged to always validate the host and require a valid certificate chain in production
320
/// environments.
321
public final class PublicKeysTrustEvaluator: ServerTrustEvaluating {
322
    private let keys: [SecKey]
323
    private let performDefaultValidation: Bool
324
    private let validateHost: Bool
325
 
326
    /// Creates a `PublicKeysTrustEvaluator` from the provided parameters.
327
    ///
328
    /// - Note: Default and host validation will fail when using this evaluator with self-signed certificates. Use
329
    ///         `PinnedCertificatesTrustEvaluator` if you need to use self-signed certificates.
330
    ///
331
    /// - Parameters:
332
    ///   - keys:                     The `SecKey`s to use to validate public keys. Defaults to the public keys of all
333
    ///                               certificates included in the main bundle.
334
    ///   - performDefaultValidation: Determines whether default validation should be performed in addition to
335
    ///                               evaluating the pinned certificates. `true` by default.
336
    ///   - validateHost:             Determines whether or not the evaluator should validate the host, in addition to
337
    ///                               performing the default evaluation, even if `performDefaultValidation` is `false`.
338
    ///                               `true` by default.
339
    public init(keys: [SecKey] = Bundle.main.af.publicKeys,
340
                performDefaultValidation: Bool = true,
341
                validateHost: Bool = true) {
342
        self.keys = keys
343
        self.performDefaultValidation = performDefaultValidation
344
        self.validateHost = validateHost
345
    }
346
 
347
    public func evaluate(_ trust: SecTrust, forHost host: String) throws {
348
        guard !keys.isEmpty else {
349
            throw AFError.serverTrustEvaluationFailed(reason: .noPublicKeysFound)
350
        }
351
 
352
        if performDefaultValidation {
353
            try trust.af.performDefaultValidation(forHost: host)
354
        }
355
 
356
        if validateHost {
357
            try trust.af.performValidation(forHost: host)
358
        }
359
 
360
        let pinnedKeysInServerKeys: Bool = {
361
            for serverPublicKey in trust.af.publicKeys {
362
                for pinnedPublicKey in keys {
363
                    if serverPublicKey == pinnedPublicKey {
364
                        return true
365
                    }
366
                }
367
            }
368
            return false
369
        }()
370
 
371
        if !pinnedKeysInServerKeys {
372
            throw AFError.serverTrustEvaluationFailed(reason: .publicKeyPinningFailed(host: host,
373
                                                                                      trust: trust,
374
                                                                                      pinnedKeys: keys,
375
                                                                                      serverKeys: trust.af.publicKeys))
376
        }
377
    }
378
}
379
 
380
#if swift(>=5.5)
381
extension ServerTrustEvaluating where Self == PublicKeysTrustEvaluator {
382
    /// Provides a default `PublicKeysTrustEvaluator` instance.
383
    public static var publicKeys: PublicKeysTrustEvaluator { PublicKeysTrustEvaluator() }
384
 
385
    /// Creates a `PublicKeysTrustEvaluator` from the provided parameters.
386
    ///
387
    /// - Note: Default and host validation will fail when using this evaluator with self-signed certificates. Use
388
    ///         `PinnedCertificatesTrustEvaluator` if you need to use self-signed certificates.
389
    ///
390
    /// - Parameters:
391
    ///   - keys:                     The `SecKey`s to use to validate public keys. Defaults to the public keys of all
392
    ///                               certificates included in the main bundle.
393
    ///   - performDefaultValidation: Determines whether default validation should be performed in addition to
394
    ///                               evaluating the pinned certificates. `true` by default.
395
    ///   - validateHost:             Determines whether or not the evaluator should validate the host, in addition to
396
    ///                               performing the default evaluation, even if `performDefaultValidation` is `false`.
397
    ///                               `true` by default.
398
    public static func publicKeys(keys: [SecKey] = Bundle.main.af.publicKeys,
399
                                  performDefaultValidation: Bool = true,
400
                                  validateHost: Bool = true) -> PublicKeysTrustEvaluator {
401
        PublicKeysTrustEvaluator(keys: keys, performDefaultValidation: performDefaultValidation, validateHost: validateHost)
402
    }
403
}
404
#endif
405
 
406
/// Uses the provided evaluators to validate the server trust. The trust is only considered valid if all of the
407
/// evaluators consider it valid.
408
public final class CompositeTrustEvaluator: ServerTrustEvaluating {
409
    private let evaluators: [ServerTrustEvaluating]
410
 
411
    /// Creates a `CompositeTrustEvaluator` from the provided evaluators.
412
    ///
413
    /// - Parameter evaluators: The `ServerTrustEvaluating` values used to evaluate the server trust.
414
    public init(evaluators: [ServerTrustEvaluating]) {
415
        self.evaluators = evaluators
416
    }
417
 
418
    public func evaluate(_ trust: SecTrust, forHost host: String) throws {
419
        try evaluators.evaluate(trust, forHost: host)
420
    }
421
}
422
 
423
#if swift(>=5.5)
424
extension ServerTrustEvaluating where Self == CompositeTrustEvaluator {
425
    /// Creates a `CompositeTrustEvaluator` from the provided evaluators.
426
    ///
427
    /// - Parameter evaluators: The `ServerTrustEvaluating` values used to evaluate the server trust.
428
    public static func composite(evaluators: [ServerTrustEvaluating]) -> CompositeTrustEvaluator {
429
        CompositeTrustEvaluator(evaluators: evaluators)
430
    }
431
}
432
#endif
433
 
434
/// Disables all evaluation which in turn will always consider any server trust as valid.
435
///
436
/// - Note: Instead of disabling server trust evaluation, it's a better idea to configure systems to properly trust test
437
///         certificates, as outlined in [this Apple tech note](https://developer.apple.com/library/archive/qa/qa1948/_index.html).
438
///
439
/// **THIS EVALUATOR SHOULD NEVER BE USED IN PRODUCTION!**
440
@available(*, deprecated, renamed: "DisabledTrustEvaluator", message: "DisabledEvaluator has been renamed DisabledTrustEvaluator.")
441
public typealias DisabledEvaluator = DisabledTrustEvaluator
442
 
443
/// Disables all evaluation which in turn will always consider any server trust as valid.
444
///
445
///
446
/// - Note: Instead of disabling server trust evaluation, it's a better idea to configure systems to properly trust test
447
///         certificates, as outlined in [this Apple tech note](https://developer.apple.com/library/archive/qa/qa1948/_index.html).
448
///
449
/// **THIS EVALUATOR SHOULD NEVER BE USED IN PRODUCTION!**
450
public final class DisabledTrustEvaluator: ServerTrustEvaluating {
451
    /// Creates an instance.
452
    public init() {}
453
 
454
    public func evaluate(_ trust: SecTrust, forHost host: String) throws {}
455
}
456
 
457
// MARK: - Extensions
458
 
459
extension Array where Element == ServerTrustEvaluating {
460
    #if os(Linux) || os(Windows)
461
    // Add this same convenience method for Linux/Windows.
462
    #else
463
    /// Evaluates the given `SecTrust` value for the given `host`.
464
    ///
465
    /// - Parameters:
466
    ///   - trust: The `SecTrust` value to evaluate.
467
    ///   - host:  The host for which to evaluate the `SecTrust` value.
468
    ///
469
    /// - Returns: Whether or not the evaluator considers the `SecTrust` value valid for `host`.
470
    public func evaluate(_ trust: SecTrust, forHost host: String) throws {
471
        for evaluator in self {
472
            try evaluator.evaluate(trust, forHost: host)
473
        }
474
    }
475
    #endif
476
}
477
 
478
extension Bundle: AlamofireExtended {}
479
extension AlamofireExtension where ExtendedType: Bundle {
480
    /// Returns all valid `cer`, `crt`, and `der` certificates in the bundle.
481
    public var certificates: [SecCertificate] {
482
        paths(forResourcesOfTypes: [".cer", ".CER", ".crt", ".CRT", ".der", ".DER"]).compactMap { path in
483
            guard
484
                let certificateData = try? Data(contentsOf: URL(fileURLWithPath: path)) as CFData,
485
                let certificate = SecCertificateCreateWithData(nil, certificateData) else { return nil }
486
 
487
            return certificate
488
        }
489
    }
490
 
491
    /// Returns all public keys for the valid certificates in the bundle.
492
    public var publicKeys: [SecKey] {
493
        certificates.af.publicKeys
494
    }
495
 
496
    /// Returns all pathnames for the resources identified by the provided file extensions.
497
    ///
498
    /// - Parameter types: The filename extensions locate.
499
    ///
500
    /// - Returns:         All pathnames for the given filename extensions.
501
    public func paths(forResourcesOfTypes types: [String]) -> [String] {
502
        Array(Set(types.flatMap { type.paths(forResourcesOfType: $0, inDirectory: nil) }))
503
    }
504
}
505
 
506
extension SecTrust: AlamofireExtended {}
507
extension AlamofireExtension where ExtendedType == SecTrust {
508
    /// Evaluates `self` after applying the `SecPolicy` value provided.
509
    ///
510
    /// - Parameter policy: The `SecPolicy` to apply to `self` before evaluation.
511
    ///
512
    /// - Throws:           Any `Error` from applying the `SecPolicy` or from evaluation.
513
    @available(iOS 12, macOS 10.14, tvOS 12, watchOS 5, *)
514
    public func evaluate(afterApplying policy: SecPolicy) throws {
515
        try apply(policy: policy).af.evaluate()
516
    }
517
 
518
    /// Attempts to validate `self` using the `SecPolicy` provided and transforming any error produced using the closure passed.
519
    ///
520
    /// - Parameters:
521
    ///   - policy:        The `SecPolicy` used to evaluate `self`.
522
    ///   - errorProducer: The closure used transform the failed `OSStatus` and `SecTrustResultType`.
523
    /// - Throws:          Any `Error` from applying the `policy`, or the result of `errorProducer` if validation fails.
524
    @available(iOS, introduced: 10, deprecated: 12, renamed: "evaluate(afterApplying:)")
525
    @available(macOS, introduced: 10.12, deprecated: 10.14, renamed: "evaluate(afterApplying:)")
526
    @available(tvOS, introduced: 10, deprecated: 12, renamed: "evaluate(afterApplying:)")
527
    @available(watchOS, introduced: 3, deprecated: 5, renamed: "evaluate(afterApplying:)")
528
    public func validate(policy: SecPolicy, errorProducer: (_ status: OSStatus, _ result: SecTrustResultType) -> Error) throws {
529
        try apply(policy: policy).af.validate(errorProducer: errorProducer)
530
    }
531
 
532
    /// Applies a `SecPolicy` to `self`, throwing if it fails.
533
    ///
534
    /// - Parameter policy: The `SecPolicy`.
535
    ///
536
    /// - Returns: `self`, with the policy applied.
537
    /// - Throws: An `AFError.serverTrustEvaluationFailed` instance with a `.policyApplicationFailed` reason.
538
    public func apply(policy: SecPolicy) throws -> SecTrust {
539
        let status = SecTrustSetPolicies(type, policy)
540
 
541
        guard status.af.isSuccess else {
542
            throw AFError.serverTrustEvaluationFailed(reason: .policyApplicationFailed(trust: type,
543
                                                                                       policy: policy,
544
                                                                                       status: status))
545
        }
546
 
547
        return type
548
    }
549
 
550
    /// Evaluate `self`, throwing an `Error` if evaluation fails.
551
    ///
552
    /// - Throws: `AFError.serverTrustEvaluationFailed` with reason `.trustValidationFailed` and associated error from
553
    ///           the underlying evaluation.
554
    @available(iOS 12, macOS 10.14, tvOS 12, watchOS 5, *)
555
    public func evaluate() throws {
556
        var error: CFError?
557
        let evaluationSucceeded = SecTrustEvaluateWithError(type, &error)
558
 
559
        if !evaluationSucceeded {
560
            throw AFError.serverTrustEvaluationFailed(reason: .trustEvaluationFailed(error: error))
561
        }
562
    }
563
 
564
    /// Validate `self`, passing any failure values through `errorProducer`.
565
    ///
566
    /// - Parameter errorProducer: The closure used to transform the failed `OSStatus` and `SecTrustResultType` into an
567
    ///                            `Error`.
568
    /// - Throws:                  The `Error` produced by the `errorProducer` closure.
569
    @available(iOS, introduced: 10, deprecated: 12, renamed: "evaluate()")
570
    @available(macOS, introduced: 10.12, deprecated: 10.14, renamed: "evaluate()")
571
    @available(tvOS, introduced: 10, deprecated: 12, renamed: "evaluate()")
572
    @available(watchOS, introduced: 3, deprecated: 5, renamed: "evaluate()")
573
    public func validate(errorProducer: (_ status: OSStatus, _ result: SecTrustResultType) -> Error) throws {
574
        var result = SecTrustResultType.invalid
575
        let status = SecTrustEvaluate(type, &result)
576
 
577
        guard status.af.isSuccess && result.af.isSuccess else {
578
            throw errorProducer(status, result)
579
        }
580
    }
581
 
582
    /// Sets a custom certificate chain on `self`, allowing full validation of a self-signed certificate and its chain.
583
    ///
584
    /// - Parameter certificates: The `SecCertificate`s to add to the chain.
585
    /// - Throws:                 Any error produced when applying the new certificate chain.
586
    public func setAnchorCertificates(_ certificates: [SecCertificate]) throws {
587
        // Add additional anchor certificates.
588
        let status = SecTrustSetAnchorCertificates(type, certificates as CFArray)
589
        guard status.af.isSuccess else {
590
            throw AFError.serverTrustEvaluationFailed(reason: .settingAnchorCertificatesFailed(status: status,
591
                                                                                               certificates: certificates))
592
        }
593
 
594
        // Trust only the set anchor certs.
595
        let onlyStatus = SecTrustSetAnchorCertificatesOnly(type, true)
596
        guard onlyStatus.af.isSuccess else {
597
            throw AFError.serverTrustEvaluationFailed(reason: .settingAnchorCertificatesFailed(status: onlyStatus,
598
                                                                                               certificates: certificates))
599
        }
600
    }
601
 
602
    /// The public keys contained in `self`.
603
    public var publicKeys: [SecKey] {
604
        certificates.af.publicKeys
605
    }
606
 
607
    #if swift(>=5.5) // Xcode 13 / 2021 SDKs.
608
    /// The `SecCertificate`s contained in `self`.
609
    public var certificates: [SecCertificate] {
610
        if #available(iOS 15, macOS 12, tvOS 15, watchOS 8, *) {
611
            return (SecTrustCopyCertificateChain(type) as? [SecCertificate]) ?? []
612
        } else {
613
            return (0..<SecTrustGetCertificateCount(type)).compactMap { index in
614
                SecTrustGetCertificateAtIndex(type, index)
615
            }
616
        }
617
    }
618
    #else
619
    /// The `SecCertificate`s contained in `self`.
620
    public var certificates: [SecCertificate] {
621
        (0..<SecTrustGetCertificateCount(type)).compactMap { index in
622
            SecTrustGetCertificateAtIndex(type, index)
623
        }
624
    }
625
    #endif
626
 
627
    /// The `Data` values for all certificates contained in `self`.
628
    public var certificateData: [Data] {
629
        certificates.af.data
630
    }
631
 
632
    /// Validates `self` after applying `SecPolicy.af.default`. This evaluation does not validate the hostname.
633
    ///
634
    /// - Parameter host: The hostname, used only in the error output if validation fails.
635
    /// - Throws: An `AFError.serverTrustEvaluationFailed` instance with a `.defaultEvaluationFailed` reason.
636
    public func performDefaultValidation(forHost host: String) throws {
637
        if #available(iOS 12, macOS 10.14, tvOS 12, watchOS 5, *) {
638
            try evaluate(afterApplying: SecPolicy.af.default)
639
        } else {
640
            try validate(policy: SecPolicy.af.default) { status, result in
641
                AFError.serverTrustEvaluationFailed(reason: .defaultEvaluationFailed(output: .init(host, type, status, result)))
642
            }
643
        }
644
    }
645
 
646
    /// Validates `self` after applying `SecPolicy.af.hostname(host)`, which performs the default validation as well as
647
    /// hostname validation.
648
    ///
649
    /// - Parameter host: The hostname to use in the validation.
650
    /// - Throws:         An `AFError.serverTrustEvaluationFailed` instance with a `.defaultEvaluationFailed` reason.
651
    public func performValidation(forHost host: String) throws {
652
        if #available(iOS 12, macOS 10.14, tvOS 12, watchOS 5, *) {
653
            try evaluate(afterApplying: SecPolicy.af.hostname(host))
654
        } else {
655
            try validate(policy: SecPolicy.af.hostname(host)) { status, result in
656
                AFError.serverTrustEvaluationFailed(reason: .hostValidationFailed(output: .init(host, type, status, result)))
657
            }
658
        }
659
    }
660
}
661
 
662
extension SecPolicy: AlamofireExtended {}
663
extension AlamofireExtension where ExtendedType == SecPolicy {
664
    /// Creates a `SecPolicy` instance which will validate server certificates but not require a host name match.
665
    public static let `default` = SecPolicyCreateSSL(true, nil)
666
 
667
    /// Creates a `SecPolicy` instance which will validate server certificates and much match the provided hostname.
668
    ///
669
    /// - Parameter hostname: The hostname to validate against.
670
    ///
671
    /// - Returns:            The `SecPolicy`.
672
    public static func hostname(_ hostname: String) -> SecPolicy {
673
        SecPolicyCreateSSL(true, hostname as CFString)
674
    }
675
 
676
    /// Creates a `SecPolicy` which checks the revocation of certificates.
677
    ///
678
    /// - Parameter options: The `RevocationTrustEvaluator.Options` for evaluation.
679
    ///
680
    /// - Returns:           The `SecPolicy`.
681
    /// - Throws:            An `AFError.serverTrustEvaluationFailed` error with reason `.revocationPolicyCreationFailed`
682
    ///                      if the policy cannot be created.
683
    public static func revocation(options: RevocationTrustEvaluator.Options) throws -> SecPolicy {
684
        guard let policy = SecPolicyCreateRevocation(options.rawValue) else {
685
            throw AFError.serverTrustEvaluationFailed(reason: .revocationPolicyCreationFailed)
686
        }
687
 
688
        return policy
689
    }
690
}
691
 
692
extension Array: AlamofireExtended {}
693
extension AlamofireExtension where ExtendedType == [SecCertificate] {
694
    /// All `Data` values for the contained `SecCertificate`s.
695
    public var data: [Data] {
696
        type.map { SecCertificateCopyData($0) as Data }
697
    }
698
 
699
    /// All public `SecKey` values for the contained `SecCertificate`s.
700
    public var publicKeys: [SecKey] {
701
        type.compactMap(\.af.publicKey)
702
    }
703
}
704
 
705
extension SecCertificate: AlamofireExtended {}
706
extension AlamofireExtension where ExtendedType == SecCertificate {
707
    /// The public key for `self`, if it can be extracted.
708
    ///
709
    /// - Note: On 2020 OSes and newer, only RSA and ECDSA keys are supported.
710
    ///
711
    public var publicKey: SecKey? {
712
        let policy = SecPolicyCreateBasicX509()
713
        var trust: SecTrust?
714
        let trustCreationStatus = SecTrustCreateWithCertificates(type, policy, &trust)
715
 
716
        guard let createdTrust = trust, trustCreationStatus == errSecSuccess else { return nil }
717
 
718
        if #available(iOS 14, macOS 11, tvOS 14, watchOS 7, *) {
719
            return SecTrustCopyKey(createdTrust)
720
        } else {
721
            return SecTrustCopyPublicKey(createdTrust)
722
        }
723
    }
724
}
725
 
726
extension OSStatus: AlamofireExtended {}
727
extension AlamofireExtension where ExtendedType == OSStatus {
728
    /// Returns whether `self` is `errSecSuccess`.
729
    public var isSuccess: Bool { type == errSecSuccess }
730
}
731
 
732
extension SecTrustResultType: AlamofireExtended {}
733
extension AlamofireExtension where ExtendedType == SecTrustResultType {
734
    /// Returns whether `self is `.unspecified` or `.proceed`.
735
    public var isSuccess: Bool {
736
        type == .unspecified || type == .proceed
737
    }
738
}
739
#endif