return runOnCallerIfComplete()

in fizz/server/ServerProtocol.cpp [1085:1665]


  return runOnCallerIfComplete(
      state.executor(),
      std::move(results),
      [&state,
       chlo = std::move(chlo),
       cookieState = std::move(cookieState),
       version = *version,
       cipher,
       pskMode = resStateResult.pskMode,
       obfuscatedAge =
           resStateResult.obfuscatedAge](FutureResultType result) mutable {
        auto& resumption = *std::get<0>(result);
        auto pskType = resumption.first;
        auto resState = std::move(resumption.second);
        auto replayCacheResult = *std::get<1>(result);

        if (resState) {
          if (!validateResumptionState(*resState, *pskMode, version, cipher)) {
            pskType = PskType::Rejected;
            pskMode = folly::none;
            resState = folly::none;
          }
        } else {
          pskMode = folly::none;
        }

        auto legacySessionId = chlo.legacy_session_id->clone();

        // If we successfully resumed, set the handshake time to the ticket's
        // handshake time to preserve it across ticket updates. If not, set it
        // to now.
        std::chrono::system_clock::time_point handshakeTime;
        if (resState) {
          handshakeTime = resState->handshakeTime;
        } else {
          handshakeTime = state.context()->getClock().getCurrentTime();
        }

        std::unique_ptr<KeyScheduler> scheduler;
        std::unique_ptr<HandshakeContext> handshakeContext;
        std::tie(scheduler, handshakeContext) = setupSchedulerAndContext(
            *state.context()->getFactory(),
            cipher,
            chlo,
            resState,
            cookieState,
            pskType,
            std::move(state.handshakeContext()),
            version);

        if (state.cipher().has_value() && cipher != *state.cipher()) {
          throw FizzException(
              "cipher mismatch with previous negotiation",
              AlertDescription::illegal_parameter);
        }

        auto alpn = negotiateAlpn(chlo, folly::none, *state.context());

        auto clockSkew = getClockSkew(
            resState,
            obfuscatedAge,
            state.context()->getClock().getCurrentTime());

        auto appToken = getAppToken(resState);

        auto earlyDataType = negotiateEarlyDataType(
            state.context()->getAcceptEarlyData(version),
            chlo,
            resState,
            cipher,
            state.keyExchangeType(),
            cookieState,
            alpn,
            replayCacheResult,
            clockSkew,
            state.context()->getClockSkewTolerance(),
            state.appTokenValidator());

        std::unique_ptr<EncryptedReadRecordLayer> earlyReadRecordLayer;
        Buf earlyExporterMaster;
        folly::Optional<SecretAvailable> earlyReadSecretAvailable;
        if (earlyDataType == EarlyDataType::Accepted) {
          auto earlyContext = handshakeContext->getHandshakeContext();
          auto earlyReadSecret = scheduler->getSecret(
              EarlySecrets::ClientEarlyTraffic, earlyContext->coalesce());
          if (!state.context()->getOmitEarlyRecordLayer()) {
            earlyReadRecordLayer =
                state.context()->getFactory()->makeEncryptedReadRecordLayer(
                    EncryptionLevel::EarlyData);
            earlyReadRecordLayer->setProtocolVersion(version);

            Protocol::setAead(
                *earlyReadRecordLayer,
                cipher,
                folly::range(earlyReadSecret.secret),
                *state.context()->getFactory(),
                *scheduler);
          }

          earlyReadSecretAvailable =
              SecretAvailable(std::move(earlyReadSecret));
          earlyExporterMaster = folly::IOBuf::copyBuffer(
              scheduler
                  ->getSecret(
                      EarlySecrets::EarlyExporter, earlyContext->coalesce())
                  .secret);
        }

        Optional<NamedGroup> group;
        Optional<Buf> serverShare;
        KeyExchangeType keyExchangeType;
        if (!pskMode || *pskMode != PskKeyExchangeMode::psk_ke) {
          Optional<Buf> clientShare;
          std::tie(group, clientShare) = negotiateGroup(
              version, chlo, state.context()->getSupportedGroups());
          if (!clientShare) {
            VLOG(8) << "Did not find key share for " << toString(*group);
            if (state.group().has_value() || cookieState) {
              throw FizzException(
                  "key share not found for already negotiated group",
                  AlertDescription::illegal_parameter);
            }

            // If we were otherwise going to accept early data we now need to
            // reject it. It's a little ugly to change our previous early data
            // decision, but doing it this way allows us to move the key
            // schedule forward as we do the key exchange.
            if (earlyDataType == EarlyDataType::Accepted) {
              earlyDataType = EarlyDataType::Rejected;
            }

            message_hash chloHash;
            chloHash.hash = handshakeContext->getHandshakeContext();
            handshakeContext =
                state.context()->getFactory()->makeHandshakeContext(cipher);
            handshakeContext->appendToTranscript(
                encodeHandshake(std::move(chloHash)));

            auto encodedHelloRetryRequest = getHelloRetryRequest(
                version,
                cipher,
                *group,
                legacySessionId ? legacySessionId->clone() : nullptr,
                *handshakeContext);

            WriteToSocket serverFlight;
            serverFlight.contents.emplace_back(
                state.writeRecordLayer()->writeHandshake(
                    std::move(encodedHelloRetryRequest)));

            if (legacySessionId && !legacySessionId->empty()) {
              TLSContent writeCCS;
              writeCCS.encryptionLevel = EncryptionLevel::Plaintext;
              writeCCS.contentType = ContentType::change_cipher_spec;
              writeCCS.data = folly::IOBuf::wrapBuffer(FakeChangeCipherSpec);
              serverFlight.contents.emplace_back(std::move(writeCCS));
            }

            // Create a new record layer in case we need to skip early data.
            auto newReadRecordLayer =
                state.context()->getFactory()->makePlaintextReadRecordLayer();
            newReadRecordLayer->setSkipEncryptedRecords(
                earlyDataType == EarlyDataType::Rejected);

            return SemiFuture<Actions>(actions(
                MutateState([handshakeContext = std::move(handshakeContext),
                             version,
                             cipher,
                             group,
                             earlyDataType,
                             replayCacheResult,
                             newReadRecordLayer = std::move(
                                 newReadRecordLayer)](State& newState) mutable {
                  // Save some information about the current state to be
                  // validated when we get the second client hello. We don't
                  // validate that the second client hello matches the first
                  // as strictly as we could according to the spec however.
                  newState.handshakeContext() = std::move(handshakeContext);
                  newState.version() = version;
                  newState.cipher() = cipher;
                  newState.group() = group;
                  newState.keyExchangeType() =
                      KeyExchangeType::HelloRetryRequest;
                  newState.earlyDataType() = earlyDataType;
                  newState.replayCacheResult() = replayCacheResult;
                  newState.readRecordLayer() = std::move(newReadRecordLayer);
                }),
                std::move(serverFlight),
                MutateState(&Transition<StateEnum::ExpectingClientHello>)));
          }

          if (state.keyExchangeType().has_value()) {
            keyExchangeType = *state.keyExchangeType();
          } else {
            keyExchangeType = KeyExchangeType::OneRtt;
          }

          serverShare = doKex(
              *state.context()->getFactory(), *group, *clientShare, *scheduler);
        } else {
          keyExchangeType = KeyExchangeType::None;
          scheduler->deriveHandshakeSecret();
        }

        std::vector<Extension> additionalExtensions;
        if (state.extensions()) {
          additionalExtensions = state.extensions()->getExtensions(chlo);
        }

        if (state.group().has_value() && (!group || *group != *state.group())) {
          throw FizzException(
              "group mismatch with previous negotiation",
              AlertDescription::illegal_parameter);
        }

        // Cookies are not required to have already negotiated the group but if
        // they did it must match (psk_ke is still allowed as we may not know if
        // we are accepting the psk when sending the cookie).
        if (cookieState && cookieState->group && group &&
            *group != *cookieState->group) {
          throw FizzException(
              "group mismatch with cookie",
              AlertDescription::illegal_parameter);
        }

        auto encodedServerHello = getServerHello(
            version,
            state.context()->getFactory()->makeRandom(),
            cipher,
            resState.has_value(),
            group,
            std::move(serverShare),
            legacySessionId ? legacySessionId->clone() : nullptr,
            *handshakeContext);

        // Derive handshake keys.
        auto handshakeWriteRecordLayer =
            state.context()->getFactory()->makeEncryptedWriteRecordLayer(
                EncryptionLevel::Handshake);
        handshakeWriteRecordLayer->setProtocolVersion(version);
        auto handshakeWriteSecret = scheduler->getSecret(
            HandshakeSecrets::ServerHandshakeTraffic,
            handshakeContext->getHandshakeContext()->coalesce());
        Protocol::setAead(
            *handshakeWriteRecordLayer,
            cipher,
            folly::range(handshakeWriteSecret.secret),
            *state.context()->getFactory(),
            *scheduler);

        auto handshakeReadRecordLayer =
            state.context()->getFactory()->makeEncryptedReadRecordLayer(
                EncryptionLevel::Handshake);
        handshakeReadRecordLayer->setProtocolVersion(version);
        handshakeReadRecordLayer->setSkipFailedDecryption(
            earlyDataType == EarlyDataType::Rejected);
        auto handshakeReadSecret = scheduler->getSecret(
            HandshakeSecrets::ClientHandshakeTraffic,
            handshakeContext->getHandshakeContext()->coalesce());
        Protocol::setAead(
            *handshakeReadRecordLayer,
            cipher,
            folly::range(handshakeReadSecret.secret),
            *state.context()->getFactory(),
            *scheduler);
        auto clientHandshakeSecret =
            folly::IOBuf::copyBuffer(handshakeReadSecret.secret);

        auto encodedEncryptedExt = getEncryptedExt(
            *handshakeContext,
            alpn,
            earlyDataType,
            std::move(additionalExtensions));

        /*
         * Determine we are requesting client auth.
         * If yes, add CertificateRequest to handshake write and transcript.
         */
        bool requestClientAuth =
            state.context()->getClientAuthMode() != ClientAuthMode::None &&
            !resState;
        Optional<Buf> encodedCertRequest;
        if (requestClientAuth) {
          encodedCertRequest = getCertificateRequest(
              state.context()->getSupportedSigSchemes(),
              state.context()->getClientCertVerifier().get(),
              *handshakeContext);
        }

        /*
         * Set the cert and signature scheme we are using.
         * If sending new cert, add Certificate to handshake write and
         * transcript.
         */
        Optional<Buf> encodedCertificate;
        SemiFuture<Optional<Buf>> signature = folly::none;
        Optional<SignatureScheme> sigScheme;
        Optional<std::shared_ptr<const Cert>> serverCert;
        std::shared_ptr<const Cert> clientCert;
        Optional<CertificateCompressionAlgorithm> certCompressionAlgo;
        if (!resState) { // TODO or reauth
          std::shared_ptr<const SelfCert> originalSelfCert;
          std::tie(originalSelfCert, sigScheme) =
              chooseCert(*state.context(), chlo);

          std::tie(encodedCertificate, certCompressionAlgo) = getCertificate(
              originalSelfCert, *state.context(), chlo, *handshakeContext);

          auto toBeSigned = handshakeContext->getHandshakeContext();
          auto asyncSelfCert =
              dynamic_cast<const AsyncSelfCert*>(originalSelfCert.get());
          if (asyncSelfCert) {
            signature = asyncSelfCert->signFuture(
                *sigScheme,
                CertificateVerifyContext::Server,
                toBeSigned->coalesce());
          } else {
            signature =
                folly::makeSemiFuture<Optional<Buf>>(originalSelfCert->sign(
                    *sigScheme,
                    CertificateVerifyContext::Server,
                    toBeSigned->coalesce()));
          }
          serverCert = std::move(originalSelfCert);
        } else {
          serverCert = std::move(resState->serverCert);
          clientCert = std::move(resState->clientCert);
        }

        auto clientRandom = std::move(chlo.random);
        return runOnCallerIfComplete(
            state.executor(),
            std::move(signature),
            [&state,
             scheduler = std::move(scheduler),
             handshakeContext = std::move(handshakeContext),
             cipher,
             clientRandom = std::move(clientRandom),
             group,
             encodedServerHello = std::move(encodedServerHello),
             handshakeWriteRecordLayer = std::move(handshakeWriteRecordLayer),
             handshakeWriteSecret = std::move(handshakeWriteSecret),
             handshakeReadRecordLayer = std::move(handshakeReadRecordLayer),
             handshakeReadSecret = std::move(handshakeReadSecret),
             earlyReadRecordLayer = std::move(earlyReadRecordLayer),
             earlyReadSecretAvailable = std::move(earlyReadSecretAvailable),
             earlyExporterMaster = std::move(earlyExporterMaster),
             clientHandshakeSecret = std::move(clientHandshakeSecret),
             encodedEncryptedExt = std::move(encodedEncryptedExt),
             encodedCertificate = std::move(encodedCertificate),
             encodedCertRequest = std::move(encodedCertRequest),
             requestClientAuth,
             pskType,
             pskMode,
             sigScheme,
             version,
             keyExchangeType,
             earlyDataType,
             replayCacheResult,
             serverCert = std::move(serverCert),
             clientCert = std::move(clientCert),
             alpn = std::move(alpn),
             clockSkew,
             appToken = std::move(appToken),
             legacySessionId = std::move(legacySessionId),
             serverCertCompAlgo = certCompressionAlgo,
             handshakeTime](Optional<Buf> sig) mutable {
              Optional<Buf> encodedCertificateVerify;
              if (sig) {
                encodedCertificateVerify = getCertificateVerify(
                    *sigScheme, std::move(*sig), *handshakeContext);
              }

              auto encodedFinished = Protocol::getFinished(
                  folly::range(handshakeWriteSecret.secret), *handshakeContext);

              folly::IOBufQueue combined;
              if (encodedCertificate) {
                if (encodedCertRequest) {
                  combined.append(std::move(encodedEncryptedExt));
                  combined.append(std::move(*encodedCertRequest));
                  combined.append(std::move(*encodedCertificate));
                  combined.append(std::move(*encodedCertificateVerify));
                  combined.append(std::move(encodedFinished));
                } else {
                  combined.append(std::move(encodedEncryptedExt));
                  combined.append(std::move(*encodedCertificate));
                  combined.append(std::move(*encodedCertificateVerify));
                  combined.append(std::move(encodedFinished));
                }
              } else {
                combined.append(std::move(encodedEncryptedExt));
                combined.append(std::move(encodedFinished));
              }

              // Some middleboxes appear to break if the first encrypted record
              // is larger than ~1300 bytes (likely if it does not fit in the
              // first packet).
              auto serverEncrypted = handshakeWriteRecordLayer->writeHandshake(
                  combined.splitAtMost(1000));
              if (!combined.empty()) {
                auto splitRecord =
                    handshakeWriteRecordLayer->writeHandshake(combined.move());
                // Split record must have the same encryption level as the main
                // handshake.
                DCHECK(
                    splitRecord.encryptionLevel ==
                    serverEncrypted.encryptionLevel);
                serverEncrypted.data->prependChain(std::move(splitRecord.data));
              }

              WriteToSocket serverFlight;
              serverFlight.contents.emplace_back(
                  state.writeRecordLayer()->writeHandshake(
                      std::move(encodedServerHello)));
              if (legacySessionId && !legacySessionId->empty()) {
                TLSContent ccsWrite;
                ccsWrite.encryptionLevel = EncryptionLevel::Plaintext;
                ccsWrite.contentType = ContentType::change_cipher_spec;
                ccsWrite.data = folly::IOBuf::wrapBuffer(FakeChangeCipherSpec);
                serverFlight.contents.emplace_back(std::move(ccsWrite));
              }
              serverFlight.contents.emplace_back(std::move(serverEncrypted));

              scheduler->deriveMasterSecret();
              auto clientFinishedContext =
                  handshakeContext->getHandshakeContext();
              auto exporterMasterVector = scheduler->getSecret(
                  MasterSecrets::ExporterMaster,
                  clientFinishedContext->coalesce());
              auto exporterMaster = folly::IOBuf::copyBuffer(
                  folly::range(exporterMasterVector.secret));

              scheduler->deriveAppTrafficSecrets(
                  clientFinishedContext->coalesce());
              auto appTrafficWriteRecordLayer =
                  state.context()->getFactory()->makeEncryptedWriteRecordLayer(
                      EncryptionLevel::AppTraffic);
              appTrafficWriteRecordLayer->setProtocolVersion(version);
              auto writeSecret =
                  scheduler->getSecret(AppTrafficSecrets::ServerAppTraffic);
              Protocol::setAead(
                  *appTrafficWriteRecordLayer,
                  cipher,
                  folly::range(writeSecret.secret),
                  *state.context()->getFactory(),
                  *scheduler);

              // If we have previously dealt with early data (before a
              // HelloRetryRequest), don't overwrite the previous result.
              auto earlyDataTypeSave = state.earlyDataType()
                  ? *state.earlyDataType()
                  : earlyDataType;

              SecretAvailable handshakeReadSecretAvailable(
                  std::move(handshakeReadSecret));
              SecretAvailable handshakeWriteSecretAvailable(
                  std::move(handshakeWriteSecret));
              SecretAvailable appWriteSecretAvailable(std::move(writeSecret));

              // Save all the necessary state except for the read record layer,
              // which is done separately as it varies if early data was
              // accepted.
              MutateState saveState(
                  [appTrafficWriteRecordLayer =
                       std::move(appTrafficWriteRecordLayer),
                   handshakeContext = std::move(handshakeContext),
                   scheduler = std::move(scheduler),
                   exporterMaster = std::move(exporterMaster),
                   serverCert = std::move(serverCert),
                   clientCert = std::move(clientCert),
                   cipher,
                   group,
                   sigScheme,
                   clientHandshakeSecret = std::move(clientHandshakeSecret),
                   pskType,
                   pskMode,
                   version,
                   keyExchangeType,
                   alpn = std::move(alpn),
                   earlyDataTypeSave,
                   replayCacheResult,
                   clockSkew,
                   appToken = std::move(appToken),
                   serverCertCompAlgo,
                   clientRandom = std::move(clientRandom),
                   handshakeTime =
                       std::move(handshakeTime)](State& newState) mutable {
                    newState.writeRecordLayer() =
                        std::move(appTrafficWriteRecordLayer);
                    newState.handshakeContext() = std::move(handshakeContext);
                    newState.keyScheduler() = std::move(scheduler);
                    newState.exporterMasterSecret() = std::move(exporterMaster);
                    newState.serverCert() = std::move(*serverCert);
                    newState.clientCert() = std::move(clientCert);
                    newState.version() = version;
                    newState.cipher() = cipher;
                    newState.group() = group;
                    newState.sigScheme() = sigScheme;
                    newState.clientHandshakeSecret() =
                        std::move(clientHandshakeSecret);
                    newState.pskType() = pskType;
                    newState.pskMode() = pskMode;
                    newState.keyExchangeType() = keyExchangeType;
                    newState.earlyDataType() = earlyDataTypeSave;
                    newState.replayCacheResult() = replayCacheResult;
                    newState.alpn() = std::move(alpn);
                    newState.clientClockSkew() = clockSkew;
                    newState.appToken() = std::move(appToken);
                    newState.serverCertCompAlgo() = serverCertCompAlgo;
                    newState.handshakeTime() = std::move(handshakeTime);
                    newState.clientRandom() = std::move(clientRandom);
                  });

              if (earlyDataType == EarlyDataType::Accepted) {
                if (state.context()->getOmitEarlyRecordLayer()) {
                  return actions(
                      MutateState([handshakeReadRecordLayer =
                                       std::move(handshakeReadRecordLayer),
                                   earlyExporterMaster =
                                       std::move(earlyExporterMaster)](
                                      State& newState) mutable {
                        newState.readRecordLayer() =
                            std::move(handshakeReadRecordLayer);
                        newState.earlyExporterMasterSecret() =
                            std::move(earlyExporterMaster);
                      }),
                      std::move(saveState),
                      std::move(*earlyReadSecretAvailable),
                      std::move(handshakeReadSecretAvailable),
                      std::move(handshakeWriteSecretAvailable),
                      std::move(appWriteSecretAvailable),
                      std::move(serverFlight),
                      MutateState(&Transition<StateEnum::ExpectingFinished>),
                      ReportEarlyHandshakeSuccess());

                } else {
                  return actions(
                      MutateState([handshakeReadRecordLayer =
                                       std::move(handshakeReadRecordLayer),
                                   earlyReadRecordLayer =
                                       std::move(earlyReadRecordLayer),
                                   earlyExporterMaster =
                                       std::move(earlyExporterMaster)](
                                      State& newState) mutable {
                        newState.readRecordLayer() =
                            std::move(earlyReadRecordLayer);
                        newState.handshakeReadRecordLayer() =
                            std::move(handshakeReadRecordLayer);
                        newState.earlyExporterMasterSecret() =
                            std::move(earlyExporterMaster);
                      }),
                      std::move(saveState),
                      std::move(*earlyReadSecretAvailable),
                      std::move(handshakeReadSecretAvailable),
                      std::move(handshakeWriteSecretAvailable),
                      std::move(appWriteSecretAvailable),
                      std::move(serverFlight),
                      MutateState(&Transition<StateEnum::AcceptingEarlyData>),
                      ReportEarlyHandshakeSuccess());
                }
              } else {
                auto transition = requestClientAuth
                    ? Transition<StateEnum::ExpectingCertificate>
                    : Transition<StateEnum::ExpectingFinished>;
                return actions(
                    MutateState([handshakeReadRecordLayer =
                                     std::move(handshakeReadRecordLayer)](
                                    State& newState) mutable {
                      newState.readRecordLayer() =
                          std::move(handshakeReadRecordLayer);
                    }),
                    std::move(saveState),
                    std::move(handshakeReadSecretAvailable),
                    std::move(handshakeWriteSecretAvailable),
                    std::move(appWriteSecretAvailable),
                    std::move(serverFlight),
                    MutateState(transition));
              }
            });
      });