Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Firefox/dom/media/webrtc/transport/   (Browser von der Mozilla Stiftung Version 136.0.1©)  Datei vom 10.2.2025 mit Größe 46 kB image not shown  

Quelle  transportlayerdtls.cpp   Sprache: C

 
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
 * You can obtain one at http://mozilla.org/MPL/2.0/. */


// Original author: ekr@rtfm.com

#include "transportlayerdtls.h"

#include <algorithm>
#include <iomanip>
#include <queue>
#include <sstream>

#include "dtlsidentity.h"
#include "keyhi.h"
#include "logging.h"
#include "mozilla/glean/DomMediaWebrtcMetrics.h"
#include "mozilla/StaticPrefs_media.h"
#include "mozilla/StaticPrefs_security.h"
#include "mozilla/UniquePtr.h"
#include "mozilla/Unused.h"
#include "nsCOMPtr.h"
#include "nsNetCID.h"
#include "nsServiceManagerUtils.h"
#include "sslexp.h"
#include "sslproto.h"

namespace mozilla {

MOZ_MTLOG_MODULE("mtransport")

static PRDescIdentity transport_layer_identity = PR_INVALID_IO_LAYER;

// TODO: Implement a mode for this where
// the channel is not ready until confirmed externally
// (e.g., after cert check).

#define UNIMPLEMENTED                                                     \
  MOZ_MTLOG(ML_ERROR, "Call to unimplemented function " << __FUNCTION__); \
  MOZ_ASSERT(false);                                                      \
  PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0)

#define MAX_ALPN_LENGTH 255

// We need to adapt the NSPR/libssl model to the TransportFlow model.
// The former wants pull semantics and TransportFlow wants push.
//
// - A TransportLayerDtls assumes it is sitting on top of another
//   TransportLayer, which means that events come in asynchronously.
// - NSS (libssl) wants to sit on top of a PRFileDesc and poll.
// - The TransportLayerNSPRAdapter is a PRFileDesc containing a
//   FIFO.
// - When TransportLayerDtls.PacketReceived() is called, we insert
//   the packets in the FIFO and then do a PR_Recv() on the NSS
//   PRFileDesc, which eventually reads off the FIFO.
//
// All of this stuff is assumed to happen solely in a single thread
// (generally the SocketTransportService thread)

void TransportLayerNSPRAdapter::PacketReceived(MediaPacket& packet) {
  if (enabled_) {
    input_.push(new MediaPacket(std::move(packet)));
  }
}

int32_t TransportLayerNSPRAdapter::Recv(void* buf, int32_t buflen) {
  if (input_.empty()) {
    PR_SetError(PR_WOULD_BLOCK_ERROR, 0);
    return -1;
  }

  MediaPacket* front = input_.front();
  int32_t count = static_cast<int32_t>(front->len());

  if (buflen < count) {
    MOZ_ASSERT(false"Not enough buffer space to receive into");
    PR_SetError(PR_BUFFER_OVERFLOW_ERROR, 0);
    return -1;
  }

  memcpy(buf, front->data(), count);

  input_.pop();
  delete front;

  return count;
}

int32_t TransportLayerNSPRAdapter::Write(const void* buf, int32_t length) {
  if (!enabled_) {
    MOZ_MTLOG(ML_WARNING, "Writing to disabled transport layer");
    return -1;
  }

  MediaPacket packet;
  // Copies. Oh well.
  packet.Copy(static_cast<const uint8_t*>(buf), static_cast<size_t>(length));
  packet.SetType(MediaPacket::DTLS);

  TransportResult r = output_->SendPacket(packet);
  if (r >= 0) {
    return r;
  }

  if (r == TE_WOULDBLOCK) {
    PR_SetError(PR_WOULD_BLOCK_ERROR, 0);
  } else {
    PR_SetError(PR_IO_ERROR, 0);
  }

  return -1;
}

// Implementation of NSPR methods
static PRStatus TransportLayerClose(PRFileDesc* f) {
  f->dtor(f);
  return PR_SUCCESS;
}

static int32_t TransportLayerRead(PRFileDesc* f, void* buf, int32_t length) {
  UNIMPLEMENTED;
  return -1;
}

static int32_t TransportLayerWrite(PRFileDesc* f, const void* buf,
                                   int32_t length) {
  TransportLayerNSPRAdapter* io =
      reinterpret_cast<TransportLayerNSPRAdapter*>(f->secret);
  return io->Write(buf, length);
}

static int32_t TransportLayerAvailable(PRFileDesc* f) {
  UNIMPLEMENTED;
  return -1;
}

int64_t TransportLayerAvailable64(PRFileDesc* f) {
  UNIMPLEMENTED;
  return -1;
}

static PRStatus TransportLayerSync(PRFileDesc* f) {
  UNIMPLEMENTED;
  return PR_FAILURE;
}

static int32_t TransportLayerSeek(PRFileDesc* f, int32_t offset,
                                  PRSeekWhence how) {
  UNIMPLEMENTED;
  return -1;
}

static int64_t TransportLayerSeek64(PRFileDesc* f, int64_t offset,
                                    PRSeekWhence how) {
  UNIMPLEMENTED;
  return -1;
}

static PRStatus TransportLayerFileInfo(PRFileDesc* f, PRFileInfo* info) {
  UNIMPLEMENTED;
  return PR_FAILURE;
}

static PRStatus TransportLayerFileInfo64(PRFileDesc* f, PRFileInfo64* info) {
  UNIMPLEMENTED;
  return PR_FAILURE;
}

static int32_t TransportLayerWritev(PRFileDesc* f, const PRIOVec* iov,
                                    int32_t iov_size, PRIntervalTime to) {
  UNIMPLEMENTED;
  return -1;
}

static PRStatus TransportLayerConnect(PRFileDesc* f, const PRNetAddr* addr,
                                      PRIntervalTime to) {
  UNIMPLEMENTED;
  return PR_FAILURE;
}

static PRFileDesc* TransportLayerAccept(PRFileDesc* sd, PRNetAddr* addr,
                                        PRIntervalTime to) {
  UNIMPLEMENTED;
  return nullptr;
}

static PRStatus TransportLayerBind(PRFileDesc* f, const PRNetAddr* addr) {
  UNIMPLEMENTED;
  return PR_FAILURE;
}

static PRStatus TransportLayerListen(PRFileDesc* f, int32_t depth) {
  UNIMPLEMENTED;
  return PR_FAILURE;
}

static PRStatus TransportLayerShutdown(PRFileDesc* f, int32_t how) {
  // This is only called from NSS when we are the server and the client refuses
  // to provide a certificate.  In this case, the handshake is destined for
  // failure, so we will just let this pass.
  TransportLayerNSPRAdapter* io =
      reinterpret_cast<TransportLayerNSPRAdapter*>(f->secret);
  io->SetEnabled(false);
  return PR_SUCCESS;
}

// This function does not support peek, or waiting until `to`
static int32_t TransportLayerRecv(PRFileDesc* f, void* buf, int32_t buflen,
                                  int32_t flags, PRIntervalTime to) {
  MOZ_ASSERT(flags == 0);
  if (flags != 0) {
    PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0);
    return -1;
  }

  TransportLayerNSPRAdapter* io =
      reinterpret_cast<TransportLayerNSPRAdapter*>(f->secret);
  return io->Recv(buf, buflen);
}

// Note: this is always nonblocking and assumes a zero timeout.
static int32_t TransportLayerSend(PRFileDesc* f, const void* buf,
                                  int32_t amount, int32_t flags,
                                  PRIntervalTime to) {
  int32_t written = TransportLayerWrite(f, buf, amount);
  return written;
}

static int32_t TransportLayerRecvfrom(PRFileDesc* f, void* buf, int32_t amount,
                                      int32_t flags, PRNetAddr* addr,
                                      PRIntervalTime to) {
  UNIMPLEMENTED;
  return -1;
}

static int32_t TransportLayerSendto(PRFileDesc* f, const void* buf,
                                    int32_t amount, int32_t flags,
                                    const PRNetAddr* addr, PRIntervalTime to) {
  UNIMPLEMENTED;
  return -1;
}

static int16_t TransportLayerPoll(PRFileDesc* f, int16_t in_flags,
                                  int16_t* out_flags) {
  UNIMPLEMENTED;
  return -1;
}

static int32_t TransportLayerAcceptRead(PRFileDesc* sd, PRFileDesc** nd,
                                        PRNetAddr** raddr, void* buf,
                                        int32_t amount, PRIntervalTime t) {
  UNIMPLEMENTED;
  return -1;
}

static int32_t TransportLayerTransmitFile(PRFileDesc* sd, PRFileDesc* f,
                                          const void* headers, int32_t hlen,
                                          PRTransmitFileFlags flags,
                                          PRIntervalTime t) {
  UNIMPLEMENTED;
  return -1;
}

static PRStatus TransportLayerGetpeername(PRFileDesc* f, PRNetAddr* addr) {
  // TODO: Modify to return unique names for each channel
  // somehow, as opposed to always the same static address. The current
  // implementation messes up the session cache, which is why it's off
  // elsewhere
  addr->inet.family = PR_AF_INET;
  addr->inet.port = 0;
  addr->inet.ip = 0;

  return PR_SUCCESS;
}

static PRStatus TransportLayerGetsockname(PRFileDesc* f, PRNetAddr* addr) {
  UNIMPLEMENTED;
  return PR_FAILURE;
}

static PRStatus TransportLayerGetsockoption(PRFileDesc* f,
                                            PRSocketOptionData* opt) {
  switch (opt->option) {
    case PR_SockOpt_Nonblocking:
      opt->value.non_blocking = PR_TRUE;
      return PR_SUCCESS;
    default:
      UNIMPLEMENTED;
      break;
  }

  return PR_FAILURE;
}

// Imitate setting socket options. These are mostly noops.
static PRStatus TransportLayerSetsockoption(PRFileDesc* f,
                                            const PRSocketOptionData* opt) {
  switch (opt->option) {
    case PR_SockOpt_Nonblocking:
      return PR_SUCCESS;
    case PR_SockOpt_NoDelay:
      return PR_SUCCESS;
    default:
      UNIMPLEMENTED;
      break;
  }

  return PR_FAILURE;
}

static int32_t TransportLayerSendfile(PRFileDesc* out, PRSendFileData* in,
                                      PRTransmitFileFlags flags,
                                      PRIntervalTime to) {
  UNIMPLEMENTED;
  return -1;
}

static PRStatus TransportLayerConnectContinue(PRFileDesc* f, int16_t flags) {
  UNIMPLEMENTED;
  return PR_FAILURE;
}

static int32_t TransportLayerReserved(PRFileDesc* f) {
  UNIMPLEMENTED;
  return -1;
}

static const struct PRIOMethods TransportLayerMethods = {
    PR_DESC_LAYERED,
    TransportLayerClose,
    TransportLayerRead,
    TransportLayerWrite,
    TransportLayerAvailable,
    TransportLayerAvailable64,
    TransportLayerSync,
    TransportLayerSeek,
    TransportLayerSeek64,
    TransportLayerFileInfo,
    TransportLayerFileInfo64,
    TransportLayerWritev,
    TransportLayerConnect,
    TransportLayerAccept,
    TransportLayerBind,
    TransportLayerListen,
    TransportLayerShutdown,
    TransportLayerRecv,
    TransportLayerSend,
    TransportLayerRecvfrom,
    TransportLayerSendto,
    TransportLayerPoll,
    TransportLayerAcceptRead,
    TransportLayerTransmitFile,
    TransportLayerGetsockname,
    TransportLayerGetpeername,
    TransportLayerReserved,
    TransportLayerReserved,
    TransportLayerGetsockoption,
    TransportLayerSetsockoption,
    TransportLayerSendfile,
    TransportLayerConnectContinue,
    TransportLayerReserved,
    TransportLayerReserved,
    TransportLayerReserved,
    TransportLayerReserved};

TransportLayerDtls::~TransportLayerDtls() {
  // Destroy the NSS instance first so it can still send out an alert before
  // we disable the nspr_io_adapter_.
  ssl_fd_ = nullptr;
  nspr_io_adapter_->SetEnabled(false);
  if (timer_) {
    timer_->Cancel();
  }
}

nsresult TransportLayerDtls::InitInternal() {
  // Get the transport service as an event target
  nsresult rv;
  target_ = do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID, &rv);

  if (NS_FAILED(rv)) {
    MOZ_MTLOG(ML_ERROR, "Couldn't get socket transport service");
    return rv;
  }

  timer_ = NS_NewTimer();
  if (!timer_) {
    MOZ_MTLOG(ML_ERROR, "Couldn't get timer");
    return rv;
  }

  return NS_OK;
}

void TransportLayerDtls::WasInserted() {
  // Connect to the lower layers
  if (!Setup()) {
    TL_SET_STATE(TS_ERROR);
  }
}

// Set the permitted and default ALPN identifiers.
// The default is here to allow for peers that don't want to negotiate ALPN
// in that case, the default string will be reported from GetNegotiatedAlpn().
// Setting the default to the empty string causes the transport layer to fail
// if ALPN is not negotiated.
// Note: we only support Unicode strings here, which are encoded into UTF-8,
// even though ALPN ostensibly allows arbitrary octet sequences.
nsresult TransportLayerDtls::SetAlpn(const std::set<std::string>& alpn_allowed,
                                     const std::string& alpn_default) {
  alpn_allowed_ = alpn_allowed;
  alpn_default_ = alpn_default;

  return NS_OK;
}

nsresult TransportLayerDtls::SetVerificationAllowAll() {
  // Defensive programming
  if (verification_mode_ != VERIFY_UNSET) return NS_ERROR_ALREADY_INITIALIZED;

  verification_mode_ = VERIFY_ALLOW_ALL;

  return NS_OK;
}

nsresult TransportLayerDtls::SetVerificationDigest(const DtlsDigest& digest) {
  // Defensive programming
  if (verification_mode_ != VERIFY_UNSET &&
      verification_mode_ != VERIFY_DIGEST) {
    return NS_ERROR_ALREADY_INITIALIZED;
  }

  digests_.push_back(digest);
  verification_mode_ = VERIFY_DIGEST;
  return NS_OK;
}

void TransportLayerDtls::SetMinMaxVersion(Version min_version,
                                          Version max_version) {
  if (min_version < Version::DTLS_1_0 || min_version > Version::DTLS_1_3 ||
      max_version < Version::DTLS_1_0 || max_version > Version::DTLS_1_3 ||
      min_version > max_version || max_version < min_version) {
    return;
  }
  minVersion_ = min_version;
  maxVersion_ = max_version;
}

// TODO: make sure this is called from STS. Otherwise
// we have thread safety issues
bool TransportLayerDtls::Setup() {
  CheckThread();
  SECStatus rv;

  if (!downward_) {
    MOZ_MTLOG(ML_ERROR, "DTLS layer with nothing below. This is useless");
    return false;
  }
  nspr_io_adapter_ = MakeUnique<TransportLayerNSPRAdapter>(downward_);

  if (!identity_) {
    MOZ_MTLOG(ML_ERROR, "Can't start DTLS without an identity");
    return false;
  }

  if (verification_mode_ == VERIFY_UNSET) {
    MOZ_MTLOG(ML_ERROR,
              "Can't start DTLS without specifying a verification mode");
    return false;
  }

  if (transport_layer_identity == PR_INVALID_IO_LAYER) {
    transport_layer_identity = PR_GetUniqueIdentity("nssstreamadapter");
  }

  UniquePRFileDesc pr_fd(
      PR_CreateIOLayerStub(transport_layer_identity, &TransportLayerMethods));
  MOZ_ASSERT(pr_fd != nullptr);
  if (!pr_fd) return false;
  pr_fd->secret = reinterpret_cast<PRFilePrivate*>(nspr_io_adapter_.get());

  UniquePRFileDesc ssl_fd(DTLS_ImportFD(nullptr, pr_fd.get()));
  MOZ_ASSERT(ssl_fd != nullptr);  // This should never happen
  if (!ssl_fd) {
    return false;
  }

  Unused << pr_fd.release();  // ownership transfered to ssl_fd;

  if (role_ == CLIENT) {
    MOZ_MTLOG(ML_INFO, "Setting up DTLS as client");
    rv = SSL_GetClientAuthDataHook(ssl_fd.get(), GetClientAuthDataHook, this);
    if (rv != SECSuccess) {
      MOZ_MTLOG(ML_ERROR, "Couldn't set identity");
      return false;
    }

    if (maxVersion_ >= Version::DTLS_1_3) {
      MOZ_MTLOG(ML_INFO, "Setting DTLS1.3 supported_versions workaround");
      rv = SSL_SetDtls13VersionWorkaround(ssl_fd.get(), PR_TRUE);
      if (rv != SECSuccess) {
        MOZ_MTLOG(ML_ERROR, "Couldn't set DTLS1.3 workaround");
        return false;
      }
    }
  } else {
    MOZ_MTLOG(ML_INFO, "Setting up DTLS as server");
    // Server side
    rv = SSL_ConfigSecureServer(ssl_fd.get(), identity_->cert().get(),
                                identity_->privkey().get(),
                                identity_->auth_type());
    if (rv != SECSuccess) {
      MOZ_MTLOG(ML_ERROR, "Couldn't set identity");
      return false;
    }

    UniqueCERTCertList zero_certs(CERT_NewCertList());
    rv = SSL_SetTrustAnchors(ssl_fd.get(), zero_certs.get());
    if (rv != SECSuccess) {
      MOZ_MTLOG(ML_ERROR, "Couldn't set trust anchors");
      return false;
    }

    // Insist on a certificate from the client
    rv = SSL_OptionSet(ssl_fd.get(), SSL_REQUEST_CERTIFICATE, PR_TRUE);
    if (rv != SECSuccess) {
      MOZ_MTLOG(ML_ERROR, "Couldn't request certificate");
      return false;
    }

    rv = SSL_OptionSet(ssl_fd.get(), SSL_REQUIRE_CERTIFICATE, PR_TRUE);
    if (rv != SECSuccess) {
      MOZ_MTLOG(ML_ERROR, "Couldn't require certificate");
      return false;
    }
  }

  SSLVersionRange version_range = {static_cast<PRUint16>(minVersion_),
                                   static_cast<PRUint16>(maxVersion_)};

  rv = SSL_VersionRangeSet(ssl_fd.get(), &version_range);
  if (rv != SECSuccess) {
    MOZ_MTLOG(ML_ERROR, "Can't disable SSLv3");
    return false;
  }

  rv = SSL_OptionSet(ssl_fd.get(), SSL_ENABLE_SESSION_TICKETS, PR_FALSE);
  if (rv != SECSuccess) {
    MOZ_MTLOG(ML_ERROR, "Couldn't disable session tickets");
    return false;
  }

  rv = SSL_OptionSet(ssl_fd.get(), SSL_NO_CACHE, PR_TRUE);
  if (rv != SECSuccess) {
    MOZ_MTLOG(ML_ERROR, "Couldn't disable session caching");
    return false;
  }

  rv = SSL_OptionSet(ssl_fd.get(), SSL_ENABLE_DEFLATE, PR_FALSE);
  if (rv != SECSuccess) {
    MOZ_MTLOG(ML_ERROR, "Couldn't disable deflate");
    return false;
  }

  rv = SSL_OptionSet(ssl_fd.get(), SSL_ENABLE_RENEGOTIATION,
                     SSL_RENEGOTIATE_NEVER);
  if (rv != SECSuccess) {
    MOZ_MTLOG(ML_ERROR, "Couldn't disable renegotiation");
    return false;
  }

  rv = SSL_OptionSet(ssl_fd.get(), SSL_ENABLE_FALSE_START, PR_FALSE);
  if (rv != SECSuccess) {
    MOZ_MTLOG(ML_ERROR, "Couldn't disable false start");
    return false;
  }

  rv = SSL_OptionSet(ssl_fd.get(), SSL_NO_LOCKS, PR_TRUE);
  if (rv != SECSuccess) {
    MOZ_MTLOG(ML_ERROR, "Couldn't disable locks");
    return false;
  }

  rv = SSL_OptionSet(ssl_fd.get(), SSL_REUSE_SERVER_ECDHE_KEY, PR_FALSE);
  if (rv != SECSuccess) {
    MOZ_MTLOG(ML_ERROR, "Couldn't disable ECDHE key reuse");
    return false;
  }

  if (!SetupCipherSuites(ssl_fd)) {
    return false;
  }

  unsigned int additional_shares =
      StaticPrefs::security_tls_client_hello_send_p256_keyshare();

  const SSLNamedGroup namedGroups[] = {
      ssl_grp_ec_curve25519, ssl_grp_ec_secp256r1, ssl_grp_ec_secp384r1,
      ssl_grp_ffdhe_2048, ssl_grp_ffdhe_3072,
      // Advertise MLKEM support but do not pro-actively send a keyshare

      // Mlkem must stay the last in the list because if we don't support it
      // the amount of supported_groups will be sent without it.
      ssl_grp_kem_mlkem768x25519};

  size_t numGroups = std::size(namedGroups);
  if (!(StaticPrefs::security_tls_enable_kyber() &&
        StaticPrefs::media_webrtc_enable_pq_dtls() &&
        maxVersion_ >= Version::DTLS_1_3)) {
    // Excluding the last group of the namedGroups.
    numGroups -= 1;
  }

  rv = SSL_NamedGroupConfig(ssl_fd.get(), namedGroups, numGroups);

  if (rv != SECSuccess) {
    MOZ_MTLOG(ML_ERROR, "Couldn't set named groups");
    return false;
  }

  if (SECSuccess !=
      SSL_SendAdditionalKeyShares(ssl_fd.get(), additional_shares)) {
    MOZ_MTLOG(ML_ERROR, "Couldn't set up additional key shares");
    return false;
  }

  // Certificate validation
  rv = SSL_AuthCertificateHook(ssl_fd.get(), AuthCertificateHook,
                               reinterpret_cast<void*>(this));
  if (rv != SECSuccess) {
    MOZ_MTLOG(ML_ERROR, "Couldn't set certificate validation hook");
    return false;
  }

  if (!SetupAlpn(ssl_fd)) {
    return false;
  }

  // Now start the handshake
  rv = SSL_ResetHandshake(ssl_fd.get(), role_ == SERVER ? PR_TRUE : PR_FALSE);
  if (rv != SECSuccess) {
    MOZ_MTLOG(ML_ERROR, "Couldn't reset handshake");
    return false;
  }
  ssl_fd_ = std::move(ssl_fd);

  // Finally, get ready to receive data
  downward_->SignalStateChange.connect(this, &TransportLayerDtls::StateChange);
  downward_->SignalPacketReceived.connect(this,
                                          &TransportLayerDtls::PacketReceived);

  if (downward_->state() == TS_OPEN) {
    TL_SET_STATE(TS_CONNECTING);
    Handshake();
  }

  return true;
}

bool TransportLayerDtls::SetupAlpn(UniquePRFileDesc& ssl_fd) const {
  if (alpn_allowed_.empty()) {
    return true;
  }

  SECStatus rv = SSL_OptionSet(ssl_fd.get(), SSL_ENABLE_NPN, PR_FALSE);
  if (rv != SECSuccess) {
    MOZ_MTLOG(ML_ERROR, "Couldn't disable NPN");
    return false;
  }

  rv = SSL_OptionSet(ssl_fd.get(), SSL_ENABLE_ALPN, PR_TRUE);
  if (rv != SECSuccess) {
    MOZ_MTLOG(ML_ERROR, "Couldn't enable ALPN");
    return false;
  }

  unsigned char buf[MAX_ALPN_LENGTH];
  size_t offset = 0;
  for (const auto& tag : alpn_allowed_) {
    if ((offset + 1 + tag.length()) >= sizeof(buf)) {
      MOZ_MTLOG(ML_ERROR, "ALPN too long");
      return false;
    }
    buf[offset++] = tag.length();
    memcpy(buf + offset, tag.c_str(), tag.length());
    offset += tag.length();
  }
  rv = SSL_SetNextProtoNego(ssl_fd.get(), buf, offset);
  if (rv != SECSuccess) {
    MOZ_MTLOG(ML_ERROR, "Couldn't set ALPN string");
    return false;
  }
  return true;
}

// Ciphers we need to enable.  These are on by default in standard firefox
// builds, but can be disabled with prefs and they aren't on in our unit tests
// since that uses NSS default configuration.
//
// Only override prefs to comply with MUST statements in the security-arch doc.
// Anything outside this list is governed by the usual combination of policy
// and user preferences.
static const uint32_t EnabledCiphers[] = {
    TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
    TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA};

// Disable all NSS suites modes without PFS or with old and rusty ciphersuites.
// Anything outside this list is governed by the usual combination of policy
// and user preferences.
static const uint32_t DisabledCiphers[] = {
    // Bug 1310061: disable all SHA384 ciphers until fixed
    TLS_AES_256_GCM_SHA384,
    TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
    TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
    TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384,
    TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384,
    TLS_DHE_RSA_WITH_AES_256_GCM_SHA384,
    TLS_DHE_DSS_WITH_AES_256_GCM_SHA384,

    TLS_DHE_RSA_WITH_AES_128_CBC_SHA,
    TLS_DHE_RSA_WITH_AES_256_CBC_SHA,

    TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA,
    TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA,
    TLS_ECDHE_ECDSA_WITH_RC4_128_SHA,
    TLS_ECDHE_RSA_WITH_RC4_128_SHA,

    TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA,
    TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA,
    TLS_DHE_DSS_WITH_RC4_128_SHA,

    TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA,
    TLS_ECDH_RSA_WITH_AES_128_CBC_SHA,
    TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA,
    TLS_ECDH_RSA_WITH_AES_256_CBC_SHA,
    TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA,
    TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA,
    TLS_ECDH_ECDSA_WITH_RC4_128_SHA,
    TLS_ECDH_RSA_WITH_RC4_128_SHA,

    TLS_RSA_WITH_AES_128_GCM_SHA256,
    TLS_RSA_WITH_AES_256_GCM_SHA384,
    TLS_RSA_WITH_AES_128_CBC_SHA,
    TLS_RSA_WITH_AES_128_CBC_SHA256,
    TLS_RSA_WITH_CAMELLIA_128_CBC_SHA,
    TLS_RSA_WITH_AES_256_CBC_SHA,
    TLS_RSA_WITH_AES_256_CBC_SHA256,
    TLS_RSA_WITH_CAMELLIA_256_CBC_SHA,
    TLS_RSA_WITH_SEED_CBC_SHA,
    TLS_RSA_WITH_3DES_EDE_CBC_SHA,
    TLS_RSA_WITH_RC4_128_SHA,
    TLS_RSA_WITH_RC4_128_MD5,

    TLS_DHE_RSA_WITH_DES_CBC_SHA,
    TLS_DHE_DSS_WITH_DES_CBC_SHA,
    TLS_RSA_WITH_DES_CBC_SHA,

    TLS_ECDHE_ECDSA_WITH_NULL_SHA,
    TLS_ECDHE_RSA_WITH_NULL_SHA,
    TLS_ECDH_ECDSA_WITH_NULL_SHA,
    TLS_ECDH_RSA_WITH_NULL_SHA,
    TLS_RSA_WITH_NULL_SHA,
    TLS_RSA_WITH_NULL_SHA256,
    TLS_RSA_WITH_NULL_MD5,
};

bool TransportLayerDtls::SetupCipherSuites(UniquePRFileDesc& ssl_fd) {
  SECStatus rv;

  // Set the SRTP ciphers
  if (!enabled_srtp_ciphers_.empty()) {
    rv = SSL_InstallExtensionHooks(ssl_fd.get(), ssl_use_srtp_xtn,
                                   TransportLayerDtls::WriteSrtpXtn, this,
                                   TransportLayerDtls::HandleSrtpXtn, this);
    if (rv != SECSuccess) {
      MOZ_MTLOG(ML_ERROR, LAYER_INFO << "unable to set SRTP extension handler");
      return false;
    }
  }

  for (const auto& cipher : EnabledCiphers) {
    MOZ_MTLOG(ML_DEBUG, LAYER_INFO << "Enabling: " << cipher);
    rv = SSL_CipherPrefSet(ssl_fd.get(), cipher, PR_TRUE);
    if (rv != SECSuccess) {
      MOZ_MTLOG(ML_ERROR, LAYER_INFO << "Unable to enable suite: " << cipher);
      return false;
    }
  }

  for (const auto& cipher : DisabledCiphers) {
    MOZ_MTLOG(ML_DEBUG, LAYER_INFO << "Disabling: " << cipher);

    PRBool enabled = false;
    rv = SSL_CipherPrefGet(ssl_fd.get(), cipher, &enabled);
    if (rv != SECSuccess) {
      MOZ_MTLOG(ML_NOTICE, LAYER_INFO << "Unable to check if suite is enabled: "
                                      << cipher);
      return false;
    }
    if (enabled) {
      rv = SSL_CipherPrefSet(ssl_fd.get(), cipher, PR_FALSE);
      if (rv != SECSuccess) {
        MOZ_MTLOG(ML_NOTICE,
                  LAYER_INFO << "Unable to disable suite: " << cipher);
        return false;
      }
    }
  }

  return true;
}

nsresult TransportLayerDtls::GetCipherSuite(uint16_t* cipherSuite) const {
  CheckThread();
  if (!cipherSuite) {
    MOZ_MTLOG(ML_ERROR, LAYER_INFO << "GetCipherSuite passed a nullptr");
    return NS_ERROR_NULL_POINTER;
  }
  if (state_ != TS_OPEN) {
    return NS_ERROR_NOT_AVAILABLE;
  }
  SSLChannelInfo info;
  SECStatus rv = SSL_GetChannelInfo(ssl_fd_.get(), &info, sizeof(info));
  if (rv != SECSuccess) {
    MOZ_MTLOG(ML_NOTICE, LAYER_INFO << "GetCipherSuite can't get channel info");
    return NS_ERROR_FAILURE;
  }
  *cipherSuite = info.cipherSuite;
  return NS_OK;
}

std::vector<uint16_t> TransportLayerDtls::GetDefaultSrtpCiphers() {
  std::vector<uint16_t> ciphers;

  ciphers.push_back(kDtlsSrtpAeadAes128Gcm);
  // Since we don't support DTLS 1.3 or SHA384 ciphers (see bug 1312976)
  // we don't really enough entropy to prefer this over 128 bit
  ciphers.push_back(kDtlsSrtpAeadAes256Gcm);
  ciphers.push_back(kDtlsSrtpAes128CmHmacSha1_80);
#ifndef NIGHTLY_BUILD
  // To support bug 1491583 lets try to find out if we get bug reports if we no
  // longer offer this in Nightly builds.
  ciphers.push_back(kDtlsSrtpAes128CmHmacSha1_32);
#endif

  return ciphers;
}

void TransportLayerDtls::StateChange(TransportLayer* layer, State state) {
  switch (state) {
    case TS_NONE:
      MOZ_ASSERT(false);  // Can't happen
      break;

    case TS_INIT:
      MOZ_MTLOG(ML_ERROR,
                LAYER_INFO << "State change of lower layer to INIT forbidden");
      TL_SET_STATE(TS_ERROR);
      break;

    case TS_CONNECTING:
      MOZ_MTLOG(ML_INFO, LAYER_INFO << "Lower layer is connecting.");
      break;

    case TS_OPEN:
      if (timer_) {
        MOZ_MTLOG(ML_INFO,
                  LAYER_INFO << "Lower layer is now open; starting TLS");
        timer_->Cancel();
        timer_->SetTarget(target_);
        // Async, since the ICE layer might need to send a STUN response, and we
        // don't want the handshake to start until that is sent.
        timer_->InitWithNamedFuncCallback(TimerCallback, this, 0,
                                          nsITimer::TYPE_ONE_SHOT,
                                          "TransportLayerDtls::TimerCallback");
        TL_SET_STATE(TS_CONNECTING);
      } else {
        // We have already completed DTLS. Can happen if the ICE layer failed
        // due to a loss of network, and then recovered.
        TL_SET_STATE(TS_OPEN);
      }
      break;

    case TS_CLOSED:
      MOZ_MTLOG(ML_INFO, LAYER_INFO << "Lower layer is now closed");
      TL_SET_STATE(TS_CLOSED);
      break;

    case TS_ERROR:
      MOZ_MTLOG(ML_ERROR, LAYER_INFO << "Lower layer experienced an error");
      TL_SET_STATE(TS_ERROR);
      break;
  }
}

void TransportLayerDtls::Handshake() {
  if (!timer_) {
    // We are done with DTLS, regardless of the state changes of lower layers
    return;
  }

  if (!handshakeTelemetryRecorded) {
    RecordStartedHandshakeTelemetry();
    handshakeTelemetryRecorded = true;
  }

  // Clear the retransmit timer
  timer_->Cancel();

  MOZ_ASSERT(state_ == TS_CONNECTING);

  SECStatus rv = SSL_ForceHandshake(ssl_fd_.get());

  if (rv == SECSuccess) {
    MOZ_MTLOG(ML_NOTICE, LAYER_INFO << "****** SSL handshake completed ******");
    if (!cert_ok_) {
      MOZ_MTLOG(ML_ERROR, LAYER_INFO << "Certificate check never occurred");
      RecordHandshakeCompletionTelemetry("CERT_FAILURE");
      TL_SET_STATE(TS_ERROR);
      return;
    }
    if (!CheckAlpn()) {
      // Despite connecting, the connection doesn't have a valid ALPN label.
      // Forcibly close the connection so that the peer isn't left hanging
      // (assuming the close_notify isn't dropped).
      ssl_fd_ = nullptr;
      RecordHandshakeCompletionTelemetry("ALPN_FAILURE");
      TL_SET_STATE(TS_ERROR);
      return;
    }

    RecordHandshakeCompletionTelemetry("SUCCESS");
    TL_SET_STATE(TS_OPEN);

    RecordTlsTelemetry();
    timer_ = nullptr;
  } else {
    int32_t err = PR_GetError();
    switch (err) {
      case SSL_ERROR_RX_MALFORMED_HANDSHAKE:
        MOZ_MTLOG(ML_ERROR, LAYER_INFO << "Malformed DTLS message; ignoring");
        // If this were TLS (and not DTLS), this would be fatal, but
        // here we're required to ignore bad messages, so fall through
        [[fallthrough]];
      case PR_WOULD_BLOCK_ERROR:
        MOZ_MTLOG(ML_NOTICE, LAYER_INFO << "Handshake would have blocked");
        PRIntervalTime timeout;
        rv = DTLS_GetHandshakeTimeout(ssl_fd_.get(), &timeout);
        if (rv == SECSuccess) {
          uint32_t timeout_ms = PR_IntervalToMilliseconds(timeout);

          MOZ_MTLOG(ML_DEBUG,
                    LAYER_INFO << "Setting DTLS timeout to " << timeout_ms);
          timer_->SetTarget(target_);
          timer_->InitWithNamedFuncCallback(
              TimerCallback, this, timeout_ms, nsITimer::TYPE_ONE_SHOT,
              "TransportLayerDtls::TimerCallback");
        }
        break;
      default:
        const char* err_msg = PR_ErrorToName(err);
        MOZ_MTLOG(ML_ERROR, LAYER_INFO << "DTLS handshake error " << err << " ("
                                       << err_msg << ")");
        RecordHandshakeCompletionTelemetry(err_msg);
        TL_SET_STATE(TS_ERROR);
        break;
    }
  }
}

// Checks if ALPN was negotiated correctly and returns false if it wasn't.
// After this returns successfully, alpn_ will be set to the negotiated
// protocol.
bool TransportLayerDtls::CheckAlpn() {
  if (alpn_allowed_.empty()) {
    return true;
  }

  SSLNextProtoState alpnState;
  char chosenAlpn[MAX_ALPN_LENGTH];
  unsigned int chosenAlpnLen;
  SECStatus rv = SSL_GetNextProto(ssl_fd_.get(), &alpnState,
                                  reinterpret_cast<unsigned char*>(chosenAlpn),
                                  &chosenAlpnLen, sizeof(chosenAlpn));
  if (rv != SECSuccess) {
    MOZ_MTLOG(ML_ERROR, LAYER_INFO << "ALPN error");
    return false;
  }
  switch (alpnState) {
    case SSL_NEXT_PROTO_SELECTED:
    case SSL_NEXT_PROTO_NEGOTIATED:
      break;  // OK

    case SSL_NEXT_PROTO_NO_SUPPORT:
      MOZ_MTLOG(ML_NOTICE,
                LAYER_INFO << "ALPN not negotiated, "
                           << (alpn_default_.empty() ? "failing"
                                                     : "selecting default"));
      alpn_ = alpn_default_;
      return !alpn_.empty();

    case SSL_NEXT_PROTO_NO_OVERLAP:
      // This only happens if there is a custom NPN/ALPN callback installed and
      // that callback doesn't properly handle ALPN.
      MOZ_MTLOG(ML_ERROR, LAYER_INFO << "error in ALPN selection callback");
      return false;

    case SSL_NEXT_PROTO_EARLY_VALUE:
      MOZ_CRASH("Unexpected 0-RTT ALPN value");
      return false;
  }

  // Warning: NSS won't null terminate the ALPN string for us.
  std::string chosen(chosenAlpn, chosenAlpnLen);
  MOZ_MTLOG(ML_NOTICE, LAYER_INFO << "Selected ALPN string: " << chosen);
  if (alpn_allowed_.find(chosen) == alpn_allowed_.end()) {
    // Maybe our peer chose a protocol we didn't offer (when we are client), or
    // something is seriously wrong.
    std::ostringstream ss;
    for (auto i = alpn_allowed_.begin(); i != alpn_allowed_.end(); ++i) {
      ss << (i == alpn_allowed_.begin() ? " '" : ", '") << *i << "'";
    }
    MOZ_MTLOG(ML_ERROR, LAYER_INFO << "Bad ALPN string: '" << chosen
                                   << "'; permitted:" << ss.str());
    return false;
  }
  alpn_ = chosen;
  return true;
}

void TransportLayerDtls::PacketReceived(TransportLayer* layer,
                                        MediaPacket& packet) {
  CheckThread();
  MOZ_MTLOG(ML_DEBUG, LAYER_INFO << "PacketReceived(" << packet.len() << ")");

  if (state_ != TS_CONNECTING && state_ != TS_OPEN) {
    MOZ_MTLOG(ML_DEBUG,
              LAYER_INFO << "Discarding packet in inappropriate state");
    return;
  }

  if (!packet.data()) {
    // Something ate this, probably the SRTP layer
    return;
  }

  if (packet.type() != MediaPacket::DTLS) {
    return;
  }

  nspr_io_adapter_->PacketReceived(packet);
  GetDecryptedPackets();
}

void TransportLayerDtls::GetDecryptedPackets() {
  // If we're still connecting, try to handshake
  if (state_ == TS_CONNECTING) {
    Handshake();
  }

  // Now try a recv if we're open, since there might be data left
  if (state_ == TS_OPEN) {
    int32_t rv;
    // One packet might contain several DTLS packets
    do {
      // nICEr uses a 9216 bytes buffer to allow support for jumbo frames
      // Can we peek to get a better idea of the actual size?
      static const size_t kBufferSize = 9216;
      auto buffer = MakeUnique<uint8_t[]>(kBufferSize);
      rv = PR_Recv(ssl_fd_.get(), buffer.get(), kBufferSize, 0,
                   PR_INTERVAL_NO_WAIT);
      if (rv > 0) {
        // We have data
        MOZ_MTLOG(ML_DEBUG, LAYER_INFO << "Read " << rv << " bytes from NSS");
        MediaPacket packet;
        packet.SetType(MediaPacket::SCTP);
        packet.Take(std::move(buffer), static_cast<size_t>(rv));
        SignalPacketReceived(this, packet);
      } else if (rv == 0) {
        TL_SET_STATE(TS_CLOSED);
      } else {
        int32_t err = PR_GetError();

        if (err == PR_WOULD_BLOCK_ERROR) {
          // This gets ignored
          MOZ_MTLOG(ML_DEBUG, LAYER_INFO << "Receive would have blocked");
        } else {
          MOZ_MTLOG(ML_NOTICE, LAYER_INFO << "NSS Error " << err);
          TL_SET_STATE(TS_ERROR);
        }
      }
    } while (rv > 0);
  }
}

void TransportLayerDtls::SetState(State state, const char* file,
                                  unsigned line) {
  if (timer_) {
    switch (state) {
      case TS_NONE:
      case TS_INIT:
        MOZ_ASSERT(false);
        break;
      case TS_CONNECTING:
        break;
      case TS_OPEN:
      case TS_CLOSED:
      case TS_ERROR:
        timer_->Cancel();
        break;
    }
  }

  TransportLayer::SetState(state, file, line);
}

TransportResult TransportLayerDtls::SendPacket(MediaPacket& packet) {
  CheckThread();
  if (state_ != TS_OPEN) {
    MOZ_MTLOG(ML_ERROR,
              LAYER_INFO << "Can't call SendPacket() in state " << state_);
    return TE_ERROR;
  }

  int32_t rv = PR_Send(ssl_fd_.get(), packet.data(), packet.len(), 0,
                       PR_INTERVAL_NO_WAIT);

  if (rv > 0) {
    // We have data
    MOZ_MTLOG(ML_DEBUG, LAYER_INFO << "Wrote " << rv << " bytes to SSL Layer");
    return rv;
  }

  if (rv == 0) {
    TL_SET_STATE(TS_CLOSED);
    return 0;
  }

  int32_t err = PR_GetError();

  if (err == PR_WOULD_BLOCK_ERROR) {
    // This gets ignored
    MOZ_MTLOG(ML_DEBUG, LAYER_INFO << "Send would have blocked");
    return TE_WOULDBLOCK;
  }

  MOZ_MTLOG(ML_NOTICE, LAYER_INFO << "NSS Error " << err);
  TL_SET_STATE(TS_ERROR);
  return TE_ERROR;
}

SECStatus TransportLayerDtls::GetClientAuthDataHook(
    void* arg, PRFileDesc* fd, CERTDistNames* caNames,
    CERTCertificate** pRetCert, SECKEYPrivateKey** pRetKey) {
  MOZ_MTLOG(ML_DEBUG, "Server requested client auth");

  TransportLayerDtls* stream = reinterpret_cast<TransportLayerDtls*>(arg);
  stream->CheckThread();

  if (!stream->identity_) {
    MOZ_MTLOG(ML_ERROR, "No identity available");
    PR_SetError(SSL_ERROR_NO_CERTIFICATE, 0);
    return SECFailure;
  }

  *pRetCert = CERT_DupCertificate(stream->identity_->cert().get());
  if (!*pRetCert) {
    PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
    return SECFailure;
  }

  *pRetKey = SECKEY_CopyPrivateKey(stream->identity_->privkey().get());
  if (!*pRetKey) {
    CERT_DestroyCertificate(*pRetCert);
    *pRetCert = nullptr;
    PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
    return SECFailure;
  }

  return SECSuccess;
}

nsresult TransportLayerDtls::SetSrtpCiphers(
    const std::vector<uint16_t>& ciphers) {
  enabled_srtp_ciphers_ = std::move(ciphers);
  return NS_OK;
}

nsresult TransportLayerDtls::GetSrtpCipher(uint16_t* cipher) const {
  CheckThread();
  if (srtp_cipher_ == 0) {
    return NS_ERROR_NOT_AVAILABLE;
  }
  *cipher = srtp_cipher_;
  return NS_OK;
}

static uint8_t* WriteUint16(uint8_t* cursor, uint16_t v) {
  *cursor++ = v >> 8;
  *cursor++ = v & 0xff;
  return cursor;
}

static SSLHandshakeType SrtpXtnServerMessage(PRFileDesc* fd) {
  SSLPreliminaryChannelInfo preinfo;
  SECStatus rv = SSL_GetPreliminaryChannelInfo(fd, &preinfo, sizeof(preinfo));
  if (rv != SECSuccess) {
    MOZ_ASSERT(false"Can't get version info");
    return ssl_hs_client_hello;
  }
  return (preinfo.protocolVersion >= SSL_LIBRARY_VERSION_TLS_1_3)
             ? ssl_hs_encrypted_extensions
             : ssl_hs_server_hello;
}

/* static */
PRBool TransportLayerDtls::WriteSrtpXtn(PRFileDesc* fd,
                                        SSLHandshakeType message, uint8_t* data,
                                        unsigned int* len, unsigned int max_len,
                                        void* arg) {
  auto self = reinterpret_cast<TransportLayerDtls*>(arg);

  // ClientHello: send all supported versions.
  if (message == ssl_hs_client_hello) {
    MOZ_ASSERT(self->role_ == CLIENT);
    MOZ_ASSERT(self->enabled_srtp_ciphers_.size(), "Haven't enabled SRTP");
    // We will take 2 octets for each cipher, plus a 2 octet length and 1 octet
    // for the length of the empty MKI.
    if (max_len < self->enabled_srtp_ciphers_.size() * 2 + 3) {
      MOZ_ASSERT(false"Not enough space to send SRTP extension");
      return false;
    }
    uint8_t* cursor = WriteUint16(data, self->enabled_srtp_ciphers_.size() * 2);
    for (auto cs : self->enabled_srtp_ciphers_) {
      cursor = WriteUint16(cursor, cs);
    }
    *cursor++ = 0;  // MKI is empty
    *len = cursor - data;
    return true;
  }

  if (message == SrtpXtnServerMessage(fd)) {
    MOZ_ASSERT(self->role_ == SERVER);
    if (!self->srtp_cipher_) {
      // Not negotiated. Definitely bad, but the connection can fail later.
      return false;
    }
    if (max_len < 5) {
      MOZ_ASSERT(false"Not enough space to send SRTP extension");
      return false;
    }

    uint8_t* cursor = WriteUint16(data, 2);  // Length = 2.
    cursor = WriteUint16(cursor, self->srtp_cipher_);
    *cursor++ = 0;  // No MKI
    *len = cursor - data;
    return true;
  }

  return false;
}

class TlsParser {
 public:
  TlsParser(const uint8_t* data, size_t len) : cursor_(data), remaining_(len) {}

  bool error() const { return error_; }
  size_t remaining() const { return remaining_; }

  template <typename T,
            class = typename std::enable_if<std::is_unsigned<T>::value>::type>
  void Read(T* v, size_t sz = sizeof(T)) {
    MOZ_ASSERT(sz <= sizeof(T),
               "Type is too small to hold the value requested");
    if (remaining_ < sz) {
      error_ = true;
      return;
    }

    T result = 0;
    for (size_t i = 0; i < sz; ++i) {
      result = (result << 8) | *cursor_++;
      remaining_--;
    }
    *v = result;
  }

  template <typename T,
            class = typename std::enable_if<std::is_unsigned<T>::value>::type>
  void ReadVector(std::vector<T>* v, size_t w) {
    MOZ_ASSERT(v->empty(), "vector needs to be empty");

    uint32_t len;
    Read(&len, w);
    if (error_ || len % sizeof(T) != 0 || len > remaining_) {
      error_ = true;
      return;
    }

    size_t count = len / sizeof(T);
    v->reserve(count);
    for (T i = 0; !error_ && i < count; ++i) {
      T item;
      Read(&item);
      if (!error_) {
        v->push_back(item);
      }
    }
  }

  void Skip(size_t n) {
    if (remaining_ < n) {
      error_ = true;
    } else {
      cursor_ += n;
      remaining_ -= n;
    }
  }

  size_t SkipVector(size_t w) {
    uint32_t len = 0;
    Read(&len, w);
    Skip(len);
    return len;
  }

 private:
  const uint8_t* cursor_;
  size_t remaining_;
  bool error_ = false;
};

/* static */
SECStatus TransportLayerDtls::HandleSrtpXtn(
    PRFileDesc* fd, SSLHandshakeType message, const uint8_t* data,
    unsigned int len, SSLAlertDescription* alert, void* arg) {
  static const uint8_t kTlsAlertHandshakeFailure = 40;
  static const uint8_t kTlsAlertIllegalParameter = 47;
  static const uint8_t kTlsAlertDecodeError = 50;
  static const uint8_t kTlsAlertUnsupportedExtension = 110;

  auto self = reinterpret_cast<TransportLayerDtls*>(arg);

  // Parse the extension.
  TlsParser parser(data, len);
  std::vector<uint16_t> advertised;
  parser.ReadVector(&advertised, 2);
  size_t mki_len = parser.SkipVector(1);
  if (parser.error() || parser.remaining() > 0) {
    *alert = kTlsAlertDecodeError;
    return SECFailure;
  }

  if (message == ssl_hs_client_hello) {
    MOZ_ASSERT(self->role_ == SERVER);
    if (self->enabled_srtp_ciphers_.empty()) {
      // We don't have SRTP enabled, which is probably bad, but no sense in
      // having the handshake fail at this point, let the client decide if this
      // is a problem.
      return SECSuccess;
    }

    for (auto supported : self->enabled_srtp_ciphers_) {
      auto it = std::find(advertised.begin(), advertised.end(), supported);
      if (it != advertised.end()) {
        self->srtp_cipher_ = supported;
        return SECSuccess;
      }
    }

    // No common cipher.
    *alert = kTlsAlertHandshakeFailure;
    return SECFailure;
  }

  if (message == SrtpXtnServerMessage(fd)) {
    MOZ_ASSERT(self->role_ == CLIENT);
    if (advertised.size() != 1 || mki_len > 0) {
      *alert = kTlsAlertIllegalParameter;
      return SECFailure;
    }
    self->srtp_cipher_ = advertised[0];
    return SECSuccess;
  }

  *alert = kTlsAlertUnsupportedExtension;
  return SECFailure;
}

nsresult TransportLayerDtls::ExportKeyingMaterial(const std::string& label,
                                                  bool use_context,
                                                  const std::string& context,
                                                  unsigned char* out,
                                                  unsigned int outlen) {
  CheckThread();
  if (state_ != TS_OPEN) {
    MOZ_ASSERT(false"Transport must be open for ExportKeyingMaterial");
    return NS_ERROR_NOT_AVAILABLE;
  }
  SECStatus rv = SSL_ExportKeyingMaterial(
      ssl_fd_.get(), label.c_str(), label.size(), use_context,
      reinterpret_cast<const unsigned char*>(context.c_str()), context.size(),
      out, outlen);
  if (rv != SECSuccess) {
    MOZ_MTLOG(ML_ERROR, "Couldn't export SSL keying material");
    return NS_ERROR_FAILURE;
  }

  return NS_OK;
}

SECStatus TransportLayerDtls::AuthCertificateHook(void* arg, PRFileDesc* fd,
                                                  PRBool checksig,
                                                  PRBool isServer) {
  TransportLayerDtls* stream = reinterpret_cast<TransportLayerDtls*>(arg);
  stream->CheckThread();
  return stream->AuthCertificateHook(fd, checksig, isServer);
}

SECStatus TransportLayerDtls::CheckDigest(
    const DtlsDigest& digest, UniqueCERTCertificate& peer_cert) const {
  DtlsDigest computed_digest(digest.algorithm_);

  MOZ_MTLOG(ML_DEBUG,
            LAYER_INFO << "Checking digest, algorithm=" << digest.algorithm_);
  nsresult res = DtlsIdentity::ComputeFingerprint(peer_cert, &computed_digest);
  if (NS_FAILED(res)) {
    MOZ_MTLOG(ML_ERROR, "Could not compute peer fingerprint for digest "
                            << digest.algorithm_);
    // Go to end
    PR_SetError(SSL_ERROR_BAD_CERTIFICATE, 0);
    return SECFailure;
  }

  if (computed_digest != digest) {
    MOZ_MTLOG(ML_ERROR, "Digest does not match");
    PR_SetError(SSL_ERROR_BAD_CERTIFICATE, 0);
    return SECFailure;
  }

  return SECSuccess;
}

SECStatus TransportLayerDtls::AuthCertificateHook(PRFileDesc* fd,
                                                  PRBool checksig,
                                                  PRBool isServer) {
  CheckThread();
  UniqueCERTCertificate peer_cert(SSL_PeerCertificate(fd));

  // We are not set up to take this being called multiple
  // times. Change this if we ever add renegotiation.
  MOZ_ASSERT(!auth_hook_called_);
  if (auth_hook_called_) {
    PR_SetError(PR_UNKNOWN_ERROR, 0);
    return SECFailure;
  }
  auth_hook_called_ = true;

  MOZ_ASSERT(verification_mode_ != VERIFY_UNSET);

  switch (verification_mode_) {
    case VERIFY_UNSET:
      // Break out to error exit
      PR_SetError(PR_UNKNOWN_ERROR, 0);
      break;

    case VERIFY_ALLOW_ALL:
      cert_ok_ = true;
      return SECSuccess;

    case VERIFY_DIGEST: {
      MOZ_ASSERT(!digests_.empty());
      // Check all the provided digests

      // Checking functions call PR_SetError()
      SECStatus rv = SECFailure;
      for (auto digest : digests_) {
        rv = CheckDigest(digest, peer_cert);

        // Matches a digest, we are good to go
        if (rv == SECSuccess) {
          cert_ok_ = true;
          return SECSuccess;
        }
      }
    } break;
    default:
      MOZ_CRASH();  // Can't happen
  }

  return SECFailure;
}

void TransportLayerDtls::TimerCallback(nsITimer* timer, void* arg) {
  TransportLayerDtls* dtls = reinterpret_cast<TransportLayerDtls*>(arg);

  MOZ_MTLOG(ML_DEBUG, "DTLS timer expired");

  dtls->Handshake();
}

void TransportLayerDtls::RecordHandshakeCompletionTelemetry(
    const char* aResult) {
  if (role_ == CLIENT) {
    mozilla::glean::webrtcdtls::client_handshake_result.Get(nsCString(aResult))
        .Add(1);
  } else {
    mozilla::glean::webrtcdtls::server_handshake_result.Get(nsCString(aResult))
        .Add(1);
  }
}

void TransportLayerDtls::RecordStartedHandshakeTelemetry() {
  if (role_ == CLIENT) {
    mozilla::glean::webrtcdtls::client_handshake_started_counter.Add(1);
  } else {
    mozilla::glean::webrtcdtls::server_handshake_started_counter.Add(1);
  }
}

void TransportLayerDtls::RecordTlsTelemetry() {
  MOZ_ASSERT(state_ == TS_OPEN);
  SSLChannelInfo info;
  SECStatus ss = SSL_GetChannelInfo(ssl_fd_.get(), &info, sizeof(info));
  if (ss != SECSuccess) {
    MOZ_MTLOG(ML_NOTICE,
              LAYER_INFO << "RecordTlsTelemetry failed to get channel info");
    return;
  }

  switch (info.protocolVersion) {
    case SSL_LIBRARY_VERSION_TLS_1_1:
      mozilla::glean::webrtcdtls::protocol_version.Get("1.0"_ns).Add(1);
      break;
    case SSL_LIBRARY_VERSION_TLS_1_2:
      mozilla::glean::webrtcdtls::protocol_version.Get("1.2"_ns).Add(1);
      break;
    case SSL_LIBRARY_VERSION_TLS_1_3:
      mozilla::glean::webrtcdtls::protocol_version.Get("1.3"_ns).Add(1);
      break;
    default:
      MOZ_CRASH("Unknown SSL version");
  }

  {
    std::ostringstream oss;
    // Record TLS cipher-suite ID as a string (eg;
    // TLS_DHE_RSA_WITH_AES_128_CBC_SHA is 0x0033)
    oss << "0x" << std::setfill('0') << std::setw(4) << std::hex
        << info.cipherSuite;
    mozilla::glean::webrtcdtls::cipher.Get(nsCString(oss.str().c_str())).Add(1);
    MOZ_MTLOG(ML_DEBUG, "cipher: " << oss.str());
  }

  uint16_t cipher;
  nsresult rv = GetSrtpCipher(&cipher);

  if (NS_FAILED(rv)) {
    MOZ_MTLOG(ML_DEBUG, "No SRTP cipher suite");
    return;
  }

  {
    std::ostringstream oss;
    // Record SRTP cipher-suite ID as a string (eg;
    // SRTP_AES128_CM_HMAC_SHA1_80 is 0x0001)
    oss << "0x" << std::setfill('0') << std::setw(4) << std::hex << cipher;
    mozilla::glean::webrtcdtls::srtp_cipher.Get(nsCString(oss.str().c_str()))
        .Add(1);
    MOZ_MTLOG(ML_DEBUG, "srtp cipher: " << oss.str());
  }
}

}  // namespace mozilla

Messung V0.5
C=92 H=97 G=94

¤ Dauer der Verarbeitung: 0.31 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.