tttls1.3 0.1.4 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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'
|