Bug 374336, add knowledge of Extended Validation / EV Certificates to PSM r=rrelyea, a=mconnor/beltzner
authorkaie@kuix.de
Tue, 23 Oct 2007 11:30:16 -0700
changeset 7105 ce6093b9f6625abba78ca0222223e899e8a0b8ad
parent 7104 c3a3944ce9c4b1778b06e076bd804699f59ea6c8
child 7106 b0ffb1d916561a3f3cb5a7e49b3a9da44f01ac3d
push id1
push userroot
push dateTue, 26 Apr 2011 22:38:44 +0000
treeherdermozilla-beta@bfdb6e623a36 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersrrelyea, mconnor, beltzner
bugs374336
milestone1.9a9pre
Bug 374336, add knowledge of Extended Validation / EV Certificates to PSM r=rrelyea, a=mconnor/beltzner
security/manager/locales/en-US/chrome/pipnss/pipnss.properties
security/manager/ssl/public/nsIIdentityInfo.idl
security/manager/ssl/src/nsIdentityChecking.cpp
security/manager/ssl/src/nsNSSCertHelper.cpp
security/manager/ssl/src/nsNSSCertificate.cpp
security/manager/ssl/src/nsNSSCertificate.h
security/manager/ssl/src/nsNSSComponent.cpp
security/manager/ssl/src/nsNSSComponent.h
--- a/security/manager/locales/en-US/chrome/pipnss/pipnss.properties
+++ b/security/manager/locales/en-US/chrome/pipnss/pipnss.properties
@@ -220,16 +220,17 @@ CertDumpKeyCompromise=Key Compromise
 CertDumpCACompromise=CA Compromise
 CertDumpAffiliationChanged=Affiliation Changed
 CertDumpSuperseded=Superseded
 CertDumpCessation=Cessation of Operation
 CertDumpHold=Certificate Hold
 CertDumpOCSPResponder=OCSP
 CertDumpCAIssuers=CA Issuers
 CertDumpCPSPointer=Certification Practice Statement pointer
+CertDumpPolicyOidEV=Extended Validation (EV) SSL Server Certificate
 CertDumpUserNotice=User Notice
 CertDumpLogotype=Logotype
 CertDumpECPublicKey=Elliptic Curve Public Key
 CertDumpECDSAWithSHA1=X9.62 ECDSA Signature with SHA1
 CertDumpECprime192v1=ANSI X9.62 elliptic curve prime192v1 (aka secp192r1, NIST P-192)
 CertDumpECprime192v2=ANSI X9.62 elliptic curve prime192v2
 CertDumpECprime192v3=ANSI X9.62 elliptic curve prime192v3
 CertDumpECprime239v1=ANSI X9.62 elliptic curve prime239v1
--- a/security/manager/ssl/public/nsIIdentityInfo.idl
+++ b/security/manager/ssl/public/nsIIdentityInfo.idl
@@ -31,14 +31,32 @@
  * 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 ***** */
 
 #include "nsISupports.idl"
 
-[scriptable, uuid(e72546a4-9b57-4766-a3aa-c05cbc7d8156)]
+[scriptable, uuid(e9da87b8-b87c-4bd1-a6bc-5a9a2c7f6d8d)]
 interface nsIIdentityInfo : nsISupports
 {
+  /**
+   * A "true" value means:
+   *   The object that implements this interface uses a certificate that
+   *   was successfully verified as an Extended Validation (EV) cert.
+   *   The test is bound to SSL Server Cert Usage.
+   */
   readonly attribute boolean isExtendedValidation;
+
+  /**
+   * This function uses the same test as attribute
+   *   isExtendedValidation
+   *
+   * If isExtendedValidation is true, this function will return
+   * a policy identifier in dotted notation (like 1.2.3.4.5).
+   *
+   * If isExtendedValidation is false, this function will return
+   * an empty (length string) value.
+   */
+  ACString getValidEVPolicyOid();
 };
 
--- a/security/manager/ssl/src/nsIdentityChecking.cpp
+++ b/security/manager/ssl/src/nsIdentityChecking.cpp
@@ -1,11 +1,595 @@
+#include "nsAppDirectoryServiceDefs.h"
+#include "nsStreamUtils.h"
+#include "nsNetUtil.h"
+#include "nsILineInputStream.h"
+#include "nsPromiseFlatString.h"
+#include "nsTArray.h"
+
+#include "cert.h"
+#include "nsNSSComponent.h"
 #include "nsNSSIOLayer.h"
+#include "nsNSSCertificate.h"
+#include "nsNSSCleaner.h"
+
+#ifdef DEBUG
+#ifndef PSM_ENABLE_TEST_EV_ROOTS
+#define PSM_ENABLE_TEST_EV_ROOTS
+#endif
+#endif
+
+NSSCleanupAutoPtrClass(CERTCertificate, CERT_DestroyCertificate)
+NSSCleanupAutoPtrClass(CERTCertList, CERT_DestroyCertList)
+NSSCleanupAutoPtrClass_WithParam(SECItem, SECITEM_FreeItem, TrueParam, PR_TRUE)
+
+#define CONST_OID static const unsigned char
+#define OI(x) { siDEROID, (unsigned char *)x, sizeof x }
+
+struct nsMyTrustedEVInfo
+{
+  const char *dotted_oid;
+  const char *oid_name; // Set this to null to signal an invalid structure,
+                        // (We can't have an empty list, so we'll use a dummy entry)
+  SECOidTag oid_tag;
+  const char *ev_root_subject;
+  const char *ev_root_issuer;
+  const char *ev_root_sha1_fingerprint;
+};
+
+static struct nsMyTrustedEVInfo myTrustedEVInfos[] = {
+  {
+    "0.0.0.0",
+    0, // for real entries use a string like "Sample INVALID EV OID"
+    SEC_OID_UNKNOWN,
+    "OU=Sample Certification Authority,O=\"Sample, Inc.\",C=US",
+    "OU=Sample Certification Authority,O=\"Sample, Inc.\",C=US",
+    "00:11:22:33:44:55:66:77:88:99:AA:BB:CC:DD:EE:FF:00:11:22:33"
+  }
+};
+
+static SECOidTag
+register_oid(const SECItem *oid_item, const char *oid_name)
+{
+  if (!oid_item)
+    return SEC_OID_UNKNOWN;
+
+  SECOidData od;
+  od.oid.len = oid_item->len;
+  od.oid.data = oid_item->data;
+  od.offset = SEC_OID_UNKNOWN;
+  od.desc = oid_name;
+  od.mechanism = CKM_INVALID_MECHANISM;
+  od.supportedExtension = INVALID_CERT_EXTENSION;
+  return SECOID_AddEntry(&od);
+}
+
+#ifdef PSM_ENABLE_TEST_EV_ROOTS
+class nsMyTrustedEVInfoClass : public nsMyTrustedEVInfo
+{
+public:
+  nsMyTrustedEVInfoClass();
+  ~nsMyTrustedEVInfoClass();
+};
+
+nsMyTrustedEVInfoClass::nsMyTrustedEVInfoClass()
+{
+  dotted_oid = nsnull;
+  oid_name = nsnull;
+  oid_tag = SEC_OID_UNKNOWN;
+  ev_root_subject = nsnull;
+  ev_root_issuer = nsnull;
+  ev_root_sha1_fingerprint = nsnull;
+}
+
+nsMyTrustedEVInfoClass::~nsMyTrustedEVInfoClass()
+{
+  delete dotted_oid;
+  delete oid_name;
+  delete ev_root_subject;
+  delete ev_root_issuer;
+  delete ev_root_sha1_fingerprint;
+}
+
+typedef nsTArray< nsMyTrustedEVInfoClass* > testEVArray; 
+static testEVArray *testEVInfos;
+static PRBool testEVInfosLoaded = PR_FALSE;
+#endif
+
+static PRBool isEVMatch(SECOidTag policyOIDTag, 
+                        CERTCertificate *rootCert, 
+                        const nsMyTrustedEVInfo &info)
+{
+  if (!rootCert)
+    return PR_FALSE;
+
+  NS_ConvertUTF8toUTF16 info_subject(info.ev_root_subject);
+  NS_ConvertUTF8toUTF16 info_issuer(info.ev_root_issuer);
+  NS_ConvertASCIItoUTF16 info_sha1(info.ev_root_sha1_fingerprint);
+
+  nsNSSCertificate c(rootCert);
+
+  nsAutoString subjectName;
+  if (NS_FAILED(c.GetSubjectName(subjectName)))
+    return PR_FALSE;
+
+  if (subjectName != info_subject)
+    return PR_FALSE;
+
+  nsAutoString issuerName;
+  if (NS_FAILED(c.GetIssuerName(issuerName)))
+    return PR_FALSE;
+
+  if (issuerName != info_issuer)
+    return PR_FALSE;
+
+  nsAutoString fingerprint;
+  if (NS_FAILED(c.GetSha1Fingerprint(fingerprint)))
+    return PR_FALSE;
+
+  if (fingerprint != info_sha1)
+    return PR_FALSE;
+
+  return (policyOIDTag == info.oid_tag);
+}
+
+#ifdef PSM_ENABLE_TEST_EV_ROOTS
+static const char kTestEVRootsFileName[] = "test_ev_roots.txt";
+
+static void
+loadTestEVInfos()
+{
+  if (!testEVInfos)
+    return;
+
+  testEVInfos->Clear();
+
+  char *env_val = getenv("ENABLE_TEST_EV_ROOTS_FILE");
+  if (!env_val)
+    return;
+    
+  int enabled_val = atoi(env_val);
+  if (!enabled_val)
+    return;
+
+  nsCOMPtr<nsIFile> aFile;
+  NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR, getter_AddRefs(aFile));
+  if (!aFile)
+    return;
+
+  aFile->AppendNative(NS_LITERAL_CSTRING(kTestEVRootsFileName));
+
+  nsresult rv;
+  nsCOMPtr<nsIInputStream> fileInputStream;
+  rv = NS_NewLocalFileInputStream(getter_AddRefs(fileInputStream), aFile);
+  if (NS_FAILED(rv))
+    return;
+
+  nsCOMPtr<nsILineInputStream> lineInputStream = do_QueryInterface(fileInputStream, &rv);
+  if (NS_FAILED(rv))
+    return;
+
+  nsCAutoString buffer;
+  PRBool isMore = PR_TRUE;
+
+  /* file format
+   *
+   * file format must be strictly followed
+   * strings in file must be UTF-8
+   * each record consists of multiple lines
+   * each line consists of a descriptor, a single space, and the data
+   * the descriptors are:
+   *   1_subject
+   *   2_issuer
+   *   3_fingerprint (in format XX:XX:XX:...)
+   *   4_readable_oid (treated as a comment)
+   * the input file must strictly follow this order
+   * the input file may contain 0, 1 or many records
+   * completely empty lines are ignored
+   * lines that start with the # char are ignored
+   */
+
+  int line_counter = 0;
+  PRBool found_error = PR_FALSE;
+
+  enum { 
+    pos_subject, pos_issuer, pos_fingerprint, pos_readable_oid
+  } reader_position = pos_subject;
+
+  nsCString subject, issuer, fingerprint, readable_oid;
+
+  while (isMore && NS_SUCCEEDED(lineInputStream->ReadLine(buffer, &isMore))) {
+    ++line_counter;
+    if (buffer.IsEmpty() || buffer.First() == '#') {
+      continue;
+    }
+
+    PRInt32 seperatorIndex = buffer.FindChar(' ', 0);
+    if (seperatorIndex == 0) {
+      found_error = PR_TRUE;
+      break;
+    }
+
+    const nsASingleFragmentCString &descriptor = Substring(buffer, 0, seperatorIndex);
+    const nsASingleFragmentCString &data = 
+            Substring(buffer, seperatorIndex + 1, 
+                      buffer.Length() - seperatorIndex + 1);
+
+    if (reader_position == pos_subject &&
+        descriptor.EqualsLiteral(("1_subject"))) {
+      subject = data;
+      reader_position = pos_issuer;
+      continue;
+    }
+    else if (reader_position == pos_issuer &&
+        descriptor.EqualsLiteral(("2_issuer"))) {
+      issuer = data;
+      reader_position = pos_fingerprint;
+      continue;
+    }
+    else if (reader_position == pos_fingerprint &&
+        descriptor.EqualsLiteral(("3_fingerprint"))) {
+      fingerprint = data;
+      reader_position = pos_readable_oid;
+      continue;
+    }
+    else if (reader_position == pos_readable_oid &&
+        descriptor.EqualsLiteral(("4_readable_oid"))) {
+      readable_oid = data;
+      reader_position = pos_subject;
+    }
+    else {
+      found_error = PR_TRUE;
+      break;
+    }
+
+    nsMyTrustedEVInfoClass *temp_ev = new nsMyTrustedEVInfoClass;
+    if (!temp_ev)
+      return;
+
+    temp_ev->ev_root_subject = strdup(subject.get());
+    temp_ev->ev_root_issuer = strdup(issuer.get());
+    temp_ev->ev_root_sha1_fingerprint = strdup(fingerprint.get());
+    temp_ev->oid_name = strdup(readable_oid.get());
+    temp_ev->dotted_oid = strdup(readable_oid.get());
+
+    SECItem ev_oid_item;
+    ev_oid_item.data = nsnull;
+    ev_oid_item.len = 0;
+    SECStatus srv = SEC_StringToOID(nsnull, &ev_oid_item,
+                                    readable_oid.get(), readable_oid.Length());
+    if (srv != SECSuccess) {
+      delete temp_ev;
+      found_error = PR_TRUE;
+      break;
+    }
+
+    temp_ev->oid_tag = register_oid(&ev_oid_item, temp_ev->oid_name);
+    SECITEM_FreeItem(&ev_oid_item, PR_FALSE);
+
+    testEVInfos->AppendElement(temp_ev);
+  }
+
+  if (found_error) {
+    fprintf(stderr, "invalid line %d in test_ev_roots file\n", line_counter);
+  }
+}
+
+static PRBool 
+isEVPolicyInExternalDebugRootsFile(SECOidTag policyOIDTag)
+{
+  if (!testEVInfos)
+    return PR_FALSE;
+
+  char *env_val = getenv("ENABLE_TEST_EV_ROOTS_FILE");
+  if (!env_val)
+    return PR_FALSE;
+    
+  int enabled_val = atoi(env_val);
+  if (!enabled_val)
+    return PR_FALSE;
+
+  for (size_t i=0; i<testEVInfos->Length(); ++i) {
+    nsMyTrustedEVInfoClass *ev = testEVInfos->ElementAt(i);
+    if (!ev)
+      continue;
+    if (policyOIDTag == ev->oid_tag)
+      return PR_TRUE;
+  }
+
+  return PR_FALSE;
+}
+
+static PRBool 
+isEVMatchInExternalDebugRootsFile(SECOidTag policyOIDTag, 
+                                  CERTCertificate *rootCert)
+{
+  if (!testEVInfos)
+    return PR_FALSE;
+
+  if (!rootCert)
+    return PR_FALSE;
+  
+  char *env_val = getenv("ENABLE_TEST_EV_ROOTS_FILE");
+  if (!env_val)
+    return PR_FALSE;
+    
+  int enabled_val = atoi(env_val);
+  if (!enabled_val)
+    return PR_FALSE;
+
+  for (size_t i=0; i<testEVInfos->Length(); ++i) {
+    nsMyTrustedEVInfoClass *ev = testEVInfos->ElementAt(i);
+    if (!ev)
+      continue;
+    if (isEVMatch(policyOIDTag, rootCert, *ev))
+      return PR_TRUE;
+  }
+
+  return PR_FALSE;
+}
+#endif
+
+static PRBool 
+isEVPolicy(SECOidTag policyOIDTag)
+{
+  for (size_t iEV=0; iEV < (sizeof(myTrustedEVInfos)/sizeof(nsMyTrustedEVInfo)); ++iEV) {
+    nsMyTrustedEVInfo &entry = myTrustedEVInfos[iEV];
+    if (!entry.oid_name) // invalid or placeholder list entry
+      continue;
+    if (policyOIDTag == entry.oid_tag) {
+      return PR_TRUE;
+    }
+  }
+
+#ifdef PSM_ENABLE_TEST_EV_ROOTS
+  if (isEVPolicyInExternalDebugRootsFile(policyOIDTag)) {
+    return PR_TRUE;
+  }
+#endif
+
+  return PR_FALSE;
+}
+
+static PRBool 
+isApprovedForEV(SECOidTag policyOIDTag, CERTCertificate *rootCert)
+{
+  if (!rootCert)
+    return PR_FALSE;
+
+  for (size_t iEV=0; iEV < (sizeof(myTrustedEVInfos)/sizeof(nsMyTrustedEVInfo)); ++iEV) {
+    nsMyTrustedEVInfo &entry = myTrustedEVInfos[iEV];
+    if (!entry.oid_name) // invalid or placeholder list entry
+      continue;
+    if (isEVMatch(policyOIDTag, rootCert, entry)) {
+      return PR_TRUE;
+    }
+  }
+
+#ifdef PSM_ENABLE_TEST_EV_ROOTS
+  if (isEVMatchInExternalDebugRootsFile(policyOIDTag, rootCert)) {
+    return PR_TRUE;
+  }
+#endif
+
+  return PR_FALSE;
+}
+
+PRStatus PR_CALLBACK
+nsNSSComponent::IdentityInfoInit()
+{
+  for (size_t iEV=0; iEV < (sizeof(myTrustedEVInfos)/sizeof(nsMyTrustedEVInfo)); ++iEV) {
+    nsMyTrustedEVInfo &entry = myTrustedEVInfos[iEV];
+    if (!entry.oid_name) // invalid or placeholder list entry
+      continue;
+
+    SECItem ev_oid_item;
+    ev_oid_item.data = nsnull;
+    ev_oid_item.len = 0;
+    SECStatus srv = SEC_StringToOID(nsnull, &ev_oid_item, 
+                                    entry.dotted_oid, 0);
+    if (srv != SECSuccess)
+      continue;
+
+    entry.oid_tag = register_oid(&ev_oid_item, entry.oid_name);
+
+    SECITEM_FreeItem(&ev_oid_item, PR_FALSE);
+  }
+
+#ifdef PSM_ENABLE_TEST_EV_ROOTS
+  if (!testEVInfosLoaded) {
+    testEVInfosLoaded = PR_TRUE;
+    testEVInfos = new testEVArray;
+    if (testEVInfos) {
+      loadTestEVInfos();
+    }
+  }
+#endif
+
+  return PR_SUCCESS;
+}
+
+// Find the first policy OID that is known to be an EV policy OID.
+static SECStatus getFirstEVPolicy(CERTCertificate *cert, SECOidTag &outOidTag)
+{
+  if (!cert)
+    return SECFailure;
+
+  if (cert->extensions) {
+    for (int i=0; cert->extensions[i] != nsnull; i++) {
+      const SECItem *oid = &cert->extensions[i]->id;
+
+      SECOidTag oidTag = SECOID_FindOIDTag(oid);
+      if (oidTag != SEC_OID_X509_CERTIFICATE_POLICIES)
+        continue;
+
+      SECItem *value = &cert->extensions[i]->value;
+
+      CERTCertificatePolicies *policies;
+      CERTPolicyInfo **policyInfos, *policyInfo;
+    
+      policies = CERT_DecodeCertificatePoliciesExtension(value);
+      if (!policies)
+        continue;
+    
+      policyInfos = policies->policyInfos;
+
+      while (*policyInfos != NULL) {
+        policyInfo = *policyInfos++;
+
+        SECOidTag oid_tag = SECOID_FindOIDTag(&policyInfo->policyID);
+        if (oid_tag == SEC_OID_UNKNOWN) // not in our list of OIDs accepted for EV
+          continue;
+
+        if (!isEVPolicy(oid_tag))
+          continue;
+
+        outOidTag = oid_tag;
+        return SECSuccess;
+      }
+    }
+  }
+
+  return SECFailure;
+}
 
 NS_IMETHODIMP
 nsNSSSocketInfo::GetIsExtendedValidation(PRBool* aIsEV)
 {
   NS_ENSURE_ARG(aIsEV);
   *aIsEV = PR_FALSE;
 
+  if (!mCert)
+    return NS_OK;
+
+  return mCert->GetIsExtendedValidation(aIsEV);
+}
+
+NS_IMETHODIMP
+nsNSSSocketInfo::GetValidEVPolicyOid(nsACString &outDottedOid)
+{
+  if (!mCert)
+    return NS_OK;
+
+  return mCert->GetValidEVPolicyOid(outDottedOid);
+}
+
+nsresult
+nsNSSCertificate::hasValidEVOidTag(SECOidTag &resultOidTag, PRBool &validEV)
+{
+  nsNSSShutDownPreventionLock locker;
+  if (isAlreadyShutDown())
+    return NS_ERROR_NOT_AVAILABLE;
+
+  nsresult nrv;
+  nsCOMPtr<nsINSSComponent> nssComponent = 
+    do_GetService(PSM_COMPONENT_CONTRACTID, &nrv);
+  if (NS_FAILED(nrv))
+    return nrv;
+  nssComponent->EnsureIdentityInfoLoaded();
+
+  validEV = PR_FALSE;
+  resultOidTag = SEC_OID_UNKNOWN;
+
+  SECOidTag oid_tag;
+  SECStatus rv = getFirstEVPolicy(mCert, oid_tag);
+  if (rv != SECSuccess)
+    return NS_OK;
+
+  if (oid_tag == SEC_OID_UNKNOWN) // not in our list of OIDs accepted for EV
+    return NS_OK;
+
+  CERTValInParam cvin[3];
+  cvin[0].type = cert_pi_policyOID;
+  cvin[0].value.arraySize = 1; 
+  cvin[0].value.array.oids = &oid_tag;
+  cvin[1].type = cert_pi_revocationFlags;
+  cvin[1].value.scalar.ul = CERT_REV_FLAG_OCSP
+                            | CERT_REV_FLAG_CRL;
+  cvin[2].type = cert_pi_end;
+
+  CERTValOutParam cvout[2];
+  cvout[0].type = cert_po_trustAnchor;
+  cvout[1].type = cert_po_end;
+
+  rv = CERT_PKIXVerifyCert(mCert, certificateUsageSSLServer,
+                           cvin, cvout, nsnull);
+  if (rv != SECSuccess)
+    return NS_OK;
+
+  CERTCertificate *issuerCert = cvout[0].value.pointer.cert;
+  CERTCertificateCleaner issuerCleaner(issuerCert);
+
+  validEV = isApprovedForEV(oid_tag, issuerCert);
+  if (validEV)
+    resultOidTag = oid_tag;
+ 
   return NS_OK;
 }
 
+NS_IMETHODIMP
+nsNSSCertificate::GetIsExtendedValidation(PRBool* aIsEV)
+{
+  nsNSSShutDownPreventionLock locker;
+  if (isAlreadyShutDown())
+    return NS_ERROR_NOT_AVAILABLE;
+
+  NS_ENSURE_ARG(aIsEV);
+  *aIsEV = PR_FALSE;
+
+  SECOidTag oid_tag;
+  return hasValidEVOidTag(oid_tag, *aIsEV);
+}
+
+NS_IMETHODIMP
+nsNSSCertificate::GetValidEVPolicyOid(nsACString &outDottedOid)
+{
+  nsNSSShutDownPreventionLock locker;
+  if (isAlreadyShutDown())
+    return NS_ERROR_NOT_AVAILABLE;
+
+  SECOidTag oid_tag;
+  PRBool valid;
+  nsresult rv = hasValidEVOidTag(oid_tag, valid);
+  if (NS_FAILED(rv))
+    return rv;
+
+  if (valid) {
+    SECOidData *oid_data = SECOID_FindOIDByTag(oid_tag);
+    if (!oid_data)
+      return NS_ERROR_FAILURE;
+
+    char *oid_str = CERT_GetOidString(&oid_data->oid);
+    if (!oid_str)
+      return NS_ERROR_FAILURE;
+
+    outDottedOid = oid_str;
+    PR_smprintf_free(oid_str);
+  }
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsNSSComponent::EnsureIdentityInfoLoaded()
+{
+  PRStatus rv = PR_CallOnce(&mIdentityInfoCallOnce, IdentityInfoInit);
+  return (rv == PR_SUCCESS) ? NS_OK : NS_ERROR_FAILURE; 
+}
+
+// only called during shutdown
+void
+nsNSSComponent::CleanupIdentityInfo()
+{
+#ifdef PSM_ENABLE_TEST_EV_ROOTS
+  if (testEVInfosLoaded) {
+    testEVInfosLoaded = PR_FALSE;
+    if (testEVInfos) {
+      for (size_t i; i<testEVInfos->Length(); ++i) {
+        delete testEVInfos->ElementAt(i);
+      }
+      testEVInfos->Clear();
+      delete testEVInfos;
+      testEVInfos = nsnull;
+    }
+  }
+#endif
+  memset(&mIdentityInfoCallOnce, 0, sizeof(PRCallOnceType));
+}
--- a/security/manager/ssl/src/nsNSSCertHelper.cpp
+++ b/security/manager/ssl/src/nsNSSCertHelper.cpp
@@ -1323,16 +1323,17 @@ ProcessUserNotice(SECItem *der_notice,
     CERT_DestroyUserNotice(notice);
   PORT_FreeArena(arena, PR_FALSE);
   return rv;
 }
 
 static nsresult
 ProcessCertificatePolicies(SECItem  *extData, 
 			   nsAString &text,
+                           SECOidTag ev_oid_tag, // SEC_OID_UNKNOWN means: not EV
 			   nsINSSComponent *nssComponent)
 {
   CERTCertificatePolicies *policies;
   CERTPolicyInfo **policyInfos, *policyInfo;
   CERTPolicyQualifier **policyQualifiers, *policyQualifier;
   nsAutoString local;
   nsresult rv = NS_OK;
 
@@ -1347,20 +1348,38 @@ ProcessCertificatePolicies(SECItem  *ext
     case SEC_OID_VERISIGN_USER_NOTICES:
       nssComponent->GetPIPNSSBundleString("CertDumpVerisignNotices", local);
       text.Append(local);
       break;
     default:
       GetDefaultOIDFormat(&policyInfo->policyID, local, '.');
       text.Append(local);
     }
+
+    PRBool needColon = PR_TRUE;
+    if (ev_oid_tag != SEC_OID_UNKNOWN) {
+      // This is an EV cert. Let's see if this oid is the EV oid,
+      // because we want to display the EV information string
+      // next to the correct OID.
+
+      SECOidTag oid_tag = SECOID_FindOIDTag(&policyInfo->policyID);
+      if (oid_tag == ev_oid_tag) {
+        text.Append(NS_LITERAL_STRING(":"));
+        text.Append(NS_LITERAL_STRING(SEPARATOR));
+        needColon = PR_FALSE;
+        nssComponent->GetPIPNSSBundleString("CertDumpPolicyOidEV", local);
+        text.Append(local);
+      }
+    }
+
     if (policyInfo->policyQualifiers) {
       /* Add all qualifiers on separate lines, indented */
       policyQualifiers = policyInfo->policyQualifiers;
-      text.Append(NS_LITERAL_STRING(":"));
+      if (needColon)
+        text.Append(NS_LITERAL_STRING(":"));
       text.Append(NS_LITERAL_STRING(SEPARATOR));
       while (*policyQualifiers != NULL) {
 	text.Append(NS_LITERAL_STRING("  "));
 	policyQualifier = *policyQualifiers++;
 	switch(policyQualifier->oid) {
 	case SEC_OID_PKIX_CPS_POINTER_QUALIFIER:
 	  nssComponent->GetPIPNSSBundleString("CertDumpCPSPointer", local);
 	  text.Append(local);
@@ -1563,17 +1582,19 @@ ProcessMSCAVersion(SECItem  *extData,
   /* Apparently, the encoding is <minor><major>, with 16 bits each */
   PR_snprintf(buf, sizeof(buf), "%d.%d", version & 0xFFFF, version>>16);
   text.AppendASCII(buf);
   return NS_OK;
 }
 
 static nsresult
 ProcessExtensionData(SECOidTag oidTag, SECItem *extData, 
-                     nsAString &text, nsINSSComponent *nssComponent)
+                     nsAString &text, 
+                     SECOidTag ev_oid_tag, // SEC_OID_UNKNOWN means: not EV
+                     nsINSSComponent *nssComponent)
 {
   nsresult rv;
   switch (oidTag) {
   case SEC_OID_NS_CERT_EXT_CERT_TYPE:
     rv = ProcessNSCertTypeExtensions(extData, text, nssComponent);
     break;
   case SEC_OID_X509_KEY_USAGE:
     rv = ProcessKeyUsageExtension(extData, text, nssComponent);
@@ -1590,17 +1611,17 @@ ProcessExtensionData(SECOidTag oidTag, S
     break;
   case SEC_OID_X509_SUBJECT_KEY_ID:
     rv = ProcessSubjectKeyId(extData, text, nssComponent);
     break;
   case SEC_OID_X509_AUTH_KEY_ID:
     rv = ProcessAuthKeyId(extData, text, nssComponent);
     break;
   case SEC_OID_X509_CERTIFICATE_POLICIES:
-    rv = ProcessCertificatePolicies(extData, text, nssComponent);
+    rv = ProcessCertificatePolicies(extData, text, ev_oid_tag, nssComponent);
     break;
   case SEC_OID_X509_CRL_DIST_POINTS:
     rv = ProcessCrlDistPoints(extData, text, nssComponent);
     break;
   case SEC_OID_X509_AUTH_INFO_ACCESS:
     rv = ProcessAuthInfoAccess(extData, text, nssComponent);
     break;
   case SEC_OID_NS_CERT_EXT_BASE_URL:
@@ -1627,16 +1648,17 @@ ProcessExtensionData(SECOidTag oidTag, S
     rv = ProcessRawBytes(nssComponent, extData, text);
     break; 
   }
   return rv;
 }
 
 static nsresult
 ProcessSingleExtension(CERTCertExtension *extension,
+                       SECOidTag ev_oid_tag, // SEC_OID_UNKNOWN means: not EV
                        nsINSSComponent *nssComponent,
                        nsIASN1PrintableItem **retExtension)
 {
   nsAutoString text, extvalue;
   GetOIDText(&extension->id, nssComponent, text);
   nsCOMPtr<nsIASN1PrintableItem>extensionItem = new nsNSSASN1PrintableItem();
   if (extensionItem == nsnull)
     return NS_ERROR_OUT_OF_MEMORY;
@@ -1650,17 +1672,17 @@ ProcessSingleExtension(CERTCertExtension
     } else {
       nssComponent->GetPIPNSSBundleString("CertDumpNonCritical", text);
     }
   } else {
     nssComponent->GetPIPNSSBundleString("CertDumpNonCritical", text);
   }
   text.Append(NS_LITERAL_STRING(SEPARATOR).get());
   nsresult rv = ProcessExtensionData(oidTag, &extension->value, extvalue, 
-                                     nssComponent);
+                                     ev_oid_tag, nssComponent);
   if (NS_FAILED(rv)) {
     extvalue.Truncate();
     rv = ProcessRawBytes(nssComponent, &extension->value, extvalue, PR_FALSE);
   }
   text.Append(extvalue);
 
   extensionItem->SetDisplayValue(text);
   *retExtension = extensionItem;
@@ -1846,33 +1868,36 @@ ProcessSubjectPublicKeyInfo(CERTSubjectP
   
   parentSequence->GetASN1Objects(getter_AddRefs(asn1Objects));
   asn1Objects->AppendElement(spkiSequence, PR_FALSE);
   return NS_OK;
 }
 
 static nsresult
 ProcessExtensions(CERTCertExtension **extensions, 
-                  nsIASN1Sequence *parentSequence, 
+                  nsIASN1Sequence *parentSequence,
+                  SECOidTag ev_oid_tag, // SEC_OID_UNKNOWN means: not EV
                   nsINSSComponent *nssComponent)
 {
   nsCOMPtr<nsIASN1Sequence> extensionSequence = new nsNSSASN1Sequence;
   if (extensionSequence == nsnull)
     return NS_ERROR_OUT_OF_MEMORY;
 
   nsString text;
   nssComponent->GetPIPNSSBundleString("CertDumpExtensions", text);
   extensionSequence->SetDisplayName(text);
   PRInt32 i;
   nsresult rv;
   nsCOMPtr<nsIASN1PrintableItem> newExtension;
   nsCOMPtr<nsIMutableArray> asn1Objects;
   extensionSequence->GetASN1Objects(getter_AddRefs(asn1Objects));
   for (i=0; extensions[i] != nsnull; i++) {
-    rv = ProcessSingleExtension(extensions[i], nssComponent,
+    rv = ProcessSingleExtension(extensions[i], 
+                                ev_oid_tag,
+                                nssComponent,
                                 getter_AddRefs(newExtension));
     if (NS_FAILED(rv))
       return rv;
 
     asn1Objects->AppendElement(newExtension, PR_FALSE);
   }
   parentSequence->GetASN1Objects(getter_AddRefs(asn1Objects));
   asn1Objects->AppendElement(extensionSequence, PR_FALSE);
@@ -2052,17 +2077,26 @@ nsNSSCertificate::CreateTBSCertificateAS
 
     printableItem->SetDisplayValue(text);
     nssComponent->GetPIPNSSBundleString("CertDumpSubjectUniqueID", text);
     printableItem->SetDisplayName(text);
     asn1Objects->AppendElement(printableItem, PR_FALSE);
 
   }
   if (mCert->extensions) {
-    rv = ProcessExtensions(mCert->extensions, sequence, nssComponent);
+    SECOidTag ev_oid_tag;
+    PRBool validEV;
+    rv = hasValidEVOidTag(ev_oid_tag, validEV);
+    if (NS_FAILED(rv))
+      return rv;
+
+    if (!validEV)
+      ev_oid_tag = SEC_OID_UNKNOWN;
+
+    rv = ProcessExtensions(mCert->extensions, sequence, ev_oid_tag, nssComponent);
     if (NS_FAILED(rv))
       return rv;
   }
   *retSequence = sequence;
   NS_ADDREF(*retSequence);  
   return NS_OK;
 }
 
--- a/security/manager/ssl/src/nsNSSCertificate.cpp
+++ b/security/manager/ssl/src/nsNSSCertificate.cpp
@@ -105,19 +105,20 @@ NSSCleanupAutoPtrClass(NSSCMSSignedData,
 // This is being stored in an PRUint32 that can otherwise
 // only take values from nsIX509Cert's list of cert types.
 // As nsIX509Cert is frozen, we choose a value not contained
 // in the list to mean not yet initialized.
 #define CERT_TYPE_NOT_YET_INITIALIZED (1 << 30)
 
 /* nsNSSCertificate */
 
-NS_IMPL_THREADSAFE_ISUPPORTS4(nsNSSCertificate, nsIX509Cert,
+NS_IMPL_THREADSAFE_ISUPPORTS5(nsNSSCertificate, nsIX509Cert,
                                                 nsIX509Cert2,
                                                 nsIX509Cert3,
+                                                nsIIdentityInfo,
                                                 nsISMimeCert)
 
 nsNSSCertificate*
 nsNSSCertificate::ConstructFromDER(char *certDER, int derLen)
 {
   nsNSSCertificate* newObject = new nsNSSCertificate();
   if (!newObject->InitFromDER(certDER, derLen)) {
     delete newObject;
--- a/security/manager/ssl/src/nsNSSCertificate.h
+++ b/security/manager/ssl/src/nsNSSCertificate.h
@@ -43,38 +43,41 @@
 
 #include "nsIX509Cert.h"
 #include "nsIX509Cert2.h"
 #include "nsIX509Cert3.h"
 #include "nsIX509CertDB.h"
 #include "nsIX509CertList.h"
 #include "nsIASN1Object.h"
 #include "nsISMimeCert.h"
+#include "nsIIdentityInfo.h"
 #include "nsNSSShutDown.h"
 #include "nsISimpleEnumerator.h"
 #include "nsISerializable.h"
 #include "nsIClassInfo.h"
 
 #include "nsNSSCertHeader.h"
 
 class nsINSSComponent;
 class nsIASN1Sequence;
 
 /* Certificate */
 class nsNSSCertificate : public nsIX509Cert3,
+                         public nsIIdentityInfo,
                          public nsISMimeCert,
                          public nsISerializable,
                          public nsIClassInfo,
                          public nsNSSShutDownObject
 {
 public:
   NS_DECL_ISUPPORTS
   NS_DECL_NSIX509CERT
   NS_DECL_NSIX509CERT2
   NS_DECL_NSIX509CERT3
+  NS_DECL_NSIIDENTITYINFO
   NS_DECL_NSISMIMECERT
   NS_DECL_NSISERIALIZABLE
   NS_DECL_NSICLASSINFO
 
   nsNSSCertificate(CERTCertificate *cert);
   nsNSSCertificate();
   /* from a request? */
   virtual ~nsNSSCertificate();
@@ -90,16 +93,17 @@ private:
   nsCOMPtr<nsIASN1Object> mASN1Structure;
   nsresult CreateASN1Struct();
   nsresult CreateTBSCertificateASN1Struct(nsIASN1Sequence **retSequence,
                                           nsINSSComponent *nssComponent);
   nsresult GetSortableDate(PRTime aTime, nsAString &_aSortableDate);
   virtual void virtualDestroyNSSReference();
   void destructorSafeDestroyNSSReference();
   PRBool InitFromDER(char* certDER, int derLen);  // return false on failure
+  nsresult hasValidEVOidTag(SECOidTag &resultOidTag, PRBool &validEV);
 };
 
 class nsNSSCertList: public nsIX509CertList
 {
 public:
   NS_DECL_ISUPPORTS
   NS_DECL_NSIX509CERTLIST
 
--- a/security/manager/ssl/src/nsNSSComponent.cpp
+++ b/security/manager/ssl/src/nsNSSComponent.cpp
@@ -286,16 +286,20 @@ nsNSSComponent::nsNSSComponent()
   PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsNSSComponent::ctor\n"));
   mUpdateTimerInitialized = PR_FALSE;
   crlDownloadTimerOn = PR_FALSE;
   crlsScheduledForDownload = nsnull;
   mTimer = nsnull;
   mCrlTimerLock = nsnull;
   mObserversRegistered = PR_FALSE;
 
+  // In order to keep startup time lower, we delay loading and 
+  // registering all identity data until first needed.
+  memset(&mIdentityInfoCallOnce, 0, sizeof(PRCallOnceType));
+
   nsSSLIOLayerHelpers::Init();
   
   NS_ASSERTION( (0 == mInstanceCount), "nsNSSComponent is a singleton, but instantiated multiple times!");
   ++mInstanceCount;
   hashTableCerts = nsnull;
   mShutdownObjectList = nsNSSShutDownList::construct();
   mIsNetworkDown = PR_FALSE;
   mSSLThread = new nsSSLThread();
@@ -1673,16 +1677,18 @@ nsNSSComponent::ShutdownNSS()
     mShutdownObjectList->evaporateAllNSSResources();
     if (SECSuccess != ::NSS_Shutdown()) {
       PR_LOG(gPIPNSSLog, PR_LOG_ALWAYS, ("NSS SHUTDOWN FAILURE\n"));
       rv = NS_ERROR_FAILURE;
     }
     else {
       PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("NSS shutdown =====>> OK <<=====\n"));
     }
+
+    CleanupIdentityInfo();
   }
 
   return rv;
 }
  
 NS_IMETHODIMP
 nsNSSComponent::Init()
 {
--- a/security/manager/ssl/src/nsNSSComponent.h
+++ b/security/manager/ssl/src/nsNSSComponent.h
@@ -162,16 +162,17 @@ class NS_NO_VTABLE nsINSSComponent : pub
   NS_IMETHOD LaunchSmartCardThread(SECMODModule *module) = 0;
 
   NS_IMETHOD ShutdownSmartCardThread(SECMODModule *module) = 0;
 
   NS_IMETHOD PostEvent(const nsAString &eventType, const nsAString &token) = 0;
 
   NS_IMETHOD DispatchEvent(const nsAString &eventType, const nsAString &token) = 0;
   
+  NS_IMETHOD EnsureIdentityInfoLoaded() = 0;
 };
 
 NS_DEFINE_STATIC_IID_ACCESSOR(nsINSSComponent, NS_INSSCOMPONENT_IID)
 
 class nsCryptoHash : public nsICryptoHash
 {
 public:
   NS_DECL_ISUPPORTS
@@ -235,16 +236,17 @@ public:
   NS_IMETHOD DownloadCRLDirectly(nsAutoString, nsAutoString);
   NS_IMETHOD RememberCert(CERTCertificate *cert);
   static nsresult GetNSSCipherIDFromPrefString(const nsACString &aPrefString, PRUint16 &aCipherId);
 
   NS_IMETHOD LaunchSmartCardThread(SECMODModule *module);
   NS_IMETHOD ShutdownSmartCardThread(SECMODModule *module);
   NS_IMETHOD PostEvent(const nsAString &eventType, const nsAString &token);
   NS_IMETHOD DispatchEvent(const nsAString &eventType, const nsAString &token);
+  NS_IMETHOD EnsureIdentityInfoLoaded();
 
 private:
 
   nsresult InitializeNSS(PRBool showWarningBox);
   nsresult ShutdownNSS();
 
 #ifdef XP_MACOSX
   void TryCFM2MachOMigration(nsIFile *cfmPath, nsIFile *machoPath);
@@ -257,16 +259,17 @@ private:
     ai_incomplete_logout
   };
   
   void ShowAlert(AlertIdentifier ai);
   void InstallLoadableRoots();
   void UnloadLoadableRoots();
   void LaunchSmartCardThreads();
   void ShutdownSmartCardThreads();
+  void CleanupIdentityInfo();
   nsresult InitializePIPNSSBundle();
   nsresult ConfigureInternalPKCS11Token();
   nsresult RegisterPSMContentListener();
   nsresult RegisterObservers();
   nsresult DownloadCrlSilently();
   nsresult PostCRLImportEvent(const nsCSubstring &urlString, nsIStreamListener *psmDownloader);
   nsresult getParamsForNextCrlToDownload(nsAutoString *url, PRTime *time, nsAutoString *key);
   nsresult DispatchEventToWindow(nsIDOMWindow *domWin, const nsAString &eventType, const nsAString &token);
@@ -298,16 +301,19 @@ private:
   PRBool mUpdateTimerInitialized;
   static int mInstanceCount;
   nsNSSShutDownList *mShutdownObjectList;
   SmartCardThreadList *mThreadList;
   PRBool mIsNetworkDown;
   nsSSLThread *mSSLThread;
   nsCertVerificationThread *mCertVerificationThread;
   nsNSSHttpInterface mHttpForNSS;
+
+  static PRStatus PR_CALLBACK IdentityInfoInit(void);
+  PRCallOnceType mIdentityInfoCallOnce;
 };
 
 class PSMContentListener : public nsIURIContentListener,
                             public nsSupportsWeakReference {
 public:
   PSMContentListener();
   virtual ~PSMContentListener();
   nsresult init();