quic/server/handshake/ServerHandshake.h (106 lines of code) (raw):

/* * Copyright (c) Meta Platforms, Inc. and affiliates. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. */ #pragma once #include <fizz/protocol/DefaultCertificateVerifier.h> #include <fizz/server/FizzServer.h> #include <fizz/server/FizzServerContext.h> #include <folly/io/IOBufQueue.h> #include <folly/io/async/DelayedDestruction.h> #include <quic/QuicConstants.h> #include <quic/QuicException.h> #include <quic/handshake/CryptoFactory.h> #include <quic/handshake/HandshakeLayer.h> #include <quic/server/handshake/AppToken.h> #include <quic/server/handshake/ServerTransportParametersExtension.h> #include <quic/state/StateData.h> namespace quic { // struct QuicConnectionStateBase; /** * ServerHandshake abstracts details of the TLS 1.3 fizz crypto handshake. The * TLS handshake can be async, so ServerHandshake provides an API to deal with * the async handshake to work well with the re-entrancy requirements of the * QUIC state machine. The use of this is meant to be the following: * * handshake->doHandshake(newData); // This can throw an exception. * auto writeBytes = handshake->getWriteBytes(); * auto hanshakeState = handshake->getHandshakeState(); * writeBytesToSocket(writeBytes); * * If the handshake is async, then data will be returned async * * void onCryptoEventAvailable() noexcept { * try { * auto writeBytes = handshake->getWriteBytes(); * auto hanshakeState = handshake->getHandshakeState(); * writeBytesToSocket(writeBytes); * } catch (const QuicTransportException& ex) { * .... * } * } * } */ class ServerHandshake : public Handshake { public: class HandshakeCallback { public: virtual ~HandshakeCallback() = default; virtual void onCryptoEventAvailable() noexcept = 0; }; /** * The 3 phases are as follows: * Handshake: We can only write crypto data with handshake keys * KeysDerived: Write crypto data with handshake keys and 1-rtt data with * 1-rtt keys. * Established: Write all data with 1-rtt keys. */ enum class Phase { Handshake, KeysDerived, Established }; explicit ServerHandshake(QuicConnectionStateBase* conn); /** * Starts accepting the TLS connection. */ virtual void accept( std::shared_ptr<ServerTransportParametersExtension> transportParams); /** * Initialize the handshake with the executor and the callback. * The class will clone the context, so the same context will not be used * directly. To get the real context used, call getContext() after invoking * initialize. */ virtual void initialize( folly::Executor* executor, HandshakeCallback* callback, std::unique_ptr<fizz::server::AppTokenValidator> validator = nullptr); /** * Performs the handshake, after a handshake you should check whether or * not an event is available. */ virtual void doHandshake( std::unique_ptr<folly::IOBuf> data, EncryptionLevel encryptionLevel); /** * Writes a session ticket on the connection. */ virtual void writeNewSessionTicket(const AppToken& appToken); /** * Returns a reference to the CryptoFactory used internaly. */ virtual const CryptoFactory& getCryptoFactory() const = 0; /** * An edge triggered API to get the handshakeReadCipher. Once you receive the * write cipher subsequent calls will return null. */ std::unique_ptr<Aead> getHandshakeReadCipher(); /** * An edge triggered API to get the oneRttWriteCipher. Once you receive the * write cipher subsequent calls will return null. */ std::unique_ptr<Aead> getOneRttWriteCipher(); /** * An edge triggered API to get the oneRttReadCipher. Once you receive the * read cipher subsequent calls will return null. */ std::unique_ptr<Aead> getOneRttReadCipher(); /** * An edge triggered API to get the zeroRttReadCipher. Once you receive the * zero rtt read cipher subsequent calls will return null. */ std::unique_ptr<Aead> getZeroRttReadCipher(); /** * An edge triggered API to get the one rtt read header cpher. Once you * receive the header cipher subsequent calls will return null. */ std::unique_ptr<PacketNumberCipher> getOneRttReadHeaderCipher(); /** * An edge triggered API to get the one rtt write header cpher. Once you * receive the header cipher subsequent calls will return null. */ std::unique_ptr<PacketNumberCipher> getOneRttWriteHeaderCipher(); /** * An edge triggered API to get the handshake rtt read header cpher. Once you * receive the header cipher subsequent calls will return null. */ std::unique_ptr<PacketNumberCipher> getHandshakeReadHeaderCipher(); /** * An edge triggered API to get the zero rtt header cpher. Once you * receive the header cipher subsequent calls will return null. */ std::unique_ptr<PacketNumberCipher> getZeroRttReadHeaderCipher(); /** * The application will not get any more callbacks from the handshake layer * after this method returns. */ virtual void cancel(); virtual Phase getPhase() const; /** * Returns the negotiated transport parameters from the client. */ virtual folly::Optional<ClientTransportParameters> getClientTransportParams(); /** * Returns whether all the events that the handshake needs are complete. */ bool isHandshakeDone(); /** * Returns the fizz server state. */ const fizz::server::State& getState() const; /** * Retuns the negotiated ALPN from the handshake. */ const folly::Optional<std::string>& getApplicationProtocol() const override; ~ServerHandshake() override = default; void onError(std::pair<std::string, TransportErrorCode> error); void onWriteData(fizz::WriteToSocket& write); void onHandshakeDone(); /** * Used to schedule actions to process which might be async. */ void addProcessingActions(fizz::server::AsyncActions actions); /** * Start an async or synchronous action, once the async guard is acquired. */ void startActions(fizz::server::AsyncActions actions); /** * Run the actions once they have been completed. */ class ActionMoveVisitor; void processActions( fizz::server::ServerStateMachine::CompletedActions actions); /** * Process any pending events that might have been queued up because there * was an async action pending. */ void processPendingEvents(); protected: Phase phase_{Phase::Handshake}; enum class CipherKind { HandshakeRead, HandshakeWrite, OneRttRead, OneRttWrite, ZeroRttRead, }; void computeCiphers(CipherKind kind, folly::ByteRange secret); fizz::server::State state_; fizz::server::ServerStateMachine machine_; QuicConnectionStateBase* conn_; folly::DelayedDestruction::DestructorGuard actionGuard_; folly::Executor* executor_; QuicCryptoState& cryptoState_; bool inProcessPendingEvents_{false}; bool waitForData_{false}; folly::IOBufQueue initialReadBuf_{folly::IOBufQueue::cacheChainLength()}; folly::IOBufQueue handshakeReadBuf_{folly::IOBufQueue::cacheChainLength()}; folly::IOBufQueue appDataReadBuf_{folly::IOBufQueue::cacheChainLength()}; HandshakeCallback* callback_{nullptr}; folly::Optional<std::pair<std::string, TransportErrorCode>> error_; std::unique_ptr<Aead> handshakeReadCipher_; std::unique_ptr<Aead> oneRttReadCipher_; std::unique_ptr<Aead> oneRttWriteCipher_; std::unique_ptr<Aead> zeroRttReadCipher_; std::unique_ptr<PacketNumberCipher> oneRttReadHeaderCipher_; std::unique_ptr<PacketNumberCipher> oneRttWriteHeaderCipher_; std::unique_ptr<PacketNumberCipher> handshakeReadHeaderCipher_; std::unique_ptr<PacketNumberCipher> zeroRttReadHeaderCipher_; bool inHandshakeStack_{false}; bool handshakeDone_{false}; bool handshakeEventAvailable_{false}; std::shared_ptr<ServerTransportParametersExtension> transportParams_; private: virtual void initializeImpl( HandshakeCallback* callback, std::unique_ptr<fizz::server::AppTokenValidator> validator) = 0; virtual EncryptionLevel getReadRecordLayerEncryptionLevel() = 0; virtual void processSocketData(folly::IOBufQueue& queue) = 0; virtual std::pair<std::unique_ptr<Aead>, std::unique_ptr<PacketNumberCipher>> buildCiphers(folly::ByteRange secret) = 0; virtual void processAccept() = 0; /* * Process a pending crypto event, if one was present. Returns if there was * a pending event. */ virtual bool processPendingCryptoEvent() = 0; virtual void writeNewSessionTicketToCrypto(const AppToken& appToken) = 0; }; // namespace quic } // namespace quic