tor-browser

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

dtlsidentity.cpp (10397B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* vim: set ts=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 file,
      5 * You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 #include "dtlsidentity.h"
      8 
      9 #include "cert.h"
     10 #include "cryptohi.h"
     11 #include "keyhi.h"
     12 #include "mozilla/Sprintf.h"
     13 #include "mozpkix/nss_scoped_ptrs.h"
     14 #include "nsError.h"
     15 #include "pk11pub.h"
     16 #include "secerr.h"
     17 #include "sechash.h"
     18 #include "sslerr.h"
     19 
     20 namespace mozilla {
     21 
     22 SECItem* WrapPrivateKeyInfoWithEmptyPassword(
     23    SECKEYPrivateKey* pk) /* encrypt this private key */
     24 {
     25  if (!pk) {
     26    PR_SetError(SEC_ERROR_INVALID_ARGS, 0);
     27    return nullptr;
     28  }
     29 
     30  UniquePK11SlotInfo slot(PK11_GetInternalSlot());
     31  if (!slot) {
     32    return nullptr;
     33  }
     34 
     35  // For private keys, NSS cannot export anything other than RSA, but we need EC
     36  // also. So, we use the private key encryption function to serialize instead,
     37  // using a hard-coded dummy password; this is not intended to provide any
     38  // additional security, it just works around a limitation in NSS.
     39  SECItem dummyPassword = {siBuffer, nullptr, 0};
     40  UniqueSECKEYEncryptedPrivateKeyInfo epki(PK11_ExportEncryptedPrivKeyInfo(
     41      slot.get(), SEC_OID_AES_128_CBC, &dummyPassword, pk, 1, nullptr));
     42 
     43  if (!epki) {
     44    return nullptr;
     45  }
     46 
     47  return SEC_ASN1EncodeItem(
     48      nullptr, nullptr, epki.get(),
     49      NSS_Get_SECKEY_EncryptedPrivateKeyInfoTemplate(nullptr, false));
     50 }
     51 
     52 SECStatus UnwrapPrivateKeyInfoWithEmptyPassword(
     53    SECItem* derPKI, const UniqueCERTCertificate& aCert,
     54    SECKEYPrivateKey** privk) {
     55  if (!derPKI || !aCert || !privk) {
     56    PR_SetError(SEC_ERROR_INVALID_ARGS, 0);
     57    return SECFailure;
     58  }
     59 
     60  UniqueSECKEYPublicKey publicKey(CERT_ExtractPublicKey(aCert.get()));
     61  // This is a pointer to data inside publicKey
     62  SECItem* publicValue = nullptr;
     63  switch (publicKey->keyType) {
     64    case dsaKey:
     65      publicValue = &publicKey->u.dsa.publicValue;
     66      break;
     67    case dhKey:
     68      publicValue = &publicKey->u.dh.publicValue;
     69      break;
     70    case rsaKey:
     71      publicValue = &publicKey->u.rsa.modulus;
     72      break;
     73    case ecKey:
     74      publicValue = &publicKey->u.ec.publicValue;
     75      break;
     76    default:
     77      MOZ_ASSERT(false);
     78      PR_SetError(SSL_ERROR_BAD_CERTIFICATE, 0);
     79      return SECFailure;
     80  }
     81 
     82  UniquePK11SlotInfo slot(PK11_GetInternalSlot());
     83  if (!slot) {
     84    return SECFailure;
     85  }
     86 
     87  UniquePLArenaPool temparena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE));
     88  if (!temparena) {
     89    return SECFailure;
     90  }
     91 
     92  SECKEYEncryptedPrivateKeyInfo* epki =
     93      PORT_ArenaZNew(temparena.get(), SECKEYEncryptedPrivateKeyInfo);
     94  if (!epki) {
     95    return SECFailure;
     96  }
     97 
     98  SECStatus rv = SEC_ASN1DecodeItem(
     99      temparena.get(), epki,
    100      NSS_Get_SECKEY_EncryptedPrivateKeyInfoTemplate(nullptr, false), derPKI);
    101  if (rv != SECSuccess) {
    102    // If SEC_ASN1DecodeItem fails, we cannot assume anything about the
    103    // validity of the data in epki. The best we can do is free the arena
    104    // and return.
    105    return rv;
    106  }
    107 
    108  // See comment in WrapPrivateKeyInfoWithEmptyPassword about this
    109  // dummy password stuff.
    110  SECItem dummyPassword = {siBuffer, nullptr, 0};
    111  return PK11_ImportEncryptedPrivateKeyInfoAndReturnKey(
    112      slot.get(), epki, &dummyPassword, nullptr, publicValue, false, false,
    113      publicKey->keyType, KU_ALL, privk, nullptr);
    114 }
    115 
    116 nsresult DtlsIdentity::Serialize(nsTArray<uint8_t>* aKeyDer,
    117                                 nsTArray<uint8_t>* aCertDer) {
    118  ScopedSECItem derPki(WrapPrivateKeyInfoWithEmptyPassword(private_key_.get()));
    119  if (!derPki) {
    120    return NS_ERROR_FAILURE;
    121  }
    122 
    123  aKeyDer->AppendElements(derPki->data, derPki->len);
    124  aCertDer->AppendElements(cert_->derCert.data, cert_->derCert.len);
    125  return NS_OK;
    126 }
    127 
    128 /* static */
    129 RefPtr<DtlsIdentity> DtlsIdentity::Deserialize(
    130    const nsTArray<uint8_t>& aKeyDer, const nsTArray<uint8_t>& aCertDer,
    131    SSLKEAType authType) {
    132  SECItem certDer = {siBuffer, const_cast<uint8_t*>(aCertDer.Elements()),
    133                     static_cast<unsigned int>(aCertDer.Length())};
    134  UniqueCERTCertificate cert(CERT_NewTempCertificate(
    135      CERT_GetDefaultCertDB(), &certDer, nullptr, true, true));
    136 
    137  SECItem derPKI = {siBuffer, const_cast<uint8_t*>(aKeyDer.Elements()),
    138                    static_cast<unsigned int>(aKeyDer.Length())};
    139 
    140  SECKEYPrivateKey* privateKey;
    141  if (UnwrapPrivateKeyInfoWithEmptyPassword(&derPKI, cert, &privateKey) !=
    142      SECSuccess) {
    143    MOZ_ASSERT(false);
    144    return nullptr;
    145  }
    146 
    147  return new DtlsIdentity(UniqueSECKEYPrivateKey(privateKey), std::move(cert),
    148                          authType);
    149 }
    150 
    151 RefPtr<DtlsIdentity> DtlsIdentity::Generate() {
    152  UniquePK11SlotInfo slot(PK11_GetInternalSlot());
    153  if (!slot) {
    154    return nullptr;
    155  }
    156 
    157  uint8_t random_name[16];
    158 
    159  SECStatus rv =
    160      PK11_GenerateRandomOnSlot(slot.get(), random_name, sizeof(random_name));
    161  if (rv != SECSuccess) return nullptr;
    162 
    163  std::string name;
    164  char chunk[3];
    165  for (unsigned char r_name : random_name) {
    166    SprintfLiteral(chunk, "%.2x", r_name);
    167    name += chunk;
    168  }
    169 
    170  std::string subject_name_string = "CN=" + name;
    171  UniqueCERTName subject_name(CERT_AsciiToName(subject_name_string.c_str()));
    172  if (!subject_name) {
    173    return nullptr;
    174  }
    175 
    176  unsigned char paramBuf[12];  // OIDs are small
    177  SECItem ecdsaParams = {siBuffer, paramBuf, sizeof(paramBuf)};
    178  SECOidData* oidData = SECOID_FindOIDByTag(SEC_OID_SECG_EC_SECP256R1);
    179  if (!oidData || (oidData->oid.len > (sizeof(paramBuf) - 2))) {
    180    return nullptr;
    181  }
    182  ecdsaParams.data[0] = SEC_ASN1_OBJECT_ID;
    183  ecdsaParams.data[1] = oidData->oid.len;
    184  memcpy(ecdsaParams.data + 2, oidData->oid.data, oidData->oid.len);
    185  ecdsaParams.len = oidData->oid.len + 2;
    186 
    187  SECKEYPublicKey* pubkey;
    188  UniqueSECKEYPrivateKey private_key(
    189      PK11_GenerateKeyPair(slot.get(), CKM_EC_KEY_PAIR_GEN, &ecdsaParams,
    190                           &pubkey, PR_FALSE, PR_TRUE, nullptr));
    191  if (private_key == nullptr) return nullptr;
    192  UniqueSECKEYPublicKey public_key(pubkey);
    193  pubkey = nullptr;
    194 
    195  UniqueCERTSubjectPublicKeyInfo spki(
    196      SECKEY_CreateSubjectPublicKeyInfo(public_key.get()));
    197  if (!spki) {
    198    return nullptr;
    199  }
    200 
    201  UniqueCERTCertificateRequest certreq(
    202      CERT_CreateCertificateRequest(subject_name.get(), spki.get(), nullptr));
    203  if (!certreq) {
    204    return nullptr;
    205  }
    206 
    207  // From 1 day before todayto 30 days after.
    208  // This is a sort of arbitrary range designed to be valid
    209  // now with some slack in case the other side expects
    210  // some before expiry.
    211  //
    212  // Note: explicit casts necessary to avoid
    213  //       warning C4307: '*' : integral constant overflow
    214  static const PRTime oneDay = PRTime(PR_USEC_PER_SEC) * PRTime(60)  // sec
    215                               * PRTime(60)                          // min
    216                               * PRTime(24);                         // hours
    217  PRTime now = PR_Now();
    218  PRTime notBefore = now - oneDay;
    219  PRTime notAfter = now + (PRTime(30) * oneDay);
    220 
    221  UniqueCERTValidity validity(CERT_CreateValidity(notBefore, notAfter));
    222  if (!validity) {
    223    return nullptr;
    224  }
    225 
    226  unsigned long serial;
    227  // Note: This serial in principle could collide, but it's unlikely
    228  rv = PK11_GenerateRandomOnSlot(
    229      slot.get(), reinterpret_cast<unsigned char*>(&serial), sizeof(serial));
    230  if (rv != SECSuccess) {
    231    return nullptr;
    232  }
    233 
    234  // NB: CERTCertificates created with CERT_CreateCertificate are not safe to
    235  // use with other NSS functions like CERT_DupCertificate.
    236  // The strategy here is to create a tbsCertificate ("to-be-signed
    237  // certificate"), encode it, and sign it, resulting in a signed DER
    238  // certificate that can be decoded into a CERTCertificate.
    239  UniqueCERTCertificate tbsCertificate(CERT_CreateCertificate(
    240      serial, subject_name.get(), validity.get(), certreq.get()));
    241  if (!tbsCertificate) {
    242    return nullptr;
    243  }
    244 
    245  PLArenaPool* arena = tbsCertificate->arena;
    246 
    247  rv = SECOID_SetAlgorithmID(arena, &tbsCertificate->signature,
    248                             SEC_OID_ANSIX962_ECDSA_SHA256_SIGNATURE, nullptr);
    249  if (rv != SECSuccess) return nullptr;
    250 
    251  // Set version to X509v3.
    252  *(tbsCertificate->version.data) = SEC_CERTIFICATE_VERSION_3;
    253  tbsCertificate->version.len = 1;
    254 
    255  SECItem innerDER;
    256  innerDER.len = 0;
    257  innerDER.data = nullptr;
    258 
    259  if (!SEC_ASN1EncodeItem(arena, &innerDER, tbsCertificate.get(),
    260                          SEC_ASN1_GET(CERT_CertificateTemplate))) {
    261    return nullptr;
    262  }
    263 
    264  SECItem* certDer = PORT_ArenaZNew(arena, SECItem);
    265  if (!certDer) {
    266    return nullptr;
    267  }
    268 
    269  rv = SEC_DerSignData(arena, certDer, innerDER.data, innerDER.len,
    270                       private_key.get(),
    271                       SEC_OID_ANSIX962_ECDSA_SHA256_SIGNATURE);
    272  if (rv != SECSuccess) {
    273    return nullptr;
    274  }
    275 
    276  UniqueCERTCertificate certificate(CERT_NewTempCertificate(
    277      CERT_GetDefaultCertDB(), certDer, nullptr, false, true));
    278 
    279  return new DtlsIdentity(std::move(private_key), std::move(certificate),
    280                          ssl_kea_ecdh);
    281 }
    282 
    283 constexpr nsLiteralCString DtlsIdentity::DEFAULT_HASH_ALGORITHM;
    284 
    285 nsresult DtlsIdentity::ComputeFingerprint(DtlsDigest* digest) const {
    286  const UniqueCERTCertificate& c = cert();
    287  MOZ_ASSERT(c);
    288 
    289  return ComputeFingerprint(c, digest);
    290 }
    291 
    292 nsresult DtlsIdentity::ComputeFingerprint(const UniqueCERTCertificate& cert,
    293                                          DtlsDigest* digest) {
    294  MOZ_ASSERT(cert);
    295 
    296  HASH_HashType ht;
    297 
    298  if (digest->algorithm_ == "sha-1") {
    299    ht = HASH_AlgSHA1;
    300  } else if (digest->algorithm_ == "sha-224") {
    301    ht = HASH_AlgSHA224;
    302  } else if (digest->algorithm_ == "sha-256") {
    303    ht = HASH_AlgSHA256;
    304  } else if (digest->algorithm_ == "sha-384") {
    305    ht = HASH_AlgSHA384;
    306  } else if (digest->algorithm_ == "sha-512") {
    307    ht = HASH_AlgSHA512;
    308  } else {
    309    return NS_ERROR_INVALID_ARG;
    310  }
    311 
    312  const SECHashObject* ho = HASH_GetHashObject(ht);
    313  MOZ_ASSERT(ho);
    314  if (!ho) {
    315    return NS_ERROR_INVALID_ARG;
    316  }
    317 
    318  MOZ_ASSERT(ho->length >= 20);  // Double check
    319  digest->value_.resize(ho->length);
    320 
    321  SECStatus rv = HASH_HashBuf(ho->type, digest->value_.data(),
    322                              cert->derCert.data, cert->derCert.len);
    323  if (rv != SECSuccess) {
    324    return NS_ERROR_FAILURE;
    325  }
    326 
    327  return NS_OK;
    328 }
    329 
    330 }  // namespace mozilla