Skip to content

Commit d3362c4

Browse files
committed
Added TLSConfiguration.getNWProtocolTLSOptions()
Supports a very basic set of options at the moment. - min/max TLS version - disabling certificate verification
1 parent 695a46a commit d3362c4

File tree

2 files changed

+135
-42
lines changed

2 files changed

+135
-42
lines changed
Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the AsyncHTTPClient open source project
4+
//
5+
// Copyright (c) 2018-2020 Apple Inc. and the AsyncHTTPClient project authors
6+
// Licensed under Apache License v2.0
7+
//
8+
// See LICENSE.txt for license information
9+
// See CONTRIBUTORS.txt for the list of AsyncHTTPClient project authors
10+
//
11+
// SPDX-License-Identifier: Apache-2.0
12+
//
13+
//===----------------------------------------------------------------------===//
14+
15+
#if canImport(Network)
16+
17+
import Foundation
18+
import Network
19+
import NIOSSL
20+
import NIOTransportServices
21+
22+
internal extension TLSVersion {
23+
/// return Network framework TLS protocol version
24+
var nwTLSProtocolVersion: tls_protocol_version_t {
25+
switch self {
26+
case .tlsv1:
27+
return .TLSv10
28+
case .tlsv11:
29+
return .TLSv11
30+
case .tlsv12:
31+
return .TLSv12
32+
case .tlsv13:
33+
return .TLSv13
34+
}
35+
}
36+
}
37+
38+
@available (macOS 10.14, iOS 12.0, tvOS 12.0, watchOS 6.0, *)
39+
internal extension TLSConfiguration {
40+
41+
/// Dispatch queue used by Network framework TLS to control certificate verification
42+
static var tlsDispatchQueue = DispatchQueue(label: "TLSDispatch")
43+
44+
/// create NWProtocolTLS.Options for use with NIOTransportServices from the NIOSSL TLSConfiguration
45+
///
46+
/// - Parameter queue: Dispatch queue to run `sec_protocol_options_set_verify_block` on.
47+
/// - Returns: Equivalent NWProtocolTLS Options
48+
func getNWProtocolTLSOptions() -> NWProtocolTLS.Options {
49+
let options = NWProtocolTLS.Options()
50+
51+
// minimum TLS protocol
52+
if #available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *) {
53+
sec_protocol_options_set_min_tls_protocol_version(options.securityProtocolOptions, self.minimumTLSVersion.nwTLSProtocolVersion)
54+
}
55+
56+
// maximum TLS protocol
57+
if let maximumTLSVersion = self.maximumTLSVersion {
58+
if #available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *) {
59+
sec_protocol_options_set_max_tls_protocol_version(options.securityProtocolOptions, maximumTLSVersion.nwTLSProtocolVersion)
60+
} else {
61+
precondition(self.maximumTLSVersion != nil, "TLSConfiguration.maximumTLSVersion is not supported")
62+
}
63+
}
64+
65+
// application protocols
66+
if self.applicationProtocols.count > 0 {
67+
preconditionFailure("TLSConfiguration.applicationProtocols is not supported")
68+
}
69+
/*for applicationProtocol in self.applicationProtocols {
70+
applicationProtocol.utf8.withContiguousStorageIfAvailable { buffer in
71+
guard let opaquePointer = OpaquePointer(buffer.baseAddress) else { return }
72+
let int8Pointer = UnsafePointer<Int8>(opaquePointer)
73+
sec_protocol_options_add_tls_application_protocol(options.securityProtocolOptions, int8Pointer)
74+
}
75+
}*/
76+
77+
// the certificate chain
78+
if self.certificateChain.count > 0 {
79+
preconditionFailure("TLSConfiguration.certificateChain is not supported")
80+
}
81+
82+
// cipher suites
83+
if self.cipherSuites.count > 0 {
84+
//preconditionFailure("TLSConfiguration.cipherSuites is not supported")
85+
}
86+
87+
// key log callback
88+
if let _ = self.keyLogCallback {
89+
preconditionFailure("TLSConfiguration.keyLogCallback is not supported")
90+
}
91+
92+
// private key
93+
if let _ = self.privateKey {
94+
preconditionFailure("TLSConfiguration.privateKey is not supported")
95+
}
96+
97+
// renegotiation support key is unsupported
98+
99+
// trust roots
100+
if let trustRoots = self.trustRoots {
101+
guard case .default = trustRoots else {
102+
preconditionFailure("TLSConfiguration.trustRoots != .default is not supported")
103+
}
104+
}
105+
106+
switch self.certificateVerification {
107+
case .none:
108+
// add verify block to control certificate verification
109+
sec_protocol_options_set_verify_block(options.securityProtocolOptions, { (sec_protocol_metadata, sec_trust, sec_protocol_verify_complete) in
110+
//let trust = sec_trust_copy_ref(sec_trust).takeRetainedValue()
111+
//var error: CFError?
112+
//if SecTrustEvaluateWithError(trust, &error) {
113+
sec_protocol_verify_complete(true)
114+
}, TLSConfiguration.tlsDispatchQueue)
115+
116+
case .noHostnameVerification:
117+
precondition(self.certificateVerification != .noHostnameVerification, "TLSConfiguration.certificateVerification = .noHostnameVerification is not supported")
118+
119+
case .fullVerification:
120+
break
121+
}
122+
123+
return options
124+
}
125+
}
126+
127+
#endif

Sources/AsyncHTTPClient/Utils.swift

Lines changed: 8 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
//
33
// This source file is part of the AsyncHTTPClient open source project
44
//
5-
// Copyright (c) 2018-2019 Apple Inc. and the AsyncHTTPClient project authors
5+
// Copyright (c) 2018-2020 Apple Inc. and the AsyncHTTPClient project authors
66
// Licensed under Apache License v2.0
77
//
88
// See LICENSE.txt for license information
@@ -67,46 +67,8 @@ extension ClientBootstrap {
6767
}
6868
}
6969

70-
// Used by temporary code below
71-
let tlsDispatchQueue = DispatchQueue(label: "TLSDispatch")
72-
7370
extension NIOClientTCPBootstrap {
7471

75-
// TEMPORARY: This is temporary code and an extended version of this should really be in NIOTransportServices. But this allows us to run tests and get results back
76-
// for the TLS tests
77-
#if canImport(Network)
78-
@available (macOS 10.14, iOS 12.0, tvOS 12.0, watchOS 6.0, *)
79-
fileprivate static func getTLSOptions(tlsConfiguration: TLSConfiguration?, queue: DispatchQueue) -> NWProtocolTLS.Options {
80-
guard let tlsConfiguration = tlsConfiguration else { return .init() }
81-
let options = NWProtocolTLS.Options()
82-
83-
sec_protocol_options_set_verify_block(options.securityProtocolOptions, { (sec_protocol_metadata, sec_trust, sec_protocol_verify_complete) in
84-
let trust = sec_trust_copy_ref(sec_trust).takeRetainedValue()
85-
86-
var error: CFError?
87-
if SecTrustEvaluateWithError(trust, &error) {
88-
sec_protocol_verify_complete(true)
89-
} else {
90-
// check error
91-
var errorCode: CFIndex = 0
92-
if let userInfo = CFErrorCopyUserInfo(error) {
93-
let userInfoDictionary = userInfo as NSDictionary
94-
let underlyingError = userInfoDictionary[kCFErrorUnderlyingErrorKey!] as! CFError
95-
errorCode = CFErrorGetCode(underlyingError)
96-
}
97-
if tlsConfiguration.certificateVerification == .none ||
98-
(tlsConfiguration.certificateVerification == .noHostnameVerification && errorCode == errSecNotTrusted) {
99-
sec_protocol_verify_complete(true)
100-
} else {
101-
sec_protocol_verify_complete(false)
102-
}
103-
}
104-
}, queue)
105-
106-
return options
107-
}
108-
#endif
109-
11072
/// create a TCP Bootstrap based off what type of `EventLoop` has been passed to the function.
11173
fileprivate static func makeBootstrap(
11274
on eventLoop: EventLoop,
@@ -116,15 +78,19 @@ extension NIOClientTCPBootstrap {
11678
) throws -> NIOClientTCPBootstrap {
11779
let bootstrap: NIOClientTCPBootstrap
11880
#if canImport(Network)
81+
// if eventLoop is compatible with NIOTransportServices create a NIOTSConnectionBootstrap
11982
if #available(OSX 10.14, iOS 12.0, tvOS 12.0, watchOS 6.0, *), eventLoop is NIOTSEventLoop {
12083
let tsBootstrap = NIOTSConnectionBootstrap(group: eventLoop).channelOption(NIOTSChannelOptions.waitForActivity, value: false)
84+
let tlsConfiguration = configuration.tlsConfiguration ?? TLSConfiguration.forClient()
85+
86+
// if we have a proxy and require TLS then use NIOSSL tls support
12187
if configuration.proxy != nil, requiresTLS {
122-
let tlsConfiguration = configuration.tlsConfiguration ?? TLSConfiguration.forClient()
12388
let sslContext = try NIOSSLContext(configuration: tlsConfiguration)
12489
let hostname = (!requiresTLS || host.isIPAddress) ? nil : host
12590
bootstrap = try NIOClientTCPBootstrap(tsBootstrap, tls: NIOSSLClientTLSProvider(context: sslContext, serverHostname: hostname))
12691
} else {
127-
let parameters = NIOClientTCPBootstrap.getTLSOptions(tlsConfiguration: configuration.tlsConfiguration, queue: tlsDispatchQueue)
92+
// create NIOClientTCPBootstrap with NIOTS TLS provider
93+
let parameters = tlsConfiguration.getNWProtocolTLSOptions()
12894
let tlsProvider = NIOTSClientTLSProvider(tlsOptions: parameters)
12995
bootstrap = NIOClientTCPBootstrap(tsBootstrap, tls: tlsProvider)
13096
}
@@ -141,7 +107,7 @@ extension NIOClientTCPBootstrap {
141107
}
142108
return bootstrap
143109
}
144-
110+
145111
static func makeHTTPClientBootstrapBase(
146112
on eventLoop: EventLoop,
147113
host: String,

0 commit comments

Comments
 (0)