tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

RTCCertificate.cpp (15667B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
      3 /* This Source Code Form is subject to the terms of the Mozilla Public
      4 * License, v. 2.0. If a copy of the MPL was not distributed with this
      5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 #include "mozilla/dom/RTCCertificate.h"
      8 
      9 #include <cstdio>
     10 #include <cstring>
     11 #include <new>
     12 #include <utility>
     13 
     14 #include "ErrorList.h"
     15 #include "MainThreadUtils.h"
     16 #include "cert.h"
     17 #include "cryptohi.h"
     18 #include "js/StructuredClone.h"
     19 #include "js/TypeDecls.h"
     20 #include "js/Value.h"
     21 #include "keyhi.h"
     22 #include "mozilla/ErrorResult.h"
     23 #include "mozilla/OwningNonNull.h"
     24 #include "mozilla/dom/BindingDeclarations.h"
     25 #include "mozilla/dom/CryptoBuffer.h"
     26 #include "mozilla/dom/CryptoKey.h"
     27 #include "mozilla/dom/KeyAlgorithmBinding.h"
     28 #include "mozilla/dom/KeyAlgorithmProxy.h"
     29 #include "mozilla/dom/Promise.h"
     30 #include "mozilla/dom/RTCCertificateBinding.h"
     31 #include "mozilla/dom/StructuredCloneHolder.h"
     32 #include "mozilla/dom/SubtleCryptoBinding.h"
     33 #include "mozilla/dom/UnionTypes.h"
     34 #include "mozilla/dom/WebCryptoCommon.h"
     35 #include "mozilla/dom/WebCryptoTask.h"
     36 #include "mozilla/fallible.h"
     37 #include "nsDebug.h"
     38 #include "nsError.h"
     39 #include "nsLiteralString.h"
     40 #include "nsStringFlags.h"
     41 #include "nsStringFwd.h"
     42 #include "nsTLiteralString.h"
     43 #include "pk11pub.h"
     44 #include "plarena.h"
     45 #include "sdp/SdpAttribute.h"
     46 #include "secasn1.h"
     47 #include "secasn1t.h"
     48 #include "seccomon.h"
     49 #include "secmodt.h"
     50 #include "secoid.h"
     51 #include "secoidt.h"
     52 #include "transport/dtlsidentity.h"
     53 #include "xpcpublic.h"
     54 
     55 namespace mozilla::dom {
     56 
     57 #define RTCCERTIFICATE_SC_VERSION 0x00000001
     58 
     59 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(RTCCertificate, mGlobal)
     60 NS_IMPL_CYCLE_COLLECTING_ADDREF(RTCCertificate)
     61 NS_IMPL_CYCLE_COLLECTING_RELEASE(RTCCertificate)
     62 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(RTCCertificate)
     63  NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
     64  NS_INTERFACE_MAP_ENTRY(nsISupports)
     65 NS_INTERFACE_MAP_END
     66 
     67 // Note: explicit casts necessary to avoid
     68 //       warning C4307: '*' : integral constant overflow
     69 #define ONE_DAY                                 \
     70  PRTime(PR_USEC_PER_SEC) * PRTime(60)  /*sec*/ \
     71      * PRTime(60) /*min*/ * PRTime(24) /*hours*/
     72 #define EXPIRATION_DEFAULT ONE_DAY* PRTime(30)
     73 #define EXPIRATION_SLACK ONE_DAY
     74 #define EXPIRATION_MAX ONE_DAY* PRTime(365) /*year*/
     75 
     76 const size_t RTCCertificateCommonNameLength = 16;
     77 const size_t RTCCertificateMinRsaSize = 1024;
     78 
     79 class GenerateRTCCertificateTask : public GenerateAsymmetricKeyTask {
     80 public:
     81  GenerateRTCCertificateTask(nsIGlobalObject* aGlobal, JSContext* aCx,
     82                             const ObjectOrString& aAlgorithm,
     83                             const Sequence<nsString>& aKeyUsages,
     84                             PRTime aExpires)
     85      : GenerateAsymmetricKeyTask(aGlobal, aCx, aAlgorithm, true, aKeyUsages),
     86        mExpires(aExpires),
     87        mAuthType(ssl_kea_null),
     88        mCertificate(nullptr),
     89        mSignatureAlg(SEC_OID_UNKNOWN) {
     90    if (NS_FAILED(mEarlyRv)) {
     91      // webrtc-pc says to throw NotSupportedError if we have passed "an
     92      // algorithm that the user agent cannot or will not use to generate a
     93      // certificate". This catches these cases.
     94      mEarlyRv = NS_ERROR_DOM_NOT_SUPPORTED_ERR;
     95    }
     96  }
     97 
     98 private:
     99  PRTime mExpires;
    100  SSLKEAType mAuthType;
    101  UniqueCERTCertificate mCertificate;
    102  SECOidTag mSignatureAlg;
    103 
    104  static CERTName* GenerateRandomName(PK11SlotInfo* aSlot) {
    105    uint8_t randomName[RTCCertificateCommonNameLength];
    106    SECStatus rv =
    107        PK11_GenerateRandomOnSlot(aSlot, randomName, sizeof(randomName));
    108    if (rv != SECSuccess) {
    109      return nullptr;
    110    }
    111 
    112    char buf[sizeof(randomName) * 2 + 4];
    113    strncpy(buf, "CN=", 4);
    114    for (size_t i = 0; i < sizeof(randomName); ++i) {
    115      snprintf(&buf[i * 2 + 3], 3, "%.2x", randomName[i]);
    116    }
    117    buf[sizeof(buf) - 1] = '\0';
    118 
    119    return CERT_AsciiToName(buf);
    120  }
    121 
    122  nsresult GenerateCertificate() {
    123    UniquePK11SlotInfo slot(PK11_GetInternalSlot());
    124    MOZ_ASSERT(slot.get());
    125 
    126    UniqueCERTName subjectName(GenerateRandomName(slot.get()));
    127    if (!subjectName) {
    128      return NS_ERROR_DOM_UNKNOWN_ERR;
    129    }
    130 
    131    UniqueSECKEYPublicKey publicKey(mKeyPair->mPublicKey->GetPublicKey());
    132    UniqueCERTSubjectPublicKeyInfo spki(
    133        SECKEY_CreateSubjectPublicKeyInfo(publicKey.get()));
    134    if (!spki) {
    135      return NS_ERROR_DOM_UNKNOWN_ERR;
    136    }
    137 
    138    UniqueCERTCertificateRequest certreq(
    139        CERT_CreateCertificateRequest(subjectName.get(), spki.get(), nullptr));
    140    if (!certreq) {
    141      return NS_ERROR_DOM_UNKNOWN_ERR;
    142    }
    143 
    144    PRTime now = PR_Now();
    145    PRTime notBefore = now - EXPIRATION_SLACK;
    146    mExpires += now;
    147 
    148    UniqueCERTValidity validity(CERT_CreateValidity(notBefore, mExpires));
    149    if (!validity) {
    150      return NS_ERROR_DOM_UNKNOWN_ERR;
    151    }
    152 
    153    unsigned long serial;
    154    // Note: This serial in principle could collide, but it's unlikely, and we
    155    // don't expect anyone to be validating certificates anyway.
    156    SECStatus rv = PK11_GenerateRandomOnSlot(
    157        slot.get(), reinterpret_cast<unsigned char*>(&serial), sizeof(serial));
    158    if (rv != SECSuccess) {
    159      return NS_ERROR_DOM_UNKNOWN_ERR;
    160    }
    161 
    162    // NB: CERTCertificates created with CERT_CreateCertificate are not safe to
    163    // use with other NSS functions like CERT_DupCertificate.  The strategy
    164    // here is to create a tbsCertificate ("to-be-signed certificate"), encode
    165    // it, and sign it, resulting in a signed DER certificate that can be
    166    // decoded into a CERTCertificate.
    167    UniqueCERTCertificate tbsCertificate(CERT_CreateCertificate(
    168        serial, subjectName.get(), validity.get(), certreq.get()));
    169    if (!tbsCertificate) {
    170      return NS_ERROR_DOM_UNKNOWN_ERR;
    171    }
    172 
    173    MOZ_ASSERT(mSignatureAlg != SEC_OID_UNKNOWN);
    174    PLArenaPool* arena = tbsCertificate->arena;
    175 
    176    rv = SECOID_SetAlgorithmID(arena, &tbsCertificate->signature, mSignatureAlg,
    177                               nullptr);
    178    if (rv != SECSuccess) {
    179      return NS_ERROR_DOM_UNKNOWN_ERR;
    180    }
    181 
    182    // Set version to X509v3.
    183    *(tbsCertificate->version.data) = SEC_CERTIFICATE_VERSION_3;
    184    tbsCertificate->version.len = 1;
    185 
    186    SECItem innerDER = {siBuffer, nullptr, 0};
    187    if (!SEC_ASN1EncodeItem(arena, &innerDER, tbsCertificate.get(),
    188                            SEC_ASN1_GET(CERT_CertificateTemplate))) {
    189      return NS_ERROR_DOM_UNKNOWN_ERR;
    190    }
    191 
    192    SECItem* certDer = PORT_ArenaZNew(arena, SECItem);
    193    if (!certDer) {
    194      return NS_ERROR_DOM_UNKNOWN_ERR;
    195    }
    196 
    197    UniqueSECKEYPrivateKey privateKey(mKeyPair->mPrivateKey->GetPrivateKey());
    198    rv = SEC_DerSignData(arena, certDer, innerDER.data, innerDER.len,
    199                         privateKey.get(), mSignatureAlg);
    200    if (rv != SECSuccess) {
    201      return NS_ERROR_DOM_UNKNOWN_ERR;
    202    }
    203 
    204    mCertificate.reset(CERT_NewTempCertificate(CERT_GetDefaultCertDB(), certDer,
    205                                               nullptr, false, true));
    206    if (!mCertificate) {
    207      return NS_ERROR_DOM_UNKNOWN_ERR;
    208    }
    209    return NS_OK;
    210  }
    211 
    212  nsresult BeforeCrypto() override {
    213    if (mAlgName.EqualsLiteral(WEBCRYPTO_ALG_RSASSA_PKCS1)) {
    214      // Double check that size is OK.
    215      auto sz = static_cast<size_t>(mRsaParams.keySizeInBits);
    216      if (sz < RTCCertificateMinRsaSize) {
    217        return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
    218      }
    219 
    220      KeyAlgorithmProxy& alg = mKeyPair->mPublicKey->Algorithm();
    221      if (alg.mType != KeyAlgorithmProxy::RSA ||
    222          !alg.mRsa.mHash.mName.EqualsLiteral(WEBCRYPTO_ALG_SHA256)) {
    223        return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
    224      }
    225 
    226      mSignatureAlg = SEC_OID_PKCS1_SHA256_WITH_RSA_ENCRYPTION;
    227      mAuthType = ssl_kea_rsa;
    228 
    229    } else if (mAlgName.EqualsLiteral(WEBCRYPTO_ALG_ECDSA)) {
    230      // We only support good curves in WebCrypto.
    231      // If that ever changes, check that a good one was chosen.
    232 
    233      mSignatureAlg = SEC_OID_ANSIX962_ECDSA_SHA256_SIGNATURE;
    234      mAuthType = ssl_kea_ecdh;
    235    } else {
    236      return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
    237    }
    238    return NS_OK;
    239  }
    240 
    241  nsresult DoCrypto() override {
    242    nsresult rv = GenerateAsymmetricKeyTask::DoCrypto();
    243    NS_ENSURE_SUCCESS(rv, rv);
    244 
    245    rv = GenerateCertificate();
    246    NS_ENSURE_SUCCESS(rv, rv);
    247 
    248    return NS_OK;
    249  }
    250 
    251  virtual void Resolve() override {
    252    // Make copies of the private key and certificate, otherwise, when this
    253    // object is deleted, the structures they reference will be deleted too.
    254    UniqueSECKEYPrivateKey key = mKeyPair->mPrivateKey->GetPrivateKey();
    255    CERTCertificate* cert = CERT_DupCertificate(mCertificate.get());
    256    RefPtr<RTCCertificate> result =
    257        new RTCCertificate(mResultPromise->GetParentObject(), key.release(),
    258                           cert, mAuthType, mExpires);
    259    mResultPromise->MaybeResolve(result);
    260  }
    261 };
    262 
    263 static PRTime ReadExpires(JSContext* aCx, const ObjectOrString& aOptions,
    264                          ErrorResult& aRv) {
    265  // This conversion might fail, but we don't really care; use the default.
    266  // If this isn't an object, or it doesn't coerce into the right type,
    267  // then we won't get the |expires| value.  Either will be caught later.
    268  RTCCertificateExpiration expiration;
    269  if (!aOptions.IsObject()) {
    270    return EXPIRATION_DEFAULT;
    271  }
    272  JS::Rooted<JS::Value> value(aCx, JS::ObjectValue(*aOptions.GetAsObject()));
    273  if (!expiration.Init(aCx, value)) {
    274    aRv.NoteJSContextException(aCx);
    275    return 0;
    276  }
    277 
    278  if (!expiration.mExpires.WasPassed()) {
    279    return EXPIRATION_DEFAULT;
    280  }
    281  static const uint64_t max =
    282      static_cast<uint64_t>(EXPIRATION_MAX / PR_USEC_PER_MSEC);
    283  if (expiration.mExpires.Value() > max) {
    284    return EXPIRATION_MAX;
    285  }
    286  return static_cast<PRTime>(expiration.mExpires.Value() * PR_USEC_PER_MSEC);
    287 }
    288 
    289 already_AddRefed<Promise> RTCCertificate::GenerateCertificate(
    290    const GlobalObject& aGlobal, const ObjectOrString& aOptions,
    291    ErrorResult& aRv, JS::Compartment* aCompartment) {
    292  nsIGlobalObject* global = xpc::NativeGlobal(aGlobal.Get());
    293  RefPtr<Promise> p = Promise::Create(global, aRv);
    294  if (aRv.Failed()) {
    295    return nullptr;
    296  }
    297  Sequence<nsString> usages;
    298  if (!usages.AppendElement(u"sign"_ns, fallible)) {
    299    aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
    300    return nullptr;
    301  }
    302 
    303  PRTime expires = ReadExpires(aGlobal.Context(), aOptions, aRv);
    304  if (aRv.Failed()) {
    305    return nullptr;
    306  }
    307  RefPtr<WebCryptoTask> task = new GenerateRTCCertificateTask(
    308      global, aGlobal.Context(), aOptions, usages, expires);
    309  task->DispatchWithPromise(p);
    310  return p.forget();
    311 }
    312 
    313 RTCCertificate::RTCCertificate(nsIGlobalObject* aGlobal)
    314    : mGlobal(aGlobal),
    315      mPrivateKey(nullptr),
    316      mCertificate(nullptr),
    317      mAuthType(ssl_kea_null),
    318      mExpires(0) {}
    319 
    320 RTCCertificate::RTCCertificate(nsIGlobalObject* aGlobal,
    321                               SECKEYPrivateKey* aPrivateKey,
    322                               CERTCertificate* aCertificate,
    323                               SSLKEAType aAuthType, PRTime aExpires)
    324    : mGlobal(aGlobal),
    325      mPrivateKey(aPrivateKey),
    326      mCertificate(aCertificate),
    327      mAuthType(aAuthType),
    328      mExpires(aExpires) {}
    329 
    330 void RTCCertificate::GetFingerprints(
    331    nsTArray<dom::RTCDtlsFingerprint>& aFingerprintsOut) {
    332  // if we have a cert and haven't already built the fingerprints
    333  if (mCertificate && mFingerprints.Length() == 0) {
    334    DtlsDigest digest(DtlsIdentity::DEFAULT_HASH_ALGORITHM);
    335    nsresult rv = DtlsIdentity::ComputeFingerprint(mCertificate, &digest);
    336    if (NS_FAILED(rv)) {
    337      // Safe to return early here since we didn't already have fingerprints
    338      // and the call to DtlsIdentity::ComputeFingerprint failed.
    339      return;
    340    }
    341    RTCDtlsFingerprint fingerprint;
    342    fingerprint.mAlgorithm.Construct(NS_ConvertASCIItoUTF16(digest.algorithm_));
    343 
    344    std::string value =
    345        SdpFingerprintAttributeList::FormatFingerprint(digest.value_);
    346    // Sadly, the SDP fingerprint is expected to be all uppercase hex,
    347    // while the RTC fingerprint is expected to be all lowercase hex.
    348    std::transform(value.begin(), value.end(), value.begin(), ::tolower);
    349    fingerprint.mValue.Construct(NS_ConvertASCIItoUTF16(value));
    350 
    351    mFingerprints.AppendElement(fingerprint);
    352  }
    353 
    354  aFingerprintsOut = mFingerprints.Clone();
    355 }
    356 
    357 RefPtr<DtlsIdentity> RTCCertificate::CreateDtlsIdentity() const {
    358  if (!mPrivateKey || !mCertificate) {
    359    return nullptr;
    360  }
    361  UniqueSECKEYPrivateKey key(SECKEY_CopyPrivateKey(mPrivateKey.get()));
    362  UniqueCERTCertificate cert(CERT_DupCertificate(mCertificate.get()));
    363  RefPtr<DtlsIdentity> id =
    364      new DtlsIdentity(std::move(key), std::move(cert), mAuthType);
    365  return id;
    366 }
    367 
    368 JSObject* RTCCertificate::WrapObject(JSContext* aCx,
    369                                     JS::Handle<JSObject*> aGivenProto) {
    370  return RTCCertificate_Binding::Wrap(aCx, this, aGivenProto);
    371 }
    372 
    373 bool RTCCertificate::WritePrivateKey(JSStructuredCloneWriter* aWriter) const {
    374  JsonWebKey jwk;
    375  nsresult rv = CryptoKey::PrivateKeyToJwk(mPrivateKey.get(), jwk);
    376  if (NS_FAILED(rv)) {
    377    return false;
    378  }
    379  nsString json;
    380  if (!jwk.ToJSON(json)) {
    381    return false;
    382  }
    383  return StructuredCloneHolder::WriteString(aWriter, json);
    384 }
    385 
    386 bool RTCCertificate::WriteCertificate(JSStructuredCloneWriter* aWriter) const {
    387  UniqueCERTCertificateList certs(CERT_CertListFromCert(mCertificate.get()));
    388  if (!certs || certs->len <= 0) {
    389    return false;
    390  }
    391  if (!JS_WriteUint32Pair(aWriter, certs->certs[0].len, 0)) {
    392    return false;
    393  }
    394  return JS_WriteBytes(aWriter, certs->certs[0].data, certs->certs[0].len);
    395 }
    396 
    397 bool RTCCertificate::WriteStructuredClone(
    398    JSContext* aCx, JSStructuredCloneWriter* aWriter) const {
    399  if (!mPrivateKey || !mCertificate) {
    400    return false;
    401  }
    402 
    403  return JS_WriteUint32Pair(aWriter, RTCCERTIFICATE_SC_VERSION, mAuthType) &&
    404         JS_WriteUint32Pair(aWriter, (mExpires >> 32) & 0xffffffff,
    405                            mExpires & 0xffffffff) &&
    406         WritePrivateKey(aWriter) && WriteCertificate(aWriter);
    407 }
    408 
    409 bool RTCCertificate::ReadPrivateKey(JSStructuredCloneReader* aReader) {
    410  nsString json;
    411  if (!StructuredCloneHolder::ReadString(aReader, json)) {
    412    return false;
    413  }
    414  JsonWebKey jwk;
    415  if (!jwk.Init(json)) {
    416    return false;
    417  }
    418  mPrivateKey = CryptoKey::PrivateKeyFromJwk(jwk);
    419  return !!mPrivateKey;
    420 }
    421 
    422 bool RTCCertificate::ReadCertificate(JSStructuredCloneReader* aReader) {
    423  CryptoBuffer cert;
    424  if (!ReadBuffer(aReader, cert) || cert.Length() == 0) {
    425    return false;
    426  }
    427 
    428  SECItem der = {siBuffer, cert.Elements(),
    429                 static_cast<unsigned int>(cert.Length())};
    430  mCertificate.reset(CERT_NewTempCertificate(CERT_GetDefaultCertDB(), &der,
    431                                             nullptr, true, true));
    432  return !!mCertificate;
    433 }
    434 
    435 // static
    436 already_AddRefed<RTCCertificate> RTCCertificate::ReadStructuredClone(
    437    JSContext* aCx, nsIGlobalObject* aGlobal,
    438    JSStructuredCloneReader* aReader) {
    439  if (!NS_IsMainThread()) {
    440    // These objects are mainthread-only.
    441    return nullptr;
    442  }
    443  uint32_t version, authType;
    444  if (!JS_ReadUint32Pair(aReader, &version, &authType) ||
    445      version != RTCCERTIFICATE_SC_VERSION) {
    446    return nullptr;
    447  }
    448  RefPtr<RTCCertificate> cert = new RTCCertificate(aGlobal);
    449  cert->mAuthType = static_cast<SSLKEAType>(authType);
    450 
    451  uint32_t high, low;
    452  if (!JS_ReadUint32Pair(aReader, &high, &low)) {
    453    return nullptr;
    454  }
    455  cert->mExpires = static_cast<PRTime>(high) << 32 | low;
    456 
    457  if (!cert->ReadPrivateKey(aReader) || !cert->ReadCertificate(aReader)) {
    458    return nullptr;
    459  }
    460 
    461  return cert.forget();
    462 }
    463 
    464 }  // namespace mozilla::dom