Fix various certificate fingerprint issues.
authorDr. Stephen Henson <steve@openssl.org>
Sat, 20 Dec 2014 15:09:50 +0000 (15:09 +0000)
committerDr. Stephen Henson <steve@openssl.org>
Mon, 5 Jan 2015 14:35:19 +0000 (14:35 +0000)
By using non-DER or invalid encodings outside the signed portion of a
certificate the fingerprint can be changed without breaking the signature.
Although no details of the signed portion of the certificate can be changed
this can cause problems with some applications: e.g. those using the
certificate fingerprint for blacklists.

1. Reject signatures with non zero unused bits.

If the BIT STRING containing the signature has non zero unused bits reject
the signature. All current signature algorithms require zero unused bits.

2. Check certificate algorithm consistency.

Check the AlgorithmIdentifier inside TBS matches the one in the
certificate signature. NB: this will result in signature failure
errors for some broken certificates.

3. Check DSA/ECDSA signatures use DER.

Reencode DSA/ECDSA signatures and compare with the original received
signature. Return an error if there is a mismatch.

This will reject various cases including garbage after signature
(thanks to Antti Karjalainen and Tuomo Untinen from the Codenomicon CROSS
program for discovering this case) and use of BER or invalid ASN.1 INTEGERs
(negative or with leading zeroes).

CVE-2014-8275
Reviewed-by: Emilia Käsper <emilia@openssl.org>
CHANGES
crypto/asn1/a_verify.c
crypto/dsa/dsa_asn1.c
crypto/ecdsa/ecs_vrf.c
crypto/x509/x_all.c

diff --git a/CHANGES b/CHANGES
index 0f37df74ccab7c45a56a71e98472ff18340c971e..c076df8f2ec39fe6e54336bf886ec8e7d518c332 100644 (file)
--- a/CHANGES
+++ b/CHANGES
 
  Changes between 1.0.1j and 1.0.1k [xx XXX xxxx]
 
+  *) Fix various certificate fingerprint issues.
+
+     By using non-DER or invalid encodings outside the signed portion of a
+     certificate the fingerprint can be changed without breaking the signature.
+     Although no details of the signed portion of the certificate can be changed
+     this can cause problems with some applications: e.g. those using the
+     certificate fingerprint for blacklists.
+
+     1. Reject signatures with non zero unused bits.
+
+     If the BIT STRING containing the signature has non zero unused bits reject
+     the signature. All current signature algorithms require zero unused bits.
+
+     2. Check certificate algorithm consistency.
+
+     Check the AlgorithmIdentifier inside TBS matches the one in the
+     certificate signature. NB: this will result in signature failure
+     errors for some broken certificates.
+
+     Thanks to Konrad Kraszewski from Google for reporting this issue.
+
+     3. Check DSA/ECDSA signatures use DER.
+
+     Reencode DSA/ECDSA signatures and compare with the original received
+     signature. Return an error if there is a mismatch.
+
+     This will reject various cases including garbage after signature
+     (thanks to Antti Karjalainen and Tuomo Untinen from the Codenomicon CROSS
+     program for discovering this case) and use of BER or invalid ASN.1 INTEGERs
+     (negative or with leading zeroes).
+
+     Further analysis was conducted and fixes were developed by Stephen Henson
+     of the OpenSSL core team.
+
+     (CVE-2014-8275)
+     [Steve Henson]
+
    *) Do not resume sessions on the server if the negotiated protocol
       version does not match the session's version. Resuming with a different
       version, while not strictly forbidden by the RFC, is of questionable
index aacf4763b585c267dc5762f0c57742f3dba06eec..fdeeef6761e65fdd2b3c28deea66a52f4233354d 100644 (file)
@@ -90,6 +90,12 @@ int ASN1_verify(i2d_of_void *i2d, X509_ALGOR *a, ASN1_BIT_STRING *signature,
                ASN1err(ASN1_F_ASN1_VERIFY,ASN1_R_UNKNOWN_MESSAGE_DIGEST_ALGORITHM);
                goto err;
                }
+
+       if (signature->type == V_ASN1_BIT_STRING && signature->flags & 0x7)
+               {
+               ASN1err(ASN1_F_ASN1_VERIFY, ASN1_R_INVALID_BIT_STRING_BITS_LEFT);
+               goto err;
+               }
        
        inl=i2d(data,NULL);
        buf_in=OPENSSL_malloc((unsigned int)inl);
@@ -150,6 +156,12 @@ int ASN1_item_verify(const ASN1_ITEM *it, X509_ALGOR *a,
                return -1;
                }
 
+       if (signature->type == V_ASN1_BIT_STRING && signature->flags & 0x7)
+               {
+               ASN1err(ASN1_F_ASN1_VERIFY, ASN1_R_INVALID_BIT_STRING_BITS_LEFT);
+               return -1;
+               }
+
        EVP_MD_CTX_init(&ctx);
 
        /* Convert signature OID into digest and public key OIDs */
index 55c75b59cfbc9cc27715795c18709350aac9903f..58559e54b1ece20eab0a04fdf475ac139e1fa65a 100644 (file)
@@ -177,13 +177,25 @@ int DSA_verify(int type, const unsigned char *dgst, int dgst_len,
             const unsigned char *sigbuf, int siglen, DSA *dsa)
        {
        DSA_SIG *s;
+       const unsigned char *p = sigbuf;
+       unsigned char *der = NULL;
+       int derlen = -1;
        int ret=-1;
 
        s = DSA_SIG_new();
        if (s == NULL) return(ret);
-       if (d2i_DSA_SIG(&s,&sigbuf,siglen) == NULL) goto err;
+       if (d2i_DSA_SIG(&s,&p,siglen) == NULL) goto err;
+       /* Ensure signature uses DER and doesn't have trailing garbage */
+       derlen = i2d_DSA_SIG(s, &der);
+       if (derlen != siglen || memcmp(sigbuf, der, derlen))
+               goto err;
        ret=DSA_do_verify(dgst,dgst_len,s,dsa);
 err:
+       if (derlen > 0)
+               {
+               OPENSSL_cleanse(der, derlen);
+               OPENSSL_free(der);
+               }
        DSA_SIG_free(s);
        return(ret);
        }
index ae14625e45295f7e72e16fddb524a0de887639dc..7191b8ab0aea2c3631c1504fdc810a7364d98d56 100644 (file)
@@ -57,6 +57,7 @@
  */
 
 #include "ecs_locl.h"
+#include "cryptlib.h"
 #ifndef OPENSSL_NO_ENGINE
 #include <openssl/engine.h>
 #endif
@@ -86,13 +87,25 @@ int ECDSA_verify(int type, const unsigned char *dgst, int dgst_len,
                const unsigned char *sigbuf, int sig_len, EC_KEY *eckey)
        {
        ECDSA_SIG *s;
+       const unsigned char *p = sigbuf;
+       unsigned char *der = NULL;
+       int derlen = -1;
        int ret=-1;
 
        s = ECDSA_SIG_new();
        if (s == NULL) return(ret);
-       if (d2i_ECDSA_SIG(&s, &sigbuf, sig_len) == NULL) goto err;
+       if (d2i_ECDSA_SIG(&s, &p, sig_len) == NULL) goto err;
+       /* Ensure signature uses DER and doesn't have trailing garbage */
+       derlen = i2d_ECDSA_SIG(s, &der);
+       if (derlen != sig_len || memcmp(sigbuf, der, derlen))
+               goto err;
        ret=ECDSA_do_verify(dgst, dgst_len, s, eckey);
 err:
+       if (derlen > 0)
+               {
+               OPENSSL_cleanse(der, derlen);
+               OPENSSL_free(der);
+               }
        ECDSA_SIG_free(s);
        return(ret);
        }
index b2223ce93b3d03409ccab8ac4e7e0a07fa722e47..d7229506f644e8b52b2203da88909622d1d2417a 100644 (file)
@@ -73,6 +73,8 @@
 
 int X509_verify(X509 *a, EVP_PKEY *r)
        {
+       if (X509_ALGOR_cmp(a->sig_alg, a->cert_info->signature))
+               return 0;
        return(ASN1_item_verify(ASN1_ITEM_rptr(X509_CINF),a->sig_alg,
                a->signature,a->cert_info,r));
        }