--- /dev/null
+nss: CVE-2014-1568
+
+the patch comes from:
+http://web.nvd.nist.gov/view/vuln/detail?vulnId=CVE-2014-1568
+https://bugzilla.mozilla.org/show_bug.cgi?id=1064636
+nss ng log:
+=====
+changeset: 11252:ad411fb64046
+user: Kai Engert <kaie@kuix.de>
+date: Tue Sep 23 19:28:34 2014 +0200
+summary: Fix bug 1064636, patch part 2, r=rrelyea
+=====
+changeset: 11253:4e90910ad2f9
+user: Kai Engert <kaie@kuix.de>
+date: Tue Sep 23 19:28:45 2014 +0200
+summary: Fix bug 1064636, patch part 3, r=rrelyea
+=====
+changeset: 11254:fb7208e91ae8
+user: Kai Engert <kaie@kuix.de>
+date: Tue Sep 23 19:28:52 2014 +0200
+summary: Fix bug 1064636, patch part 1, r=rrelyea
+=====
+changeset: 11255:8dd6c6ac977d
+user: Kai Engert <kaie@kuix.de>
+date: Tue Sep 23 19:39:40 2014 +0200
+summary: Bug 1064636, follow up commit to fix Windows build bustage
+
+Upstream-Status: Backport
+Signed-off-by: Li Wang <li.wang@windriver.com>
+---
+ nss/lib/cryptohi/secvfy.c | 202 +++++++++++++++++++++++++++-----------------
+ nss/lib/softoken/pkcs11c.c | 69 +++++++--------
+ nss/lib/util/manifest.mn | 2 +
+ nss/lib/util/nssutil.def | 6 ++
+ nss/lib/util/pkcs1sig.c | 169 ++++++++++++++++++++++++++++++++++++
+ nss/lib/util/pkcs1sig.h | 30 +++++++
+ 6 files changed, 360 insertions(+), 118 deletions(-)
+ create mode 100644 nss/lib/util/pkcs1sig.c
+ create mode 100644 nss/lib/util/pkcs1sig.h
+
+diff --git a/nss/lib/cryptohi/secvfy.c b/nss/lib/cryptohi/secvfy.c
+index c1ac39b..0a20672 100644
+--- a/nss/lib/cryptohi/secvfy.c
++++ b/nss/lib/cryptohi/secvfy.c
+@@ -12,78 +12,111 @@
+ #include "secasn1.h"
+ #include "secoid.h"
+ #include "pk11func.h"
++#include "pkcs1sig.h"
+ #include "secdig.h"
+ #include "secerr.h"
+ #include "keyi.h"
+
+ /*
+-** Decrypt signature block using public key
+-** Store the hash algorithm oid tag in *tagp
+-** Store the digest in the digest buffer
+-** Store the digest length in *digestlen
++** Recover the DigestInfo from an RSA PKCS#1 signature.
++**
++** If givenDigestAlg != SEC_OID_UNKNOWN, copy givenDigestAlg to digestAlgOut.
++** Otherwise, parse the DigestInfo structure and store the decoded digest
++** algorithm into digestAlgOut.
++**
++** Store the encoded DigestInfo into digestInfo.
++** Store the DigestInfo length into digestInfoLen.
++**
++** This function does *not* verify that the AlgorithmIdentifier in the
++** DigestInfo identifies givenDigestAlg or that the DigestInfo is encoded
++** correctly; verifyPKCS1DigestInfo does that.
++**
+ ** XXX this is assuming that the signature algorithm has WITH_RSA_ENCRYPTION
+ */
+ static SECStatus
+-DecryptSigBlock(SECOidTag *tagp, unsigned char *digest,
+- unsigned int *digestlen, unsigned int maxdigestlen,
+- SECKEYPublicKey *key, const SECItem *sig, char *wincx)
++recoverPKCS1DigestInfo(SECOidTag givenDigestAlg,
++ /*out*/ SECOidTag* digestAlgOut,
++ /*out*/ unsigned char** digestInfo,
++ /*out*/ unsigned int* digestInfoLen,
++ SECKEYPublicKey* key,
++ const SECItem* sig, void* wincx)
+ {
+- SGNDigestInfo *di = NULL;
+- unsigned char *buf = NULL;
+- SECStatus rv;
+- SECOidTag tag;
+- SECItem it;
+-
+- if (key == NULL) goto loser;
+-
++ SGNDigestInfo* di = NULL;
++ SECItem it;
++ PRBool rv = SECSuccess;
++
++ PORT_Assert(digestAlgOut);
++ PORT_Assert(digestInfo);
++ PORT_Assert(digestInfoLen);
++ PORT_Assert(key);
++ PORT_Assert(key->keyType == rsaKey);
++ PORT_Assert(sig);
++
++ it.data = NULL;
+ it.len = SECKEY_PublicKeyStrength(key);
+- if (!it.len) goto loser;
+- it.data = buf = (unsigned char *)PORT_Alloc(it.len);
+- if (!buf) goto loser;
++ if (it.len != 0) {
++ it.data = (unsigned char *)PORT_Alloc(it.len);
++ }
++ if (it.len == 0 || it.data == NULL ) {
++ rv = SECFailure;
++ }
+
+- /* decrypt the block */
+- rv = PK11_VerifyRecover(key, (SECItem *)sig, &it, wincx);
+- if (rv != SECSuccess) goto loser;
++ if (rv == SECSuccess) {
++ /* decrypt the block */
++ rv = PK11_VerifyRecover(key, sig, &it, wincx);
++ }
+
+- di = SGN_DecodeDigestInfo(&it);
+- if (di == NULL) goto sigloser;
++ if (rv == SECSuccess) {
++ if (givenDigestAlg != SEC_OID_UNKNOWN) {
++ /* We don't need to parse the DigestInfo if the caller gave us the
++ * digest algorithm to use. Later verifyPKCS1DigestInfo will verify
++ * that the DigestInfo identifies the given digest algorithm and
++ * that the DigestInfo is encoded absolutely correctly.
++ */
++ *digestInfoLen = it.len;
++ *digestInfo = (unsigned char*)it.data;
++ *digestAlgOut = givenDigestAlg;
++ return SECSuccess;
++ }
++ }
+
+- /*
+- ** Finally we have the digest info; now we can extract the algorithm
+- ** ID and the signature block
+- */
+- tag = SECOID_GetAlgorithmTag(&di->digestAlgorithm);
+- /* Check that tag is an appropriate algorithm */
+- if (tag == SEC_OID_UNKNOWN) {
+- goto sigloser;
+- }
+- /* make sure the "parameters" are not too bogus. */
+- if (di->digestAlgorithm.parameters.len > 2) {
+- goto sigloser;
+- }
+- if (di->digest.len > maxdigestlen) {
+- PORT_SetError(SEC_ERROR_OUTPUT_LEN);
+- goto loser;
++ if (rv == SECSuccess) {
++ /* The caller didn't specify a digest algorithm to use, so choose the
++ * digest algorithm by parsing the AlgorithmIdentifier within the
++ * DigestInfo.
++ */
++ di = SGN_DecodeDigestInfo(&it);
++ if (!di) {
++ rv = SECFailure;
++ }
+ }
+- PORT_Memcpy(digest, di->digest.data, di->digest.len);
+- *tagp = tag;
+- *digestlen = di->digest.len;
+- goto done;
+
+- sigloser:
+- PORT_SetError(SEC_ERROR_BAD_SIGNATURE);
++ if (rv == SECSuccess) {
++ *digestAlgOut = SECOID_GetAlgorithmTag(&di->digestAlgorithm);
++ if (*digestAlgOut == SEC_OID_UNKNOWN) {
++ rv = SECFailure;
++ }
++ }
+
+- loser:
+- rv = SECFailure;
++ if (di) {
++ SGN_DestroyDigestInfo(di);
++ }
++
++ if (rv == SECSuccess) {
++ *digestInfoLen = it.len;
++ *digestInfo = (unsigned char*)it.data;
++ } else {
++ if (it.data) {
++ PORT_Free(it.data);
++ }
++ *digestInfo = NULL;
++ *digestInfoLen = 0;
++ PORT_SetError(SEC_ERROR_BAD_SIGNATURE);
++ }
+
+- done:
+- if (di != NULL) SGN_DestroyDigestInfo(di);
+- if (buf != NULL) PORT_Free(buf);
+-
+ return rv;
+ }
+
+-
+ struct VFYContextStr {
+ SECOidTag hashAlg; /* the hash algorithm */
+ SECKEYPublicKey *key;
+@@ -99,14 +132,14 @@ struct VFYContextStr {
+ union {
+ unsigned char buffer[1];
+
+- /* the digest in the decrypted RSA signature */
+- unsigned char rsadigest[HASH_LENGTH_MAX];
+ /* the full DSA signature... 40 bytes */
+ unsigned char dsasig[DSA_MAX_SIGNATURE_LEN];
+ /* the full ECDSA signature */
+ unsigned char ecdsasig[2 * MAX_ECKEY_LEN];
+ } u;
+- unsigned int rsadigestlen;
++ unsigned int pkcs1RSADigestInfoLen;
++ /* the encoded DigestInfo from a RSA PKCS#1 signature */
++ unsigned char *pkcs1RSADigestInfo;
+ void * wincx;
+ void *hashcx;
+ const SECHashObject *hashobj;
+@@ -117,6 +150,17 @@ struct VFYContextStr {
+ * VFY_EndWithSignature call. */
+ };
+
++static SECStatus
++verifyPKCS1DigestInfo(const VFYContext* cx, const SECItem* digest)
++{
++ SECItem pkcs1DigestInfo;
++ pkcs1DigestInfo.data = cx->pkcs1RSADigestInfo;
++ pkcs1DigestInfo.len = cx->pkcs1RSADigestInfoLen;
++ return _SGN_VerifyPKCS1DigestInfo(
++ cx->hashAlg, digest, &pkcs1DigestInfo,
++ PR_TRUE /*XXX: unsafeAllowMissingParameters*/);
++}
++
+ /*
+ * decode the ECDSA or DSA signature from it's DER wrapping.
+ * The unwrapped/raw signature is placed in the buffer pointed
+@@ -376,16 +420,16 @@ vfy_CreateContext(const SECKEYPublicKey *key, const SECItem *sig,
+ cx->encAlg = encAlg;
+ cx->hashAlg = hashAlg;
+ cx->key = SECKEY_CopyPublicKey(key);
++ cx->pkcs1RSADigestInfo = NULL;
+ rv = SECSuccess;
+ if (sig) {
+ switch (type) {
+ case rsaKey:
+- rv = DecryptSigBlock(&cx->hashAlg, cx->u.buffer, &cx->rsadigestlen,
+- HASH_LENGTH_MAX, cx->key, sig, (char*)wincx);
+- if (cx->hashAlg != hashAlg && hashAlg != SEC_OID_UNKNOWN) {
+- PORT_SetError(SEC_ERROR_BAD_SIGNATURE);
+- rv = SECFailure;
+- }
++ rv = recoverPKCS1DigestInfo(hashAlg, &cx->hashAlg,
++ &cx->pkcs1RSADigestInfo,
++ &cx->pkcs1RSADigestInfoLen,
++ cx->key,
++ sig, wincx);
+ break;
+ case dsaKey:
+ case ecKey:
+@@ -469,6 +513,9 @@ VFY_DestroyContext(VFYContext *cx, PRBool freeit)
+ if (cx->key) {
+ SECKEY_DestroyPublicKey(cx->key);
+ }
++ if (cx->pkcs1RSADigestInfo) {
++ PORT_Free(cx->pkcs1RSADigestInfo);
++ }
+ if (freeit) {
+ PORT_ZFree(cx, sizeof(VFYContext));
+ }
+@@ -548,21 +595,25 @@ VFY_EndWithSignature(VFYContext *cx, SECItem *sig)
+ }
+ break;
+ case rsaKey:
++ {
++ SECItem digest;
++ digest.data = final;
++ digest.len = part;
+ if (sig) {
+- SECOidTag hashid = SEC_OID_UNKNOWN;
+- rv = DecryptSigBlock(&hashid, cx->u.buffer, &cx->rsadigestlen,
+- HASH_LENGTH_MAX, cx->key, sig, (char*)cx->wincx);
+- if ((rv != SECSuccess) || (hashid != cx->hashAlg)) {
+- PORT_SetError(SEC_ERROR_BAD_SIGNATURE);
++ SECOidTag hashid;
++ PORT_Assert(cx->hashAlg != SEC_OID_UNKNOWN);
++ rv = recoverPKCS1DigestInfo(cx->hashAlg, &hashid,
++ &cx->pkcs1RSADigestInfo,
++ &cx->pkcs1RSADigestInfoLen,
++ cx->key,
++ sig, cx->wincx);
++ PORT_Assert(cx->hashAlg == hashid);
++ if (rv != SECSuccess) {
+ return SECFailure;
+ }
+ }
+- if ((part != cx->rsadigestlen) ||
+- PORT_Memcmp(final, cx->u.buffer, part)) {
+- PORT_SetError(SEC_ERROR_BAD_SIGNATURE);
+- return SECFailure;
+- }
+- break;
++ return verifyPKCS1DigestInfo(cx, &digest);
++ }
+ default:
+ PORT_SetError(SEC_ERROR_BAD_SIGNATURE);
+ return SECFailure; /* shouldn't happen */
+@@ -595,12 +646,7 @@ vfy_VerifyDigest(const SECItem *digest, const SECKEYPublicKey *key,
+ if (cx != NULL) {
+ switch (key->keyType) {
+ case rsaKey:
+- if ((digest->len != cx->rsadigestlen) ||
+- PORT_Memcmp(digest->data, cx->u.buffer, digest->len)) {
+- PORT_SetError(SEC_ERROR_BAD_SIGNATURE);
+- } else {
+- rv = SECSuccess;
+- }
++ rv = verifyPKCS1DigestInfo(cx, digest);
+ break;
+ case dsaKey:
+ case ecKey:
+diff --git a/nss/lib/softoken/pkcs11c.c b/nss/lib/softoken/pkcs11c.c
+index 89b5bd8..ba6dcfa 100644
+--- a/nss/lib/softoken/pkcs11c.c
++++ b/nss/lib/softoken/pkcs11c.c
+@@ -23,6 +23,7 @@
+ #include "blapi.h"
+ #include "pkcs11.h"
+ #include "pkcs11i.h"
++#include "pkcs1sig.h"
+ #include "lowkeyi.h"
+ #include "secder.h"
+ #include "secdig.h"
+@@ -2580,54 +2581,42 @@ sftk_hashCheckSign(SFTKHashVerifyInfo *info, unsigned char *sig,
+ }
+
+ SECStatus
+-RSA_HashCheckSign(SECOidTag hashOid, NSSLOWKEYPublicKey *key,
++RSA_HashCheckSign(SECOidTag digestOid, NSSLOWKEYPublicKey *key,
+ unsigned char *sig, unsigned int sigLen,
+- unsigned char *digest, unsigned int digestLen)
++ unsigned char *digestData, unsigned int digestLen)
+ {
++ unsigned char *pkcs1DigestInfoData;
++ SECItem pkcs1DigestInfo;
++ SECItem digest;
++ unsigned int bufferSize;
++ SECStatus rv;
+
+- SECItem it;
+- SGNDigestInfo *di = NULL;
+- SECStatus rv = SECSuccess;
+-
+- it.data = NULL;
+-
+- if (key == NULL) goto loser;
+-
+- it.len = nsslowkey_PublicModulusLen(key);
+- if (!it.len) goto loser;
++ /* pkcs1DigestInfo.data must be less than key->u.rsa.modulus.len */
++ bufferSize = key->u.rsa.modulus.len;
++ pkcs1DigestInfoData = PORT_ZAlloc(bufferSize);
++ if (!pkcs1DigestInfoData) {
++ PORT_SetError(SEC_ERROR_NO_MEMORY);
++ return SECFailure;
++ }
+
+- it.data = (unsigned char *) PORT_Alloc(it.len);
+- if (it.data == NULL) goto loser;
++ pkcs1DigestInfo.data = pkcs1DigestInfoData;
++ pkcs1DigestInfo.len = bufferSize;
+
+ /* decrypt the block */
+- rv = RSA_CheckSignRecover(key, it.data, &it.len, it.len, sig, sigLen);
+- if (rv != SECSuccess) goto loser;
+-
+- di = SGN_DecodeDigestInfo(&it);
+- if (di == NULL) goto loser;
+- if (di->digest.len != digestLen) goto loser;
+-
+- /* make sure the tag is OK */
+- if (SECOID_GetAlgorithmTag(&di->digestAlgorithm) != hashOid) {
+- goto loser;
+- }
+- /* make sure the "parameters" are not too bogus. */
+- if (di->digestAlgorithm.parameters.len > 2) {
+- goto loser;
+- }
+- /* Now check the signature */
+- if (PORT_Memcmp(digest, di->digest.data, di->digest.len) == 0) {
+- goto done;
++ rv = RSA_CheckSignRecover(key, pkcs1DigestInfo.data,
++ &pkcs1DigestInfo.len, pkcs1DigestInfo.len,
++ sig, sigLen);
++ if (rv != SECSuccess) {
++ PORT_SetError(SEC_ERROR_BAD_SIGNATURE);
++ } else {
++ digest.data = (PRUint8*) digestData;
++ digest.len = digestLen;
++ rv = _SGN_VerifyPKCS1DigestInfo(
++ digestOid, &digest, &pkcs1DigestInfo,
++ PR_TRUE /*XXX: unsafeAllowMissingParameters*/);
+ }
+
+- loser:
+- PORT_SetError(SEC_ERROR_BAD_SIGNATURE);
+- rv = SECFailure;
+-
+- done:
+- if (it.data != NULL) PORT_Free(it.data);
+- if (di != NULL) SGN_DestroyDigestInfo(di);
+-
++ PORT_Free(pkcs1DigestInfoData);
+ return rv;
+ }
+
+diff --git a/nss/lib/util/manifest.mn b/nss/lib/util/manifest.mn
+index ed54a16..9ff3758 100644
+--- a/nss/lib/util/manifest.mn
++++ b/nss/lib/util/manifest.mn
+@@ -22,6 +22,7 @@ EXPORTS = \
+ pkcs11t.h \
+ pkcs11n.h \
+ pkcs11u.h \
++ pkcs1sig.h \
+ portreg.h \
+ secasn1.h \
+ secasn1t.h \
+@@ -58,6 +59,7 @@ CSRCS = \
+ nssrwlk.c \
+ nssilock.c \
+ oidstring.c \
++ pkcs1sig.c \
+ portreg.c \
+ secalgid.c \
+ secasn1d.c \
+diff --git a/nss/lib/util/nssutil.def b/nss/lib/util/nssutil.def
+index 86a0ad7..9d98df2 100644
+--- a/nss/lib/util/nssutil.def
++++ b/nss/lib/util/nssutil.def
+@@ -271,3 +271,9 @@ SECITEM_ZfreeArray;
+ ;+ local:
+ ;+ *;
+ ;+};
++;+NSSUTIL_3.17.1 { # NSS Utilities 3.17.1 release
++;+ global:
++_SGN_VerifyPKCS1DigestInfo;
++;+ local:
++;+ *;
++;+};
+diff --git a/nss/lib/util/pkcs1sig.c b/nss/lib/util/pkcs1sig.c
+new file mode 100644
+index 0000000..03b16f5
+--- /dev/null
++++ b/nss/lib/util/pkcs1sig.c
+@@ -0,0 +1,169 @@
++/* 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 "pkcs1sig.h"
++#include "hasht.h"
++#include "secerr.h"
++#include "secasn1t.h"
++#include "secoid.h"
++
++typedef struct pkcs1PrefixStr pkcs1Prefix;
++struct pkcs1PrefixStr {
++ unsigned int len;
++ PRUint8 *data;
++};
++
++typedef struct pkcs1PrefixesStr pkcs1Prefixes;
++struct pkcs1PrefixesStr {
++ unsigned int digestLen;
++ pkcs1Prefix prefixWithParams;
++ pkcs1Prefix prefixWithoutParams;
++};
++
++/* The value for SGN_PKCS1_DIGESTINFO_MAX_PREFIX_LEN_EXCLUDING_OID is based on
++ * the possible prefix encodings as explained below.
++ */
++#define MAX_PREFIX_LEN_EXCLUDING_OID 10
++
++static SECStatus
++encodePrefix(const SECOidData *hashOid, unsigned int digestLen,
++ pkcs1Prefix *prefix, PRBool withParams)
++{
++ /* with params coding is:
++ * Sequence (2 bytes) {
++ * Sequence (2 bytes) {
++ * Oid (2 bytes) {
++ * Oid value (derOid->oid.len)
++ * }
++ * NULL (2 bytes)
++ * }
++ * OCTECT (2 bytes);
++ *
++ * without params coding is:
++ * Sequence (2 bytes) {
++ * Sequence (2 bytes) {
++ * Oid (2 bytes) {
++ * Oid value (derOid->oid.len)
++ * }
++ * }
++ * OCTECT (2 bytes);
++ */
++
++ unsigned int innerSeqLen = 2 + hashOid->oid.len;
++ unsigned int outerSeqLen = 2 + innerSeqLen + 2 + digestLen;
++ unsigned int extra = 0;
++
++ if (withParams) {
++ innerSeqLen += 2;
++ outerSeqLen += 2;
++ extra = 2;
++ }
++
++ if (innerSeqLen >= 128 ||
++ outerSeqLen >= 128 ||
++ (outerSeqLen + 2 - digestLen) >
++ (MAX_PREFIX_LEN_EXCLUDING_OID + hashOid->oid.len)) {
++ /* this is actually a library failure, It shouldn't happen */
++ PORT_SetError(SEC_ERROR_INVALID_ARGS);
++ return SECFailure;
++ }
++
++ prefix->len = 6 + hashOid->oid.len + extra + 2;
++ prefix->data = PORT_Alloc(prefix->len);
++ if (!prefix->data) {
++ PORT_SetError(SEC_ERROR_NO_MEMORY);
++ return SECFailure;
++ }
++
++ prefix->data[0] = SEC_ASN1_SEQUENCE|SEC_ASN1_CONSTRUCTED;
++ prefix->data[1] = outerSeqLen;
++ prefix->data[2] = SEC_ASN1_SEQUENCE|SEC_ASN1_CONSTRUCTED;
++ prefix->data[3] = innerSeqLen;
++ prefix->data[4] = SEC_ASN1_OBJECT_ID;
++ prefix->data[5] = hashOid->oid.len;
++ PORT_Memcpy(&prefix->data[6], hashOid->oid.data, hashOid->oid.len);
++ if (withParams) {
++ prefix->data[6 + hashOid->oid.len] = SEC_ASN1_NULL;
++ prefix->data[6 + hashOid->oid.len + 1] = 0;
++ }
++ prefix->data[6 + hashOid->oid.len + extra] = SEC_ASN1_OCTET_STRING;
++ prefix->data[6 + hashOid->oid.len + extra + 1] = digestLen;
++
++ return SECSuccess;
++}
++
++SECStatus
++_SGN_VerifyPKCS1DigestInfo(SECOidTag digestAlg,
++ const SECItem* digest,
++ const SECItem* dataRecoveredFromSignature,
++ PRBool unsafeAllowMissingParameters)
++{
++ SECOidData *hashOid;
++ pkcs1Prefixes pp;
++ const pkcs1Prefix* expectedPrefix;
++ SECStatus rv, rv2, rv3;
++
++ if (!digest || !digest->data ||
++ !dataRecoveredFromSignature || !dataRecoveredFromSignature->data) {
++ PORT_SetError(SEC_ERROR_INVALID_ARGS);
++ return SECFailure;
++ }
++
++ hashOid = SECOID_FindOIDByTag(digestAlg);
++ if (hashOid == NULL) {
++ PORT_SetError(SEC_ERROR_INVALID_ARGS);
++ return SECFailure;
++ }
++
++ pp.digestLen = digest->len;
++ pp.prefixWithParams.data = NULL;
++ pp.prefixWithoutParams.data = NULL;
++
++ rv2 = encodePrefix(hashOid, pp.digestLen, &pp.prefixWithParams, PR_TRUE);
++ rv3 = encodePrefix(hashOid, pp.digestLen, &pp.prefixWithoutParams, PR_FALSE);
++
++ rv = SECSuccess;
++ if (rv2 != SECSuccess || rv3 != SECSuccess) {
++ rv = SECFailure;
++ }
++
++ if (rv == SECSuccess) {
++ /* We don't attempt to avoid timing attacks on these comparisons because
++ * signature verification is a public key operation, not a private key
++ * operation.
++ */
++
++ if (dataRecoveredFromSignature->len ==
++ pp.prefixWithParams.len + pp.digestLen) {
++ expectedPrefix = &pp.prefixWithParams;
++ } else if (unsafeAllowMissingParameters &&
++ dataRecoveredFromSignature->len ==
++ pp.prefixWithoutParams.len + pp.digestLen) {
++ expectedPrefix = &pp.prefixWithoutParams;
++ } else {
++ PORT_SetError(SEC_ERROR_BAD_SIGNATURE);
++ rv = SECFailure;
++ }
++ }
++
++ if (rv == SECSuccess) {
++ if (memcmp(dataRecoveredFromSignature->data, expectedPrefix->data,
++ expectedPrefix->len) ||
++ memcmp(dataRecoveredFromSignature->data + expectedPrefix->len,
++ digest->data, digest->len)) {
++ PORT_SetError(SEC_ERROR_BAD_SIGNATURE);
++ rv = SECFailure;
++ }
++ }
++
++ if (pp.prefixWithParams.data) {
++ PORT_Free(pp.prefixWithParams.data);
++ }
++ if (pp.prefixWithoutParams.data) {
++ PORT_Free(pp.prefixWithoutParams.data);
++ }
++
++ return rv;
++}
+diff --git a/nss/lib/util/pkcs1sig.h b/nss/lib/util/pkcs1sig.h
+new file mode 100644
+index 0000000..7c52b15
+--- /dev/null
++++ b/nss/lib/util/pkcs1sig.h
+@@ -0,0 +1,30 @@
++/* 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/.
++ */
++
++#ifndef _PKCS1SIG_H_
++#define _PKCS1SIG_H_
++
++#include "hasht.h"
++#include "seccomon.h"
++#include "secoidt.h"
++
++/* SGN_VerifyPKCS1DigestInfo verifies that the length of the digest is correct
++ * for the given algorithm, then verifies that the recovered data from the
++ * PKCS#1 signature is a properly-formatted DigestInfo that identifies the
++ * given digest algorithm, then verifies that the digest in the DigestInfo
++ * matches the given digest.
++ *
++ * dataRecoveredFromSignature must be the result of calling PK11_VerifyRecover
++ * or equivalent.
++ *
++ * If unsafeAllowMissingParameters is true (not recommended), then a DigestInfo
++ * without the mandatory ASN.1 NULL parameter will also be accepted.
++ */
++SECStatus _SGN_VerifyPKCS1DigestInfo(SECOidTag digestAlg,
++ const SECItem* digest,
++ const SECItem* dataRecoveredFromSignature,
++ PRBool unsafeAllowMissingParameters);
++
++#endif /* _PKCS1SIG_H_ */
+--
+1.7.9.5