tor-browser

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

nsNSSCertificateDB.cpp (43543B)


      1 /* This Source Code Form is subject to the terms of the Mozilla Public
      2 * License, v. 2.0. If a copy of the MPL was not distributed with this
      3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      4 
      5 #include "nsNSSCertificateDB.h"
      6 
      7 #include "CertVerifier.h"
      8 #include "CryptoTask.h"
      9 #include "ExtendedValidation.h"
     10 #include "NSSCertDBTrustDomain.h"
     11 #include "certdb.h"
     12 #include "mozilla/Assertions.h"
     13 #include "mozilla/Base64.h"
     14 #include "mozilla/Casting.h"
     15 #include "mozilla/Logging.h"
     16 #include "mozilla/Services.h"
     17 #include "mozpkix/Time.h"
     18 #include "mozpkix/pkixnss.h"
     19 #include "mozpkix/pkixtypes.h"
     20 #include "nsArray.h"
     21 #include "nsArrayUtils.h"
     22 #include "nsCOMPtr.h"
     23 #include "nsComponentManagerUtils.h"
     24 #include "nsICertificateDialogs.h"
     25 #include "nsIFile.h"
     26 #include "nsIMutableArray.h"
     27 #include "nsIObserverService.h"
     28 #include "nsIPrompt.h"
     29 #include "nsNSSCertHelper.h"
     30 #include "nsNSSCertTrust.h"
     31 #include "nsNSSCertificate.h"
     32 #include "nsNSSComponent.h"
     33 #include "nsNSSHelper.h"
     34 #include "nsPKCS12Blob.h"
     35 #include "nsPromiseFlatString.h"
     36 #include "nsProxyRelease.h"
     37 #include "nsReadableUtils.h"
     38 #include "nsThreadUtils.h"
     39 #include "nspr.h"
     40 #include "secasn1.h"
     41 #include "secder.h"
     42 #include "secerr.h"
     43 #include "ssl.h"
     44 
     45 #ifdef MOZ_WIDGET_ANDROID
     46 #  include "mozilla/java/ClientAuthCertificateManagerWrappers.h"
     47 #endif
     48 
     49 #ifdef XP_WIN
     50 #  include <winsock.h>  // for ntohl
     51 #endif
     52 
     53 using namespace mozilla;
     54 using namespace mozilla::psm;
     55 
     56 extern LazyLogModule gPIPNSSLog;
     57 
     58 NS_IMPL_ISUPPORTS(nsNSSCertificateDB, nsIX509CertDB)
     59 
     60 NS_IMETHODIMP
     61 nsNSSCertificateDB::FindCertByDBKey(const nsACString& aDBKey,
     62                                    /*out*/ nsIX509Cert** _cert) {
     63  NS_ENSURE_ARG_POINTER(_cert);
     64  *_cert = nullptr;
     65 
     66  if (aDBKey.IsEmpty()) {
     67    return NS_ERROR_INVALID_ARG;
     68  }
     69 
     70  nsresult rv = BlockUntilLoadableCertsLoaded();
     71  if (NS_FAILED(rv)) {
     72    return rv;
     73  }
     74 
     75  UniqueCERTCertificate cert;
     76  rv = FindCertByDBKey(aDBKey, cert);
     77  if (NS_FAILED(rv)) {
     78    return rv;
     79  }
     80  // If we can't find the certificate, that's not an error. Just return null.
     81  if (!cert) {
     82    return NS_OK;
     83  }
     84  nsCOMPtr<nsIX509Cert> nssCert = new nsNSSCertificate(cert.get());
     85  nssCert.forget(_cert);
     86  return NS_OK;
     87 }
     88 
     89 nsresult nsNSSCertificateDB::FindCertByDBKey(const nsACString& aDBKey,
     90                                             UniqueCERTCertificate& cert) {
     91  static_assert(sizeof(uint64_t) == 8, "type size sanity check");
     92  static_assert(sizeof(uint32_t) == 4, "type size sanity check");
     93  // (From nsNSSCertificate::GetDbKey)
     94  // The format of the key is the base64 encoding of the following:
     95  // 4 bytes: {0, 0, 0, 0} (this was intended to be the module ID, but it was
     96  //                        never implemented)
     97  // 4 bytes: {0, 0, 0, 0} (this was intended to be the slot ID, but it was
     98  //                        never implemented)
     99  // 4 bytes: <serial number length in big-endian order>
    100  // 4 bytes: <DER-encoded issuer distinguished name length in big-endian order>
    101  // n bytes: <bytes of serial number>
    102  // m bytes: <DER-encoded issuer distinguished name>
    103  nsAutoCString decoded;
    104  nsAutoCString tmpDBKey(aDBKey);
    105  // Filter out any whitespace for backwards compatibility.
    106  tmpDBKey.StripWhitespace();
    107  nsresult rv = Base64Decode(tmpDBKey, decoded);
    108  if (NS_FAILED(rv)) {
    109    return rv;
    110  }
    111  if (decoded.Length() < 16) {
    112    return NS_ERROR_ILLEGAL_INPUT;
    113  }
    114  const char* reader = decoded.BeginReading();
    115  uint64_t zeroes = *BitwiseCast<const uint64_t*, const char*>(reader);
    116  if (zeroes != 0) {
    117    return NS_ERROR_ILLEGAL_INPUT;
    118  }
    119  reader += sizeof(uint64_t);
    120  // Note: We surround the ntohl() argument with parentheses to stop the macro
    121  //       from thinking two arguments were passed.
    122  uint32_t serialNumberLen =
    123      ntohl((*BitwiseCast<const uint32_t*, const char*>(reader)));
    124  reader += sizeof(uint32_t);
    125  uint32_t issuerLen =
    126      ntohl((*BitwiseCast<const uint32_t*, const char*>(reader)));
    127  reader += sizeof(uint32_t);
    128  if (decoded.Length() != 16ULL + serialNumberLen + issuerLen) {
    129    return NS_ERROR_ILLEGAL_INPUT;
    130  }
    131  CERTIssuerAndSN issuerSN;
    132  issuerSN.serialNumber.len = serialNumberLen;
    133  issuerSN.serialNumber.data = BitwiseCast<unsigned char*, const char*>(reader);
    134  reader += serialNumberLen;
    135  issuerSN.derIssuer.len = issuerLen;
    136  issuerSN.derIssuer.data = BitwiseCast<unsigned char*, const char*>(reader);
    137  reader += issuerLen;
    138  MOZ_ASSERT(reader == decoded.EndReading());
    139 
    140  AutoSearchingForClientAuthCertificates _;
    141  cert.reset(CERT_FindCertByIssuerAndSN(CERT_GetDefaultCertDB(), &issuerSN));
    142  return NS_OK;
    143 }
    144 
    145 SECStatus collect_certs(void* arg, SECItem** certs, int numcerts) {
    146  nsTArray<nsTArray<uint8_t>>* certsArray =
    147      reinterpret_cast<nsTArray<nsTArray<uint8_t>>*>(arg);
    148 
    149  while (numcerts--) {
    150    nsTArray<uint8_t> certArray;
    151    SECItem* cert = *certs;
    152    certArray.AppendElements(cert->data, cert->len);
    153    certsArray->AppendElement(std::move(certArray));
    154    certs++;
    155  }
    156  return (SECSuccess);
    157 }
    158 
    159 nsresult nsNSSCertificateDB::getCertsFromPackage(
    160    nsTArray<nsTArray<uint8_t>>& collectArgs, uint8_t* data, uint32_t length) {
    161  if (CERT_DecodeCertPackage(BitwiseCast<char*, uint8_t*>(data), length,
    162                             collect_certs, &collectArgs) != SECSuccess) {
    163    return NS_ERROR_FAILURE;
    164  }
    165  return NS_OK;
    166 }
    167 
    168 // When using the sql-backed softoken, trust settings are authenticated using a
    169 // key in the secret database. Thus, if the user has a password, we need to
    170 // authenticate to the token in order to be able to change trust settings.
    171 SECStatus ChangeCertTrustWithPossibleAuthentication(
    172    const UniqueCERTCertificate& cert, CERTCertTrust& trust, void* ctx) {
    173  MOZ_ASSERT(cert, "cert must be non-null");
    174  if (!cert) {
    175    PR_SetError(SEC_ERROR_LIBRARY_FAILURE, 0);
    176    return SECFailure;
    177  }
    178 
    179  RefPtr<SharedCertVerifier> certVerifier(GetDefaultCertVerifier());
    180  if (!certVerifier) {
    181    PR_SetError(SEC_ERROR_LIBRARY_FAILURE, 0);
    182    return SECFailure;
    183  }
    184 
    185  // NSS ignores the first argument to CERT_ChangeCertTrust
    186  SECStatus srv = CERT_ChangeCertTrust(nullptr, cert.get(), &trust);
    187  if (srv != SECSuccess && PR_GetError() != SEC_ERROR_TOKEN_NOT_LOGGED_IN) {
    188    return SECFailure;
    189  }
    190  if (srv == SECSuccess) {
    191    certVerifier->ClearTrustCache();
    192    return SECSuccess;
    193  }
    194 
    195  // CERT_ChangeCertTrust failed with SEC_ERROR_TOKEN_NOT_LOGGED_IN, so
    196  // authenticate and try again.
    197  if (cert->slot) {
    198    // If this certificate is on an external PKCS#11 token, we have to
    199    // authenticate to that token.
    200    srv = PK11_Authenticate(cert->slot, PR_TRUE, ctx);
    201  } else {
    202    // Otherwise, the certificate is on the internal module.
    203    UniquePK11SlotInfo internalSlot(PK11_GetInternalKeySlot());
    204    srv = PK11_Authenticate(internalSlot.get(), PR_TRUE, ctx);
    205  }
    206  if (srv != SECSuccess) {
    207    return srv;
    208  }
    209  srv = CERT_ChangeCertTrust(nullptr, cert.get(), &trust);
    210  if (srv != SECSuccess) {
    211    return srv;
    212  }
    213 
    214  certVerifier->ClearTrustCache();
    215  return SECSuccess;
    216 }
    217 
    218 static nsresult ImportCertsIntoPermanentStorage(
    219    const UniqueCERTCertList& certChain) {
    220  bool encounteredFailure = false;
    221  PRErrorCode savedErrorCode = 0;
    222  UniquePK11SlotInfo slot(PK11_GetInternalKeySlot());
    223  for (CERTCertListNode* chainNode = CERT_LIST_HEAD(certChain);
    224       !CERT_LIST_END(chainNode, certChain);
    225       chainNode = CERT_LIST_NEXT(chainNode)) {
    226    UniquePORTString nickname(CERT_MakeCANickname(chainNode->cert));
    227    SECStatus srv = PK11_ImportCert(slot.get(), chainNode->cert,
    228                                    CK_INVALID_HANDLE, nickname.get(),
    229                                    false);  // this parameter is ignored by NSS
    230    if (srv != SECSuccess) {
    231      encounteredFailure = true;
    232      savedErrorCode = PR_GetError();
    233    }
    234  }
    235 
    236  if (encounteredFailure) {
    237    return GetXPCOMFromNSSError(savedErrorCode);
    238  }
    239 
    240  return NS_OK;
    241 }
    242 
    243 nsresult nsNSSCertificateDB::handleCACertDownload(NotNull<nsIArray*> x509Certs,
    244                                                  nsIInterfaceRequestor* ctx) {
    245  // First thing we have to do is figure out which certificate we're
    246  // gonna present to the user.  The CA may have sent down a list of
    247  // certs which may or may not be a chained list of certs.  Until
    248  // the day we can design some solid UI for the general case, we'll
    249  // code to the > 90% case.  That case is where a CA sends down a
    250  // list that is a hierarchy whose root is either the first or
    251  // the last cert.  What we're gonna do is compare the first
    252  // 2 entries, if the second was signed by the first, we assume
    253  // the root cert is the first cert and display it.  Otherwise,
    254  // we compare the last 2 entries, if the second to last cert was
    255  // signed by the last cert, then we assume the last cert is the
    256  // root and display it.
    257 
    258  uint32_t numCerts;
    259 
    260  x509Certs->GetLength(&numCerts);
    261 
    262  if (numCerts == 0) return NS_OK;  // Nothing to import, so nothing to do.
    263 
    264  nsCOMPtr<nsIX509Cert> certToShow;
    265  uint32_t selCertIndex;
    266  if (numCerts == 1) {
    267    // There's only one cert, so let's show it.
    268    selCertIndex = 0;
    269    certToShow = do_QueryElementAt(x509Certs, selCertIndex);
    270  } else {
    271    nsCOMPtr<nsIX509Cert> cert0;    // first cert
    272    nsCOMPtr<nsIX509Cert> cert1;    // second cert
    273    nsCOMPtr<nsIX509Cert> certn_2;  // second to last cert
    274    nsCOMPtr<nsIX509Cert> certn_1;  // last cert
    275 
    276    cert0 = do_QueryElementAt(x509Certs, 0);
    277    cert1 = do_QueryElementAt(x509Certs, 1);
    278    certn_2 = do_QueryElementAt(x509Certs, numCerts - 2);
    279    certn_1 = do_QueryElementAt(x509Certs, numCerts - 1);
    280 
    281    nsAutoString cert0SubjectName;
    282    nsAutoString cert1IssuerName;
    283    nsAutoString certn_2IssuerName;
    284    nsAutoString certn_1SubjectName;
    285 
    286    cert0->GetSubjectName(cert0SubjectName);
    287    cert1->GetIssuerName(cert1IssuerName);
    288    certn_2->GetIssuerName(certn_2IssuerName);
    289    certn_1->GetSubjectName(certn_1SubjectName);
    290 
    291    if (cert1IssuerName.Equals(cert0SubjectName)) {
    292      // In this case, the first cert in the list signed the second,
    293      // so the first cert is the root.  Let's display it.
    294      selCertIndex = 0;
    295      certToShow = cert0;
    296    } else if (certn_2IssuerName.Equals(certn_1SubjectName)) {
    297      // In this case the last cert has signed the second to last cert.
    298      // The last cert is the root, so let's display it.
    299      selCertIndex = numCerts - 1;
    300      certToShow = certn_1;
    301    } else {
    302      // It's not a chain, so let's just show the first one in the
    303      // downloaded list.
    304      selCertIndex = 0;
    305      certToShow = cert0;
    306    }
    307  }
    308 
    309  if (!certToShow) return NS_ERROR_FAILURE;
    310 
    311  nsCOMPtr<nsICertificateDialogs> dialogs;
    312  nsresult rv = ::getNSSDialogs(getter_AddRefs(dialogs),
    313                                NS_GET_IID(nsICertificateDialogs),
    314                                NS_CERTIFICATEDIALOGS_CONTRACTID);
    315  if (NS_FAILED(rv)) {
    316    return rv;
    317  }
    318 
    319  UniqueCERTCertificate tmpCert(certToShow->GetCert());
    320  if (!tmpCert) {
    321    return NS_ERROR_FAILURE;
    322  }
    323 
    324  if (!CERT_IsCACert(tmpCert.get(), nullptr)) {
    325    DisplayCertificateAlert(ctx, "NotACACert", certToShow);
    326    return NS_ERROR_FAILURE;
    327  }
    328 
    329  if (tmpCert->isperm) {
    330    DisplayCertificateAlert(ctx, "CaCertExists", certToShow);
    331    return NS_ERROR_FAILURE;
    332  }
    333 
    334  uint32_t trustBits;
    335  bool allows;
    336  rv = dialogs->ConfirmDownloadCACert(ctx, certToShow, &trustBits, &allows);
    337  if (NS_FAILED(rv)) return rv;
    338 
    339  if (!allows) return NS_ERROR_NOT_AVAILABLE;
    340 
    341  MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("trust is %d\n", trustBits));
    342  UniquePORTString nickname(CERT_MakeCANickname(tmpCert.get()));
    343 
    344  MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
    345          ("Created nick \"%s\"\n", nickname.get()));
    346 
    347  nsNSSCertTrust trust;
    348  trust.SetValidCA();
    349  trust.AddCATrust(!!(trustBits & nsIX509CertDB::TRUSTED_SSL),
    350                   !!(trustBits & nsIX509CertDB::TRUSTED_EMAIL));
    351 
    352  UniquePK11SlotInfo slot(PK11_GetInternalKeySlot());
    353  SECStatus srv = PK11_ImportCert(slot.get(), tmpCert.get(), CK_INVALID_HANDLE,
    354                                  nickname.get(),
    355                                  false);  // this parameter is ignored by NSS
    356  if (srv != SECSuccess) {
    357    return MapSECStatus(srv);
    358  }
    359  srv =
    360      ChangeCertTrustWithPossibleAuthentication(tmpCert, trust.GetTrust(), ctx);
    361  if (srv != SECSuccess) {
    362    return MapSECStatus(srv);
    363  }
    364 
    365  // Import additional delivered certificates that can be verified.
    366 
    367  // build a CertList for filtering
    368  UniqueCERTCertList certList(CERT_NewCertList());
    369  if (!certList) {
    370    return NS_ERROR_FAILURE;
    371  }
    372 
    373  // get all remaining certs into temp store
    374 
    375  for (uint32_t i = 0; i < numCerts; i++) {
    376    if (i == selCertIndex) {
    377      // we already processed that one
    378      continue;
    379    }
    380 
    381    nsCOMPtr<nsIX509Cert> remainingCert = do_QueryElementAt(x509Certs, i);
    382    if (!remainingCert) {
    383      continue;
    384    }
    385 
    386    UniqueCERTCertificate tmpCert2(remainingCert->GetCert());
    387    if (!tmpCert2) {
    388      continue;  // Let's try to import the rest of 'em
    389    }
    390 
    391    if (CERT_AddCertToListTail(certList.get(), tmpCert2.get()) != SECSuccess) {
    392      continue;
    393    }
    394 
    395    (void)tmpCert2.release();
    396  }
    397 
    398  return ImportCertsIntoPermanentStorage(certList);
    399 }
    400 
    401 nsresult nsNSSCertificateDB::ConstructCertArrayFromUniqueCertList(
    402    const UniqueCERTCertList& aCertListIn,
    403    nsTArray<RefPtr<nsIX509Cert>>& aCertListOut) {
    404  if (!aCertListIn.get()) {
    405    return NS_ERROR_INVALID_ARG;
    406  }
    407 
    408  for (CERTCertListNode* node = CERT_LIST_HEAD(aCertListIn.get());
    409       !CERT_LIST_END(node, aCertListIn.get()); node = CERT_LIST_NEXT(node)) {
    410    RefPtr<nsIX509Cert> cert = new nsNSSCertificate(node->cert);
    411    aCertListOut.AppendElement(cert);
    412  }
    413  return NS_OK;
    414 }
    415 
    416 NS_IMETHODIMP
    417 nsNSSCertificateDB::ImportCertificates(uint8_t* data, uint32_t length,
    418                                       uint32_t type,
    419                                       nsIInterfaceRequestor* ctx) {
    420  // We currently only handle CA certificates.
    421  if (type != nsIX509Cert::CA_CERT) {
    422    return NS_ERROR_FAILURE;
    423  }
    424 
    425  nsTArray<nsTArray<uint8_t>> certsArray;
    426  nsresult rv = getCertsFromPackage(certsArray, data, length);
    427  if (NS_FAILED(rv)) {
    428    return rv;
    429  }
    430 
    431  nsCOMPtr<nsIMutableArray> array = nsArrayBase::Create();
    432  if (!array) {
    433    return NS_ERROR_FAILURE;
    434  }
    435 
    436  // Now let's create some certs to work with
    437  for (nsTArray<uint8_t>& certDER : certsArray) {
    438    nsCOMPtr<nsIX509Cert> cert = new nsNSSCertificate(std::move(certDER));
    439    nsresult rv = array->AppendElement(cert);
    440    if (NS_FAILED(rv)) {
    441      return rv;
    442    }
    443  }
    444 
    445  return handleCACertDownload(WrapNotNull(array), ctx);
    446 }
    447 
    448 /**
    449 * Decodes a given array of DER-encoded certificates into temporary storage.
    450 *
    451 * @param certs
    452 *        Array in which the decoded certificates are stored as arrays of
    453 *        unsigned chars.
    454 * @param temporaryCerts
    455 *        List of decoded certificates.
    456 */
    457 static nsresult ImportCertsIntoTempStorage(
    458    nsTArray<nsTArray<uint8_t>>& certs,
    459    /*out*/ const UniqueCERTCertList& temporaryCerts) {
    460  NS_ENSURE_ARG_POINTER(temporaryCerts);
    461 
    462  for (nsTArray<uint8_t>& certDER : certs) {
    463    CERTCertificate* certificate;
    464    SECItem certItem;
    465    certItem.len = certDER.Length();
    466    certItem.data = certDER.Elements();
    467    certificate = CERT_NewTempCertificate(CERT_GetDefaultCertDB(), &certItem,
    468                                          nullptr, false, true);
    469 
    470    UniqueCERTCertificate cert(certificate);
    471    if (!cert) {
    472      continue;
    473    }
    474 
    475    if (CERT_AddCertToListTail(temporaryCerts.get(), cert.get()) ==
    476        SECSuccess) {
    477      (void)cert.release();
    478    }
    479  }
    480 
    481  return NS_OK;
    482 }
    483 
    484 NS_IMETHODIMP
    485 nsNSSCertificateDB::ImportEmailCertificate(uint8_t* data, uint32_t length,
    486                                           nsIInterfaceRequestor* ctx) {
    487  nsTArray<nsTArray<uint8_t>> certsArray;
    488 
    489  nsresult rv = getCertsFromPackage(certsArray, data, length);
    490  if (NS_FAILED(rv)) {
    491    return rv;
    492  }
    493 
    494  UniqueCERTCertList temporaryCerts(CERT_NewCertList());
    495  if (!temporaryCerts) {
    496    return NS_ERROR_FAILURE;
    497  }
    498 
    499  rv = ImportCertsIntoTempStorage(certsArray, temporaryCerts);
    500  if (NS_FAILED(rv)) {
    501    return rv;
    502  }
    503 
    504  return ImportCertsIntoPermanentStorage(temporaryCerts);
    505 }
    506 
    507 nsresult nsNSSCertificateDB::ImportCACerts(nsTArray<nsTArray<uint8_t>>& caCerts,
    508                                           nsIInterfaceRequestor* ctx) {
    509  UniqueCERTCertList temporaryCerts(CERT_NewCertList());
    510  if (!temporaryCerts) {
    511    return NS_ERROR_FAILURE;
    512  }
    513 
    514  nsresult rv = ImportCertsIntoTempStorage(caCerts, temporaryCerts);
    515  if (NS_FAILED(rv)) {
    516    return rv;
    517  }
    518 
    519  return ImportCertsIntoPermanentStorage(temporaryCerts);
    520 }
    521 
    522 void nsNSSCertificateDB::DisplayCertificateAlert(nsIInterfaceRequestor* ctx,
    523                                                 const char* stringID,
    524                                                 nsIX509Cert* certToShow) {
    525  if (!NS_IsMainThread()) {
    526    NS_ERROR(
    527        "nsNSSCertificateDB::DisplayCertificateAlert called off the main "
    528        "thread");
    529    return;
    530  }
    531 
    532  nsCOMPtr<nsIInterfaceRequestor> my_ctx = ctx;
    533  if (!my_ctx) {
    534    my_ctx = new PipUIContext();
    535  }
    536 
    537  // This shall be replaced by embedding ovverridable prompts
    538  // as discussed in bug 310446, and should make use of certToShow.
    539 
    540  nsAutoString tmpMessage;
    541  GetPIPNSSBundleString(stringID, tmpMessage);
    542  nsCOMPtr<nsIPrompt> prompt(do_GetInterface(my_ctx));
    543  if (!prompt) {
    544    return;
    545  }
    546 
    547  prompt->Alert(nullptr, tmpMessage.get());
    548 }
    549 
    550 NS_IMETHODIMP
    551 nsNSSCertificateDB::ImportUserCertificate(uint8_t* data, uint32_t length,
    552                                          nsIInterfaceRequestor* ctx) {
    553  if (!NS_IsMainThread()) {
    554    NS_ERROR(
    555        "nsNSSCertificateDB::ImportUserCertificate called off the main thread");
    556    return NS_ERROR_NOT_SAME_THREAD;
    557  }
    558 
    559  nsTArray<nsTArray<uint8_t>> certsArray;
    560 
    561  nsresult rv = getCertsFromPackage(certsArray, data, length);
    562  if (NS_FAILED(rv)) {
    563    return rv;
    564  }
    565 
    566  SECItem certItem;
    567 
    568  if (certsArray.IsEmpty()) {
    569    return NS_OK;
    570  }
    571 
    572  certItem.len = certsArray.ElementAt(0).Length();
    573  certItem.data = certsArray.ElementAt(0).Elements();
    574 
    575  UniqueCERTCertificate cert(CERT_NewTempCertificate(
    576      CERT_GetDefaultCertDB(), &certItem, nullptr, false, true));
    577  if (!cert) {
    578    return NS_ERROR_FAILURE;
    579  }
    580 
    581  UniquePK11SlotInfo slot(PK11_KeyForCertExists(cert.get(), nullptr, ctx));
    582  if (!slot) {
    583    nsCOMPtr<nsIX509Cert> certToShow = new nsNSSCertificate(cert.get());
    584    DisplayCertificateAlert(ctx, "UserCertIgnoredNoPrivateKey", certToShow);
    585    return NS_ERROR_FAILURE;
    586  }
    587  slot = nullptr;
    588 
    589  /* pick a nickname for the cert */
    590  nsAutoCString nickname;
    591  if (cert->nickname) {
    592    nickname = cert->nickname;
    593  } else {
    594    get_default_nickname(cert.get(), ctx, nickname);
    595  }
    596 
    597  /* user wants to import the cert */
    598  slot.reset(PK11_ImportCertForKey(cert.get(), nickname.get(), ctx));
    599  if (!slot) {
    600    return NS_ERROR_FAILURE;
    601  }
    602  slot = nullptr;
    603 
    604  {
    605    nsCOMPtr<nsIX509Cert> certToShow = new nsNSSCertificate(cert.get());
    606    DisplayCertificateAlert(ctx, "UserCertImported", certToShow);
    607  }
    608 
    609  rv = NS_OK;
    610  if (!certsArray.IsEmpty()) {
    611    certsArray.RemoveElementAt(0);
    612    rv = ImportCACerts(certsArray, ctx);
    613  }
    614 
    615  nsCOMPtr<nsIObserverService> observerService =
    616      mozilla::services::GetObserverService();
    617  if (observerService) {
    618    observerService->NotifyObservers(nullptr, "psm:user-certificate-added",
    619                                     nullptr);
    620  }
    621 
    622  return rv;
    623 }
    624 
    625 NS_IMETHODIMP
    626 nsNSSCertificateDB::DeleteCertificate(nsIX509Cert* aCert) {
    627  NS_ENSURE_ARG_POINTER(aCert);
    628  UniqueCERTCertificate cert(aCert->GetCert());
    629  if (!cert) {
    630    return NS_ERROR_FAILURE;
    631  }
    632 
    633  // Temporary certificates aren't on a slot and will go away when the
    634  // nsIX509Cert is destructed.
    635  if (cert->slot) {
    636    uint32_t certType;
    637    nsresult rv = aCert->GetCertType(&certType);
    638    if (NS_WARN_IF(NS_FAILED(rv))) {
    639      return rv;
    640    }
    641    if (certType == nsIX509Cert::USER_CERT) {
    642      SECStatus srv = PK11_Authenticate(cert->slot, true, nullptr);
    643      if (srv != SECSuccess) {
    644        return NS_ERROR_FAILURE;
    645      }
    646      srv = PK11_DeleteTokenCertAndKey(cert.get(), nullptr);
    647      if (srv != SECSuccess) {
    648        return NS_ERROR_FAILURE;
    649      }
    650    } else {
    651      // For certificates that can't be deleted (e.g. built-in roots), un-set
    652      // all trust bits.
    653      nsNSSCertTrust trust(0, 0);
    654      SECStatus srv = ChangeCertTrustWithPossibleAuthentication(
    655          cert, trust.GetTrust(), nullptr);
    656      if (srv != SECSuccess) {
    657        return NS_ERROR_FAILURE;
    658      }
    659      if (!PK11_IsReadOnly(cert->slot)) {
    660        srv = SEC_DeletePermCertificate(cert.get());
    661        if (srv != SECSuccess) {
    662          return NS_ERROR_FAILURE;
    663        }
    664      }
    665    }
    666  }
    667 
    668  nsCOMPtr<nsIObserverService> observerService =
    669      mozilla::services::GetObserverService();
    670  if (observerService) {
    671    observerService->NotifyObservers(nullptr, "psm:user-certificate-deleted",
    672                                     nullptr);
    673  }
    674 
    675  return NS_OK;
    676 }
    677 
    678 NS_IMETHODIMP
    679 nsNSSCertificateDB::SetCertTrust(nsIX509Cert* cert, uint32_t type,
    680                                 uint32_t trusted) {
    681  NS_ENSURE_ARG_POINTER(cert);
    682  nsNSSCertTrust trust;
    683  switch (type) {
    684    case nsIX509Cert::CA_CERT:
    685      trust.SetValidCA();
    686      trust.AddCATrust(!!(trusted & nsIX509CertDB::TRUSTED_SSL),
    687                       !!(trusted & nsIX509CertDB::TRUSTED_EMAIL));
    688      break;
    689    case nsIX509Cert::SERVER_CERT:
    690      trust.SetValidPeer();
    691      trust.AddPeerTrust(trusted & nsIX509CertDB::TRUSTED_SSL, false);
    692      break;
    693    case nsIX509Cert::EMAIL_CERT:
    694      trust.SetValidPeer();
    695      trust.AddPeerTrust(false, !!(trusted & nsIX509CertDB::TRUSTED_EMAIL));
    696      break;
    697    default:
    698      // Ignore any other type of certificate (including invalid types).
    699      return NS_OK;
    700  }
    701 
    702  UniqueCERTCertificate nsscert(cert->GetCert());
    703  SECStatus srv = ChangeCertTrustWithPossibleAuthentication(
    704      nsscert, trust.GetTrust(), nullptr);
    705  return MapSECStatus(srv);
    706 }
    707 
    708 NS_IMETHODIMP
    709 nsNSSCertificateDB::IsCertTrusted(nsIX509Cert* cert, uint32_t certType,
    710                                  uint32_t trustType, bool* _isTrusted) {
    711  NS_ENSURE_ARG_POINTER(_isTrusted);
    712  *_isTrusted = false;
    713 
    714  nsresult rv = BlockUntilLoadableCertsLoaded();
    715  if (NS_FAILED(rv)) {
    716    return rv;
    717  }
    718 
    719  SECStatus srv;
    720  UniqueCERTCertificate nsscert(cert->GetCert());
    721  CERTCertTrust nsstrust;
    722  srv = CERT_GetCertTrust(nsscert.get(), &nsstrust);
    723  if (srv != SECSuccess) {
    724    // CERT_GetCertTrust returns SECFailure if given a temporary cert that
    725    // doesn't have any trust information yet. This isn't an error.
    726    return NS_OK;
    727  }
    728 
    729  nsNSSCertTrust trust(&nsstrust);
    730  if (certType == nsIX509Cert::CA_CERT) {
    731    if (trustType & nsIX509CertDB::TRUSTED_SSL) {
    732      *_isTrusted = trust.HasTrustedCA(true, false);
    733    } else if (trustType & nsIX509CertDB::TRUSTED_EMAIL) {
    734      *_isTrusted = trust.HasTrustedCA(false, true);
    735    } else {
    736      return NS_ERROR_FAILURE;
    737    }
    738  } else if (certType == nsIX509Cert::SERVER_CERT) {
    739    if (trustType & nsIX509CertDB::TRUSTED_SSL) {
    740      *_isTrusted = trust.HasTrustedPeer(true, false);
    741    } else if (trustType & nsIX509CertDB::TRUSTED_EMAIL) {
    742      *_isTrusted = trust.HasTrustedPeer(false, true);
    743    } else {
    744      return NS_ERROR_FAILURE;
    745    }
    746  } else if (certType == nsIX509Cert::EMAIL_CERT) {
    747    if (trustType & nsIX509CertDB::TRUSTED_SSL) {
    748      *_isTrusted = trust.HasTrustedPeer(true, false);
    749    } else if (trustType & nsIX509CertDB::TRUSTED_EMAIL) {
    750      *_isTrusted = trust.HasTrustedPeer(false, true);
    751    } else {
    752      return NS_ERROR_FAILURE;
    753    }
    754  } /* user: ignore */
    755  return NS_OK;
    756 }
    757 
    758 NS_IMETHODIMP
    759 nsNSSCertificateDB::ImportCertsFromFile(nsIFile* aFile, uint32_t aType) {
    760  NS_ENSURE_ARG(aFile);
    761  switch (aType) {
    762    case nsIX509Cert::CA_CERT:
    763    case nsIX509Cert::EMAIL_CERT:
    764      // good
    765      break;
    766 
    767    default:
    768      // not supported (yet)
    769      return NS_ERROR_FAILURE;
    770  }
    771 
    772  PRFileDesc* fd = nullptr;
    773  nsresult rv = aFile->OpenNSPRFileDesc(PR_RDONLY, 0, &fd);
    774  if (NS_FAILED(rv)) {
    775    return rv;
    776  }
    777  if (!fd) {
    778    return NS_ERROR_FAILURE;
    779  }
    780 
    781  PRFileInfo fileInfo;
    782  if (PR_GetOpenFileInfo(fd, &fileInfo) != PR_SUCCESS) {
    783    return NS_ERROR_FAILURE;
    784  }
    785 
    786  auto buf = MakeUnique<unsigned char[]>(fileInfo.size);
    787  int32_t bytesObtained = PR_Read(fd, buf.get(), fileInfo.size);
    788  PR_Close(fd);
    789 
    790  if (bytesObtained != fileInfo.size) {
    791    return NS_ERROR_FAILURE;
    792  }
    793 
    794  nsCOMPtr<nsIInterfaceRequestor> cxt = new PipUIContext();
    795 
    796  switch (aType) {
    797    case nsIX509Cert::CA_CERT:
    798      return ImportCertificates(buf.get(), bytesObtained, aType, cxt);
    799    case nsIX509Cert::EMAIL_CERT:
    800      return ImportEmailCertificate(buf.get(), bytesObtained, cxt);
    801    default:
    802      MOZ_ASSERT(false, "Unsupported type should have been filtered out");
    803      break;
    804  }
    805 
    806  return NS_ERROR_FAILURE;
    807 }
    808 
    809 NS_IMETHODIMP
    810 nsNSSCertificateDB::ImportPKCS12File(nsIFile* aFile, const nsAString& aPassword,
    811                                     uint32_t* aError) {
    812  if (!NS_IsMainThread()) {
    813    return NS_ERROR_NOT_SAME_THREAD;
    814  }
    815  nsresult rv = BlockUntilLoadableCertsLoaded();
    816  if (NS_FAILED(rv)) {
    817    return rv;
    818  }
    819 
    820  NS_ENSURE_ARG(aFile);
    821  nsPKCS12Blob blob;
    822  rv = blob.ImportFromFile(aFile, aPassword, *aError);
    823  nsCOMPtr<nsIObserverService> observerService =
    824      mozilla::services::GetObserverService();
    825  if (NS_SUCCEEDED(rv) && observerService) {
    826    observerService->NotifyObservers(nullptr, "psm:user-certificate-added",
    827                                     nullptr);
    828  }
    829 
    830  return rv;
    831 }
    832 
    833 NS_IMETHODIMP
    834 nsNSSCertificateDB::ExportPKCS12File(
    835    nsIFile* aFile, const nsTArray<RefPtr<nsIX509Cert>>& aCerts,
    836    const nsAString& aPassword, uint32_t* aError) {
    837  if (!NS_IsMainThread()) {
    838    return NS_ERROR_NOT_SAME_THREAD;
    839  }
    840  nsresult rv = BlockUntilLoadableCertsLoaded();
    841  if (NS_FAILED(rv)) {
    842    return rv;
    843  }
    844 
    845  NS_ENSURE_ARG(aFile);
    846  if (aCerts.IsEmpty()) {
    847    return NS_OK;
    848  }
    849  nsPKCS12Blob blob;
    850  return blob.ExportToFile(aFile, aCerts, aPassword, *aError);
    851 }
    852 
    853 NS_IMETHODIMP
    854 nsNSSCertificateDB::ConstructX509FromBase64(const nsACString& base64,
    855                                            /*out*/ nsIX509Cert** _retval) {
    856  if (!_retval) {
    857    return NS_ERROR_INVALID_POINTER;
    858  }
    859 
    860  // Base64Decode() doesn't consider a zero length input as an error, and just
    861  // returns the empty string. We don't want this behavior, so the below check
    862  // catches this case.
    863  if (base64.Length() < 1) {
    864    return NS_ERROR_ILLEGAL_VALUE;
    865  }
    866 
    867  nsAutoCString certDER;
    868  nsresult rv = Base64Decode(base64, certDER);
    869  if (NS_FAILED(rv)) {
    870    return rv;
    871  }
    872 
    873  return ConstructX509FromSpan(AsBytes(Span(certDER)), _retval);
    874 }
    875 
    876 NS_IMETHODIMP
    877 nsNSSCertificateDB::ConstructX509(const nsTArray<uint8_t>& certDER,
    878                                  nsIX509Cert** _retval) {
    879  return ConstructX509FromSpan(Span(certDER.Elements(), certDER.Length()),
    880                               _retval);
    881 }
    882 
    883 nsresult nsNSSCertificateDB::ConstructX509FromSpan(
    884    Span<const uint8_t> aInputSpan, nsIX509Cert** _retval) {
    885  if (NS_WARN_IF(!_retval)) {
    886    return NS_ERROR_INVALID_POINTER;
    887  }
    888 
    889  if (aInputSpan.Length() > std::numeric_limits<unsigned int>::max()) {
    890    return NS_ERROR_ILLEGAL_VALUE;
    891  }
    892 
    893  SECItem certData;
    894  certData.type = siDERCertBuffer;
    895  certData.data = const_cast<unsigned char*>(
    896      reinterpret_cast<const unsigned char*>(aInputSpan.Elements()));
    897  certData.len = aInputSpan.Length();
    898 
    899  UniqueCERTCertificate cert(CERT_NewTempCertificate(
    900      CERT_GetDefaultCertDB(), &certData, nullptr, false, true));
    901  if (!cert)
    902    return (PORT_GetError() == SEC_ERROR_NO_MEMORY) ? NS_ERROR_OUT_OF_MEMORY
    903                                                    : NS_ERROR_FAILURE;
    904 
    905  nsCOMPtr<nsIX509Cert> nssCert = new nsNSSCertificate(cert.get());
    906  nssCert.forget(_retval);
    907  return NS_OK;
    908 }
    909 
    910 void nsNSSCertificateDB::get_default_nickname(CERTCertificate* cert,
    911                                              nsIInterfaceRequestor* ctx,
    912                                              nsCString& nickname) {
    913  nickname.Truncate();
    914 
    915  CK_OBJECT_HANDLE keyHandle;
    916 
    917  if (NS_FAILED(BlockUntilLoadableCertsLoaded())) {
    918    return;
    919  }
    920 
    921  CERTCertDBHandle* defaultcertdb = CERT_GetDefaultCertDB();
    922  nsAutoCString username;
    923  UniquePORTString tempCN(CERT_GetCommonName(&cert->subject));
    924  if (tempCN) {
    925    username = tempCN.get();
    926  }
    927 
    928  nsAutoCString caname;
    929  UniquePORTString tempIssuerOrg(CERT_GetOrgName(&cert->issuer));
    930  if (tempIssuerOrg) {
    931    caname = tempIssuerOrg.get();
    932  }
    933 
    934  nsAutoString tmpNickFmt;
    935  GetPIPNSSBundleString("nick_template", tmpNickFmt);
    936  NS_ConvertUTF16toUTF8 nickFmt(tmpNickFmt);
    937 
    938  nsAutoCString baseName;
    939  baseName.AppendPrintf(nickFmt.get(), username.get(), caname.get());
    940  if (baseName.IsEmpty()) {
    941    return;
    942  }
    943 
    944  nickname = baseName;
    945 
    946  /*
    947   * We need to see if the private key exists on a token, if it does
    948   * then we need to check for nicknames that already exist on the smart
    949   * card.
    950   */
    951  UniquePK11SlotInfo slot(PK11_KeyForCertExists(cert, &keyHandle, ctx));
    952  if (!slot) return;
    953 
    954  if (!PK11_IsInternal(slot.get())) {
    955    nsAutoCString tmp;
    956    tmp.AppendPrintf("%s:%s", PK11_GetTokenName(slot.get()), baseName.get());
    957    if (tmp.IsEmpty()) {
    958      nickname.Truncate();
    959      return;
    960    }
    961    baseName = tmp;
    962    nickname = baseName;
    963  }
    964 
    965  int count = 1;
    966  while (true) {
    967    if (count > 1) {
    968      nsAutoCString tmp;
    969      tmp.AppendPrintf("%s #%d", baseName.get(), count);
    970      if (tmp.IsEmpty()) {
    971        nickname.Truncate();
    972        return;
    973      }
    974      nickname = tmp;
    975    }
    976 
    977    UniqueCERTCertificate dummycert;
    978 
    979    if (PK11_IsInternal(slot.get())) {
    980      /* look up the nickname to make sure it isn't in use already */
    981      dummycert.reset(CERT_FindCertByNickname(defaultcertdb, nickname.get()));
    982    } else {
    983      // Check the cert against others that already live on the smart card.
    984      dummycert.reset(PK11_FindCertFromNickname(nickname.get(), ctx));
    985      if (dummycert) {
    986        // Make sure the subject names are different.
    987        if (CERT_CompareName(&cert->subject, &dummycert->subject) == SECEqual) {
    988          /*
    989           * There is another certificate with the same nickname and
    990           * the same subject name on the smart card, so let's use this
    991           * nickname.
    992           */
    993          dummycert = nullptr;
    994        }
    995      }
    996    }
    997    if (!dummycert) {
    998      break;
    999    }
   1000    count++;
   1001  }
   1002 }
   1003 
   1004 NS_IMETHODIMP
   1005 nsNSSCertificateDB::AddCertFromBase64(const nsACString& aBase64,
   1006                                      const nsACString& aTrust,
   1007                                      nsIX509Cert** addedCertificate) {
   1008  // Base64Decode() doesn't consider a zero length input as an error, and just
   1009  // returns the empty string. We don't want this behavior, so the below check
   1010  // catches this case.
   1011  if (aBase64.Length() < 1) {
   1012    return NS_ERROR_ILLEGAL_VALUE;
   1013  }
   1014 
   1015  nsAutoCString aCertDER;
   1016  nsresult rv = Base64Decode(aBase64, aCertDER);
   1017  if (NS_FAILED(rv)) {
   1018    return rv;
   1019  }
   1020  return AddCert(aCertDER, aTrust, addedCertificate);
   1021 }
   1022 
   1023 NS_IMETHODIMP
   1024 nsNSSCertificateDB::AddCert(const nsACString& aCertDER,
   1025                            const nsACString& aTrust,
   1026                            nsIX509Cert** addedCertificate) {
   1027  MOZ_ASSERT(addedCertificate);
   1028  if (!addedCertificate) {
   1029    return NS_ERROR_INVALID_ARG;
   1030  }
   1031  *addedCertificate = nullptr;
   1032 
   1033  nsNSSCertTrust trust;
   1034  if (CERT_DecodeTrustString(&trust.GetTrust(),
   1035                             PromiseFlatCString(aTrust).get()) != SECSuccess) {
   1036    return NS_ERROR_FAILURE;
   1037  }
   1038 
   1039  nsCOMPtr<nsIX509Cert> newCert;
   1040  nsresult rv =
   1041      ConstructX509FromSpan(AsBytes(Span(aCertDER)), getter_AddRefs(newCert));
   1042  if (NS_FAILED(rv)) {
   1043    return rv;
   1044  }
   1045 
   1046  UniqueCERTCertificate tmpCert(newCert->GetCert());
   1047  if (!tmpCert) {
   1048    return NS_ERROR_FAILURE;
   1049  }
   1050 
   1051  // If there's already a certificate that matches this one in the database, we
   1052  // still want to set its trust to the given value.
   1053  if (tmpCert->isperm) {
   1054    rv = SetCertTrustFromString(newCert, aTrust);
   1055    if (NS_FAILED(rv)) {
   1056      return rv;
   1057    }
   1058    newCert.forget(addedCertificate);
   1059    return NS_OK;
   1060  }
   1061 
   1062  UniquePORTString nickname(CERT_MakeCANickname(tmpCert.get()));
   1063 
   1064  MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
   1065          ("Created nick \"%s\"\n", nickname.get()));
   1066 
   1067  UniquePK11SlotInfo slot(PK11_GetInternalKeySlot());
   1068  SECStatus srv = PK11_ImportCert(slot.get(), tmpCert.get(), CK_INVALID_HANDLE,
   1069                                  nickname.get(),
   1070                                  false);  // this parameter is ignored by NSS
   1071  if (srv != SECSuccess) {
   1072    return MapSECStatus(srv);
   1073  }
   1074  srv = ChangeCertTrustWithPossibleAuthentication(tmpCert, trust.GetTrust(),
   1075                                                  nullptr);
   1076  if (srv != SECSuccess) {
   1077    return MapSECStatus(srv);
   1078  }
   1079  newCert.forget(addedCertificate);
   1080  return NS_OK;
   1081 }
   1082 
   1083 NS_IMETHODIMP
   1084 nsNSSCertificateDB::SetCertTrustFromString(nsIX509Cert* cert,
   1085                                           const nsACString& trustString) {
   1086  NS_ENSURE_ARG(cert);
   1087 
   1088  CERTCertTrust trust;
   1089  SECStatus srv =
   1090      CERT_DecodeTrustString(&trust, PromiseFlatCString(trustString).get());
   1091  if (srv != SECSuccess) {
   1092    return MapSECStatus(srv);
   1093  }
   1094  UniqueCERTCertificate nssCert(cert->GetCert());
   1095 
   1096  srv = ChangeCertTrustWithPossibleAuthentication(nssCert, trust, nullptr);
   1097  return MapSECStatus(srv);
   1098 }
   1099 
   1100 NS_IMETHODIMP nsNSSCertificateDB::AsPKCS7Blob(
   1101    const nsTArray<RefPtr<nsIX509Cert>>& certList, nsACString& _retval) {
   1102  if (certList.IsEmpty()) {
   1103    return NS_ERROR_INVALID_ARG;
   1104  }
   1105 
   1106  UniqueNSSCMSMessage cmsg(NSS_CMSMessage_Create(nullptr));
   1107  if (!cmsg) {
   1108    MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
   1109            ("nsNSSCertificateDB::AsPKCS7Blob - can't create CMS message"));
   1110    return NS_ERROR_OUT_OF_MEMORY;
   1111  }
   1112 
   1113  UniqueNSSCMSSignedData sigd(nullptr);
   1114  for (const auto& cert : certList) {
   1115    // We need an owning handle when calling nsIX509Cert::GetCert().
   1116    UniqueCERTCertificate nssCert(cert->GetCert());
   1117    if (!sigd) {
   1118      sigd.reset(
   1119          NSS_CMSSignedData_CreateCertsOnly(cmsg.get(), nssCert.get(), false));
   1120      if (!sigd) {
   1121        MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
   1122                ("nsNSSCertificateDB::AsPKCS7Blob - can't create SignedData"));
   1123        return NS_ERROR_FAILURE;
   1124      }
   1125    } else if (NSS_CMSSignedData_AddCertificate(sigd.get(), nssCert.get()) !=
   1126               SECSuccess) {
   1127      MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
   1128              ("nsNSSCertificateDB::AsPKCS7Blob - can't add cert"));
   1129      return NS_ERROR_FAILURE;
   1130    }
   1131  }
   1132 
   1133  NSSCMSContentInfo* cinfo = NSS_CMSMessage_GetContentInfo(cmsg.get());
   1134  if (NSS_CMSContentInfo_SetContent_SignedData(cmsg.get(), cinfo, sigd.get()) !=
   1135      SECSuccess) {
   1136    MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
   1137            ("nsNSSCertificateDB::AsPKCS7Blob - can't attach SignedData"));
   1138    return NS_ERROR_FAILURE;
   1139  }
   1140  // cmsg owns sigd now.
   1141  (void)sigd.release();
   1142 
   1143  UniquePLArenaPool arena(PORT_NewArena(1024));
   1144  if (!arena) {
   1145    MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
   1146            ("nsNSSCertificateDB::AsPKCS7Blob - out of memory"));
   1147    return NS_ERROR_OUT_OF_MEMORY;
   1148  }
   1149 
   1150  SECItem certP7 = {siBuffer, nullptr, 0};
   1151  NSSCMSEncoderContext* ecx = NSS_CMSEncoder_Start(
   1152      cmsg.get(), nullptr, nullptr, &certP7, arena.get(), nullptr, nullptr,
   1153      nullptr, nullptr, nullptr, nullptr);
   1154  if (!ecx) {
   1155    MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
   1156            ("nsNSSCertificateDB::AsPKCS7Blob - can't create encoder"));
   1157    return NS_ERROR_FAILURE;
   1158  }
   1159 
   1160  if (NSS_CMSEncoder_Finish(ecx) != SECSuccess) {
   1161    MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
   1162            ("nsNSSCertificateDB::AsPKCS7Blob - failed to add encoded data"));
   1163    return NS_ERROR_FAILURE;
   1164  }
   1165 
   1166  _retval.Assign(nsDependentCSubstring(
   1167      reinterpret_cast<const char*>(certP7.data), certP7.len));
   1168  return NS_OK;
   1169 }
   1170 
   1171 NS_IMETHODIMP
   1172 nsNSSCertificateDB::GetCerts(nsTArray<RefPtr<nsIX509Cert>>& _retval) {
   1173  nsresult rv = BlockUntilLoadableCertsLoaded();
   1174  if (NS_FAILED(rv)) {
   1175    return rv;
   1176  }
   1177 
   1178  rv = CheckForSmartCardChanges();
   1179  if (NS_FAILED(rv)) {
   1180    return rv;
   1181  }
   1182 
   1183  nsCOMPtr<nsIInterfaceRequestor> ctx = new PipUIContext();
   1184  AutoSearchingForClientAuthCertificates _;
   1185  UniqueCERTCertList certList(PK11_ListCerts(PK11CertListUnique, ctx));
   1186  if (!certList) {
   1187    return NS_ERROR_FAILURE;
   1188  }
   1189  return nsNSSCertificateDB::ConstructCertArrayFromUniqueCertList(certList,
   1190                                                                  _retval);
   1191 }
   1192 
   1193 static mozilla::Result<VerifyUsage, nsresult> MapX509UsageToVerifierUsage(
   1194    nsIX509CertDB::VerifyUsage usage) {
   1195  switch (usage) {
   1196    case nsIX509CertDB::verifyUsageTLSServer:
   1197      return VerifyUsage::TLSServer;
   1198    case nsIX509CertDB::verifyUsageTLSServerCA:
   1199      return VerifyUsage::TLSServerCA;
   1200    case nsIX509CertDB::verifyUsageTLSClient:
   1201      return VerifyUsage::TLSClient;
   1202    case nsIX509CertDB::verifyUsageTLSClientCA:
   1203      return VerifyUsage::TLSClientCA;
   1204    case nsIX509CertDB::verifyUsageEmailSigner:
   1205      return VerifyUsage::EmailSigner;
   1206    case nsIX509CertDB::verifyUsageEmailRecipient:
   1207      return VerifyUsage::EmailRecipient;
   1208    case nsIX509CertDB::verifyUsageEmailCA:
   1209      return VerifyUsage::EmailCA;
   1210  }
   1211  return Err(NS_ERROR_INVALID_ARG);
   1212 }
   1213 
   1214 nsresult VerifyCertAtTime(nsIX509Cert* aCert, nsIX509CertDB::VerifyUsage aUsage,
   1215                          uint32_t aFlags, const nsACString& aHostname,
   1216                          mozilla::pkix::Time aTime,
   1217                          const Maybe<nsTArray<uint8_t>>& aSctsFromTls,
   1218                          nsTArray<RefPtr<nsIX509Cert>>& aVerifiedChain,
   1219                          bool* aHasEVPolicy,
   1220                          int32_t* /*PRErrorCode*/ _retval) {
   1221  NS_ENSURE_ARG_POINTER(aCert);
   1222  NS_ENSURE_ARG_POINTER(aHasEVPolicy);
   1223  NS_ENSURE_ARG_POINTER(_retval);
   1224 
   1225  if (!aVerifiedChain.IsEmpty()) {
   1226    return NS_ERROR_INVALID_ARG;
   1227  }
   1228 
   1229  *aHasEVPolicy = false;
   1230  *_retval = PR_UNKNOWN_ERROR;
   1231 
   1232  RefPtr<SharedCertVerifier> certVerifier(GetDefaultCertVerifier());
   1233  NS_ENSURE_TRUE(certVerifier, NS_ERROR_FAILURE);
   1234 
   1235  nsTArray<nsTArray<uint8_t>> resultChain;
   1236  EVStatus evStatus;
   1237  mozilla::pkix::Result result;
   1238 
   1239  nsTArray<uint8_t> certBytes;
   1240  nsresult nsrv = aCert->GetRawDER(certBytes);
   1241  if (NS_FAILED(nsrv)) {
   1242    return nsrv;
   1243  }
   1244 
   1245  if (!aHostname.IsVoid() && aUsage == nsIX509CertDB::verifyUsageTLSServer) {
   1246    result =
   1247        certVerifier->VerifySSLServerCert(certBytes, aTime,
   1248                                          nullptr,  // Assume no context
   1249                                          aHostname, resultChain, aFlags,
   1250                                          Nothing(),     // extraCertificates
   1251                                          Nothing(),     // stapledOCSPResponse
   1252                                          aSctsFromTls,  // sctsFromTLSExtension
   1253                                          Nothing(),     // dcInfo
   1254                                          OriginAttributes(), &evStatus);
   1255  } else {
   1256    const nsCString& flatHostname = PromiseFlatCString(aHostname);
   1257    VerifyUsage vu = MOZ_TRY(MapX509UsageToVerifierUsage(aUsage));
   1258    result = certVerifier->VerifyCert(
   1259        certBytes, vu, aTime,
   1260        nullptr,  // Assume no context
   1261        aHostname.IsVoid() ? nullptr : flatHostname.get(), resultChain, aFlags,
   1262        Nothing(),     // extraCertificates
   1263        Nothing(),     // stapledOCSPResponse
   1264        aSctsFromTls,  // sctsFromTLSExtension
   1265        OriginAttributes(), &evStatus);
   1266  }
   1267 
   1268  if (result == mozilla::pkix::Success) {
   1269    for (auto& certDER : resultChain) {
   1270      RefPtr<nsIX509Cert> cert = new nsNSSCertificate(std::move(certDER));
   1271      aVerifiedChain.AppendElement(cert);
   1272    }
   1273 
   1274    if (evStatus == EVStatus::EV) {
   1275      *aHasEVPolicy = true;
   1276    }
   1277  }
   1278 
   1279  *_retval = mozilla::pkix::MapResultToPRErrorCode(result);
   1280 
   1281  return NS_OK;
   1282 }
   1283 
   1284 class VerifyCertAtTimeTask final : public CryptoTask {
   1285 public:
   1286  VerifyCertAtTimeTask(nsIX509Cert* aCert, nsIX509CertDB::VerifyUsage aUsage,
   1287                       uint32_t aFlags, const nsACString& aHostname,
   1288                       uint64_t aTime, const nsTArray<uint8_t>& aSctsFromTls,
   1289                       nsICertVerificationCallback* aCallback)
   1290      : mCert(aCert),
   1291        mUsage(aUsage),
   1292        mFlags(aFlags),
   1293        mHostname(aHostname),
   1294        mTime(aTime),
   1295        mCallback(new nsMainThreadPtrHolder<nsICertVerificationCallback>(
   1296            "nsICertVerificationCallback", aCallback)),
   1297        mPRErrorCode(SEC_ERROR_LIBRARY_FAILURE),
   1298        mHasEVPolicy(false) {
   1299    if (aSctsFromTls.Length() > 0) {
   1300      mSctsFromTls.emplace(aSctsFromTls.Clone());
   1301    }
   1302  }
   1303 
   1304 private:
   1305  virtual nsresult CalculateResult() override {
   1306    nsCOMPtr<nsIX509CertDB> certDB = do_GetService(NS_X509CERTDB_CONTRACTID);
   1307    if (!certDB) {
   1308      return NS_ERROR_FAILURE;
   1309    }
   1310    return VerifyCertAtTime(mCert, mUsage, mFlags, mHostname,
   1311                            mozilla::pkix::TimeFromEpochInSeconds(mTime),
   1312                            mSctsFromTls, mVerifiedCertList, &mHasEVPolicy,
   1313                            &mPRErrorCode);
   1314  }
   1315 
   1316  virtual void CallCallback(nsresult rv) override {
   1317    if (NS_FAILED(rv)) {
   1318      nsTArray<RefPtr<nsIX509Cert>> tmp;
   1319      (void)mCallback->VerifyCertFinished(SEC_ERROR_LIBRARY_FAILURE, tmp,
   1320                                          false);
   1321    } else {
   1322      (void)mCallback->VerifyCertFinished(mPRErrorCode, mVerifiedCertList,
   1323                                          mHasEVPolicy);
   1324    }
   1325  }
   1326 
   1327  nsCOMPtr<nsIX509Cert> mCert;
   1328  nsIX509CertDB::VerifyUsage mUsage;
   1329  uint32_t mFlags;
   1330  nsCString mHostname;
   1331  uint64_t mTime;
   1332  nsMainThreadPtrHandle<nsICertVerificationCallback> mCallback;
   1333  int32_t mPRErrorCode;
   1334  nsTArray<RefPtr<nsIX509Cert>> mVerifiedCertList;
   1335  bool mHasEVPolicy;
   1336  Maybe<nsTArray<uint8_t>> mSctsFromTls;
   1337 };
   1338 
   1339 NS_IMETHODIMP
   1340 nsNSSCertificateDB::AsyncVerifyCertAtTime(
   1341    nsIX509Cert* aCert, nsIX509CertDB::VerifyUsage aUsage, uint32_t aFlags,
   1342    const nsACString& aHostname, uint64_t aTime,
   1343    const nsTArray<uint8_t>& aSctsFromTls,
   1344    nsICertVerificationCallback* aCallback) {
   1345  RefPtr<VerifyCertAtTimeTask> task(new VerifyCertAtTimeTask(
   1346      aCert, aUsage, aFlags, aHostname, aTime, aSctsFromTls, aCallback));
   1347  return task->Dispatch();
   1348 }
   1349 
   1350 NS_IMETHODIMP
   1351 nsNSSCertificateDB::ClearOCSPCache() {
   1352  RefPtr<SharedCertVerifier> certVerifier(GetDefaultCertVerifier());
   1353  NS_ENSURE_TRUE(certVerifier, NS_ERROR_FAILURE);
   1354  certVerifier->ClearOCSPCache();
   1355  return NS_OK;
   1356 }
   1357 
   1358 NS_IMETHODIMP
   1359 nsNSSCertificateDB::GetAndroidCertificateFromAlias(
   1360    const nsAString& aAlias, /*out*/ nsIX509Cert** aResult) {
   1361  *aResult = nullptr;
   1362 #ifndef MOZ_WIDGET_ANDROID
   1363  return NS_ERROR_NOT_AVAILABLE;
   1364 #else
   1365  if (!jni::IsAvailable()) {
   1366    MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("JNI not available"));
   1367    return NS_ERROR_NOT_AVAILABLE;
   1368  }
   1369 
   1370  jni::String::LocalRef alias = jni::StringParam(aAlias);
   1371  jni::ByteArray::LocalRef certificateBytes =
   1372      java::ClientAuthCertificateManager::GetCertificateFromAlias(alias);
   1373  if (!certificateBytes) {
   1374    return NS_ERROR_NOT_AVAILABLE;
   1375  }
   1376  nsTArray<uint8_t> certificateByteArray(
   1377      certificateBytes->GetElements().Elements(), certificateBytes->Length());
   1378  nsCOMPtr<nsIX509Cert> certificate(
   1379      new nsNSSCertificate(std::move(certificateByteArray)));
   1380  certificate.forget(aResult);
   1381  return NS_OK;
   1382 #endif  // MOZ_WIDGET_ANDROID
   1383 }