File nss-RSA_signature_forgery.patch of Package mozilla-nss

Fix for RSA signature forging bug
MFSA-2014-73/CVE-2014-1568 (bmo#1064636, bmo#1069405)
bnc#897890

diff --git a/lib/cryptohi/secvfy.c b/lib/cryptohi/secvfy.c
--- a/lib/cryptohi/secvfy.c
+++ b/lib/cryptohi/secvfy.c
@@ -7,121 +7,165 @@
 
 #include <stdio.h>
 #include "cryptohi.h"
 #include "sechash.h"
 #include "keyhi.h"
 #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;
+    SGNDigestInfo* di = NULL;
+    SECItem it;
+    PRBool rv = SECSuccess;
 
-    if (key == NULL) goto loser;
+    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);
+    }
+    
+    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;
+        }
+    }
 
-    di = SGN_DecodeDigestInfo(&it);
-    if (di == NULL) goto sigloser;
+    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;
+        }
+    }
 
-    /*
-    ** 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;
+    if (rv == SECSuccess) {
+        *digestAlgOut = SECOID_GetAlgorithmTag(&di->digestAlgorithm);
+        if (*digestAlgOut == SEC_OID_UNKNOWN) {
+            rv = SECFailure;
+        }
     }
-    /* make sure the "parameters" are not too bogus. */
-    if (di->digestAlgorithm.parameters.len > 2) {
-	goto sigloser;
+
+    if (di) {
+        SGN_DestroyDigestInfo(di);
     }
-    if (di->digest.len > maxdigestlen) {
-	PORT_SetError(SEC_ERROR_OUTPUT_LEN);
-	goto loser;
+
+    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);
     }
-    PORT_Memcpy(digest, di->digest.data, di->digest.len);
-    *tagp = tag;
-    *digestlen = di->digest.len;
-    goto done;
 
-  sigloser:
-    PORT_SetError(SEC_ERROR_BAD_SIGNATURE);
-
-  loser:
-    rv = SECFailure;
-
-  done:
-    if (di   != NULL) SGN_DestroyDigestInfo(di);
-    if (buf  != NULL) PORT_Free(buf);
-    
     return rv;
 }
 
-
 struct VFYContextStr {
     SECOidTag hashAlg;  /* the hash algorithm */
     SECKEYPublicKey *key;
     /*
      * This buffer holds either the digest or the full signature
      * depending on the type of the signature (key->keyType).  It is
      * defined as a union to make sure it always has enough space.
      *
      * Use the "buffer" union member to reference the buffer.
      * Note: do not take the size of the "buffer" union member.  Take
      * the size of the union or some other union member instead.
      */
     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;
     SECOidTag encAlg;  /* enc alg */
     PRBool hasSignature;  /* true if the signature was provided in the
                            * VFY_CreateContext call.  If false, the
                            * signature must be provided with a
                            * 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
  * to by dsig and has enough room for len bytes.
  */
 static SECStatus
 decodeECorDSASignature(SECOidTag algid, const SECItem *sig, unsigned char *dsig,
 		       unsigned int len) {
@@ -371,26 +415,26 @@ vfy_CreateContext(const SECKEYPublicKey 
 	goto loser;
     }
 
     cx->wincx = wincx;
     cx->hasSignature = (sig != NULL);
     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:
 	    sigLen = SECKEY_SignatureLen(key);
 	    if (sigLen == 0) {
 		/* error set by SECKEY_SignatureLen */
 		rv = SECFailure;	
 		break;
@@ -464,16 +508,19 @@ VFY_DestroyContext(VFYContext *cx, PRBoo
     if (cx) {
 	if (cx->hashcx != NULL) {
 	    (*cx->hashobj->destroy)(cx->hashcx, PR_TRUE);
 	    cx->hashcx = NULL;
 	}
 	if (cx->key) {
 	    SECKEY_DestroyPublicKey(cx->key);
 	}
+    if (cx->pkcs1RSADigestInfo) {
+        PORT_Free(cx->pkcs1RSADigestInfo);
+    }
 	if (freeit) {
 	    PORT_ZFree(cx, sizeof(VFYContext));
 	}
     }
 }
 
 SECStatus
 VFY_Begin(VFYContext *cx)
@@ -543,31 +590,35 @@ VFY_EndWithSignature(VFYContext *cx, SEC
 	hash.data = final;
 	hash.len = part;
 	if (PK11_Verify(cx->key,&dsasig,&hash,cx->wincx) != SECSuccess) {
 		PORT_SetError(SEC_ERROR_BAD_SIGNATURE);
 		return SECFailure;
 	}
 	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 */
     }
     return SECSuccess;
 }
 
 SECStatus
@@ -590,22 +641,17 @@ vfy_VerifyDigest(const SECItem *digest, 
     SECItem dsasig; /* also used for ECDSA */
 
     rv = SECFailure;
 
     cx = vfy_CreateContext(key, sig, encAlg, hashAlg, NULL, wincx);
     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:
 	    dsasig.data = cx->u.buffer;
 	    dsasig.len = SECKEY_SignatureLen(cx->key);
 	    if (dsasig.len == 0) {
 		break;
 	    }
diff --git a/lib/softoken/pkcs11c.c b/lib/softoken/pkcs11c.c
--- a/lib/softoken/pkcs11c.c
+++ b/lib/softoken/pkcs11c.c
@@ -18,16 +18,17 @@
  *   that created or generated them.
  */
 #include "seccomon.h"
 #include "secitem.h"
 #include "secport.h"
 #include "blapi.h"
 #include "pkcs11.h"
 #include "pkcs11i.h"
+#include "pkcs1sig.h"
 #include "lowkeyi.h"
 #include "secder.h"
 #include "secdig.h"
 #include "lowpbe.h"	/* We do PBE below */
 #include "pkcs11t.h"
 #include "secoid.h"
 #include "alghmac.h"
 #include "softoken.h"
@@ -2851,75 +2852,52 @@ sftk_hashCheckSign(SFTKHashVerifyInfo *i
         return SECFailure;
     }
 
     return RSA_HashCheckSign(info->hashOid, info->key, sig, sigLen, digest,
                              digestLen);
 }
 
 SECStatus
-RSA_HashCheckSign(SECOidTag hashOid, NSSLOWKEYPublicKey *key,
+RSA_HashCheckSign(SECOidTag digestOid, NSSLOWKEYPublicKey *key,
                   const unsigned char *sig, unsigned int sigLen,
-                  const unsigned char *hash, unsigned int hashLen)
+                  const unsigned char *digestData, unsigned int digestLen)
 {
-    SECItem it;
-    SGNDigestInfo *di = NULL;
-    SECStatus rv = SECSuccess;
-
-    it.data = NULL;
-    it.len = nsslowkey_PublicModulusLen(key);
-    if (!it.len) {
-        goto loser;
-    }
-
-    it.data = (unsigned char *)PORT_Alloc(it.len);
-    if (it.data == NULL) {
-        goto loser;
-    }
-
+    unsigned char *pkcs1DigestInfoData;
+    SECItem pkcs1DigestInfo;
+    SECItem digest;
+    unsigned int bufferSize;
+    SECStatus rv;
+
+    /* 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;
+    }
+
+    pkcs1DigestInfo.data = pkcs1DigestInfoData;
+    pkcs1DigestInfo.len = bufferSize;
+    
     /* decrypt the block */
-    rv = RSA_CheckSignRecover(&key->u.rsa, it.data, &it.len, it.len, sig,
-                              sigLen);
+    rv = RSA_CheckSignRecover(&key->u.rsa, pkcs1DigestInfo.data,
+                             &pkcs1DigestInfo.len, pkcs1DigestInfo.len,
+                             sig, sigLen);
     if (rv != SECSuccess) {
-        goto loser;
-    }
-
-    di = SGN_DecodeDigestInfo(&it);
-    if (di == NULL) {
-        goto loser;
-    }
-    if (di->digest.len != hashLen) {
-        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(hash, di->digest.data, di->digest.len) == 0) {
-        goto done;
-    }
-
-  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_SetError(SEC_ERROR_BAD_SIGNATURE);
+    } else {
+        digest.data = (PRUint8*) digestData;
+        digest.len = digestLen;
+        rv = _SGN_VerifyPKCS1DigestInfo(
+                digestOid, &digest, &pkcs1DigestInfo,
+                PR_TRUE /*XXX: unsafeAllowMissingParameters*/);
+    }
+
+    PORT_Free(pkcs1DigestInfoData);
     return rv;
 }
 
 static SECStatus
 sftk_RSACheckSign(NSSLOWKEYPublicKey *key, const unsigned char *sig,
                   unsigned int sigLen, const unsigned char *digest,
                   unsigned int digestLen)
 {
diff --git a/lib/util/manifest.mn b/lib/util/manifest.mn
--- a/lib/util/manifest.mn
+++ b/lib/util/manifest.mn
@@ -17,16 +17,17 @@ EXPORTS = \
 	nssrwlkt.h \
 	nssutil.h \
 	pkcs11.h \
 	pkcs11f.h \
 	pkcs11p.h \
 	pkcs11t.h \
 	pkcs11n.h \
 	pkcs11u.h \
+	pkcs1sig.h \
 	portreg.h \
 	secasn1.h \
 	secasn1t.h \
 	seccomon.h \
 	secder.h \
 	secdert.h \
 	secdig.h \
 	secdigt.h \
@@ -53,16 +54,17 @@ CSRCS = \
 	dersubr.c \
 	dertime.c \
 	errstrs.c \
 	nssb64d.c \
 	nssb64e.c \
 	nssrwlk.c \
 	nssilock.c \
 	oidstring.c \
+	pkcs1sig.c \
 	portreg.c \
 	secalgid.c \
 	secasn1d.c \
 	secasn1e.c \
 	secasn1u.c \
 	secitem.c \
 	secload.c \
 	secoid.c \
diff --git a/lib/util/nssutil.def b/lib/util/nssutil.def
--- a/lib/util/nssutil.def
+++ b/lib/util/nssutil.def
@@ -266,8 +266,14 @@ NSSUTIL_QuoteSize;
 SECITEM_AllocArray;
 SECITEM_DupArray;
 SECITEM_FreeArray;
 SECITEM_ReallocItemV2;
 SECITEM_ZfreeArray;
 ;+    local:
 ;+       *;
 ;+};
+;+NSSUTIL_3.17.1 {         # NSS Utilities 3.17.1 release
+;+    global:
+_SGN_VerifyPKCS1DigestInfo;
+;+    local:
+;+       *;
+;+};
diff --git a/lib/util/pkcs1sig.c b/lib/util/pkcs1sig.c
new file mode 100644
--- /dev/null
+++ b/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/lib/util/pkcs1sig.h b/lib/util/pkcs1sig.h
new file mode 100644
--- /dev/null
+++ b/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_ */
openSUSE Build Service is sponsored by