Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: TTTLS13::Connection#transcript & TTTLS13::Convert.obj2html #86

Merged
merged 4 commits into from
Jan 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions example/helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -62,3 +62,24 @@ def recv_http_response(client)
parser << client.read until client.eof?
buf
end

def transcript_htmlize(transcript)
m = {
TTTLS13::CH1 => 'ClientHello',
TTTLS13::HRR => 'HelloRetryRequest',
TTTLS13::CH => 'ClientHello',
TTTLS13::SH => 'ServerHello',
TTTLS13::EE => 'EncryptedExtensions',
TTTLS13::CR => 'CertificateRequest',
TTTLS13::CT => 'Certificate',
TTTLS13::CV => 'CertificateVerify',
TTTLS13::SF => 'Finished',
TTTLS13::EOED => 'EndOfEarlyData',
TTTLS13::CCT => 'Certificate',
TTTLS13::CCV => 'CertificateVerify',
TTTLS13::CF => 'Finished'
}.map { |k, v| [k, '<details><summary>' + v + '</summary>%s</details>'] }.to_h
transcript.map do |k, v|
format(m[k], TTTLS13::Convert.obj2html(v.first))
end.join('<br>')
end
15 changes: 14 additions & 1 deletion example/https_server.rb
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,20 @@
parser.on_message_complete = lambda do
if !parser.http_method.nil?
logger.info 'Receive Request'
server.write(simple_http_response('TEST'))
html = <<HTML
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>tttls1.3 test server</title>
</head>
<body>
%s
</body>
</html>
HTML
html = format(html, transcript_htmlize(server.transcript))
server.write(simple_http_response(html))
server.close
else
logger.warn 'Not Request'
Expand Down
76 changes: 38 additions & 38 deletions lib/tttls1.3/client.rb
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,7 @@ def initialize(socket, hostname, **settings)
# rubocop: disable Metrics/MethodLength
# rubocop: disable Metrics/PerceivedComplexity
def connect
transcript = Transcript.new
@transcript = Transcript.new
key_schedule = nil # TTTLS13::KeySchedule
psk = nil
priv_keys = {} # Hash of NamedGroup => OpenSSL::PKey::$Object
Expand All @@ -173,7 +173,7 @@ def connect
psk: psk,
shared_secret: nil,
cipher_suite: @settings[:psk_cipher_suite],
transcript: transcript
transcript: @transcript
)
end

Expand Down Expand Up @@ -205,7 +205,7 @@ def connect
ch_outer = ch
# use ClientHelloInner messages for the transcript hash
ch = inner.nil? ? ch : inner
transcript[CH] = [ch, ch.serialize]
@transcript[CH] = [ch, ch.serialize]
send_ccs if @settings[:compatibility_mode]
if use_early_data?
e_wcipher = gen_cipher(
Expand All @@ -214,7 +214,7 @@ def connect
key_schedule.early_data_write_iv
)
sslkeylogfile&.write_client_early_traffic_secret(
transcript[CH].first.random,
@transcript[CH].first.random,
key_schedule.client_early_traffic_secret
)
send_early_data(e_wcipher)
Expand All @@ -224,7 +224,7 @@ def connect
when ClientState::WAIT_SH
logger.debug('ClientState::WAIT_SH')

sh, = transcript[SH] = recv_server_hello
sh, = @transcript[SH] = recv_server_hello

# downgrade protection
if !sh.negotiated_tls_1_3? && sh.downgraded?
Expand All @@ -241,7 +241,7 @@ def connect
unless sh.legacy_compression_method == "\x00"

# validate sh using ch
ch, = transcript[CH]
ch, = @transcript[CH]
terminate(:illegal_parameter) \
unless sh.legacy_version == ch.legacy_version
terminate(:illegal_parameter) \
Expand All @@ -252,8 +252,8 @@ def connect
unless (sh.extensions.keys - ch.extensions.keys).empty?

# validate sh using hrr
if transcript.include?(HRR)
hrr, = transcript[HRR]
if @transcript.include?(HRR)
hrr, = @transcript[HRR]
terminate(:illegal_parameter) \
unless sh.cipher_suite == hrr.cipher_suite

Expand All @@ -265,10 +265,10 @@ def connect

# handling HRR
if sh.hrr?
terminate(:unexpected_message) if transcript.include?(HRR)
terminate(:unexpected_message) if @transcript.include?(HRR)

ch1, = transcript[CH1] = transcript.delete(CH)
hrr, = transcript[HRR] = transcript.delete(SH)
ch1, = @transcript[CH1] = @transcript.delete(CH)
hrr, = @transcript[HRR] = @transcript.delete(SH)
ch1_outer = ch_outer
ch_outer = nil

Expand Down Expand Up @@ -302,7 +302,7 @@ def connect
# use ClientHelloInner messages for the transcript hash
ch_outer = ch
ch = inner.nil? ? ch : inner
transcript[CH] = [ch, ch.serialize]
@transcript[CH] = [ch, ch.serialize]

@state = ClientState::WAIT_SH
next
Expand Down Expand Up @@ -331,25 +331,25 @@ def connect
psk: psk,
shared_secret: shared_secret,
cipher_suite: @cipher_suite,
transcript: transcript
transcript: @transcript
)

# rejected ECH
# NOTE: It can compute (hrr_)accept_ech until client selects the
# cipher_suite.
if !sh.hrr? && use_ech?
if !transcript.include?(HRR) && !key_schedule.accept_ech?
if !@transcript.include?(HRR) && !key_schedule.accept_ech?
# 1sh SH
transcript[CH] = [ch_outer, ch_outer.serialize]
@transcript[CH] = [ch_outer, ch_outer.serialize]
@rejected_ech = true
elsif transcript.include?(HRR) &&
elsif @transcript.include?(HRR) &&
key_schedule.hrr_accept_ech? != key_schedule.accept_ech?
# 2nd SH
terminate(:illegal_parameter)
elsif transcript.include?(HRR) && !key_schedule.hrr_accept_ech?
elsif @transcript.include?(HRR) && !key_schedule.hrr_accept_ech?
# 2nd SH
transcript[CH1] = [ch1_outer, ch1_outer.serialize]
transcript[CH] = [ch_outer, ch_outer.serialize]
@transcript[CH1] = [ch1_outer, ch1_outer.serialize]
@transcript[CH] = [ch_outer, ch_outer.serialize]
@rejected_ech = true
end
end
Expand All @@ -360,7 +360,7 @@ def connect
key_schedule.client_handshake_write_iv
)
sslkeylogfile&.write_client_handshake_traffic_secret(
transcript[CH].first.random,
@transcript[CH].first.random,
key_schedule.client_handshake_traffic_secret
)
hs_rcipher = gen_cipher(
Expand All @@ -369,17 +369,17 @@ def connect
key_schedule.server_handshake_write_iv
)
sslkeylogfile&.write_server_handshake_traffic_secret(
transcript[CH].first.random,
@transcript[CH].first.random,
key_schedule.server_handshake_traffic_secret
)
@state = ClientState::WAIT_EE
when ClientState::WAIT_EE
logger.debug('ClientState::WAIT_EE')

ee, = transcript[EE] = recv_encrypted_extensions(hs_rcipher)
ee, = @transcript[EE] = recv_encrypted_extensions(hs_rcipher)
terminate(:illegal_parameter) unless ee.appearable_extensions?

ch, = transcript[CH]
ch, = @transcript[CH]
terminate(:unsupported_extension) \
unless (ee.extensions.keys - ch.extensions.keys).empty?

Expand Down Expand Up @@ -408,20 +408,20 @@ def connect
case message.msg_type
when Message::HandshakeType::CERTIFICATE,
Message::HandshakeType::COMPRESSED_CERTIFICATE
ct, = transcript[CT] = [message, orig_msg]
ct, = @transcript[CT] = [message, orig_msg]
terminate(:bad_certificate) \
if ct.is_a?(Message::CompressedCertificate) &&
!@settings[:compress_certificate_algorithms]
.include?(ct.algorithm)

ct = ct.certificate_message \
if ct.is_a?(Message::CompressedCertificate)
alert = check_invalid_certificate(ct, transcript[CH].first)
alert = check_invalid_certificate(ct, @transcript[CH].first)
terminate(alert) unless alert.nil?

@state = ClientState::WAIT_CV
when Message::HandshakeType::CERTIFICATE_REQUEST
transcript[CR] = [message, orig_msg]
@transcript[CR] = [message, orig_msg]
# TODO: client authentication
@state = ClientState::WAIT_CERT
else
Expand All @@ -430,25 +430,25 @@ def connect
when ClientState::WAIT_CERT
logger.debug('ClientState::WAIT_CERT')

ct, = transcript[CT] = recv_certificate(hs_rcipher)
ct, = @transcript[CT] = recv_certificate(hs_rcipher)
if ct.is_a?(Message::CompressedCertificate) &&
!@settings[:compress_certificate_algorithms].include?(ct.algorithm)
terminate(:bad_certificate)
elsif ct.is_a?(Message::CompressedCertificate)
ct = ct.certificate_message
end

alert = check_invalid_certificate(ct, transcript[CH].first)
alert = check_invalid_certificate(ct, @transcript[CH].first)
terminate(alert) unless alert.nil?

@state = ClientState::WAIT_CV
when ClientState::WAIT_CV
logger.debug('ClientState::WAIT_CV')

cv, = transcript[CV] = recv_certificate_verify(hs_rcipher)
cv, = @transcript[CV] = recv_certificate_verify(hs_rcipher)
digest = CipherSuite.digest(@cipher_suite)
hash = transcript.hash(digest, CT)
ct, = transcript[CT]
hash = @transcript.hash(digest, CT)
ct, = @transcript[CT]
ct = ct.certificate_message \
if ct.is_a?(Message::CompressedCertificate)
terminate(:decrypt_error) \
Expand All @@ -459,34 +459,34 @@ def connect
when ClientState::WAIT_FINISHED
logger.debug('ClientState::WAIT_FINISHED')

sf, = transcript[SF] = recv_finished(hs_rcipher)
sf, = @transcript[SF] = recv_finished(hs_rcipher)
digest = CipherSuite.digest(@cipher_suite)
terminate(:decrypt_error) unless verified_finished?(
finished: sf,
digest: digest,
finished_key: key_schedule.server_finished_key,
hash: transcript.hash(digest, CV)
hash: @transcript.hash(digest, CV)
)

if use_early_data? && succeed_early_data?
eoed = send_eoed(e_wcipher)
transcript[EOED] = [eoed, eoed.serialize]
@transcript[EOED] = [eoed, eoed.serialize]
end
# TODO: Send Certificate [+ CertificateVerify]
signature = sign_finished(
digest: digest,
finished_key: key_schedule.client_finished_key,
hash: transcript.hash(digest, EOED)
hash: @transcript.hash(digest, EOED)
)
cf = send_finished(signature, hs_wcipher)
transcript[CF] = [cf, cf.serialize]
@transcript[CF] = [cf, cf.serialize]
@alert_wcipher = @ap_wcipher = gen_cipher(
@cipher_suite,
key_schedule.client_application_write_key,
key_schedule.client_application_write_iv
)
sslkeylogfile&.write_client_traffic_secret_0(
transcript[CH].first.random,
@transcript[CH].first.random,
key_schedule.client_application_traffic_secret
)
@ap_rcipher = gen_cipher(
Expand All @@ -495,7 +495,7 @@ def connect
key_schedule.server_application_write_iv
)
sslkeylogfile&.write_server_traffic_secret_0(
transcript[CH].first.random,
@transcript[CH].first.random,
key_schedule.server_application_traffic_secret
)
@exporter_secret = key_schedule.exporter_secret
Expand Down
1 change: 1 addition & 0 deletions lib/tttls1.3/connection.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ module TTTLS13
# rubocop: disable Metrics/ClassLength
class Connection
include Logging
attr_reader :transcript

# @param socket [Socket]
def initialize(socket)
Expand Down
Loading