mirror of https://github.com/ctz/rustls
Split up client::hs
This now contains only functionality shared between protocol versions. Version-specific stuff is in tls12 and tls13 modules.
This commit is contained in:
parent
0f540c9014
commit
bdd0a37faa
1510
src/client/hs.rs
1510
src/client/hs.rs
File diff suppressed because it is too large
Load Diff
|
@ -25,7 +25,10 @@ use std::mem;
|
|||
use sct;
|
||||
use webpki;
|
||||
|
||||
#[macro_use]
|
||||
mod hs;
|
||||
mod tls12;
|
||||
mod tls13;
|
||||
mod common;
|
||||
pub mod handy;
|
||||
|
||||
|
|
|
@ -0,0 +1,758 @@
|
|||
use crate::msgs::enums::{ContentType, HandshakeType};
|
||||
use crate::msgs::enums::{ProtocolVersion, AlertDescription};
|
||||
use crate::msgs::message::{Message, MessagePayload};
|
||||
use crate::msgs::base::{Payload, PayloadU8};
|
||||
use crate::msgs::handshake::{HandshakePayload, HandshakeMessagePayload};
|
||||
use crate::msgs::handshake::DecomposedSignatureScheme;
|
||||
use crate::msgs::handshake::ServerKeyExchangePayload;
|
||||
use crate::msgs::handshake::DigitallySignedStruct;
|
||||
use crate::msgs::enums::ClientCertificateType;
|
||||
use crate::msgs::codec::Codec;
|
||||
use crate::msgs::persist;
|
||||
use crate::msgs::ccs::ChangeCipherSpecPayload;
|
||||
use crate::client::ClientSessionImpl;
|
||||
use crate::session::SessionSecrets;
|
||||
use crate::suites;
|
||||
use crate::verify;
|
||||
use crate::ticketer;
|
||||
#[cfg(feature = "logging")]
|
||||
use crate::log::{debug, trace, warn};
|
||||
use crate::error::TLSError;
|
||||
use crate::handshake::{check_message, check_handshake_message};
|
||||
|
||||
use crate::client::common::{ServerCertDetails, ServerKXDetails, HandshakeDetails};
|
||||
use crate::client::common::{ReceivedTicketDetails, ClientAuthDetails};
|
||||
use crate::client::hs;
|
||||
|
||||
use std::mem;
|
||||
use ring::constant_time;
|
||||
|
||||
pub struct ExpectCertificate {
|
||||
pub handshake: HandshakeDetails,
|
||||
pub server_cert: ServerCertDetails,
|
||||
pub may_send_cert_status: bool,
|
||||
pub must_issue_new_ticket: bool,
|
||||
}
|
||||
|
||||
impl ExpectCertificate {
|
||||
fn into_expect_certificate_status_or_server_kx(self) -> hs::NextState {
|
||||
Box::new(ExpectCertificateStatusOrServerKX {
|
||||
handshake: self.handshake,
|
||||
server_cert: self.server_cert,
|
||||
must_issue_new_ticket: self.must_issue_new_ticket,
|
||||
})
|
||||
}
|
||||
|
||||
fn into_expect_server_kx(self) -> hs::NextState {
|
||||
Box::new(ExpectServerKX {
|
||||
handshake: self.handshake,
|
||||
server_cert: self.server_cert,
|
||||
must_issue_new_ticket: self.must_issue_new_ticket,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl hs::State for ExpectCertificate {
|
||||
fn check_message(&self, m: &Message) -> Result<(), TLSError> {
|
||||
check_handshake_message(m, &[HandshakeType::Certificate])
|
||||
}
|
||||
|
||||
fn handle(mut self: Box<Self>, sess: &mut ClientSessionImpl, m: Message) -> hs::NextStateOrError {
|
||||
let cert_chain = extract_handshake!(m, HandshakePayload::Certificate).unwrap();
|
||||
sess.common.hs_transcript.add_message(&m);
|
||||
|
||||
self.server_cert.cert_chain = cert_chain.clone();
|
||||
|
||||
if self.may_send_cert_status {
|
||||
Ok(self.into_expect_certificate_status_or_server_kx())
|
||||
} else {
|
||||
Ok(self.into_expect_server_kx())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct ExpectCertificateStatus {
|
||||
handshake: HandshakeDetails,
|
||||
server_cert: ServerCertDetails,
|
||||
must_issue_new_ticket: bool,
|
||||
}
|
||||
|
||||
impl ExpectCertificateStatus {
|
||||
fn into_expect_server_kx(self) -> hs::NextState {
|
||||
Box::new(ExpectServerKX {
|
||||
handshake: self.handshake,
|
||||
server_cert: self.server_cert,
|
||||
must_issue_new_ticket: self.must_issue_new_ticket,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl hs::State for ExpectCertificateStatus {
|
||||
fn check_message(&self, m: &Message) -> Result<(), TLSError> {
|
||||
check_handshake_message(m, &[HandshakeType::CertificateStatus])
|
||||
}
|
||||
|
||||
fn handle(mut self: Box<Self>, sess: &mut ClientSessionImpl, m: Message) -> hs::NextStateOrError {
|
||||
sess.common.hs_transcript.add_message(&m);
|
||||
let mut status = extract_handshake_mut!(m, HandshakePayload::CertificateStatus).unwrap();
|
||||
|
||||
self.server_cert.ocsp_response = status.take_ocsp_response();
|
||||
debug!("Server stapled OCSP response is {:?}", self.server_cert.ocsp_response);
|
||||
Ok(self.into_expect_server_kx())
|
||||
}
|
||||
}
|
||||
|
||||
struct ExpectCertificateStatusOrServerKX {
|
||||
handshake: HandshakeDetails,
|
||||
server_cert: ServerCertDetails,
|
||||
must_issue_new_ticket: bool,
|
||||
}
|
||||
|
||||
impl ExpectCertificateStatusOrServerKX {
|
||||
fn into_expect_server_kx(self) -> hs::NextState {
|
||||
Box::new(ExpectServerKX {
|
||||
handshake: self.handshake,
|
||||
server_cert: self.server_cert,
|
||||
must_issue_new_ticket: self.must_issue_new_ticket,
|
||||
})
|
||||
}
|
||||
|
||||
fn into_expect_certificate_status(self) -> hs::NextState {
|
||||
Box::new(ExpectCertificateStatus {
|
||||
handshake: self.handshake,
|
||||
server_cert: self.server_cert,
|
||||
must_issue_new_ticket: self.must_issue_new_ticket,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl hs::State for ExpectCertificateStatusOrServerKX {
|
||||
fn check_message(&self, m: &Message) -> Result<(), TLSError> {
|
||||
check_handshake_message(m,
|
||||
&[HandshakeType::ServerKeyExchange,
|
||||
HandshakeType::CertificateStatus])
|
||||
}
|
||||
|
||||
fn handle(self: Box<Self>, sess: &mut ClientSessionImpl, m: Message) -> hs::NextStateOrError {
|
||||
if m.is_handshake_type(HandshakeType::ServerKeyExchange) {
|
||||
self.into_expect_server_kx().handle(sess, m)
|
||||
} else {
|
||||
self.into_expect_certificate_status().handle(sess, m)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct ExpectServerKX {
|
||||
handshake: HandshakeDetails,
|
||||
server_cert: ServerCertDetails,
|
||||
must_issue_new_ticket: bool,
|
||||
}
|
||||
|
||||
impl ExpectServerKX {
|
||||
fn into_expect_server_done_or_certreq(self, skx: ServerKXDetails) -> hs::NextState {
|
||||
Box::new(ExpectServerDoneOrCertReq {
|
||||
handshake: self.handshake,
|
||||
server_cert: self.server_cert,
|
||||
server_kx: skx,
|
||||
must_issue_new_ticket: self.must_issue_new_ticket,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl hs::State for ExpectServerKX {
|
||||
fn check_message(&self, m: &Message) -> Result<(), TLSError> {
|
||||
check_handshake_message(m, &[HandshakeType::ServerKeyExchange])
|
||||
}
|
||||
|
||||
fn handle(self: Box<Self>, sess: &mut ClientSessionImpl, m: Message) -> hs::NextStateOrError {
|
||||
let opaque_kx = extract_handshake!(m, HandshakePayload::ServerKeyExchange).unwrap();
|
||||
let maybe_decoded_kx = opaque_kx.unwrap_given_kxa(&sess.common.get_suite_assert().kx);
|
||||
sess.common.hs_transcript.add_message(&m);
|
||||
|
||||
if maybe_decoded_kx.is_none() {
|
||||
sess.common.send_fatal_alert(AlertDescription::DecodeError);
|
||||
return Err(TLSError::CorruptMessagePayload(ContentType::Handshake));
|
||||
}
|
||||
|
||||
let decoded_kx = maybe_decoded_kx.unwrap();
|
||||
|
||||
// Save the signature and signed parameters for later verification.
|
||||
let mut kx_params = Vec::new();
|
||||
decoded_kx.encode_params(&mut kx_params);
|
||||
let skx = ServerKXDetails::new(kx_params, decoded_kx.get_sig().unwrap());
|
||||
|
||||
#[cfg_attr(not(feature = "logging"), allow(unused_variables))]
|
||||
{
|
||||
if let ServerKeyExchangePayload::ECDHE(ecdhe) = decoded_kx {
|
||||
debug!("ECDHE curve is {:?}", ecdhe.params.curve_params);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(self.into_expect_server_done_or_certreq(skx))
|
||||
}
|
||||
}
|
||||
|
||||
fn emit_certificate(client_auth: &mut ClientAuthDetails,
|
||||
sess: &mut ClientSessionImpl) {
|
||||
let chosen_cert = client_auth.cert.take();
|
||||
|
||||
let cert = Message {
|
||||
typ: ContentType::Handshake,
|
||||
version: ProtocolVersion::TLSv1_2,
|
||||
payload: MessagePayload::Handshake(HandshakeMessagePayload {
|
||||
typ: HandshakeType::Certificate,
|
||||
payload: HandshakePayload::Certificate(chosen_cert.unwrap_or_else(Vec::new)),
|
||||
}),
|
||||
};
|
||||
|
||||
sess.common.hs_transcript.add_message(&cert);
|
||||
sess.common.send_msg(cert, false);
|
||||
}
|
||||
|
||||
fn emit_clientkx(sess: &mut ClientSessionImpl,
|
||||
kxd: &suites::KeyExchangeResult) {
|
||||
let mut buf = Vec::new();
|
||||
let ecpoint = PayloadU8::new(Vec::from(kxd.pubkey.as_ref()));
|
||||
ecpoint.encode(&mut buf);
|
||||
let pubkey = Payload::new(buf);
|
||||
|
||||
let ckx = Message {
|
||||
typ: ContentType::Handshake,
|
||||
version: ProtocolVersion::TLSv1_2,
|
||||
payload: MessagePayload::Handshake(HandshakeMessagePayload {
|
||||
typ: HandshakeType::ClientKeyExchange,
|
||||
payload: HandshakePayload::ClientKeyExchange(pubkey),
|
||||
}),
|
||||
};
|
||||
|
||||
sess.common.hs_transcript.add_message(&ckx);
|
||||
sess.common.send_msg(ckx, false);
|
||||
}
|
||||
|
||||
fn emit_certverify(client_auth: &mut ClientAuthDetails,
|
||||
sess: &mut ClientSessionImpl) -> Result<(), TLSError> {
|
||||
if client_auth.signer.is_none() {
|
||||
trace!("Not sending CertificateVerify, no key");
|
||||
sess.common.hs_transcript.abandon_client_auth();
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let message = sess.common.hs_transcript.take_handshake_buf();
|
||||
let signer = client_auth.signer.take().unwrap();
|
||||
let scheme = signer.get_scheme();
|
||||
let sig = signer.sign(&message)?;
|
||||
let body = DigitallySignedStruct::new(scheme, sig);
|
||||
|
||||
let m = Message {
|
||||
typ: ContentType::Handshake,
|
||||
version: ProtocolVersion::TLSv1_2,
|
||||
payload: MessagePayload::Handshake(HandshakeMessagePayload {
|
||||
typ: HandshakeType::CertificateVerify,
|
||||
payload: HandshakePayload::CertificateVerify(body),
|
||||
}),
|
||||
};
|
||||
|
||||
sess.common.hs_transcript.add_message(&m);
|
||||
sess.common.send_msg(m, false);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn emit_ccs(sess: &mut ClientSessionImpl) {
|
||||
let ccs = Message {
|
||||
typ: ContentType::ChangeCipherSpec,
|
||||
version: ProtocolVersion::TLSv1_2,
|
||||
payload: MessagePayload::ChangeCipherSpec(ChangeCipherSpecPayload {}),
|
||||
};
|
||||
|
||||
sess.common.send_msg(ccs, false);
|
||||
sess.common.we_now_encrypting();
|
||||
}
|
||||
|
||||
fn emit_finished(sess: &mut ClientSessionImpl) {
|
||||
let vh = sess.common.hs_transcript.get_current_hash();
|
||||
let verify_data = sess.common.secrets
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.client_verify_data(&vh);
|
||||
let verify_data_payload = Payload::new(verify_data);
|
||||
|
||||
let f = Message {
|
||||
typ: ContentType::Handshake,
|
||||
version: ProtocolVersion::TLSv1_2,
|
||||
payload: MessagePayload::Handshake(HandshakeMessagePayload {
|
||||
typ: HandshakeType::Finished,
|
||||
payload: HandshakePayload::Finished(verify_data_payload),
|
||||
}),
|
||||
};
|
||||
|
||||
sess.common.hs_transcript.add_message(&f);
|
||||
sess.common.send_msg(f, true);
|
||||
}
|
||||
|
||||
// --- Either a CertificateRequest, or a ServerHelloDone. ---
|
||||
// Existence of the CertificateRequest tells us the server is asking for
|
||||
// client auth. Otherwise we go straight to ServerHelloDone.
|
||||
struct ExpectCertificateRequest {
|
||||
handshake: HandshakeDetails,
|
||||
server_cert: ServerCertDetails,
|
||||
server_kx: ServerKXDetails,
|
||||
must_issue_new_ticket: bool,
|
||||
}
|
||||
|
||||
impl ExpectCertificateRequest {
|
||||
fn into_expect_server_done(self, client_auth: ClientAuthDetails) -> hs::NextState {
|
||||
Box::new(ExpectServerDone {
|
||||
handshake: self.handshake,
|
||||
server_cert: self.server_cert,
|
||||
server_kx: self.server_kx,
|
||||
client_auth: Some(client_auth),
|
||||
must_issue_new_ticket: self.must_issue_new_ticket,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl hs::State for ExpectCertificateRequest {
|
||||
fn check_message(&self, m: &Message) -> Result<(), TLSError> {
|
||||
check_handshake_message(m, &[HandshakeType::CertificateRequest])
|
||||
}
|
||||
|
||||
fn handle(self: Box<Self>, sess: &mut ClientSessionImpl, m: Message) -> hs::NextStateOrError {
|
||||
let certreq = extract_handshake!(m, HandshakePayload::CertificateRequest).unwrap();
|
||||
sess.common.hs_transcript.add_message(&m);
|
||||
debug!("Got CertificateRequest {:?}", certreq);
|
||||
|
||||
let mut client_auth = ClientAuthDetails::new();
|
||||
|
||||
// The RFC jovially describes the design here as 'somewhat complicated'
|
||||
// and 'somewhat underspecified'. So thanks for that.
|
||||
|
||||
// We only support RSA signing at the moment. If you don't support that,
|
||||
// we're not doing client auth.
|
||||
if !certreq.certtypes.contains(&ClientCertificateType::RSASign) {
|
||||
warn!("Server asked for client auth but without RSASign");
|
||||
return Ok(self.into_expect_server_done(client_auth));
|
||||
}
|
||||
|
||||
let canames = certreq.canames
|
||||
.iter()
|
||||
.map(|p| p.0.as_slice())
|
||||
.collect::<Vec<&[u8]>>();
|
||||
let maybe_certkey =
|
||||
sess.config.client_auth_cert_resolver.resolve(&canames, &certreq.sigschemes);
|
||||
|
||||
if let Some(mut certkey) = maybe_certkey {
|
||||
debug!("Attempting client auth");
|
||||
let maybe_signer = certkey.key.choose_scheme(&certreq.sigschemes);
|
||||
client_auth.cert = Some(certkey.take_cert());
|
||||
client_auth.signer = maybe_signer;
|
||||
} else {
|
||||
debug!("Client auth requested but no cert/sigscheme available");
|
||||
}
|
||||
|
||||
Ok(self.into_expect_server_done(client_auth))
|
||||
}
|
||||
}
|
||||
|
||||
struct ExpectServerDoneOrCertReq {
|
||||
handshake: HandshakeDetails,
|
||||
server_cert: ServerCertDetails,
|
||||
server_kx: ServerKXDetails,
|
||||
must_issue_new_ticket: bool,
|
||||
}
|
||||
|
||||
impl ExpectServerDoneOrCertReq {
|
||||
fn into_expect_certificate_req(self) -> hs::NextState {
|
||||
Box::new(ExpectCertificateRequest {
|
||||
handshake: self.handshake,
|
||||
server_cert: self.server_cert,
|
||||
server_kx: self.server_kx,
|
||||
must_issue_new_ticket: self.must_issue_new_ticket,
|
||||
})
|
||||
}
|
||||
|
||||
fn into_expect_server_done(self) -> hs::NextState {
|
||||
Box::new(ExpectServerDone {
|
||||
handshake: self.handshake,
|
||||
server_cert: self.server_cert,
|
||||
server_kx: self.server_kx,
|
||||
client_auth: None,
|
||||
must_issue_new_ticket: self.must_issue_new_ticket,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl hs::State for ExpectServerDoneOrCertReq {
|
||||
fn check_message(&self, m: &Message) -> Result<(), TLSError> {
|
||||
check_handshake_message(m,
|
||||
&[HandshakeType::CertificateRequest,
|
||||
HandshakeType::ServerHelloDone])
|
||||
}
|
||||
|
||||
fn handle(self: Box<Self>, sess: &mut ClientSessionImpl, m: Message) -> hs::NextStateOrError {
|
||||
if extract_handshake!(m, HandshakePayload::CertificateRequest).is_some() {
|
||||
self.into_expect_certificate_req().handle(sess, m)
|
||||
} else {
|
||||
sess.common.hs_transcript.abandon_client_auth();
|
||||
self.into_expect_server_done().handle(sess, m)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
struct ExpectServerDone {
|
||||
handshake: HandshakeDetails,
|
||||
server_cert: ServerCertDetails,
|
||||
server_kx: ServerKXDetails,
|
||||
client_auth: Option<ClientAuthDetails>,
|
||||
must_issue_new_ticket: bool,
|
||||
}
|
||||
|
||||
impl ExpectServerDone {
|
||||
fn into_expect_new_ticket(self,
|
||||
certv: verify::ServerCertVerified,
|
||||
sigv: verify::HandshakeSignatureValid) -> hs::NextState {
|
||||
Box::new(ExpectNewTicket {
|
||||
handshake: self.handshake,
|
||||
resuming: false,
|
||||
cert_verified: certv,
|
||||
sig_verified: sigv,
|
||||
})
|
||||
}
|
||||
|
||||
fn into_expect_ccs(self,
|
||||
certv: verify::ServerCertVerified,
|
||||
sigv: verify::HandshakeSignatureValid) -> hs::NextState {
|
||||
Box::new(ExpectCCS {
|
||||
handshake: self.handshake,
|
||||
ticket: ReceivedTicketDetails::new(),
|
||||
resuming: false,
|
||||
cert_verified: certv,
|
||||
sig_verified: sigv,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl hs::State for ExpectServerDone {
|
||||
fn check_message(&self, m: &Message) -> Result<(), TLSError> {
|
||||
check_handshake_message(m, &[HandshakeType::ServerHelloDone])
|
||||
}
|
||||
|
||||
fn handle(self: Box<Self>, sess: &mut ClientSessionImpl, m: Message) -> hs::NextStateOrError {
|
||||
let mut st = *self;
|
||||
sess.common.hs_transcript.add_message(&m);
|
||||
|
||||
debug!("Server cert is {:?}", st.server_cert.cert_chain);
|
||||
debug!("Server DNS name is {:?}", st.handshake.dns_name);
|
||||
|
||||
// 1. Verify the cert chain.
|
||||
// 2. Verify any SCTs provided with the certificate.
|
||||
// 3. Verify that the top certificate signed their kx.
|
||||
// 4. If doing client auth, send our Certificate.
|
||||
// 5. Complete the key exchange:
|
||||
// a) generate our kx pair
|
||||
// b) emit a ClientKeyExchange containing it
|
||||
// c) if doing client auth, emit a CertificateVerify
|
||||
// d) emit a CCS
|
||||
// e) derive the shared keys, and start encryption
|
||||
// 6. emit a Finished, our first encrypted message under the new keys.
|
||||
|
||||
// 1.
|
||||
if st.server_cert.cert_chain.is_empty() {
|
||||
return Err(TLSError::NoCertificatesPresented);
|
||||
}
|
||||
|
||||
let certv = sess.config
|
||||
.get_verifier()
|
||||
.verify_server_cert(&sess.config.root_store,
|
||||
&st.server_cert.cert_chain,
|
||||
st.handshake.dns_name.as_ref(),
|
||||
&st.server_cert.ocsp_response)
|
||||
.map_err(|err| hs::send_cert_error_alert(sess, err))?;
|
||||
|
||||
// 2. Verify any included SCTs.
|
||||
match (st.server_cert.scts.as_ref(), sess.config.ct_logs) {
|
||||
(Some(scts), Some(logs)) => {
|
||||
verify::verify_scts(&st.server_cert.cert_chain[0],
|
||||
scts,
|
||||
logs)?;
|
||||
}
|
||||
(_, _) => {}
|
||||
}
|
||||
|
||||
// 3.
|
||||
// Build up the contents of the signed message.
|
||||
// It's ClientHello.random || ServerHello.random || ServerKeyExchange.params
|
||||
let sigv = {
|
||||
let mut message = Vec::new();
|
||||
message.extend_from_slice(&st.handshake.randoms.client);
|
||||
message.extend_from_slice(&st.handshake.randoms.server);
|
||||
message.extend_from_slice(&st.server_kx.kx_params);
|
||||
|
||||
// Check the signature is compatible with the ciphersuite.
|
||||
let sig = &st.server_kx.kx_sig;
|
||||
let scs = sess.common.get_suite_assert();
|
||||
if scs.sign != sig.scheme.sign() {
|
||||
let error_message =
|
||||
format!("peer signed kx with wrong algorithm (got {:?} expect {:?})",
|
||||
sig.scheme.sign(), scs.sign);
|
||||
return Err(TLSError::PeerMisbehavedError(error_message));
|
||||
}
|
||||
|
||||
verify::verify_signed_struct(&message,
|
||||
&st.server_cert.cert_chain[0],
|
||||
sig)
|
||||
.map_err(|err| hs::send_cert_error_alert(sess, err))?
|
||||
};
|
||||
sess.server_cert_chain = st.server_cert.take_chain();
|
||||
|
||||
// 4.
|
||||
if st.client_auth.is_some() {
|
||||
emit_certificate(st.client_auth.as_mut().unwrap(),
|
||||
sess);
|
||||
}
|
||||
|
||||
// 5a.
|
||||
let kxd = sess.common.get_suite_assert()
|
||||
.do_client_kx(&st.server_kx.kx_params)
|
||||
.ok_or_else(|| TLSError::PeerMisbehavedError("key exchange failed".to_string()))?;
|
||||
|
||||
// 5b.
|
||||
emit_clientkx(sess, &kxd);
|
||||
// nb. EMS handshake hash only runs up to ClientKeyExchange.
|
||||
let handshake_hash = sess.common.hs_transcript.get_current_hash();
|
||||
|
||||
// 5c.
|
||||
if st.client_auth.is_some() {
|
||||
emit_certverify(st.client_auth.as_mut().unwrap(),
|
||||
sess)?;
|
||||
}
|
||||
|
||||
// 5d.
|
||||
emit_ccs(sess);
|
||||
|
||||
// 5e. Now commit secrets.
|
||||
let hashalg = sess.common.get_suite_assert().get_hash();
|
||||
let secrets = if st.handshake.using_ems {
|
||||
SessionSecrets::new_ems(&st.handshake.randoms,
|
||||
&handshake_hash,
|
||||
hashalg,
|
||||
&kxd.premaster_secret)
|
||||
} else {
|
||||
SessionSecrets::new(&st.handshake.randoms,
|
||||
hashalg,
|
||||
&kxd.premaster_secret)
|
||||
};
|
||||
sess.config.key_log.log(sess.common.protocol.labels().client_random,
|
||||
&secrets.randoms.client,
|
||||
&secrets.master_secret);
|
||||
sess.common.start_encryption_tls12(secrets);
|
||||
|
||||
// 6.
|
||||
emit_finished(sess);
|
||||
|
||||
if st.must_issue_new_ticket {
|
||||
Ok(st.into_expect_new_ticket(certv, sigv))
|
||||
} else {
|
||||
Ok(st.into_expect_ccs(certv, sigv))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// -- Waiting for their CCS --
|
||||
pub struct ExpectCCS {
|
||||
pub handshake: HandshakeDetails,
|
||||
pub ticket: ReceivedTicketDetails,
|
||||
pub resuming: bool,
|
||||
pub cert_verified: verify::ServerCertVerified,
|
||||
pub sig_verified: verify::HandshakeSignatureValid,
|
||||
}
|
||||
|
||||
impl ExpectCCS {
|
||||
fn into_expect_finished(self) -> hs::NextState {
|
||||
Box::new(ExpectFinished {
|
||||
handshake: self.handshake,
|
||||
ticket: self.ticket,
|
||||
resuming: self.resuming,
|
||||
cert_verified: self.cert_verified,
|
||||
sig_verified: self.sig_verified,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl hs::State for ExpectCCS {
|
||||
fn check_message(&self, m: &Message) -> Result<(), TLSError> {
|
||||
check_message(m, &[ContentType::ChangeCipherSpec], &[])
|
||||
}
|
||||
|
||||
fn handle(self: Box<Self>, sess: &mut ClientSessionImpl, _m: Message) -> hs::NextStateOrError {
|
||||
// CCS should not be received interleaved with fragmented handshake-level
|
||||
// message.
|
||||
if !sess.common.handshake_joiner.is_empty() {
|
||||
warn!("CCS received interleaved with fragmented handshake");
|
||||
return Err(TLSError::InappropriateMessage {
|
||||
expect_types: vec![ ContentType::Handshake ],
|
||||
got_type: ContentType::ChangeCipherSpec,
|
||||
});
|
||||
}
|
||||
|
||||
// nb. msgs layer validates trivial contents of CCS
|
||||
sess.common.peer_now_encrypting();
|
||||
|
||||
Ok(self.into_expect_finished())
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ExpectNewTicket {
|
||||
pub handshake: HandshakeDetails,
|
||||
pub resuming: bool,
|
||||
pub cert_verified: verify::ServerCertVerified,
|
||||
pub sig_verified: verify::HandshakeSignatureValid,
|
||||
}
|
||||
|
||||
impl ExpectNewTicket {
|
||||
fn into_expect_ccs(self, ticket: ReceivedTicketDetails) -> hs::NextState {
|
||||
Box::new(ExpectCCS {
|
||||
handshake: self.handshake,
|
||||
ticket,
|
||||
resuming: self.resuming,
|
||||
cert_verified: self.cert_verified,
|
||||
sig_verified: self.sig_verified,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl hs::State for ExpectNewTicket {
|
||||
fn check_message(&self, m: &Message) -> Result<(), TLSError> {
|
||||
check_handshake_message(m, &[HandshakeType::NewSessionTicket])
|
||||
}
|
||||
|
||||
fn handle(self: Box<Self>, sess: &mut ClientSessionImpl, m: Message) -> hs::NextStateOrError {
|
||||
sess.common.hs_transcript.add_message(&m);
|
||||
|
||||
let nst = extract_handshake_mut!(m, HandshakePayload::NewSessionTicket).unwrap();
|
||||
let recvd = ReceivedTicketDetails::from(nst.ticket.0, nst.lifetime_hint);
|
||||
Ok(self.into_expect_ccs(recvd))
|
||||
}
|
||||
}
|
||||
|
||||
// -- Waiting for their finished --
|
||||
fn save_session(handshake: &mut HandshakeDetails,
|
||||
recvd_ticket: &mut ReceivedTicketDetails,
|
||||
sess: &mut ClientSessionImpl) {
|
||||
// Save a ticket. If we got a new ticket, save that. Otherwise, save the
|
||||
// original ticket again.
|
||||
let mut ticket = mem::replace(&mut recvd_ticket.new_ticket, Vec::new());
|
||||
if ticket.is_empty() && handshake.resuming_session.is_some() {
|
||||
ticket = handshake.resuming_session.as_mut().unwrap().take_ticket();
|
||||
}
|
||||
|
||||
if handshake.session_id.is_empty() && ticket.is_empty() {
|
||||
debug!("Session not saved: server didn't allocate id or ticket");
|
||||
return;
|
||||
}
|
||||
|
||||
let key = persist::ClientSessionKey::session_for_dns_name(handshake.dns_name.as_ref());
|
||||
|
||||
let scs = sess.common.get_suite_assert();
|
||||
let master_secret = sess.common.secrets.as_ref().unwrap().get_master_secret();
|
||||
let version = sess.get_protocol_version().unwrap();
|
||||
let mut value = persist::ClientSessionValue::new(version,
|
||||
scs.suite,
|
||||
&handshake.session_id,
|
||||
ticket,
|
||||
master_secret);
|
||||
value.set_times(ticketer::timebase(),
|
||||
recvd_ticket.new_ticket_lifetime,
|
||||
0);
|
||||
if handshake.using_ems {
|
||||
value.set_extended_ms_used();
|
||||
}
|
||||
|
||||
let worked = sess.config.session_persistence.put(key.get_encoding(),
|
||||
value.get_encoding());
|
||||
|
||||
if worked {
|
||||
debug!("Session saved");
|
||||
} else {
|
||||
debug!("Session not saved");
|
||||
}
|
||||
}
|
||||
|
||||
struct ExpectFinished {
|
||||
handshake: HandshakeDetails,
|
||||
ticket: ReceivedTicketDetails,
|
||||
resuming: bool,
|
||||
cert_verified: verify::ServerCertVerified,
|
||||
sig_verified: verify::HandshakeSignatureValid,
|
||||
}
|
||||
|
||||
impl ExpectFinished {
|
||||
fn into_expect_traffic(self, fin: verify::FinishedMessageVerified) -> hs::NextState {
|
||||
Box::new(ExpectTraffic {
|
||||
_cert_verified: self.cert_verified,
|
||||
_sig_verified: self.sig_verified,
|
||||
_fin_verified: fin,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl hs::State for ExpectFinished {
|
||||
fn check_message(&self, m: &Message) -> Result<(), TLSError> {
|
||||
check_handshake_message(m, &[HandshakeType::Finished])
|
||||
}
|
||||
|
||||
fn handle(self: Box<Self>, sess: &mut ClientSessionImpl, m: Message) -> hs::NextStateOrError {
|
||||
let mut st = *self;
|
||||
let finished = extract_handshake!(m, HandshakePayload::Finished).unwrap();
|
||||
|
||||
// Work out what verify_data we expect.
|
||||
let vh = sess.common.hs_transcript.get_current_hash();
|
||||
let expect_verify_data = sess.common.secrets
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.server_verify_data(&vh);
|
||||
|
||||
// Constant-time verification of this is relatively unimportant: they only
|
||||
// get one chance. But it can't hurt.
|
||||
let fin = constant_time::verify_slices_are_equal(&expect_verify_data, &finished.0)
|
||||
.map_err(|_| {
|
||||
sess.common.send_fatal_alert(AlertDescription::DecryptError);
|
||||
TLSError::DecryptError
|
||||
})
|
||||
.map(|_| verify::FinishedMessageVerified::assertion())?;
|
||||
|
||||
// Hash this message too.
|
||||
sess.common.hs_transcript.add_message(&m);
|
||||
|
||||
save_session(&mut st.handshake,
|
||||
&mut st.ticket,
|
||||
sess);
|
||||
|
||||
if st.resuming {
|
||||
emit_ccs(sess);
|
||||
emit_finished(sess);
|
||||
}
|
||||
|
||||
sess.common.we_now_encrypting();
|
||||
sess.common.start_traffic();
|
||||
Ok(st.into_expect_traffic(fin))
|
||||
}
|
||||
}
|
||||
|
||||
// -- Traffic transit state --
|
||||
struct ExpectTraffic {
|
||||
_cert_verified: verify::ServerCertVerified,
|
||||
_sig_verified: verify::HandshakeSignatureValid,
|
||||
_fin_verified: verify::FinishedMessageVerified,
|
||||
}
|
||||
|
||||
impl hs::State for ExpectTraffic {
|
||||
fn check_message(&self, m: &Message) -> Result<(), TLSError> {
|
||||
check_message(m, &[ContentType::ApplicationData], &[])
|
||||
}
|
||||
|
||||
fn handle(self: Box<Self>, sess: &mut ClientSessionImpl, mut m: Message) -> hs::NextStateOrError {
|
||||
sess.common.take_received_plaintext(m.take_opaque_payload().unwrap());
|
||||
Ok(self)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,768 @@
|
|||
use crate::msgs::enums::{ContentType, HandshakeType, ExtensionType, SignatureScheme};
|
||||
use crate::msgs::enums::{ProtocolVersion, AlertDescription};
|
||||
use crate::msgs::message::{Message, MessagePayload};
|
||||
use crate::msgs::base::{Payload, PayloadU8};
|
||||
use crate::msgs::handshake::{HandshakePayload, HandshakeMessagePayload};
|
||||
use crate::msgs::handshake::{SessionID, ServerHelloPayload, HasServerExtensions};
|
||||
use crate::msgs::handshake::EncryptedExtensions;
|
||||
use crate::msgs::handshake::{CertificatePayloadTLS13, CertificateEntry};
|
||||
use crate::msgs::handshake::DigitallySignedStruct;
|
||||
use crate::msgs::codec::Codec;
|
||||
use crate::msgs::persist;
|
||||
use crate::client::ClientSessionImpl;
|
||||
use crate::key_schedule::SecretKind;
|
||||
use crate::cipher;
|
||||
use crate::verify;
|
||||
use crate::sign;
|
||||
use crate::ticketer;
|
||||
#[cfg(feature = "logging")]
|
||||
use crate::log::{debug, warn};
|
||||
use crate::error::TLSError;
|
||||
use crate::handshake::{check_message, check_handshake_message};
|
||||
#[cfg(feature = "quic")]
|
||||
use crate::{
|
||||
quic,
|
||||
msgs::base::PayloadU16,
|
||||
session::Protocol
|
||||
};
|
||||
|
||||
use crate::client::common::{ServerCertDetails, HandshakeDetails};
|
||||
use crate::client::common::{ClientHelloDetails, ClientAuthDetails};
|
||||
use crate::client::hs;
|
||||
|
||||
use ring::constant_time;
|
||||
use webpki;
|
||||
|
||||
// Extensions we expect in plaintext in the ServerHello.
|
||||
static ALLOWED_PLAINTEXT_EXTS: &'static [ExtensionType] = &[
|
||||
ExtensionType::KeyShare,
|
||||
ExtensionType::PreSharedKey,
|
||||
ExtensionType::SupportedVersions,
|
||||
];
|
||||
|
||||
// Only the intersection of things we offer, and those disallowed
|
||||
// in TLS1.3
|
||||
static DISALLOWED_TLS13_EXTS: &'static [ExtensionType] = &[
|
||||
ExtensionType::ECPointFormats,
|
||||
ExtensionType::SessionTicket,
|
||||
ExtensionType::RenegotiationInfo,
|
||||
ExtensionType::ExtendedMasterSecret,
|
||||
];
|
||||
|
||||
pub fn validate_server_hello(sess: &mut ClientSessionImpl,
|
||||
server_hello: &ServerHelloPayload) -> Result<(), TLSError> {
|
||||
for ext in &server_hello.extensions {
|
||||
if !ALLOWED_PLAINTEXT_EXTS.contains(&ext.get_type()) {
|
||||
sess.common.send_fatal_alert(AlertDescription::UnsupportedExtension);
|
||||
return Err(TLSError::PeerMisbehavedError("server sent unexpected cleartext ext"
|
||||
.to_string()));
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn validate_encrypted_extensions(sess: &mut ClientSessionImpl,
|
||||
hello: &ClientHelloDetails,
|
||||
exts: &EncryptedExtensions) -> Result<(), TLSError> {
|
||||
if exts.has_duplicate_extension() {
|
||||
sess.common.send_fatal_alert(AlertDescription::DecodeError);
|
||||
return Err(TLSError::PeerMisbehavedError("server sent duplicate encrypted extensions"
|
||||
.to_string()));
|
||||
}
|
||||
|
||||
if hello.server_sent_unsolicited_extensions(exts, &[]) {
|
||||
sess.common.send_fatal_alert(AlertDescription::UnsupportedExtension);
|
||||
let msg = "server sent unsolicited encrypted extension".to_string();
|
||||
return Err(TLSError::PeerMisbehavedError(msg));
|
||||
}
|
||||
|
||||
for ext in exts {
|
||||
if ALLOWED_PLAINTEXT_EXTS.contains(&ext.get_type()) ||
|
||||
DISALLOWED_TLS13_EXTS.contains(&ext.get_type()) {
|
||||
sess.common.send_fatal_alert(AlertDescription::UnsupportedExtension);
|
||||
let msg = "server sent inappropriate encrypted extension".to_string();
|
||||
return Err(TLSError::PeerMisbehavedError(msg));
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub struct ExpectEncryptedExtensions {
|
||||
pub handshake: HandshakeDetails,
|
||||
pub server_cert: ServerCertDetails,
|
||||
pub hello: ClientHelloDetails,
|
||||
}
|
||||
|
||||
impl ExpectEncryptedExtensions {
|
||||
fn into_expect_finished_resume(self,
|
||||
certv: verify::ServerCertVerified,
|
||||
sigv: verify::HandshakeSignatureValid) -> hs::NextState {
|
||||
Box::new(ExpectFinished {
|
||||
handshake: self.handshake,
|
||||
client_auth: None,
|
||||
cert_verified: certv,
|
||||
sig_verified: sigv,
|
||||
})
|
||||
}
|
||||
|
||||
fn into_expect_certificate_or_certreq(self) -> hs::NextState {
|
||||
Box::new(ExpectCertificateOrCertReq {
|
||||
handshake: self.handshake,
|
||||
server_cert: self.server_cert,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl hs::State for ExpectEncryptedExtensions {
|
||||
fn check_message(&self, m: &Message) -> Result<(), TLSError> {
|
||||
check_handshake_message(m, &[HandshakeType::EncryptedExtensions])
|
||||
}
|
||||
|
||||
fn handle(self: Box<Self>, sess: &mut ClientSessionImpl, m: Message) -> hs::NextStateOrError {
|
||||
let exts = extract_handshake!(m, HandshakePayload::EncryptedExtensions).unwrap();
|
||||
debug!("TLS1.3 encrypted extensions: {:?}", exts);
|
||||
sess.common.hs_transcript.add_message(&m);
|
||||
|
||||
validate_encrypted_extensions(sess, &self.hello, exts)?;
|
||||
hs::process_alpn_protocol(sess, exts.get_alpn_protocol())?;
|
||||
|
||||
#[cfg(feature = "quic")] {
|
||||
// QUIC transport parameters
|
||||
if let Some(params) = exts.get_quic_params_extension() {
|
||||
sess.common.quic.params = Some(params);
|
||||
}
|
||||
}
|
||||
|
||||
if self.handshake.resuming_session.is_some() {
|
||||
let was_early_traffic = sess.common.early_traffic;
|
||||
if was_early_traffic {
|
||||
if exts.early_data_extension_offered() {
|
||||
sess.early_data.accepted();
|
||||
} else {
|
||||
sess.early_data.rejected();
|
||||
sess.common.early_traffic = false;
|
||||
}
|
||||
}
|
||||
|
||||
if was_early_traffic && !sess.common.early_traffic {
|
||||
// If no early traffic, set the encryption key for handshakes
|
||||
let suite = sess.common.get_suite_assert();
|
||||
let write_key = sess.common.get_key_schedule()
|
||||
.derive(SecretKind::ClientHandshakeTrafficSecret,
|
||||
&self.handshake.hash_at_client_recvd_server_hello);
|
||||
sess.common.set_message_encrypter(cipher::new_tls13_write(suite, &write_key));
|
||||
sess.config.key_log.log(sess.common.protocol.labels().client_handshake_traffic_secret,
|
||||
&self.handshake.randoms.client,
|
||||
&write_key);
|
||||
sess.common.get_mut_key_schedule()
|
||||
.current_client_traffic_secret = write_key;
|
||||
}
|
||||
let certv = verify::ServerCertVerified::assertion();
|
||||
let sigv = verify::HandshakeSignatureValid::assertion();
|
||||
Ok(self.into_expect_finished_resume(certv, sigv))
|
||||
} else {
|
||||
if exts.early_data_extension_offered() {
|
||||
let msg = "server sent early data extension without resumption".to_string();
|
||||
return Err(TLSError::PeerMisbehavedError(msg));
|
||||
}
|
||||
Ok(self.into_expect_certificate_or_certreq())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct ExpectCertificate {
|
||||
handshake: HandshakeDetails,
|
||||
server_cert: ServerCertDetails,
|
||||
client_auth: Option<ClientAuthDetails>,
|
||||
}
|
||||
|
||||
impl ExpectCertificate {
|
||||
fn into_expect_certificate_verify(self) -> hs::NextState {
|
||||
Box::new(ExpectCertificateVerify {
|
||||
handshake: self.handshake,
|
||||
server_cert: self.server_cert,
|
||||
client_auth: self.client_auth,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl hs::State for ExpectCertificate {
|
||||
fn check_message(&self, m: &Message) -> Result<(), TLSError> {
|
||||
check_handshake_message(m, &[HandshakeType::Certificate])
|
||||
}
|
||||
|
||||
fn handle(mut self: Box<Self>, sess: &mut ClientSessionImpl, m: Message) -> hs::NextStateOrError {
|
||||
let cert_chain = extract_handshake!(m, HandshakePayload::CertificateTLS13).unwrap();
|
||||
sess.common.hs_transcript.add_message(&m);
|
||||
|
||||
// This is only non-empty for client auth.
|
||||
if !cert_chain.context.0.is_empty() {
|
||||
warn!("certificate with non-empty context during handshake");
|
||||
sess.common.send_fatal_alert(AlertDescription::DecodeError);
|
||||
return Err(TLSError::CorruptMessagePayload(ContentType::Handshake));
|
||||
}
|
||||
|
||||
if cert_chain.any_entry_has_duplicate_extension() ||
|
||||
cert_chain.any_entry_has_unknown_extension() {
|
||||
warn!("certificate chain contains unsolicited/unknown extension");
|
||||
sess.common.send_fatal_alert(AlertDescription::UnsupportedExtension);
|
||||
return Err(TLSError::PeerMisbehavedError("bad cert chain extensions".to_string()));
|
||||
}
|
||||
|
||||
self.server_cert.ocsp_response = cert_chain.get_end_entity_ocsp();
|
||||
self.server_cert.scts = cert_chain.get_end_entity_scts();
|
||||
self.server_cert.cert_chain = cert_chain.convert();
|
||||
|
||||
if let Some(sct_list) = self.server_cert.scts.as_ref() {
|
||||
if hs::sct_list_is_invalid(sct_list) {
|
||||
let error_msg = "server sent invalid SCT list".to_string();
|
||||
return Err(TLSError::PeerMisbehavedError(error_msg));
|
||||
}
|
||||
|
||||
if sess.config.ct_logs.is_none() {
|
||||
let error_msg = "server sent unsolicited SCT list".to_string();
|
||||
return Err(TLSError::PeerMisbehavedError(error_msg));
|
||||
}
|
||||
}
|
||||
|
||||
Ok(self.into_expect_certificate_verify())
|
||||
}
|
||||
}
|
||||
|
||||
struct ExpectCertificateOrCertReq {
|
||||
handshake: HandshakeDetails,
|
||||
server_cert: ServerCertDetails,
|
||||
}
|
||||
|
||||
impl ExpectCertificateOrCertReq {
|
||||
fn into_expect_certificate(self) -> hs::NextState {
|
||||
Box::new(ExpectCertificate {
|
||||
handshake: self.handshake,
|
||||
server_cert: self.server_cert,
|
||||
client_auth: None,
|
||||
})
|
||||
}
|
||||
|
||||
fn into_expect_certificate_req(self) -> hs::NextState {
|
||||
Box::new(ExpectCertificateRequest {
|
||||
handshake: self.handshake,
|
||||
server_cert: self.server_cert,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl hs::State for ExpectCertificateOrCertReq {
|
||||
fn check_message(&self, m: &Message) -> Result<(), TLSError> {
|
||||
check_handshake_message(m,
|
||||
&[HandshakeType::Certificate,
|
||||
HandshakeType::CertificateRequest])
|
||||
}
|
||||
|
||||
fn handle(self: Box<Self>, sess: &mut ClientSessionImpl, m: Message) -> hs::NextStateOrError {
|
||||
if m.is_handshake_type(HandshakeType::Certificate) {
|
||||
self.into_expect_certificate().handle(sess, m)
|
||||
} else {
|
||||
self.into_expect_certificate_req().handle(sess, m)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// --- TLS1.3 CertificateVerify ---
|
||||
struct ExpectCertificateVerify {
|
||||
handshake: HandshakeDetails,
|
||||
server_cert: ServerCertDetails,
|
||||
client_auth: Option<ClientAuthDetails>,
|
||||
}
|
||||
|
||||
impl ExpectCertificateVerify {
|
||||
fn into_expect_finished(self,
|
||||
certv: verify::ServerCertVerified,
|
||||
sigv: verify::HandshakeSignatureValid) -> hs::NextState {
|
||||
Box::new(ExpectFinished {
|
||||
handshake: self.handshake,
|
||||
client_auth: self.client_auth,
|
||||
cert_verified: certv,
|
||||
sig_verified: sigv,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fn send_cert_error_alert(sess: &mut ClientSessionImpl, err: TLSError) -> TLSError {
|
||||
match err {
|
||||
TLSError::WebPKIError(webpki::Error::BadDER) => {
|
||||
sess.common.send_fatal_alert(AlertDescription::DecodeError);
|
||||
}
|
||||
TLSError::PeerMisbehavedError(_) => {
|
||||
sess.common.send_fatal_alert(AlertDescription::IllegalParameter);
|
||||
}
|
||||
_ => {
|
||||
sess.common.send_fatal_alert(AlertDescription::BadCertificate);
|
||||
}
|
||||
};
|
||||
|
||||
err
|
||||
}
|
||||
|
||||
impl hs::State for ExpectCertificateVerify {
|
||||
fn check_message(&self, m: &Message) -> Result<(), TLSError> {
|
||||
check_handshake_message(m, &[HandshakeType::CertificateVerify])
|
||||
}
|
||||
|
||||
fn handle(mut self: Box<Self>, sess: &mut ClientSessionImpl, m: Message) -> hs::NextStateOrError {
|
||||
let cert_verify = extract_handshake!(m, HandshakePayload::CertificateVerify).unwrap();
|
||||
|
||||
debug!("Server cert is {:?}", self.server_cert.cert_chain);
|
||||
|
||||
// 1. Verify the certificate chain.
|
||||
if self.server_cert.cert_chain.is_empty() {
|
||||
return Err(TLSError::NoCertificatesPresented);
|
||||
}
|
||||
|
||||
let certv = sess.config
|
||||
.get_verifier()
|
||||
.verify_server_cert(&sess.config.root_store,
|
||||
&self.server_cert.cert_chain,
|
||||
self.handshake.dns_name.as_ref(),
|
||||
&self.server_cert.ocsp_response)
|
||||
.map_err(|err| send_cert_error_alert(sess, err))?;
|
||||
|
||||
// 2. Verify their signature on the handshake.
|
||||
let handshake_hash = sess.common.hs_transcript.get_current_hash();
|
||||
let sigv = verify::verify_tls13(&self.server_cert.cert_chain[0],
|
||||
cert_verify,
|
||||
&handshake_hash,
|
||||
b"TLS 1.3, server CertificateVerify\x00")
|
||||
.map_err(|err| send_cert_error_alert(sess, err))?;
|
||||
|
||||
// 3. Verify any included SCTs.
|
||||
match (self.server_cert.scts.as_ref(), sess.config.ct_logs) {
|
||||
(Some(scts), Some(logs)) => {
|
||||
verify::verify_scts(&self.server_cert.cert_chain[0],
|
||||
scts,
|
||||
logs)?;
|
||||
}
|
||||
(_, _) => {}
|
||||
}
|
||||
|
||||
sess.server_cert_chain = self.server_cert.take_chain();
|
||||
sess.common.hs_transcript.add_message(&m);
|
||||
|
||||
Ok(self.into_expect_finished(certv, sigv))
|
||||
}
|
||||
}
|
||||
|
||||
// TLS1.3 version of CertificateRequest handling. We then move to expecting the server
|
||||
// Certificate. Unfortunately the CertificateRequest type changed in an annoying way
|
||||
// in TLS1.3.
|
||||
struct ExpectCertificateRequest {
|
||||
handshake: HandshakeDetails,
|
||||
server_cert: ServerCertDetails,
|
||||
}
|
||||
|
||||
impl ExpectCertificateRequest {
|
||||
fn into_expect_certificate(self, client_auth: ClientAuthDetails) -> hs::NextState {
|
||||
Box::new(ExpectCertificate {
|
||||
handshake: self.handshake,
|
||||
server_cert: self.server_cert,
|
||||
client_auth: Some(client_auth),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl hs::State for ExpectCertificateRequest {
|
||||
fn check_message(&self, m: &Message) -> Result<(), TLSError> {
|
||||
check_handshake_message(m, &[HandshakeType::CertificateRequest])
|
||||
}
|
||||
|
||||
fn handle(self: Box<Self>, sess: &mut ClientSessionImpl, m: Message) -> hs::NextStateOrError {
|
||||
let certreq = &extract_handshake!(m, HandshakePayload::CertificateRequestTLS13).unwrap();
|
||||
sess.common.hs_transcript.add_message(&m);
|
||||
debug!("Got CertificateRequest {:?}", certreq);
|
||||
|
||||
// Fortunately the problems here in TLS1.2 and prior are corrected in
|
||||
// TLS1.3.
|
||||
|
||||
// Must be empty during handshake.
|
||||
if !certreq.context.0.is_empty() {
|
||||
warn!("Server sent non-empty certreq context");
|
||||
sess.common.send_fatal_alert(AlertDescription::DecodeError);
|
||||
return Err(TLSError::CorruptMessagePayload(ContentType::Handshake));
|
||||
}
|
||||
|
||||
let tls13_sign_schemes = sign::supported_sign_tls13();
|
||||
let no_sigschemes = Vec::new();
|
||||
let compat_sigschemes = certreq.get_sigalgs_extension()
|
||||
.unwrap_or(&no_sigschemes)
|
||||
.iter()
|
||||
.cloned()
|
||||
.filter(|scheme| tls13_sign_schemes.contains(scheme))
|
||||
.collect::<Vec<SignatureScheme>>();
|
||||
|
||||
if compat_sigschemes.is_empty() {
|
||||
sess.common.send_fatal_alert(AlertDescription::HandshakeFailure);
|
||||
return Err(TLSError::PeerIncompatibleError("server sent bad certreq schemes".to_string()));
|
||||
}
|
||||
|
||||
let no_canames = Vec::new();
|
||||
let canames = certreq.get_authorities_extension()
|
||||
.unwrap_or(&no_canames)
|
||||
.iter()
|
||||
.map(|p| p.0.as_slice())
|
||||
.collect::<Vec<&[u8]>>();
|
||||
let maybe_certkey =
|
||||
sess.config.client_auth_cert_resolver.resolve(&canames, &compat_sigschemes);
|
||||
|
||||
let mut client_auth = ClientAuthDetails::new();
|
||||
if let Some(mut certkey) = maybe_certkey {
|
||||
debug!("Attempting client auth");
|
||||
let maybe_signer = certkey.key.choose_scheme(&compat_sigschemes);
|
||||
client_auth.cert = Some(certkey.take_cert());
|
||||
client_auth.signer = maybe_signer;
|
||||
client_auth.auth_context = Some(certreq.context.0.clone());
|
||||
} else {
|
||||
debug!("Client auth requested but no cert selected");
|
||||
}
|
||||
|
||||
Ok(self.into_expect_certificate(client_auth))
|
||||
}
|
||||
}
|
||||
|
||||
fn emit_certificate_tls13(client_auth: &mut ClientAuthDetails,
|
||||
sess: &mut ClientSessionImpl) {
|
||||
let context = client_auth.auth_context
|
||||
.take()
|
||||
.unwrap_or_else(Vec::new);
|
||||
|
||||
let mut cert_payload = CertificatePayloadTLS13 {
|
||||
context: PayloadU8::new(context),
|
||||
list: Vec::new(),
|
||||
};
|
||||
|
||||
if let Some(cert_chain) = client_auth.cert.take() {
|
||||
for cert in cert_chain {
|
||||
cert_payload.list.push(CertificateEntry::new(cert));
|
||||
}
|
||||
}
|
||||
|
||||
let m = Message {
|
||||
typ: ContentType::Handshake,
|
||||
version: ProtocolVersion::TLSv1_3,
|
||||
payload: MessagePayload::Handshake(HandshakeMessagePayload {
|
||||
typ: HandshakeType::Certificate,
|
||||
payload: HandshakePayload::CertificateTLS13(cert_payload),
|
||||
}),
|
||||
};
|
||||
sess.common.hs_transcript.add_message(&m);
|
||||
sess.common.send_msg(m, true);
|
||||
}
|
||||
|
||||
fn emit_certverify_tls13(client_auth: &mut ClientAuthDetails,
|
||||
sess: &mut ClientSessionImpl) -> Result<(), TLSError> {
|
||||
if client_auth.signer.is_none() {
|
||||
debug!("Skipping certverify message (no client scheme/key)");
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let mut message = Vec::new();
|
||||
message.resize(64, 0x20u8);
|
||||
message.extend_from_slice(b"TLS 1.3, client CertificateVerify\x00");
|
||||
message.extend_from_slice(&sess.common.hs_transcript.get_current_hash());
|
||||
|
||||
let signer = client_auth.signer.take().unwrap();
|
||||
let scheme = signer.get_scheme();
|
||||
let sig = signer.sign(&message)?;
|
||||
let dss = DigitallySignedStruct::new(scheme, sig);
|
||||
|
||||
let m = Message {
|
||||
typ: ContentType::Handshake,
|
||||
version: ProtocolVersion::TLSv1_3,
|
||||
payload: MessagePayload::Handshake(HandshakeMessagePayload {
|
||||
typ: HandshakeType::CertificateVerify,
|
||||
payload: HandshakePayload::CertificateVerify(dss),
|
||||
}),
|
||||
};
|
||||
|
||||
sess.common.hs_transcript.add_message(&m);
|
||||
sess.common.send_msg(m, true);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn emit_finished_tls13(sess: &mut ClientSessionImpl) {
|
||||
let handshake_hash = sess.common.hs_transcript.get_current_hash();
|
||||
let verify_data = sess.common
|
||||
.get_key_schedule()
|
||||
.sign_finish(SecretKind::ClientHandshakeTrafficSecret, &handshake_hash);
|
||||
let verify_data_payload = Payload::new(verify_data);
|
||||
|
||||
let m = Message {
|
||||
typ: ContentType::Handshake,
|
||||
version: ProtocolVersion::TLSv1_3,
|
||||
payload: MessagePayload::Handshake(HandshakeMessagePayload {
|
||||
typ: HandshakeType::Finished,
|
||||
payload: HandshakePayload::Finished(verify_data_payload),
|
||||
}),
|
||||
};
|
||||
|
||||
sess.common.hs_transcript.add_message(&m);
|
||||
sess.common.send_msg(m, true);
|
||||
}
|
||||
|
||||
fn emit_end_of_early_data_tls13(sess: &mut ClientSessionImpl) {
|
||||
#[cfg(feature = "quic")]
|
||||
{
|
||||
if let Protocol::Quic = sess.common.protocol { return; }
|
||||
}
|
||||
|
||||
let m = Message {
|
||||
typ: ContentType::Handshake,
|
||||
version: ProtocolVersion::TLSv1_3,
|
||||
payload: MessagePayload::Handshake(HandshakeMessagePayload {
|
||||
typ: HandshakeType::EndOfEarlyData,
|
||||
payload: HandshakePayload::EndOfEarlyData,
|
||||
}),
|
||||
};
|
||||
|
||||
sess.common.hs_transcript.add_message(&m);
|
||||
sess.common.send_msg(m, true);
|
||||
}
|
||||
|
||||
struct ExpectFinished {
|
||||
handshake: HandshakeDetails,
|
||||
client_auth: Option<ClientAuthDetails>,
|
||||
cert_verified: verify::ServerCertVerified,
|
||||
sig_verified: verify::HandshakeSignatureValid,
|
||||
}
|
||||
|
||||
impl ExpectFinished {
|
||||
fn into_expect_traffic(self,
|
||||
fin: verify::FinishedMessageVerified) -> ExpectTraffic {
|
||||
ExpectTraffic {
|
||||
handshake: self.handshake,
|
||||
_cert_verified: self.cert_verified,
|
||||
_sig_verified: self.sig_verified,
|
||||
_fin_verified: fin,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl hs::State for ExpectFinished {
|
||||
fn check_message(&self, m: &Message) -> hs::CheckResult {
|
||||
check_handshake_message(m, &[HandshakeType::Finished])
|
||||
}
|
||||
|
||||
fn handle(self: Box<Self>, sess: &mut ClientSessionImpl, m: Message) -> hs::NextStateOrError {
|
||||
let mut st = *self;
|
||||
let finished = extract_handshake!(m, HandshakePayload::Finished).unwrap();
|
||||
|
||||
let handshake_hash = sess.common.hs_transcript.get_current_hash();
|
||||
let expect_verify_data = sess.common
|
||||
.get_key_schedule()
|
||||
.sign_finish(SecretKind::ServerHandshakeTrafficSecret, &handshake_hash);
|
||||
|
||||
let fin = constant_time::verify_slices_are_equal(&expect_verify_data, &finished.0)
|
||||
.map_err(|_| {
|
||||
sess.common.send_fatal_alert(AlertDescription::DecryptError);
|
||||
TLSError::DecryptError
|
||||
})
|
||||
.map(|_| verify::FinishedMessageVerified::assertion())?;
|
||||
|
||||
let suite = sess.common.get_suite_assert();
|
||||
let maybe_write_key = if sess.common.early_traffic {
|
||||
/* Derive the client-to-server encryption key before key schedule update */
|
||||
let key = sess.common
|
||||
.get_key_schedule()
|
||||
.derive(SecretKind::ClientHandshakeTrafficSecret,
|
||||
&st.handshake.hash_at_client_recvd_server_hello);
|
||||
Some(key)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
sess.common.hs_transcript.add_message(&m);
|
||||
|
||||
/* Transition to application data */
|
||||
sess.common.get_mut_key_schedule().input_empty();
|
||||
|
||||
/* Traffic from server is now decrypted with application data keys. */
|
||||
let handshake_hash = sess.common.hs_transcript.get_current_hash();
|
||||
let read_key = sess.common
|
||||
.get_key_schedule()
|
||||
.derive(SecretKind::ServerApplicationTrafficSecret, &handshake_hash);
|
||||
sess.config.key_log.log(sess.common.protocol.labels().server_traffic_secret_0,
|
||||
&st.handshake.randoms.client,
|
||||
&read_key);
|
||||
sess.common.set_message_decrypter(cipher::new_tls13_read(suite, &read_key));
|
||||
sess.common
|
||||
.get_mut_key_schedule()
|
||||
.current_server_traffic_secret = read_key;
|
||||
|
||||
let exporter_secret = sess.common
|
||||
.get_key_schedule()
|
||||
.derive(SecretKind::ExporterMasterSecret, &handshake_hash);
|
||||
sess.config.key_log.log(sess.common.protocol.labels().exporter_secret,
|
||||
&st.handshake.randoms.client,
|
||||
&exporter_secret);
|
||||
sess.common
|
||||
.get_mut_key_schedule()
|
||||
.current_exporter_secret = exporter_secret;
|
||||
|
||||
/* The EndOfEarlyData message to server is still encrypted with early data keys,
|
||||
* but appears in the transcript after the server Finished. */
|
||||
if let Some(write_key) = maybe_write_key {
|
||||
emit_end_of_early_data_tls13(sess);
|
||||
sess.common.early_traffic = false;
|
||||
sess.early_data.finished();
|
||||
sess.common.set_message_encrypter(cipher::new_tls13_write(suite, &write_key));
|
||||
sess.config.key_log.log(sess.common.protocol.labels().client_handshake_traffic_secret,
|
||||
&st.handshake.randoms.client,
|
||||
&write_key);
|
||||
sess.common.get_mut_key_schedule().current_client_traffic_secret = write_key;
|
||||
}
|
||||
|
||||
/* Send our authentication/finished messages. These are still encrypted
|
||||
* with our handshake keys. */
|
||||
if st.client_auth.is_some() {
|
||||
emit_certificate_tls13(st.client_auth.as_mut().unwrap(),
|
||||
sess);
|
||||
emit_certverify_tls13(st.client_auth.as_mut().unwrap(),
|
||||
sess)?;
|
||||
}
|
||||
|
||||
emit_finished_tls13(sess);
|
||||
|
||||
/* Now move to our application traffic keys. */
|
||||
hs::check_aligned_handshake(sess)?;
|
||||
let write_key = sess.common
|
||||
.get_key_schedule()
|
||||
.derive(SecretKind::ClientApplicationTrafficSecret, &handshake_hash);
|
||||
sess.config.key_log.log(sess.common.protocol.labels().client_traffic_secret_0,
|
||||
&st.handshake.randoms.client,
|
||||
&write_key);
|
||||
sess.common.set_message_encrypter(cipher::new_tls13_write(suite, &write_key));
|
||||
sess.common
|
||||
.get_mut_key_schedule()
|
||||
.current_client_traffic_secret = write_key;
|
||||
|
||||
sess.common.we_now_encrypting();
|
||||
sess.common.start_traffic();
|
||||
|
||||
let st = st.into_expect_traffic(fin);
|
||||
#[cfg(feature = "quic")] {
|
||||
if sess.common.protocol == Protocol::Quic {
|
||||
let key_schedule = sess.common.key_schedule.as_ref().unwrap();
|
||||
sess.common.quic.traffic_secrets = Some(quic::Secrets {
|
||||
client: key_schedule.current_client_traffic_secret.clone(),
|
||||
server: key_schedule.current_server_traffic_secret.clone(),
|
||||
});
|
||||
return Ok(Box::new(ExpectQUICTraffic(st)));
|
||||
}
|
||||
}
|
||||
|
||||
Ok(Box::new(st))
|
||||
}
|
||||
}
|
||||
|
||||
// -- Traffic transit state (TLS1.3) --
|
||||
// In this state we can be sent tickets, keyupdates,
|
||||
// and application data.
|
||||
struct ExpectTraffic {
|
||||
handshake: HandshakeDetails,
|
||||
_cert_verified: verify::ServerCertVerified,
|
||||
_sig_verified: verify::HandshakeSignatureValid,
|
||||
_fin_verified: verify::FinishedMessageVerified,
|
||||
}
|
||||
|
||||
impl ExpectTraffic {
|
||||
fn handle_new_ticket_tls13(&mut self, sess: &mut ClientSessionImpl, m: Message) -> Result<(), TLSError> {
|
||||
let nst = extract_handshake!(m, HandshakePayload::NewSessionTicketTLS13).unwrap();
|
||||
let handshake_hash = sess.common.hs_transcript.get_current_hash();
|
||||
let resumption_master_secret = sess.common
|
||||
.get_key_schedule()
|
||||
.derive(SecretKind::ResumptionMasterSecret, &handshake_hash);
|
||||
let secret = sess.common
|
||||
.get_key_schedule()
|
||||
.derive_ticket_psk(&resumption_master_secret, &nst.nonce.0);
|
||||
|
||||
let mut value = persist::ClientSessionValue::new(ProtocolVersion::TLSv1_3,
|
||||
sess.common.get_suite_assert().suite,
|
||||
&SessionID::empty(),
|
||||
nst.ticket.0.clone(),
|
||||
secret);
|
||||
value.set_times(ticketer::timebase(),
|
||||
nst.lifetime,
|
||||
nst.age_add);
|
||||
|
||||
if let Some(sz) = nst.get_max_early_data_size() {
|
||||
value.set_max_early_data_size(sz);
|
||||
#[cfg(feature = "quic")] {
|
||||
if sess.common.protocol == Protocol::Quic {
|
||||
if sz != 0 && sz != 0xffff_ffff {
|
||||
return Err(TLSError::PeerMisbehavedError("invalid max_early_data_size".into()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let key = persist::ClientSessionKey::session_for_dns_name(self.handshake.dns_name.as_ref());
|
||||
#[allow(unused_mut)]
|
||||
let mut ticket = value.get_encoding();
|
||||
|
||||
#[cfg(feature = "quic")] {
|
||||
if sess.common.protocol == Protocol::Quic {
|
||||
PayloadU16::encode_slice(sess.common.quic.params.as_ref().unwrap(), &mut ticket);
|
||||
}
|
||||
}
|
||||
|
||||
let worked = sess.config.session_persistence.put(key.get_encoding(),
|
||||
ticket);
|
||||
|
||||
if worked {
|
||||
debug!("Ticket saved");
|
||||
} else {
|
||||
debug!("Ticket not saved");
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn handle_key_update(&mut self, sess: &mut ClientSessionImpl, m: Message) -> Result<(), TLSError> {
|
||||
let kur = extract_handshake!(m, HandshakePayload::KeyUpdate).unwrap();
|
||||
sess.common.process_key_update(kur, SecretKind::ServerApplicationTrafficSecret)
|
||||
}
|
||||
}
|
||||
|
||||
impl hs::State for ExpectTraffic {
|
||||
fn check_message(&self, m: &Message) -> Result<(), TLSError> {
|
||||
check_message(m,
|
||||
&[ContentType::ApplicationData, ContentType::Handshake],
|
||||
&[HandshakeType::NewSessionTicket, HandshakeType::KeyUpdate])
|
||||
}
|
||||
|
||||
fn handle(mut self: Box<Self>, sess: &mut ClientSessionImpl, mut m: Message) -> hs::NextStateOrError {
|
||||
if m.is_content_type(ContentType::ApplicationData) {
|
||||
sess.common.take_received_plaintext(m.take_opaque_payload().unwrap());
|
||||
} else if m.is_handshake_type(HandshakeType::NewSessionTicket) {
|
||||
self.handle_new_ticket_tls13(sess, m)?;
|
||||
} else if m.is_handshake_type(HandshakeType::KeyUpdate) {
|
||||
self.handle_key_update(sess, m)?;
|
||||
}
|
||||
|
||||
Ok(self)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "quic")]
|
||||
pub struct ExpectQUICTraffic(ExpectTraffic);
|
||||
|
||||
#[cfg(feature = "quic")]
|
||||
impl hs::State for ExpectQUICTraffic {
|
||||
fn check_message(&self, m: &Message) -> hs::CheckResult {
|
||||
check_message(m, &[ContentType::Handshake], &[HandshakeType::NewSessionTicket])
|
||||
}
|
||||
|
||||
fn handle(mut self: Box<Self>, sess: &mut ClientSessionImpl, m: Message) -> hs::NextStateOrError {
|
||||
self.0.handle_new_ticket_tls13(sess, m)?;
|
||||
Ok(self)
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue