summaryrefslogtreecommitdiff
path: root/mozilla/security/nss/lib/pki/pkistore.c
diff options
context:
space:
mode:
Diffstat (limited to 'mozilla/security/nss/lib/pki/pkistore.c')
-rw-r--r--mozilla/security/nss/lib/pki/pkistore.c774
1 files changed, 774 insertions, 0 deletions
diff --git a/mozilla/security/nss/lib/pki/pkistore.c b/mozilla/security/nss/lib/pki/pkistore.c
new file mode 100644
index 0000000..3371fdd
--- /dev/null
+++ b/mozilla/security/nss/lib/pki/pkistore.c
@@ -0,0 +1,774 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is the Netscape security libraries.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1994-2000
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifdef DEBUG
+static const char CVS_ID[] = "@(#) $RCSfile: pkistore.c,v $ $Revision: 1.33.40.1 $ $Date: 2010/12/17 20:14:38 $";
+#endif /* DEBUG */
+
+#ifndef PKIM_H
+#include "pkim.h"
+#endif /* PKIM_H */
+
+#ifndef PKI_H
+#include "pki.h"
+#endif /* PKI_H */
+
+#ifndef NSSPKI_H
+#include "nsspki.h"
+#endif /* NSSPKI_H */
+
+#ifndef BASE_H
+#include "base.h"
+#endif /* BASE_H */
+
+#ifndef PKISTORE_H
+#include "pkistore.h"
+#endif /* PKISTORE_H */
+
+#include "cert.h"
+
+#include "prbit.h"
+
+/*
+ * Certificate Store
+ *
+ * This differs from the cache in that it is a true storage facility. Items
+ * stay in until they are explicitly removed. It is only used by crypto
+ * contexts at this time, but may be more generally useful...
+ *
+ */
+
+struct nssCertificateStoreStr
+{
+ PRBool i_alloced_arena;
+ NSSArena *arena;
+ PZLock *lock;
+ nssHash *subject;
+ nssHash *issuer_and_serial;
+};
+
+typedef struct certificate_hash_entry_str certificate_hash_entry;
+
+struct certificate_hash_entry_str
+{
+ NSSCertificate *cert;
+ NSSTrust *trust;
+ nssSMIMEProfile *profile;
+};
+
+/* forward static declarations */
+static NSSCertificate *
+nssCertStore_FindCertByIssuerAndSerialNumberLocked (
+ nssCertificateStore *store,
+ NSSDER *issuer,
+ NSSDER *serial
+);
+
+NSS_IMPLEMENT nssCertificateStore *
+nssCertificateStore_Create (
+ NSSArena *arenaOpt
+)
+{
+ NSSArena *arena;
+ nssCertificateStore *store;
+ PRBool i_alloced_arena;
+ if (arenaOpt) {
+ arena = arenaOpt;
+ i_alloced_arena = PR_FALSE;
+ } else {
+ arena = nssArena_Create();
+ if (!arena) {
+ return NULL;
+ }
+ i_alloced_arena = PR_TRUE;
+ }
+ store = nss_ZNEW(arena, nssCertificateStore);
+ if (!store) {
+ goto loser;
+ }
+ store->lock = PZ_NewLock(nssILockOther);
+ if (!store->lock) {
+ goto loser;
+ }
+ /* Create the issuer/serial --> {cert, trust, S/MIME profile } hash */
+ store->issuer_and_serial = nssHash_CreateCertificate(arena, 0);
+ if (!store->issuer_and_serial) {
+ goto loser;
+ }
+ /* Create the subject DER --> subject list hash */
+ store->subject = nssHash_CreateItem(arena, 0);
+ if (!store->subject) {
+ goto loser;
+ }
+ store->arena = arena;
+ store->i_alloced_arena = i_alloced_arena;
+ return store;
+loser:
+ if (store) {
+ if (store->lock) {
+ PZ_DestroyLock(store->lock);
+ }
+ if (store->issuer_and_serial) {
+ nssHash_Destroy(store->issuer_and_serial);
+ }
+ if (store->subject) {
+ nssHash_Destroy(store->subject);
+ }
+ }
+ if (i_alloced_arena) {
+ nssArena_Destroy(arena);
+ }
+ return NULL;
+}
+
+extern const NSSError NSS_ERROR_BUSY;
+
+NSS_IMPLEMENT PRStatus
+nssCertificateStore_Destroy (
+ nssCertificateStore *store
+)
+{
+ if (nssHash_Count(store->issuer_and_serial) > 0) {
+ nss_SetError(NSS_ERROR_BUSY);
+ return PR_FAILURE;
+ }
+ PZ_DestroyLock(store->lock);
+ nssHash_Destroy(store->issuer_and_serial);
+ nssHash_Destroy(store->subject);
+ if (store->i_alloced_arena) {
+ nssArena_Destroy(store->arena);
+ } else {
+ nss_ZFreeIf(store);
+ }
+ return PR_SUCCESS;
+}
+
+static PRStatus
+add_certificate_entry (
+ nssCertificateStore *store,
+ NSSCertificate *cert
+)
+{
+ PRStatus nssrv;
+ certificate_hash_entry *entry;
+ entry = nss_ZNEW(cert->object.arena, certificate_hash_entry);
+ if (!entry) {
+ return PR_FAILURE;
+ }
+ entry->cert = cert;
+ nssrv = nssHash_Add(store->issuer_and_serial, cert, entry);
+ if (nssrv != PR_SUCCESS) {
+ nss_ZFreeIf(entry);
+ }
+ return nssrv;
+}
+
+static PRStatus
+add_subject_entry (
+ nssCertificateStore *store,
+ NSSCertificate *cert
+)
+{
+ PRStatus nssrv;
+ nssList *subjectList;
+ subjectList = (nssList *)nssHash_Lookup(store->subject, &cert->subject);
+ if (subjectList) {
+ /* The subject is already in, add this cert to the list */
+ nssrv = nssList_AddUnique(subjectList, cert);
+ } else {
+ /* Create a new subject list for the subject */
+ subjectList = nssList_Create(NULL, PR_FALSE);
+ if (!subjectList) {
+ return PR_FAILURE;
+ }
+ nssList_SetSortFunction(subjectList, nssCertificate_SubjectListSort);
+ /* Add the cert entry to this list of subjects */
+ nssrv = nssList_Add(subjectList, cert);
+ if (nssrv != PR_SUCCESS) {
+ return nssrv;
+ }
+ /* Add the subject list to the cache */
+ nssrv = nssHash_Add(store->subject, &cert->subject, subjectList);
+ }
+ return nssrv;
+}
+
+/* declared below */
+static void
+remove_certificate_entry (
+ nssCertificateStore *store,
+ NSSCertificate *cert
+);
+
+/* Caller must hold store->lock */
+static PRStatus
+nssCertificateStore_AddLocked (
+ nssCertificateStore *store,
+ NSSCertificate *cert
+)
+{
+ PRStatus nssrv = add_certificate_entry(store, cert);
+ if (nssrv == PR_SUCCESS) {
+ nssrv = add_subject_entry(store, cert);
+ if (nssrv == PR_FAILURE) {
+ remove_certificate_entry(store, cert);
+ }
+ }
+ return nssrv;
+}
+
+
+NSS_IMPLEMENT NSSCertificate *
+nssCertificateStore_FindOrAdd (
+ nssCertificateStore *store,
+ NSSCertificate *c
+)
+{
+ PRStatus nssrv;
+ NSSCertificate *rvCert = NULL;
+
+ PZ_Lock(store->lock);
+ rvCert = nssCertStore_FindCertByIssuerAndSerialNumberLocked(
+ store, &c->issuer, &c->serial);
+ if (!rvCert) {
+ nssrv = nssCertificateStore_AddLocked(store, c);
+ if (PR_SUCCESS == nssrv) {
+ rvCert = nssCertificate_AddRef(c);
+ }
+ }
+ PZ_Unlock(store->lock);
+ return rvCert;
+}
+
+static void
+remove_certificate_entry (
+ nssCertificateStore *store,
+ NSSCertificate *cert
+)
+{
+ certificate_hash_entry *entry;
+ entry = (certificate_hash_entry *)
+ nssHash_Lookup(store->issuer_and_serial, cert);
+ if (entry) {
+ nssHash_Remove(store->issuer_and_serial, cert);
+ if (entry->trust) {
+ nssTrust_Destroy(entry->trust);
+ }
+ if (entry->profile) {
+ nssSMIMEProfile_Destroy(entry->profile);
+ }
+ nss_ZFreeIf(entry);
+ }
+}
+
+static void
+remove_subject_entry (
+ nssCertificateStore *store,
+ NSSCertificate *cert
+)
+{
+ nssList *subjectList;
+ /* Get the subject list for the cert's subject */
+ subjectList = (nssList *)nssHash_Lookup(store->subject, &cert->subject);
+ if (subjectList) {
+ /* Remove the cert from the subject hash */
+ nssList_Remove(subjectList, cert);
+ nssHash_Remove(store->subject, &cert->subject);
+ if (nssList_Count(subjectList) == 0) {
+ nssList_Destroy(subjectList);
+ } else {
+ /* The cert being released may have keyed the subject entry.
+ * Since there are still subject certs around, get another and
+ * rekey the entry just in case.
+ */
+ NSSCertificate *subjectCert;
+ (void)nssList_GetArray(subjectList, (void **)&subjectCert, 1);
+ nssHash_Add(store->subject, &subjectCert->subject, subjectList);
+ }
+ }
+}
+
+NSS_IMPLEMENT void
+nssCertificateStore_RemoveCertLOCKED (
+ nssCertificateStore *store,
+ NSSCertificate *cert
+)
+{
+ certificate_hash_entry *entry;
+ entry = (certificate_hash_entry *)
+ nssHash_Lookup(store->issuer_and_serial, cert);
+ if (entry && entry->cert == cert) {
+ remove_certificate_entry(store, cert);
+ remove_subject_entry(store, cert);
+ }
+}
+
+NSS_IMPLEMENT void
+nssCertificateStore_Lock (
+ nssCertificateStore *store, nssCertificateStoreTrace* out
+)
+{
+#ifdef DEBUG
+ PORT_Assert(out);
+ out->store = store;
+ out->lock = store->lock;
+ out->locked = PR_TRUE;
+ PZ_Lock(out->lock);
+#else
+ PZ_Lock(store->lock);
+#endif
+}
+
+NSS_IMPLEMENT void
+nssCertificateStore_Unlock (
+ nssCertificateStore *store, const nssCertificateStoreTrace* in,
+ nssCertificateStoreTrace* out
+)
+{
+#ifdef DEBUG
+ PORT_Assert(in);
+ PORT_Assert(out);
+ out->store = store;
+ out->lock = store->lock;
+ PORT_Assert(!out->locked);
+ out->unlocked = PR_TRUE;
+
+ PORT_Assert(in->store == out->store);
+ PORT_Assert(in->lock == out->lock);
+ PORT_Assert(in->locked);
+ PORT_Assert(!in->unlocked);
+
+ PZ_Unlock(out->lock);
+#else
+ PZ_Unlock(store->lock);
+#endif
+}
+
+static NSSCertificate **
+get_array_from_list (
+ nssList *certList,
+ NSSCertificate *rvOpt[],
+ PRUint32 maximumOpt,
+ NSSArena *arenaOpt
+)
+{
+ PRUint32 count;
+ NSSCertificate **rvArray = NULL;
+ count = nssList_Count(certList);
+ if (count == 0) {
+ return NULL;
+ }
+ if (maximumOpt > 0) {
+ count = PR_MIN(maximumOpt, count);
+ }
+ if (rvOpt) {
+ nssList_GetArray(certList, (void **)rvOpt, count);
+ } else {
+ rvArray = nss_ZNEWARRAY(arenaOpt, NSSCertificate *, count + 1);
+ if (rvArray) {
+ nssList_GetArray(certList, (void **)rvArray, count);
+ }
+ }
+ return rvArray;
+}
+
+NSS_IMPLEMENT NSSCertificate **
+nssCertificateStore_FindCertificatesBySubject (
+ nssCertificateStore *store,
+ NSSDER *subject,
+ NSSCertificate *rvOpt[],
+ PRUint32 maximumOpt,
+ NSSArena *arenaOpt
+)
+{
+ NSSCertificate **rvArray = NULL;
+ nssList *subjectList;
+ PZ_Lock(store->lock);
+ subjectList = (nssList *)nssHash_Lookup(store->subject, subject);
+ if (subjectList) {
+ nssCertificateList_AddReferences(subjectList);
+ rvArray = get_array_from_list(subjectList,
+ rvOpt, maximumOpt, arenaOpt);
+ }
+ PZ_Unlock(store->lock);
+ return rvArray;
+}
+
+/* Because only subject indexing is implemented, all other lookups require
+ * full traversal (unfortunately, PLHashTable doesn't allow you to exit
+ * early from the enumeration). The assumptions are that 1) lookups by
+ * fields other than subject will be rare, and 2) the hash will not have
+ * a large number of entries. These assumptions will be tested.
+ *
+ * XXX
+ * For NSS 3.4, it is worth consideration to do all forms of indexing,
+ * because the only crypto context is global and persistent.
+ */
+
+struct nickname_template_str
+{
+ NSSUTF8 *nickname;
+ nssList *subjectList;
+};
+
+static void match_nickname(const void *k, void *v, void *a)
+{
+ PRStatus nssrv;
+ NSSCertificate *c;
+ NSSUTF8 *nickname;
+ nssList *subjectList = (nssList *)v;
+ struct nickname_template_str *nt = (struct nickname_template_str *)a;
+ nssrv = nssList_GetArray(subjectList, (void **)&c, 1);
+ nickname = nssCertificate_GetNickname(c, NULL);
+ if (nssrv == PR_SUCCESS && nickname &&
+ nssUTF8_Equal(nickname, nt->nickname, &nssrv))
+ {
+ nt->subjectList = subjectList;
+ }
+}
+
+/*
+ * Find all cached certs with this label.
+ */
+NSS_IMPLEMENT NSSCertificate **
+nssCertificateStore_FindCertificatesByNickname (
+ nssCertificateStore *store,
+ const NSSUTF8 *nickname,
+ NSSCertificate *rvOpt[],
+ PRUint32 maximumOpt,
+ NSSArena *arenaOpt
+)
+{
+ NSSCertificate **rvArray = NULL;
+ struct nickname_template_str nt;
+ nt.nickname = (char*) nickname;
+ nt.subjectList = NULL;
+ PZ_Lock(store->lock);
+ nssHash_Iterate(store->subject, match_nickname, &nt);
+ if (nt.subjectList) {
+ nssCertificateList_AddReferences(nt.subjectList);
+ rvArray = get_array_from_list(nt.subjectList,
+ rvOpt, maximumOpt, arenaOpt);
+ }
+ PZ_Unlock(store->lock);
+ return rvArray;
+}
+
+struct email_template_str
+{
+ NSSASCII7 *email;
+ nssList *emailList;
+};
+
+static void match_email(const void *k, void *v, void *a)
+{
+ PRStatus nssrv;
+ NSSCertificate *c;
+ nssList *subjectList = (nssList *)v;
+ struct email_template_str *et = (struct email_template_str *)a;
+ nssrv = nssList_GetArray(subjectList, (void **)&c, 1);
+ if (nssrv == PR_SUCCESS &&
+ nssUTF8_Equal(c->email, et->email, &nssrv))
+ {
+ nssListIterator *iter = nssList_CreateIterator(subjectList);
+ if (iter) {
+ for (c = (NSSCertificate *)nssListIterator_Start(iter);
+ c != (NSSCertificate *)NULL;
+ c = (NSSCertificate *)nssListIterator_Next(iter))
+ {
+ nssList_Add(et->emailList, c);
+ }
+ nssListIterator_Finish(iter);
+ nssListIterator_Destroy(iter);
+ }
+ }
+}
+
+/*
+ * Find all cached certs with this email address.
+ */
+NSS_IMPLEMENT NSSCertificate **
+nssCertificateStore_FindCertificatesByEmail (
+ nssCertificateStore *store,
+ NSSASCII7 *email,
+ NSSCertificate *rvOpt[],
+ PRUint32 maximumOpt,
+ NSSArena *arenaOpt
+)
+{
+ NSSCertificate **rvArray = NULL;
+ struct email_template_str et;
+ et.email = email;
+ et.emailList = nssList_Create(NULL, PR_FALSE);
+ if (!et.emailList) {
+ return NULL;
+ }
+ PZ_Lock(store->lock);
+ nssHash_Iterate(store->subject, match_email, &et);
+ if (et.emailList) {
+ /* get references before leaving the store's lock protection */
+ nssCertificateList_AddReferences(et.emailList);
+ }
+ PZ_Unlock(store->lock);
+ if (et.emailList) {
+ rvArray = get_array_from_list(et.emailList,
+ rvOpt, maximumOpt, arenaOpt);
+ nssList_Destroy(et.emailList);
+ }
+ return rvArray;
+}
+
+/* Caller holds store->lock */
+static NSSCertificate *
+nssCertStore_FindCertByIssuerAndSerialNumberLocked (
+ nssCertificateStore *store,
+ NSSDER *issuer,
+ NSSDER *serial
+)
+{
+ certificate_hash_entry *entry;
+ NSSCertificate *rvCert = NULL;
+ NSSCertificate index;
+
+ index.issuer = *issuer;
+ index.serial = *serial;
+ entry = (certificate_hash_entry *)
+ nssHash_Lookup(store->issuer_and_serial, &index);
+ if (entry) {
+ rvCert = nssCertificate_AddRef(entry->cert);
+ }
+ return rvCert;
+}
+
+NSS_IMPLEMENT NSSCertificate *
+nssCertificateStore_FindCertificateByIssuerAndSerialNumber (
+ nssCertificateStore *store,
+ NSSDER *issuer,
+ NSSDER *serial
+)
+{
+ NSSCertificate *rvCert = NULL;
+
+ PZ_Lock(store->lock);
+ rvCert = nssCertStore_FindCertByIssuerAndSerialNumberLocked (
+ store, issuer, serial);
+ PZ_Unlock(store->lock);
+ return rvCert;
+}
+
+static PRStatus
+issuer_and_serial_from_encoding (
+ NSSBER *encoding,
+ NSSDER *issuer,
+ NSSDER *serial
+)
+{
+ SECItem derCert, derIssuer, derSerial;
+ SECStatus secrv;
+ derCert.data = (unsigned char *)encoding->data;
+ derCert.len = encoding->size;
+ secrv = CERT_IssuerNameFromDERCert(&derCert, &derIssuer);
+ if (secrv != SECSuccess) {
+ return PR_FAILURE;
+ }
+ secrv = CERT_SerialNumberFromDERCert(&derCert, &derSerial);
+ if (secrv != SECSuccess) {
+ PORT_Free(derIssuer.data);
+ return PR_FAILURE;
+ }
+ issuer->data = derIssuer.data;
+ issuer->size = derIssuer.len;
+ serial->data = derSerial.data;
+ serial->size = derSerial.len;
+ return PR_SUCCESS;
+}
+
+NSS_IMPLEMENT NSSCertificate *
+nssCertificateStore_FindCertificateByEncodedCertificate (
+ nssCertificateStore *store,
+ NSSDER *encoding
+)
+{
+ PRStatus nssrv = PR_FAILURE;
+ NSSDER issuer, serial;
+ NSSCertificate *rvCert = NULL;
+ nssrv = issuer_and_serial_from_encoding(encoding, &issuer, &serial);
+ if (nssrv != PR_SUCCESS) {
+ return NULL;
+ }
+ rvCert = nssCertificateStore_FindCertificateByIssuerAndSerialNumber(store,
+ &issuer,
+ &serial);
+ PORT_Free(issuer.data);
+ PORT_Free(serial.data);
+ return rvCert;
+}
+
+NSS_EXTERN PRStatus
+nssCertificateStore_AddTrust (
+ nssCertificateStore *store,
+ NSSTrust *trust
+)
+{
+ NSSCertificate *cert;
+ certificate_hash_entry *entry;
+ cert = trust->certificate;
+ PZ_Lock(store->lock);
+ entry = (certificate_hash_entry *)
+ nssHash_Lookup(store->issuer_and_serial, cert);
+ if (entry) {
+ NSSTrust* newTrust = nssTrust_AddRef(trust);
+ if (entry->trust) {
+ nssTrust_Destroy(entry->trust);
+ }
+ entry->trust = newTrust;
+ }
+ PZ_Unlock(store->lock);
+ return (entry) ? PR_SUCCESS : PR_FAILURE;
+}
+
+NSS_IMPLEMENT NSSTrust *
+nssCertificateStore_FindTrustForCertificate (
+ nssCertificateStore *store,
+ NSSCertificate *cert
+)
+{
+ certificate_hash_entry *entry;
+ NSSTrust *rvTrust = NULL;
+ PZ_Lock(store->lock);
+ entry = (certificate_hash_entry *)
+ nssHash_Lookup(store->issuer_and_serial, cert);
+ if (entry && entry->trust) {
+ rvTrust = nssTrust_AddRef(entry->trust);
+ }
+ PZ_Unlock(store->lock);
+ return rvTrust;
+}
+
+NSS_EXTERN PRStatus
+nssCertificateStore_AddSMIMEProfile (
+ nssCertificateStore *store,
+ nssSMIMEProfile *profile
+)
+{
+ NSSCertificate *cert;
+ certificate_hash_entry *entry;
+ cert = profile->certificate;
+ PZ_Lock(store->lock);
+ entry = (certificate_hash_entry *)
+ nssHash_Lookup(store->issuer_and_serial, cert);
+ if (entry) {
+ nssSMIMEProfile* newProfile = nssSMIMEProfile_AddRef(profile);
+ if (entry->profile) {
+ nssSMIMEProfile_Destroy(entry->profile);
+ }
+ entry->profile = newProfile;
+ }
+ PZ_Unlock(store->lock);
+ return (entry) ? PR_SUCCESS : PR_FAILURE;
+}
+
+NSS_IMPLEMENT nssSMIMEProfile *
+nssCertificateStore_FindSMIMEProfileForCertificate (
+ nssCertificateStore *store,
+ NSSCertificate *cert
+)
+{
+ certificate_hash_entry *entry;
+ nssSMIMEProfile *rvProfile = NULL;
+ PZ_Lock(store->lock);
+ entry = (certificate_hash_entry *)
+ nssHash_Lookup(store->issuer_and_serial, cert);
+ if (entry && entry->profile) {
+ rvProfile = nssSMIMEProfile_AddRef(entry->profile);
+ }
+ PZ_Unlock(store->lock);
+ return rvProfile;
+}
+
+/* XXX this is also used by cache and should be somewhere else */
+
+static PLHashNumber
+nss_certificate_hash (
+ const void *key
+)
+{
+ unsigned int i;
+ PLHashNumber h;
+ NSSCertificate *c = (NSSCertificate *)key;
+ h = 0;
+ for (i=0; i<c->issuer.size; i++)
+ h = PR_ROTATE_LEFT32(h, 4) ^ ((unsigned char *)c->issuer.data)[i];
+ for (i=0; i<c->serial.size; i++)
+ h = PR_ROTATE_LEFT32(h, 4) ^ ((unsigned char *)c->serial.data)[i];
+ return h;
+}
+
+static int
+nss_compare_certs(const void *v1, const void *v2)
+{
+ PRStatus ignore;
+ NSSCertificate *c1 = (NSSCertificate *)v1;
+ NSSCertificate *c2 = (NSSCertificate *)v2;
+ return (int)(nssItem_Equal(&c1->issuer, &c2->issuer, &ignore) &&
+ nssItem_Equal(&c1->serial, &c2->serial, &ignore));
+}
+
+NSS_IMPLEMENT nssHash *
+nssHash_CreateCertificate (
+ NSSArena *arenaOpt,
+ PRUint32 numBuckets
+)
+{
+ return nssHash_Create(arenaOpt,
+ numBuckets,
+ nss_certificate_hash,
+ nss_compare_certs,
+ PL_CompareValues);
+}
+
+NSS_IMPLEMENT void
+nssCertificateStore_DumpStoreInfo (
+ nssCertificateStore *store,
+ void (* cert_dump_iter)(const void *, void *, void *),
+ void *arg
+)
+{
+ PZ_Lock(store->lock);
+ nssHash_Iterate(store->issuer_and_serial, cert_dump_iter, arg);
+ PZ_Unlock(store->lock);
+}
+