Coverage Report

Created: 2026-02-14 07:10

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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
}