static enum ssl_hs_wait_t do_send_server_certificate()

in ssl/handshake_server.cc [1100:1221]


static enum ssl_hs_wait_t do_send_server_certificate(SSL_HANDSHAKE *hs) {
  SSL *const ssl = hs->ssl;
  ScopedCBB cbb;

  if (ssl_cipher_uses_certificate_auth(hs->new_cipher)) {
    if (!ssl_has_certificate(hs)) {
      OPENSSL_PUT_ERROR(SSL, SSL_R_NO_CERTIFICATE_SET);
      return ssl_hs_error;
    }

    if (!ssl_output_cert_chain(hs)) {
      return ssl_hs_error;
    }

    if (hs->certificate_status_expected) {
      CBB body, ocsp_response;
      if (!ssl->method->init_message(ssl, cbb.get(), &body,
                                     SSL3_MT_CERTIFICATE_STATUS) ||
          !CBB_add_u8(&body, TLSEXT_STATUSTYPE_ocsp) ||
          !CBB_add_u24_length_prefixed(&body, &ocsp_response) ||
          !CBB_add_bytes(
              &ocsp_response,
              CRYPTO_BUFFER_data(hs->config->cert->ocsp_response.get()),
              CRYPTO_BUFFER_len(hs->config->cert->ocsp_response.get())) ||
          !ssl_add_message_cbb(ssl, cbb.get())) {
        OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
        return ssl_hs_error;
      }
    }
  }

  // Assemble ServerKeyExchange parameters if needed.
  uint32_t alg_k = hs->new_cipher->algorithm_mkey;
  uint32_t alg_a = hs->new_cipher->algorithm_auth;
  if (ssl_cipher_requires_server_key_exchange(hs->new_cipher) ||
      ((alg_a & SSL_aPSK) && hs->config->psk_identity_hint)) {
    // Pre-allocate enough room to comfortably fit an ECDHE public key. Prepend
    // the client and server randoms for the signing transcript.
    CBB child;
    if (!CBB_init(cbb.get(), SSL3_RANDOM_SIZE * 2 + 128) ||
        !CBB_add_bytes(cbb.get(), ssl->s3->client_random, SSL3_RANDOM_SIZE) ||
        !CBB_add_bytes(cbb.get(), ssl->s3->server_random, SSL3_RANDOM_SIZE)) {
      return ssl_hs_error;
    }

    // PSK ciphers begin with an identity hint.
    if (alg_a & SSL_aPSK) {
      size_t len = hs->config->psk_identity_hint == nullptr
                       ? 0
                       : strlen(hs->config->psk_identity_hint.get());
      if (!CBB_add_u16_length_prefixed(cbb.get(), &child) ||
          !CBB_add_bytes(&child,
                         (const uint8_t *)hs->config->psk_identity_hint.get(),
                         len)) {
        return ssl_hs_error;
      }
    }

    if (alg_k & SSL_kECDHE) {
      // Determine the group to use.
      uint16_t group_id;
      if (!tls1_get_shared_group(hs, &group_id)) {
        OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
        ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_HANDSHAKE_FAILURE);
        return ssl_hs_error;
      }
      hs->new_session->group_id = group_id;

      hs->key_shares[0] = SSLKeyShare::Create(group_id);
      if (!hs->key_shares[0] ||
          !CBB_add_u8(cbb.get(), NAMED_CURVE_TYPE) ||
          !CBB_add_u16(cbb.get(), group_id) ||
          !CBB_add_u8_length_prefixed(cbb.get(), &child)) {
        return ssl_hs_error;
      }

      SSL_HANDSHAKE_HINTS *const hints = hs->hints.get();
      bool hint_ok = false;
      if (hints && !hs->hints_requested &&
          hints->ecdhe_group_id == group_id &&
          !hints->ecdhe_public_key.empty() &&
          !hints->ecdhe_private_key.empty()) {
        CBS cbs = MakeConstSpan(hints->ecdhe_private_key);
        hint_ok = hs->key_shares[0]->DeserializePrivateKey(&cbs);
      }
      if (hint_ok) {
        // Reuse the ECDH key from handshake hints.
        if (!CBB_add_bytes(&child, hints->ecdhe_public_key.data(),
                           hints->ecdhe_public_key.size())) {
          return ssl_hs_error;
        }
      } else {
        // Generate a key, and emit the public half.
        if (!hs->key_shares[0]->Offer(&child)) {
          return ssl_hs_error;
        }
        // If generating hints, save the ECDHE key.
        if (hints && hs->hints_requested) {
          bssl::ScopedCBB private_key_cbb;
          if (!hints->ecdhe_public_key.CopyFrom(
                  MakeConstSpan(CBB_data(&child), CBB_len(&child))) ||
              !CBB_init(private_key_cbb.get(), 32) ||
              !hs->key_shares[0]->SerializePrivateKey(private_key_cbb.get()) ||
              !CBBFinishArray(private_key_cbb.get(),
                              &hints->ecdhe_private_key)) {
            return ssl_hs_error;
          }
          hints->ecdhe_group_id = group_id;
        }
      }
    } else {
      assert(alg_k & SSL_kPSK);
    }

    if (!CBBFinishArray(cbb.get(), &hs->server_params)) {
      return ssl_hs_error;
    }
  }

  hs->state = state12_send_server_key_exchange;
  return ssl_hs_ok;
}