Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Firefox/security/manager/ssl/   (Browser von der Mozilla Stiftung Version 136.0.1©)  Datei vom 10.2.2025 mit Größe 43 kB image not shown  

Quelle  nsNSSCertificateDB.cpp   Sprache: C

 
/* 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/. */


#include "nsNSSCertificateDB.h"

#include "CertVerifier.h"
#include "CryptoTask.h"
#include "ExtendedValidation.h"
#include "NSSCertDBTrustDomain.h"
#include "certdb.h"
#include "mozilla/glean/SecurityCertverifierMetrics.h"
#include "mozilla/Assertions.h"
#include "mozilla/Base64.h"
#include "mozilla/Casting.h"
#include "mozilla/Logging.h"
#include "mozilla/Services.h"
#include "mozilla/Unused.h"
#include "mozpkix/Time.h"
#include "mozpkix/pkixnss.h"
#include "mozpkix/pkixtypes.h"
#include "nsArray.h"
#include "nsArrayUtils.h"
#include "nsCOMPtr.h"
#include "nsComponentManagerUtils.h"
#include "nsICertificateDialogs.h"
#include "nsIFile.h"
#include "nsIMutableArray.h"
#include "nsIObserverService.h"
#include "nsIPrompt.h"
#include "nsNSSCertHelper.h"
#include "nsNSSCertTrust.h"
#include "nsNSSCertificate.h"
#include "nsNSSComponent.h"
#include "nsNSSHelper.h"
#include "nsPKCS12Blob.h"
#include "nsPromiseFlatString.h"
#include "nsProxyRelease.h"
#include "nsReadableUtils.h"
#include "nsThreadUtils.h"
#include "nspr.h"
#include "secasn1.h"
#include "secder.h"
#include "secerr.h"
#include "ssl.h"

#ifdef XP_WIN
#  include <winsock.h>  // for ntohl
#endif

using namespace mozilla;
using namespace mozilla::psm;

extern LazyLogModule gPIPNSSLog;

NS_IMPL_ISUPPORTS(nsNSSCertificateDB, nsIX509CertDB)

NS_IMETHODIMP
nsNSSCertificateDB::CountTrustObjects(uint32_t* aCount) {
  UniquePK11SlotInfo slot(PK11_GetInternalKeySlot());
  PK11GenericObject* objects =
      PK11_FindGenericObjects(slot.get(), CKO_NSS_TRUST);
  int count = 0;
  for (PK11GenericObject* cursor = objects; cursor;
       cursor = PK11_GetNextGenericObject(cursor)) {
    count++;
  }
  PK11_DestroyGenericObjects(objects);

  mozilla::glean::cert_verifier::trust_obj_count.Set(count);

  *aCount = count;
  return NS_OK;
}

NS_IMETHODIMP
nsNSSCertificateDB::FindCertByDBKey(const nsACString& aDBKey,
                                    /*out*/ nsIX509Cert** _cert) {
  NS_ENSURE_ARG_POINTER(_cert);
  *_cert = nullptr;

  if (aDBKey.IsEmpty()) {
    return NS_ERROR_INVALID_ARG;
  }

  nsresult rv = BlockUntilLoadableCertsLoaded();
  if (NS_FAILED(rv)) {
    return rv;
  }

  UniqueCERTCertificate cert;
  rv = FindCertByDBKey(aDBKey, cert);
  if (NS_FAILED(rv)) {
    return rv;
  }
  // If we can't find the certificate, that's not an error. Just return null.
  if (!cert) {
    return NS_OK;
  }
  nsCOMPtr<nsIX509Cert> nssCert = new nsNSSCertificate(cert.get());
  nssCert.forget(_cert);
  return NS_OK;
}

nsresult nsNSSCertificateDB::FindCertByDBKey(const nsACString& aDBKey,
                                             UniqueCERTCertificate& cert) {
  static_assert(sizeof(uint64_t) == 8, "type size sanity check");
  static_assert(sizeof(uint32_t) == 4, "type size sanity check");
  // (From nsNSSCertificate::GetDbKey)
  // The format of the key is the base64 encoding of the following:
  // 4 bytes: {0, 0, 0, 0} (this was intended to be the module ID, but it was
  //                        never implemented)
  // 4 bytes: {0, 0, 0, 0} (this was intended to be the slot ID, but it was
  //                        never implemented)
  // 4 bytes: <serial number length in big-endian order>
  // 4 bytes: <DER-encoded issuer distinguished name length in big-endian order>
  // n bytes: <bytes of serial number>
  // m bytes: <DER-encoded issuer distinguished name>
  nsAutoCString decoded;
  nsAutoCString tmpDBKey(aDBKey);
  // Filter out any whitespace for backwards compatibility.
  tmpDBKey.StripWhitespace();
  nsresult rv = Base64Decode(tmpDBKey, decoded);
  if (NS_FAILED(rv)) {
    return rv;
  }
  if (decoded.Length() < 16) {
    return NS_ERROR_ILLEGAL_INPUT;
  }
  const char* reader = decoded.BeginReading();
  uint64_t zeroes = *BitwiseCast<const uint64_t*, const char*>(reader);
  if (zeroes != 0) {
    return NS_ERROR_ILLEGAL_INPUT;
  }
  reader += sizeof(uint64_t);
  // Note: We surround the ntohl() argument with parentheses to stop the macro
  //       from thinking two arguments were passed.
  uint32_t serialNumberLen =
      ntohl((*BitwiseCast<const uint32_t*, const char*>(reader)));
  reader += sizeof(uint32_t);
  uint32_t issuerLen =
      ntohl((*BitwiseCast<const uint32_t*, const char*>(reader)));
  reader += sizeof(uint32_t);
  if (decoded.Length() != 16ULL + serialNumberLen + issuerLen) {
    return NS_ERROR_ILLEGAL_INPUT;
  }
  CERTIssuerAndSN issuerSN;
  issuerSN.serialNumber.len = serialNumberLen;
  issuerSN.serialNumber.data = BitwiseCast<unsigned char*, const char*>(reader);
  reader += serialNumberLen;
  issuerSN.derIssuer.len = issuerLen;
  issuerSN.derIssuer.data = BitwiseCast<unsigned char*, const char*>(reader);
  reader += issuerLen;
  MOZ_ASSERT(reader == decoded.EndReading());

  cert.reset(CERT_FindCertByIssuerAndSN(CERT_GetDefaultCertDB(), &issuerSN));
  return NS_OK;
}

SECStatus collect_certs(void* arg, SECItem** certs, int numcerts) {
  nsTArray<nsTArray<uint8_t>>* certsArray =
      reinterpret_cast<nsTArray<nsTArray<uint8_t>>*>(arg);

  while (numcerts--) {
    nsTArray<uint8_t> certArray;
    SECItem* cert = *certs;
    certArray.AppendElements(cert->data, cert->len);
    certsArray->AppendElement(std::move(certArray));
    certs++;
  }
  return (SECSuccess);
}

nsresult nsNSSCertificateDB::getCertsFromPackage(
    nsTArray<nsTArray<uint8_t>>& collectArgs, uint8_t* data, uint32_t length) {
  if (CERT_DecodeCertPackage(BitwiseCast<char*, uint8_t*>(data), length,
                             collect_certs, &collectArgs) != SECSuccess) {
    return NS_ERROR_FAILURE;
  }
  return NS_OK;
}

// When using the sql-backed softoken, trust settings are authenticated using a
// key in the secret database. Thus, if the user has a password, we need to
// authenticate to the token in order to be able to change trust settings.
SECStatus ChangeCertTrustWithPossibleAuthentication(
    const UniqueCERTCertificate& cert, CERTCertTrust& trust, void* ctx) {
  MOZ_ASSERT(cert, "cert must be non-null");
  if (!cert) {
    PR_SetError(SEC_ERROR_LIBRARY_FAILURE, 0);
    return SECFailure;
  }

  RefPtr<SharedCertVerifier> certVerifier(GetDefaultCertVerifier());
  if (!certVerifier) {
    PR_SetError(SEC_ERROR_LIBRARY_FAILURE, 0);
    return SECFailure;
  }

  // NSS ignores the first argument to CERT_ChangeCertTrust
  SECStatus srv = CERT_ChangeCertTrust(nullptr, cert.get(), &trust);
  if (srv != SECSuccess && PR_GetError() != SEC_ERROR_TOKEN_NOT_LOGGED_IN) {
    return SECFailure;
  }
  if (srv == SECSuccess) {
    certVerifier->ClearTrustCache();
    return SECSuccess;
  }

  // CERT_ChangeCertTrust failed with SEC_ERROR_TOKEN_NOT_LOGGED_IN, so
  // authenticate and try again.
  if (cert->slot) {
    // If this certificate is on an external PKCS#11 token, we have to
    // authenticate to that token.
    srv = PK11_Authenticate(cert->slot, PR_TRUE, ctx);
  } else {
    // Otherwise, the certificate is on the internal module.
    UniquePK11SlotInfo internalSlot(PK11_GetInternalKeySlot());
    srv = PK11_Authenticate(internalSlot.get(), PR_TRUE, ctx);
  }
  if (srv != SECSuccess) {
    return srv;
  }
  srv = CERT_ChangeCertTrust(nullptr, cert.get(), &trust);
  if (srv != SECSuccess) {
    return srv;
  }

  certVerifier->ClearTrustCache();
  return SECSuccess;
}

static nsresult ImportCertsIntoPermanentStorage(
    const UniqueCERTCertList& certChain) {
  bool encounteredFailure = false;
  PRErrorCode savedErrorCode = 0;
  UniquePK11SlotInfo slot(PK11_GetInternalKeySlot());
  for (CERTCertListNode* chainNode = CERT_LIST_HEAD(certChain);
       !CERT_LIST_END(chainNode, certChain);
       chainNode = CERT_LIST_NEXT(chainNode)) {
    UniquePORTString nickname(CERT_MakeCANickname(chainNode->cert));
    SECStatus srv = PK11_ImportCert(slot.get(), chainNode->cert,
                                    CK_INVALID_HANDLE, nickname.get(),
                                    false);  // this parameter is ignored by NSS
    if (srv != SECSuccess) {
      encounteredFailure = true;
      savedErrorCode = PR_GetError();
    }
  }

  if (encounteredFailure) {
    return GetXPCOMFromNSSError(savedErrorCode);
  }

  return NS_OK;
}

nsresult nsNSSCertificateDB::handleCACertDownload(NotNull<nsIArray*> x509Certs,
                                                  nsIInterfaceRequestor* ctx) {
  // First thing we have to do is figure out which certificate we're
  // gonna present to the user.  The CA may have sent down a list of
  // certs which may or may not be a chained list of certs.  Until
  // the day we can design some solid UI for the general case, we'll
  // code to the > 90% case.  That case is where a CA sends down a
  // list that is a hierarchy whose root is either the first or
  // the last cert.  What we're gonna do is compare the first
  // 2 entries, if the second was signed by the first, we assume
  // the root cert is the first cert and display it.  Otherwise,
  // we compare the last 2 entries, if the second to last cert was
  // signed by the last cert, then we assume the last cert is the
  // root and display it.

  uint32_t numCerts;

  x509Certs->GetLength(&numCerts);

  if (numCerts == 0) return NS_OK;  // Nothing to import, so nothing to do.

  nsCOMPtr<nsIX509Cert> certToShow;
  uint32_t selCertIndex;
  if (numCerts == 1) {
    // There's only one cert, so let's show it.
    selCertIndex = 0;
    certToShow = do_QueryElementAt(x509Certs, selCertIndex);
  } else {
    nsCOMPtr<nsIX509Cert> cert0;    // first cert
    nsCOMPtr<nsIX509Cert> cert1;    // second cert
    nsCOMPtr<nsIX509Cert> certn_2;  // second to last cert
    nsCOMPtr<nsIX509Cert> certn_1;  // last cert

    cert0 = do_QueryElementAt(x509Certs, 0);
    cert1 = do_QueryElementAt(x509Certs, 1);
    certn_2 = do_QueryElementAt(x509Certs, numCerts - 2);
    certn_1 = do_QueryElementAt(x509Certs, numCerts - 1);

    nsAutoString cert0SubjectName;
    nsAutoString cert1IssuerName;
    nsAutoString certn_2IssuerName;
    nsAutoString certn_1SubjectName;

    cert0->GetSubjectName(cert0SubjectName);
    cert1->GetIssuerName(cert1IssuerName);
    certn_2->GetIssuerName(certn_2IssuerName);
    certn_1->GetSubjectName(certn_1SubjectName);

    if (cert1IssuerName.Equals(cert0SubjectName)) {
      // In this case, the first cert in the list signed the second,
      // so the first cert is the root.  Let's display it.
      selCertIndex = 0;
      certToShow = cert0;
    } else if (certn_2IssuerName.Equals(certn_1SubjectName)) {
      // In this case the last cert has signed the second to last cert.
      // The last cert is the root, so let's display it.
      selCertIndex = numCerts - 1;
      certToShow = certn_1;
    } else {
      // It's not a chain, so let's just show the first one in the
      // downloaded list.
      selCertIndex = 0;
      certToShow = cert0;
    }
  }

  if (!certToShow) return NS_ERROR_FAILURE;

  nsCOMPtr<nsICertificateDialogs> dialogs;
  nsresult rv = ::getNSSDialogs(getter_AddRefs(dialogs),
                                NS_GET_IID(nsICertificateDialogs),
                                NS_CERTIFICATEDIALOGS_CONTRACTID);
  if (NS_FAILED(rv)) {
    return rv;
  }

  UniqueCERTCertificate tmpCert(certToShow->GetCert());
  if (!tmpCert) {
    return NS_ERROR_FAILURE;
  }

  if (!CERT_IsCACert(tmpCert.get(), nullptr)) {
    DisplayCertificateAlert(ctx, "NotACACert", certToShow);
    return NS_ERROR_FAILURE;
  }

  if (tmpCert->isperm) {
    DisplayCertificateAlert(ctx, "CaCertExists", certToShow);
    return NS_ERROR_FAILURE;
  }

  uint32_t trustBits;
  bool allows;
  rv = dialogs->ConfirmDownloadCACert(ctx, certToShow, &trustBits, &allows);
  if (NS_FAILED(rv)) return rv;

  if (!allows) return NS_ERROR_NOT_AVAILABLE;

  MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("trust is %d\n", trustBits));
  UniquePORTString nickname(CERT_MakeCANickname(tmpCert.get()));

  MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
          ("Created nick \"%s\"\n", nickname.get()));

  nsNSSCertTrust trust;
  trust.SetValidCA();
  trust.AddCATrust(!!(trustBits & nsIX509CertDB::TRUSTED_SSL),
                   !!(trustBits & nsIX509CertDB::TRUSTED_EMAIL));

  UniquePK11SlotInfo slot(PK11_GetInternalKeySlot());
  SECStatus srv = PK11_ImportCert(slot.get(), tmpCert.get(), CK_INVALID_HANDLE,
                                  nickname.get(),
                                  false);  // this parameter is ignored by NSS
  if (srv != SECSuccess) {
    return MapSECStatus(srv);
  }
  srv =
      ChangeCertTrustWithPossibleAuthentication(tmpCert, trust.GetTrust(), ctx);
  if (srv != SECSuccess) {
    return MapSECStatus(srv);
  }

  // Import additional delivered certificates that can be verified.

  // build a CertList for filtering
  UniqueCERTCertList certList(CERT_NewCertList());
  if (!certList) {
    return NS_ERROR_FAILURE;
  }

  // get all remaining certs into temp store

  for (uint32_t i = 0; i < numCerts; i++) {
    if (i == selCertIndex) {
      // we already processed that one
      continue;
    }

    nsCOMPtr<nsIX509Cert> remainingCert = do_QueryElementAt(x509Certs, i);
    if (!remainingCert) {
      continue;
    }

    UniqueCERTCertificate tmpCert2(remainingCert->GetCert());
    if (!tmpCert2) {
      continue;  // Let's try to import the rest of 'em
    }

    if (CERT_AddCertToListTail(certList.get(), tmpCert2.get()) != SECSuccess) {
      continue;
    }

    Unused << tmpCert2.release();
  }

  return ImportCertsIntoPermanentStorage(certList);
}

nsresult nsNSSCertificateDB::ConstructCertArrayFromUniqueCertList(
    const UniqueCERTCertList& aCertListIn,
    nsTArray<RefPtr<nsIX509Cert>>& aCertListOut) {
  if (!aCertListIn.get()) {
    return NS_ERROR_INVALID_ARG;
  }

  for (CERTCertListNode* node = CERT_LIST_HEAD(aCertListIn.get());
       !CERT_LIST_END(node, aCertListIn.get()); node = CERT_LIST_NEXT(node)) {
    RefPtr<nsIX509Cert> cert = new nsNSSCertificate(node->cert);
    aCertListOut.AppendElement(cert);
  }
  return NS_OK;
}

NS_IMETHODIMP
nsNSSCertificateDB::ImportCertificates(uint8_t* data, uint32_t length,
                                       uint32_t type,
                                       nsIInterfaceRequestor* ctx) {
  // We currently only handle CA certificates.
  if (type != nsIX509Cert::CA_CERT) {
    return NS_ERROR_FAILURE;
  }

  nsTArray<nsTArray<uint8_t>> certsArray;
  nsresult rv = getCertsFromPackage(certsArray, data, length);
  if (NS_FAILED(rv)) {
    return rv;
  }

  nsCOMPtr<nsIMutableArray> array = nsArrayBase::Create();
  if (!array) {
    return NS_ERROR_FAILURE;
  }

  // Now let's create some certs to work with
  for (nsTArray<uint8_t>& certDER : certsArray) {
    nsCOMPtr<nsIX509Cert> cert = new nsNSSCertificate(std::move(certDER));
    nsresult rv = array->AppendElement(cert);
    if (NS_FAILED(rv)) {
      return rv;
    }
  }

  return handleCACertDownload(WrapNotNull(array), ctx);
}

/**
 * Decodes a given array of DER-encoded certificates into temporary storage.
 *
 * @param certs
 *        Array in which the decoded certificates are stored as arrays of
 *        unsigned chars.
 * @param temporaryCerts
 *        List of decoded certificates.
 */

static nsresult ImportCertsIntoTempStorage(
    nsTArray<nsTArray<uint8_t>>& certs,
    /*out*/ const UniqueCERTCertList& temporaryCerts) {
  NS_ENSURE_ARG_POINTER(temporaryCerts);

  for (nsTArray<uint8_t>& certDER : certs) {
    CERTCertificate* certificate;
    SECItem certItem;
    certItem.len = certDER.Length();
    certItem.data = certDER.Elements();
    certificate = CERT_NewTempCertificate(CERT_GetDefaultCertDB(), &certItem,
                                          nullptr, falsetrue);

    UniqueCERTCertificate cert(certificate);
    if (!cert) {
      continue;
    }

    if (CERT_AddCertToListTail(temporaryCerts.get(), cert.get()) ==
        SECSuccess) {
      Unused << cert.release();
    }
  }

  return NS_OK;
}

NS_IMETHODIMP
nsNSSCertificateDB::ImportEmailCertificate(uint8_t* data, uint32_t length,
                                           nsIInterfaceRequestor* ctx) {
  nsTArray<nsTArray<uint8_t>> certsArray;

  nsresult rv = getCertsFromPackage(certsArray, data, length);
  if (NS_FAILED(rv)) {
    return rv;
  }

  UniqueCERTCertList temporaryCerts(CERT_NewCertList());
  if (!temporaryCerts) {
    return NS_ERROR_FAILURE;
  }

  rv = ImportCertsIntoTempStorage(certsArray, temporaryCerts);
  if (NS_FAILED(rv)) {
    return rv;
  }

  return ImportCertsIntoPermanentStorage(temporaryCerts);
}

nsresult nsNSSCertificateDB::ImportCACerts(nsTArray<nsTArray<uint8_t>>& caCerts,
                                           nsIInterfaceRequestor* ctx) {
  UniqueCERTCertList temporaryCerts(CERT_NewCertList());
  if (!temporaryCerts) {
    return NS_ERROR_FAILURE;
  }

  nsresult rv = ImportCertsIntoTempStorage(caCerts, temporaryCerts);
  if (NS_FAILED(rv)) {
    return rv;
  }

  return ImportCertsIntoPermanentStorage(temporaryCerts);
}

void nsNSSCertificateDB::DisplayCertificateAlert(nsIInterfaceRequestor* ctx,
                                                 const char* stringID,
                                                 nsIX509Cert* certToShow) {
  if (!NS_IsMainThread()) {
    NS_ERROR(
        "nsNSSCertificateDB::DisplayCertificateAlert called off the main "
        "thread");
    return;
  }

  nsCOMPtr<nsIInterfaceRequestor> my_ctx = ctx;
  if (!my_ctx) {
    my_ctx = new PipUIContext();
  }

  // This shall be replaced by embedding ovverridable prompts
  // as discussed in bug 310446, and should make use of certToShow.

  nsAutoString tmpMessage;
  GetPIPNSSBundleString(stringID, tmpMessage);
  nsCOMPtr<nsIPrompt> prompt(do_GetInterface(my_ctx));
  if (!prompt) {
    return;
  }

  prompt->Alert(nullptr, tmpMessage.get());
}

NS_IMETHODIMP
nsNSSCertificateDB::ImportUserCertificate(uint8_t* data, uint32_t length,
                                          nsIInterfaceRequestor* ctx) {
  if (!NS_IsMainThread()) {
    NS_ERROR(
        "nsNSSCertificateDB::ImportUserCertificate called off the main thread");
    return NS_ERROR_NOT_SAME_THREAD;
  }

  nsTArray<nsTArray<uint8_t>> certsArray;

  nsresult rv = getCertsFromPackage(certsArray, data, length);
  if (NS_FAILED(rv)) {
    return rv;
  }

  SECItem certItem;

  if (certsArray.IsEmpty()) {
    return NS_OK;
  }

  certItem.len = certsArray.ElementAt(0).Length();
  certItem.data = certsArray.ElementAt(0).Elements();

  UniqueCERTCertificate cert(CERT_NewTempCertificate(
      CERT_GetDefaultCertDB(), &certItem, nullptr, falsetrue));
  if (!cert) {
    return NS_ERROR_FAILURE;
  }

  UniquePK11SlotInfo slot(PK11_KeyForCertExists(cert.get(), nullptr, ctx));
  if (!slot) {
    nsCOMPtr<nsIX509Cert> certToShow = new nsNSSCertificate(cert.get());
    DisplayCertificateAlert(ctx, "UserCertIgnoredNoPrivateKey", certToShow);
    return NS_ERROR_FAILURE;
  }
  slot = nullptr;

  /* pick a nickname for the cert */
  nsAutoCString nickname;
  if (cert->nickname) {
    nickname = cert->nickname;
  } else {
    get_default_nickname(cert.get(), ctx, nickname);
  }

  /* user wants to import the cert */
  slot.reset(PK11_ImportCertForKey(cert.get(), nickname.get(), ctx));
  if (!slot) {
    return NS_ERROR_FAILURE;
  }
  slot = nullptr;

  {
    nsCOMPtr<nsIX509Cert> certToShow = new nsNSSCertificate(cert.get());
    DisplayCertificateAlert(ctx, "UserCertImported", certToShow);
  }

  rv = NS_OK;
  if (!certsArray.IsEmpty()) {
    certsArray.RemoveElementAt(0);
    rv = ImportCACerts(certsArray, ctx);
  }

  nsCOMPtr<nsIObserverService> observerService =
      mozilla::services::GetObserverService();
  if (observerService) {
    observerService->NotifyObservers(nullptr, "psm:user-certificate-added",
                                     nullptr);
  }

  return rv;
}

NS_IMETHODIMP
nsNSSCertificateDB::DeleteCertificate(nsIX509Cert* aCert) {
  NS_ENSURE_ARG_POINTER(aCert);
  UniqueCERTCertificate cert(aCert->GetCert());
  if (!cert) {
    return NS_ERROR_FAILURE;
  }

  // Temporary certificates aren't on a slot and will go away when the
  // nsIX509Cert is destructed.
  if (cert->slot) {
    uint32_t certType;
    nsresult rv = aCert->GetCertType(&certType);
    if (NS_WARN_IF(NS_FAILED(rv))) {
      return rv;
    }
    if (certType == nsIX509Cert::USER_CERT) {
      SECStatus srv = PK11_Authenticate(cert->slot, true, nullptr);
      if (srv != SECSuccess) {
        return NS_ERROR_FAILURE;
      }
      srv = PK11_DeleteTokenCertAndKey(cert.get(), nullptr);
      if (srv != SECSuccess) {
        return NS_ERROR_FAILURE;
      }
    } else {
      // For certificates that can't be deleted (e.g. built-in roots), un-set
      // all trust bits.
      nsNSSCertTrust trust(0, 0);
      SECStatus srv = ChangeCertTrustWithPossibleAuthentication(
          cert, trust.GetTrust(), nullptr);
      if (srv != SECSuccess) {
        return NS_ERROR_FAILURE;
      }
      if (!PK11_IsReadOnly(cert->slot)) {
        srv = SEC_DeletePermCertificate(cert.get());
        if (srv != SECSuccess) {
          return NS_ERROR_FAILURE;
        }
      }
    }
  }

  nsCOMPtr<nsIObserverService> observerService =
      mozilla::services::GetObserverService();
  if (observerService) {
    observerService->NotifyObservers(nullptr, "psm:user-certificate-deleted",
                                     nullptr);
  }

  return NS_OK;
}

NS_IMETHODIMP
nsNSSCertificateDB::SetCertTrust(nsIX509Cert* cert, uint32_t type,
                                 uint32_t trusted) {
  NS_ENSURE_ARG_POINTER(cert);
  nsNSSCertTrust trust;
  switch (type) {
    case nsIX509Cert::CA_CERT:
      trust.SetValidCA();
      trust.AddCATrust(!!(trusted & nsIX509CertDB::TRUSTED_SSL),
                       !!(trusted & nsIX509CertDB::TRUSTED_EMAIL));
      break;
    case nsIX509Cert::SERVER_CERT:
      trust.SetValidPeer();
      trust.AddPeerTrust(trusted & nsIX509CertDB::TRUSTED_SSL, false);
      break;
    case nsIX509Cert::EMAIL_CERT:
      trust.SetValidPeer();
      trust.AddPeerTrust(false, !!(trusted & nsIX509CertDB::TRUSTED_EMAIL));
      break;
    default:
      // Ignore any other type of certificate (including invalid types).
      return NS_OK;
  }

  UniqueCERTCertificate nsscert(cert->GetCert());
  SECStatus srv = ChangeCertTrustWithPossibleAuthentication(
      nsscert, trust.GetTrust(), nullptr);
  return MapSECStatus(srv);
}

NS_IMETHODIMP
nsNSSCertificateDB::IsCertTrusted(nsIX509Cert* cert, uint32_t certType,
                                  uint32_t trustType, bool* _isTrusted) {
  NS_ENSURE_ARG_POINTER(_isTrusted);
  *_isTrusted = false;

  nsresult rv = BlockUntilLoadableCertsLoaded();
  if (NS_FAILED(rv)) {
    return rv;
  }

  SECStatus srv;
  UniqueCERTCertificate nsscert(cert->GetCert());
  CERTCertTrust nsstrust;
  srv = CERT_GetCertTrust(nsscert.get(), &nsstrust);
  if (srv != SECSuccess) {
    // CERT_GetCertTrust returns SECFailure if given a temporary cert that
    // doesn't have any trust information yet. This isn't an error.
    return NS_OK;
  }

  nsNSSCertTrust trust(&nsstrust);
  if (certType == nsIX509Cert::CA_CERT) {
    if (trustType & nsIX509CertDB::TRUSTED_SSL) {
      *_isTrusted = trust.HasTrustedCA(truefalse);
    } else if (trustType & nsIX509CertDB::TRUSTED_EMAIL) {
      *_isTrusted = trust.HasTrustedCA(falsetrue);
    } else {
      return NS_ERROR_FAILURE;
    }
  } else if (certType == nsIX509Cert::SERVER_CERT) {
    if (trustType & nsIX509CertDB::TRUSTED_SSL) {
      *_isTrusted = trust.HasTrustedPeer(truefalse);
    } else if (trustType & nsIX509CertDB::TRUSTED_EMAIL) {
      *_isTrusted = trust.HasTrustedPeer(falsetrue);
    } else {
      return NS_ERROR_FAILURE;
    }
  } else if (certType == nsIX509Cert::EMAIL_CERT) {
    if (trustType & nsIX509CertDB::TRUSTED_SSL) {
      *_isTrusted = trust.HasTrustedPeer(truefalse);
    } else if (trustType & nsIX509CertDB::TRUSTED_EMAIL) {
      *_isTrusted = trust.HasTrustedPeer(falsetrue);
    } else {
      return NS_ERROR_FAILURE;
    }
  } /* user: ignore */
  return NS_OK;
}

NS_IMETHODIMP
nsNSSCertificateDB::ImportCertsFromFile(nsIFile* aFile, uint32_t aType) {
  NS_ENSURE_ARG(aFile);
  switch (aType) {
    case nsIX509Cert::CA_CERT:
    case nsIX509Cert::EMAIL_CERT:
      // good
      break;

    default:
      // not supported (yet)
      return NS_ERROR_FAILURE;
  }

  PRFileDesc* fd = nullptr;
  nsresult rv = aFile->OpenNSPRFileDesc(PR_RDONLY, 0, &fd);
  if (NS_FAILED(rv)) {
    return rv;
  }
  if (!fd) {
    return NS_ERROR_FAILURE;
  }

  PRFileInfo fileInfo;
  if (PR_GetOpenFileInfo(fd, &fileInfo) != PR_SUCCESS) {
    return NS_ERROR_FAILURE;
  }

  auto buf = MakeUnique<unsigned char[]>(fileInfo.size);
  int32_t bytesObtained = PR_Read(fd, buf.get(), fileInfo.size);
  PR_Close(fd);

  if (bytesObtained != fileInfo.size) {
    return NS_ERROR_FAILURE;
  }

  nsCOMPtr<nsIInterfaceRequestor> cxt = new PipUIContext();

  switch (aType) {
    case nsIX509Cert::CA_CERT:
      return ImportCertificates(buf.get(), bytesObtained, aType, cxt);
    case nsIX509Cert::EMAIL_CERT:
      return ImportEmailCertificate(buf.get(), bytesObtained, cxt);
    default:
      MOZ_ASSERT(false"Unsupported type should have been filtered out");
      break;
  }

  return NS_ERROR_FAILURE;
}

NS_IMETHODIMP
nsNSSCertificateDB::ImportPKCS12File(nsIFile* aFile, const nsAString& aPassword,
                                     uint32_t* aError) {
  if (!NS_IsMainThread()) {
    return NS_ERROR_NOT_SAME_THREAD;
  }
  nsresult rv = BlockUntilLoadableCertsLoaded();
  if (NS_FAILED(rv)) {
    return rv;
  }

  NS_ENSURE_ARG(aFile);
  nsPKCS12Blob blob;
  rv = blob.ImportFromFile(aFile, aPassword, *aError);
  nsCOMPtr<nsIObserverService> observerService =
      mozilla::services::GetObserverService();
  if (NS_SUCCEEDED(rv) && observerService) {
    observerService->NotifyObservers(nullptr, "psm:user-certificate-added",
                                     nullptr);
  }

  return rv;
}

NS_IMETHODIMP
nsNSSCertificateDB::ExportPKCS12File(
    nsIFile* aFile, const nsTArray<RefPtr<nsIX509Cert>>& aCerts,
    const nsAString& aPassword, uint32_t* aError) {
  if (!NS_IsMainThread()) {
    return NS_ERROR_NOT_SAME_THREAD;
  }
  nsresult rv = BlockUntilLoadableCertsLoaded();
  if (NS_FAILED(rv)) {
    return rv;
  }

  NS_ENSURE_ARG(aFile);
  if (aCerts.IsEmpty()) {
    return NS_OK;
  }
  nsPKCS12Blob blob;
  return blob.ExportToFile(aFile, aCerts, aPassword, *aError);
}

NS_IMETHODIMP
nsNSSCertificateDB::ConstructX509FromBase64(const nsACString& base64,
                                            /*out*/ nsIX509Cert** _retval) {
  if (!_retval) {
    return NS_ERROR_INVALID_POINTER;
  }

  // Base64Decode() doesn't consider a zero length input as an error, and just
  // returns the empty string. We don't want this behavior, so the below check
  // catches this case.
  if (base64.Length() < 1) {
    return NS_ERROR_ILLEGAL_VALUE;
  }

  nsAutoCString certDER;
  nsresult rv = Base64Decode(base64, certDER);
  if (NS_FAILED(rv)) {
    return rv;
  }

  return ConstructX509FromSpan(AsBytes(Span(certDER)), _retval);
}

NS_IMETHODIMP
nsNSSCertificateDB::ConstructX509(const nsTArray<uint8_t>& certDER,
                                  nsIX509Cert** _retval) {
  return ConstructX509FromSpan(Span(certDER.Elements(), certDER.Length()),
                               _retval);
}

nsresult nsNSSCertificateDB::ConstructX509FromSpan(
    Span<const uint8_t> aInputSpan, nsIX509Cert** _retval) {
  if (NS_WARN_IF(!_retval)) {
    return NS_ERROR_INVALID_POINTER;
  }

  if (aInputSpan.Length() > std::numeric_limits<unsigned int>::max()) {
    return NS_ERROR_ILLEGAL_VALUE;
  }

  SECItem certData;
  certData.type = siDERCertBuffer;
  certData.data = const_cast<unsigned char*>(
      reinterpret_cast<const unsigned char*>(aInputSpan.Elements()));
  certData.len = aInputSpan.Length();

  UniqueCERTCertificate cert(CERT_NewTempCertificate(
      CERT_GetDefaultCertDB(), &certData, nullptr, falsetrue));
  if (!cert)
    return (PORT_GetError() == SEC_ERROR_NO_MEMORY) ? NS_ERROR_OUT_OF_MEMORY
                                                    : NS_ERROR_FAILURE;

  nsCOMPtr<nsIX509Cert> nssCert = new nsNSSCertificate(cert.get());
  nssCert.forget(_retval);
  return NS_OK;
}

void nsNSSCertificateDB::get_default_nickname(CERTCertificate* cert,
                                              nsIInterfaceRequestor* ctx,
                                              nsCString& nickname) {
  nickname.Truncate();

  CK_OBJECT_HANDLE keyHandle;

  if (NS_FAILED(BlockUntilLoadableCertsLoaded())) {
    return;
  }

  CERTCertDBHandle* defaultcertdb = CERT_GetDefaultCertDB();
  nsAutoCString username;
  UniquePORTString tempCN(CERT_GetCommonName(&cert->subject));
  if (tempCN) {
    username = tempCN.get();
  }

  nsAutoCString caname;
  UniquePORTString tempIssuerOrg(CERT_GetOrgName(&cert->issuer));
  if (tempIssuerOrg) {
    caname = tempIssuerOrg.get();
  }

  nsAutoString tmpNickFmt;
  GetPIPNSSBundleString("nick_template", tmpNickFmt);
  NS_ConvertUTF16toUTF8 nickFmt(tmpNickFmt);

  nsAutoCString baseName;
  baseName.AppendPrintf(nickFmt.get(), username.get(), caname.get());
  if (baseName.IsEmpty()) {
    return;
  }

  nickname = baseName;

  /*
   * We need to see if the private key exists on a token, if it does
   * then we need to check for nicknames that already exist on the smart
   * card.
   */

  UniquePK11SlotInfo slot(PK11_KeyForCertExists(cert, &keyHandle, ctx));
  if (!slot) return;

  if (!PK11_IsInternal(slot.get())) {
    nsAutoCString tmp;
    tmp.AppendPrintf("%s:%s", PK11_GetTokenName(slot.get()), baseName.get());
    if (tmp.IsEmpty()) {
      nickname.Truncate();
      return;
    }
    baseName = tmp;
    nickname = baseName;
  }

  int count = 1;
  while (true) {
    if (count > 1) {
      nsAutoCString tmp;
      tmp.AppendPrintf("%s #%d", baseName.get(), count);
      if (tmp.IsEmpty()) {
        nickname.Truncate();
        return;
      }
      nickname = tmp;
    }

    UniqueCERTCertificate dummycert;

    if (PK11_IsInternal(slot.get())) {
      /* look up the nickname to make sure it isn't in use already */
      dummycert.reset(CERT_FindCertByNickname(defaultcertdb, nickname.get()));
    } else {
      // Check the cert against others that already live on the smart card.
      dummycert.reset(PK11_FindCertFromNickname(nickname.get(), ctx));
      if (dummycert) {
        // Make sure the subject names are different.
        if (CERT_CompareName(&cert->subject, &dummycert->subject) == SECEqual) {
          /*
           * There is another certificate with the same nickname and
           * the same subject name on the smart card, so let's use this
           * nickname.
           */

          dummycert = nullptr;
        }
      }
    }
    if (!dummycert) {
      break;
    }
    count++;
  }
}

NS_IMETHODIMP
nsNSSCertificateDB::AddCertFromBase64(const nsACString& aBase64,
                                      const nsACString& aTrust,
                                      nsIX509Cert** addedCertificate) {
  // Base64Decode() doesn't consider a zero length input as an error, and just
  // returns the empty string. We don't want this behavior, so the below check
  // catches this case.
  if (aBase64.Length() < 1) {
    return NS_ERROR_ILLEGAL_VALUE;
  }

  nsAutoCString aCertDER;
  nsresult rv = Base64Decode(aBase64, aCertDER);
  if (NS_FAILED(rv)) {
    return rv;
  }
  return AddCert(aCertDER, aTrust, addedCertificate);
}

NS_IMETHODIMP
nsNSSCertificateDB::AddCert(const nsACString& aCertDER,
                            const nsACString& aTrust,
                            nsIX509Cert** addedCertificate) {
  MOZ_ASSERT(addedCertificate);
  if (!addedCertificate) {
    return NS_ERROR_INVALID_ARG;
  }
  *addedCertificate = nullptr;

  nsNSSCertTrust trust;
  if (CERT_DecodeTrustString(&trust.GetTrust(),
                             PromiseFlatCString(aTrust).get()) != SECSuccess) {
    return NS_ERROR_FAILURE;
  }

  nsCOMPtr<nsIX509Cert> newCert;
  nsresult rv =
      ConstructX509FromSpan(AsBytes(Span(aCertDER)), getter_AddRefs(newCert));
  if (NS_FAILED(rv)) {
    return rv;
  }

  UniqueCERTCertificate tmpCert(newCert->GetCert());
  if (!tmpCert) {
    return NS_ERROR_FAILURE;
  }

  // If there's already a certificate that matches this one in the database, we
  // still want to set its trust to the given value.
  if (tmpCert->isperm) {
    rv = SetCertTrustFromString(newCert, aTrust);
    if (NS_FAILED(rv)) {
      return rv;
    }
    newCert.forget(addedCertificate);
    return NS_OK;
  }

  UniquePORTString nickname(CERT_MakeCANickname(tmpCert.get()));

  MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
          ("Created nick \"%s\"\n", nickname.get()));

  UniquePK11SlotInfo slot(PK11_GetInternalKeySlot());
  SECStatus srv = PK11_ImportCert(slot.get(), tmpCert.get(), CK_INVALID_HANDLE,
                                  nickname.get(),
                                  false);  // this parameter is ignored by NSS
  if (srv != SECSuccess) {
    return MapSECStatus(srv);
  }
  srv = ChangeCertTrustWithPossibleAuthentication(tmpCert, trust.GetTrust(),
                                                  nullptr);
  if (srv != SECSuccess) {
    return MapSECStatus(srv);
  }
  newCert.forget(addedCertificate);
  return NS_OK;
}

NS_IMETHODIMP
nsNSSCertificateDB::SetCertTrustFromString(nsIX509Cert* cert,
                                           const nsACString& trustString) {
  NS_ENSURE_ARG(cert);

  CERTCertTrust trust;
  SECStatus srv =
      CERT_DecodeTrustString(&trust, PromiseFlatCString(trustString).get());
  if (srv != SECSuccess) {
    return MapSECStatus(srv);
  }
  UniqueCERTCertificate nssCert(cert->GetCert());

  srv = ChangeCertTrustWithPossibleAuthentication(nssCert, trust, nullptr);
  return MapSECStatus(srv);
}

NS_IMETHODIMP nsNSSCertificateDB::AsPKCS7Blob(
    const nsTArray<RefPtr<nsIX509Cert>>& certList, nsACString& _retval) {
  if (certList.IsEmpty()) {
    return NS_ERROR_INVALID_ARG;
  }

  UniqueNSSCMSMessage cmsg(NSS_CMSMessage_Create(nullptr));
  if (!cmsg) {
    MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
            ("nsNSSCertificateDB::AsPKCS7Blob - can't create CMS message"));
    return NS_ERROR_OUT_OF_MEMORY;
  }

  UniqueNSSCMSSignedData sigd(nullptr);
  for (const auto& cert : certList) {
    // We need an owning handle when calling nsIX509Cert::GetCert().
    UniqueCERTCertificate nssCert(cert->GetCert());
    if (!sigd) {
      sigd.reset(
          NSS_CMSSignedData_CreateCertsOnly(cmsg.get(), nssCert.get(), false));
      if (!sigd) {
        MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
                ("nsNSSCertificateDB::AsPKCS7Blob - can't create SignedData"));
        return NS_ERROR_FAILURE;
      }
    } else if (NSS_CMSSignedData_AddCertificate(sigd.get(), nssCert.get()) !=
               SECSuccess) {
      MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
              ("nsNSSCertificateDB::AsPKCS7Blob - can't add cert"));
      return NS_ERROR_FAILURE;
    }
  }

  NSSCMSContentInfo* cinfo = NSS_CMSMessage_GetContentInfo(cmsg.get());
  if (NSS_CMSContentInfo_SetContent_SignedData(cmsg.get(), cinfo, sigd.get()) !=
      SECSuccess) {
    MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
            ("nsNSSCertificateDB::AsPKCS7Blob - can't attach SignedData"));
    return NS_ERROR_FAILURE;
  }
  // cmsg owns sigd now.
  Unused << sigd.release();

  UniquePLArenaPool arena(PORT_NewArena(1024));
  if (!arena) {
    MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
            ("nsNSSCertificateDB::AsPKCS7Blob - out of memory"));
    return NS_ERROR_OUT_OF_MEMORY;
  }

  SECItem certP7 = {siBuffer, nullptr, 0};
  NSSCMSEncoderContext* ecx = NSS_CMSEncoder_Start(
      cmsg.get(), nullptr, nullptr, &certP7, arena.get(), nullptr, nullptr,
      nullptr, nullptr, nullptr, nullptr);
  if (!ecx) {
    MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
            ("nsNSSCertificateDB::AsPKCS7Blob - can't create encoder"));
    return NS_ERROR_FAILURE;
  }

  if (NSS_CMSEncoder_Finish(ecx) != SECSuccess) {
    MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
            ("nsNSSCertificateDB::AsPKCS7Blob - failed to add encoded data"));
    return NS_ERROR_FAILURE;
  }

  _retval.Assign(nsDependentCSubstring(
      reinterpret_cast<const char*>(certP7.data), certP7.len));
  return NS_OK;
}

NS_IMETHODIMP
nsNSSCertificateDB::GetCerts(nsTArray<RefPtr<nsIX509Cert>>& _retval) {
  nsresult rv = BlockUntilLoadableCertsLoaded();
  if (NS_FAILED(rv)) {
    return rv;
  }

  rv = CheckForSmartCardChanges();
  if (NS_FAILED(rv)) {
    return rv;
  }

  nsCOMPtr<nsIInterfaceRequestor> ctx = new PipUIContext();
  UniqueCERTCertList certList(PK11_ListCerts(PK11CertListUnique, ctx));
  if (!certList) {
    return NS_ERROR_FAILURE;
  }
  return nsNSSCertificateDB::ConstructCertArrayFromUniqueCertList(certList,
                                                                  _retval);
}

nsresult IsCertBuiltInRoot(const RefPtr<nsIX509Cert>& cert,
                           bool& isBuiltInRoot) {
  nsTArray<uint8_t> der;
  nsresult rv = cert->GetRawDER(der);
  if (NS_FAILED(rv)) {
    return rv;
  }
  pkix::Input certInput;
  pkix::Result result = certInput.Init(der.Elements(), der.Length());
  if (result != pkix::Result::Success) {
    return NS_ERROR_FAILURE;
  }
  result = IsCertBuiltInRoot(certInput, isBuiltInRoot);
  if (result != pkix::Result::Success) {
    return NS_ERROR_FAILURE;
  }
  return NS_OK;
}

NS_IMETHODIMP
nsNSSCertificateDB::AsyncHasThirdPartyRoots(nsIAsyncBoolCallback* aCallback) {
  NS_ENSURE_ARG_POINTER(aCallback);
  nsMainThreadPtrHandle<nsIAsyncBoolCallback> callback(
      new nsMainThreadPtrHolder<nsIAsyncBoolCallback>("AsyncHasThirdPartyRoots",
                                                      aCallback));

  return NS_DispatchBackgroundTask(
      NS_NewRunnableFunction(
          "nsNSSCertificateDB::AsyncHasThirdPartyRoots",
          [cb = std::move(callback), self = RefPtr{this}] {
            bool hasThirdPartyRoots = [self]() -> bool {
              nsTArray<RefPtr<nsIX509Cert>> certs;
              nsresult rv = self->GetCerts(certs);
              if (NS_FAILED(rv)) {
                return false;
              }

              for (const auto& cert : certs) {
                bool isTrusted = false;
                nsresult rv =
                    self->IsCertTrusted(cert, nsIX509Cert::CA_CERT,
                                        nsIX509CertDB::TRUSTED_SSL, &isTrusted);
                if (NS_FAILED(rv)) {
                  return false;
                }

                if (!isTrusted) {
                  continue;
                }

                bool isBuiltInRoot = false;
                rv = IsCertBuiltInRoot(cert, isBuiltInRoot);
                if (NS_FAILED(rv)) {
                  return false;
                }

                if (!isBuiltInRoot) {
                  return true;
                }
              }

              return false;
            }();

            NS_DispatchToMainThread(NS_NewRunnableFunction(
                "nsNSSCertificateDB::AsyncHasThirdPartyRoots callback",
                [cb, hasThirdPartyRoots]() {
                  cb->OnResult(hasThirdPartyRoots);
                }));
          }),
      NS_DISPATCH_EVENT_MAY_BLOCK);
}

nsresult VerifyCertAtTime(nsIX509Cert* aCert,
                          int64_t /*SECCertificateUsage*/ aUsage,
                          uint32_t aFlags, const nsACString& aHostname,
                          mozilla::pkix::Time aTime,
                          nsTArray<RefPtr<nsIX509Cert>>& aVerifiedChain,
                          bool* aHasEVPolicy,
                          int32_t* /*PRErrorCode*/ _retval) {
  NS_ENSURE_ARG_POINTER(aCert);
  NS_ENSURE_ARG_POINTER(aHasEVPolicy);
  NS_ENSURE_ARG_POINTER(_retval);

  if (!aVerifiedChain.IsEmpty()) {
    return NS_ERROR_INVALID_ARG;
  }

  *aHasEVPolicy = false;
  *_retval = PR_UNKNOWN_ERROR;

  RefPtr<SharedCertVerifier> certVerifier(GetDefaultCertVerifier());
  NS_ENSURE_TRUE(certVerifier, NS_ERROR_FAILURE);

  nsTArray<nsTArray<uint8_t>> resultChain;
  EVStatus evStatus;
  mozilla::pkix::Result result;

  nsTArray<uint8_t> certBytes;
  nsresult nsrv = aCert->GetRawDER(certBytes);
  if (NS_FAILED(nsrv)) {
    return nsrv;
  }

  if (!aHostname.IsVoid() && aUsage == certificateUsageSSLServer) {
    result =
        certVerifier->VerifySSLServerCert(certBytes, aTime,
                                          nullptr,  // Assume no context
                                          aHostname, resultChain, aFlags,
                                          Nothing(),  // extraCertificates
                                          Nothing(),  // stapledOCSPResponse
                                          Nothing(),  // sctsFromTLSExtension
                                          Nothing(),  // dcInfo
                                          OriginAttributes(), &evStatus);
  } else {
    const nsCString& flatHostname = PromiseFlatCString(aHostname);
    result = certVerifier->VerifyCert(
        certBytes, aUsage, aTime,
        nullptr,  // Assume no context
        aHostname.IsVoid() ? nullptr : flatHostname.get(), resultChain, aFlags,
        Nothing(),  // extraCertificates
        Nothing(),  // stapledOCSPResponse
        Nothing(),  // sctsFromTLSExtension
        OriginAttributes(), &evStatus);
  }

  if (result == mozilla::pkix::Success) {
    for (auto& certDER : resultChain) {
      RefPtr<nsIX509Cert> cert = new nsNSSCertificate(std::move(certDER));
      aVerifiedChain.AppendElement(cert);
    }

    if (evStatus == EVStatus::EV) {
      *aHasEVPolicy = true;
    }
  }

  *_retval = mozilla::pkix::MapResultToPRErrorCode(result);

  return NS_OK;
}

class VerifyCertAtTimeTask final : public CryptoTask {
 public:
  VerifyCertAtTimeTask(nsIX509Cert* aCert, int64_t aUsage, uint32_t aFlags,
                       const nsACString& aHostname, uint64_t aTime,
                       nsICertVerificationCallback* aCallback)
      : mCert(aCert),
        mUsage(aUsage),
        mFlags(aFlags),
        mHostname(aHostname),
        mTime(aTime),
        mCallback(new nsMainThreadPtrHolder<nsICertVerificationCallback>(
            "nsICertVerificationCallback", aCallback)),
        mPRErrorCode(SEC_ERROR_LIBRARY_FAILURE),
        mHasEVPolicy(false) {}

 private:
  virtual nsresult CalculateResult() override {
    nsCOMPtr<nsIX509CertDB> certDB = do_GetService(NS_X509CERTDB_CONTRACTID);
    if (!certDB) {
      return NS_ERROR_FAILURE;
    }
    return VerifyCertAtTime(mCert, mUsage, mFlags, mHostname,
                            mozilla::pkix::TimeFromEpochInSeconds(mTime),
                            mVerifiedCertList, &mHasEVPolicy, &mPRErrorCode);
  }

  virtual void CallCallback(nsresult rv) override {
    if (NS_FAILED(rv)) {
      nsTArray<RefPtr<nsIX509Cert>> tmp;
      Unused << mCallback->VerifyCertFinished(SEC_ERROR_LIBRARY_FAILURE, tmp,
                                              false);
    } else {
      Unused << mCallback->VerifyCertFinished(mPRErrorCode, mVerifiedCertList,
                                              mHasEVPolicy);
    }
  }

  nsCOMPtr<nsIX509Cert> mCert;
  int64_t mUsage;
  uint32_t mFlags;
  nsCString mHostname;
  uint64_t mTime;
  nsMainThreadPtrHandle<nsICertVerificationCallback> mCallback;
  int32_t mPRErrorCode;
  nsTArray<RefPtr<nsIX509Cert>> mVerifiedCertList;
  bool mHasEVPolicy;
};

NS_IMETHODIMP
nsNSSCertificateDB::AsyncVerifyCertAtTime(
    nsIX509Cert* aCert, int64_t /*SECCertificateUsage*/ aUsage, uint32_t aFlags,
    const nsACString& aHostname, uint64_t aTime,
    nsICertVerificationCallback* aCallback) {
  RefPtr<VerifyCertAtTimeTask> task(new VerifyCertAtTimeTask(
      aCert, aUsage, aFlags, aHostname, aTime, aCallback));
  return task->Dispatch();
}

NS_IMETHODIMP
nsNSSCertificateDB::ClearOCSPCache() {
  RefPtr<SharedCertVerifier> certVerifier(GetDefaultCertVerifier());
  NS_ENSURE_TRUE(certVerifier, NS_ERROR_FAILURE);
  certVerifier->ClearOCSPCache();
  return NS_OK;
}

96%


¤ Dauer der Verarbeitung: 0.19 Sekunden  ¤

*© 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 ist noch experimentell.