mirror of https://github.com/ctz/rustls
1090 lines
36 KiB
Rust
1090 lines
36 KiB
Rust
use crate::check::{inappropriate_handshake_message, inappropriate_message};
|
|
use crate::common_state::{CommonState, Side, State};
|
|
use crate::conn::ConnectionRandoms;
|
|
use crate::enums::ProtocolVersion;
|
|
use crate::enums::{AlertDescription, ContentType, HandshakeType};
|
|
use crate::error::{Error, InvalidMessage, PeerMisbehaved};
|
|
use crate::hash_hs::HandshakeHash;
|
|
use crate::kx;
|
|
#[cfg(feature = "logging")]
|
|
use crate::log::{debug, trace, warn};
|
|
use crate::msgs::base::{Payload, PayloadU8};
|
|
use crate::msgs::ccs::ChangeCipherSpecPayload;
|
|
use crate::msgs::codec::Codec;
|
|
use crate::msgs::handshake::{
|
|
CertificatePayload, HandshakeMessagePayload, HandshakePayload, NewSessionTicketPayload,
|
|
ServerECDHParams, SessionId,
|
|
};
|
|
use crate::msgs::message::{Message, MessagePayload};
|
|
use crate::msgs::persist;
|
|
use crate::sign::Signer;
|
|
#[cfg(feature = "secret_extraction")]
|
|
use crate::suites::PartiallyExtractedSecrets;
|
|
use crate::suites::SupportedCipherSuite;
|
|
use crate::ticketer::TimeBase;
|
|
use crate::tls12::{self, ConnectionSecrets, Tls12CipherSuite};
|
|
use crate::verify::{self, DigitallySignedStruct};
|
|
|
|
use super::client_conn::ClientConnectionData;
|
|
use super::hs::ClientContext;
|
|
use crate::client::common::ClientAuthDetails;
|
|
use crate::client::common::ServerCertDetails;
|
|
use crate::client::{hs, ClientConfig, ServerName};
|
|
|
|
use ring::agreement::PublicKey;
|
|
use ring::constant_time;
|
|
|
|
use std::sync::Arc;
|
|
|
|
pub(super) use server_hello::CompleteServerHelloHandling;
|
|
|
|
mod server_hello {
|
|
use crate::msgs::enums::ExtensionType;
|
|
use crate::msgs::handshake::HasServerExtensions;
|
|
use crate::msgs::handshake::ServerHelloPayload;
|
|
|
|
use super::*;
|
|
|
|
pub(in crate::client) struct CompleteServerHelloHandling {
|
|
pub(in crate::client) config: Arc<ClientConfig>,
|
|
pub(in crate::client) resuming_session: Option<persist::Tls12ClientSessionValue>,
|
|
pub(in crate::client) server_name: ServerName,
|
|
pub(in crate::client) randoms: ConnectionRandoms,
|
|
pub(in crate::client) using_ems: bool,
|
|
pub(in crate::client) transcript: HandshakeHash,
|
|
}
|
|
|
|
impl CompleteServerHelloHandling {
|
|
pub(in crate::client) fn handle_server_hello(
|
|
mut self,
|
|
cx: &mut ClientContext,
|
|
suite: &'static Tls12CipherSuite,
|
|
server_hello: &ServerHelloPayload,
|
|
tls13_supported: bool,
|
|
) -> hs::NextStateOrError {
|
|
server_hello
|
|
.random
|
|
.write_slice(&mut self.randoms.server);
|
|
|
|
// Look for TLS1.3 downgrade signal in server random
|
|
// both the server random and TLS12_DOWNGRADE_SENTINEL are
|
|
// public values and don't require constant time comparison
|
|
let has_downgrade_marker = self.randoms.server[24..] == tls12::DOWNGRADE_SENTINEL;
|
|
if tls13_supported && has_downgrade_marker {
|
|
return Err({
|
|
cx.common.send_fatal_alert(
|
|
AlertDescription::IllegalParameter,
|
|
PeerMisbehaved::AttemptedDowngradeToTls12WhenTls13IsSupported,
|
|
)
|
|
});
|
|
}
|
|
|
|
// Doing EMS?
|
|
self.using_ems = server_hello.ems_support_acked();
|
|
|
|
// Might the server send a ticket?
|
|
let must_issue_new_ticket = if server_hello
|
|
.find_extension(ExtensionType::SessionTicket)
|
|
.is_some()
|
|
{
|
|
debug!("Server supports tickets");
|
|
true
|
|
} else {
|
|
false
|
|
};
|
|
|
|
// Might the server send a CertificateStatus between Certificate and
|
|
// ServerKeyExchange?
|
|
let may_send_cert_status = server_hello
|
|
.find_extension(ExtensionType::StatusRequest)
|
|
.is_some();
|
|
if may_send_cert_status {
|
|
debug!("Server may staple OCSP response");
|
|
}
|
|
|
|
// See if we're successfully resuming.
|
|
if let Some(ref resuming) = self.resuming_session {
|
|
if resuming.session_id == server_hello.session_id {
|
|
debug!("Server agreed to resume");
|
|
|
|
// Is the server telling lies about the ciphersuite?
|
|
if resuming.suite() != suite {
|
|
return Err(PeerMisbehaved::ResumptionOfferedWithVariedCipherSuite.into());
|
|
}
|
|
|
|
// And about EMS support?
|
|
if resuming.extended_ms() != self.using_ems {
|
|
return Err(PeerMisbehaved::ResumptionOfferedWithVariedEms.into());
|
|
}
|
|
|
|
let secrets =
|
|
ConnectionSecrets::new_resume(self.randoms, suite, resuming.secret());
|
|
self.config.key_log.log(
|
|
"CLIENT_RANDOM",
|
|
&secrets.randoms.client,
|
|
&secrets.master_secret,
|
|
);
|
|
cx.common
|
|
.start_encryption_tls12(&secrets, Side::Client);
|
|
|
|
// Since we're resuming, we verified the certificate and
|
|
// proof of possession in the prior session.
|
|
cx.common.peer_certificates = Some(resuming.server_cert_chain().to_vec());
|
|
let cert_verified = verify::ServerCertVerified::assertion();
|
|
let sig_verified = verify::HandshakeSignatureValid::assertion();
|
|
|
|
return if must_issue_new_ticket {
|
|
Ok(Box::new(ExpectNewTicket {
|
|
config: self.config,
|
|
secrets,
|
|
resuming_session: self.resuming_session,
|
|
session_id: server_hello.session_id,
|
|
server_name: self.server_name,
|
|
using_ems: self.using_ems,
|
|
transcript: self.transcript,
|
|
resuming: true,
|
|
cert_verified,
|
|
sig_verified,
|
|
}))
|
|
} else {
|
|
Ok(Box::new(ExpectCcs {
|
|
config: self.config,
|
|
secrets,
|
|
resuming_session: self.resuming_session,
|
|
session_id: server_hello.session_id,
|
|
server_name: self.server_name,
|
|
using_ems: self.using_ems,
|
|
transcript: self.transcript,
|
|
ticket: None,
|
|
resuming: true,
|
|
cert_verified,
|
|
sig_verified,
|
|
}))
|
|
};
|
|
}
|
|
}
|
|
|
|
Ok(Box::new(ExpectCertificate {
|
|
config: self.config,
|
|
resuming_session: self.resuming_session,
|
|
session_id: server_hello.session_id,
|
|
server_name: self.server_name,
|
|
randoms: self.randoms,
|
|
using_ems: self.using_ems,
|
|
transcript: self.transcript,
|
|
suite,
|
|
may_send_cert_status,
|
|
must_issue_new_ticket,
|
|
}))
|
|
}
|
|
}
|
|
}
|
|
|
|
struct ExpectCertificate {
|
|
config: Arc<ClientConfig>,
|
|
resuming_session: Option<persist::Tls12ClientSessionValue>,
|
|
session_id: SessionId,
|
|
server_name: ServerName,
|
|
randoms: ConnectionRandoms,
|
|
using_ems: bool,
|
|
transcript: HandshakeHash,
|
|
pub(super) suite: &'static Tls12CipherSuite,
|
|
may_send_cert_status: bool,
|
|
must_issue_new_ticket: bool,
|
|
}
|
|
|
|
impl State<ClientConnectionData> for ExpectCertificate {
|
|
fn handle(
|
|
mut self: Box<Self>,
|
|
_cx: &mut ClientContext<'_>,
|
|
m: Message,
|
|
) -> hs::NextStateOrError {
|
|
self.transcript.add_message(&m);
|
|
let server_cert_chain = require_handshake_msg_move!(
|
|
m,
|
|
HandshakeType::Certificate,
|
|
HandshakePayload::Certificate
|
|
)?;
|
|
|
|
if self.may_send_cert_status {
|
|
Ok(Box::new(ExpectCertificateStatusOrServerKx {
|
|
config: self.config,
|
|
resuming_session: self.resuming_session,
|
|
session_id: self.session_id,
|
|
server_name: self.server_name,
|
|
randoms: self.randoms,
|
|
using_ems: self.using_ems,
|
|
transcript: self.transcript,
|
|
suite: self.suite,
|
|
server_cert_chain,
|
|
must_issue_new_ticket: self.must_issue_new_ticket,
|
|
}))
|
|
} else {
|
|
let server_cert = ServerCertDetails::new(server_cert_chain, vec![]);
|
|
|
|
Ok(Box::new(ExpectServerKx {
|
|
config: self.config,
|
|
resuming_session: self.resuming_session,
|
|
session_id: self.session_id,
|
|
server_name: self.server_name,
|
|
randoms: self.randoms,
|
|
using_ems: self.using_ems,
|
|
transcript: self.transcript,
|
|
suite: self.suite,
|
|
server_cert,
|
|
must_issue_new_ticket: self.must_issue_new_ticket,
|
|
}))
|
|
}
|
|
}
|
|
}
|
|
|
|
struct ExpectCertificateStatusOrServerKx {
|
|
config: Arc<ClientConfig>,
|
|
resuming_session: Option<persist::Tls12ClientSessionValue>,
|
|
session_id: SessionId,
|
|
server_name: ServerName,
|
|
randoms: ConnectionRandoms,
|
|
using_ems: bool,
|
|
transcript: HandshakeHash,
|
|
suite: &'static Tls12CipherSuite,
|
|
server_cert_chain: CertificatePayload,
|
|
must_issue_new_ticket: bool,
|
|
}
|
|
|
|
impl State<ClientConnectionData> for ExpectCertificateStatusOrServerKx {
|
|
fn handle(self: Box<Self>, cx: &mut ClientContext<'_>, m: Message) -> hs::NextStateOrError {
|
|
match m.payload {
|
|
MessagePayload::Handshake {
|
|
parsed:
|
|
HandshakeMessagePayload {
|
|
payload: HandshakePayload::ServerKeyExchange(..),
|
|
..
|
|
},
|
|
..
|
|
} => Box::new(ExpectServerKx {
|
|
config: self.config,
|
|
resuming_session: self.resuming_session,
|
|
session_id: self.session_id,
|
|
server_name: self.server_name,
|
|
randoms: self.randoms,
|
|
using_ems: self.using_ems,
|
|
transcript: self.transcript,
|
|
suite: self.suite,
|
|
server_cert: ServerCertDetails::new(self.server_cert_chain, vec![]),
|
|
must_issue_new_ticket: self.must_issue_new_ticket,
|
|
})
|
|
.handle(cx, m),
|
|
MessagePayload::Handshake {
|
|
parsed:
|
|
HandshakeMessagePayload {
|
|
payload: HandshakePayload::CertificateStatus(..),
|
|
..
|
|
},
|
|
..
|
|
} => Box::new(ExpectCertificateStatus {
|
|
config: self.config,
|
|
resuming_session: self.resuming_session,
|
|
session_id: self.session_id,
|
|
server_name: self.server_name,
|
|
randoms: self.randoms,
|
|
using_ems: self.using_ems,
|
|
transcript: self.transcript,
|
|
suite: self.suite,
|
|
server_cert_chain: self.server_cert_chain,
|
|
must_issue_new_ticket: self.must_issue_new_ticket,
|
|
})
|
|
.handle(cx, m),
|
|
payload => Err(inappropriate_handshake_message(
|
|
&payload,
|
|
&[ContentType::Handshake],
|
|
&[
|
|
HandshakeType::ServerKeyExchange,
|
|
HandshakeType::CertificateStatus,
|
|
],
|
|
)),
|
|
}
|
|
}
|
|
}
|
|
|
|
struct ExpectCertificateStatus {
|
|
config: Arc<ClientConfig>,
|
|
resuming_session: Option<persist::Tls12ClientSessionValue>,
|
|
session_id: SessionId,
|
|
server_name: ServerName,
|
|
randoms: ConnectionRandoms,
|
|
using_ems: bool,
|
|
transcript: HandshakeHash,
|
|
suite: &'static Tls12CipherSuite,
|
|
server_cert_chain: CertificatePayload,
|
|
must_issue_new_ticket: bool,
|
|
}
|
|
|
|
impl State<ClientConnectionData> for ExpectCertificateStatus {
|
|
fn handle(
|
|
mut self: Box<Self>,
|
|
_cx: &mut ClientContext<'_>,
|
|
m: Message,
|
|
) -> hs::NextStateOrError {
|
|
self.transcript.add_message(&m);
|
|
let server_cert_ocsp_response = require_handshake_msg_move!(
|
|
m,
|
|
HandshakeType::CertificateStatus,
|
|
HandshakePayload::CertificateStatus
|
|
)?
|
|
.into_inner();
|
|
|
|
trace!(
|
|
"Server stapled OCSP response is {:?}",
|
|
&server_cert_ocsp_response
|
|
);
|
|
|
|
let server_cert = ServerCertDetails::new(self.server_cert_chain, server_cert_ocsp_response);
|
|
|
|
Ok(Box::new(ExpectServerKx {
|
|
config: self.config,
|
|
resuming_session: self.resuming_session,
|
|
session_id: self.session_id,
|
|
server_name: self.server_name,
|
|
randoms: self.randoms,
|
|
using_ems: self.using_ems,
|
|
transcript: self.transcript,
|
|
suite: self.suite,
|
|
server_cert,
|
|
must_issue_new_ticket: self.must_issue_new_ticket,
|
|
}))
|
|
}
|
|
}
|
|
|
|
struct ExpectServerKx {
|
|
config: Arc<ClientConfig>,
|
|
resuming_session: Option<persist::Tls12ClientSessionValue>,
|
|
session_id: SessionId,
|
|
server_name: ServerName,
|
|
randoms: ConnectionRandoms,
|
|
using_ems: bool,
|
|
transcript: HandshakeHash,
|
|
suite: &'static Tls12CipherSuite,
|
|
server_cert: ServerCertDetails,
|
|
must_issue_new_ticket: bool,
|
|
}
|
|
|
|
impl State<ClientConnectionData> for ExpectServerKx {
|
|
fn handle(mut self: Box<Self>, cx: &mut ClientContext<'_>, m: Message) -> hs::NextStateOrError {
|
|
let opaque_kx = require_handshake_msg!(
|
|
m,
|
|
HandshakeType::ServerKeyExchange,
|
|
HandshakePayload::ServerKeyExchange
|
|
)?;
|
|
self.transcript.add_message(&m);
|
|
|
|
let ecdhe = opaque_kx
|
|
.unwrap_given_kxa(self.suite.kx)
|
|
.ok_or_else(|| {
|
|
cx.common.send_fatal_alert(
|
|
AlertDescription::DecodeError,
|
|
InvalidMessage::MissingKeyExchange,
|
|
)
|
|
})?;
|
|
|
|
// Save the signature and signed parameters for later verification.
|
|
let mut kx_params = Vec::new();
|
|
ecdhe.params.encode(&mut kx_params);
|
|
let server_kx = ServerKxDetails::new(kx_params, ecdhe.dss);
|
|
|
|
#[cfg_attr(not(feature = "logging"), allow(unused_variables))]
|
|
{
|
|
debug!("ECDHE curve is {:?}", ecdhe.params.curve_params);
|
|
}
|
|
|
|
Ok(Box::new(ExpectServerDoneOrCertReq {
|
|
config: self.config,
|
|
resuming_session: self.resuming_session,
|
|
session_id: self.session_id,
|
|
server_name: self.server_name,
|
|
randoms: self.randoms,
|
|
using_ems: self.using_ems,
|
|
transcript: self.transcript,
|
|
suite: self.suite,
|
|
server_cert: self.server_cert,
|
|
server_kx,
|
|
must_issue_new_ticket: self.must_issue_new_ticket,
|
|
}))
|
|
}
|
|
}
|
|
|
|
fn emit_certificate(
|
|
transcript: &mut HandshakeHash,
|
|
cert_chain: CertificatePayload,
|
|
common: &mut CommonState,
|
|
) {
|
|
let cert = Message {
|
|
version: ProtocolVersion::TLSv1_2,
|
|
payload: MessagePayload::handshake(HandshakeMessagePayload {
|
|
typ: HandshakeType::Certificate,
|
|
payload: HandshakePayload::Certificate(cert_chain),
|
|
}),
|
|
};
|
|
|
|
transcript.add_message(&cert);
|
|
common.send_msg(cert, false);
|
|
}
|
|
|
|
fn emit_clientkx(transcript: &mut HandshakeHash, common: &mut CommonState, pubkey: &PublicKey) {
|
|
let mut buf = Vec::new();
|
|
let ecpoint = PayloadU8::new(Vec::from(pubkey.as_ref()));
|
|
ecpoint.encode(&mut buf);
|
|
let pubkey = Payload::new(buf);
|
|
|
|
let ckx = Message {
|
|
version: ProtocolVersion::TLSv1_2,
|
|
payload: MessagePayload::handshake(HandshakeMessagePayload {
|
|
typ: HandshakeType::ClientKeyExchange,
|
|
payload: HandshakePayload::ClientKeyExchange(pubkey),
|
|
}),
|
|
};
|
|
|
|
transcript.add_message(&ckx);
|
|
common.send_msg(ckx, false);
|
|
}
|
|
|
|
fn emit_certverify(
|
|
transcript: &mut HandshakeHash,
|
|
signer: &dyn Signer,
|
|
common: &mut CommonState,
|
|
) -> Result<(), Error> {
|
|
let message = transcript
|
|
.take_handshake_buf()
|
|
.ok_or_else(|| Error::General("Expected transcript".to_owned()))?;
|
|
|
|
let scheme = signer.scheme();
|
|
let sig = signer.sign(&message)?;
|
|
let body = DigitallySignedStruct::new(scheme, sig);
|
|
|
|
let m = Message {
|
|
version: ProtocolVersion::TLSv1_2,
|
|
payload: MessagePayload::handshake(HandshakeMessagePayload {
|
|
typ: HandshakeType::CertificateVerify,
|
|
payload: HandshakePayload::CertificateVerify(body),
|
|
}),
|
|
};
|
|
|
|
transcript.add_message(&m);
|
|
common.send_msg(m, false);
|
|
Ok(())
|
|
}
|
|
|
|
fn emit_ccs(common: &mut CommonState) {
|
|
let ccs = Message {
|
|
version: ProtocolVersion::TLSv1_2,
|
|
payload: MessagePayload::ChangeCipherSpec(ChangeCipherSpecPayload {}),
|
|
};
|
|
|
|
common.send_msg(ccs, false);
|
|
}
|
|
|
|
fn emit_finished(
|
|
secrets: &ConnectionSecrets,
|
|
transcript: &mut HandshakeHash,
|
|
common: &mut CommonState,
|
|
) {
|
|
let vh = transcript.get_current_hash();
|
|
let verify_data = secrets.client_verify_data(&vh);
|
|
let verify_data_payload = Payload::new(verify_data);
|
|
|
|
let f = Message {
|
|
version: ProtocolVersion::TLSv1_2,
|
|
payload: MessagePayload::handshake(HandshakeMessagePayload {
|
|
typ: HandshakeType::Finished,
|
|
payload: HandshakePayload::Finished(verify_data_payload),
|
|
}),
|
|
};
|
|
|
|
transcript.add_message(&f);
|
|
common.send_msg(f, true);
|
|
}
|
|
|
|
struct ServerKxDetails {
|
|
kx_params: Vec<u8>,
|
|
kx_sig: DigitallySignedStruct,
|
|
}
|
|
|
|
impl ServerKxDetails {
|
|
fn new(params: Vec<u8>, sig: DigitallySignedStruct) -> Self {
|
|
Self {
|
|
kx_params: params,
|
|
kx_sig: sig,
|
|
}
|
|
}
|
|
}
|
|
|
|
// --- 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 ExpectServerDoneOrCertReq {
|
|
config: Arc<ClientConfig>,
|
|
resuming_session: Option<persist::Tls12ClientSessionValue>,
|
|
session_id: SessionId,
|
|
server_name: ServerName,
|
|
randoms: ConnectionRandoms,
|
|
using_ems: bool,
|
|
transcript: HandshakeHash,
|
|
suite: &'static Tls12CipherSuite,
|
|
server_cert: ServerCertDetails,
|
|
server_kx: ServerKxDetails,
|
|
must_issue_new_ticket: bool,
|
|
}
|
|
|
|
impl State<ClientConnectionData> for ExpectServerDoneOrCertReq {
|
|
fn handle(mut self: Box<Self>, cx: &mut ClientContext<'_>, m: Message) -> hs::NextStateOrError {
|
|
if matches!(
|
|
m.payload,
|
|
MessagePayload::Handshake {
|
|
parsed: HandshakeMessagePayload {
|
|
payload: HandshakePayload::CertificateRequest(_),
|
|
..
|
|
},
|
|
..
|
|
}
|
|
) {
|
|
Box::new(ExpectCertificateRequest {
|
|
config: self.config,
|
|
resuming_session: self.resuming_session,
|
|
session_id: self.session_id,
|
|
server_name: self.server_name,
|
|
randoms: self.randoms,
|
|
using_ems: self.using_ems,
|
|
transcript: self.transcript,
|
|
suite: self.suite,
|
|
server_cert: self.server_cert,
|
|
server_kx: self.server_kx,
|
|
must_issue_new_ticket: self.must_issue_new_ticket,
|
|
})
|
|
.handle(cx, m)
|
|
} else {
|
|
self.transcript.abandon_client_auth();
|
|
|
|
Box::new(ExpectServerDone {
|
|
config: self.config,
|
|
resuming_session: self.resuming_session,
|
|
session_id: self.session_id,
|
|
server_name: self.server_name,
|
|
randoms: self.randoms,
|
|
using_ems: self.using_ems,
|
|
transcript: self.transcript,
|
|
suite: self.suite,
|
|
server_cert: self.server_cert,
|
|
server_kx: self.server_kx,
|
|
client_auth: None,
|
|
must_issue_new_ticket: self.must_issue_new_ticket,
|
|
})
|
|
.handle(cx, m)
|
|
}
|
|
}
|
|
}
|
|
|
|
struct ExpectCertificateRequest {
|
|
config: Arc<ClientConfig>,
|
|
resuming_session: Option<persist::Tls12ClientSessionValue>,
|
|
session_id: SessionId,
|
|
server_name: ServerName,
|
|
randoms: ConnectionRandoms,
|
|
using_ems: bool,
|
|
transcript: HandshakeHash,
|
|
suite: &'static Tls12CipherSuite,
|
|
server_cert: ServerCertDetails,
|
|
server_kx: ServerKxDetails,
|
|
must_issue_new_ticket: bool,
|
|
}
|
|
|
|
impl State<ClientConnectionData> for ExpectCertificateRequest {
|
|
fn handle(
|
|
mut self: Box<Self>,
|
|
_cx: &mut ClientContext<'_>,
|
|
m: Message,
|
|
) -> hs::NextStateOrError {
|
|
let certreq = require_handshake_msg!(
|
|
m,
|
|
HandshakeType::CertificateRequest,
|
|
HandshakePayload::CertificateRequest
|
|
)?;
|
|
self.transcript.add_message(&m);
|
|
debug!("Got CertificateRequest {:?}", certreq);
|
|
|
|
// The RFC jovially describes the design here as 'somewhat complicated'
|
|
// and 'somewhat underspecified'. So thanks for that.
|
|
//
|
|
// We ignore certreq.certtypes as a result, since the information it contains
|
|
// is entirely duplicated in certreq.sigschemes.
|
|
|
|
const NO_CONTEXT: Option<Vec<u8>> = None; // TLS 1.2 doesn't use a context.
|
|
let client_auth = ClientAuthDetails::resolve(
|
|
self.config
|
|
.client_auth_cert_resolver
|
|
.as_ref(),
|
|
Some(&certreq.canames),
|
|
&certreq.sigschemes,
|
|
NO_CONTEXT,
|
|
);
|
|
|
|
Ok(Box::new(ExpectServerDone {
|
|
config: self.config,
|
|
resuming_session: self.resuming_session,
|
|
session_id: self.session_id,
|
|
server_name: self.server_name,
|
|
randoms: self.randoms,
|
|
using_ems: self.using_ems,
|
|
transcript: self.transcript,
|
|
suite: self.suite,
|
|
server_cert: self.server_cert,
|
|
server_kx: self.server_kx,
|
|
client_auth: Some(client_auth),
|
|
must_issue_new_ticket: self.must_issue_new_ticket,
|
|
}))
|
|
}
|
|
}
|
|
|
|
struct ExpectServerDone {
|
|
config: Arc<ClientConfig>,
|
|
resuming_session: Option<persist::Tls12ClientSessionValue>,
|
|
session_id: SessionId,
|
|
server_name: ServerName,
|
|
randoms: ConnectionRandoms,
|
|
using_ems: bool,
|
|
transcript: HandshakeHash,
|
|
suite: &'static Tls12CipherSuite,
|
|
server_cert: ServerCertDetails,
|
|
server_kx: ServerKxDetails,
|
|
client_auth: Option<ClientAuthDetails>,
|
|
must_issue_new_ticket: bool,
|
|
}
|
|
|
|
impl State<ClientConnectionData> for ExpectServerDone {
|
|
fn handle(self: Box<Self>, cx: &mut ClientContext<'_>, m: Message) -> hs::NextStateOrError {
|
|
match m.payload {
|
|
MessagePayload::Handshake {
|
|
parsed:
|
|
HandshakeMessagePayload {
|
|
payload: HandshakePayload::ServerHelloDone,
|
|
..
|
|
},
|
|
..
|
|
} => {}
|
|
payload => {
|
|
return Err(inappropriate_handshake_message(
|
|
&payload,
|
|
&[ContentType::Handshake],
|
|
&[HandshakeType::ServerHelloDone],
|
|
));
|
|
}
|
|
}
|
|
|
|
let mut st = *self;
|
|
st.transcript.add_message(&m);
|
|
|
|
cx.common.check_aligned_handshake()?;
|
|
|
|
trace!("Server cert is {:?}", st.server_cert.cert_chain);
|
|
debug!("Server DNS name is {:?}", st.server_name);
|
|
|
|
let suite = st.suite;
|
|
|
|
// 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.
|
|
let (end_entity, intermediates) = st
|
|
.server_cert
|
|
.cert_chain
|
|
.split_first()
|
|
.ok_or(Error::NoCertificatesPresented)?;
|
|
let now = std::time::SystemTime::now();
|
|
let cert_verified = st
|
|
.config
|
|
.verifier
|
|
.verify_server_cert(
|
|
end_entity,
|
|
intermediates,
|
|
&st.server_name,
|
|
&st.server_cert.ocsp_response,
|
|
now,
|
|
)
|
|
.map_err(|err| {
|
|
cx.common
|
|
.send_cert_verify_error_alert(err)
|
|
})?;
|
|
|
|
// 3.
|
|
// Build up the contents of the signed message.
|
|
// It's ClientHello.random || ServerHello.random || ServerKeyExchange.params
|
|
let sig_verified = {
|
|
let mut message = Vec::new();
|
|
message.extend_from_slice(&st.randoms.client);
|
|
message.extend_from_slice(&st.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;
|
|
if !SupportedCipherSuite::from(suite).usable_for_signature_algorithm(sig.scheme.sign())
|
|
{
|
|
warn!(
|
|
"peer signed kx with wrong algorithm (got {:?} expect {:?})",
|
|
sig.scheme.sign(),
|
|
suite.sign
|
|
);
|
|
return Err(PeerMisbehaved::SignedKxWithWrongAlgorithm.into());
|
|
}
|
|
|
|
st.config
|
|
.verifier
|
|
.verify_tls12_signature(&message, &st.server_cert.cert_chain[0], sig)
|
|
.map_err(|err| {
|
|
cx.common
|
|
.send_cert_verify_error_alert(err)
|
|
})?
|
|
};
|
|
cx.common.peer_certificates = Some(st.server_cert.cert_chain);
|
|
|
|
// 4.
|
|
if let Some(client_auth) = &st.client_auth {
|
|
let certs = match client_auth {
|
|
ClientAuthDetails::Empty { .. } => Vec::new(),
|
|
ClientAuthDetails::Verify { certkey, .. } => certkey.cert.clone(),
|
|
};
|
|
emit_certificate(&mut st.transcript, certs, cx.common);
|
|
}
|
|
|
|
// 5a.
|
|
let ecdh_params =
|
|
tls12::decode_ecdh_params::<ServerECDHParams>(cx.common, &st.server_kx.kx_params)?;
|
|
let group =
|
|
kx::KeyExchange::choose(ecdh_params.curve_params.named_group, &st.config.kx_groups)
|
|
.ok_or(Error::PeerMisbehaved(
|
|
PeerMisbehaved::SelectedUnofferedKxGroup,
|
|
))?;
|
|
let kx = kx::KeyExchange::start(group).ok_or(Error::FailedToGetRandomBytes)?;
|
|
|
|
// 5b.
|
|
let mut transcript = st.transcript;
|
|
emit_clientkx(&mut transcript, cx.common, &kx.pubkey);
|
|
// nb. EMS handshake hash only runs up to ClientKeyExchange.
|
|
let ems_seed = st
|
|
.using_ems
|
|
.then(|| transcript.get_current_hash());
|
|
|
|
// 5c.
|
|
if let Some(ClientAuthDetails::Verify { signer, .. }) = &st.client_auth {
|
|
emit_certverify(&mut transcript, signer.as_ref(), cx.common)?;
|
|
}
|
|
|
|
// 5d.
|
|
emit_ccs(cx.common);
|
|
|
|
// 5e. Now commit secrets.
|
|
let secrets = ConnectionSecrets::from_key_exchange(
|
|
kx,
|
|
&ecdh_params.public.0,
|
|
ems_seed,
|
|
st.randoms,
|
|
suite,
|
|
)?;
|
|
|
|
st.config.key_log.log(
|
|
"CLIENT_RANDOM",
|
|
&secrets.randoms.client,
|
|
&secrets.master_secret,
|
|
);
|
|
cx.common
|
|
.start_encryption_tls12(&secrets, Side::Client);
|
|
cx.common
|
|
.record_layer
|
|
.start_encrypting();
|
|
|
|
// 6.
|
|
emit_finished(&secrets, &mut transcript, cx.common);
|
|
|
|
if st.must_issue_new_ticket {
|
|
Ok(Box::new(ExpectNewTicket {
|
|
config: st.config,
|
|
secrets,
|
|
resuming_session: st.resuming_session,
|
|
session_id: st.session_id,
|
|
server_name: st.server_name,
|
|
using_ems: st.using_ems,
|
|
transcript,
|
|
resuming: false,
|
|
cert_verified,
|
|
sig_verified,
|
|
}))
|
|
} else {
|
|
Ok(Box::new(ExpectCcs {
|
|
config: st.config,
|
|
secrets,
|
|
resuming_session: st.resuming_session,
|
|
session_id: st.session_id,
|
|
server_name: st.server_name,
|
|
using_ems: st.using_ems,
|
|
transcript,
|
|
ticket: None,
|
|
resuming: false,
|
|
cert_verified,
|
|
sig_verified,
|
|
}))
|
|
}
|
|
}
|
|
}
|
|
|
|
struct ExpectNewTicket {
|
|
config: Arc<ClientConfig>,
|
|
secrets: ConnectionSecrets,
|
|
resuming_session: Option<persist::Tls12ClientSessionValue>,
|
|
session_id: SessionId,
|
|
server_name: ServerName,
|
|
using_ems: bool,
|
|
transcript: HandshakeHash,
|
|
resuming: bool,
|
|
cert_verified: verify::ServerCertVerified,
|
|
sig_verified: verify::HandshakeSignatureValid,
|
|
}
|
|
|
|
impl State<ClientConnectionData> for ExpectNewTicket {
|
|
fn handle(
|
|
mut self: Box<Self>,
|
|
_cx: &mut ClientContext<'_>,
|
|
m: Message,
|
|
) -> hs::NextStateOrError {
|
|
self.transcript.add_message(&m);
|
|
|
|
let nst = require_handshake_msg_move!(
|
|
m,
|
|
HandshakeType::NewSessionTicket,
|
|
HandshakePayload::NewSessionTicket
|
|
)?;
|
|
|
|
Ok(Box::new(ExpectCcs {
|
|
config: self.config,
|
|
secrets: self.secrets,
|
|
resuming_session: self.resuming_session,
|
|
session_id: self.session_id,
|
|
server_name: self.server_name,
|
|
using_ems: self.using_ems,
|
|
transcript: self.transcript,
|
|
ticket: Some(nst),
|
|
resuming: self.resuming,
|
|
cert_verified: self.cert_verified,
|
|
sig_verified: self.sig_verified,
|
|
}))
|
|
}
|
|
}
|
|
|
|
// -- Waiting for their CCS --
|
|
struct ExpectCcs {
|
|
config: Arc<ClientConfig>,
|
|
secrets: ConnectionSecrets,
|
|
resuming_session: Option<persist::Tls12ClientSessionValue>,
|
|
session_id: SessionId,
|
|
server_name: ServerName,
|
|
using_ems: bool,
|
|
transcript: HandshakeHash,
|
|
ticket: Option<NewSessionTicketPayload>,
|
|
resuming: bool,
|
|
cert_verified: verify::ServerCertVerified,
|
|
sig_verified: verify::HandshakeSignatureValid,
|
|
}
|
|
|
|
impl State<ClientConnectionData> for ExpectCcs {
|
|
fn handle(self: Box<Self>, cx: &mut ClientContext<'_>, m: Message) -> hs::NextStateOrError {
|
|
match m.payload {
|
|
MessagePayload::ChangeCipherSpec(..) => {}
|
|
payload => {
|
|
return Err(inappropriate_message(
|
|
&payload,
|
|
&[ContentType::ChangeCipherSpec],
|
|
));
|
|
}
|
|
}
|
|
// CCS should not be received interleaved with fragmented handshake-level
|
|
// message.
|
|
cx.common.check_aligned_handshake()?;
|
|
|
|
// nb. msgs layer validates trivial contents of CCS
|
|
cx.common
|
|
.record_layer
|
|
.start_decrypting();
|
|
|
|
Ok(Box::new(ExpectFinished {
|
|
config: self.config,
|
|
secrets: self.secrets,
|
|
resuming_session: self.resuming_session,
|
|
session_id: self.session_id,
|
|
server_name: self.server_name,
|
|
using_ems: self.using_ems,
|
|
transcript: self.transcript,
|
|
ticket: self.ticket,
|
|
resuming: self.resuming,
|
|
cert_verified: self.cert_verified,
|
|
sig_verified: self.sig_verified,
|
|
}))
|
|
}
|
|
}
|
|
|
|
struct ExpectFinished {
|
|
config: Arc<ClientConfig>,
|
|
resuming_session: Option<persist::Tls12ClientSessionValue>,
|
|
session_id: SessionId,
|
|
server_name: ServerName,
|
|
using_ems: bool,
|
|
transcript: HandshakeHash,
|
|
ticket: Option<NewSessionTicketPayload>,
|
|
secrets: ConnectionSecrets,
|
|
resuming: bool,
|
|
cert_verified: verify::ServerCertVerified,
|
|
sig_verified: verify::HandshakeSignatureValid,
|
|
}
|
|
|
|
impl ExpectFinished {
|
|
// -- Waiting for their finished --
|
|
fn save_session(&mut self, cx: &mut ClientContext<'_>) {
|
|
// Save a ticket. If we got a new ticket, save that. Otherwise, save the
|
|
// original ticket again.
|
|
let (mut ticket, lifetime) = match self.ticket.take() {
|
|
Some(nst) => (nst.ticket.0, nst.lifetime_hint),
|
|
None => (Vec::new(), 0),
|
|
};
|
|
|
|
if ticket.is_empty() {
|
|
if let Some(resuming_session) = &mut self.resuming_session {
|
|
ticket = resuming_session.take_ticket();
|
|
}
|
|
}
|
|
|
|
if self.session_id.is_empty() && ticket.is_empty() {
|
|
debug!("Session not saved: server didn't allocate id or ticket");
|
|
return;
|
|
}
|
|
|
|
let time_now = match TimeBase::now() {
|
|
Ok(time_now) => time_now,
|
|
#[allow(unused_variables)]
|
|
Err(e) => {
|
|
debug!("Session not saved: {}", e);
|
|
return;
|
|
}
|
|
};
|
|
|
|
let session_value = persist::Tls12ClientSessionValue::new(
|
|
self.secrets.suite(),
|
|
self.session_id,
|
|
ticket,
|
|
self.secrets.get_master_secret(),
|
|
cx.common
|
|
.peer_certificates
|
|
.clone()
|
|
.unwrap_or_default(),
|
|
time_now,
|
|
lifetime,
|
|
self.using_ems,
|
|
);
|
|
|
|
self.config
|
|
.resumption
|
|
.store
|
|
.set_tls12_session(&self.server_name, session_value);
|
|
}
|
|
}
|
|
|
|
impl State<ClientConnectionData> for ExpectFinished {
|
|
fn handle(self: Box<Self>, cx: &mut ClientContext<'_>, m: Message) -> hs::NextStateOrError {
|
|
let mut st = *self;
|
|
let finished =
|
|
require_handshake_msg!(m, HandshakeType::Finished, HandshakePayload::Finished)?;
|
|
|
|
cx.common.check_aligned_handshake()?;
|
|
|
|
// Work out what verify_data we expect.
|
|
let vh = st.transcript.get_current_hash();
|
|
let expect_verify_data = st.secrets.server_verify_data(&vh);
|
|
|
|
// Constant-time verification of this is relatively unimportant: they only
|
|
// get one chance. But it can't hurt.
|
|
let _fin_verified =
|
|
constant_time::verify_slices_are_equal(&expect_verify_data, &finished.0)
|
|
.map_err(|_| {
|
|
cx.common
|
|
.send_fatal_alert(AlertDescription::DecryptError, Error::DecryptError)
|
|
})
|
|
.map(|_| verify::FinishedMessageVerified::assertion())?;
|
|
|
|
// Hash this message too.
|
|
st.transcript.add_message(&m);
|
|
|
|
st.save_session(cx);
|
|
|
|
if st.resuming {
|
|
emit_ccs(cx.common);
|
|
cx.common
|
|
.record_layer
|
|
.start_encrypting();
|
|
emit_finished(&st.secrets, &mut st.transcript, cx.common);
|
|
}
|
|
|
|
cx.common.start_traffic();
|
|
Ok(Box::new(ExpectTraffic {
|
|
secrets: st.secrets,
|
|
_cert_verified: st.cert_verified,
|
|
_sig_verified: st.sig_verified,
|
|
_fin_verified,
|
|
}))
|
|
}
|
|
}
|
|
|
|
// -- Traffic transit state --
|
|
struct ExpectTraffic {
|
|
secrets: ConnectionSecrets,
|
|
_cert_verified: verify::ServerCertVerified,
|
|
_sig_verified: verify::HandshakeSignatureValid,
|
|
_fin_verified: verify::FinishedMessageVerified,
|
|
}
|
|
|
|
impl State<ClientConnectionData> for ExpectTraffic {
|
|
fn handle(self: Box<Self>, cx: &mut ClientContext<'_>, m: Message) -> hs::NextStateOrError {
|
|
match m.payload {
|
|
MessagePayload::ApplicationData(payload) => cx
|
|
.common
|
|
.take_received_plaintext(payload),
|
|
payload => {
|
|
return Err(inappropriate_message(
|
|
&payload,
|
|
&[ContentType::ApplicationData],
|
|
));
|
|
}
|
|
}
|
|
Ok(self)
|
|
}
|
|
|
|
fn export_keying_material(
|
|
&self,
|
|
output: &mut [u8],
|
|
label: &[u8],
|
|
context: Option<&[u8]>,
|
|
) -> Result<(), Error> {
|
|
self.secrets
|
|
.export_keying_material(output, label, context);
|
|
Ok(())
|
|
}
|
|
|
|
#[cfg(feature = "secret_extraction")]
|
|
fn extract_secrets(&self) -> Result<PartiallyExtractedSecrets, Error> {
|
|
self.secrets
|
|
.extract_secrets(Side::Client)
|
|
}
|
|
}
|