Browse Source

Merge pull request #451 from libtom/pr/wycheproof-gcm

Wycheproof failing GCM test - invalid/modified tag
karel-m 7 years ago
parent
commit
62cd87342e

+ 6 - 0
doc/crypt.tex

@@ -2305,6 +2305,9 @@ int gcm_memory(
 This will initialize the GCM state with the given key, IV and AAD value then proceed to encrypt or decrypt the message text and store the final
 This will initialize the GCM state with the given key, IV and AAD value then proceed to encrypt or decrypt the message text and store the final
 message tag.  The definition of the variables is the same as it is for all the manual functions.
 message tag.  The definition of the variables is the same as it is for all the manual functions.
 
 
+IMPORTANT NOTICE: for \textit{direction == GCM\_DECRYPT} the caller has to fill \textit{tag} and \textit{taglen} with expected
+tag value. The \textit{gcm\_memory} in decrypt mode validates the \textit{tag} value and returns \textit{CRYPT\_ERROR} on mismatch.
+
 If you are processing many packets under the same key you shouldn't use this function as it invokes the pre--computation with each call.
 If you are processing many packets under the same key you shouldn't use this function as it invokes the pre--computation with each call.
 
 
 \subsection{Example Usage}
 \subsection{Example Usage}
@@ -2515,6 +2518,9 @@ encrypt (\textit{direction} equals \textbf{CHACHA20POLY1305\_ENCRYPT}) or decryp
 \textbf{CHACHA20POLY1305\_DECRYPT}) the message text and store the final message tag. The definition of the
 \textbf{CHACHA20POLY1305\_DECRYPT}) the message text and store the final message tag. The definition of the
 variables is the same as it is for all the manual functions.
 variables is the same as it is for all the manual functions.
 
 
+IMPORTANT NOTICE: for \textit{direction == CHACHA20POLY1305\_DECRYPT} the caller has to fill \textit{tag} and \textit{taglen} with expected
+tag value. The \textit{chacha20poly1305\_memory} in decrypt mode validates the \textit{tag} value and returns \textit{CRYPT\_ERROR} on mismatch.
+
 \chapter{One-Way Cryptographic Hash Functions}
 \chapter{One-Way Cryptographic Hash Functions}
 \mysection{Core Functions}
 \mysection{Core Functions}
 Like the ciphers, there are hash core functions and a universal data type to hold the hash state called \textit{hash\_state}.  To initialize hash
 Like the ciphers, there are hash core functions and a universal data type to hold the hash state called \textit{hash\_state}.  To initialize hash

+ 0 - 1
src/encauth/chachapoly/chacha20poly1305_decrypt.c

@@ -25,7 +25,6 @@ int chacha20poly1305_decrypt(chacha20poly1305_state *st, const unsigned char *in
    unsigned long padlen;
    unsigned long padlen;
    int err;
    int err;
 
 
-   if (inlen == 0) return CRYPT_OK; /* nothing to do */
    LTC_ARGCHK(st != NULL);
    LTC_ARGCHK(st != NULL);
 
 
    if (st->aadflg) {
    if (st->aadflg) {

+ 0 - 1
src/encauth/chachapoly/chacha20poly1305_encrypt.c

@@ -25,7 +25,6 @@ int chacha20poly1305_encrypt(chacha20poly1305_state *st, const unsigned char *in
    unsigned long padlen;
    unsigned long padlen;
    int err;
    int err;
 
 
-   if (inlen == 0) return CRYPT_OK; /* nothing to do */
    LTC_ARGCHK(st != NULL);
    LTC_ARGCHK(st != NULL);
 
 
    if ((err = chacha_crypt(&st->chacha, in, inlen, out)) != CRYPT_OK)         return err;
    if ((err = chacha_crypt(&st->chacha, in, inlen, out)) != CRYPT_OK)         return err;

+ 9 - 1
src/encauth/chachapoly/chacha20poly1305_memory.c

@@ -43,6 +43,7 @@ int chacha20poly1305_memory(const unsigned char *key, unsigned long keylen,
    LTC_ARGCHK(in  != NULL);
    LTC_ARGCHK(in  != NULL);
    LTC_ARGCHK(out != NULL);
    LTC_ARGCHK(out != NULL);
    LTC_ARGCHK(tag != NULL);
    LTC_ARGCHK(tag != NULL);
+   LTC_ARGCHK(taglen != NULL);
 
 
    if ((err = chacha20poly1305_init(&st, key, keylen)) != CRYPT_OK)          { goto LBL_ERR; }
    if ((err = chacha20poly1305_init(&st, key, keylen)) != CRYPT_OK)          { goto LBL_ERR; }
    if ((err = chacha20poly1305_setiv(&st, iv, ivlen)) != CRYPT_OK)           { goto LBL_ERR; }
    if ((err = chacha20poly1305_setiv(&st, iv, ivlen)) != CRYPT_OK)           { goto LBL_ERR; }
@@ -51,15 +52,22 @@ int chacha20poly1305_memory(const unsigned char *key, unsigned long keylen,
    }
    }
    if (direction == CHACHA20POLY1305_ENCRYPT) {
    if (direction == CHACHA20POLY1305_ENCRYPT) {
       if ((err = chacha20poly1305_encrypt(&st, in, inlen, out)) != CRYPT_OK) { goto LBL_ERR; }
       if ((err = chacha20poly1305_encrypt(&st, in, inlen, out)) != CRYPT_OK) { goto LBL_ERR; }
+      if ((err = chacha20poly1305_done(&st, tag, taglen)) != CRYPT_OK)       { goto LBL_ERR; }
    }
    }
    else if (direction == CHACHA20POLY1305_DECRYPT) {
    else if (direction == CHACHA20POLY1305_DECRYPT) {
+      unsigned char buf[MAXBLOCKSIZE];
+      unsigned long buflen = sizeof(buf);
       if ((err = chacha20poly1305_decrypt(&st, in, inlen, out)) != CRYPT_OK) { goto LBL_ERR; }
       if ((err = chacha20poly1305_decrypt(&st, in, inlen, out)) != CRYPT_OK) { goto LBL_ERR; }
+      if ((err = chacha20poly1305_done(&st, buf, &buflen)) != CRYPT_OK)      { goto LBL_ERR; }
+      if (buflen != *taglen || XMEM_NEQ(buf, tag, buflen) != 0) {
+         err = CRYPT_ERROR;
+         goto LBL_ERR;
+      }
    }
    }
    else {
    else {
       err = CRYPT_INVALID_ARG;
       err = CRYPT_INVALID_ARG;
       goto LBL_ERR;
       goto LBL_ERR;
    }
    }
-   err = chacha20poly1305_done(&st, tag, taglen);
 LBL_ERR:
 LBL_ERR:
 #ifdef LTC_CLEAN_STACK
 #ifdef LTC_CLEAN_STACK
    zeromem(&st, sizeof(chacha20poly1305_state));
    zeromem(&st, sizeof(chacha20poly1305_state));

+ 36 - 1
src/encauth/chachapoly/chacha20poly1305_test.c

@@ -77,10 +77,10 @@ int chacha20poly1305_test(void)
 
 
    /* chacha20poly1305_memory - decrypt */
    /* chacha20poly1305_memory - decrypt */
    len = sizeof(dmac);
    len = sizeof(dmac);
+   XMEMCPY(dmac, tag, sizeof(tag));
    if ((err = chacha20poly1305_memory(k, sizeof(k), i12, sizeof(i12), aad, sizeof(aad),
    if ((err = chacha20poly1305_memory(k, sizeof(k), i12, sizeof(i12), aad, sizeof(aad),
                                       ct, mlen, pt, dmac, &len, CHACHA20POLY1305_DECRYPT)) != CRYPT_OK) return err;
                                       ct, mlen, pt, dmac, &len, CHACHA20POLY1305_DECRYPT)) != CRYPT_OK) return err;
    if (compare_testvector(pt, mlen, m, mlen, "DEC-PT2", 3) != 0) return CRYPT_FAIL_TESTVECTOR;
    if (compare_testvector(pt, mlen, m, mlen, "DEC-PT2", 3) != 0) return CRYPT_FAIL_TESTVECTOR;
-   if (compare_testvector(dmac, len, tag, sizeof(tag), "DEC-TAG2", 4) != 0) return CRYPT_FAIL_TESTVECTOR;
 
 
    /* encrypt - rfc7905 */
    /* encrypt - rfc7905 */
    if ((err = chacha20poly1305_init(&st1, k, sizeof(k))) != CRYPT_OK) return err;
    if ((err = chacha20poly1305_init(&st1, k, sizeof(k))) != CRYPT_OK) return err;
@@ -123,6 +123,41 @@ int chacha20poly1305_test(void)
    if (compare_testvector(pt, mlen, m, mlen, "DEC-PT4", 1) != 0) return CRYPT_FAIL_TESTVECTOR;
    if (compare_testvector(pt, mlen, m, mlen, "DEC-PT4", 1) != 0) return CRYPT_FAIL_TESTVECTOR;
    if (compare_testvector(dmac, len, emac, len, "DEC-TAG4", 2) != 0) return CRYPT_FAIL_TESTVECTOR;
    if (compare_testvector(dmac, len, emac, len, "DEC-TAG4", 2) != 0) return CRYPT_FAIL_TESTVECTOR;
 
 
+   /* wycheproof failing test - https://github.com/libtom/libtomcrypt/pull/451 */
+   {
+      unsigned char key[] = { 0x00,0x11,0x22,0x33,0x44,0x55,0x66,0x77,0x88,0x99,0xaa,0xbb,0xcc,0xdd,0xee,0xff,
+                              0x00,0x11,0x22,0x33,0x44,0x55,0x66,0x77,0x88,0x99,0xaa,0xbb,0xcc,0xdd,0xee,0xff };
+      unsigned char iv[]  = { 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b };
+      unsigned char valid_tag[]   = { 0xa3,0xe3,0xfd,0xf9,0xfb,0xa6,0x86,0x1b,0x5a,0xd2,0x60,0x7f,0x40,0xb7,0xf4,0x47 };
+      unsigned char invalid_tag[] = { 0xa2,0xe3,0xfd,0xf9,0xfb,0xa6,0x86,0x1b,0x5a,0xd2,0x60,0x7f,0x40,0xb7,0xf4,0x47 };
+      unsigned char waad[] = { 0x61,0x61,0x64 };
+      unsigned char wct[]  = { 0x00 };
+      unsigned char wpt[20] = { 0 };
+      unsigned char wtag[20] = { 0 };
+      unsigned long taglen;
+
+      /* encrypt */
+      taglen = sizeof(wtag);
+      err = chacha20poly1305_memory(key, sizeof(key), iv, sizeof(iv), waad, sizeof(waad),
+                                    wpt, 0, wct, wtag, &taglen, CHACHA20POLY1305_ENCRYPT);
+      if (err != CRYPT_OK) return CRYPT_FAIL_TESTVECTOR;
+      if (compare_testvector(wtag, taglen, valid_tag, sizeof(valid_tag), "WYCH", 1) != 0) return CRYPT_FAIL_TESTVECTOR;
+
+      /* VALID tag */
+      taglen = sizeof(valid_tag);
+      err = chacha20poly1305_memory(key, sizeof(key), iv, sizeof(iv), waad, sizeof(waad),
+                                    wpt, 0, wct, valid_tag, &taglen, CHACHA20POLY1305_DECRYPT);
+      if (err != CRYPT_OK) return CRYPT_FAIL_TESTVECTOR;
+
+      /* INVALID tag */
+      taglen = sizeof(invalid_tag);
+      err = chacha20poly1305_memory(key, sizeof(key), iv, sizeof(iv), waad, sizeof(waad),
+                                    wpt, 0, wct, invalid_tag, &taglen, CHACHA20POLY1305_DECRYPT);
+      if (err == CRYPT_OK) {
+         return CRYPT_FAIL_TESTVECTOR; /* should fail */
+      }
+   }
+
    return CRYPT_OK;
    return CRYPT_OK;
 #endif
 #endif
 }
 }

+ 18 - 1
src/encauth/gcm/gcm_memory.c

@@ -93,7 +93,24 @@ int gcm_memory(      int           cipher,
     if ((err = gcm_process(gcm, pt, ptlen, ct, direction)) != CRYPT_OK) {
     if ((err = gcm_process(gcm, pt, ptlen, ct, direction)) != CRYPT_OK) {
        goto LTC_ERR;
        goto LTC_ERR;
     }
     }
-    err = gcm_done(gcm, tag, taglen);
+    if (direction == GCM_ENCRYPT) {
+      if ((err = gcm_done(gcm, tag, taglen)) != CRYPT_OK) {
+         goto LTC_ERR;
+      }
+    }
+    else if (direction == GCM_DECRYPT) {
+       unsigned char buf[MAXBLOCKSIZE];
+       unsigned long buflen = sizeof(buf);
+       if ((err = gcm_done(gcm, buf, &buflen)) != CRYPT_OK) {
+          goto LTC_ERR;
+       }
+       if (buflen != *taglen || XMEM_NEQ(buf, tag, buflen) != 0) {
+          err = CRYPT_ERROR;
+       }
+    }
+    else {
+       err = CRYPT_INVALID_ARG;
+    }
 LTC_ERR:
 LTC_ERR:
     XFREE(orig);
     XFREE(orig);
     return err;
     return err;

+ 28 - 3
src/encauth/gcm/gcm_test.c

@@ -363,6 +363,7 @@ int gcm_test(void)
        }
        }
 
 
        y = sizeof(T[1]);
        y = sizeof(T[1]);
+       XMEMCPY(T[1], tests[x].T, 16);
        if ((err = gcm_memory(idx, tests[x].K, tests[x].keylen,
        if ((err = gcm_memory(idx, tests[x].K, tests[x].keylen,
                              tests[x].IV, tests[x].IVlen,
                              tests[x].IV, tests[x].IVlen,
                              tests[x].A, tests[x].alen,
                              tests[x].A, tests[x].alen,
@@ -374,12 +375,36 @@ int gcm_test(void)
        if (compare_testvector(out[1], tests[x].ptlen, tests[x].P, tests[x].ptlen, "GCM PT", x)) {
        if (compare_testvector(out[1], tests[x].ptlen, tests[x].P, tests[x].ptlen, "GCM PT", x)) {
           return CRYPT_FAIL_TESTVECTOR;
           return CRYPT_FAIL_TESTVECTOR;
        }
        }
+   }
 
 
-       if (compare_testvector(T[1], y, tests[x].T, 16, "GCM Decrypt Tag", x)) {
-          return CRYPT_FAIL_TESTVECTOR;
-       }
+   /* wycheproof failing test - https://github.com/libtom/libtomcrypt/pull/451 */
+   {
+      unsigned char key[] = { 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f };
+      unsigned char iv[]  = { 0x50,0x51,0x52,0x53,0x54,0x55,0x56,0x57,0x58,0x59,0x5a,0x5b };
+      unsigned char valid_tag[]   = { 0xd8,0x84,0x7d,0xbc,0x32,0x6a,0x06,0xe9,0x88,0xc7,0x7a,0xd3,0x86,0x3e,0x60,0x83 };
+      unsigned char invalid_tag[] = { 0xd9,0x84,0x7d,0xbc,0x32,0x6a,0x06,0xe9,0x88,0xc7,0x7a,0xd3,0x86,0x3e,0x60,0x83 };
+      unsigned char msg[] = { 0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,0x28,0x29,0x2a,0x2b,0x2c,0x2d,0x2e,0x2f };
+      unsigned char ct[]  = { 0xeb,0x15,0x6d,0x08,0x1e,0xd6,0xb6,0xb5,0x5f,0x46,0x12,0xf0,0x21,0xd8,0x7b,0x39 };
+      unsigned char pt[20] = { 0 };
+      unsigned long taglen;
+
+      /* VALID tag */
+      taglen = sizeof(valid_tag);
+      err = gcm_memory(idx, key, sizeof(key), iv, sizeof(iv), NULL, 0,
+                       pt, sizeof(ct), ct, valid_tag, &taglen, GCM_DECRYPT);
+      if ((err != CRYPT_OK) || (XMEMCMP(msg, pt, sizeof(msg)) != 0)) {
+         return CRYPT_FAIL_TESTVECTOR;
+      }
 
 
+      /* INVALID tag */
+      taglen = sizeof(invalid_tag);
+      err = gcm_memory(idx, key, sizeof(key), iv, sizeof(iv), NULL, 0,
+                       pt, sizeof(ct), ct, invalid_tag, &taglen, GCM_DECRYPT);
+      if (err == CRYPT_OK) {
+         return CRYPT_FAIL_TESTVECTOR; /* should fail */
+      }
    }
    }
+
    return CRYPT_OK;
    return CRYPT_OK;
 #endif
 #endif
 }
 }