tttls1.3 0.1.4 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +3 -0
- data/README.md +35 -13
- data/Rakefile +2 -4
- data/example/helper.rb +30 -7
- data/example/https_client.rb +3 -20
- data/example/https_client_using_0rtt.rb +10 -24
- data/example/https_client_using_hrr.rb +3 -20
- data/example/https_client_using_ticket.rb +3 -20
- data/example/https_server.rb +43 -0
- data/interop/client_spec.rb +111 -22
- data/interop/helper.rb +1 -0
- data/interop/server_spec.rb +182 -0
- data/lib/tttls1.3/client.rb +115 -98
- data/lib/tttls1.3/connection.rb +119 -32
- data/lib/tttls1.3/message/certificate.rb +18 -0
- data/lib/tttls1.3/message/client_hello.rb +38 -0
- data/lib/tttls1.3/message/encrypted_extensions.rb +20 -16
- data/lib/tttls1.3/message/extension/key_share.rb +24 -2
- data/lib/tttls1.3/message/extension/supported_groups.rb +0 -87
- data/lib/tttls1.3/message/extensions.rb +1 -27
- data/lib/tttls1.3/message/new_session_ticket.rb +14 -0
- data/lib/tttls1.3/message/record.rb +23 -20
- data/lib/tttls1.3/message/server_hello.rb +27 -0
- data/lib/tttls1.3/message.rb +35 -2
- data/lib/tttls1.3/named_group.rb +89 -0
- data/lib/tttls1.3/server.rb +439 -0
- data/lib/tttls1.3/transcript.rb +6 -0
- data/lib/tttls1.3/version.rb +1 -1
- data/lib/tttls1.3.rb +3 -0
- data/spec/certificate_spec.rb +28 -1
- data/spec/client_spec.rb +14 -10
- data/spec/connection_spec.rb +43 -13
- data/spec/encrypted_extensions_spec.rb +4 -4
- data/spec/fixtures/rsa_ca.crt +29 -0
- data/spec/fixtures/rsa_ca.key +51 -0
- data/spec/fixtures/rsa_rsa.crt +23 -0
- data/spec/fixtures/rsa_rsa.key +27 -0
- data/spec/fixtures/rsa_secp256r1.crt +19 -0
- data/spec/fixtures/rsa_secp256r1.key +5 -0
- data/spec/fixtures/rsa_secp384r1.crt +19 -0
- data/spec/fixtures/rsa_secp384r1.key +6 -0
- data/spec/fixtures/rsa_secp521r1.crt +20 -0
- data/spec/fixtures/rsa_secp521r1.key +7 -0
- data/spec/server_spec.rb +186 -0
- data/spec/spec_helper.rb +43 -0
- metadata +28 -2
@@ -4,11 +4,29 @@
|
|
4
4
|
module TTTLS13
|
5
5
|
using Refinements
|
6
6
|
module Message
|
7
|
+
# https://www.iana.org/assignments/tls-extensiontype-values/tls-extensiontype-values.xhtml#tls-extensiontype-values-1
|
8
|
+
APPEARABLE_SH_EXTENSIONS = [
|
9
|
+
ExtensionType::PRE_SHARED_KEY,
|
10
|
+
ExtensionType::PASSWORD_SALT,
|
11
|
+
ExtensionType::SUPPORTED_VERSIONS,
|
12
|
+
ExtensionType::KEY_SHARE
|
13
|
+
].freeze
|
14
|
+
private_constant :APPEARABLE_SH_EXTENSIONS
|
15
|
+
|
16
|
+
APPEARABLE_HRR_EXTENSIONS = [
|
17
|
+
ExtensionType::COOKIE,
|
18
|
+
ExtensionType::PASSWORD_SALT,
|
19
|
+
ExtensionType::SUPPORTED_VERSIONS,
|
20
|
+
ExtensionType::KEY_SHARE
|
21
|
+
].freeze
|
22
|
+
private_constant :APPEARABLE_HRR_EXTENSIONS
|
23
|
+
|
7
24
|
class ServerHello
|
8
25
|
# special value of the SHA-256 of "HelloRetryRequest"
|
9
26
|
HRR_RANDOM \
|
10
27
|
= "\xcf\x21\xad\x74\xe5\x9a\x61\x11\xbe\x1d\x8c\x02\x1e\x65\xb8\x91" \
|
11
28
|
"\xc2\xa2\x11\x16\x7a\xbb\x8c\x5e\x07\x9e\x09\xe2\xc8\xa8\x33\x9c"
|
29
|
+
private_constant :HRR_RANDOM
|
12
30
|
|
13
31
|
attr_reader :msg_type
|
14
32
|
attr_reader :legacy_version
|
@@ -111,6 +129,15 @@ module TTTLS13
|
|
111
129
|
def hrr?
|
112
130
|
@hrr
|
113
131
|
end
|
132
|
+
|
133
|
+
# @return [Boolean]
|
134
|
+
def only_appearable_extensions?
|
135
|
+
exs = @extensions.keys - APPEARABLE_SH_EXTENSIONS
|
136
|
+
exs = @extensions.keys - APPEARABLE_HRR_EXTENSIONS if hrr?
|
137
|
+
return true if exs.empty?
|
138
|
+
|
139
|
+
!(exs - DEFINED_EXTENSIONS).empty?
|
140
|
+
end
|
114
141
|
end
|
115
142
|
end
|
116
143
|
end
|
data/lib/tttls1.3/message.rb
CHANGED
@@ -1,8 +1,6 @@
|
|
1
1
|
# encoding: ascii-8bit
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
|
-
Dir[File.dirname(__FILE__) + '/message/*.rb'].each { |f| require f }
|
5
|
-
|
6
4
|
module TTTLS13
|
7
5
|
module Message
|
8
6
|
module ContentType
|
@@ -44,5 +42,40 @@ module TTTLS13
|
|
44
42
|
KEY_UPDATE = "\x18"
|
45
43
|
MESSAGE_HASH = "\xfe"
|
46
44
|
end
|
45
|
+
|
46
|
+
module ExtensionType
|
47
|
+
SERVER_NAME = "\x00\x00"
|
48
|
+
MAX_FRAGMENT_LENGTH = "\x00\x01"
|
49
|
+
STATUS_REQUEST = "\x00\x05"
|
50
|
+
SUPPORTED_GROUPS = "\x00\x0a"
|
51
|
+
SIGNATURE_ALGORITHMS = "\x00\x0d"
|
52
|
+
USE_SRTP = "\x00\x0e"
|
53
|
+
HEARTBEAT = "\x00\x0f"
|
54
|
+
APPLICATION_LAYER_PROTOCOL_NEGOTIATION = "\x00\x10"
|
55
|
+
SIGNED_CERTIFICATE_TIMESTAMP = "\x00\x12"
|
56
|
+
CLIENT_CERTIFICATE_TYPE = "\x00\x13"
|
57
|
+
SERVER_CERTIFICATE_TYPE = "\x00\x14"
|
58
|
+
PADDING = "\x00\x15"
|
59
|
+
RECORD_SIZE_LIMIT = "\x00\x1c"
|
60
|
+
PWD_PROTECT = "\x00\x1d"
|
61
|
+
PWD_CLEAR = "\x00\x1e"
|
62
|
+
PASSWORD_SALT = "\x00\x1f"
|
63
|
+
PRE_SHARED_KEY = "\x00\x29"
|
64
|
+
EARLY_DATA = "\x00\x2a"
|
65
|
+
SUPPORTED_VERSIONS = "\x00\x2b"
|
66
|
+
COOKIE = "\x00\x2c"
|
67
|
+
PSK_KEY_EXCHANGE_MODES = "\x00\x2d"
|
68
|
+
CERTIFICATE_AUTHORITIES = "\x00\x2f"
|
69
|
+
OID_FILTERS = "\x00\x30"
|
70
|
+
POST_HANDSHAKE_AUTH = "\x00\x31"
|
71
|
+
SIGNATURE_ALGORITHMS_CERT = "\x00\x32"
|
72
|
+
KEY_SHARE = "\x00\x33"
|
73
|
+
end
|
74
|
+
|
75
|
+
DEFINED_EXTENSIONS = ExtensionType.constants.map do |c|
|
76
|
+
ExtensionType.const_get(c)
|
77
|
+
end.freeze
|
47
78
|
end
|
48
79
|
end
|
80
|
+
|
81
|
+
Dir[File.dirname(__FILE__) + '/message/*.rb'].each { |f| require f }
|
@@ -0,0 +1,89 @@
|
|
1
|
+
# encoding: ascii-8bit
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module TTTLS13
|
5
|
+
module NamedGroup
|
6
|
+
SECP256R1 = "\x00\x17"
|
7
|
+
SECP384R1 = "\x00\x18"
|
8
|
+
SECP521R1 = "\x00\x19"
|
9
|
+
# X25519 = "\x00\x1d" # UNSUPPORTED
|
10
|
+
# X448 = "\x00\x1e" # UNSUPPORTED
|
11
|
+
# FFDHE2048 = "\x01\x00" # UNSUPPORTED
|
12
|
+
# FFDHE3072 = "\x01\x01" # UNSUPPORTED
|
13
|
+
# FFDHE4096 = "\x01\x02" # UNSUPPORTED
|
14
|
+
# FFDHE6144 = "\x01\x03" # UNSUPPORTED
|
15
|
+
# FFDHE8192 = "\x01\x04" # UNSUPPORTED
|
16
|
+
# ffdhe_private_use "\x01\xfc" ~ "\x01\xff"
|
17
|
+
# ecdhe_private_use "\xfe\x00" ~ "\xfe\xff"
|
18
|
+
|
19
|
+
class << self
|
20
|
+
# NOTE:
|
21
|
+
# For secp256r1, secp384r1, and secp521r1
|
22
|
+
#
|
23
|
+
# struct {
|
24
|
+
# uint8 legacy_form = 4;
|
25
|
+
# opaque X[coordinate_length];
|
26
|
+
# opaque Y[coordinate_length];
|
27
|
+
# } UncompressedPointRepresentation;
|
28
|
+
#
|
29
|
+
# @param group [TTTLS13::Message::Extension::NamedGroup]
|
30
|
+
#
|
31
|
+
# @raise [TTTLS13::Error::ErrorAlerts]
|
32
|
+
#
|
33
|
+
# @return [Integer]
|
34
|
+
def key_exchange_len(group)
|
35
|
+
case group
|
36
|
+
when SECP256R1
|
37
|
+
65
|
38
|
+
when SECP384R1
|
39
|
+
97
|
40
|
+
when SECP521R1
|
41
|
+
133
|
42
|
+
# not supported other NamedGroup
|
43
|
+
# when X25519
|
44
|
+
# 32
|
45
|
+
# when X448
|
46
|
+
# 56
|
47
|
+
# when FFDHE2048
|
48
|
+
# 256
|
49
|
+
# when FFDHE4096
|
50
|
+
# 512
|
51
|
+
# when FFDHE6144
|
52
|
+
# 768
|
53
|
+
# when FFDHE8192
|
54
|
+
# 1024
|
55
|
+
else
|
56
|
+
raise Error::ErrorAlerts, :internal_error
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
# NOTE:
|
61
|
+
# SECG | ANSI X9.62 | NIST
|
62
|
+
# ------------+---------------+-------------
|
63
|
+
# secp256r1 | prime256v1 | NIST P-256
|
64
|
+
# secp384r1 | | NIST P-384
|
65
|
+
# secp521r1 | | NIST P-521
|
66
|
+
#
|
67
|
+
# https://tools.ietf.org/html/rfc4492#appendix-A
|
68
|
+
#
|
69
|
+
# @param groups [Array of TTTLS13::Message::Extension::NamedGroup]
|
70
|
+
#
|
71
|
+
# @raise [TTTLS13::Error::ErrorAlerts]
|
72
|
+
#
|
73
|
+
# @return [String] EC_builtin_curves
|
74
|
+
def curve_name(group)
|
75
|
+
case group
|
76
|
+
when SECP256R1
|
77
|
+
'prime256v1'
|
78
|
+
when SECP384R1
|
79
|
+
'secp384r1'
|
80
|
+
when SECP521R1
|
81
|
+
'secp521r1'
|
82
|
+
else
|
83
|
+
# not supported other NamedGroup
|
84
|
+
raise Error::ErrorAlerts, :internal_error
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
@@ -0,0 +1,439 @@
|
|
1
|
+
# encoding: ascii-8bit
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module TTTLS13
|
5
|
+
using Refinements
|
6
|
+
module ServerState
|
7
|
+
# initial value is 0, eof value is -1
|
8
|
+
START = 1
|
9
|
+
RECVD_CH = 2
|
10
|
+
NEGOTIATED = 3
|
11
|
+
WAIT_EOED = 4
|
12
|
+
WAIT_FLIGHT2 = 5
|
13
|
+
WAIT_CERT = 6
|
14
|
+
WAIT_CV = 7
|
15
|
+
WAIT_FINISHED = 8
|
16
|
+
CONNECTED = 9
|
17
|
+
end
|
18
|
+
|
19
|
+
DEFAULT_SP_CIPHER_SUITES = [
|
20
|
+
CipherSuite::TLS_AES_256_GCM_SHA384,
|
21
|
+
CipherSuite::TLS_CHACHA20_POLY1305_SHA256,
|
22
|
+
CipherSuite::TLS_AES_128_GCM_SHA256
|
23
|
+
].freeze
|
24
|
+
private_constant :DEFAULT_SP_CIPHER_SUITES
|
25
|
+
|
26
|
+
DEFAULT_SP_SIGNATURE_ALGORITHMS = [
|
27
|
+
SignatureScheme::ECDSA_SECP256R1_SHA256,
|
28
|
+
SignatureScheme::ECDSA_SECP384R1_SHA384,
|
29
|
+
SignatureScheme::ECDSA_SECP521R1_SHA512,
|
30
|
+
SignatureScheme::RSA_PSS_RSAE_SHA256,
|
31
|
+
SignatureScheme::RSA_PSS_RSAE_SHA384,
|
32
|
+
SignatureScheme::RSA_PSS_RSAE_SHA512,
|
33
|
+
SignatureScheme::RSA_PKCS1_SHA256,
|
34
|
+
SignatureScheme::RSA_PKCS1_SHA384,
|
35
|
+
SignatureScheme::RSA_PKCS1_SHA512
|
36
|
+
].freeze
|
37
|
+
private_constant :DEFAULT_SP_SIGNATURE_ALGORITHMS
|
38
|
+
|
39
|
+
DEFAULT_SP_NAMED_GROUP_LIST = [
|
40
|
+
NamedGroup::SECP256R1,
|
41
|
+
NamedGroup::SECP384R1,
|
42
|
+
NamedGroup::SECP521R1
|
43
|
+
].freeze
|
44
|
+
private_constant :DEFAULT_SP_NAMED_GROUP_LIST
|
45
|
+
|
46
|
+
DEFAULT_SERVER_SETTINGS = {
|
47
|
+
crt_file: nil,
|
48
|
+
key_file: nil,
|
49
|
+
cipher_suites: DEFAULT_SP_CIPHER_SUITES,
|
50
|
+
signature_algorithms: DEFAULT_SP_SIGNATURE_ALGORITHMS,
|
51
|
+
supported_groups: DEFAULT_SP_NAMED_GROUP_LIST,
|
52
|
+
loglevel: Logger::WARN
|
53
|
+
}.freeze
|
54
|
+
private_constant :DEFAULT_SERVER_SETTINGS
|
55
|
+
|
56
|
+
# rubocop: disable Metrics/ClassLength
|
57
|
+
class Server < Connection
|
58
|
+
# @param socket [Socket]
|
59
|
+
# @param settings [Hash]
|
60
|
+
def initialize(socket, **settings)
|
61
|
+
super(socket)
|
62
|
+
|
63
|
+
@endpoint = :server
|
64
|
+
@settings = DEFAULT_SERVER_SETTINGS.merge(settings)
|
65
|
+
logger.level = @settings[:loglevel]
|
66
|
+
|
67
|
+
raise Error::ConfigError unless valid_settings?
|
68
|
+
return if @settings[:crt_file].nil?
|
69
|
+
|
70
|
+
crt_str = File.read(@settings[:crt_file])
|
71
|
+
@crt = OpenSSL::X509::Certificate.new(crt_str) # TODO: spki rsassaPss
|
72
|
+
klass = @crt.public_key.class
|
73
|
+
@key = klass.new(File.read(@settings[:key_file]))
|
74
|
+
raise Error::ConfigError unless @crt.check_private_key(@key)
|
75
|
+
end
|
76
|
+
|
77
|
+
# NOTE:
|
78
|
+
# START <-----+
|
79
|
+
# Recv ClientHello | | Send HelloRetryRequest
|
80
|
+
# v |
|
81
|
+
# RECVD_CH ----+
|
82
|
+
# | Select parameters
|
83
|
+
# v
|
84
|
+
# NEGOTIATED
|
85
|
+
# | Send ServerHello
|
86
|
+
# | K_send = handshake
|
87
|
+
# | Send EncryptedExtensions
|
88
|
+
# | [Send CertificateRequest]
|
89
|
+
# Can send | [Send Certificate + CertificateVerify]
|
90
|
+
# app data | Send Finished
|
91
|
+
# after --> | K_send = application
|
92
|
+
# here +--------+--------+
|
93
|
+
# No 0-RTT | | 0-RTT
|
94
|
+
# | |
|
95
|
+
# K_recv = handshake | | K_recv = early data
|
96
|
+
# [Skip decrypt errors] | +------> WAIT_EOED -+
|
97
|
+
# | | Recv | | Recv EndOfEarlyData
|
98
|
+
# | | early data | | K_recv = handshake
|
99
|
+
# | +------------+ |
|
100
|
+
# | |
|
101
|
+
# +> WAIT_FLIGHT2 <--------+
|
102
|
+
# |
|
103
|
+
# +--------+--------+
|
104
|
+
# No auth | | Client auth
|
105
|
+
# | |
|
106
|
+
# | v
|
107
|
+
# | WAIT_CERT
|
108
|
+
# | Recv | | Recv Certificate
|
109
|
+
# | empty | v
|
110
|
+
# | Certificate | WAIT_CV
|
111
|
+
# | | | Recv
|
112
|
+
# | v | CertificateVerify
|
113
|
+
# +-> WAIT_FINISHED <---+
|
114
|
+
# | Recv Finished
|
115
|
+
# | K_recv = application
|
116
|
+
# v
|
117
|
+
# CONNECTED
|
118
|
+
#
|
119
|
+
# https://tools.ietf.org/html/rfc8446#appendix-A.2
|
120
|
+
#
|
121
|
+
# rubocop: disable Metrics/AbcSize
|
122
|
+
# rubocop: disable Metrics/BlockLength
|
123
|
+
# rubocop: disable Metrics/CyclomaticComplexity
|
124
|
+
# rubocop: disable Metrics/MethodLength
|
125
|
+
# rubocop: disable Metrics/PerceivedComplexity
|
126
|
+
def accept
|
127
|
+
@state = ServerState::START
|
128
|
+
loop do
|
129
|
+
case @state
|
130
|
+
when ServerState::START
|
131
|
+
logger.debug('ServerState::START')
|
132
|
+
|
133
|
+
ch = @transcript[CH] = recv_client_hello
|
134
|
+
terminate(:illegal_parameter) unless ch.only_appearable_extensions?
|
135
|
+
@state = ServerState::RECVD_CH
|
136
|
+
when ServerState::RECVD_CH
|
137
|
+
logger.debug('ServerState::RECVD_CH')
|
138
|
+
|
139
|
+
# support only TLS 1.3
|
140
|
+
terminate(:protocol_version) unless negotiated_tls_1_3?
|
141
|
+
|
142
|
+
# validate/select parameters
|
143
|
+
@cipher_suite = select_cipher_suite
|
144
|
+
@named_group = select_named_group
|
145
|
+
@signature_scheme = select_signature_scheme
|
146
|
+
terminate(:handshake_failure) \
|
147
|
+
if @cipher_suite.nil? || @named_group.nil? || @signature_scheme.nil?
|
148
|
+
terminamte(:illegal_parameter) unless valid_ch_compression_methods?
|
149
|
+
terminate(:unrecognized_name) unless recognized_server_name?
|
150
|
+
|
151
|
+
@state = ServerState::NEGOTIATED
|
152
|
+
when ServerState::NEGOTIATED
|
153
|
+
logger.debug('ServerState::NEGOTIATED')
|
154
|
+
|
155
|
+
exs, @priv_key = gen_sh_extensions
|
156
|
+
@transcript[SH] = send_server_hello(exs)
|
157
|
+
send_ccs # compatibility mode
|
158
|
+
|
159
|
+
# generate shared secret
|
160
|
+
terminate(:illegal_parameter) unless valid_ch_key_share?
|
161
|
+
ke = @transcript[CH].extensions[Message::ExtensionType::KEY_SHARE]
|
162
|
+
&.key_share_entry
|
163
|
+
&.find { |e| e.group == @named_group }
|
164
|
+
&.key_exchange
|
165
|
+
# TODO: Send HelloRetryRequest
|
166
|
+
shared_secret = gen_shared_secret(ke, @priv_key, @named_group)
|
167
|
+
@key_schedule = KeySchedule.new(psk: @psk,
|
168
|
+
shared_secret: shared_secret,
|
169
|
+
cipher_suite: @cipher_suite,
|
170
|
+
transcript: @transcript)
|
171
|
+
@write_cipher = gen_cipher(@cipher_suite,
|
172
|
+
@key_schedule.server_handshake_write_key,
|
173
|
+
@key_schedule.server_handshake_write_iv)
|
174
|
+
@read_cipher = gen_cipher(@cipher_suite,
|
175
|
+
@key_schedule.client_handshake_write_key,
|
176
|
+
@key_schedule.client_handshake_write_iv)
|
177
|
+
@state = ServerState::WAIT_FLIGHT2
|
178
|
+
when ServerState::WAIT_EOED
|
179
|
+
logger.debug('ServerState::WAIT_EOED')
|
180
|
+
when ServerState::WAIT_FLIGHT2
|
181
|
+
logger.debug('ServerState::WAIT_FLIGHT2')
|
182
|
+
|
183
|
+
ee = @transcript[EE] = gen_encrypted_extensions
|
184
|
+
# TODO: [Send CertificateRequest]
|
185
|
+
ct = @transcript[CT] = gen_certificate
|
186
|
+
cv = @transcript[CV] = gen_certificate_verify
|
187
|
+
sf = @transcript[SF] = gen_finished
|
188
|
+
send_server_parameters([ee, ct, cv, sf])
|
189
|
+
@state = ServerState::WAIT_FINISHED
|
190
|
+
when ServerState::WAIT_CERT
|
191
|
+
logger.debug('ServerState::WAIT_CERT')
|
192
|
+
when ServerState::WAIT_CV
|
193
|
+
logger.debug('ServerState::WAIT_CV')
|
194
|
+
when ServerState::WAIT_FINISHED
|
195
|
+
logger.debug('ServerState::WAIT_FINISHED')
|
196
|
+
|
197
|
+
@transcript[CF] = recv_finished
|
198
|
+
terminate(:decrypt_error) unless verified_finished?
|
199
|
+
@write_cipher = gen_cipher(@cipher_suite,
|
200
|
+
@key_schedule.server_application_write_key,
|
201
|
+
@key_schedule.server_application_write_iv)
|
202
|
+
@read_cipher = gen_cipher(@cipher_suite,
|
203
|
+
@key_schedule.client_application_write_key,
|
204
|
+
@key_schedule.client_application_write_iv)
|
205
|
+
@state = ServerState::CONNECTED
|
206
|
+
when ServerState::CONNECTED
|
207
|
+
logger.debug('ServerState::CONNECTED')
|
208
|
+
|
209
|
+
break
|
210
|
+
end
|
211
|
+
end
|
212
|
+
end
|
213
|
+
# rubocop: enable Metrics/AbcSize
|
214
|
+
# rubocop: enable Metrics/BlockLength
|
215
|
+
# rubocop: enable Metrics/CyclomaticComplexity
|
216
|
+
# rubocop: enable Metrics/MethodLength
|
217
|
+
# rubocop: enable Metrics/PerceivedComplexity
|
218
|
+
|
219
|
+
private
|
220
|
+
|
221
|
+
# @return [Boolean]
|
222
|
+
def valid_settings?
|
223
|
+
mod = CipherSuite
|
224
|
+
defined = mod.constants.map { |c| mod.const_get(c) }
|
225
|
+
return false unless (@settings[:cipher_suites] - defined).empty?
|
226
|
+
|
227
|
+
mod = SignatureScheme
|
228
|
+
defined = mod.constants.map { |c| mod.const_get(c) }
|
229
|
+
return false unless (@settings[:signature_algorithms] - defined).empty?
|
230
|
+
|
231
|
+
mod = NamedGroup
|
232
|
+
defined = mod.constants.map { |c| mod.const_get(c) }
|
233
|
+
return false unless (@settings[:supported_groups] - defined).empty?
|
234
|
+
|
235
|
+
true
|
236
|
+
end
|
237
|
+
|
238
|
+
# @raise [TTTLS13::Error::ErrorAlerts]
|
239
|
+
#
|
240
|
+
# @return [TTTLS13::Message::ClientHello]
|
241
|
+
def recv_client_hello
|
242
|
+
ch = recv_message
|
243
|
+
terminate(:unexpected_message) unless ch.is_a?(Message::ClientHello)
|
244
|
+
|
245
|
+
ch
|
246
|
+
end
|
247
|
+
|
248
|
+
# @param exs [TTTLS13::Message::Extensions]
|
249
|
+
#
|
250
|
+
# @return [TTTLS13::Message::ServerHello]
|
251
|
+
def send_server_hello(exs)
|
252
|
+
ch_session_id = @transcript[CH].legacy_session_id
|
253
|
+
sh = Message::ServerHello.new(
|
254
|
+
legacy_session_id_echo: ch_session_id,
|
255
|
+
cipher_suite: @cipher_suite,
|
256
|
+
extensions: exs
|
257
|
+
)
|
258
|
+
send_handshakes(Message::ContentType::HANDSHAKE, [sh], @write_cipher)
|
259
|
+
|
260
|
+
sh
|
261
|
+
end
|
262
|
+
|
263
|
+
# @param messages [Array of TTTLS13::Message::$Object]
|
264
|
+
#
|
265
|
+
# @return [Array of TTTLS13::Message::$Object]
|
266
|
+
def send_server_parameters(messages)
|
267
|
+
send_handshakes(Message::ContentType::APPLICATION_DATA,
|
268
|
+
messages.reject(&:nil?),
|
269
|
+
@write_cipher)
|
270
|
+
|
271
|
+
messages
|
272
|
+
end
|
273
|
+
|
274
|
+
# @return [TTTLS13::Message::EncryptedExtensions]
|
275
|
+
def gen_encrypted_extensions
|
276
|
+
Message::EncryptedExtensions.new(gen_ee_extensions)
|
277
|
+
end
|
278
|
+
|
279
|
+
# @return [TTTLS13::Message::Certificate, nil]
|
280
|
+
def gen_certificate
|
281
|
+
return nil if @crt.nil?
|
282
|
+
|
283
|
+
ce = Message::CertificateEntry.new(@crt)
|
284
|
+
Message::Certificate.new(certificate_list: [ce])
|
285
|
+
end
|
286
|
+
|
287
|
+
# @return [TTTLS13::Message::CertificateVerify, nil]
|
288
|
+
def gen_certificate_verify
|
289
|
+
return nil if @key.nil?
|
290
|
+
|
291
|
+
Message::CertificateVerify.new(signature_scheme: @signature_scheme,
|
292
|
+
signature: sign_certificate_verify)
|
293
|
+
end
|
294
|
+
|
295
|
+
# @return [TTTLS13::Message::Finished]
|
296
|
+
def gen_finished
|
297
|
+
Message::Finished.new(sign_finished)
|
298
|
+
end
|
299
|
+
|
300
|
+
# @raise [TTTLS13::Error::ErrorAlerts]
|
301
|
+
#
|
302
|
+
# @return [TTTLS13::Message::Finished]
|
303
|
+
def recv_finished
|
304
|
+
cf = recv_message
|
305
|
+
terminate(:unexpected_message) unless cf.is_a?(Message::Finished)
|
306
|
+
|
307
|
+
cf
|
308
|
+
end
|
309
|
+
|
310
|
+
# @return [TTTLS13::Message::Extensions]
|
311
|
+
# @return [OpenSSL::PKey::EC.$Object]
|
312
|
+
def gen_sh_extensions
|
313
|
+
exs = []
|
314
|
+
# supported_versions: only TLS 1.3
|
315
|
+
exs << Message::Extension::SupportedVersions.new(
|
316
|
+
msg_type: Message::HandshakeType::SERVER_HELLO
|
317
|
+
)
|
318
|
+
|
319
|
+
# key_share
|
320
|
+
key_share, priv_key \
|
321
|
+
= Message::Extension::KeyShare.gen_sh_key_share(@named_group)
|
322
|
+
exs << key_share
|
323
|
+
|
324
|
+
[Message::Extensions.new(exs), priv_key]
|
325
|
+
end
|
326
|
+
|
327
|
+
# @return [TTTLS13::Message::Extensions]
|
328
|
+
def gen_ee_extensions
|
329
|
+
exs = []
|
330
|
+
|
331
|
+
# server_name
|
332
|
+
exs << Message::Extension::ServerName.new('') \
|
333
|
+
if @transcript[CH].extensions
|
334
|
+
.include?(Message::ExtensionType::SERVER_NAME)
|
335
|
+
|
336
|
+
# supported_groups
|
337
|
+
exs \
|
338
|
+
<< Message::Extension::SupportedGroups.new(@settings[:supported_groups])
|
339
|
+
|
340
|
+
Message::Extensions.new(exs)
|
341
|
+
end
|
342
|
+
|
343
|
+
# @return [String]
|
344
|
+
def sign_certificate_verify
|
345
|
+
context = 'TLS 1.3, server CertificateVerify'
|
346
|
+
do_sign_certificate_verify(private_key: @key,
|
347
|
+
signature_scheme: @signature_scheme,
|
348
|
+
context: context,
|
349
|
+
handshake_context_end: CT)
|
350
|
+
end
|
351
|
+
|
352
|
+
# @return [String]
|
353
|
+
def sign_finished
|
354
|
+
digest = CipherSuite.digest(@cipher_suite)
|
355
|
+
finished_key = @key_schedule.server_finished_key
|
356
|
+
do_sign_finished(digest: digest,
|
357
|
+
finished_key: finished_key,
|
358
|
+
handshake_context_end: CV)
|
359
|
+
end
|
360
|
+
|
361
|
+
# @return [Boolean]
|
362
|
+
def verified_finished?
|
363
|
+
digest = CipherSuite.digest(@cipher_suite)
|
364
|
+
finished_key = @key_schedule.client_finished_key
|
365
|
+
signature = @transcript[CF].verify_data
|
366
|
+
do_verified_finished?(digest: digest,
|
367
|
+
finished_key: finished_key,
|
368
|
+
handshake_context_end: EOED,
|
369
|
+
signature: signature)
|
370
|
+
end
|
371
|
+
|
372
|
+
# @return [Boolean]
|
373
|
+
def negotiated_tls_1_3?
|
374
|
+
ch = @transcript[CH]
|
375
|
+
ch_lv = ch.legacy_version
|
376
|
+
ch_sv = ch.extensions[Message::ExtensionType::SUPPORTED_VERSIONS]
|
377
|
+
&.versions || []
|
378
|
+
|
379
|
+
ch_lv == Message::ProtocolVersion::TLS_1_2 &&
|
380
|
+
ch_sv.include?(Message::ProtocolVersion::TLS_1_3)
|
381
|
+
end
|
382
|
+
|
383
|
+
# @return [TTTLS13::CipherSuite, nil]
|
384
|
+
def select_cipher_suite
|
385
|
+
@transcript[CH].cipher_suites.find do |cs|
|
386
|
+
@settings[:cipher_suites].include?(cs)
|
387
|
+
end
|
388
|
+
end
|
389
|
+
|
390
|
+
# @return [TTTLS13::NamedGroup, nil]
|
391
|
+
def select_named_group
|
392
|
+
groups \
|
393
|
+
= @transcript[CH].extensions[Message::ExtensionType::SUPPORTED_GROUPS]
|
394
|
+
&.named_group_list || []
|
395
|
+
|
396
|
+
groups.find do |sg|
|
397
|
+
@settings[:supported_groups].include?(sg)
|
398
|
+
end
|
399
|
+
end
|
400
|
+
|
401
|
+
# @return [TTTLS13::SignatureScheme, nil]
|
402
|
+
def select_signature_scheme
|
403
|
+
algorithms \
|
404
|
+
= @transcript[CH].extensions[Message::ExtensionType::SIGNATURE_ALGORITHMS]
|
405
|
+
&.supported_signature_algorithms || []
|
406
|
+
|
407
|
+
do_select_signature_algorithms(algorithms, @crt).find do |ss|
|
408
|
+
@settings[:signature_algorithms].include?(ss)
|
409
|
+
end
|
410
|
+
end
|
411
|
+
|
412
|
+
# @return [Boolean]
|
413
|
+
def valid_ch_compression_methods?
|
414
|
+
@transcript[CH].legacy_compression_methods == ["\x00"]
|
415
|
+
end
|
416
|
+
|
417
|
+
# @return [Boolean]
|
418
|
+
def recognized_server_name?
|
419
|
+
server_name \
|
420
|
+
= @transcript[CH].extensions[Message::ExtensionType::SERVER_NAME]
|
421
|
+
&.server_name
|
422
|
+
|
423
|
+
return true if server_name.nil?
|
424
|
+
|
425
|
+
matching_san?(@crt, server_name)
|
426
|
+
end
|
427
|
+
|
428
|
+
# @return [Boolean]
|
429
|
+
def valid_ch_key_share?
|
430
|
+
ks = @transcript[CH].extensions[Message::ExtensionType::KEY_SHARE]
|
431
|
+
ks_groups = ks&.key_share_entry&.map(&:group) || []
|
432
|
+
sg = @transcript[CH].extensions[Message::ExtensionType::SUPPORTED_GROUPS]
|
433
|
+
sp_groups = sg&.named_group_list || []
|
434
|
+
|
435
|
+
ks_groups.uniq == ks_groups && (ks_groups - sp_groups).empty?
|
436
|
+
end
|
437
|
+
end
|
438
|
+
# rubocop: enable Metrics/ClassLength
|
439
|
+
end
|
data/lib/tttls1.3/transcript.rb
CHANGED
@@ -23,6 +23,8 @@ module TTTLS13
|
|
23
23
|
super
|
24
24
|
end
|
25
25
|
|
26
|
+
alias super_include? include?
|
27
|
+
|
26
28
|
# @param digest [String] name of digest algorithm
|
27
29
|
# @param end_index [Integer]
|
28
30
|
#
|
@@ -43,6 +45,10 @@ module TTTLS13
|
|
43
45
|
OpenSSL::Digest.digest(digest, truncated)
|
44
46
|
end
|
45
47
|
|
48
|
+
def include?(key)
|
49
|
+
super_include?(key) && !self[key].nil?
|
50
|
+
end
|
51
|
+
|
46
52
|
private
|
47
53
|
|
48
54
|
# @param digest [String] name of digest algorithm
|
data/lib/tttls1.3/version.rb
CHANGED
data/lib/tttls1.3.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require 'openssl'
|
4
|
+
require 'pp'
|
4
5
|
require 'logger'
|
5
6
|
|
6
7
|
require 'tttls1.3/version'
|
@@ -9,6 +10,7 @@ require 'tttls1.3/logging'
|
|
9
10
|
require 'tttls1.3/error'
|
10
11
|
require 'tttls1.3/cipher_suites'
|
11
12
|
require 'tttls1.3/signature_scheme'
|
13
|
+
require 'tttls1.3/named_group'
|
12
14
|
require 'tttls1.3/cryptograph'
|
13
15
|
require 'tttls1.3/transcript'
|
14
16
|
require 'tttls1.3/key_schedule'
|
@@ -16,3 +18,4 @@ require 'tttls1.3/message'
|
|
16
18
|
require 'tttls1.3/sequence_number'
|
17
19
|
require 'tttls1.3/connection'
|
18
20
|
require 'tttls1.3/client'
|
21
|
+
require 'tttls1.3/server'
|