class SSLShake::TLS
Constants
- ALERT_DESCRIPTIONS
tools.ietf.org/html/rfc5246#appendix-A.3 tools.ietf.org/html/rfc8446#appendix-B.2
- ALERT_SEVERITY
- CONTENT_TYPES
- HANDSHAKE_TYPES
- OPENSSL_1_0_2_TLS10_CIPHERS
Additional collection of ciphers used by different apps and versions
- OPENSSL_1_0_2_TLS11_CIPHERS
- OPENSSL_1_0_2_TLS12_CIPHERS
- SSL3_CIPHERS
- TLS10_CIPHERS
- TLS13_CIPHERS
- TLS_CIPHERS
- VERSIONS
Public Instance Methods
hello(version, cipher_search = nil, extensions = nil)
click to toggle source
# File lib/sslshake/tls.rb, line 118 def hello(version, cipher_search = nil, extensions = nil) case version when 'ssl3' ciphers = cipher_string(SSL3_CIPHERS, cipher_search) when 'tls1.0', 'tls1.1' ciphers = cipher_string(TLS_CIPHERS, cipher_search) (extensions ||= '') << '000f000101' # add Heartbeat extensions << '000d00140012040308040401050308050501080606010201' # add signature_algorithms extensions << '000b00020100' # add ec_points_format extensions << '000a000a0008fafa001d00170018' # add elliptic_curve when 'tls1.2' ciphers = cipher_string(TLS_CIPHERS, cipher_search) (extensions ||= '') << '000f000101' # add Heartbeat extensions << '000d00140012040308040401050308050501080606010201' # add signature_algorithms extensions << '000b00020100' # add ec_points_format extensions << '000a000a0008fafa001d00170018' # add elliptic_curve when 'tls1.3' ciphers = cipher_string(TLS13_CIPHERS, cipher_search) (extensions ||= '') << '002b0003020304' # TLSv1.3 Supported Versions extension extensions << '000d00140012040308040401050308050501080606010201' # add signature_algorithms extensions << '000a00080006001d00170018' # Supported Groups extension # This is a pre-generated public/private key pair using the x25519 curve: # It was generated from the command line with: # # > openssl-1.1.1e/apps/openssl genpkey -algorithm x25519 > pkey # > openssl-1.1.1e/apps/openssl pkey -noout -text < pkey # priv: # 30:90:f3:89:f4:9e:52:59:3c:ba:e9:f4:78:84:a0: # 23:86:73:5e:f5:c9:46:6c:3a:c3:4e:ec:56:57:81: # 5d:62 # pub: # e7:08:71:36:d0:81:e0:16:19:3a:cb:67:ca:b8:28: # d9:45:92:16:ff:36:63:0d:0d:5a:3d:9d:47:ce:3e: # cd:7e public_key= 'e7087136d081e016193acb67cab828d9459216ff36630d0d5a3d9d47ce3ecd7e' extensions << '003300260024001d0020' + public_key else fail UserError, "This version is not supported: #{version.inspect}" end hello_tls(version, ciphers, extensions || '') end
parse_hello(socket, opts)
click to toggle source
# File lib/sslshake/tls.rb, line 160 def parse_hello(socket, opts) # rubocop:disable Meterics/AbcSize raw = socket_read(socket, 5, opts[:timeout], opts[:retries]) .unpack('H*')[0].upcase.scan(/../) type = raw.shift if type == CONTENT_TYPES['Alert'] raw.shift(2) # Shift off version len = raw.shift(2).join.to_i(16) raw_alert = socket_read(socket, len, opts[:timeout], opts[:retries]) .unpack('H*')[0].upcase.scan(/../) severity = ALERT_SEVERITY.key(raw_alert.shift) desc_raw = raw_alert.shift description = ALERT_DESCRIPTIONS.key(desc_raw) return { 'error' => "SSL Alert: #{severity} #{description || desc_raw}" } end unless type == CONTENT_TYPES['Handshake'] return { 'error' => 'Failed to parse response. It is not an SSL handshake.' } end res = {} res['version'] = VERSIONS.key(raw.shift(2).join('')) len = raw.shift(2).join.to_i(16) res['raw'] = response = socket_read(socket, len, opts[:timeout], opts[:retries]) .unpack('H*')[0].upcase raw = response.scan(/../) res['handshake_type'] = HANDSHAKE_TYPES.key(raw.shift) _len = raw.shift(3) res['handshake_tls_version'] = VERSIONS.key(raw.shift(2).join('')) res['random'] = raw.shift(32).join('') len = raw.shift.to_i(16) res['session_id'] = raw.shift(len).join('') ciphers = case res['version'] when 'ssl3' SSL3_CIPHERS else TLS_CIPHERS end res['cipher_suite'] = ciphers.key(raw.shift(2).join('')) res['compression_method'] = raw.shift # # TLS 1.3 pretends to be TLS 1.2 in the preceeding headers for # compatibility. To correctly identify it, we have to look at # any Supported Versions extensions that the server sent us. # all_ext_len = raw.shift(2).join.to_i(16) all_ext_data = raw.shift(all_ext_len) while all_ext_data.length > 0 ext_type = all_ext_data.shift(2).join ext_len = all_ext_data.shift(2).join.to_i(16) ext_data = all_ext_data.shift(ext_len) if ext_type == '002B' && ext_data.join == '0304' # Supported Versions res['version'] = 'tls1.3' end end res['success'] = true res['success'] = (res['version'] == opts[:protocol]) unless opts[:protocol].nil? res rescue SystemCallError, Alert => _ return { 'error' => 'Failed to parse response. The connection was terminated.' } end
ssl_hello(content, version)
click to toggle source
# File lib/sslshake/tls.rb, line 110 def ssl_hello(content, version) ssl_record( CONTENT_TYPES['Handshake'], HANDSHAKE_TYPES['ClientHello'] + int_bytes(content.length / 2, 3) + content, version, ) end
ssl_record(content_type, content, version)
click to toggle source
# File lib/sslshake/tls.rb, line 105 def ssl_record(content_type, content, version) res = content_type + version + int_bytes(content.length / 2, 2) + content [res].pack('H*') end
Private Instance Methods
hello_tls(version, ciphers, extensions)
click to toggle source
# File lib/sslshake/tls.rb, line 228 def hello_tls(version, ciphers, extensions) random = SecureRandom.hex(32) session_id = '' compressions = '00' c = VERSIONS[version] + random + int_bytes(session_id.length / 2, 1) + session_id + int_bytes(ciphers.length / 2, 2) + ciphers + int_bytes(compressions.length / 2, 1) + compressions + int_bytes(extensions.length / 2, 2) + extensions ssl_hello(c, VERSIONS[version]) end