in rustls-0.19.1/rustls/src/server/hs.rs [627:959]
fn handle(mut self: Box<Self>, sess: &mut ServerSessionImpl, m: Message) -> NextStateOrError {
let client_hello =
require_handshake_msg!(m, HandshakeType::ClientHello, HandshakePayload::ClientHello)?;
let tls13_enabled = sess
.config
.supports_version(ProtocolVersion::TLSv1_3);
let tls12_enabled = sess
.config
.supports_version(ProtocolVersion::TLSv1_2);
trace!("we got a clienthello {:?}", client_hello);
if !client_hello
.compression_methods
.contains(&Compression::Null)
{
sess.common
.send_fatal_alert(AlertDescription::IllegalParameter);
return Err(TLSError::PeerIncompatibleError(
"client did not offer Null compression".to_string(),
));
}
if client_hello.has_duplicate_extension() {
return Err(decode_error(sess, "client sent duplicate extensions"));
}
// No handshake messages should follow this one in this flight.
check_aligned_handshake(sess)?;
// Are we doing TLS1.3?
let maybe_versions_ext = client_hello.get_versions_extension();
if let Some(versions) = maybe_versions_ext {
if versions.contains(&ProtocolVersion::TLSv1_3) && tls13_enabled {
sess.common.negotiated_version = Some(ProtocolVersion::TLSv1_3);
} else if !versions.contains(&ProtocolVersion::TLSv1_2) || !tls12_enabled {
return Err(bad_version(sess, "TLS1.2 not offered/enabled"));
}
} else if client_hello.client_version.get_u16() < ProtocolVersion::TLSv1_2.get_u16() {
return Err(bad_version(sess, "Client does not support TLSv1_2"));
} else if !tls12_enabled && tls13_enabled {
return Err(bad_version(
sess,
"Server requires TLS1.3, but client omitted versions ext",
));
}
if sess.common.negotiated_version == None {
sess.common.negotiated_version = Some(ProtocolVersion::TLSv1_2);
}
// --- Common to TLS1.2 and TLS1.3: ciphersuite and certificate selection.
// Extract and validate the SNI DNS name, if any, before giving it to
// the cert resolver. In particular, if it is invalid then we should
// send an Illegal Parameter alert instead of the Internal Error alert
// (or whatever) that we'd send if this were checked later or in a
// different way.
let sni: Option<webpki::DNSName> = match client_hello.get_sni_extension() {
Some(sni) => {
if sni.has_duplicate_names_for_type() {
return Err(decode_error(
sess,
"ClientHello SNI contains duplicate name types",
));
}
if let Some(hostname) = sni.get_single_hostname() {
Some(hostname.into())
} else {
return Err(illegal_param(
sess,
"ClientHello SNI did not contain a hostname",
));
}
}
None => None,
};
if !self.done_retry {
// save only the first SNI
save_sni(sess, sni.clone());
}
// We communicate to the upper layer what kind of key they should choose
// via the sigschemes value. Clients tend to treat this extension
// orthogonally to offered ciphersuites (even though, in TLS1.2 it is not).
// So: reduce the offered sigschemes to those compatible with the
// intersection of ciphersuites.
let mut common_suites = sess.config.ciphersuites.clone();
common_suites.retain(|scs| {
client_hello
.cipher_suites
.contains(&scs.suite)
});
let mut sigschemes_ext = client_hello
.get_sigalgs_extension()
.cloned()
.unwrap_or_else(SupportedSignatureSchemes::default);
sigschemes_ext
.retain(|scheme| suites::compatible_sigscheme_for_suites(*scheme, &common_suites));
let alpn_protocols = client_hello
.get_alpn_extension()
.map(|protos| protos.to_slices());
// Choose a certificate.
let mut certkey = {
let sni_ref = sni
.as_ref()
.map(webpki::DNSName::as_ref);
trace!("sni {:?}", sni_ref);
trace!("sig schemes {:?}", sigschemes_ext);
trace!("alpn protocols {:?}", alpn_protocols);
let alpn_slices = match alpn_protocols {
Some(ref vec) => Some(vec.as_slice()),
None => None,
};
let client_hello = ClientHello::new(sni_ref, &sigschemes_ext, alpn_slices);
let certkey = sess
.config
.cert_resolver
.resolve(client_hello);
certkey.ok_or_else(|| {
sess.common
.send_fatal_alert(AlertDescription::AccessDenied);
TLSError::General("no server certificate chain resolved".to_string())
})?
};
// Reduce our supported ciphersuites by the certificate.
// (no-op for TLS1.3)
let suitable_suites =
suites::reduce_given_sigalg(&sess.config.ciphersuites, certkey.key.algorithm());
// And version
let protocol_version = sess.common.negotiated_version.unwrap();
let suitable_suites = suites::reduce_given_version(&suitable_suites, protocol_version);
let maybe_ciphersuite = if sess.config.ignore_client_order {
suites::choose_ciphersuite_preferring_server(
&client_hello.cipher_suites,
&suitable_suites,
)
} else {
suites::choose_ciphersuite_preferring_client(
&client_hello.cipher_suites,
&suitable_suites,
)
};
if maybe_ciphersuite.is_none() {
return Err(incompatible(sess, "no ciphersuites in common"));
}
debug!(
"decided upon suite {:?}",
maybe_ciphersuite.as_ref().unwrap()
);
sess.common
.set_suite(maybe_ciphersuite.unwrap());
// Start handshake hash.
let starting_hash = sess
.common
.get_suite_assert()
.get_hash();
if !self
.handshake
.transcript
.start_hash(starting_hash)
{
sess.common
.send_fatal_alert(AlertDescription::IllegalParameter);
return Err(TLSError::PeerIncompatibleError(
"hash differed on retry".to_string(),
));
}
// Save their Random.
client_hello
.random
.write_slice(&mut self.handshake.randoms.client);
if sess.common.is_tls13() {
return self
.into_complete_tls13_client_hello_handling()
.handle_client_hello(sess, certkey, &m);
}
// -- TLS1.2 only from hereon in --
self.handshake
.transcript
.add_message(&m);
if client_hello.ems_support_offered() {
self.handshake.using_ems = true;
}
let groups_ext = client_hello
.get_namedgroups_extension()
.ok_or_else(|| incompatible(sess, "client didn't describe groups"))?;
let ecpoints_ext = client_hello
.get_ecpoints_extension()
.ok_or_else(|| incompatible(sess, "client didn't describe ec points"))?;
trace!("namedgroups {:?}", groups_ext);
trace!("ecpoints {:?}", ecpoints_ext);
if !ecpoints_ext.contains(&ECPointFormat::Uncompressed) {
sess.common
.send_fatal_alert(AlertDescription::IllegalParameter);
return Err(TLSError::PeerIncompatibleError(
"client didn't support uncompressed ec points".to_string(),
));
}
// -- If TLS1.3 is enabled, signal the downgrade in the server random
if tls13_enabled {
self.handshake
.randoms
.set_tls12_downgrade_marker();
}
// -- Check for resumption --
// We can do this either by (in order of preference):
// 1. receiving a ticket that decrypts
// 2. receiving a sessionid that is in our cache
//
// If we receive a ticket, the sessionid won't be in our
// cache, so don't check.
//
// If either works, we end up with a ServerSessionValue
// which is passed to start_resumption and concludes
// our handling of the ClientHello.
//
let mut ticket_received = false;
if let Some(ticket_ext) = client_hello.get_ticket_extension() {
if let ClientExtension::SessionTicketOffer(ref ticket) = *ticket_ext {
ticket_received = true;
debug!("Ticket received");
let maybe_resume = sess
.config
.ticketer
.decrypt(&ticket.0)
.and_then(|plain| persist::ServerSessionValue::read_bytes(&plain));
if can_resume(sess, &self.handshake, &maybe_resume) {
return self.start_resumption(
sess,
client_hello,
sni.as_ref(),
&client_hello.session_id,
maybe_resume.unwrap(),
);
} else {
debug!("Ticket didn't decrypt");
}
}
}
// If we're not offered a ticket or a potential session ID,
// allocate a session ID.
if self.handshake.session_id.is_empty() && !ticket_received {
let mut bytes = [0u8; 32];
rand::fill_random(&mut bytes);
self.handshake.session_id = SessionID::new(&bytes);
}
// Perhaps resume? If we received a ticket, the sessionid
// does not correspond to a real session.
if !client_hello.session_id.is_empty() && !ticket_received {
let maybe_resume = sess
.config
.session_storage
.get(&client_hello.session_id.get_encoding())
.and_then(|x| persist::ServerSessionValue::read_bytes(&x));
if can_resume(sess, &self.handshake, &maybe_resume) {
return self.start_resumption(
sess,
client_hello,
sni.as_ref(),
&client_hello.session_id,
maybe_resume.unwrap(),
);
}
}
// Now we have chosen a ciphersuite, we can make kx decisions.
let sigschemes = sess
.common
.get_suite_assert()
.resolve_sig_schemes(&sigschemes_ext);
if sigschemes.is_empty() {
return Err(incompatible(sess, "no supported sig scheme"));
}
let group = suites::KeyExchange::supported_groups()
.iter()
.filter(|group| groups_ext.contains(group))
.nth(0)
.cloned()
.ok_or_else(|| incompatible(sess, "no supported group"))?;
let ecpoint = ECPointFormatList::supported()
.iter()
.filter(|format| ecpoints_ext.contains(format))
.nth(0)
.cloned()
.ok_or_else(|| incompatible(sess, "no supported point format"))?;
debug_assert_eq!(ecpoint, ECPointFormat::Uncompressed);
self.emit_server_hello(sess, Some(&mut certkey), client_hello, None)?;
self.emit_certificate(sess, &mut certkey);
self.emit_cert_status(sess, &mut certkey);
let kx = self.emit_server_kx(sess, sigschemes, group, &mut certkey)?;
let doing_client_auth = self.emit_certificate_req(sess)?;
self.emit_server_hello_done(sess);
if doing_client_auth {
Ok(self.into_expect_tls12_certificate(kx))
} else {
Ok(self.into_expect_tls12_client_kx(kx))
}
}