| use std::prelude::v1::*; |
| use msgs::enums::{ContentType, HandshakeType, ExtensionType, SignatureScheme}; |
| use msgs::enums::{Compression, ProtocolVersion, AlertDescription, NamedGroup}; |
| use msgs::message::{Message, MessagePayload}; |
| use msgs::base::{Payload, PayloadU8}; |
| use msgs::handshake::{HandshakePayload, HandshakeMessagePayload, ClientHelloPayload}; |
| use msgs::handshake::{SessionID, Random, ServerHelloPayload}; |
| use msgs::handshake::{ClientExtension, HasServerExtensions}; |
| use msgs::handshake::{SupportedSignatureSchemes, SupportedMandatedSignatureSchemes}; |
| use msgs::handshake::DecomposedSignatureScheme; |
| use msgs::handshake::{NamedGroups, SupportedGroups, KeyShareEntry, EncryptedExtensions}; |
| use msgs::handshake::{ECPointFormatList, SupportedPointFormats}; |
| use msgs::handshake::{ProtocolNameList, ConvertProtocolNameList}; |
| use msgs::handshake::{CertificatePayloadTLS13, CertificateEntry}; |
| use msgs::handshake::ServerKeyExchangePayload; |
| use msgs::handshake::DigitallySignedStruct; |
| use msgs::handshake::{PresharedKeyIdentity, PresharedKeyOffer, HelloRetryRequest}; |
| use msgs::handshake::{CertificateStatusRequest, SCTList}; |
| use msgs::enums::{ClientCertificateType, PSKKeyExchangeMode, ECPointFormat}; |
| use msgs::codec::Codec; |
| use msgs::persist; |
| use msgs::ccs::ChangeCipherSpecPayload; |
| use client::ClientSessionImpl; |
| use session::SessionSecrets; |
| use key_schedule::{KeySchedule, SecretKind}; |
| use cipher; |
| use suites; |
| use hash_hs; |
| use verify; |
| use rand; |
| use ticketer; |
| use error::TLSError; |
| use handshake::{check_message, check_handshake_message}; |
| |
| use client::common::{ServerCertDetails, ServerKXDetails, HandshakeDetails}; |
| use client::common::{ClientHelloDetails, ReceivedTicketDetails, ClientAuthDetails}; |
| |
| use std::mem; |
| use ring::constant_time; |
| use webpki; |
| |
| // draft-ietf-tls-tls13-23 |
| const TLS13_DRAFT: u16 = 0x7f17; |
| |
| macro_rules! extract_handshake( |
| ( $m:expr, $t:path ) => ( |
| match $m.payload { |
| MessagePayload::Handshake(ref hsp) => match hsp.payload { |
| $t(ref hm) => Some(hm), |
| _ => None |
| }, |
| _ => None |
| } |
| ) |
| ); |
| |
| macro_rules! extract_handshake_mut( |
| ( $m:expr, $t:path ) => ( |
| match $m.payload { |
| MessagePayload::Handshake(hsp) => match hsp.payload { |
| $t(hm) => Some(hm), |
| _ => None |
| }, |
| _ => None |
| } |
| ) |
| ); |
| |
| type CheckResult = Result<(), TLSError>; |
| type NextState = Box<State + Send + Sync>; |
| type NextStateOrError = Result<NextState, TLSError>; |
| |
| pub trait State { |
| fn check_message(&self, m: &Message) -> CheckResult; |
| fn handle(self: Box<Self>, sess: &mut ClientSessionImpl, m: Message) -> NextStateOrError; |
| } |
| |
| fn illegal_param(sess: &mut ClientSessionImpl, why: &str) -> TLSError { |
| sess.common.send_fatal_alert(AlertDescription::IllegalParameter); |
| TLSError::PeerMisbehavedError(why.to_string()) |
| } |
| |
| fn check_aligned_handshake(sess: &mut ClientSessionImpl) -> Result<(), TLSError> { |
| if !sess.common.handshake_joiner.is_empty() { |
| Err(illegal_param(sess, "keys changed with pending hs fragment")) |
| } else { |
| Ok(()) |
| } |
| } |
| |
| fn find_session(sess: &mut ClientSessionImpl, dns_name: webpki::DNSNameRef) |
| -> Option<persist::ClientSessionValue> { |
| let key = persist::ClientSessionKey::session_for_dns_name(dns_name); |
| let key_buf = key.get_encoding(); |
| |
| let maybe_value = sess.config.session_persistence.get(&key_buf); |
| |
| if maybe_value.is_none() { |
| debug!("No cached session for {:?}", dns_name); |
| return None; |
| } |
| |
| let value = maybe_value.unwrap(); |
| if let Some(result) = persist::ClientSessionValue::read_bytes(&value) { |
| if result.has_expired(ticketer::timebase()) { |
| None |
| } else { |
| Some(result) |
| } |
| } else { |
| None |
| } |
| } |
| |
| fn find_kx_hint(sess: &mut ClientSessionImpl, dns_name: webpki::DNSNameRef) -> Option<NamedGroup> { |
| let key = persist::ClientSessionKey::hint_for_dns_name(dns_name); |
| let key_buf = key.get_encoding(); |
| |
| let maybe_value = sess.config.session_persistence.get(&key_buf); |
| maybe_value.and_then(|enc| NamedGroup::read_bytes(&enc)) |
| } |
| |
| fn save_kx_hint(sess: &mut ClientSessionImpl, dns_name: webpki::DNSNameRef, group: NamedGroup) { |
| let key = persist::ClientSessionKey::hint_for_dns_name(dns_name); |
| |
| sess.config.session_persistence.put(key.get_encoding(), group.get_encoding()); |
| } |
| |
| /// If we have a ticket, we use the sessionid as a signal that we're |
| /// doing an abbreviated handshake. See section 3.4 in RFC5077. |
| fn randomise_sessionid_for_ticket(csv: &mut persist::ClientSessionValue) { |
| if !csv.ticket.0.is_empty() { |
| let mut random_id = [0u8; 32]; |
| rand::fill_random(&mut random_id); |
| csv.session_id = SessionID::new(&random_id); |
| } |
| } |
| |
| /// This implements the horrifying TLS1.3 hack where PSK binders have a |
| /// data dependency on the message they are contained within. |
| pub fn fill_in_psk_binder(sess: &mut ClientSessionImpl, |
| handshake: &mut HandshakeDetails, |
| hmp: &mut HandshakeMessagePayload) { |
| // We need to know the hash function of the suite we're trying to resume into. |
| let resuming = handshake.resuming_session.as_ref().unwrap(); |
| let suite_hash = sess.find_cipher_suite(resuming.cipher_suite).unwrap().get_hash(); |
| |
| // The binder is calculated over the clienthello, but doesn't include itself or its |
| // length, or the length of its container. |
| let binder_plaintext = hmp.get_encoding_for_binder_signing(); |
| let handshake_hash = |
| handshake.transcript.get_hash_given(suite_hash, &binder_plaintext); |
| |
| let mut empty_hash_ctx = hash_hs::HandshakeHash::new(); |
| empty_hash_ctx.start_hash(suite_hash); |
| let empty_hash = empty_hash_ctx.get_current_hash(); |
| |
| // Run a fake key_schedule to simulate what the server will do if it choses |
| // to resume. |
| let mut key_schedule = KeySchedule::new(suite_hash); |
| key_schedule.input_secret(&resuming.master_secret.0); |
| let base_key = key_schedule.derive(SecretKind::ResumptionPSKBinderKey, &empty_hash); |
| let real_binder = key_schedule.sign_verify_data(&base_key, &handshake_hash); |
| |
| if let HandshakePayload::ClientHello(ref mut ch) = hmp.payload { |
| ch.set_psk_binder(real_binder); |
| }; |
| } |
| |
| struct InitialState { |
| handshake: HandshakeDetails, |
| } |
| |
| impl InitialState { |
| fn new(host_name: webpki::DNSName, extra_exts: Vec<ClientExtension>) -> InitialState { |
| InitialState { |
| handshake: HandshakeDetails::new(host_name, extra_exts), |
| } |
| } |
| |
| fn emit_initial_client_hello(mut self, sess: &mut ClientSessionImpl) -> NextState { |
| if sess.config.client_auth_cert_resolver.has_certs() { |
| self.handshake.transcript.set_client_auth_enabled(); |
| } |
| let hello_details = ClientHelloDetails::new(); |
| emit_client_hello_for_retry(sess, self.handshake, hello_details, None) |
| } |
| } |
| |
| |
| pub fn start_handshake(sess: &mut ClientSessionImpl, host_name: webpki::DNSName, |
| extra_exts: Vec<ClientExtension>) -> NextState { |
| InitialState::new(host_name, extra_exts) |
| .emit_initial_client_hello(sess) |
| } |
| |
| struct ExpectServerHello { |
| handshake: HandshakeDetails, |
| hello: ClientHelloDetails, |
| server_cert: ServerCertDetails, |
| may_send_cert_status: bool, |
| must_issue_new_ticket: bool, |
| } |
| |
| struct ExpectServerHelloOrHelloRetryRequest(ExpectServerHello); |
| |
| fn emit_fake_ccs(hs: &mut HandshakeDetails, sess: &mut ClientSessionImpl) { |
| if hs.sent_tls13_fake_ccs { |
| return; |
| } |
| |
| let m = Message { |
| typ: ContentType::ChangeCipherSpec, |
| version: ProtocolVersion::TLSv1_2, |
| payload: MessagePayload::ChangeCipherSpec(ChangeCipherSpecPayload {}) |
| }; |
| sess.common.send_msg(m, false); |
| hs.sent_tls13_fake_ccs = true; |
| } |
| |
| fn compatible_suite(sess: &ClientSessionImpl, |
| resuming_suite: Option<&suites::SupportedCipherSuite>) -> bool { |
| match resuming_suite { |
| Some(resuming_suite) => { |
| if let Some(suite) = sess.common.get_suite() { |
| suite.can_resume_to(&resuming_suite) |
| } else { |
| true |
| } |
| } |
| None => false |
| } |
| } |
| |
| fn emit_client_hello_for_retry(sess: &mut ClientSessionImpl, |
| mut handshake: HandshakeDetails, |
| mut hello: ClientHelloDetails, |
| retryreq: Option<&HelloRetryRequest>) -> NextState { |
| // Do we have a SessionID or ticket cached for this host? |
| handshake.resuming_session = find_session(sess, handshake.dns_name.as_ref()); |
| let (session_id, ticket, resume_version) = if handshake.resuming_session.is_some() { |
| let resuming = handshake.resuming_session.as_mut().unwrap(); |
| if resuming.version == ProtocolVersion::TLSv1_2 { |
| randomise_sessionid_for_ticket(resuming); |
| } |
| debug!("Resuming session"); |
| (resuming.session_id, resuming.ticket.0.clone(), resuming.version) |
| } else { |
| debug!("Not resuming any session"); |
| (SessionID::empty(), Vec::new(), ProtocolVersion::Unknown(0)) |
| }; |
| |
| let support_tls12 = sess.config.versions.contains(&ProtocolVersion::TLSv1_2); |
| let support_tls13 = sess.config.versions.contains(&ProtocolVersion::TLSv1_3); |
| |
| let mut supported_versions = Vec::new(); |
| if support_tls13 { |
| supported_versions.push(ProtocolVersion::Unknown(TLS13_DRAFT)); |
| } |
| |
| if support_tls12 { |
| supported_versions.push(ProtocolVersion::TLSv1_2); |
| } |
| |
| let mut key_shares = vec![]; |
| |
| if support_tls13 { |
| // Choose our groups: |
| // - if we've been asked via HelloRetryRequest for a specific |
| // one, do that. |
| // - if not, we might have a hint of what the server supports |
| // - if not, send just X25519. |
| // |
| let groups = retryreq.and_then(|req| req.get_requested_key_share_group()) |
| .or_else(|| find_kx_hint(sess, handshake.dns_name.as_ref())) |
| .or_else(|| Some(NamedGroup::X25519)) |
| .map(|grp| vec![ grp ]) |
| .unwrap(); |
| |
| for group in groups { |
| // in reply to HelloRetryRequest, we must not alter any existing key |
| // shares |
| if let Some(already_offered_share) = hello.find_key_share(group) { |
| key_shares.push(KeyShareEntry::new(group, &already_offered_share.pubkey)); |
| hello.offered_key_shares.push(already_offered_share); |
| continue; |
| } |
| |
| if let Some(key_share) = suites::KeyExchange::start_ecdhe(group) { |
| key_shares.push(KeyShareEntry::new(group, &key_share.pubkey)); |
| hello.offered_key_shares.push(key_share); |
| } |
| } |
| } |
| |
| let mut exts = Vec::new(); |
| if !supported_versions.is_empty() { |
| exts.push(ClientExtension::SupportedVersions(supported_versions)); |
| } |
| if sess.config.enable_sni { |
| exts.push(ClientExtension::make_sni(handshake.dns_name.as_ref())); |
| } |
| exts.push(ClientExtension::ECPointFormats(ECPointFormatList::supported())); |
| exts.push(ClientExtension::NamedGroups(NamedGroups::supported())); |
| exts.push(ClientExtension::SignatureAlgorithms(SupportedSignatureSchemes::supported_verify())); |
| exts.push(ClientExtension::ExtendedMasterSecretRequest); |
| exts.push(ClientExtension::CertificateStatusRequest(CertificateStatusRequest::build_ocsp())); |
| |
| if sess.config.ct_logs.is_some() { |
| exts.push(ClientExtension::SignedCertificateTimestampRequest); |
| } |
| |
| if support_tls13 { |
| exts.push(ClientExtension::KeyShare(key_shares)); |
| } |
| |
| if let Some(cookie) = retryreq.and_then(|req| req.get_cookie()) { |
| exts.push(ClientExtension::Cookie(cookie.clone())); |
| } |
| |
| if support_tls13 && sess.config.enable_tickets { |
| // We could support PSK_KE here too. Such connections don't |
| // have forward secrecy, and are similar to TLS1.2 resumption. |
| let psk_modes = vec![ PSKKeyExchangeMode::PSK_DHE_KE ]; |
| exts.push(ClientExtension::PresharedKeyModes(psk_modes)); |
| } |
| |
| if !sess.config.alpn_protocols.is_empty() { |
| exts.push(ClientExtension::Protocols(ProtocolNameList::from_strings(&sess.config |
| .alpn_protocols))); |
| } |
| |
| |
| let fill_in_binder = if support_tls13 && sess.config.enable_tickets && |
| resume_version == ProtocolVersion::TLSv1_3 && |
| !ticket.is_empty() { |
| let resuming_suite = handshake.resuming_session |
| .as_ref() |
| .and_then(|resume| sess.find_cipher_suite(resume.cipher_suite)); |
| |
| if compatible_suite(sess, resuming_suite) { |
| // Finally, and only for TLS1.3 with a ticket resumption, include a binder |
| // for our ticket. This must go last. |
| // |
| // Include an empty binder. It gets filled in below because it depends on |
| // the message it's contained in (!!!). |
| let (obfuscated_ticket_age, suite) = { |
| let resuming = handshake.resuming_session |
| .as_ref() |
| .unwrap(); |
| (resuming.get_obfuscated_ticket_age(ticketer::timebase()), resuming.cipher_suite) |
| }; |
| |
| let binder_len = sess.find_cipher_suite(suite).unwrap().get_hash().output_len; |
| let binder = vec![0u8; binder_len]; |
| |
| let psk_identity = PresharedKeyIdentity::new(ticket, obfuscated_ticket_age); |
| let psk_ext = PresharedKeyOffer::new(psk_identity, binder); |
| exts.push(ClientExtension::PresharedKey(psk_ext)); |
| true |
| } else { |
| false |
| } |
| } else if sess.config.enable_tickets { |
| // If we have a ticket, include it. Otherwise, request one. |
| if ticket.is_empty() { |
| exts.push(ClientExtension::SessionTicketRequest); |
| } else { |
| exts.push(ClientExtension::SessionTicketOffer(Payload::new(ticket))); |
| } |
| false |
| } else { |
| false |
| }; |
| |
| exts.extend(handshake.extra_exts.iter().cloned()); |
| |
| // Note what extensions we sent. |
| hello.sent_extensions = exts.iter() |
| .map(|ext| ext.get_type()) |
| .collect(); |
| |
| let mut chp = HandshakeMessagePayload { |
| typ: HandshakeType::ClientHello, |
| payload: HandshakePayload::ClientHello(ClientHelloPayload { |
| client_version: ProtocolVersion::TLSv1_2, |
| random: Random::from_slice(&handshake.randoms.client), |
| session_id, |
| cipher_suites: sess.get_cipher_suites(), |
| compression_methods: vec![Compression::Null], |
| extensions: exts, |
| }), |
| }; |
| |
| if fill_in_binder { |
| fill_in_psk_binder(sess, &mut handshake, &mut chp); |
| } |
| |
| let ch = Message { |
| typ: ContentType::Handshake, |
| // "This value MUST be set to 0x0303 for all records generated |
| // by a TLS 1.3 implementation other than an initial ClientHello |
| // (i.e., one not generated after a HelloRetryRequest)" |
| version: if retryreq.is_some() { |
| ProtocolVersion::TLSv1_2 |
| } else { |
| ProtocolVersion::TLSv1_0 |
| }, |
| payload: MessagePayload::Handshake(chp), |
| }; |
| |
| if retryreq.is_some() { |
| // send dummy CCS to fool middleboxes prior |
| // to second client hello |
| emit_fake_ccs(&mut handshake, sess); |
| } |
| |
| trace!("Sending ClientHello {:#?}", ch); |
| |
| handshake.transcript.add_message(&ch); |
| sess.common.send_msg(ch, false); |
| |
| let next = ExpectServerHello { |
| handshake, hello, |
| server_cert: ServerCertDetails::new(), |
| may_send_cert_status: false, |
| must_issue_new_ticket: false, |
| }; |
| |
| if support_tls13 && retryreq.is_none() { |
| Box::new(ExpectServerHelloOrHelloRetryRequest(next)) |
| } else { |
| Box::new(next) |
| } |
| } |
| |
| // 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, |
| ]; |
| |
| fn validate_server_hello_tls13(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 process_alpn_protocol(sess: &mut ClientSessionImpl, |
| proto: Option<&str>) |
| -> Result<(), TLSError> { |
| sess.alpn_protocol = proto.map(|s| s.to_owned()); |
| if sess.alpn_protocol.is_some() && |
| !sess.config.alpn_protocols.contains(sess.alpn_protocol.as_ref().unwrap()) { |
| return Err(illegal_param(sess, "server sent non-offered ALPN protocol")); |
| } |
| debug!("ALPN protocol is {:?}", sess.alpn_protocol); |
| Ok(()) |
| } |
| |
| impl ExpectServerHello { |
| fn start_handshake_traffic(&mut self, |
| sess: &mut ClientSessionImpl, |
| server_hello: &ServerHelloPayload) |
| -> Result<(), TLSError> { |
| let suite = sess.common.get_suite_assert(); |
| let hash = suite.get_hash(); |
| let mut key_schedule = KeySchedule::new(hash); |
| |
| if let Some(selected_psk) = server_hello.get_psk_index() { |
| if let Some(ref resuming) = self.handshake.resuming_session { |
| let resume_from_suite = sess.find_cipher_suite(resuming.cipher_suite).unwrap(); |
| if !resume_from_suite.can_resume_to(suite) { |
| return Err(TLSError::PeerMisbehavedError("server resuming incompatible suite" |
| .to_string())); |
| } |
| |
| if selected_psk != 0 { |
| return Err(TLSError::PeerMisbehavedError("server selected invalid psk" |
| .to_string())); |
| } |
| |
| debug!("Resuming using PSK"); |
| key_schedule.input_secret(&resuming.master_secret.0); |
| } else { |
| return Err(TLSError::PeerMisbehavedError("server selected unoffered psk".to_string())); |
| } |
| } else { |
| debug!("Not resuming"); |
| key_schedule.input_empty(); |
| self.handshake.resuming_session.take(); |
| } |
| |
| let their_key_share = server_hello.get_key_share() |
| .ok_or_else(|| { |
| sess.common.send_fatal_alert(AlertDescription::MissingExtension); |
| TLSError::PeerMisbehavedError("missing key share".to_string()) |
| })?; |
| |
| let our_key_share = self.hello.find_key_share_and_discard_others(their_key_share.group) |
| .ok_or_else(|| illegal_param(sess, "wrong group for key share"))?; |
| let shared = our_key_share.complete(&their_key_share.payload.0) |
| .ok_or_else(|| TLSError::PeerMisbehavedError("key exchange failed" |
| .to_string()))?; |
| |
| save_kx_hint(sess, self.handshake.dns_name.as_ref(), their_key_share.group); |
| key_schedule.input_secret(&shared.premaster_secret); |
| |
| check_aligned_handshake(sess)?; |
| |
| let handshake_hash = self.handshake.transcript.get_current_hash(); |
| let write_key = key_schedule.derive(SecretKind::ClientHandshakeTrafficSecret, &handshake_hash); |
| let read_key = key_schedule.derive(SecretKind::ServerHandshakeTrafficSecret, &handshake_hash); |
| sess.common.set_message_encrypter(cipher::new_tls13_write(suite, &write_key)); |
| sess.common.set_message_decrypter(cipher::new_tls13_read(suite, &read_key)); |
| // sess.config.key_log.log("CLIENT_HANDSHAKE_TRAFFIC_SECRET", |
| // &self.handshake.randoms.client, |
| // &write_key); |
| // sess.config.key_log.log("SERVER_HANDSHAKE_TRAFFIC_SECRET", |
| // &self.handshake.randoms.client, |
| // &read_key); |
| key_schedule.current_client_traffic_secret = write_key; |
| key_schedule.current_server_traffic_secret = read_key; |
| |
| sess.common.set_key_schedule(key_schedule); |
| |
| Ok(()) |
| } |
| |
| fn into_expect_tls13_encrypted_extensions(self) -> NextState { |
| Box::new(ExpectTLS13EncryptedExtensions { |
| handshake: self.handshake, |
| server_cert: self.server_cert, |
| hello: self.hello, |
| }) |
| } |
| |
| fn into_expect_tls12_new_ticket_resume(self, |
| certv: verify::ServerCertVerified, |
| sigv: verify::HandshakeSignatureValid) -> NextState { |
| Box::new(ExpectTLS12NewTicket { |
| handshake: self.handshake, |
| resuming: true, |
| cert_verified: certv, |
| sig_verified: sigv, |
| }) |
| } |
| |
| fn into_expect_tls12_ccs_resume(self, |
| certv: verify::ServerCertVerified, |
| sigv: verify::HandshakeSignatureValid) -> NextState { |
| Box::new(ExpectTLS12CCS { |
| handshake: self.handshake, |
| ticket: ReceivedTicketDetails::new(), |
| resuming: true, |
| cert_verified: certv, |
| sig_verified: sigv, |
| }) |
| } |
| |
| fn into_expect_tls12_certificate(self) -> NextState { |
| Box::new(ExpectTLS12Certificate { |
| handshake: self.handshake, |
| server_cert: self.server_cert, |
| may_send_cert_status: self.may_send_cert_status, |
| must_issue_new_ticket: self.must_issue_new_ticket, |
| }) |
| } |
| } |
| |
| impl State for ExpectServerHello { |
| fn check_message(&self, m: &Message) -> CheckResult { |
| check_handshake_message(m, &[HandshakeType::ServerHello]) |
| } |
| |
| fn handle(mut self: Box<Self>, sess: &mut ClientSessionImpl, m: Message) -> NextStateOrError { |
| let server_hello = extract_handshake!(m, HandshakePayload::ServerHello).unwrap(); |
| trace!("We got ServerHello {:#?}", server_hello); |
| |
| use ProtocolVersion::{TLSv1_2, TLSv1_3}; |
| |
| let server_version = if server_hello.legacy_version == TLSv1_2 { |
| server_hello.get_supported_versions() |
| .unwrap_or(server_hello.legacy_version) |
| } else { |
| server_hello.legacy_version |
| }; |
| |
| match server_version { |
| TLSv1_3 | ProtocolVersion::Unknown(TLS13_DRAFT) if sess.config |
| .versions |
| .contains(&TLSv1_3) => { |
| sess.common.negotiated_version = Some(TLSv1_3); |
| } |
| TLSv1_2 if sess.config.versions.contains(&TLSv1_2) => { |
| sess.common.negotiated_version = Some(TLSv1_2); |
| |
| if server_hello.get_supported_versions().is_some() { |
| return Err(illegal_param(sess, "server chose v1.2 using v1.3 extension")); |
| } |
| } |
| _ => { |
| sess.common.send_fatal_alert(AlertDescription::ProtocolVersion); |
| return Err(TLSError::PeerIncompatibleError("server does not support TLS v1.2/v1.3" |
| .to_string())); |
| } |
| }; |
| |
| if server_hello.compression_method != Compression::Null { |
| return Err(illegal_param(sess, "server chose non-Null compression")); |
| } |
| |
| if server_hello.has_duplicate_extension() { |
| sess.common.send_fatal_alert(AlertDescription::DecodeError); |
| return Err(TLSError::PeerMisbehavedError("server sent duplicate extensions".to_string())); |
| } |
| |
| let allowed_unsolicited = [ ExtensionType::RenegotiationInfo ]; |
| if self.hello.server_sent_unsolicited_extensions(&server_hello.extensions, |
| &allowed_unsolicited) { |
| sess.common.send_fatal_alert(AlertDescription::UnsupportedExtension); |
| return Err(TLSError::PeerMisbehavedError("server sent unsolicited extension".to_string())); |
| } |
| |
| // Extract ALPN protocol |
| if !sess.common.is_tls13() { |
| process_alpn_protocol(sess, server_hello.get_alpn_protocol())?; |
| } |
| |
| // Reject QUIC if TLS1.2 is in use. |
| if !sess.common.is_tls13() && |
| server_hello.find_extension(ExtensionType::TransportParameters).is_some() { |
| sess.common.send_fatal_alert(AlertDescription::UnsupportedExtension); |
| return Err(TLSError::PeerMisbehavedError("server wants to do quic+tls1.2".to_string())); |
| } |
| |
| // If ECPointFormats extension is supplied by the server, it must contain |
| // Uncompressed. But it's allowed to be omitted. |
| if let Some(point_fmts) = server_hello.get_ecpoints_extension() { |
| if !point_fmts.contains(&ECPointFormat::Uncompressed) { |
| sess.common.send_fatal_alert(AlertDescription::HandshakeFailure); |
| return Err(TLSError::PeerMisbehavedError("server does not support uncompressed points" |
| .to_string())); |
| } |
| } |
| |
| let scs = sess.find_cipher_suite(server_hello.cipher_suite); |
| |
| if scs.is_none() { |
| sess.common.send_fatal_alert(AlertDescription::HandshakeFailure); |
| return Err(TLSError::PeerMisbehavedError("server chose non-offered ciphersuite" |
| .to_string())); |
| } |
| |
| debug!("Using ciphersuite {:?}", server_hello.cipher_suite); |
| if !sess.common.set_suite(scs.unwrap()) { |
| return Err(illegal_param(sess, "server varied selected ciphersuite")); |
| } |
| |
| let version = sess.common.negotiated_version.unwrap(); |
| if !sess.common.get_suite_assert().usable_for_version(version) { |
| return Err(illegal_param(sess, "server chose unusable ciphersuite for version")); |
| } |
| |
| // Start our handshake hash, and input the server-hello. |
| self.handshake.transcript.start_hash(sess.common.get_suite_assert().get_hash()); |
| self.handshake.transcript.add_message(&m); |
| |
| // For TLS1.3, start message encryption using |
| // handshake_traffic_secret. |
| if sess.common.is_tls13() { |
| validate_server_hello_tls13(sess, server_hello)?; |
| self.start_handshake_traffic(sess, server_hello)?; |
| emit_fake_ccs(&mut self.handshake, sess); |
| return Ok(self.into_expect_tls13_encrypted_extensions()); |
| } |
| |
| // TLS1.2 only from here-on |
| |
| // Save ServerRandom and SessionID |
| server_hello.random.write_slice(&mut self.handshake.randoms.server); |
| self.handshake.session_id = server_hello.session_id; |
| |
| // Doing EMS? |
| if server_hello.ems_support_acked() { |
| self.handshake.using_ems = true; |
| } |
| |
| // Might the server send a ticket? |
| let with_tickets = if server_hello.find_extension(ExtensionType::SessionTicket).is_some() { |
| debug!("Server supports tickets"); |
| true |
| } else { |
| false |
| }; |
| self.must_issue_new_ticket = with_tickets; |
| |
| // Might the server send a CertificateStatus between Certificate and |
| // ServerKeyExchange? |
| if server_hello.find_extension(ExtensionType::StatusRequest).is_some() { |
| debug!("Server may staple OCSP response"); |
| self.may_send_cert_status = true; |
| } |
| |
| // Save any sent SCTs for verification against the certificate. |
| if let Some(sct_list) = server_hello.get_sct_list() { |
| debug!("Server sent {:?} SCTs", sct_list.len()); |
| |
| if sct_list_is_invalid(sct_list) { |
| let error_msg = "server sent invalid SCT list".to_string(); |
| return Err(TLSError::PeerMisbehavedError(error_msg)); |
| } |
| self.server_cert.scts = Some(sct_list.clone()); |
| } |
| |
| // See if we're successfully resuming. |
| let mut abbreviated_handshake = false; |
| if let Some(ref resuming) = self.handshake.resuming_session { |
| if resuming.session_id == self.handshake.session_id { |
| debug!("Server agreed to resume"); |
| abbreviated_handshake = true; |
| |
| // Is the server telling lies about the ciphersuite? |
| if resuming.cipher_suite != scs.unwrap().suite { |
| let error_msg = "abbreviated handshake offered, but with varied cs".to_string(); |
| return Err(TLSError::PeerMisbehavedError(error_msg)); |
| } |
| |
| // And about EMS support? |
| if resuming.extended_ms != self.handshake.using_ems { |
| let error_msg = "server varied ems support over resume".to_string(); |
| return Err(TLSError::PeerMisbehavedError(error_msg)); |
| } |
| |
| let secrets = SessionSecrets::new_resume(&self.handshake.randoms, |
| scs.unwrap().get_hash(), |
| &resuming.master_secret.0); |
| // sess.config.key_log.log("CLIENT_RANDOM", |
| // &secrets.randoms.client, |
| // &secrets.master_secret); |
| sess.common.start_encryption_tls12(secrets); |
| } |
| } |
| |
| if abbreviated_handshake { |
| // Since we're resuming, we verified the certificate and |
| // proof of possession in the prior session. |
| let certv = verify::ServerCertVerified::assertion(); |
| let sigv = verify::HandshakeSignatureValid::assertion(); |
| |
| if self.must_issue_new_ticket { |
| Ok(self.into_expect_tls12_new_ticket_resume(certv, sigv)) |
| } else { |
| Ok(self.into_expect_tls12_ccs_resume(certv, sigv)) |
| } |
| } else { |
| Ok(self.into_expect_tls12_certificate()) |
| } |
| } |
| } |
| |
| impl ExpectServerHelloOrHelloRetryRequest { |
| fn into_expect_server_hello(self) -> NextState { |
| Box::new(self.0) |
| } |
| |
| fn handle_hello_retry_request(mut self, sess: &mut ClientSessionImpl, m: Message) -> NextStateOrError { |
| check_handshake_message(&m, &[HandshakeType::HelloRetryRequest])?; |
| |
| let hrr = extract_handshake!(m, HandshakePayload::HelloRetryRequest).unwrap(); |
| trace!("Got HRR {:?}", hrr); |
| |
| let has_cookie = hrr.get_cookie().is_some(); |
| let req_group = hrr.get_requested_key_share_group(); |
| |
| // A retry request is illegal if it contains no cookie and asks for |
| // retry of a group we already sent. |
| if !has_cookie && req_group.map(|g| self.0.hello.has_key_share(g)).unwrap_or(false) { |
| return Err(illegal_param(sess, "server requested hrr with our group")); |
| } |
| |
| // Or asks for us to retry on an unsupported group. |
| if let Some(group) = req_group { |
| if !NamedGroups::supported().contains(&group) { |
| return Err(illegal_param(sess, "server requested hrr with bad group")); |
| } |
| } |
| |
| // Or has an empty cookie. |
| if has_cookie && hrr.get_cookie().unwrap().0.is_empty() { |
| return Err(illegal_param(sess, "server requested hrr with empty cookie")); |
| } |
| |
| // Or has something unrecognised |
| if hrr.has_unknown_extension() { |
| sess.common.send_fatal_alert(AlertDescription::UnsupportedExtension); |
| return Err(TLSError::PeerIncompatibleError("server sent hrr with unhandled extension" |
| .to_string())); |
| } |
| |
| // Or has the same extensions more than once |
| if hrr.has_duplicate_extension() { |
| return Err(illegal_param(sess, "server send duplicate hrr extensions")); |
| } |
| |
| // Or asks us to change nothing. |
| if !has_cookie && req_group.is_none() { |
| return Err(illegal_param(sess, "server requested hrr with no changes")); |
| } |
| |
| // Or asks us to talk a protocol we didn't offer, or doesn't support HRR at all. |
| match hrr.get_supported_versions() { |
| Some(ProtocolVersion::TLSv1_3) | |
| Some(ProtocolVersion::Unknown(TLS13_DRAFT)) => { |
| sess.common.negotiated_version = Some(ProtocolVersion::TLSv1_3); |
| } |
| _ => { |
| return Err(illegal_param(sess, "server requested unsupported version in hrr")); |
| } |
| } |
| |
| // Or asks us to use a ciphersuite we didn't offer. |
| let maybe_cs = sess.find_cipher_suite(hrr.cipher_suite); |
| let cs = match maybe_cs { |
| Some(cs) => cs, |
| None => { |
| return Err(illegal_param(sess, "server requested unsupported cs in hrr")); |
| } |
| }; |
| |
| // HRR selects the ciphersuite. |
| sess.common.set_suite(cs); |
| |
| // This is the draft19 change where the transcript became a tree |
| self.0.handshake.transcript.start_hash(cs.get_hash()); |
| self.0.handshake.transcript.rollup_for_hrr(); |
| self.0.handshake.transcript.add_message(&m); |
| |
| Ok(emit_client_hello_for_retry(sess, |
| self.0.handshake, |
| self.0.hello, |
| Some(hrr))) |
| } |
| } |
| |
| impl State for ExpectServerHelloOrHelloRetryRequest { |
| fn check_message(&self, m: &Message) -> CheckResult { |
| check_handshake_message(m, |
| &[HandshakeType::ServerHello, |
| HandshakeType::HelloRetryRequest]) |
| } |
| |
| fn handle(self: Box<Self>, sess: &mut ClientSessionImpl, m: Message) -> NextStateOrError { |
| if m.is_handshake_type(HandshakeType::ServerHello) { |
| self.into_expect_server_hello().handle(sess, m) |
| } else { |
| self.handle_hello_retry_request(sess, m) |
| } |
| } |
| } |
| |
| 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(()) |
| } |
| |
| struct ExpectTLS13EncryptedExtensions { |
| handshake: HandshakeDetails, |
| server_cert: ServerCertDetails, |
| hello: ClientHelloDetails, |
| } |
| |
| impl ExpectTLS13EncryptedExtensions { |
| fn into_expect_tls13_finished_resume(self, |
| certv: verify::ServerCertVerified, |
| sigv: verify::HandshakeSignatureValid) -> NextState { |
| Box::new(ExpectTLS13Finished { |
| handshake: self.handshake, |
| client_auth: None, |
| cert_verified: certv, |
| sig_verified: sigv, |
| }) |
| } |
| |
| fn into_expect_tls13_certificate_or_certreq(self) -> NextState { |
| Box::new(ExpectTLS13CertificateOrCertReq { |
| handshake: self.handshake, |
| server_cert: self.server_cert, |
| }) |
| } |
| } |
| |
| impl State for ExpectTLS13EncryptedExtensions { |
| fn check_message(&self, m: &Message) -> Result<(), TLSError> { |
| check_handshake_message(m, &[HandshakeType::EncryptedExtensions]) |
| } |
| |
| fn handle(mut self: Box<Self>, sess: &mut ClientSessionImpl, m: Message) -> NextStateOrError { |
| let exts = extract_handshake!(m, HandshakePayload::EncryptedExtensions).unwrap(); |
| debug!("TLS1.3 encrypted extensions: {:?}", exts); |
| self.handshake.transcript.add_message(&m); |
| |
| validate_encrypted_extensions(sess, &self.hello, exts)?; |
| process_alpn_protocol(sess, exts.get_alpn_protocol())?; |
| |
| // QUIC transport parameters |
| if let Some(params) = exts.get_quic_params_extension() { |
| sess.quic_params = Some(params); |
| } |
| |
| if self.handshake.resuming_session.is_some() { |
| let certv = verify::ServerCertVerified::assertion(); |
| let sigv = verify::HandshakeSignatureValid::assertion(); |
| Ok(self.into_expect_tls13_finished_resume(certv, sigv)) |
| } else { |
| Ok(self.into_expect_tls13_certificate_or_certreq()) |
| } |
| } |
| } |
| |
| |
| fn sct_list_is_invalid(scts: &SCTList) -> bool { |
| scts.is_empty() || |
| scts.iter().any(|sct| sct.0.is_empty()) |
| } |
| |
| struct ExpectTLS13Certificate { |
| handshake: HandshakeDetails, |
| server_cert: ServerCertDetails, |
| client_auth: Option<ClientAuthDetails>, |
| } |
| |
| impl ExpectTLS13Certificate { |
| fn into_expect_tls13_certificate_verify(self) -> NextState { |
| Box::new(ExpectTLS13CertificateVerify { |
| handshake: self.handshake, |
| server_cert: self.server_cert, |
| client_auth: self.client_auth, |
| }) |
| } |
| } |
| |
| impl State for ExpectTLS13Certificate { |
| 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) -> NextStateOrError { |
| let cert_chain = extract_handshake!(m, HandshakePayload::CertificateTLS13).unwrap(); |
| self.handshake.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 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_tls13_certificate_verify()) |
| } |
| } |
| |
| struct ExpectTLS12Certificate { |
| handshake: HandshakeDetails, |
| server_cert: ServerCertDetails, |
| may_send_cert_status: bool, |
| must_issue_new_ticket: bool, |
| } |
| |
| impl ExpectTLS12Certificate { |
| fn into_expect_tls12_certificate_status_or_server_kx(self) -> NextState { |
| Box::new(ExpectTLS12CertificateStatusOrServerKX { |
| handshake: self.handshake, |
| server_cert: self.server_cert, |
| must_issue_new_ticket: self.must_issue_new_ticket, |
| }) |
| } |
| |
| fn into_expect_tls12_server_kx(self) -> NextState { |
| Box::new(ExpectTLS12ServerKX { |
| handshake: self.handshake, |
| server_cert: self.server_cert, |
| must_issue_new_ticket: self.must_issue_new_ticket, |
| }) |
| } |
| } |
| |
| impl State for ExpectTLS12Certificate { |
| 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) -> NextStateOrError { |
| let cert_chain = extract_handshake!(m, HandshakePayload::Certificate).unwrap(); |
| self.handshake.transcript.add_message(&m); |
| |
| self.server_cert.cert_chain = cert_chain.clone(); |
| |
| if self.may_send_cert_status { |
| Ok(self.into_expect_tls12_certificate_status_or_server_kx()) |
| } else { |
| Ok(self.into_expect_tls12_server_kx()) |
| } |
| } |
| } |
| |
| struct ExpectTLS12CertificateStatus { |
| handshake: HandshakeDetails, |
| server_cert: ServerCertDetails, |
| must_issue_new_ticket: bool, |
| } |
| |
| impl ExpectTLS12CertificateStatus { |
| fn into_expect_tls12_server_kx(self) -> NextState { |
| Box::new(ExpectTLS12ServerKX { |
| handshake: self.handshake, |
| server_cert: self.server_cert, |
| must_issue_new_ticket: self.must_issue_new_ticket, |
| }) |
| } |
| } |
| |
| impl State for ExpectTLS12CertificateStatus { |
| 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) -> NextStateOrError { |
| self.handshake.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_tls12_server_kx()) |
| } |
| } |
| |
| struct ExpectTLS12CertificateStatusOrServerKX { |
| handshake: HandshakeDetails, |
| server_cert: ServerCertDetails, |
| must_issue_new_ticket: bool, |
| } |
| |
| impl ExpectTLS12CertificateStatusOrServerKX { |
| fn into_expect_tls12_server_kx(self) -> NextState { |
| Box::new(ExpectTLS12ServerKX { |
| handshake: self.handshake, |
| server_cert: self.server_cert, |
| must_issue_new_ticket: self.must_issue_new_ticket, |
| }) |
| } |
| |
| fn into_expect_tls12_certificate_status(self) -> NextState { |
| Box::new(ExpectTLS12CertificateStatus { |
| handshake: self.handshake, |
| server_cert: self.server_cert, |
| must_issue_new_ticket: self.must_issue_new_ticket, |
| }) |
| } |
| } |
| |
| impl State for ExpectTLS12CertificateStatusOrServerKX { |
| 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) -> NextStateOrError { |
| if m.is_handshake_type(HandshakeType::ServerKeyExchange) { |
| self.into_expect_tls12_server_kx().handle(sess, m) |
| } else { |
| self.into_expect_tls12_certificate_status().handle(sess, m) |
| } |
| } |
| } |
| |
| struct ExpectTLS13CertificateOrCertReq { |
| handshake: HandshakeDetails, |
| server_cert: ServerCertDetails, |
| } |
| |
| impl ExpectTLS13CertificateOrCertReq { |
| fn into_expect_tls13_certificate(self) -> NextState { |
| Box::new(ExpectTLS13Certificate { |
| handshake: self.handshake, |
| server_cert: self.server_cert, |
| client_auth: None, |
| }) |
| } |
| |
| fn into_expect_tls13_certificate_req(self) -> NextState { |
| Box::new(ExpectTLS13CertificateRequest { |
| handshake: self.handshake, |
| server_cert: self.server_cert, |
| }) |
| } |
| } |
| |
| impl State for ExpectTLS13CertificateOrCertReq { |
| 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) -> NextStateOrError { |
| if m.is_handshake_type(HandshakeType::Certificate) { |
| self.into_expect_tls13_certificate().handle(sess, m) |
| } else { |
| self.into_expect_tls13_certificate_req().handle(sess, m) |
| } |
| } |
| } |
| |
| struct ExpectTLS12ServerKX { |
| handshake: HandshakeDetails, |
| server_cert: ServerCertDetails, |
| must_issue_new_ticket: bool, |
| } |
| |
| impl ExpectTLS12ServerKX { |
| fn into_expect_tls12_server_done_or_certreq(self, skx: ServerKXDetails) -> NextState { |
| Box::new(ExpectTLS12ServerDoneOrCertReq { |
| handshake: self.handshake, |
| server_cert: self.server_cert, |
| server_kx: skx, |
| must_issue_new_ticket: self.must_issue_new_ticket, |
| }) |
| } |
| } |
| |
| impl State for ExpectTLS12ServerKX { |
| fn check_message(&self, m: &Message) -> Result<(), TLSError> { |
| check_handshake_message(m, &[HandshakeType::ServerKeyExchange]) |
| } |
| |
| fn handle(mut self: Box<Self>, sess: &mut ClientSessionImpl, m: Message) -> 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); |
| self.handshake.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()); |
| |
| if let ServerKeyExchangePayload::ECDHE(ecdhe) = decoded_kx { |
| debug!("ECDHE curve is {:?}", ecdhe.params.curve_params); |
| } |
| |
| Ok(self.into_expect_tls12_server_done_or_certreq(skx)) |
| } |
| } |
| |
| // --- TLS1.3 CertificateVerify --- |
| struct ExpectTLS13CertificateVerify { |
| handshake: HandshakeDetails, |
| server_cert: ServerCertDetails, |
| client_auth: Option<ClientAuthDetails>, |
| } |
| |
| impl ExpectTLS13CertificateVerify { |
| fn into_expect_tls13_finished(self, |
| certv: verify::ServerCertVerified, |
| sigv: verify::HandshakeSignatureValid) -> NextState { |
| Box::new(ExpectTLS13Finished { |
| 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 State for ExpectTLS13CertificateVerify { |
| 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) -> 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 = self.handshake.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(); |
| self.handshake.transcript.add_message(&m); |
| |
| Ok(self.into_expect_tls13_finished(certv, sigv)) |
| } |
| } |
| |
| fn emit_certificate(handshake: &mut HandshakeDetails, |
| 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)), |
| }), |
| }; |
| |
| handshake.transcript.add_message(&cert); |
| sess.common.send_msg(cert, false); |
| } |
| |
| fn emit_clientkx(handshake: &mut HandshakeDetails, |
| sess: &mut ClientSessionImpl, |
| kxd: &suites::KeyExchangeResult) { |
| let mut buf = Vec::new(); |
| let ecpoint = PayloadU8::new(kxd.pubkey.clone()); |
| 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), |
| }), |
| }; |
| |
| handshake.transcript.add_message(&ckx); |
| sess.common.send_msg(ckx, false); |
| } |
| |
| fn emit_certverify(handshake: &mut HandshakeDetails, |
| client_auth: &mut ClientAuthDetails, |
| sess: &mut ClientSessionImpl) -> Result<(), TLSError> { |
| if client_auth.signer.is_none() { |
| trace!("Not sending CertificateVerify, no key"); |
| handshake.transcript.abandon_client_auth(); |
| return Ok(()); |
| } |
| |
| let message = handshake.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), |
| }), |
| }; |
| |
| handshake.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(handshake: &mut HandshakeDetails, |
| sess: &mut ClientSessionImpl) { |
| let vh = handshake.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), |
| }), |
| }; |
| |
| handshake.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 ExpectTLS12CertificateRequest { |
| handshake: HandshakeDetails, |
| server_cert: ServerCertDetails, |
| server_kx: ServerKXDetails, |
| must_issue_new_ticket: bool, |
| } |
| |
| impl ExpectTLS12CertificateRequest { |
| fn into_expect_tls12_server_done(self, client_auth: ClientAuthDetails) -> NextState { |
| Box::new(ExpectTLS12ServerDone { |
| 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 State for ExpectTLS12CertificateRequest { |
| fn check_message(&self, m: &Message) -> Result<(), TLSError> { |
| check_handshake_message(m, &[HandshakeType::CertificateRequest]) |
| } |
| |
| fn handle(mut self: Box<Self>, sess: &mut ClientSessionImpl, m: Message) -> NextStateOrError { |
| let certreq = extract_handshake!(m, HandshakePayload::CertificateRequest).unwrap(); |
| self.handshake.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_tls12_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_tls12_server_done(client_auth)) |
| } |
| } |
| |
| // TLS1.3 version of the above. We then move to expecting the server Certificate. |
| // Unfortunately the CertificateRequest type changed in an annoying way in TLS1.3. |
| struct ExpectTLS13CertificateRequest { |
| handshake: HandshakeDetails, |
| server_cert: ServerCertDetails, |
| } |
| |
| impl ExpectTLS13CertificateRequest { |
| fn into_expect_tls13_certificate(self, client_auth: ClientAuthDetails) -> NextState { |
| Box::new(ExpectTLS13Certificate { |
| handshake: self.handshake, |
| server_cert: self.server_cert, |
| client_auth: Some(client_auth), |
| }) |
| } |
| } |
| |
| impl State for ExpectTLS13CertificateRequest { |
| fn check_message(&self, m: &Message) -> Result<(), TLSError> { |
| check_handshake_message(m, &[HandshakeType::CertificateRequest]) |
| } |
| |
| fn handle(mut self: Box<Self>, sess: &mut ClientSessionImpl, m: Message) -> NextStateOrError { |
| let certreq = &extract_handshake!(m, HandshakePayload::CertificateRequestTLS13).unwrap(); |
| self.handshake.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 = SupportedSignatureSchemes::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::DecodeError); |
| 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_tls13_certificate(client_auth)) |
| } |
| } |
| |
| struct ExpectTLS12ServerDoneOrCertReq { |
| handshake: HandshakeDetails, |
| server_cert: ServerCertDetails, |
| server_kx: ServerKXDetails, |
| must_issue_new_ticket: bool, |
| } |
| |
| impl ExpectTLS12ServerDoneOrCertReq { |
| fn into_expect_tls12_certificate_req(self) -> NextState { |
| Box::new(ExpectTLS12CertificateRequest { |
| 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_tls12_server_done(self) -> NextState { |
| Box::new(ExpectTLS12ServerDone { |
| 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 State for ExpectTLS12ServerDoneOrCertReq { |
| fn check_message(&self, m: &Message) -> Result<(), TLSError> { |
| check_handshake_message(m, |
| &[HandshakeType::CertificateRequest, |
| HandshakeType::ServerHelloDone]) |
| } |
| |
| fn handle(mut self: Box<Self>, sess: &mut ClientSessionImpl, m: Message) -> NextStateOrError { |
| if extract_handshake!(m, HandshakePayload::CertificateRequest).is_some() { |
| self.into_expect_tls12_certificate_req().handle(sess, m) |
| } else { |
| self.handshake.transcript.abandon_client_auth(); |
| self.into_expect_tls12_server_done().handle(sess, m) |
| } |
| } |
| } |
| |
| |
| struct ExpectTLS12ServerDone { |
| handshake: HandshakeDetails, |
| server_cert: ServerCertDetails, |
| server_kx: ServerKXDetails, |
| client_auth: Option<ClientAuthDetails>, |
| must_issue_new_ticket: bool, |
| } |
| |
| impl ExpectTLS12ServerDone { |
| fn into_expect_tls12_new_ticket(self, |
| certv: verify::ServerCertVerified, |
| sigv: verify::HandshakeSignatureValid) -> NextState { |
| Box::new(ExpectTLS12NewTicket { |
| handshake: self.handshake, |
| resuming: false, |
| cert_verified: certv, |
| sig_verified: sigv, |
| }) |
| } |
| |
| fn into_expect_tls12_ccs(self, |
| certv: verify::ServerCertVerified, |
| sigv: verify::HandshakeSignatureValid) -> NextState { |
| Box::new(ExpectTLS12CCS { |
| handshake: self.handshake, |
| ticket: ReceivedTicketDetails::new(), |
| resuming: false, |
| cert_verified: certv, |
| sig_verified: sigv, |
| }) |
| } |
| } |
| |
| impl State for ExpectTLS12ServerDone { |
| fn check_message(&self, m: &Message) -> Result<(), TLSError> { |
| check_handshake_message(m, &[HandshakeType::ServerHelloDone]) |
| } |
| |
| fn handle(self: Box<Self>, sess: &mut ClientSessionImpl, m: Message) -> NextStateOrError { |
| let mut st = *self; |
| st.handshake.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| 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| send_cert_error_alert(sess, err))? |
| }; |
| sess.server_cert_chain = st.server_cert.take_chain(); |
| |
| // 4. |
| if st.client_auth.is_some() { |
| emit_certificate(&mut st.handshake, |
| 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(&mut st.handshake, sess, &kxd); |
| // nb. EMS handshake hash only runs up to ClientKeyExchange. |
| let handshake_hash = st.handshake.transcript.get_current_hash(); |
| |
| // 5c. |
| if st.client_auth.is_some() { |
| emit_certverify(&mut st.handshake, |
| 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("CLIENT_RANDOM", |
| // &secrets.randoms.client, |
| // &secrets.master_secret); |
| sess.common.start_encryption_tls12(secrets); |
| |
| // 6. |
| emit_finished(&mut st.handshake, sess); |
| |
| if st.must_issue_new_ticket { |
| Ok(st.into_expect_tls12_new_ticket(certv, sigv)) |
| } else { |
| Ok(st.into_expect_tls12_ccs(certv, sigv)) |
| } |
| } |
| } |
| |
| // -- Waiting for their CCS -- |
| struct ExpectTLS12CCS { |
| handshake: HandshakeDetails, |
| ticket: ReceivedTicketDetails, |
| resuming: bool, |
| cert_verified: verify::ServerCertVerified, |
| sig_verified: verify::HandshakeSignatureValid, |
| } |
| |
| impl ExpectTLS12CCS { |
| fn into_expect_tls12_finished(self) -> NextState { |
| Box::new(ExpectTLS12Finished { |
| handshake: self.handshake, |
| ticket: self.ticket, |
| resuming: self.resuming, |
| cert_verified: self.cert_verified, |
| sig_verified: self.sig_verified, |
| }) |
| } |
| } |
| |
| impl State for ExpectTLS12CCS { |
| fn check_message(&self, m: &Message) -> Result<(), TLSError> { |
| check_message(m, &[ContentType::ChangeCipherSpec], &[]) |
| } |
| |
| fn handle(self: Box<Self>, sess: &mut ClientSessionImpl, _m: Message) -> 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_tls12_finished()) |
| } |
| } |
| |
| struct ExpectTLS12NewTicket { |
| handshake: HandshakeDetails, |
| resuming: bool, |
| cert_verified: verify::ServerCertVerified, |
| sig_verified: verify::HandshakeSignatureValid, |
| } |
| |
| impl ExpectTLS12NewTicket { |
| fn into_expect_tls12_ccs(self, ticket: ReceivedTicketDetails) -> NextState { |
| Box::new(ExpectTLS12CCS { |
| handshake: self.handshake, |
| ticket, |
| resuming: self.resuming, |
| cert_verified: self.cert_verified, |
| sig_verified: self.sig_verified, |
| }) |
| } |
| } |
| |
| impl State for ExpectTLS12NewTicket { |
| fn check_message(&self, m: &Message) -> Result<(), TLSError> { |
| check_handshake_message(m, &[HandshakeType::NewSessionTicket]) |
| } |
| |
| fn handle(mut self: Box<Self>, _sess: &mut ClientSessionImpl, m: Message) -> NextStateOrError { |
| self.handshake.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_tls12_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"); |
| } |
| } |
| |
| fn emit_certificate_tls13(handshake: &mut HandshakeDetails, |
| 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), |
| }), |
| }; |
| handshake.transcript.add_message(&m); |
| sess.common.send_msg(m, true); |
| } |
| |
| fn emit_certverify_tls13(handshake: &mut HandshakeDetails, |
| 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(&handshake.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), |
| }), |
| }; |
| |
| handshake.transcript.add_message(&m); |
| sess.common.send_msg(m, true); |
| Ok(()) |
| } |
| |
| fn emit_finished_tls13(handshake: &mut HandshakeDetails, |
| sess: &mut ClientSessionImpl) { |
| let handshake_hash = handshake.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), |
| }), |
| }; |
| |
| handshake.transcript.add_message(&m); |
| sess.common.send_msg(m, true); |
| } |
| |
| struct ExpectTLS13Finished { |
| handshake: HandshakeDetails, |
| client_auth: Option<ClientAuthDetails>, |
| cert_verified: verify::ServerCertVerified, |
| sig_verified: verify::HandshakeSignatureValid, |
| } |
| |
| impl ExpectTLS13Finished { |
| fn into_expect_tls13_traffic(self, |
| fin: verify::FinishedMessageVerified) -> NextState { |
| Box::new(ExpectTLS13Traffic { |
| handshake: self.handshake, |
| _cert_verified: self.cert_verified, |
| _sig_verified: self.sig_verified, |
| _fin_verified: fin, |
| }) |
| } |
| } |
| |
| impl State for ExpectTLS13Finished { |
| fn check_message(&self, m: &Message) -> CheckResult { |
| check_handshake_message(m, &[HandshakeType::Finished]) |
| } |
| |
| fn handle(self: Box<Self>, sess: &mut ClientSessionImpl, m: Message) -> NextStateOrError { |
| let mut st = *self; |
| let finished = extract_handshake!(m, HandshakePayload::Finished).unwrap(); |
| |
| let handshake_hash = st.handshake.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())?; |
| |
| st.handshake.transcript.add_message(&m); |
| |
| /* Transition to application data */ |
| sess.common.get_mut_key_schedule().input_empty(); |
| |
| /* Traffic from server is now encrypted with application data keys. */ |
| let handshake_hash = st.handshake.transcript.get_current_hash(); |
| let read_key = sess.common |
| .get_key_schedule() |
| .derive(SecretKind::ServerApplicationTrafficSecret, &handshake_hash); |
| // sess.config.key_log.log("SERVER_TRAFFIC_SECRET_0", |
| // &st.handshake.randoms.client, |
| // &read_key); |
| let suite = sess.common.get_suite_assert(); |
| 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("EXPORTER_SECRET", &st.handshake.randoms.client, &exporter_secret); |
| sess.common |
| .get_mut_key_schedule() |
| .current_exporter_secret = exporter_secret; |
| |
| /* Send our authentication/finished messages. These are still encrypted |
| * with our handshake keys. */ |
| if st.client_auth.is_some() { |
| emit_certificate_tls13(&mut st.handshake, |
| st.client_auth.as_mut().unwrap(), |
| sess); |
| emit_certverify_tls13(&mut st.handshake, |
| st.client_auth.as_mut().unwrap(), |
| sess)?; |
| } |
| |
| emit_finished_tls13(&mut st.handshake, |
| sess); |
| |
| /* Now move to our application traffic keys. */ |
| check_aligned_handshake(sess)?; |
| let write_key = sess.common |
| .get_key_schedule() |
| .derive(SecretKind::ClientApplicationTrafficSecret, &handshake_hash); |
| // sess.config.key_log.log("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(); |
| Ok(st.into_expect_tls13_traffic(fin)) |
| } |
| } |
| |
| struct ExpectTLS12Finished { |
| handshake: HandshakeDetails, |
| ticket: ReceivedTicketDetails, |
| resuming: bool, |
| cert_verified: verify::ServerCertVerified, |
| sig_verified: verify::HandshakeSignatureValid, |
| } |
| |
| impl ExpectTLS12Finished { |
| fn into_expect_tls12_traffic(self, |
| fin: verify::FinishedMessageVerified) -> NextState { |
| Box::new(ExpectTLS12Traffic { |
| _cert_verified: self.cert_verified, |
| _sig_verified: self.sig_verified, |
| _fin_verified: fin, |
| }) |
| } |
| } |
| |
| impl State for ExpectTLS12Finished { |
| fn check_message(&self, m: &Message) -> Result<(), TLSError> { |
| check_handshake_message(m, &[HandshakeType::Finished]) |
| } |
| |
| fn handle(self: Box<Self>, sess: &mut ClientSessionImpl, m: Message) -> NextStateOrError { |
| let mut st = *self; |
| let finished = extract_handshake!(m, HandshakePayload::Finished).unwrap(); |
| |
| // Work out what verify_data we expect. |
| let vh = st.handshake.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. |
| st.handshake.transcript.add_message(&m); |
| |
| save_session(&mut st.handshake, |
| &mut st.ticket, |
| sess); |
| |
| if st.resuming { |
| emit_ccs(sess); |
| emit_finished(&mut st.handshake, sess); |
| } |
| |
| sess.common.we_now_encrypting(); |
| sess.common.start_traffic(); |
| Ok(st.into_expect_tls12_traffic(fin)) |
| } |
| } |
| |
| // -- Traffic transit state -- |
| struct ExpectTLS12Traffic { |
| _cert_verified: verify::ServerCertVerified, |
| _sig_verified: verify::HandshakeSignatureValid, |
| _fin_verified: verify::FinishedMessageVerified, |
| } |
| |
| impl State for ExpectTLS12Traffic { |
| fn check_message(&self, m: &Message) -> Result<(), TLSError> { |
| check_message(m, &[ContentType::ApplicationData], &[]) |
| } |
| |
| fn handle(self: Box<Self>, sess: &mut ClientSessionImpl, mut m: Message) -> NextStateOrError { |
| sess.common.take_received_plaintext(m.take_opaque_payload().unwrap()); |
| Ok(self) |
| } |
| } |
| |
| // -- Traffic transit state (TLS1.3) -- |
| // In this state we can be sent tickets, keyupdates, |
| // and application data. |
| struct ExpectTLS13Traffic { |
| handshake: HandshakeDetails, |
| _cert_verified: verify::ServerCertVerified, |
| _sig_verified: verify::HandshakeSignatureValid, |
| _fin_verified: verify::FinishedMessageVerified, |
| } |
| |
| impl ExpectTLS13Traffic { |
| 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 = self.handshake.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); |
| |
| let key = persist::ClientSessionKey::session_for_dns_name(self.handshake.dns_name.as_ref()); |
| |
| let worked = sess.config.session_persistence.put(key.get_encoding(), |
| value.get_encoding()); |
| |
| 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 State for ExpectTLS13Traffic { |
| 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) -> 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) |
| } |
| } |