in rustls-0.19.1/rustls/src/client/hs.rs [188:415]
fn emit_client_hello_for_retry(
sess: &mut ClientSessionImpl,
mut handshake: HandshakeDetails,
mut hello: ClientHelloDetails,
retryreq: Option<&HelloRetryRequest>,
) -> NextState {
// Do we have a SessionID or ticket cached for this host?
handshake.resuming_session = find_session(sess, handshake.dns_name.as_ref());
let (session_id, ticket, resume_version) = if handshake.resuming_session.is_some() {
let resuming = handshake
.resuming_session
.as_mut()
.unwrap();
if resuming.version == ProtocolVersion::TLSv1_2 {
random_sessionid_for_ticket(resuming);
}
debug!("Resuming session");
(
resuming.session_id,
resuming.ticket.0.clone(),
resuming.version,
)
} else {
debug!("Not resuming any session");
if handshake.session_id.is_empty() && !sess.common.is_quic() {
handshake.session_id = random_sessionid();
}
(
handshake.session_id,
Vec::new(),
ProtocolVersion::Unknown(0),
)
};
let support_tls12 = sess
.config
.supports_version(ProtocolVersion::TLSv1_2);
let support_tls13 = sess
.config
.supports_version(ProtocolVersion::TLSv1_3);
let mut supported_versions = Vec::new();
if support_tls13 {
supported_versions.push(ProtocolVersion::TLSv1_3);
}
if support_tls12 {
supported_versions.push(ProtocolVersion::TLSv1_2);
}
let mut exts = Vec::new();
if !supported_versions.is_empty() {
exts.push(ClientExtension::SupportedVersions(supported_versions));
}
if sess.config.enable_sni {
exts.push(ClientExtension::make_sni(handshake.dns_name.as_ref()));
}
exts.push(ClientExtension::ECPointFormats(
ECPointFormatList::supported(),
));
exts.push(ClientExtension::NamedGroups(
suites::KeyExchange::supported_groups().to_vec(),
));
exts.push(ClientExtension::SignatureAlgorithms(
sess.config
.get_verifier()
.supported_verify_schemes(),
));
exts.push(ClientExtension::ExtendedMasterSecretRequest);
exts.push(ClientExtension::CertificateStatusRequest(
CertificateStatusRequest::build_ocsp(),
));
if sess.config.ct_logs.is_some() {
exts.push(ClientExtension::SignedCertificateTimestampRequest);
}
if support_tls13 {
tls13::choose_kx_groups(sess, &mut exts, &mut hello, &mut handshake, retryreq);
}
if let Some(cookie) = retryreq.and_then(HelloRetryRequest::get_cookie) {
exts.push(ClientExtension::Cookie(cookie.clone()));
}
if support_tls13 && sess.config.enable_tickets {
// We could support PSK_KE here too. Such connections don't
// have forward secrecy, and are similar to TLS1.2 resumption.
let psk_modes = vec![PSKKeyExchangeMode::PSK_DHE_KE];
exts.push(ClientExtension::PresharedKeyModes(psk_modes));
}
if !sess.config.alpn_protocols.is_empty() {
exts.push(ClientExtension::Protocols(ProtocolNameList::from_slices(
&sess
.config
.alpn_protocols
.iter()
.map(|proto| &proto[..])
.collect::<Vec<_>>(),
)));
}
// Extra extensions must be placed before the PSK extension
exts.extend(handshake.extra_exts.iter().cloned());
let fill_in_binder = if support_tls13
&& sess.config.enable_tickets
&& resume_version == ProtocolVersion::TLSv1_3
&& !ticket.is_empty()
{
tls13::prepare_resumption(sess, ticket, &handshake, &mut exts, retryreq.is_some())
} else if sess.config.enable_tickets {
// If we have a ticket, include it. Otherwise, request one.
if ticket.is_empty() {
exts.push(ClientExtension::SessionTicketRequest);
} else {
exts.push(ClientExtension::SessionTicketOffer(Payload::new(ticket)));
}
false
} else {
false
};
// Note what extensions we sent.
hello.sent_extensions = exts
.iter()
.map(ClientExtension::get_type)
.collect();
let mut chp = HandshakeMessagePayload {
typ: HandshakeType::ClientHello,
payload: HandshakePayload::ClientHello(ClientHelloPayload {
client_version: ProtocolVersion::TLSv1_2,
random: Random::from_slice(&handshake.randoms.client),
session_id,
cipher_suites: sess.get_cipher_suites(),
compression_methods: vec![Compression::Null],
extensions: exts,
}),
};
let early_key_schedule = if fill_in_binder {
Some(tls13::fill_in_psk_binder(sess, &mut handshake, &mut chp))
} else {
None
};
let ch = Message {
typ: ContentType::Handshake,
// "This value MUST be set to 0x0303 for all records generated
// by a TLS 1.3 implementation other than an initial ClientHello
// (i.e., one not generated after a HelloRetryRequest)"
version: if retryreq.is_some() {
ProtocolVersion::TLSv1_2
} else {
ProtocolVersion::TLSv1_0
},
payload: MessagePayload::Handshake(chp),
};
if retryreq.is_some() {
// send dummy CCS to fool middleboxes prior
// to second client hello
tls13::emit_fake_ccs(&mut handshake, sess);
}
trace!("Sending ClientHello {:#?}", ch);
handshake.transcript.add_message(&ch);
sess.common.send_msg(ch, false);
// Calculate the hash of ClientHello and use it to derive EarlyTrafficSecret
if sess.early_data.is_enabled() {
// For middlebox compatibility
tls13::emit_fake_ccs(&mut handshake, sess);
// It is safe to call unwrap() because fill_in_binder is true.
let resuming_suite = handshake
.resuming_session
.as_ref()
.and_then(|resume| sess.find_cipher_suite(resume.cipher_suite))
.unwrap();
let client_hello_hash = handshake
.transcript
.get_hash_given(resuming_suite.get_hash(), &[]);
let client_early_traffic_secret = early_key_schedule
.as_ref()
.unwrap()
.client_early_traffic_secret(
&client_hello_hash,
&*sess.config.key_log,
&handshake.randoms.client,
);
// Set early data encryption key
sess.common
.record_layer
.set_message_encrypter(cipher::new_tls13_write(
resuming_suite,
&client_early_traffic_secret,
));
#[cfg(feature = "quic")]
{
sess.common.quic.early_secret = Some(client_early_traffic_secret);
}
// Now the client can send encrypted early data
sess.common.early_traffic = true;
trace!("Starting early data traffic");
}
let next = ExpectServerHello {
handshake,
hello,
early_key_schedule,
server_cert: ServerCertDetails::new(),
may_send_cert_status: false,
must_issue_new_ticket: false,
};
if support_tls13 && retryreq.is_none() {
Box::new(ExpectServerHelloOrHelloRetryRequest(next))
} else {
Box::new(next)
}
}