Bug 1338897 - Avoid using NSS Base64 functions in PSM. r=keeler draft
authorCykesiopka <cykesiopka.bmo@gmail.com>
Fri, 17 Mar 2017 23:31:40 +0800
changeset 500726 4863b2e5eabef0555e8e1ebe39216d0d9393f3e9
parent 500705 156ee3ddc81024e455385e9ba0b3bcbb2b9c89a6
child 549688 fd45e8a152f44d016bad963d79c38e62580ea03a
push id49778
push usercykesiopka.bmo@gmail.com
push dateFri, 17 Mar 2017 15:32:15 +0000
reviewerskeeler
bugs1338897
milestone55.0a1
Bug 1338897 - Avoid using NSS Base64 functions in PSM. r=keeler The NSS Base64 functions are less safe and convenient to use than the XPCOM ones. They're also an unnecessary dependency on NSS. The NSS Base64 functions behave slightly differently than the XPCOM ones: 1. ATOB_ConvertAsciiToItem() / NSSBase64_DecodeBuffer() silently ignore invalid characters like CRLF, space and so on. Base64Decode() will return an error if these characters are encountered. 2. BTOA_DataToAscii() will produce output that has CRLF inserted every 64 characters. Base64Encode() doesn't do this. For the reasons listed below, no unexpected compatibility issues should arise: 1. AppSignatureVerification.cpp already filters out CRLF and spaces for Manifest and Signature values before decoding. 2. ExtendedValidation.cpp is only given what should be valid hard-coded input to decode. 3. ContentSignatureVerifier.cpp already splits on CRLF for when it needs to decode PEM certs. Spaces shouldn't be likely. For Content-Signature header verification, examination of real input to a running instance of Firefox suggests CRLF and spaces will not be present in the header to decode. 4. nsCryptoHash.cpp encode is affected, but we actually don't want the CRLF behaviour. 5. nsDataSignatureVerifier.cpp decode is affected, but we add whitespace stripping to maintain backwards compatibility. 6. nsKeygenHandler.cpp encode is affected, but the previous CRLF behaviour was arguably a bug, since neither WHATWG or W3C specs specified this. MozReview-Commit-ID: IWMFxqVZMeX
security/apps/AppSignatureVerification.cpp
security/certverifier/ExtendedValidation.cpp
security/manager/ssl/ContentSignatureVerifier.cpp
security/manager/ssl/PublicKeyPinningService.cpp
security/manager/ssl/nsCryptoHash.cpp
security/manager/ssl/nsDataSignatureVerifier.cpp
security/manager/ssl/nsKeygenHandler.cpp
security/manager/ssl/nsKeygenHandler.h
security/manager/ssl/nsSiteSecurityService.cpp
security/manager/ssl/tests/unit/test_datasignatureverifier.js
security/manager/ssl/tests/unit/test_hash_algorithms.js
security/manager/ssl/tests/unit/test_hmac.js
security/manager/ssl/tests/unit/tlsserver/cmd/GenerateOCSPResponse.cpp
security/nss.symbols
--- a/security/apps/AppSignatureVerification.cpp
+++ b/security/apps/AppSignatureVerification.cpp
@@ -5,52 +5,61 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsNSSCertificateDB.h"
 
 #include "AppTrustDomain.h"
 #include "CryptoTask.h"
 #include "NSSCertDBTrustDomain.h"
 #include "ScopedNSSTypes.h"
-#include "base64.h"
 #include "certdb.h"
+#include "mozilla/Base64.h"
 #include "mozilla/Casting.h"
 #include "mozilla/Logging.h"
 #include "mozilla/RefPtr.h"
 #include "mozilla/UniquePtr.h"
 #include "nsCOMPtr.h"
 #include "nsComponentManagerUtils.h"
 #include "nsDataSignatureVerifier.h"
+#include "nsDependentString.h"
 #include "nsHashKeys.h"
 #include "nsIDirectoryEnumerator.h"
 #include "nsIFile.h"
 #include "nsIFileStreams.h"
 #include "nsIInputStream.h"
 #include "nsIStringEnumerator.h"
 #include "nsIZipReader.h"
 #include "nsNSSCertificate.h"
 #include "nsNetUtil.h"
 #include "nsProxyRelease.h"
 #include "nsString.h"
 #include "nsTHashtable.h"
-#include "nssb64.h"
 #include "pkix/pkix.h"
 #include "pkix/pkixnss.h"
 #include "plstr.h"
 #include "secmime.h"
 
 
 using namespace mozilla::pkix;
 using namespace mozilla;
 using namespace mozilla::psm;
 
 extern mozilla::LazyLogModule gPIPNSSLog;
 
 namespace {
 
+// The digest must have a lifetime greater than or equal to the returned string.
+inline nsDependentCSubstring
+DigestToDependentString(const Digest& digest)
+{
+  return nsDependentCSubstring(
+    BitwiseCast<char*, unsigned char*>(digest.get().data),
+    digest.get().len);
+}
+
 // Reads a maximum of 1MB from a stream into the supplied buffer.
 // The reason for the 1MB limit is because this function is used to read
 // signature-related files and we want to avoid OOM. The uncompressed length of
 // an entry can be hundreds of times larger than the compressed version,
 // especially if someone has specifically crafted the entry to cause OOM or to
 // consume massive amounts of disk space.
 //
 // @param stream  The input stream to read from.
@@ -159,21 +168,22 @@ FindAndLoadOneEntry(nsIZipReader * zip,
 //                it is from a signed archive or unpacked into a directory
 // @param digestFromManifest The digest that we're supposed to check the file's
 //                           contents against, from the manifest
 // @param buf A scratch buffer that we use for doing the I/O, which must have
 //            already been allocated. The size of this buffer is the unit
 //            size of our I/O.
 nsresult
 VerifyStreamContentDigest(nsIInputStream* stream,
-                          const SECItem& digestFromManifest, SECItem& buf)
+                          const nsCString& digestFromManifest, SECItem& buf)
 {
   MOZ_ASSERT(buf.len > 0);
-  if (digestFromManifest.len != SHA1_LENGTH)
+  if (digestFromManifest.Length() != SHA1_LENGTH) {
     return NS_ERROR_SIGNED_JAR_MANIFEST_INVALID;
+  }
 
   nsresult rv;
   uint64_t len64;
   rv = stream->Available(&len64);
   NS_ENSURE_SUCCESS(rv, rv);
   if (len64 > UINT32_MAX) {
     return NS_ERROR_SIGNED_JAR_ENTRY_TOO_LARGE;
   }
@@ -212,26 +222,27 @@ VerifyStreamContentDigest(nsIInputStream
     return NS_ERROR_SIGNED_JAR_ENTRY_INVALID;
   }
 
   // Verify that the digests match.
   Digest digest;
   rv = digest.End(SEC_OID_SHA1, digestContext);
   NS_ENSURE_SUCCESS(rv, rv);
 
-  if (SECITEM_CompareItem(&digestFromManifest, &digest.get()) != SECEqual) {
+  nsDependentCSubstring digestStr(DigestToDependentString(digest));
+  if (!digestStr.Equals(digestFromManifest)) {
     return NS_ERROR_SIGNED_JAR_MODIFIED_ENTRY;
   }
 
   return NS_OK;
 }
 
 nsresult
 VerifyEntryContentDigest(nsIZipReader* zip, const nsACString& aFilename,
-                         const SECItem& digestFromManifest, SECItem& buf)
+                         const nsCString& digestFromManifest, SECItem& buf)
 {
   nsCOMPtr<nsIInputStream> stream;
   nsresult rv = zip->GetInputStream(aFilename, getter_AddRefs(stream));
   if (NS_FAILED(rv)) {
     return NS_ERROR_SIGNED_JAR_ENTRY_MISSING;
   }
 
   return VerifyStreamContentDigest(stream, digestFromManifest, buf);
@@ -239,17 +250,17 @@ VerifyEntryContentDigest(nsIZipReader* z
 
 // @oaram aDir       directory containing the unpacked signed archive
 // @param aFilename  path of the target file relative to aDir
 // @param digestFromManifest The digest that we're supposed to check the file's
 //                           contents against, from the manifest
 // @param buf A scratch buffer that we use for doing the I/O
 nsresult
 VerifyFileContentDigest(nsIFile* aDir, const nsAString& aFilename,
-                        const SECItem& digestFromManifest, SECItem& buf)
+                        const nsCString& digestFromManifest, SECItem& buf)
 {
   // Find the file corresponding to the manifest path
   nsCOMPtr<nsIFile> file;
   nsresult rv = aDir->Clone(getter_AddRefs(file));
   if (NS_FAILED(rv)) {
     return rv;
   }
 
@@ -435,24 +446,24 @@ CheckManifestVersion(const char* & nextL
 // In order to get benefit (1), we do NOT implement the fallback to the older
 // mechanism as the spec requires/suggests. Also, for simplity's sake, we only
 // support exactly one SHA1-Digest-Manifest attribute, and no other
 // algorithms.
 //
 // filebuf must be null-terminated. On output, mfDigest will contain the
 // decoded value of SHA1-Digest-Manifest.
 nsresult
-ParseSF(const char* filebuf, /*out*/ SECItem & mfDigest)
+ParseSF(const char* filebuf, /*out*/ nsCString& mfDigest)
 {
-  nsresult rv;
-
   const char* nextLineStart = filebuf;
-  rv = CheckManifestVersion(nextLineStart, NS_LITERAL_CSTRING(JAR_SF_HEADER));
-  if (NS_FAILED(rv))
+  nsresult rv = CheckManifestVersion(nextLineStart,
+                                     NS_LITERAL_CSTRING(JAR_SF_HEADER));
+  if (NS_FAILED(rv)) {
     return rv;
+  }
 
   // Find SHA1-Digest-Manifest
   for (;;) {
     nsAutoCString curLine;
     rv = ReadLine(nextLineStart, curLine);
     if (NS_FAILED(rv)) {
       return rv;
     }
@@ -466,17 +477,17 @@ ParseSF(const char* filebuf, /*out*/ SEC
     nsAutoCString attrName;
     nsAutoCString attrValue;
     rv = ParseAttribute(curLine, attrName, attrValue);
     if (NS_FAILED(rv)) {
       return rv;
     }
 
     if (attrName.LowerCaseEqualsLiteral("sha1-digest-manifest")) {
-      rv = MapSECStatus(ATOB_ConvertAsciiToItem(&mfDigest, attrValue.get()));
+      rv = Base64Decode(attrValue, mfDigest);
       if (NS_FAILED(rv)) {
         return rv;
       }
 
       // There could be multiple SHA1-Digest-Manifest attributes, which
       // would be an error, but it's better to just skip any erroneous
       // duplicate entries rather than trying to detect them, because:
       //
@@ -522,33 +533,33 @@ ParseMF(const char* filebuf, nsIZipReade
 
     // Manifest containing no file entries is OK, though useless.
     if (*nextLineStart == '\0') {
       return NS_OK;
     }
   }
 
   nsAutoCString curItemName;
-  ScopedAutoSECItem digest;
+  nsAutoCString digest;
 
   for (;;) {
     nsAutoCString curLine;
     rv = ReadLine(nextLineStart, curLine);
     NS_ENSURE_SUCCESS(rv, rv);
 
     if (curLine.Length() == 0) {
       // end of section (blank line or end-of-file)
 
       if (curItemName.Length() == 0) {
         // '...Each section must start with an attribute with the name as
         // "Name",...', so every section must have a Name attribute.
         return NS_ERROR_SIGNED_JAR_MANIFEST_INVALID;
       }
 
-      if (digest.len == 0) {
+      if (digest.IsEmpty()) {
         // We require every entry to have a digest, since we require every
         // entry to be signed and we don't allow duplicate entries.
         return NS_ERROR_SIGNED_JAR_MANIFEST_INVALID;
       }
 
       if (mfItems.Contains(curItemName)) {
         // Duplicate entry
         return NS_ERROR_SIGNED_JAR_MANIFEST_INVALID;
@@ -563,39 +574,41 @@ ParseMF(const char* filebuf, nsIZipReade
       mfItems.PutEntry(curItemName);
 
       if (*nextLineStart == '\0') // end-of-file
         break;
 
       // reset so we know we haven't encountered either of these for the next
       // item yet.
       curItemName.Truncate();
-      digest.reset();
+      digest.Truncate();
 
       continue; // skip the rest of the loop below
     }
 
     nsAutoCString attrName;
     nsAutoCString attrValue;
     rv = ParseAttribute(curLine, attrName, attrValue);
     if (NS_FAILED(rv)) {
       return rv;
     }
 
     // Lines to look for:
 
     // (1) Digest:
     if (attrName.LowerCaseEqualsLiteral("sha1-digest"))
     {
-      if (digest.len > 0) // multiple SHA1 digests in section
+      if (!digest.IsEmpty()) { // multiple SHA1 digests in section
         return NS_ERROR_SIGNED_JAR_MANIFEST_INVALID;
+      }
 
-      rv = MapSECStatus(ATOB_ConvertAsciiToItem(&digest, attrValue.get()));
-      if (NS_FAILED(rv))
+      rv = Base64Decode(attrValue, digest);
+      if (NS_FAILED(rv)) {
         return NS_ERROR_SIGNED_JAR_MANIFEST_INVALID;
+      }
 
       continue;
     }
 
     // (2) Name: associates this manifest section with a file in the jar.
     if (attrName.LowerCaseEqualsLiteral("name"))
     {
       if (MOZ_UNLIKELY(curItemName.Length() > 0)) // multiple names in section
@@ -749,33 +762,35 @@ OpenSignedAppFile(AppTrustedRoot aTruste
   sigBuffer.type = siBuffer;
   UniqueCERTCertList builtChain;
   rv = VerifySignature(aTrustedRoot, sigBuffer, sfCalculatedDigest.get(),
                        builtChain);
   if (NS_FAILED(rv)) {
     return rv;
   }
 
-  ScopedAutoSECItem mfDigest;
+  nsAutoCString mfDigest;
   rv = ParseSF(BitwiseCast<char*, unsigned char*>(sfBuffer.data), mfDigest);
   if (NS_FAILED(rv)) {
     return rv;
   }
 
   // Manifest (MF) file
   nsAutoCString mfFilename;
   ScopedAutoSECItem manifestBuffer;
   Digest mfCalculatedDigest;
   rv = FindAndLoadOneEntry(zip, NS_LITERAL_CSTRING(JAR_MF_SEARCH_STRING),
                            mfFilename, manifestBuffer, &mfCalculatedDigest);
   if (NS_FAILED(rv)) {
     return rv;
   }
 
-  if (SECITEM_CompareItem(&mfDigest, &mfCalculatedDigest.get()) != SECEqual) {
+  nsDependentCSubstring calculatedDigest(
+    DigestToDependentString(mfCalculatedDigest));
+  if (!mfDigest.Equals(calculatedDigest)) {
     return NS_ERROR_SIGNED_JAR_MANIFEST_INVALID;
   }
 
   // Allocate the I/O buffer only once per JAR, instead of once per entry, in
   // order to minimize malloc/free calls and in order to avoid fragmenting
   // memory.
   ScopedAutoSECItem buf(128 * 1024);
 
@@ -1045,17 +1060,17 @@ ParseMFUnpacked(const char* aFilebuf, ns
 
     // Manifest containing no file entries is OK, though useless.
     if (*nextLineStart == '\0') {
       return NS_OK;
     }
   }
 
   nsAutoString curItemName;
-  ScopedAutoSECItem digest;
+  nsAutoCString digest;
 
   for (;;) {
     nsAutoCString curLine;
     rv = ReadLine(nextLineStart, curLine);
     if (NS_FAILED(rv)) {
       return rv;
     }
 
@@ -1063,17 +1078,17 @@ ParseMFUnpacked(const char* aFilebuf, ns
       // end of section (blank line or end-of-file)
 
       if (curItemName.Length() == 0) {
         // '...Each section must start with an attribute with the name as
         // "Name",...', so every section must have a Name attribute.
         return NS_ERROR_SIGNED_JAR_MANIFEST_INVALID;
       }
 
-      if (digest.len == 0) {
+      if (digest.IsEmpty()) {
         // We require every entry to have a digest, since we require every
         // entry to be signed and we don't allow duplicate entries.
         return NS_ERROR_SIGNED_JAR_MANIFEST_INVALID;
       }
 
       if (aMfItems.Contains(curItemName)) {
         // Duplicate entry
         return NS_ERROR_SIGNED_JAR_MANIFEST_INVALID;
@@ -1091,38 +1106,38 @@ ParseMFUnpacked(const char* aFilebuf, ns
       if (*nextLineStart == '\0') {
         // end-of-file
         break;
       }
 
       // reset so we know we haven't encountered either of these for the next
       // item yet.
       curItemName.Truncate();
-      digest.reset();
+      digest.Truncate();
 
       continue; // skip the rest of the loop below
     }
 
     nsAutoCString attrName;
     nsAutoCString attrValue;
     rv = ParseAttribute(curLine, attrName, attrValue);
     if (NS_FAILED(rv)) {
       return rv;
     }
 
     // Lines to look for:
 
     // (1) Digest:
     if (attrName.LowerCaseEqualsLiteral("sha1-digest")) {
-      if (digest.len > 0) {
+      if (!digest.IsEmpty()) {
         // multiple SHA1 digests in section
         return NS_ERROR_SIGNED_JAR_MANIFEST_INVALID;
       }
 
-      rv = MapSECStatus(ATOB_ConvertAsciiToItem(&digest, attrValue.get()));
+      rv = Base64Decode(attrValue, digest);
       if (NS_FAILED(rv)) {
         return NS_ERROR_SIGNED_JAR_MANIFEST_INVALID;
       }
 
       continue;
     }
 
     // (2) Name: associates this manifest section with a file in the jar.
@@ -1305,33 +1320,35 @@ VerifySignedDirectory(AppTrustedRoot aTr
   rv = VerifySignature(aTrustedRoot, sigBuffer, sfCalculatedDigest.get(),
                        builtChain);
   if (NS_FAILED(rv)) {
     return NS_ERROR_SIGNED_JAR_MANIFEST_INVALID;
   }
 
   // Get the expected manifest hash from the signed .sf file
 
-  ScopedAutoSECItem mfDigest;
+  nsAutoCString mfDigest;
   rv = ParseSF(BitwiseCast<char*, unsigned char*>(sfBuffer.data), mfDigest);
   if (NS_FAILED(rv)) {
     return NS_ERROR_SIGNED_JAR_MANIFEST_INVALID;
   }
 
   // Load manifest (MF) file and verify signature
 
   nsAutoString mfFilename(NS_LITERAL_STRING("manifest.mf"));
   ScopedAutoSECItem manifestBuffer;
   Digest mfCalculatedDigest;
   rv = LoadOneMetafile(metaDir, mfFilename, manifestBuffer, &mfCalculatedDigest);
   if (NS_FAILED(rv)) {
     return NS_ERROR_SIGNED_JAR_MANIFEST_INVALID;
   }
 
-  if (SECITEM_CompareItem(&mfDigest, &mfCalculatedDigest.get()) != SECEqual) {
+  nsDependentCSubstring calculatedDigest(
+    DigestToDependentString(mfCalculatedDigest));
+  if (!mfDigest.Equals(calculatedDigest)) {
     return NS_ERROR_SIGNED_JAR_MANIFEST_INVALID;
   }
 
   // Parse manifest and verify signed hash of all listed files
 
   // Allocate the I/O buffer only once per JAR, instead of once per entry, in
   // order to minimize malloc/free calls and in order to avoid fragmenting
   // memory.
--- a/security/certverifier/ExtendedValidation.cpp
+++ b/security/certverifier/ExtendedValidation.cpp
@@ -1,24 +1,26 @@
 /* -*- 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 "ExtendedValidation.h"
 
-#include "base64.h"
 #include "cert.h"
 #include "certdb.h"
 #include "hasht.h"
 #include "mozilla/ArrayUtils.h"
 #include "mozilla/Assertions.h"
+#include "mozilla/Base64.h"
 #include "mozilla/Casting.h"
 #include "mozilla/PodOperations.h"
+#include "nsDependentString.h"
+#include "nsString.h"
 #include "pk11pub.h"
 #include "pkix/pkixtypes.h"
 #include "prerror.h"
 
 struct nsMyTrustedEVInfo
 {
   // See bug 1338873 about making these fields const.
   const char* dotted_oid;
@@ -1244,35 +1246,38 @@ LoadExtendedValidationInfo()
 
     SECStatus srv;
 #ifdef DEBUG
     // This section of code double-checks that we calculated the correct
     // certificate hash given the issuer and serial number and that it is
     // actually present in our loaded root certificates module. It is
     // unnecessary to check this in non-debug builds since we will safely fall
     // back to DV if the EV information is incorrect.
-    mozilla::ScopedAutoSECItem derIssuer;
-    srv = ATOB_ConvertAsciiToItem(&derIssuer, entry.issuer_base64);
-    MOZ_ASSERT(srv == SECSuccess, "Could not base64-decode built-in EV issuer");
-    if (srv != SECSuccess) {
-      return NS_ERROR_FAILURE;
+    nsAutoCString derIssuer;
+    nsresult rv = Base64Decode(nsDependentCString(entry.issuer_base64),
+                               derIssuer);
+    MOZ_ASSERT(NS_SUCCEEDED(rv), "Could not base64-decode built-in EV issuer");
+    if (NS_FAILED(rv)) {
+      return rv;
     }
 
-    mozilla::ScopedAutoSECItem serialNumber;
-    srv = ATOB_ConvertAsciiToItem(&serialNumber, entry.serial_base64);
-    MOZ_ASSERT(srv == SECSuccess, "Could not base64-decode built-in EV serial");
-    if (srv != SECSuccess) {
-      return NS_ERROR_FAILURE;
+    nsAutoCString serialNumber;
+    rv = Base64Decode(nsDependentCString(entry.serial_base64), serialNumber);
+    MOZ_ASSERT(NS_SUCCEEDED(rv), "Could not base64-decode built-in EV serial");
+    if (NS_FAILED(rv)) {
+      return rv;
     }
 
     CERTIssuerAndSN ias;
-    ias.derIssuer.data = derIssuer.data;
-    ias.derIssuer.len = derIssuer.len;
-    ias.serialNumber.data = serialNumber.data;
-    ias.serialNumber.len = serialNumber.len;
+    ias.derIssuer.data =
+      BitwiseCast<unsigned char*, const char*>(derIssuer.get());
+    ias.derIssuer.len = derIssuer.Length();
+    ias.serialNumber.data =
+      BitwiseCast<unsigned char*, const char*>(serialNumber.get());
+    ias.serialNumber.len = serialNumber.Length();
     ias.serialNumber.type = siUnsignedInteger;
 
     UniqueCERTCertificate cert(CERT_FindCertByIssuerAndSN(nullptr, &ias));
 
     // If an entry is missing in the NSS root database, it may be because the
     // root database is out of sync with what we expect (e.g. a different
     // version of system NSS is installed).
     if (!cert) {
--- a/security/manager/ssl/ContentSignatureVerifier.cpp
+++ b/security/manager/ssl/ContentSignatureVerifier.cpp
@@ -6,28 +6,28 @@
 
 #include "ContentSignatureVerifier.h"
 
 #include "BRNameMatchingPolicy.h"
 #include "SharedCertVerifier.h"
 #include "cryptohi.h"
 #include "keyhi.h"
 #include "mozilla/Assertions.h"
+#include "mozilla/Base64.h"
 #include "mozilla/Casting.h"
 #include "mozilla/Unused.h"
 #include "nsCOMPtr.h"
 #include "nsContentUtils.h"
 #include "nsISupportsPriority.h"
 #include "nsIURI.h"
 #include "nsNSSComponent.h"
 #include "nsPromiseFlatString.h"
 #include "nsSecurityHeaderParser.h"
 #include "nsStreamUtils.h"
 #include "nsWhitespaceTokenizer.h"
-#include "nssb64.h"
 #include "pkix/pkix.h"
 #include "pkix/pkixtypes.h"
 #include "secerr.h"
 
 NS_IMPL_ISUPPORTS(ContentSignatureVerifier,
                   nsIContentSignatureVerifier,
                   nsIInterfaceRequestor,
                   nsIStreamListener)
@@ -95,22 +95,27 @@ ReadChainIntoCertList(const nsACString& 
     if (token.IsEmpty()) {
       continue;
     }
     if (inBlock) {
       if (token.Equals(footer)) {
         inBlock = false;
         certFound = true;
         // base64 decode data, make certs, append to chain
-        ScopedAutoSECItem der;
-        if (!NSSBase64_DecodeBuffer(nullptr, &der, blockData.BeginReading(),
-                                    blockData.Length())) {
+        nsAutoCString derString;
+        nsresult rv = Base64Decode(blockData, derString);
+        if (NS_FAILED(rv)) {
           CSVerifier_LOG(("CSVerifier: decoding the signature failed\n"));
-          return NS_ERROR_FAILURE;
+          return rv;
         }
+        SECItem der = {
+          siBuffer,
+          BitwiseCast<unsigned char*, const char*>(derString.get()),
+          derString.Length(),
+        };
         UniqueCERTCertificate tmpCert(
           CERT_NewTempCertificate(CERT_GetDefaultCertDB(), &der, nullptr, false,
                                   true));
         if (!tmpCert) {
           return NS_ERROR_FAILURE;
         }
         // if adding tmpCert succeeds, tmpCert will now be owned by aCertList
         SECStatus res = CERT_AddCertToListTail(aCertList, tmpCert.get());
@@ -210,25 +215,30 @@ ContentSignatureVerifier::CreateContextI
 
   // in case we were not able to extract a key
   if (!mKey) {
     CSVerifier_LOG(("CSVerifier: unable to extract a key\n"));
     return NS_ERROR_INVALID_SIGNATURE;
   }
 
   // Base 64 decode the signature
-  ScopedAutoSECItem rawSignatureItem;
-  if (!NSSBase64_DecodeBuffer(nullptr, &rawSignatureItem, mSignature.get(),
-                              mSignature.Length())) {
+  nsAutoCString rawSignature;
+  rv = Base64Decode(mSignature, rawSignature);
+  if (NS_FAILED(rv)) {
     CSVerifier_LOG(("CSVerifier: decoding the signature failed\n"));
-    return NS_ERROR_FAILURE;
+    return rv;
   }
 
   // get signature object
   ScopedAutoSECItem signatureItem;
+  SECItem rawSignatureItem = {
+    siBuffer,
+    BitwiseCast<unsigned char*, const char*>(rawSignature.get()),
+    rawSignature.Length(),
+  };
   // We have a raw ecdsa signature r||s so we have to DER-encode it first
   // Note that we have to check rawSignatureItem->len % 2 here as
   // DSAU_EncodeDerSigWithLen asserts this
   if (rawSignatureItem.len == 0 || rawSignatureItem.len % 2 != 0) {
     CSVerifier_LOG(("CSVerifier: signature length is bad\n"));
     return NS_ERROR_FAILURE;
   }
   if (DSAU_EncodeDerSigWithLen(&signatureItem, &rawSignatureItem,
--- a/security/manager/ssl/PublicKeyPinningService.cpp
+++ b/security/manager/ssl/PublicKeyPinningService.cpp
@@ -10,17 +10,16 @@
 #include "mozilla/BinarySearch.h"
 #include "mozilla/Casting.h"
 #include "mozilla/Logging.h"
 #include "mozilla/Telemetry.h"
 #include "nsDependentString.h"
 #include "nsISiteSecurityService.h"
 #include "nsServiceManagerUtils.h"
 #include "nsSiteSecurityService.h"
-#include "nssb64.h"
 #include "pkix/pkixtypes.h"
 #include "seccomon.h"
 #include "sechash.h"
 
 #include "StaticHPKPins.h" // autogenerated by genHPKPStaticpins.js
 
 using namespace mozilla;
 using namespace mozilla::pkix;
--- a/security/manager/ssl/nsCryptoHash.cpp
+++ b/security/manager/ssl/nsCryptoHash.cpp
@@ -3,19 +3,20 @@
  * 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 "nsCryptoHash.h"
 
 #include <algorithm>
 
-#include "base64.h"
 #include "mozilla/ArrayUtils.h"
+#include "mozilla/Base64.h"
 #include "mozilla/Casting.h"
+#include "nsDependentString.h"
 #include "nsIInputStream.h"
 #include "nsIKeyModule.h"
 #include "nsString.h"
 #include "pk11pub.h"
 #include "sechash.h"
 
 using namespace mozilla;
 
@@ -218,28 +219,22 @@ nsCryptoHash::Finish(bool ascii, nsACStr
   }
 
   uint32_t hashLen = 0;
   unsigned char buffer[HASH_LENGTH_MAX];
   HASH_End(mHashContext.get(), buffer, &hashLen, HASH_LENGTH_MAX);
 
   mInitialized = false;
 
-  if (ascii)
-  {
-    UniquePORTString asciiData(BTOA_DataToAscii(buffer, hashLen));
-    NS_ENSURE_TRUE(asciiData, NS_ERROR_OUT_OF_MEMORY);
-
-    _retval.Assign(asciiData.get());
-  }
-  else
-  {
-    _retval.Assign((const char*)buffer, hashLen);
+  if (ascii) {
+    nsDependentCSubstring dataStr(BitwiseCast<char*>(buffer), hashLen);
+    return Base64Encode(dataStr, _retval);
   }
 
+  _retval.Assign(BitwiseCast<char*>(buffer), hashLen);
   return NS_OK;
 }
 
 //---------------------------------------------
 // Implementing nsICryptoHMAC
 //---------------------------------------------
 
 NS_IMPL_ISUPPORTS(nsCryptoHMAC, nsICryptoHMAC)
@@ -423,28 +418,22 @@ nsCryptoHMAC::Finish(bool aASCII, nsACSt
   uint32_t hashLen = 0;
   unsigned char buffer[HASH_LENGTH_MAX];
   SECStatus srv = PK11_DigestFinal(mHMACContext.get(), buffer, &hashLen,
                                    HASH_LENGTH_MAX);
   if (srv != SECSuccess) {
     return NS_ERROR_FAILURE;
   }
 
-  if (aASCII)
-  {
-    UniquePORTString asciiData(BTOA_DataToAscii(buffer, hashLen));
-    NS_ENSURE_TRUE(asciiData, NS_ERROR_OUT_OF_MEMORY);
-
-    _retval.Assign(asciiData.get());
-  }
-  else
-  {
-    _retval.Assign((const char*)buffer, hashLen);
+  if (aASCII) {
+    nsDependentCSubstring dataStr(BitwiseCast<char*>(buffer), hashLen);
+    return Base64Encode(dataStr, _retval);
   }
 
+  _retval.Assign(BitwiseCast<char*>(buffer), hashLen);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsCryptoHMAC::Reset()
 {
   nsNSSShutDownPreventionLock locker;
   if (isAlreadyShutDown()) {
--- a/security/manager/ssl/nsDataSignatureVerifier.cpp
+++ b/security/manager/ssl/nsDataSignatureVerifier.cpp
@@ -4,22 +4,22 @@
 
 #include "nsDataSignatureVerifier.h"
 
 #include "ScopedNSSTypes.h"
 #include "SharedCertVerifier.h"
 #include "cms.h"
 #include "cryptohi.h"
 #include "keyhi.h"
+#include "mozilla/Base64.h"
 #include "mozilla/Casting.h"
 #include "mozilla/Unused.h"
 #include "nsCOMPtr.h"
 #include "nsNSSComponent.h"
 #include "nsString.h"
-#include "nssb64.h"
 #include "pkix/pkixnss.h"
 #include "pkix/pkixtypes.h"
 #include "secerr.h"
 
 using namespace mozilla;
 using namespace mozilla::pkix;
 using namespace mozilla::psm;
 
@@ -64,48 +64,62 @@ nsDataSignatureVerifier::VerifyData(cons
 
   // Allocate an arena to handle the majority of the allocations
   UniquePLArenaPool arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE));
   if (!arena) {
     return NS_ERROR_OUT_OF_MEMORY;
   }
 
   // Base 64 decode the key
-  SECItem keyItem;
-  PORT_Memset(&keyItem, 0, sizeof(SECItem));
-  if (!NSSBase64_DecodeBuffer(arena.get(), &keyItem,
-                              PromiseFlatCString(aPublicKey).get(),
-                              aPublicKey.Length())) {
-    return NS_ERROR_FAILURE;
+  // For compatibility reasons we need to remove all whitespace first, since
+  // Base64Decode() will not accept invalid characters.
+  nsAutoCString b64KeyNoWhitespace(aPublicKey);
+  b64KeyNoWhitespace.StripWhitespace();
+  nsAutoCString key;
+  nsresult rv = Base64Decode(b64KeyNoWhitespace, key);
+  if (NS_FAILED(rv)) {
+    return rv;
   }
 
   // Extract the public key from the data
+  SECItem keyItem = {
+    siBuffer,
+    BitwiseCast<unsigned char*, const char*>(key.get()),
+    key.Length(),
+  };
   UniqueCERTSubjectPublicKeyInfo pki(
     SECKEY_DecodeDERSubjectPublicKeyInfo(&keyItem));
   if (!pki) {
     return NS_ERROR_FAILURE;
   }
 
   UniqueSECKEYPublicKey publicKey(SECKEY_ExtractPublicKey(pki.get()));
   if (!publicKey) {
     return NS_ERROR_FAILURE;
   }
 
   // Base 64 decode the signature
-  SECItem signatureItem;
-  PORT_Memset(&signatureItem, 0, sizeof(SECItem));
-  if (!NSSBase64_DecodeBuffer(arena.get(), &signatureItem,
-                              PromiseFlatCString(aSignature).get(),
-                              aSignature.Length())) {
-    return NS_ERROR_FAILURE;
+  // For compatibility reasons we need to remove all whitespace first, since
+  // Base64Decode() will not accept invalid characters.
+  nsAutoCString b64SignatureNoWhitespace(aSignature);
+  b64SignatureNoWhitespace.StripWhitespace();
+  nsAutoCString signature;
+  rv = Base64Decode(b64SignatureNoWhitespace, signature);
+  if (NS_FAILED(rv)) {
+    return rv;
   }
 
   // Decode the signature and algorithm
   CERTSignedData sigData;
   PORT_Memset(&sigData, 0, sizeof(CERTSignedData));
+  SECItem signatureItem = {
+    siBuffer,
+    BitwiseCast<unsigned char*, const char*>(signature.get()),
+    signature.Length(),
+  };
   SECStatus srv = SEC_QuickDERDecodeItem(arena.get(), &sigData,
                                          CERT_SignatureDataTemplate,
                                          &signatureItem);
   if (srv != SECSuccess) {
     return NS_ERROR_FAILURE;
   }
 
   // Perform the final verification
--- a/security/manager/ssl/nsKeygenHandler.cpp
+++ b/security/manager/ssl/nsKeygenHandler.cpp
@@ -1,24 +1,27 @@
 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
  *
  * 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 "base64.h"
+#include "nsKeygenHandler.h"
+
 #include "cryptohi.h"
 #include "keyhi.h"
 #include "mozilla/Assertions.h"
+#include "mozilla/Base64.h"
+#include "mozilla/Casting.h"
+#include "nsDependentString.h"
 #include "nsIContent.h"
 #include "nsIDOMHTMLSelectElement.h"
 #include "nsIGenKeypairInfoDlg.h"
 #include "nsIServiceManager.h"
 #include "nsITokenDialogs.h"
-#include "nsKeygenHandler.h"
 #include "nsKeygenHandlerContent.h"
 #include "nsKeygenThread.h"
 #include "nsNSSComponent.h" // for PIPNSS string bundle calls.
 #include "nsNSSHelper.h"
 #include "nsReadableUtils.h"
 #include "nsUnicharUtils.h"
 #include "nsXULAppAPI.h"
 #include "nspr.h"
@@ -401,32 +404,33 @@ nsKeygenFormProcessor::GetPublicKey(cons
                                     const nsAString& aKeyParams)
 {
     nsNSSShutDownPreventionLock locker;
     if (isAlreadyShutDown()) {
       return NS_ERROR_NOT_AVAILABLE;
     }
 
     nsresult rv = NS_ERROR_FAILURE;
-    UniquePORTString keystring;
+    nsAutoCString keystring;
     char *keyparamsString = nullptr;
     uint32_t keyGenMechanism;
     PK11SlotInfo *slot = nullptr;
     PK11RSAGenParams rsaParams;
     mozilla::UniqueSECItem ecParams;
     SECOidTag algTag;
     int keysize = 0;
     void *params = nullptr; // Non-owning.
     SECKEYPrivateKey *privateKey = nullptr;
     SECKEYPublicKey *publicKey = nullptr;
     CERTSubjectPublicKeyInfo *spkInfo = nullptr;
     SECStatus srv = SECFailure;
     SECItem spkiItem;
     SECItem pkacItem;
     SECItem signedItem;
+    nsDependentCSubstring signedItemStr;
     CERTPublicKeyAndChallenge pkac;
     pkac.challenge.data = nullptr;
     nsCOMPtr<nsIGeneratingKeypairInfoDialogs> dialogs;
     nsKeygenThread *KeygenRunnable = 0;
     nsCOMPtr<nsIKeygenThread> runnable;
 
     // permanent and sensitive flags for keygen
     PK11AttrFlags attrFlags = PK11_ATTR_TOKEN | PK11_ATTR_SENSITIVE | PK11_ATTR_PRIVATE;
@@ -615,24 +619,25 @@ nsKeygenFormProcessor::GetPublicKey(cons
                           privateKey, algTag);
     if (srv != SECSuccess) {
         goto loser;
     }
 
     /*
      * Convert the signed public key and challenge into base64/ascii.
      */
-    keystring = UniquePORTString(
-      BTOA_DataToAscii(signedItem.data, signedItem.len));
-    if (!keystring) {
-        rv = NS_ERROR_OUT_OF_MEMORY;
+    signedItemStr.Assign(
+        mozilla::BitwiseCast<char*, unsigned char*>(signedItem.data),
+        signedItem.len);
+    rv = mozilla::Base64Encode(signedItemStr, keystring);
+    if (NS_FAILED(rv)) {
         goto loser;
     }
 
-    CopyASCIItoUTF16(keystring.get(), aOutPublicKey);
+    CopyASCIItoUTF16(keystring, aOutPublicKey);
 
     rv = NS_OK;
 
 loser:
     if (srv != SECSuccess) {
         if ( privateKey ) {
             PK11_DestroyTokenObject(privateKey->pkcs11Slot,privateKey->pkcs11ID);
         }
--- a/security/manager/ssl/nsKeygenHandler.h
+++ b/security/manager/ssl/nsKeygenHandler.h
@@ -9,16 +9,17 @@
 
 #include "ScopedNSSTypes.h"
 #include "keythi.h"
 #include "nsCOMPtr.h"
 #include "nsError.h"
 #include "nsIFormProcessor.h"
 #include "nsIInterfaceRequestor.h"
 #include "nsNSSShutDown.h"
+#include "nsString.h"
 #include "nsTArray.h"
 #include "secmodt.h"
 
 nsresult GetSlotWithMechanism(uint32_t mechanism,
                               nsIInterfaceRequestor* ctx,
                               PK11SlotInfo** retSlot,
                               nsNSSShutDownPreventionLock& /*proofOfLock*/);
 
--- a/security/manager/ssl/nsSiteSecurityService.cpp
+++ b/security/manager/ssl/nsSiteSecurityService.cpp
@@ -3,17 +3,16 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsSiteSecurityService.h"
 
 #include "CertVerifier.h"
 #include "PublicKeyPinningService.h"
 #include "ScopedNSSTypes.h"
 #include "SharedCertVerifier.h"
-#include "base64.h"
 #include "mozilla/Assertions.h"
 #include "mozilla/Base64.h"
 #include "mozilla/dom/PContent.h"
 #include "mozilla/dom/ToJSValue.h"
 #include "mozilla/LinkedList.h"
 #include "mozilla/Logging.h"
 #include "mozilla/Preferences.h"
 #include "nsArrayEnumerator.h"
--- a/security/manager/ssl/tests/unit/test_datasignatureverifier.js
+++ b/security/manager/ssl/tests/unit/test_datasignatureverifier.js
@@ -115,17 +115,23 @@ const signatures = [
 
 // Key 1, Data 1, SHA512 hash algorithm
 "MIGTMA0GCSqGSIb3DQEBDQUAA4GBAF0+XYD/r0Annz1GJ24GTkAlWY/OixCSV6Ix" +
 "OMM7P2d/jgOP+ICKIpxqaSE0CbkLiegUiidIOWvFqDxQJWlAAukDUWISGFfJMFxX" +
 "3jzJ0bBfeNY/1Qo8jMQopcNco/NlNgoSKAUOBtk31aFgNoVC3kWUk6pO97KEiJ+e" +
 "bQp9Z2/M",
 
 // Invalid signature data ("foobar" base 64 encoded)
-"Zm9vYmFy"
+"Zm9vYmFy",
+
+// Key 1, Data 1, SHA512 hash algorithm, with embedded whitespace.
+`MIGTMA0GCSqGSIb3DQEBDQUAA4GBAF0+XYD/r0Annz1GJ24GTkAlWY/OixCSV6Ix
+   OMM7P2d/jgOP+ICKIpxqaSE0CbkLiegUiidIOWvFqDxQJWlAAukDUWISGFfJMFxX
+ 3jzJ0bBfeNY/1Qo8jMQopcNco/NlNgoSKAUOBtk31aFgNoVC3kWUk6pO97KEiJ+e
+   bQp9Z2/M`,
 ];
 
 const tests = [
 // Data   Signature  Key   Expected   Throws
 // Pass cases
   [0,     0,         0,    true,      false], // 0
   [0,     1,         0,    true,      false], // 1
   [0,     2,         0,    true,      false], // 2
@@ -164,16 +170,19 @@ const tests = [
   [0,     13,        0,    false,     false], // 33
   [1,     14,        0,    false,     false], // 34
   [1,     15,        0,    false,     false], // 35
 // Invalid data cases
   [0,     0,         2,    false,     true],  // 36
   [0,     1,         2,    false,     true],  // 37
   [0,     16,        0,    false,     true],  // 38
   [1,     16,        0,    false,     true],  // 39
+  // Test embedded whitespace (e.g. signature read directly from file) is
+  // ignored for backwards compatibility purposes.
+  [1,     17,        1,    true,      false],
 ];
 
 function run_test() {
   let verifier = Cc["@mozilla.org/security/datasignatureverifier;1"]
                    .createInstance(Ci.nsIDataSignatureVerifier);
 
   for (let testCase of tests) {
     let testShouldThrow = testCase[4];
--- a/security/manager/ssl/tests/unit/test_hash_algorithms.js
+++ b/security/manager/ssl/tests/unit/test_hash_algorithms.js
@@ -71,19 +71,18 @@ const ALGORITHMS = [
   {
     initString: "sha512",
     initConstant: Ci.nsICryptoHash.SHA512,
     hexHashes: [
       "07e547d9586f6a73f73fbac0435ed76951218fb7d0c8d788a309d785436bbb642e93a252a954f23912547d1e8a3b5ed6e1bfd7097821233fa0538f3db854fee6",
       "cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e",
     ],
     b64Hashes: [
-      // TODO(Bug 1338897): Stop inserting CRLFs every 64 characters.
-      "B+VH2VhvanP3P7rAQ17XaVEhj7fQyNeIownXhUNru2Quk6JSqVTyORJUfR6KO17W\r\n4b/XCXghIz+gU489uFT+5g==",
-      "z4PhNX7vuL3xVChQ1m2AB9Yg5AULVxXcg/SpIdNs6c5H0NE8XYXysP+DGNKHfuwv\r\nY7kxvUdBeoGlODJ6+SfaPg==",
+      "B+VH2VhvanP3P7rAQ17XaVEhj7fQyNeIownXhUNru2Quk6JSqVTyORJUfR6KO17W4b/XCXghIz+gU489uFT+5g==",
+      "z4PhNX7vuL3xVChQ1m2AB9Yg5AULVxXcg/SpIdNs6c5H0NE8XYXysP+DGNKHfuwvY7kxvUdBeoGlODJ6+SfaPg==",
     ],
   },
 ];
 
 function doHash(algo, value, cmp) {
   let hash = Cc["@mozilla.org/security/hash;1"].createInstance(Ci.nsICryptoHash);
   hash.initWithString(algo);
 
--- a/security/manager/ssl/tests/unit/test_hmac.js
+++ b/security/manager/ssl/tests/unit/test_hmac.js
@@ -87,18 +87,17 @@ function testVectors() {
     {
       algoID: Ci.nsICryptoHMAC.SHA384, algoName: "SHA-384",
       expectedDigest: "af45d2e376484031617f78d2b58a6b1b9c7ef464f5a01b47e42ec3736322445e8e2240ca5e69e2c78b3239ecfab21649",
       expectedBase64: "r0XS43ZIQDFhf3jStYprG5x+9GT1oBtH5C7Dc2MiRF6OIkDKXmnix4syOez6shZJ",
     },
     {
       algoID: Ci.nsICryptoHMAC.SHA512, algoName: "SHA-512",
       expectedDigest: "164b7a7bfcf819e2e395fbe73b56e0a387bd64222e831fd610270cd7ea2505549758bf75c05a994a6d034f65f8f0e6fdcaeab1a34d4a6b4b636e070a38bce737",
-      // TODO(Bug 1338897): Stop inserting CRLFs every 64 characters.
-      expectedBase64: "Fkt6e/z4GeLjlfvnO1bgo4e9ZCIugx/WECcM1+olBVSXWL91wFqZSm0DT2X48Ob9\r\nyuqxo01Ka0tjbgcKOLznNw==",
+      expectedBase64: "Fkt6e/z4GeLjlfvnO1bgo4e9ZCIugx/WECcM1+olBVSXWL91wFqZSm0DT2X48Ob9yuqxo01Ka0tjbgcKOLznNw==",
     },
   ];
 
   for (let vector of vectors) {
     let digest = getHMAC(dataTestVector, keyTestVector, vector.algoID, false);
     equal(hexify(digest), vector.expectedDigest,
           `Actual and expected ${vector.algoName} digests should match`);
     let b64Digest = getHMAC(dataTestVector, keyTestVector, vector.algoID, true);
--- a/security/manager/ssl/tests/unit/tlsserver/cmd/GenerateOCSPResponse.cpp
+++ b/security/manager/ssl/tests/unit/tlsserver/cmd/GenerateOCSPResponse.cpp
@@ -10,17 +10,16 @@
  */
 
 #include <stdio.h>
 #include <string>
 #include <vector>
 
 #include "mozilla/ArrayUtils.h"
 
-#include "base64.h"
 #include "cert.h"
 #include "nspr.h"
 #include "nss.h"
 #include "plarenas.h"
 #include "prerror.h"
 #include "ssl.h"
 #include "secerr.h"
 
--- a/security/nss.symbols
+++ b/security/nss.symbols
@@ -11,17 +11,16 @@
 # excluded on Windows, but it doesn't matter because the symbols are already
 # exported in NSPR (Windows peculiarity).
 PR_*
 PL_*
 #endif
 #include ../db/sqlite3/src/sqlite.symbols
 ATOB_AsciiToData
 ATOB_AsciiToData_Util
-ATOB_ConvertAsciiToItem
 ATOB_ConvertAsciiToItem_Util
 BTOA_ConvertItemToAscii_Util
 BTOA_DataToAscii
 BTOA_DataToAscii_Util
 CERT_AddCertToListHead
 CERT_AddCertToListTail
 CERT_AddExtension
 CERT_AddExtensionByOID
@@ -175,17 +174,16 @@ HASH_Begin
 HASH_Create
 HASH_Destroy
 HASH_End
 HASH_GetHashObject
 HASH_GetType
 HASH_HashBuf
 HASH_ResultLenByOidTag
 HASH_Update
-NSSBase64_DecodeBuffer
 NSSBase64_EncodeItem_Util
 NSS_CMSContentInfo_GetContent
 NSS_CMSContentInfo_SetContent_Data
 NSS_CMSContentInfo_SetContent_EnvelopedData
 NSS_CMSContentInfo_SetContent_SignedData
 NSS_CMSDecoder_Cancel
 NSS_CMSDecoder_Finish
 NSS_CMSDecoder_Start
@@ -504,17 +502,16 @@ SEC_GetSignatureAlgorithmOidTag
 SEC_IA5StringTemplate @DATA@
 SEC_IA5StringTemplate_Util @DATA@
 SEC_IntegerTemplate @DATA@
 SEC_IntegerTemplate_Util @DATA@
 SECITEM_AllocArray
 SECITEM_AllocItem
 SECITEM_AllocItem_Util
 SECITEM_ArenaDupItem_Util
-SECITEM_CompareItem
 SECITEM_CompareItem_Util
 SECITEM_CopyItem
 SECITEM_CopyItem_Util
 SECITEM_DupArray
 SECITEM_DupItem
 SECITEM_DupItem_Util
 SECITEM_FreeItem
 SECITEM_FreeItem_Util