Blob Blame History Raw

# HG changeset patch
# User Kai Engert <kaie@kuix.de>
# Date 1411493314 -7200
# Node ID ad411fb64046d987272043f311ca477022c6fef4
# Parent  70ae6afde27f9c977badc5271efa835c8a4ec4f0
Fix bug 1064636, patch part 2, r=rrelyea

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);
+	    PORT_Assert(cx->hashAlg != SEC_OID_UNKNOWN);
+	    SECOidTag hashid;
+	    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;
 	    }