|
86 | 86 | }
|
87 | 87 | }
|
88 | 88 |
|
89 |
| - // the certificate chain |
90 |
| - if self.certificateChain.count > 0 { |
91 |
| - preconditionFailure("TLSConfiguration.certificateChain is not supported") |
92 |
| - } |
93 |
| - |
94 | 89 | // cipher suites
|
95 | 90 | if self.cipherSuites.count > 0 {
|
96 | 91 | // TODO: Requires NIOSSL to provide list of cipher values before we can continue
|
|
102 | 97 | preconditionFailure("TLSConfiguration.keyLogCallback is not supported")
|
103 | 98 | }
|
104 | 99 |
|
| 100 | + // the certificate chain |
| 101 | + if self.certificateChain.count > 0 { |
| 102 | + preconditionFailure("TLSConfiguration.certificateChain is not supported") |
| 103 | + } |
| 104 | + |
105 | 105 | // private key
|
106 | 106 | if self.privateKey != nil {
|
107 | 107 | preconditionFailure("TLSConfiguration.privateKey is not supported")
|
|
110 | 110 | // renegotiation support key is unsupported
|
111 | 111 |
|
112 | 112 | // trust roots
|
113 |
| - if let trustRoots = self.trustRoots { |
114 |
| - guard case .default = trustRoots else { |
115 |
| - preconditionFailure("TLSConfiguration.trustRoots != .default is not supported") |
| 113 | + var secTrustRoots: [SecCertificate]? |
| 114 | + switch trustRoots { |
| 115 | + case .some(.certificates(let certificates)): |
| 116 | + do { |
| 117 | + secTrustRoots = try certificates.compactMap { certificate in |
| 118 | + return try SecCertificateCreateWithData(nil, Data(certificate.toDERBytes()) as CFData) |
| 119 | + } |
| 120 | + } catch { |
| 121 | + // failed to load |
116 | 122 | }
|
| 123 | + case .some(.file): |
| 124 | + preconditionFailure("TLSConfiguration.trustRoots.file is not supported") |
| 125 | + break |
| 126 | + |
| 127 | + case .some(.default), .none: |
| 128 | + break |
117 | 129 | }
|
118 | 130 |
|
119 |
| - switch self.certificateVerification { |
120 |
| - case .none: |
| 131 | + precondition(self.certificateVerification != .noHostnameVerification, "TLSConfiguration.certificateVerification = .noHostnameVerification is not supported") |
| 132 | + |
| 133 | + if certificateVerification != .fullVerification || trustRoots != nil { |
121 | 134 | // add verify block to control certificate verification
|
122 | 135 | sec_protocol_options_set_verify_block(
|
123 | 136 | options.securityProtocolOptions,
|
124 |
| - { _, _, sec_protocol_verify_complete in |
125 |
| - sec_protocol_verify_complete(true) |
126 |
| - }, TLSConfiguration.tlsDispatchQueue |
| 137 | + { sec_metadata, sec_trust, sec_protocol_verify_complete in |
| 138 | + guard self.certificateVerification != .none else { |
| 139 | + sec_protocol_verify_complete(true) |
| 140 | + return |
| 141 | + } |
| 142 | + |
| 143 | + let trust = sec_trust_copy_ref(sec_trust).takeRetainedValue() |
| 144 | + if let trustRootCertificates = secTrustRoots { |
| 145 | + SecTrustSetAnchorCertificates(trust, trustRootCertificates as CFArray) |
| 146 | + } |
| 147 | + if #available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *) { |
| 148 | + SecTrustEvaluateAsyncWithError(trust, Self.tlsDispatchQueue) { (trust, result, error) in |
| 149 | + if let error = error { |
| 150 | + print("Trust failed: \(error.localizedDescription)") |
| 151 | + } |
| 152 | + sec_protocol_verify_complete(result) |
| 153 | + } |
| 154 | + } else { |
| 155 | + SecTrustEvaluateAsync(trust, Self.tlsDispatchQueue) { (trust, result) in |
| 156 | + switch result { |
| 157 | + case .proceed, .unspecified: |
| 158 | + sec_protocol_verify_complete(true) |
| 159 | + default: |
| 160 | + sec_protocol_verify_complete(false) |
| 161 | + } |
| 162 | + } |
| 163 | + } |
| 164 | + }, Self.tlsDispatchQueue |
127 | 165 | )
|
128 |
| - |
129 |
| - case .noHostnameVerification: |
130 |
| - precondition(self.certificateVerification != .noHostnameVerification, "TLSConfiguration.certificateVerification = .noHostnameVerification is not supported") |
131 |
| - |
132 |
| - case .fullVerification: |
133 |
| - break |
134 | 166 | }
|
135 |
| - |
136 | 167 | return options
|
137 | 168 | }
|
138 | 169 | }
|
|
0 commit comments