use crate::check::inappropriate_message;
use crate::common_state::{CommonState, Side, State};
use crate::conn::ConnectionRandoms;
use crate::crypto::ActiveKeyExchange;
use crate::enums::ProtocolVersion;
use crate::enums::{AlertDescription, ContentType, HandshakeType};
use crate::error::{Error, PeerIncompatible, PeerMisbehaved};
use crate::hash_hs::HandshakeHash;
#[cfg(feature = "logging")]
use crate::log::{debug, trace};
use crate::msgs::base::Payload;
use crate::msgs::ccs::ChangeCipherSpecPayload;
use crate::msgs::codec::Codec;
use crate::msgs::handshake::{
    CertificateChain, ClientEcdhParams, HandshakeMessagePayload, HandshakePayload,
};
use crate::msgs::handshake::{NewSessionTicketPayload, SessionId};
use crate::msgs::message::{Message, MessagePayload};
use crate::msgs::persist;
use crate::suites::PartiallyExtractedSecrets;
use crate::tls12::{self, ConnectionSecrets, Tls12CipherSuite};
use crate::verify;

use super::common::ActiveCertifiedKey;
use super::hs::{self, ServerContext};
use super::server_conn::{ProducesTickets, ServerConfig, ServerConnectionData};

use pki_types::UnixTime;
use subtle::ConstantTimeEq;

use alloc::borrow::ToOwned;
use alloc::boxed::Box;
use alloc::string::ToString;
use alloc::sync::Arc;
use alloc::vec;
use alloc::vec::Vec;

pub(super) use client_hello::CompleteClientHelloHandling;

mod client_hello {
    use pki_types::CertificateDer;

    use crate::crypto::SupportedKxGroup;
    use crate::enums::SignatureScheme;
    use crate::msgs::enums::ECPointFormat;
    use crate::msgs::enums::{ClientCertificateType, Compression};
    use crate::msgs::handshake::{CertificateChain, ServerEcdhParams};
    use crate::msgs::handshake::{CertificateRequestPayload, ClientSessionTicket, Random};
    use crate::msgs::handshake::{CertificateStatus, EcdheServerKeyExchange};
    use crate::msgs::handshake::{ClientExtension, SessionId};
    use crate::msgs::handshake::{ClientHelloPayload, ServerHelloPayload};
    use crate::msgs::handshake::{ServerExtension, ServerKeyExchangePayload};
    use crate::sign;
    use crate::verify::DigitallySignedStruct;

    use super::*;

    pub(in crate::server) struct CompleteClientHelloHandling {
        pub(in crate::server) config: Arc<ServerConfig>,
        pub(in crate::server) transcript: HandshakeHash,
        pub(in crate::server) session_id: SessionId,
        pub(in crate::server) suite: &'static Tls12CipherSuite,
        pub(in crate::server) using_ems: bool,
        pub(in crate::server) randoms: ConnectionRandoms,
        pub(in crate::server) send_ticket: bool,
        pub(in crate::server) extra_exts: Vec<ServerExtension>,
    }

    impl CompleteClientHelloHandling {
        pub(in crate::server) fn handle_client_hello(
            mut self,
            cx: &mut ServerContext<'_>,
            server_key: ActiveCertifiedKey,
            chm: &Message,
            client_hello: &ClientHelloPayload,
            sigschemes_ext: Vec<SignatureScheme>,
            tls13_enabled: bool,
        ) -> hs::NextStateOrError {
            // -- TLS1.2 only from hereon in --
            self.transcript.add_message(chm);

            if client_hello.ems_support_offered() {
                self.using_ems = true;
            }

            let groups_ext = client_hello
                .get_namedgroups_extension()
                .ok_or_else(|| {
                    cx.common.send_fatal_alert(
                        AlertDescription::HandshakeFailure,
                        PeerIncompatible::NamedGroupsExtensionRequired,
                    )
                })?;
            let ecpoints_ext = client_hello
                .get_ecpoints_extension()
                .ok_or_else(|| {
                    cx.common.send_fatal_alert(
                        AlertDescription::HandshakeFailure,
                        PeerIncompatible::EcPointsExtensionRequired,
                    )
                })?;

            trace!("namedgroups {:?}", groups_ext);
            trace!("ecpoints {:?}", ecpoints_ext);

            if !ecpoints_ext.contains(&ECPointFormat::Uncompressed) {
                return Err(cx.common.send_fatal_alert(
                    AlertDescription::IllegalParameter,
                    PeerIncompatible::UncompressedEcPointsRequired,
                ));
            }

            // -- If TLS1.3 is enabled, signal the downgrade in the server random
            if tls13_enabled {
                self.randoms.server[24..].copy_from_slice(&tls12::DOWNGRADE_SENTINEL);
            }

            // -- Check for resumption --
            // We can do this either by (in order of preference):
            // 1. receiving a ticket that decrypts
            // 2. receiving a sessionid that is in our cache
            //
            // If we receive a ticket, the sessionid won't be in our
            // cache, so don't check.
            //
            // If either works, we end up with a ServerConnectionValue
            // which is passed to start_resumption and concludes
            // our handling of the ClientHello.
            //
            let mut ticket_received = false;
            let resume_data = client_hello
                .get_ticket_extension()
                .and_then(|ticket_ext| match ticket_ext {
                    ClientExtension::SessionTicket(ClientSessionTicket::Offer(ticket)) => {
                        Some(ticket)
                    }
                    _ => None,
                })
                .and_then(|ticket| {
                    ticket_received = true;
                    debug!("Ticket received");
                    let data = self.config.ticketer.decrypt(&ticket.0);
                    if data.is_none() {
                        debug!("Ticket didn't decrypt");
                    }
                    data
                })
                .or_else(|| {
                    // Perhaps resume?  If we received a ticket, the sessionid
                    // does not correspond to a real session.
                    if client_hello.session_id.is_empty() || ticket_received {
                        return None;
                    }

                    self.config
                        .session_storage
                        .get(&client_hello.session_id.get_encoding())
                })
                .and_then(|x| persist::ServerSessionValue::read_bytes(&x).ok())
                .filter(|resumedata| {
                    hs::can_resume(self.suite.into(), &cx.data.sni, self.using_ems, resumedata)
                });

            if let Some(data) = resume_data {
                return self.start_resumption(cx, client_hello, &client_hello.session_id, data);
            }

            // Now we have chosen a ciphersuite, we can make kx decisions.
            let sigschemes = self
                .suite
                .resolve_sig_schemes(&sigschemes_ext);

            if sigschemes.is_empty() {
                return Err(cx.common.send_fatal_alert(
                    AlertDescription::HandshakeFailure,
                    PeerIncompatible::NoSignatureSchemesInCommon,
                ));
            }

            let group = self
                .config
                .provider
                .kx_groups
                .iter()
                .find(|skxg| groups_ext.contains(&skxg.name()))
                .cloned()
                .ok_or_else(|| {
                    cx.common.send_fatal_alert(
                        AlertDescription::HandshakeFailure,
                        PeerIncompatible::NoKxGroupsInCommon,
                    )
                })?;

            let ecpoint = ECPointFormat::SUPPORTED
                .iter()
                .find(|format| ecpoints_ext.contains(format))
                .cloned()
                .ok_or_else(|| {
                    cx.common.send_fatal_alert(
                        AlertDescription::HandshakeFailure,
                        PeerIncompatible::NoEcPointFormatsInCommon,
                    )
                })?;

            debug_assert_eq!(ecpoint, ECPointFormat::Uncompressed);

            let mut ocsp_response = server_key.get_ocsp();

            // If we're not offered a ticket or a potential session ID, allocate a session ID.
            if !self.config.session_storage.can_cache() {
                self.session_id = SessionId::empty();
            } else if self.session_id.is_empty() && !ticket_received {
                self.session_id = SessionId::random(self.config.provider.secure_random)?;
            }

            self.send_ticket = emit_server_hello(
                &self.config,
                &mut self.transcript,
                cx,
                self.session_id,
                self.suite,
                self.using_ems,
                &mut ocsp_response,
                client_hello,
                None,
                &self.randoms,
                self.extra_exts,
            )?;
            emit_certificate(&mut self.transcript, cx.common, server_key.get_cert());
            if let Some(ocsp_response) = ocsp_response {
                emit_cert_status(&mut self.transcript, cx.common, ocsp_response);
            }
            let server_kx = emit_server_kx(
                &mut self.transcript,
                cx.common,
                sigschemes,
                group,
                server_key.get_key(),
                &self.randoms,
            )?;
            let doing_client_auth = emit_certificate_req(&self.config, &mut self.transcript, cx)?;
            emit_server_hello_done(&mut self.transcript, cx.common);

            if doing_client_auth {
                Ok(Box::new(ExpectCertificate {
                    config: self.config,
                    transcript: self.transcript,
                    randoms: self.randoms,
                    session_id: self.session_id,
                    suite: self.suite,
                    using_ems: self.using_ems,
                    server_kx,
                    send_ticket: self.send_ticket,
                }))
            } else {
                Ok(Box::new(ExpectClientKx {
                    config: self.config,
                    transcript: self.transcript,
                    randoms: self.randoms,
                    session_id: self.session_id,
                    suite: self.suite,
                    using_ems: self.using_ems,
                    server_kx,
                    client_cert: None,
                    send_ticket: self.send_ticket,
                }))
            }
        }

        fn start_resumption(
            mut self,
            cx: &mut ServerContext<'_>,
            client_hello: &ClientHelloPayload,
            id: &SessionId,
            resumedata: persist::ServerSessionValue,
        ) -> hs::NextStateOrError {
            debug!("Resuming connection");

            if resumedata.extended_ms && !self.using_ems {
                return Err(cx.common.send_fatal_alert(
                    AlertDescription::IllegalParameter,
                    PeerMisbehaved::ResumptionAttemptedWithVariedEms,
                ));
            }

            self.session_id = *id;
            self.send_ticket = emit_server_hello(
                &self.config,
                &mut self.transcript,
                cx,
                self.session_id,
                self.suite,
                self.using_ems,
                &mut None,
                client_hello,
                Some(&resumedata),
                &self.randoms,
                self.extra_exts,
            )?;

            let secrets = ConnectionSecrets::new_resume(
                self.randoms,
                self.suite,
                &resumedata.master_secret.0,
            );
            self.config.key_log.log(
                "CLIENT_RANDOM",
                &secrets.randoms.client,
                &secrets.master_secret,
            );
            cx.common
                .start_encryption_tls12(&secrets, Side::Server);
            cx.common.peer_certificates = resumedata.client_cert_chain;

            if self.send_ticket {
                emit_ticket(
                    &secrets,
                    &mut self.transcript,
                    self.using_ems,
                    cx,
                    &*self.config.ticketer,
                )?;
            }
            emit_ccs(cx.common);
            cx.common
                .record_layer
                .start_encrypting();
            emit_finished(&secrets, &mut self.transcript, cx.common);

            Ok(Box::new(ExpectCcs {
                config: self.config,
                secrets,
                transcript: self.transcript,
                session_id: self.session_id,
                using_ems: self.using_ems,
                resuming: true,
                send_ticket: self.send_ticket,
            }))
        }
    }

    fn emit_server_hello(
        config: &ServerConfig,
        transcript: &mut HandshakeHash,
        cx: &mut ServerContext<'_>,
        session_id: SessionId,
        suite: &'static Tls12CipherSuite,
        using_ems: bool,
        ocsp_response: &mut Option<&[u8]>,
        hello: &ClientHelloPayload,
        resumedata: Option<&persist::ServerSessionValue>,
        randoms: &ConnectionRandoms,
        extra_exts: Vec<ServerExtension>,
    ) -> Result<bool, Error> {
        let mut ep = hs::ExtensionProcessing::new();
        ep.process_common(config, cx, ocsp_response, hello, resumedata, extra_exts)?;
        ep.process_tls12(config, hello, using_ems);

        let sh = Message {
            version: ProtocolVersion::TLSv1_2,
            payload: MessagePayload::handshake(HandshakeMessagePayload {
                typ: HandshakeType::ServerHello,
                payload: HandshakePayload::ServerHello(ServerHelloPayload {
                    legacy_version: ProtocolVersion::TLSv1_2,
                    random: Random::from(randoms.server),
                    session_id,
                    cipher_suite: suite.common.suite,
                    compression_method: Compression::Null,
                    extensions: ep.exts,
                }),
            }),
        };

        trace!("sending server hello {:?}", sh);
        transcript.add_message(&sh);
        cx.common.send_msg(sh, false);
        Ok(ep.send_ticket)
    }

    fn emit_certificate(
        transcript: &mut HandshakeHash,
        common: &mut CommonState,
        cert_chain: &[CertificateDer<'static>],
    ) {
        let c = Message {
            version: ProtocolVersion::TLSv1_2,
            payload: MessagePayload::handshake(HandshakeMessagePayload {
                typ: HandshakeType::Certificate,
                payload: HandshakePayload::Certificate(CertificateChain(cert_chain.to_vec())),
            }),
        };

        transcript.add_message(&c);
        common.send_msg(c, false);
    }

    fn emit_cert_status(transcript: &mut HandshakeHash, common: &mut CommonState, ocsp: &[u8]) {
        let st = CertificateStatus::new(ocsp.to_owned());

        let c = Message {
            version: ProtocolVersion::TLSv1_2,
            payload: MessagePayload::handshake(HandshakeMessagePayload {
                typ: HandshakeType::CertificateStatus,
                payload: HandshakePayload::CertificateStatus(st),
            }),
        };

        transcript.add_message(&c);
        common.send_msg(c, false);
    }

    fn emit_server_kx(
        transcript: &mut HandshakeHash,
        common: &mut CommonState,
        sigschemes: Vec<SignatureScheme>,
        selected_group: &'static dyn SupportedKxGroup,
        signing_key: &dyn sign::SigningKey,
        randoms: &ConnectionRandoms,
    ) -> Result<Box<dyn ActiveKeyExchange>, Error> {
        let kx = selected_group
            .start()
            .map_err(|_| Error::FailedToGetRandomBytes)?;
        let secdh = ServerEcdhParams::new(&*kx);

        let mut msg = Vec::new();
        msg.extend(randoms.client);
        msg.extend(randoms.server);
        secdh.encode(&mut msg);

        let signer = signing_key
            .choose_scheme(&sigschemes)
            .ok_or_else(|| Error::General("incompatible signing key".to_string()))?;
        let sigscheme = signer.scheme();
        let sig = signer.sign(&msg)?;

        let skx = ServerKeyExchangePayload::Ecdhe(EcdheServerKeyExchange {
            params: secdh,
            dss: DigitallySignedStruct::new(sigscheme, sig),
        });

        let m = Message {
            version: ProtocolVersion::TLSv1_2,
            payload: MessagePayload::handshake(HandshakeMessagePayload {
                typ: HandshakeType::ServerKeyExchange,
                payload: HandshakePayload::ServerKeyExchange(skx),
            }),
        };

        transcript.add_message(&m);
        common.send_msg(m, false);
        Ok(kx)
    }

    fn emit_certificate_req(
        config: &ServerConfig,
        transcript: &mut HandshakeHash,
        cx: &mut ServerContext<'_>,
    ) -> Result<bool, Error> {
        let client_auth = &config.verifier;

        if !client_auth.offer_client_auth() {
            return Ok(false);
        }

        let verify_schemes = client_auth.supported_verify_schemes();

        let names = config
            .verifier
            .root_hint_subjects()
            .to_vec();

        let cr = CertificateRequestPayload {
            certtypes: vec![
                ClientCertificateType::RSASign,
                ClientCertificateType::ECDSASign,
            ],
            sigschemes: verify_schemes,
            canames: names,
        };

        let m = Message {
            version: ProtocolVersion::TLSv1_2,
            payload: MessagePayload::handshake(HandshakeMessagePayload {
                typ: HandshakeType::CertificateRequest,
                payload: HandshakePayload::CertificateRequest(cr),
            }),
        };

        trace!("Sending CertificateRequest {:?}", m);
        transcript.add_message(&m);
        cx.common.send_msg(m, false);
        Ok(true)
    }

    fn emit_server_hello_done(transcript: &mut HandshakeHash, common: &mut CommonState) {
        let m = Message {
            version: ProtocolVersion::TLSv1_2,
            payload: MessagePayload::handshake(HandshakeMessagePayload {
                typ: HandshakeType::ServerHelloDone,
                payload: HandshakePayload::ServerHelloDone,
            }),
        };

        transcript.add_message(&m);
        common.send_msg(m, false);
    }
}

// --- Process client's Certificate for client auth ---
struct ExpectCertificate {
    config: Arc<ServerConfig>,
    transcript: HandshakeHash,
    randoms: ConnectionRandoms,
    session_id: SessionId,
    suite: &'static Tls12CipherSuite,
    using_ems: bool,
    server_kx: Box<dyn ActiveKeyExchange>,
    send_ticket: bool,
}

impl State<ServerConnectionData> for ExpectCertificate {
    fn handle(mut self: Box<Self>, cx: &mut ServerContext<'_>, m: Message) -> hs::NextStateOrError {
        self.transcript.add_message(&m);
        let cert_chain = require_handshake_msg_move!(
            m,
            HandshakeType::Certificate,
            HandshakePayload::Certificate
        )?;

        // If we can't determine if the auth is mandatory, abort
        let mandatory = self
            .config
            .verifier
            .client_auth_mandatory();

        trace!("certs {:?}", cert_chain);

        let client_cert = match cert_chain.split_first() {
            None if mandatory => {
                return Err(cx.common.send_fatal_alert(
                    AlertDescription::CertificateRequired,
                    Error::NoCertificatesPresented,
                ));
            }
            None => {
                debug!("client auth requested but no certificate supplied");
                self.transcript.abandon_client_auth();
                None
            }
            Some((end_entity, intermediates)) => {
                self.config
                    .verifier
                    .verify_client_cert(end_entity, intermediates, UnixTime::now())
                    .map_err(|err| {
                        cx.common
                            .send_cert_verify_error_alert(err)
                    })?;

                Some(cert_chain)
            }
        };

        Ok(Box::new(ExpectClientKx {
            config: self.config,
            transcript: self.transcript,
            randoms: self.randoms,
            session_id: self.session_id,
            suite: self.suite,
            using_ems: self.using_ems,
            server_kx: self.server_kx,
            client_cert,
            send_ticket: self.send_ticket,
        }))
    }
}

// --- Process client's KeyExchange ---
struct ExpectClientKx {
    config: Arc<ServerConfig>,
    transcript: HandshakeHash,
    randoms: ConnectionRandoms,
    session_id: SessionId,
    suite: &'static Tls12CipherSuite,
    using_ems: bool,
    server_kx: Box<dyn ActiveKeyExchange>,
    client_cert: Option<CertificateChain>,
    send_ticket: bool,
}

impl State<ServerConnectionData> for ExpectClientKx {
    fn handle(mut self: Box<Self>, cx: &mut ServerContext<'_>, m: Message) -> hs::NextStateOrError {
        let client_kx = require_handshake_msg!(
            m,
            HandshakeType::ClientKeyExchange,
            HandshakePayload::ClientKeyExchange
        )?;
        self.transcript.add_message(&m);
        let ems_seed = self
            .using_ems
            .then(|| self.transcript.get_current_hash());

        // Complete key agreement, and set up encryption with the
        // resulting premaster secret.
        let peer_kx_params =
            tls12::decode_ecdh_params::<ClientEcdhParams>(cx.common, &client_kx.0)?;
        let secrets = ConnectionSecrets::from_key_exchange(
            self.server_kx,
            &peer_kx_params.public.0,
            ems_seed,
            self.randoms,
            self.suite,
        )?;

        self.config.key_log.log(
            "CLIENT_RANDOM",
            &secrets.randoms.client,
            &secrets.master_secret,
        );
        cx.common
            .start_encryption_tls12(&secrets, Side::Server);

        if let Some(client_cert) = self.client_cert {
            Ok(Box::new(ExpectCertificateVerify {
                config: self.config,
                secrets,
                transcript: self.transcript,
                session_id: self.session_id,
                using_ems: self.using_ems,
                client_cert,
                send_ticket: self.send_ticket,
            }))
        } else {
            Ok(Box::new(ExpectCcs {
                config: self.config,
                secrets,
                transcript: self.transcript,
                session_id: self.session_id,
                using_ems: self.using_ems,
                resuming: false,
                send_ticket: self.send_ticket,
            }))
        }
    }
}

// --- Process client's certificate proof ---
struct ExpectCertificateVerify {
    config: Arc<ServerConfig>,
    secrets: ConnectionSecrets,
    transcript: HandshakeHash,
    session_id: SessionId,
    using_ems: bool,
    client_cert: CertificateChain,
    send_ticket: bool,
}

impl State<ServerConnectionData> for ExpectCertificateVerify {
    fn handle(mut self: Box<Self>, cx: &mut ServerContext<'_>, m: Message) -> hs::NextStateOrError {
        let rc = {
            let sig = require_handshake_msg!(
                m,
                HandshakeType::CertificateVerify,
                HandshakePayload::CertificateVerify
            )?;

            match self.transcript.take_handshake_buf() {
                Some(msgs) => {
                    let certs = &self.client_cert;
                    self.config
                        .verifier
                        .verify_tls12_signature(&msgs, &certs[0], sig)
                }
                None => {
                    // This should be unreachable; the handshake buffer was initialized with
                    // client authentication if the verifier wants to offer it.
                    // `transcript.abandon_client_auth()` can extract it, but its only caller in
                    // this flow will also set `ExpectClientKx::client_cert` to `None`, making it
                    // impossible to reach this state.
                    return Err(cx.common.send_fatal_alert(
                        AlertDescription::AccessDenied,
                        Error::General("client authentication not set up".into()),
                    ));
                }
            }
        };

        if let Err(e) = rc {
            return Err(cx
                .common
                .send_cert_verify_error_alert(e));
        }

        trace!("client CertificateVerify OK");
        cx.common.peer_certificates = Some(self.client_cert);

        self.transcript.add_message(&m);
        Ok(Box::new(ExpectCcs {
            config: self.config,
            secrets: self.secrets,
            transcript: self.transcript,
            session_id: self.session_id,
            using_ems: self.using_ems,
            resuming: false,
            send_ticket: self.send_ticket,
        }))
    }
}

// --- Process client's ChangeCipherSpec ---
struct ExpectCcs {
    config: Arc<ServerConfig>,
    secrets: ConnectionSecrets,
    transcript: HandshakeHash,
    session_id: SessionId,
    using_ems: bool,
    resuming: bool,
    send_ticket: bool,
}

impl State<ServerConnectionData> for ExpectCcs {
    fn handle(self: Box<Self>, cx: &mut ServerContext<'_>, 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()?;

        cx.common
            .record_layer
            .start_decrypting();
        Ok(Box::new(ExpectFinished {
            config: self.config,
            secrets: self.secrets,
            transcript: self.transcript,
            session_id: self.session_id,
            using_ems: self.using_ems,
            resuming: self.resuming,
            send_ticket: self.send_ticket,
        }))
    }
}

// --- Process client's Finished ---
fn get_server_connection_value_tls12(
    secrets: &ConnectionSecrets,
    using_ems: bool,
    cx: &ServerContext<'_>,
    time_now: UnixTime,
) -> persist::ServerSessionValue {
    let version = ProtocolVersion::TLSv1_2;

    let mut v = persist::ServerSessionValue::new(
        cx.data.sni.as_ref(),
        version,
        secrets.suite().common.suite,
        secrets.master_secret(),
        cx.common.peer_certificates.clone(),
        cx.common.alpn_protocol.clone(),
        cx.data.resumption_data.clone(),
        time_now,
        0,
    );

    if using_ems {
        v.set_extended_ms_used();
    }

    v
}

fn emit_ticket(
    secrets: &ConnectionSecrets,
    transcript: &mut HandshakeHash,
    using_ems: bool,
    cx: &mut ServerContext<'_>,
    ticketer: &dyn ProducesTickets,
) -> Result<(), Error> {
    let plain =
        get_server_connection_value_tls12(secrets, using_ems, cx, UnixTime::now()).get_encoding();

    // If we can't produce a ticket for some reason, we can't
    // report an error. Send an empty one.
    let ticket = ticketer
        .encrypt(&plain)
        .unwrap_or_default();
    let ticket_lifetime = ticketer.lifetime();

    let m = Message {
        version: ProtocolVersion::TLSv1_2,
        payload: MessagePayload::handshake(HandshakeMessagePayload {
            typ: HandshakeType::NewSessionTicket,
            payload: HandshakePayload::NewSessionTicket(NewSessionTicketPayload::new(
                ticket_lifetime,
                ticket,
            )),
        }),
    };

    transcript.add_message(&m);
    cx.common.send_msg(m, false);
    Ok(())
}

fn emit_ccs(common: &mut CommonState) {
    let m = Message {
        version: ProtocolVersion::TLSv1_2,
        payload: MessagePayload::ChangeCipherSpec(ChangeCipherSpecPayload {}),
    };

    common.send_msg(m, false);
}

fn emit_finished(
    secrets: &ConnectionSecrets,
    transcript: &mut HandshakeHash,
    common: &mut CommonState,
) {
    let vh = transcript.get_current_hash();
    let verify_data = secrets.server_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 ExpectFinished {
    config: Arc<ServerConfig>,
    secrets: ConnectionSecrets,
    transcript: HandshakeHash,
    session_id: SessionId,
    using_ems: bool,
    resuming: bool,
    send_ticket: bool,
}

impl State<ServerConnectionData> for ExpectFinished {
    fn handle(mut self: Box<Self>, cx: &mut ServerContext<'_>, m: Message) -> hs::NextStateOrError {
        let finished =
            require_handshake_msg!(m, HandshakeType::Finished, HandshakePayload::Finished)?;

        cx.common.check_aligned_handshake()?;

        let vh = self.transcript.get_current_hash();
        let expect_verify_data = self.secrets.client_verify_data(&vh);

        let _fin_verified = match ConstantTimeEq::ct_eq(&expect_verify_data[..], &finished.0).into()
        {
            true => verify::FinishedMessageVerified::assertion(),
            false => {
                return Err(cx
                    .common
                    .send_fatal_alert(AlertDescription::DecryptError, Error::DecryptError));
            }
        };

        // Save connection, perhaps
        if !self.resuming && !self.session_id.is_empty() {
            let value = get_server_connection_value_tls12(
                &self.secrets,
                self.using_ems,
                cx,
                UnixTime::now(),
            );

            let worked = self
                .config
                .session_storage
                .put(self.session_id.get_encoding(), value.get_encoding());
            if worked {
                debug!("Session saved");
            } else {
                debug!("Session not saved");
            }
        }

        // Send our CCS and Finished.
        self.transcript.add_message(&m);
        if !self.resuming {
            if self.send_ticket {
                emit_ticket(
                    &self.secrets,
                    &mut self.transcript,
                    self.using_ems,
                    cx,
                    &*self.config.ticketer,
                )?;
            }
            emit_ccs(cx.common);
            cx.common
                .record_layer
                .start_encrypting();
            emit_finished(&self.secrets, &mut self.transcript, cx.common);
        }

        cx.common.start_traffic();
        Ok(Box::new(ExpectTraffic {
            secrets: self.secrets,
            _fin_verified,
        }))
    }
}

// --- Process traffic ---
struct ExpectTraffic {
    secrets: ConnectionSecrets,
    _fin_verified: verify::FinishedMessageVerified,
}

impl ExpectTraffic {}

impl State<ServerConnectionData> for ExpectTraffic {
    fn handle(self: Box<Self>, cx: &mut ServerContext<'_>, 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(())
    }

    fn extract_secrets(&self) -> Result<PartiallyExtractedSecrets, Error> {
        self.secrets
            .extract_secrets(Side::Server)
    }
}
