1
0
mirror of https://github.com/facebookincubator/mvfst.git synced 2025-11-24 04:01:07 +03:00
Files
mvfst/quic/client/handshake/ClientHandshake.cpp
Amaury Séchet 63e9b70ce6 Move 0RTT cipher management to the handshake. (#97)
Summary:
Make sure that trigger on error is performed when running connect.

This avoids some bucket brigading around the connect function and helps to isolate QuicClientTransport pskCache management, reduce the memory footprint of the handhsake and reduce temporal coupling.
Pull Request resolved: https://github.com/facebookincubator/mvfst/pull/97

Test Plan:
Imported from GitHub, without a `Test Plan:` line.

 ---
## Proxygen Canary
Traffic Canary: https://our.intern.facebook.com/intern/traffic/canary?fbid=212582886632633
* elb.prod.sof1c01 - binary - 2020-03-04 10:31 - https://fburl.com/dyndash/cu6glbvv
* flb.prod.fisb3c02 - binary - 2020-03-04 10:31 - https://fburl.com/dyndash/qaozda7o
* olb.prod.rlla0c01.p2 - binary - 2020-03-04 10:31 - https://fburl.com/dyndash/m7des08a
* slb.prod_regional.rfrc0c00 - binary - 2020-03-04 10:31 - https://fburl.com/dyndash/j2ftlr65
* slb.regional.rlla0c01.p2 - binary - 2020-03-04 10:31 - https://fburl.com/dyndash/98by8k4u
 ---

Reviewed By: mjoras

Differential Revision: D19873714

Pulled By: sharmafb

fbshipit-source-id: 7b0980e9d2590227bec5426560f5dd5d6a34ae85
2020-03-10 11:46:19 -07:00

218 lines
6.5 KiB
C++

/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
*/
#include <quic/client/handshake/ClientHandshake.h>
#include <quic/client/state/ClientStateMachine.h>
#include <quic/state/QuicStreamFunctions.h>
namespace quic {
ClientHandshake::ClientHandshake(QuicClientConnectionState* conn)
: conn_(conn) {}
void ClientHandshake::doHandshake(
std::unique_ptr<folly::IOBuf> data,
EncryptionLevel encryptionLevel) {
if (!data) {
return;
}
// TODO: deal with clear text alert messages. It's possible that a MITM who
// mucks with the finished messages could cause the decryption to be invalid
// on the server, which would result in a cleartext close or a cleartext
// alert. We currently switch to 1-rtt ciphers immediately for reads and
// throw away the cleartext cipher for reads, this would result in us
// dropping the alert and timing out instead.
if (phase_ == Phase::Initial) {
// This could be an HRR or a cleartext alert.
phase_ = Phase::Handshake;
}
// First add it to the right read buffer.
switch (encryptionLevel) {
case EncryptionLevel::Initial:
initialReadBuf_.append(std::move(data));
break;
case EncryptionLevel::Handshake:
handshakeReadBuf_.append(std::move(data));
break;
case EncryptionLevel::EarlyData:
case EncryptionLevel::AppData:
appDataReadBuf_.append(std::move(data));
break;
}
// Get the current buffer type the transport is accepting.
waitForData_ = false;
while (!waitForData_) {
switch (getReadRecordLayerEncryptionLevel()) {
case EncryptionLevel::Initial:
processSocketData(initialReadBuf_);
break;
case EncryptionLevel::Handshake:
processSocketData(handshakeReadBuf_);
break;
case EncryptionLevel::EarlyData:
case EncryptionLevel::AppData:
processSocketData(appDataReadBuf_);
break;
}
throwOnError();
}
}
std::unique_ptr<Aead> ClientHandshake::getOneRttWriteCipher() {
throwOnError();
return std::move(oneRttWriteCipher_);
}
std::unique_ptr<Aead> ClientHandshake::getOneRttReadCipher() {
throwOnError();
return std::move(oneRttReadCipher_);
}
std::unique_ptr<Aead> ClientHandshake::getHandshakeReadCipher() {
throwOnError();
return std::move(handshakeReadCipher_);
}
std::unique_ptr<Aead> ClientHandshake::getHandshakeWriteCipher() {
throwOnError();
return std::move(handshakeWriteCipher_);
}
std::unique_ptr<PacketNumberCipher>
ClientHandshake::getOneRttReadHeaderCipher() {
throwOnError();
return std::move(oneRttReadHeaderCipher_);
}
std::unique_ptr<PacketNumberCipher>
ClientHandshake::getOneRttWriteHeaderCipher() {
throwOnError();
return std::move(oneRttWriteHeaderCipher_);
}
std::unique_ptr<PacketNumberCipher>
ClientHandshake::getHandshakeReadHeaderCipher() {
throwOnError();
return std::move(handshakeReadHeaderCipher_);
}
std::unique_ptr<PacketNumberCipher>
ClientHandshake::getHandshakeWriteHeaderCipher() {
throwOnError();
return std::move(handshakeWriteHeaderCipher_);
}
/**
* Notify the crypto layer that we received one rtt protected data.
* This allows us to know that the peer has implicitly acked the 1-rtt keys.
*/
void ClientHandshake::onRecvOneRttProtectedData() {
if (phase_ != Phase::Established) {
phase_ = Phase::Established;
}
}
ClientHandshake::Phase ClientHandshake::getPhase() const {
return phase_;
}
folly::Optional<ServerTransportParameters>
ClientHandshake::getServerTransportParams() {
return transportParams_->getServerTransportParams();
}
folly::Optional<bool> ClientHandshake::getZeroRttRejected() {
return std::move(zeroRttRejected_);
}
void ClientHandshake::computeCiphers(CipherKind kind, folly::ByteRange secret) {
std::unique_ptr<Aead> aead;
std::unique_ptr<PacketNumberCipher> packetNumberCipher;
std::tie(aead, packetNumberCipher) = buildCiphers(kind, secret);
switch (kind) {
case CipherKind::HandshakeWrite:
handshakeWriteCipher_ = std::move(aead);
handshakeWriteHeaderCipher_ = std::move(packetNumberCipher);
break;
case CipherKind::HandshakeRead:
handshakeReadCipher_ = std::move(aead);
handshakeReadHeaderCipher_ = std::move(packetNumberCipher);
break;
case CipherKind::OneRttWrite:
oneRttWriteCipher_ = std::move(aead);
oneRttWriteHeaderCipher_ = std::move(packetNumberCipher);
break;
case CipherKind::OneRttRead:
oneRttReadCipher_ = std::move(aead);
oneRttReadHeaderCipher_ = std::move(packetNumberCipher);
break;
case CipherKind::ZeroRttWrite:
conn_->zeroRttWriteCipher = std::move(aead);
conn_->zeroRttWriteHeaderCipher = std::move(packetNumberCipher);
break;
default:
// Report error?
break;
}
}
void ClientHandshake::raiseError(folly::exception_wrapper error) {
error_ = std::move(error);
}
void ClientHandshake::throwOnError() {
if (error_) {
error_.throw_exception();
}
}
void ClientHandshake::waitForData() {
waitForData_ = true;
}
void ClientHandshake::writeDataToStream(
EncryptionLevel encryptionLevel,
Buf data) {
if (encryptionLevel == EncryptionLevel::AppData) {
// Don't write 1-rtt handshake data on the client.
return;
}
auto cryptoStream = getCryptoStream(*conn_->cryptoState, encryptionLevel);
writeDataToQuicStream(*cryptoStream, std::move(data));
}
void ClientHandshake::computeZeroRttCipher() {
VLOG(10) << "Computing Client zero rtt keys";
earlyDataAttempted_ = true;
}
void ClientHandshake::computeOneRttCipher(bool earlyDataAccepted) {
// The 1-rtt handshake should have succeeded if we know that the early
// write failed. We currently treat the data as lost.
// TODO: we need to deal with HRR based rejection as well, however we don't
// have an API right now.
if (earlyDataAttempted_ && !earlyDataAccepted) {
if (matchEarlyParameters()) {
zeroRttRejected_ = true;
} else {
// TODO: support app retry of zero rtt data.
error_ = folly::make_exception_wrapper<QuicInternalException>(
"Changing parameters when early data attempted not supported",
LocalErrorCode::EARLY_DATA_REJECTED);
return;
}
}
// After a successful handshake we should send packets with the type of
// ClientCleartext. We assume that by the time we get the data for the QUIC
// stream, the server would have also acked all the client initial packets.
phase_ = Phase::OneRttKeysDerived;
}
} // namespace quic