580f11
580f11
# HG changeset patch
580f11
# User Kai Engert <kaie@kuix.de>
580f11
# Date 1411493314 -7200
580f11
# Node ID ad411fb64046d987272043f311ca477022c6fef4
580f11
# Parent  70ae6afde27f9c977badc5271efa835c8a4ec4f0
580f11
Fix bug 1064636, patch part 2, r=rrelyea
580f11
580f11
diff --git a/lib/cryptohi/secvfy.c b/lib/cryptohi/secvfy.c
580f11
--- a/lib/cryptohi/secvfy.c
580f11
+++ b/lib/cryptohi/secvfy.c
580f11
@@ -7,121 +7,165 @@
580f11
 
580f11
 #include <stdio.h>
580f11
 #include "cryptohi.h"
580f11
 #include "sechash.h"
580f11
 #include "keyhi.h"
580f11
 #include "secasn1.h"
580f11
 #include "secoid.h"
580f11
 #include "pk11func.h"
580f11
+#include "pkcs1sig.h"
580f11
 #include "secdig.h"
580f11
 #include "secerr.h"
580f11
 #include "keyi.h"
580f11
 
580f11
 /*
580f11
-** Decrypt signature block using public key
580f11
-** Store the hash algorithm oid tag in *tagp
580f11
-** Store the digest in the digest buffer
580f11
-** Store the digest length in *digestlen
580f11
+** Recover the DigestInfo from an RSA PKCS#1 signature.
580f11
+**
580f11
+** If givenDigestAlg != SEC_OID_UNKNOWN, copy givenDigestAlg to digestAlgOut.
580f11
+** Otherwise, parse the DigestInfo structure and store the decoded digest
580f11
+** algorithm into digestAlgOut.
580f11
+**
580f11
+** Store the encoded DigestInfo into digestInfo.
580f11
+** Store the DigestInfo length into digestInfoLen.
580f11
+**
580f11
+** This function does *not* verify that the AlgorithmIdentifier in the
580f11
+** DigestInfo identifies givenDigestAlg or that the DigestInfo is encoded
580f11
+** correctly; verifyPKCS1DigestInfo does that.
580f11
+**
580f11
 ** XXX this is assuming that the signature algorithm has WITH_RSA_ENCRYPTION
580f11
 */
580f11
 static SECStatus
580f11
-DecryptSigBlock(SECOidTag *tagp, unsigned char *digest,
580f11
-		unsigned int *digestlen, unsigned int maxdigestlen,
580f11
-		SECKEYPublicKey *key, const SECItem *sig, char *wincx)
580f11
+recoverPKCS1DigestInfo(SECOidTag givenDigestAlg,
580f11
+                       /*out*/ SECOidTag* digestAlgOut,
580f11
+                       /*out*/ unsigned char** digestInfo,
580f11
+                       /*out*/ unsigned int* digestInfoLen,
580f11
+                       SECKEYPublicKey* key,
580f11
+                       const SECItem* sig, void* wincx)
580f11
 {
580f11
-    SGNDigestInfo *di   = NULL;
580f11
-    unsigned char *buf  = NULL;
580f11
-    SECStatus      rv;
580f11
-    SECOidTag      tag;
580f11
-    SECItem        it;
580f11
+    SGNDigestInfo* di = NULL;
580f11
+    SECItem it;
580f11
+    PRBool rv = SECSuccess;
580f11
 
580f11
-    if (key == NULL) goto loser;
580f11
+    PORT_Assert(digestAlgOut);
580f11
+    PORT_Assert(digestInfo);
580f11
+    PORT_Assert(digestInfoLen);
580f11
+    PORT_Assert(key);
580f11
+    PORT_Assert(key->keyType == rsaKey);
580f11
+    PORT_Assert(sig);
580f11
 
580f11
+    it.data = NULL;
580f11
     it.len  = SECKEY_PublicKeyStrength(key);
580f11
-    if (!it.len) goto loser;
580f11
-    it.data = buf = (unsigned char *)PORT_Alloc(it.len);
580f11
-    if (!buf) goto loser;
580f11
+    if (it.len != 0) {
580f11
+        it.data = (unsigned char *)PORT_Alloc(it.len);
580f11
+    }
580f11
+    if (it.len == 0 || it.data == NULL ) {
580f11
+        rv = SECFailure;
580f11
+    }
580f11
 
580f11
-    /* decrypt the block */
580f11
-    rv = PK11_VerifyRecover(key, (SECItem *)sig, &it, wincx);
580f11
-    if (rv != SECSuccess) goto loser;
580f11
+    if (rv == SECSuccess) {
580f11
+        /* decrypt the block */
580f11
+        rv = PK11_VerifyRecover(key, sig, &it, wincx);
580f11
+    }
580f11
+    
580f11
+    if (rv == SECSuccess) {
580f11
+        if (givenDigestAlg != SEC_OID_UNKNOWN) {
580f11
+            /* We don't need to parse the DigestInfo if the caller gave us the
580f11
+             * digest algorithm to use. Later verifyPKCS1DigestInfo will verify
580f11
+             * that the DigestInfo identifies the given digest algorithm and
580f11
+             * that the DigestInfo is encoded absolutely correctly.
580f11
+             */
580f11
+            *digestInfoLen = it.len;
580f11
+            *digestInfo = (unsigned char*)it.data;
580f11
+            *digestAlgOut = givenDigestAlg;
580f11
+            return SECSuccess;
580f11
+        }
580f11
+    }
580f11
 
580f11
-    di = SGN_DecodeDigestInfo(&it);
580f11
-    if (di == NULL) goto sigloser;
580f11
+    if (rv == SECSuccess) {
580f11
+        /* The caller didn't specify a digest algorithm to use, so choose the
580f11
+         * digest algorithm by parsing the AlgorithmIdentifier within the
580f11
+         * DigestInfo.
580f11
+         */
580f11
+        di = SGN_DecodeDigestInfo(&it);
580f11
+        if (!di) {
580f11
+            rv = SECFailure;
580f11
+        }
580f11
+    }
580f11
 
580f11
-    /*
580f11
-    ** Finally we have the digest info; now we can extract the algorithm
580f11
-    ** ID and the signature block
580f11
-    */
580f11
-    tag = SECOID_GetAlgorithmTag(&di->digestAlgorithm);
580f11
-    /* Check that tag is an appropriate algorithm */
580f11
-    if (tag == SEC_OID_UNKNOWN) {
580f11
-	goto sigloser;
580f11
+    if (rv == SECSuccess) {
580f11
+        *digestAlgOut = SECOID_GetAlgorithmTag(&di->digestAlgorithm);
580f11
+        if (*digestAlgOut == SEC_OID_UNKNOWN) {
580f11
+            rv = SECFailure;
580f11
+        }
580f11
     }
580f11
-    /* make sure the "parameters" are not too bogus. */
580f11
-    if (di->digestAlgorithm.parameters.len > 2) {
580f11
-	goto sigloser;
580f11
+
580f11
+    if (di) {
580f11
+        SGN_DestroyDigestInfo(di);
580f11
     }
580f11
-    if (di->digest.len > maxdigestlen) {
580f11
-	PORT_SetError(SEC_ERROR_OUTPUT_LEN);
580f11
-	goto loser;
580f11
+
580f11
+    if (rv == SECSuccess) {
580f11
+        *digestInfoLen = it.len;
580f11
+        *digestInfo = (unsigned char*)it.data;
580f11
+    } else {
580f11
+        if (it.data) {
580f11
+            PORT_Free(it.data);
580f11
+        }
580f11
+        *digestInfo = NULL;
580f11
+        *digestInfoLen = 0;
580f11
+        PORT_SetError(SEC_ERROR_BAD_SIGNATURE);
580f11
     }
580f11
-    PORT_Memcpy(digest, di->digest.data, di->digest.len);
580f11
-    *tagp = tag;
580f11
-    *digestlen = di->digest.len;
580f11
-    goto done;
580f11
 
580f11
-  sigloser:
580f11
-    PORT_SetError(SEC_ERROR_BAD_SIGNATURE);
580f11
-
580f11
-  loser:
580f11
-    rv = SECFailure;
580f11
-
580f11
-  done:
580f11
-    if (di   != NULL) SGN_DestroyDigestInfo(di);
580f11
-    if (buf  != NULL) PORT_Free(buf);
580f11
-    
580f11
     return rv;
580f11
 }
580f11
 
580f11
-
580f11
 struct VFYContextStr {
580f11
     SECOidTag hashAlg;  /* the hash algorithm */
580f11
     SECKEYPublicKey *key;
580f11
     /*
580f11
      * This buffer holds either the digest or the full signature
580f11
      * depending on the type of the signature (key->keyType).  It is
580f11
      * defined as a union to make sure it always has enough space.
580f11
      *
580f11
      * Use the "buffer" union member to reference the buffer.
580f11
      * Note: do not take the size of the "buffer" union member.  Take
580f11
      * the size of the union or some other union member instead.
580f11
      */
580f11
     union {
580f11
 	unsigned char buffer[1];
580f11
 
580f11
-	/* the digest in the decrypted RSA signature */
580f11
-	unsigned char rsadigest[HASH_LENGTH_MAX];
580f11
 	/* the full DSA signature... 40 bytes */
580f11
 	unsigned char dsasig[DSA_MAX_SIGNATURE_LEN];
580f11
 	/* the full ECDSA signature */
580f11
 	unsigned char ecdsasig[2 * MAX_ECKEY_LEN];
580f11
     } u;
580f11
-    unsigned int rsadigestlen;
580f11
+    unsigned int pkcs1RSADigestInfoLen;
580f11
+    /* the encoded DigestInfo from a RSA PKCS#1 signature */
580f11
+    unsigned char *pkcs1RSADigestInfo;
580f11
     void * wincx;
580f11
     void *hashcx;
580f11
     const SECHashObject *hashobj;
580f11
     SECOidTag encAlg;  /* enc alg */
580f11
     PRBool hasSignature;  /* true if the signature was provided in the
580f11
                            * VFY_CreateContext call.  If false, the
580f11
                            * signature must be provided with a
580f11
                            * VFY_EndWithSignature call. */
580f11
 };
580f11
 
580f11
+static SECStatus
580f11
+verifyPKCS1DigestInfo(const VFYContext* cx, const SECItem* digest)
580f11
+{
580f11
+  SECItem pkcs1DigestInfo;
580f11
+  pkcs1DigestInfo.data = cx->pkcs1RSADigestInfo;
580f11
+  pkcs1DigestInfo.len = cx->pkcs1RSADigestInfoLen;
580f11
+  return _SGN_VerifyPKCS1DigestInfo(
580f11
+           cx->hashAlg, digest, &pkcs1DigestInfo,
580f11
+           PR_TRUE /*XXX: unsafeAllowMissingParameters*/);
580f11
+}
580f11
+
580f11
 /*
580f11
  * decode the ECDSA or DSA signature from it's DER wrapping.
580f11
  * The unwrapped/raw signature is placed in the buffer pointed
580f11
  * to by dsig and has enough room for len bytes.
580f11
  */
580f11
 static SECStatus
580f11
 decodeECorDSASignature(SECOidTag algid, const SECItem *sig, unsigned char *dsig,
580f11
 		       unsigned int len) {
580f11
@@ -371,26 +415,26 @@ vfy_CreateContext(const SECKEYPublicKey 
580f11
 	goto loser;
580f11
     }
580f11
 
580f11
     cx->wincx = wincx;
580f11
     cx->hasSignature = (sig != NULL);
580f11
     cx->encAlg = encAlg;
580f11
     cx->hashAlg = hashAlg;
580f11
     cx->key = SECKEY_CopyPublicKey(key);
580f11
+    cx->pkcs1RSADigestInfo = NULL;
580f11
     rv = SECSuccess;
580f11
     if (sig) {
580f11
 	switch (type) {
580f11
 	case rsaKey:
580f11
-	    rv = DecryptSigBlock(&cx->hashAlg, cx->u.buffer, &cx->rsadigestlen,
580f11
-			HASH_LENGTH_MAX, cx->key, sig, (char*)wincx);
580f11
-	    if (cx->hashAlg != hashAlg && hashAlg != SEC_OID_UNKNOWN) {
580f11
-		PORT_SetError(SEC_ERROR_BAD_SIGNATURE);
580f11
-		rv = SECFailure;	
580f11
-	    }
580f11
+	    rv = recoverPKCS1DigestInfo(hashAlg, &cx->hashAlg,
580f11
+					&cx->pkcs1RSADigestInfo,
580f11
+					&cx->pkcs1RSADigestInfoLen,
580f11
+					cx->key,
580f11
+					sig, wincx);
580f11
 	    break;
580f11
 	case dsaKey:
580f11
 	case ecKey:
580f11
 	    sigLen = SECKEY_SignatureLen(key);
580f11
 	    if (sigLen == 0) {
580f11
 		/* error set by SECKEY_SignatureLen */
580f11
 		rv = SECFailure;	
580f11
 		break;
580f11
@@ -464,16 +508,19 @@ VFY_DestroyContext(VFYContext *cx, PRBoo
580f11
     if (cx) {
580f11
 	if (cx->hashcx != NULL) {
580f11
 	    (*cx->hashobj->destroy)(cx->hashcx, PR_TRUE);
580f11
 	    cx->hashcx = NULL;
580f11
 	}
580f11
 	if (cx->key) {
580f11
 	    SECKEY_DestroyPublicKey(cx->key);
580f11
 	}
580f11
+    if (cx->pkcs1RSADigestInfo) {
580f11
+        PORT_Free(cx->pkcs1RSADigestInfo);
580f11
+    }
580f11
 	if (freeit) {
580f11
 	    PORT_ZFree(cx, sizeof(VFYContext));
580f11
 	}
580f11
     }
580f11
 }
580f11
 
580f11
 SECStatus
580f11
 VFY_Begin(VFYContext *cx)
580f11
@@ -543,31 +590,35 @@ VFY_EndWithSignature(VFYContext *cx, SEC
580f11
 	hash.data = final;
580f11
 	hash.len = part;
580f11
 	if (PK11_Verify(cx->key,&dsasig,&hash,cx->wincx) != SECSuccess) {
580f11
 		PORT_SetError(SEC_ERROR_BAD_SIGNATURE);
580f11
 		return SECFailure;
580f11
 	}
580f11
 	break;
580f11
       case rsaKey:
580f11
+      {
580f11
+        SECItem digest;
580f11
+        digest.data = final;
580f11
+        digest.len = part;
580f11
 	if (sig) {
580f11
-	    SECOidTag hashid = SEC_OID_UNKNOWN;
580f11
-	    rv = DecryptSigBlock(&hashid, cx->u.buffer, &cx->rsadigestlen,
580f11
-		    HASH_LENGTH_MAX, cx->key, sig, (char*)cx->wincx);
580f11
-	    if ((rv != SECSuccess) || (hashid != cx->hashAlg)) {
580f11
-		PORT_SetError(SEC_ERROR_BAD_SIGNATURE);
580f11
+	    PORT_Assert(cx->hashAlg != SEC_OID_UNKNOWN);
580f11
+	    SECOidTag hashid;
580f11
+	    rv = recoverPKCS1DigestInfo(cx->hashAlg, &hashid,
580f11
+					&cx->pkcs1RSADigestInfo,
580f11
+					&cx->pkcs1RSADigestInfoLen,
580f11
+					cx->key,
580f11
+					sig, cx->wincx);
580f11
+	    PORT_Assert(cx->hashAlg == hashid);
580f11
+	    if (rv != SECSuccess) {
580f11
 		return SECFailure;
580f11
 	    }
580f11
 	}
580f11
-	if ((part != cx->rsadigestlen) ||
580f11
-	    PORT_Memcmp(final, cx->u.buffer, part)) {
580f11
-	    PORT_SetError(SEC_ERROR_BAD_SIGNATURE);
580f11
-	    return SECFailure;
580f11
-	}
580f11
-	break;
580f11
+	return verifyPKCS1DigestInfo(cx, &digest);
580f11
+      }
580f11
       default:
580f11
 	PORT_SetError(SEC_ERROR_BAD_SIGNATURE);
580f11
 	return SECFailure; /* shouldn't happen */
580f11
     }
580f11
     return SECSuccess;
580f11
 }
580f11
 
580f11
 SECStatus
580f11
@@ -590,22 +641,17 @@ vfy_VerifyDigest(const SECItem *digest, 
580f11
     SECItem dsasig; /* also used for ECDSA */
580f11
 
580f11
     rv = SECFailure;
580f11
 
580f11
     cx = vfy_CreateContext(key, sig, encAlg, hashAlg, NULL, wincx);
580f11
     if (cx != NULL) {
580f11
 	switch (key->keyType) {
580f11
 	case rsaKey:
580f11
-	    if ((digest->len != cx->rsadigestlen) ||
580f11
-		PORT_Memcmp(digest->data, cx->u.buffer, digest->len)) {
580f11
-		PORT_SetError(SEC_ERROR_BAD_SIGNATURE);
580f11
-	    } else {
580f11
-		rv = SECSuccess;
580f11
-	    }
580f11
+	    rv = verifyPKCS1DigestInfo(cx, digest);
580f11
 	    break;
580f11
 	case dsaKey:
580f11
 	case ecKey:
580f11
 	    dsasig.data = cx->u.buffer;
580f11
 	    dsasig.len = SECKEY_SignatureLen(cx->key);
580f11
 	    if (dsasig.len == 0) {
580f11
 		break;
580f11
 	    }
580f11