/src/grpc-swift/Sources/GRPC/TLSVersion.swift
Line | Count | Source |
1 | | /* |
2 | | * Copyright 2022, gRPC Authors All rights reserved. |
3 | | * |
4 | | * Licensed under the Apache License, Version 2.0 (the "License"); |
5 | | * you may not use this file except in compliance with the License. |
6 | | * You may obtain a copy of the License at |
7 | | * |
8 | | * http://www.apache.org/licenses/LICENSE-2.0 |
9 | | * |
10 | | * Unless required by applicable law or agreed to in writing, software |
11 | | * distributed under the License is distributed on an "AS IS" BASIS, |
12 | | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
13 | | * See the License for the specific language governing permissions and |
14 | | * limitations under the License. |
15 | | */ |
16 | | |
17 | | import NIOCore |
18 | | |
19 | | #if canImport(NIOSSL) |
20 | | import NIOSSL |
21 | | #endif |
22 | | #if canImport(Network) |
23 | | import Network |
24 | | import NIOTransportServices |
25 | | #endif |
26 | | |
27 | | // The same as 'TLSVersion' which is defined in NIOSSL which we don't always have. |
28 | | enum GRPCTLSVersion: Hashable { |
29 | | case tlsv1 |
30 | | case tlsv11 |
31 | | case tlsv12 |
32 | | case tlsv13 |
33 | | } |
34 | | |
35 | | #if canImport(NIOSSL) |
36 | | extension GRPCTLSVersion { |
37 | 0 | init(_ tlsVersion: TLSVersion) { |
38 | 0 | switch tlsVersion { |
39 | 0 | case .tlsv1: |
40 | 0 | self = .tlsv1 |
41 | 0 | case .tlsv11: |
42 | 0 | self = .tlsv11 |
43 | 0 | case .tlsv12: |
44 | 0 | self = .tlsv12 |
45 | 0 | case .tlsv13: |
46 | 0 | self = .tlsv13 |
47 | 0 | } |
48 | 0 | } |
49 | | } |
50 | | #endif |
51 | | |
52 | | #if canImport(Network) |
53 | | @available(macOS 10.14, iOS 12.0, tvOS 12.0, watchOS 6.0, *) |
54 | | extension GRPCTLSVersion { |
55 | | init?(_ metadata: NWProtocolTLS.Metadata) { |
56 | | let protocolMetadata = metadata.securityProtocolMetadata |
57 | | |
58 | | if #available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 7.0, *) { |
59 | | let nwTLSVersion = sec_protocol_metadata_get_negotiated_tls_protocol_version(protocolMetadata) |
60 | | switch nwTLSVersion { |
61 | | case .TLSv10: |
62 | | self = .tlsv1 |
63 | | case .TLSv11: |
64 | | self = .tlsv11 |
65 | | case .TLSv12: |
66 | | self = .tlsv12 |
67 | | case .TLSv13: |
68 | | self = .tlsv13 |
69 | | case .DTLSv10, .DTLSv12: |
70 | | return nil |
71 | | @unknown default: |
72 | | return nil |
73 | | } |
74 | | } else { |
75 | | let sslVersion = sec_protocol_metadata_get_negotiated_protocol_version(protocolMetadata) |
76 | | switch sslVersion { |
77 | | case .sslProtocolUnknown: |
78 | | return nil |
79 | | case .tlsProtocol1, .tlsProtocol1Only: |
80 | | self = .tlsv1 |
81 | | case .tlsProtocol11: |
82 | | self = .tlsv11 |
83 | | case .tlsProtocol12: |
84 | | self = .tlsv12 |
85 | | case .tlsProtocol13: |
86 | | self = .tlsv13 |
87 | | case .dtlsProtocol1, |
88 | | .dtlsProtocol12, |
89 | | .sslProtocol2, |
90 | | .sslProtocol3, |
91 | | .sslProtocol3Only, |
92 | | .sslProtocolAll, |
93 | | .tlsProtocolMaxSupported: |
94 | | return nil |
95 | | @unknown default: |
96 | | return nil |
97 | | } |
98 | | } |
99 | | } |
100 | | } |
101 | | #endif |
102 | | |
103 | | extension Channel { |
104 | | /// This method tries to get the TLS version from either the Network.framework or NIOSSL |
105 | | /// - Precondition: Must be called on the `EventLoop` the `Channel` is running on. |
106 | | func getTLSVersionSync( |
107 | | file: StaticString = #fileID, |
108 | | line: UInt = #line |
109 | 0 | ) throws -> GRPCTLSVersion? { |
110 | | #if canImport(Network) |
111 | | if #available(macOS 10.14, iOS 12.0, tvOS 12.0, watchOS 6.0, *) { |
112 | | do { |
113 | | // cast can never fail because we explicitly ask for the NWProtocolTLS Metadata. |
114 | | // it may still be nil if Network.framework isn't used for TLS in which case we will |
115 | | // fall through and try to get the TLS version from NIOSSL |
116 | | if let metadata = try self.getMetadataSync( |
117 | | definition: NWProtocolTLS.definition, |
118 | | file: file, |
119 | | line: line |
120 | | ) as! NWProtocolTLS.Metadata? { |
121 | | return GRPCTLSVersion(metadata) |
122 | | } |
123 | | } catch is NIOTSChannelIsNotANIOTSConnectionChannel { |
124 | | // Not a NIOTS channel, we might be using NIOSSL so try that next. |
125 | | } |
126 | | } |
127 | | #endif |
128 | | #if canImport(NIOSSL) |
129 | 0 | return try self.pipeline.syncOperations.nioSSL_tlsVersion().map(GRPCTLSVersion.init) |
130 | | #else |
131 | | return nil |
132 | | #endif |
133 | 0 | } |
134 | | } |