summaryrefslogtreecommitdiffstats
path: root/security/manager/ssl/nsNSSCertificate.cpp
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
commit26a029d407be480d791972afb5975cf62c9360a6 (patch)
treef435a8308119effd964b339f76abb83a57c29483 /security/manager/ssl/nsNSSCertificate.cpp
parentInitial commit. (diff)
downloadfirefox-26a029d407be480d791972afb5975cf62c9360a6.tar.xz
firefox-26a029d407be480d791972afb5975cf62c9360a6.zip
Adding upstream version 124.0.1.upstream/124.0.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'security/manager/ssl/nsNSSCertificate.cpp')
-rw-r--r--security/manager/ssl/nsNSSCertificate.cpp686
1 files changed, 686 insertions, 0 deletions
diff --git a/security/manager/ssl/nsNSSCertificate.cpp b/security/manager/ssl/nsNSSCertificate.cpp
new file mode 100644
index 0000000000..d0413c0340
--- /dev/null
+++ b/security/manager/ssl/nsNSSCertificate.cpp
@@ -0,0 +1,686 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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 "nsNSSCertificate.h"
+
+#include "CertVerifier.h"
+#include "ExtendedValidation.h"
+#include "NSSCertDBTrustDomain.h"
+#include "X509CertValidity.h"
+#include "certdb.h"
+#include "ipc/IPCMessageUtils.h"
+#include "mozilla/Assertions.h"
+#include "mozilla/Base64.h"
+#include "mozilla/Casting.h"
+#include "mozilla/NotNull.h"
+#include "mozilla/Span.h"
+#include "mozilla/TextUtils.h"
+#include "mozilla/Unused.h"
+#include "mozilla/ipc/TransportSecurityInfoUtils.h"
+#include "mozilla/ipc/IPDLParamTraits.h"
+#include "mozilla/net/DNS.h"
+#include "mozpkix/Result.h"
+#include "mozpkix/pkixnss.h"
+#include "mozpkix/pkixtypes.h"
+#include "mozpkix/pkixutil.h"
+#include "nsArray.h"
+#include "nsCOMPtr.h"
+#include "nsIClassInfoImpl.h"
+#include "nsIObjectInputStream.h"
+#include "nsIObjectOutputStream.h"
+#include "nsIX509Cert.h"
+#include "nsNSSCertHelper.h"
+#include "nsNSSCertTrust.h"
+#include "nsPK11TokenDB.h"
+#include "nsPKCS12Blob.h"
+#include "nsProxyRelease.h"
+#include "nsReadableUtils.h"
+#include "nsString.h"
+#include "nsThreadUtils.h"
+#include "nsUnicharUtils.h"
+#include "nspr.h"
+#include "prerror.h"
+#include "secasn1.h"
+#include "secder.h"
+#include "secerr.h"
+#include "ssl.h"
+
+#ifdef XP_WIN
+# include <winsock.h> // for htonl
+#endif
+
+using namespace mozilla;
+using namespace mozilla::psm;
+
+extern LazyLogModule gPIPNSSLog;
+
+NS_IMPL_ISUPPORTS(nsNSSCertificate, nsIX509Cert, nsISerializable, nsIClassInfo)
+
+nsNSSCertificate::nsNSSCertificate() : mCert("nsNSSCertificate::mCert") {}
+
+nsNSSCertificate::nsNSSCertificate(CERTCertificate* cert)
+ : mCert("nsNSSCertificate::mCert") {
+ if (cert) {
+ mDER.AppendElements(cert->derCert.data, cert->derCert.len);
+ auto lock = mCert.Lock();
+ auto& maybeCert = lock.ref();
+ maybeCert.emplace(UniqueCERTCertificate(CERT_DupCertificate(cert)));
+ }
+}
+
+nsNSSCertificate::nsNSSCertificate(nsTArray<uint8_t>&& der)
+ : mDER(std::move(der)), mCert("nsNSSCertificate::mCert") {}
+
+UniqueCERTCertificate nsNSSCertificate::GetOrInstantiateCert() {
+ auto lock = mCert.Lock();
+ auto& maybeCert = lock.ref();
+ if (maybeCert.isSome()) {
+ return UniqueCERTCertificate(CERT_DupCertificate((*maybeCert).get()));
+ }
+
+ if (!EnsureNSSInitializedChromeOrContent()) {
+ return nullptr;
+ }
+
+ SECItem derItem = {siBuffer, mDER.Elements(),
+ static_cast<unsigned int>(mDER.Length())};
+ UniqueCERTCertificate cert(CERT_NewTempCertificate(
+ CERT_GetDefaultCertDB(), &derItem, nullptr, false, true));
+ if (!cert) {
+ return nullptr;
+ }
+ maybeCert.emplace(std::move(cert));
+
+ return UniqueCERTCertificate(CERT_DupCertificate((*maybeCert).get()));
+}
+
+nsresult nsNSSCertificate::GetCertType(uint32_t* aCertType) {
+ UniqueCERTCertificate cert(GetOrInstantiateCert());
+ if (!cert) {
+ return NS_ERROR_FAILURE;
+ }
+ CERTCertTrust certTrust{0, 0, 0};
+ // If there is no stored trust information, CERT_GetCertTrust will return
+ // SECFailure. This isn't a failure. In this case, all trust bits will remain
+ // unset.
+ Unused << CERT_GetCertTrust(cert.get(), &certTrust);
+ nsNSSCertTrust trust(&certTrust);
+ if (cert->nickname && trust.HasAnyUser()) {
+ *aCertType = nsIX509Cert::USER_CERT;
+ return NS_OK;
+ }
+ if (trust.HasAnyCA()) {
+ *aCertType = nsIX509Cert::CA_CERT;
+ return NS_OK;
+ }
+ if (trust.HasPeer(true, false)) {
+ *aCertType = nsIX509Cert::SERVER_CERT;
+ return NS_OK;
+ }
+ if (trust.HasPeer(false, true) && cert->emailAddr) {
+ *aCertType = nsIX509Cert::EMAIL_CERT;
+ return NS_OK;
+ }
+ if (CERT_IsCACert(cert.get(), nullptr)) {
+ *aCertType = nsIX509Cert::CA_CERT;
+ return NS_OK;
+ }
+ if (cert->emailAddr) {
+ *aCertType = nsIX509Cert::EMAIL_CERT;
+ return NS_OK;
+ }
+ *aCertType = nsIX509Cert::UNKNOWN_CERT;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsNSSCertificate::GetDbKey(nsACString& aDbKey) {
+ static_assert(sizeof(uint64_t) == 8, "type size consistency check");
+ static_assert(sizeof(uint32_t) == 4, "type size consistency check");
+
+ pkix::Input certInput;
+ pkix::Result result = certInput.Init(mDER.Elements(), mDER.Length());
+ if (result != pkix::Result::Success) {
+ return NS_ERROR_INVALID_ARG;
+ }
+ // NB: since we're not building a trust path, the endEntityOrCA parameter is
+ // irrelevant.
+ pkix::BackCert cert(certInput, pkix::EndEntityOrCA::MustBeEndEntity, nullptr);
+ result = cert.Init();
+ if (result != pkix::Result::Success) {
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ // 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 buf;
+ const char leadingZeroes[] = {0, 0, 0, 0, 0, 0, 0, 0};
+ buf.Append(leadingZeroes, sizeof(leadingZeroes));
+ uint32_t serialNumberLen = htonl(cert.GetSerialNumber().GetLength());
+ buf.Append(BitwiseCast<const char*, const uint32_t*>(&serialNumberLen),
+ sizeof(uint32_t));
+ uint32_t issuerLen = htonl(cert.GetIssuer().GetLength());
+ buf.Append(BitwiseCast<const char*, const uint32_t*>(&issuerLen),
+ sizeof(uint32_t));
+ buf.Append(BitwiseCast<const char*, const unsigned char*>(
+ cert.GetSerialNumber().UnsafeGetData()),
+ cert.GetSerialNumber().GetLength());
+ buf.Append(BitwiseCast<const char*, const unsigned char*>(
+ cert.GetIssuer().UnsafeGetData()),
+ cert.GetIssuer().GetLength());
+
+ return Base64Encode(buf, aDbKey);
+}
+
+NS_IMETHODIMP
+nsNSSCertificate::GetDisplayName(nsAString& aDisplayName) {
+ aDisplayName.Truncate();
+
+ UniqueCERTCertificate cert(GetOrInstantiateCert());
+ if (!cert) {
+ return NS_ERROR_FAILURE;
+ }
+
+ UniquePORTString commonName(CERT_GetCommonName(&cert->subject));
+ UniquePORTString organizationalUnitName(CERT_GetOrgUnitName(&cert->subject));
+ UniquePORTString organizationName(CERT_GetOrgName(&cert->subject));
+
+ // Only use the nickname for built-in roots where we already have a hard-coded
+ // reasonable display name (unfortunately we have to strip off the leading
+ // slot identifier followed by a ':'). Otherwise, attempt to use the following
+ // in order:
+ // - the common name, if present
+ // - an organizational unit name, if present
+ // - an organization name, if present
+ // - the entire subject distinguished name, if non-empty
+ // - an email address, if one can be found
+ // In the unlikely event that none of these fields are present and non-empty
+ // (the subject really shouldn't be empty), an empty string is returned.
+ nsAutoCString builtInRootNickname;
+ nsAutoCString fullNickname(cert->nickname);
+ static const nsLiteralCString kBuiltinObjectTokenPrefix =
+ "Builtin Object Token:"_ns;
+ if (StringBeginsWith(fullNickname, kBuiltinObjectTokenPrefix)) {
+ // Substring will gracefully handle the case where index is the last
+ // character in the string (that is, if the nickname is just
+ // "Builtin Object Token:"). In that case, we'll get an empty string.
+ builtInRootNickname =
+ Substring(fullNickname, kBuiltinObjectTokenPrefix.Length());
+ }
+ const char* nameOptions[] = {builtInRootNickname.get(),
+ commonName.get(),
+ organizationalUnitName.get(),
+ organizationName.get(),
+ cert->subjectName,
+ cert->emailAddr};
+
+ for (auto nameOption : nameOptions) {
+ if (nameOption) {
+ size_t len = strlen(nameOption);
+ if (len > 0) {
+ LossyUTF8ToUTF16(nameOption, len, aDisplayName);
+ return NS_OK;
+ }
+ }
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsNSSCertificate::GetEmailAddress(nsAString& aEmailAddress) {
+ UniqueCERTCertificate cert(GetOrInstantiateCert());
+ if (!cert) {
+ return NS_ERROR_FAILURE;
+ }
+ if (cert->emailAddr) {
+ CopyUTF8toUTF16(MakeStringSpan(cert->emailAddr), aEmailAddress);
+ } else {
+ GetPIPNSSBundleString("CertNoEmailAddress", aEmailAddress);
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsNSSCertificate::GetEmailAddresses(nsTArray<nsString>& aAddresses) {
+ UniqueCERTCertificate cert(GetOrInstantiateCert());
+ if (!cert) {
+ return NS_ERROR_FAILURE;
+ }
+ uint32_t length = 0;
+ for (const char* aAddr = CERT_GetFirstEmailAddress(cert.get()); aAddr;
+ aAddr = CERT_GetNextEmailAddress(cert.get(), aAddr)) {
+ ++(length);
+ }
+
+ aAddresses.SetCapacity(length);
+
+ for (const char* aAddr = CERT_GetFirstEmailAddress(cert.get()); aAddr;
+ aAddr = CERT_GetNextEmailAddress(cert.get(), aAddr)) {
+ CopyASCIItoUTF16(MakeStringSpan(aAddr), *aAddresses.AppendElement());
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsNSSCertificate::ContainsEmailAddress(const nsAString& aEmailAddress,
+ bool* result) {
+ NS_ENSURE_ARG(result);
+ *result = false;
+
+ UniqueCERTCertificate cert(GetOrInstantiateCert());
+ if (!cert) {
+ return NS_ERROR_FAILURE;
+ }
+ for (const char* aAddr = CERT_GetFirstEmailAddress(cert.get()); aAddr;
+ aAddr = CERT_GetNextEmailAddress(cert.get(), aAddr)) {
+ nsAutoString certAddr;
+ LossyUTF8ToUTF16(aAddr, strlen(aAddr), certAddr);
+ ToLowerCase(certAddr);
+
+ nsAutoString testAddr(aEmailAddress);
+ ToLowerCase(testAddr);
+
+ if (certAddr == testAddr) {
+ *result = true;
+ break;
+ }
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsNSSCertificate::GetCommonName(nsAString& aCommonName) {
+ aCommonName.Truncate();
+ UniqueCERTCertificate cert(GetOrInstantiateCert());
+ if (!cert) {
+ return NS_ERROR_FAILURE;
+ }
+ UniquePORTString commonName(CERT_GetCommonName(&cert->subject));
+ if (commonName) {
+ LossyUTF8ToUTF16(commonName.get(), strlen(commonName.get()), aCommonName);
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsNSSCertificate::GetOrganization(nsAString& aOrganization) {
+ aOrganization.Truncate();
+ UniqueCERTCertificate cert(GetOrInstantiateCert());
+ if (!cert) {
+ return NS_ERROR_FAILURE;
+ }
+ UniquePORTString organization(CERT_GetOrgName(&cert->subject));
+ if (organization) {
+ LossyUTF8ToUTF16(organization.get(), strlen(organization.get()),
+ aOrganization);
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsNSSCertificate::GetIssuerCommonName(nsAString& aCommonName) {
+ aCommonName.Truncate();
+ UniqueCERTCertificate cert(GetOrInstantiateCert());
+ if (!cert) {
+ return NS_ERROR_FAILURE;
+ }
+ UniquePORTString commonName(CERT_GetCommonName(&cert->issuer));
+ if (commonName) {
+ LossyUTF8ToUTF16(commonName.get(), strlen(commonName.get()), aCommonName);
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsNSSCertificate::GetIssuerOrganization(nsAString& aOrganization) {
+ aOrganization.Truncate();
+ UniqueCERTCertificate cert(GetOrInstantiateCert());
+ if (!cert) {
+ return NS_ERROR_FAILURE;
+ }
+ UniquePORTString organization(CERT_GetOrgName(&cert->issuer));
+ if (organization) {
+ LossyUTF8ToUTF16(organization.get(), strlen(organization.get()),
+ aOrganization);
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsNSSCertificate::GetIssuerOrganizationUnit(nsAString& aOrganizationUnit) {
+ aOrganizationUnit.Truncate();
+ UniqueCERTCertificate cert(GetOrInstantiateCert());
+ if (!cert) {
+ return NS_ERROR_FAILURE;
+ }
+ UniquePORTString organizationUnit(CERT_GetOrgUnitName(&cert->issuer));
+ if (organizationUnit) {
+ LossyUTF8ToUTF16(organizationUnit.get(), strlen(organizationUnit.get()),
+ aOrganizationUnit);
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsNSSCertificate::GetOrganizationalUnit(nsAString& aOrganizationalUnit) {
+ aOrganizationalUnit.Truncate();
+ UniqueCERTCertificate cert(GetOrInstantiateCert());
+ if (!cert) {
+ return NS_ERROR_FAILURE;
+ }
+ UniquePORTString orgunit(CERT_GetOrgUnitName(&cert->subject));
+ if (orgunit) {
+ LossyUTF8ToUTF16(orgunit.get(), strlen(orgunit.get()), aOrganizationalUnit);
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsNSSCertificate::GetSubjectName(nsAString& _subjectName) {
+ _subjectName.Truncate();
+ UniqueCERTCertificate cert(GetOrInstantiateCert());
+ if (!cert) {
+ return NS_ERROR_FAILURE;
+ }
+ if (cert->subjectName) {
+ LossyUTF8ToUTF16(cert->subjectName, strlen(cert->subjectName),
+ _subjectName);
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsNSSCertificate::GetIssuerName(nsAString& _issuerName) {
+ _issuerName.Truncate();
+ UniqueCERTCertificate cert(GetOrInstantiateCert());
+ if (!cert) {
+ return NS_ERROR_FAILURE;
+ }
+ if (cert->issuerName) {
+ LossyUTF8ToUTF16(cert->issuerName, strlen(cert->issuerName), _issuerName);
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsNSSCertificate::GetSerialNumber(nsAString& _serialNumber) {
+ _serialNumber.Truncate();
+ UniqueCERTCertificate cert(GetOrInstantiateCert());
+ if (!cert) {
+ return NS_ERROR_FAILURE;
+ }
+ UniquePORTString tmpstr(
+ CERT_Hexify(&cert->serialNumber, true /* use colon delimiters */));
+ if (tmpstr) {
+ _serialNumber = NS_ConvertASCIItoUTF16(tmpstr.get());
+ return NS_OK;
+ }
+ return NS_ERROR_FAILURE;
+}
+
+nsresult nsNSSCertificate::GetCertificateHash(nsAString& aFingerprint,
+ SECOidTag aHashAlg) {
+ aFingerprint.Truncate();
+
+ if (!EnsureNSSInitializedChromeOrContent()) {
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+
+ nsTArray<uint8_t> digestArray;
+ nsresult rv =
+ Digest::DigestBuf(aHashAlg, mDER.Elements(), mDER.Length(), digestArray);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ SECItem digestItem = {siBuffer, digestArray.Elements(),
+ static_cast<unsigned int>(digestArray.Length())};
+
+ UniquePORTString fpStr(
+ CERT_Hexify(&digestItem, true /* use colon delimiters */));
+ if (!fpStr) {
+ return NS_ERROR_FAILURE;
+ }
+
+ aFingerprint.AssignASCII(fpStr.get());
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsNSSCertificate::GetSha256Fingerprint(nsAString& aSha256Fingerprint) {
+ return GetCertificateHash(aSha256Fingerprint, SEC_OID_SHA256);
+}
+
+NS_IMETHODIMP
+nsNSSCertificate::GetSha1Fingerprint(nsAString& _sha1Fingerprint) {
+ return GetCertificateHash(_sha1Fingerprint, SEC_OID_SHA1);
+}
+
+NS_IMETHODIMP
+nsNSSCertificate::GetTokenName(nsAString& aTokenName) {
+ UniqueCERTCertificate cert(GetOrInstantiateCert());
+ if (!cert) {
+ return NS_ERROR_FAILURE;
+ }
+ UniquePK11SlotInfo internalSlot(PK11_GetInternalSlot());
+ if (!internalSlot) {
+ return NS_ERROR_FAILURE;
+ }
+ nsCOMPtr<nsIPK11Token> token(
+ new nsPK11Token(cert->slot ? cert->slot : internalSlot.get()));
+ nsAutoCString tmp;
+ nsresult rv = token->GetTokenName(tmp);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ aTokenName.Assign(NS_ConvertUTF8toUTF16(tmp));
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsNSSCertificate::GetSha256SubjectPublicKeyInfoDigest(
+ nsACString& aSha256SPKIDigest) {
+ aSha256SPKIDigest.Truncate();
+
+ if (!EnsureNSSInitializedChromeOrContent()) {
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+
+ pkix::Input certInput;
+ pkix::Result result = certInput.Init(mDER.Elements(), mDER.Length());
+ if (result != pkix::Result::Success) {
+ return NS_ERROR_INVALID_ARG;
+ }
+ // NB: since we're not building a trust path, the endEntityOrCA parameter is
+ // irrelevant.
+ pkix::BackCert cert(certInput, pkix::EndEntityOrCA::MustBeEndEntity, nullptr);
+ result = cert.Init();
+ if (result != pkix::Result::Success) {
+ return NS_ERROR_INVALID_ARG;
+ }
+ pkix::Input derPublicKey = cert.GetSubjectPublicKeyInfo();
+ nsTArray<uint8_t> digestArray;
+ nsresult rv = Digest::DigestBuf(SEC_OID_SHA256, derPublicKey.UnsafeGetData(),
+ derPublicKey.GetLength(), digestArray);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ rv = Base64Encode(nsDependentCSubstring(
+ reinterpret_cast<const char*>(digestArray.Elements()),
+ digestArray.Length()),
+ aSha256SPKIDigest);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsNSSCertificate::GetRawDER(nsTArray<uint8_t>& aArray) {
+ aArray.SetLength(mDER.Length());
+ memcpy(aArray.Elements(), mDER.Elements(), mDER.Length());
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsNSSCertificate::GetBase64DERString(nsACString& base64DERString) {
+ nsDependentCSubstring derString(
+ reinterpret_cast<const char*>(mDER.Elements()), mDER.Length());
+ nsresult rv = Base64Encode(derString, base64DERString);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ return NS_OK;
+}
+
+CERTCertificate* nsNSSCertificate::GetCert() {
+ UniqueCERTCertificate cert(GetOrInstantiateCert());
+ return cert.release(); // caller takes ownership
+}
+
+NS_IMETHODIMP
+nsNSSCertificate::GetValidity(nsIX509CertValidity** aValidity) {
+ NS_ENSURE_ARG(aValidity);
+ pkix::Input certInput;
+ pkix::Result rv = certInput.Init(mDER.Elements(), mDER.Length());
+ if (rv != pkix::Success) {
+ return NS_ERROR_FAILURE;
+ }
+ nsCOMPtr<nsIX509CertValidity> validity = new X509CertValidity(certInput);
+ validity.forget(aValidity);
+ return NS_OK;
+}
+
+// NB: Any updates (except disk-only fields) must be kept in sync with
+// |SerializeToIPC|.
+NS_IMETHODIMP
+nsNSSCertificate::Write(nsIObjectOutputStream* aStream) {
+ // This field used to be the cached EV status, but it is no longer necessary.
+ nsresult rv = aStream->Write32(0);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ rv = aStream->Write32(mDER.Length());
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ return aStream->WriteBytes(Span(mDER));
+}
+
+// NB: Any updates (except disk-only fields) must be kept in sync with
+// |DeserializeFromIPC|.
+NS_IMETHODIMP
+nsNSSCertificate::Read(nsIObjectInputStream* aStream) {
+ auto lock = mCert.Lock();
+ auto& maybeCert = lock.ref();
+ if (!mDER.IsEmpty() || maybeCert.isSome()) {
+ return NS_ERROR_ALREADY_INITIALIZED;
+ }
+
+ // This field is no longer used.
+ uint32_t unusedCachedEVStatus;
+ nsresult rv = aStream->Read32(&unusedCachedEVStatus);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ uint32_t len;
+ rv = aStream->Read32(&len);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ rv = aStream->ReadByteArray(len, mDER);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ return NS_OK;
+}
+
+void nsNSSCertificate::SerializeToIPC(IPC::MessageWriter* aWriter) {
+ bool hasCert = !mDER.IsEmpty();
+ WriteParam(aWriter, hasCert);
+
+ if (!hasCert) {
+ return;
+ }
+
+ WriteParam(aWriter, mDER);
+}
+
+bool nsNSSCertificate::DeserializeFromIPC(IPC::MessageReader* aReader) {
+ auto lock = mCert.Lock();
+ auto& maybeCert = lock.ref();
+ if (!mDER.IsEmpty() || maybeCert.isSome()) {
+ return false;
+ }
+
+ bool hasCert = false;
+ if (!ReadParam(aReader, &hasCert)) {
+ return false;
+ }
+
+ if (!hasCert) {
+ return true;
+ }
+
+ if (!ReadParam(aReader, &mDER)) {
+ return false;
+ }
+ return true;
+}
+
+NS_IMETHODIMP
+nsNSSCertificate::GetInterfaces(nsTArray<nsIID>& array) {
+ array.Clear();
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsNSSCertificate::GetScriptableHelper(nsIXPCScriptable** _retval) {
+ *_retval = nullptr;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsNSSCertificate::GetContractID(nsACString& aContractID) {
+ aContractID.SetIsVoid(true);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsNSSCertificate::GetClassDescription(nsACString& aClassDescription) {
+ aClassDescription.SetIsVoid(true);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsNSSCertificate::GetClassID(nsCID** aClassID) {
+ *aClassID = (nsCID*)moz_xmalloc(sizeof(nsCID));
+ return GetClassIDNoAlloc(*aClassID);
+}
+
+NS_IMETHODIMP
+nsNSSCertificate::GetFlags(uint32_t* aFlags) {
+ *aFlags = nsIClassInfo::THREADSAFE;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsNSSCertificate::GetClassIDNoAlloc(nsCID* aClassIDNoAlloc) {
+ static NS_DEFINE_CID(kNSSCertificateCID, NS_X509CERT_CID);
+
+ *aClassIDNoAlloc = kNSSCertificateCID;
+ return NS_OK;
+}