gnu-crypto-discuss
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [GNU Crypto] Small problem with HMac


From: Casey Marshall
Subject: Re: [GNU Crypto] Small problem with HMac
Date: Thu, 09 Jan 2003 23:29:30 -0800
User-agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.0.1) Gecko/20021130

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

Raif S. Naffah wrote:
| hello Casey,
|
| On Thursday 09 January 2003 23:22, Casey Marshall wrote:
|
|>Hi,
|>
|>It looks like there is a small problem with how HMac is reset...
|>
|>The attached seems to fix this...
|>
|>@@ -180,18 +184,31 @@
|>       underlyingHash.reset();
|>       opadHash = (IMessageDigest) underlyingHash.clone();
|>
|>+      ipad = new byte[blockSize];
|>+
|
| this allocates new byte array _every_ time an hmac is init-ed.

Not when the key is being re-used, since there is a `return' on line 165.

| we only
| need doing that for new keys, and only if the key length is different
| from the previously used initialisation (if there was one).

Acutally, since the size of ipad is equivalent to the block size, it
only need be allocated once, upon the first initialization.

Going through the test case attached, I also noticed that HMac also
doesn't handle keys larger than the block size properly. The revised
diff handles this and fixes the allocation of ipad.

| a test case of a re-use would be nice to have ;-)
|

TestOfHMac.java attached exercises HMAC-MD5 and HMAC-SHA1 as per RFC
2202, and implicitly tests re-use. Two of the tests fail because GNU
Crypto's implementation rejects keys that are shorter than the output
length. Other than those two the tests pass.

Cheers,

- --
Casey Marshall || address@hidden
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.0.7 (GNU/Linux)
Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org

iD8DBQE+HnZagAuWMgRGsWsRAp8RAJ9ggqS0bI0vxTC7llwR++C/Zf/z4gCfZq4+
5utKL6PX839SHNStT3cZP60=
=M0rF
-----END PGP SIGNATURE-----
Index: source/gnu/crypto/mac/HMac.java
===================================================================
RCS file: /cvsroot/gnu-crypto/gnu-crypto/source/gnu/crypto/mac/HMac.java,v
retrieving revision 1.4
diff -u -r1.4 HMac.java
--- source/gnu/crypto/mac/HMac.java     7 Nov 2002 17:17:45 -0000       1.4
+++ source/gnu/crypto/mac/HMac.java     10 Jan 2003 07:04:34 -0000
@@ -3,7 +3,7 @@
 // ----------------------------------------------------------------------------
 // $Id: HMac.java,v 1.4 2002/11/07 17:17:45 raif Exp $
 //
-// Copyright (C) 2001, 2002, Free Software Foundation, Inc.
+// Copyright (C) 2001, 2002, 2003, Free Software Foundation, Inc.
 //
 // This file is part of GNU Crypto.
 //
@@ -104,6 +104,7 @@
    protected int blockSize;
    protected IMessageDigest ipadHash;
    protected IMessageDigest opadHash;
+   protected byte[] ipad;
 
    // Constructor(s)
    // -------------------------------------------------------------------------
@@ -138,6 +139,9 @@
       if (this.opadHash != null) {
          result.opadHash = (IMessageDigest) this.opadHash.clone();
       }
+      if (this.ipad != null) {
+         result.ipad = (byte[]) this.ipad.clone();
+      }
 
       return result;
    }
@@ -167,7 +171,17 @@
 
       if (K.length < macSize) {
          throw new InvalidKeyException("Key too short");
-      } else if (K.length != blockSize) {
+      }
+      
+      if (K.length > blockSize) {
+         // (0) replace K with HASH(K) if K is larger than the hash's
+         //     block size. Then pad with zeros until it is the correct
+         //     size (the next `if').
+         underlyingHash.update(K, 0, K.length);
+         K = underlyingHash.digest();
+      }
+      
+      if (K.length < blockSize) {
          // (1) append zeros to the end of K to create a B byte string
          //     (e.g., if K is of length 20 bytes and B=64, then K will be
          //     appended with 44 zero bytes 0x00)
@@ -180,18 +194,32 @@
       underlyingHash.reset();
       opadHash = (IMessageDigest) underlyingHash.clone();
 
+      if (ipad == null)
+         ipad = new byte[blockSize];
+
       // (2) XOR (bitwise exclusive-OR) the B byte string computed in step
       //     (1) with ipad
       // (3) append the stream of data 'text' to the B byte string resulting
       //     from step (2)
       // (4) apply H to the stream generated in step (3)
       for (int i = 0; i < blockSize; i++) {
-         underlyingHash.update((byte)(K[i] ^ IPAD_BYTE));
+         ipad[i] = (byte)(K[i] ^ IPAD_BYTE);
+      }
+      for (int i = 0; i < blockSize; i++) {
          opadHash.update((byte)(K[i] ^ OPAD_BYTE));
       }
-
+      
+      underlyingHash.update(ipad, 0, blockSize);
       ipadHash = (IMessageDigest) underlyingHash.clone();
       K = null;
+   }
+
+   public void reset() {
+      super.reset();
+      if (ipad != null) {
+         underlyingHash.update(ipad, 0, blockSize);
+         ipadHash = (IMessageDigest) underlyingHash.clone();
+      }
    }
 
    public byte[] digest() {
package gnu.testlet.gnu.crypto.mac;

// --------------------------------------------------------------------------
// $Id$
//
// Copyright (C) 2003, Free Software Foundation, Inc.
//
// This file is part of GNU Crypto.
//
// GNU Crypto is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2, or (at your option)
// any later version.
//
// GNU Crypto is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; see the file COPYING.  If not, write to the
//
//    Free Software Foundation Inc.,
//    59 Temple Place - Suite 330,
//    Boston, MA 02111-1307
//    USA
//
// Linking this library statically or dynamically with other modules is
// making a combined work based on this library.  Thus, the terms and
// conditions of the GNU General Public License cover the whole
// combination.
//
// As a special exception, the copyright holders of this library give
// you permission to link this library with independent modules to
// produce an executable, regardless of the license terms of these
// independent modules, and to copy and distribute the resulting
// executable under terms of your choice, provided that you also meet,
// for each linked independent module, the terms and conditions of the
// license of that module.  An independent module is a module which is
// not derived from or based on this library.  If you modify this
// library, you may extend this exception to your version of the
// library, but you are not obligated to do so.  If you do not wish to
// do so, delete this exception statement from your version.
// 
// --------------------------------------------------------------------------

// Tags: GNU-CRYPTO

import gnu.crypto.mac.IMac;
import gnu.crypto.mac.MacFactory;
import gnu.crypto.util.Util;
import gnu.testlet.TestHarness;
import gnu.testlet.Testlet;
import java.util.HashMap;

/**
 * Conformance tests of the HMAC-MD5 and HMAC-SHA1 message
 * authentication code algorithms.
 *
 * <p>References:</p>
 * <ol>
 * <li>P. Cheng and R. Glenn, <a href="http://www.ietf.org/rfc/rfc2202.txt";>RFC
 * 2202: Test Cases for HMAC-MD5 and HMAC-SHA-1</a>.</li>
 * </ol>
 *
 * @version $Revision$
 */
public class TestOfHMac implements Testlet {

   // Constants and variables.
   // -----------------------------------------------------------------------

   // HMac-MD5 test cases.

   static final byte[][][] HMAC_MD5_VECTORS = {
      { Util.toBytesFromString("0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b"),
        "Hi There".getBytes(),
        Util.toBytesFromString("9294727a3638bb1c13f48ef8158bfc9d") },
      { "Jefe".getBytes(), "what do ya want for nothing?".getBytes(),
        Util.toBytesFromString("750c783e6ab0b503eaa86e310a5db738") },
      { Util.toBytesFromString("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"),
        new byte[50] /* filled in below, 0xDD 50 times */,
        Util.toBytesFromString("56be34521d144c88dbb8c733f0e8b3f6") },
      { 
Util.toBytesFromString("0102030405060708090a0b0c0d0e0f10111213141516171819"),
        new byte[50] /* filled in below, 0xCD 50 times */,
        Util.toBytesFromString("697eaf0aca3a3aea3a75164746ffaa79") },
      { Util.toBytesFromString("0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c"),
        "Test With Truncation".getBytes(),
        Util.toBytesFromString("56461ef2342edc00f9bab995690efd4c") },
      { new byte[80] /* filled in below, 0xAA 80 times */,
        "Test Using Larger Than Block-Size Key - Hash Key First".getBytes(),
        Util.toBytesFromString("6b1ab7fe4bd7bf8f0b62e6ce61b9d0cd") },
      { new byte[80] /* filled in below, 0xAA 80 times */,
        "Test Using Larger Than Block-Size Key and Larger Than One Block-Size 
Data".getBytes(),
        Util.toBytesFromString("6f630fad67cda0ee1fb1f562db3aa53e") }
   };

   // HMac-SHA-1 test cases.

   static final byte[][][] HMAC_SHA1_VECTORS = {
      { Util.toBytesFromString("0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b"),
        "Hi There".getBytes(),
        Util.toBytesFromString("b617318655057264e28bc0b6fb378c8ef146be00") },
      { "Jefe".getBytes(), "what do ya want for nothing?".getBytes(),
        Util.toBytesFromString("effcdf6ae5eb2fa2d27416d5f184df9c259a7c79") },
      { Util.toBytesFromString("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"),
        new byte[50] /* filled in below, 0xDD 50 times */,
        Util.toBytesFromString("125d7342b9ac11cd91a39af48aa17b4f63f175d3") },
      { 
Util.toBytesFromString("0102030405060708090a0b0c0d0e0f10111213141516171819"),
        new byte[50] /* filled in below, 0xDD 50 times */,
        Util.toBytesFromString("4c9007f4026250c6bc8414f9bf50c86c2d7235da") },
      { Util.toBytesFromString("0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c"),
        "Test With Truncation".getBytes(),
        Util.toBytesFromString("4c1a03424b55e07fe7f27be1d58bb9324a9a5a04") },
      { new byte[80] /* filled in below, 0xAA 80 times */,
        "Test Using Larger Than Block-Size Key - Hash Key First".getBytes(),
        Util.toBytesFromString("aa4ae5e15272d00e95705637ce8a3b55ed402112") },
      { new byte[80] /* filled in below, 0xAA 80 times */,
        "Test Using Larger Than Block-Size Key and Larger Than One Block-Size 
Data".getBytes(),
        Util.toBytesFromString("e8e99d0f45237d786d6bbaa7965c7808bbff1a91") }
   };

   static {
      for (int i = 0; i < 50; i++) {
         HMAC_MD5_VECTORS[2][1][i] = (byte) 0xDD;
         HMAC_MD5_VECTORS[3][1][i] = (byte) 0xCD;
         HMAC_SHA1_VECTORS[2][1][i] = (byte) 0xDD;
         HMAC_SHA1_VECTORS[3][1][i] = (byte) 0xCD;
      }
      for (int i = 0; i < 80; i++) {
         HMAC_MD5_VECTORS[5][0][i] = (byte) 0xAA;
         HMAC_MD5_VECTORS[6][0][i] = (byte) 0xAA;
         HMAC_SHA1_VECTORS[5][0][i] = (byte) 0xAA;
         HMAC_SHA1_VECTORS[6][0][i] = (byte) 0xAA;
      }
   }

   // Constructors.
   // -----------------------------------------------------------------------

   // default 0-arguments constructor.

   // Instance methods.
   // -----------------------------------------------------------------------

   public void test(TestHarness harness) {
      testHMacMD5(harness);
      testHMacSHA1(harness);
   }

   // Own methods.
   // -----------------------------------------------------------------------

   /**
    * Check HMAC-MD5.
    *
    * @param harness The test harness.
    */
   private void testHMacMD5(TestHarness harness) {
      IMac mac = MacFactory.getInstance("hmac-md5");
      HashMap attr = new HashMap();
      for (int i = 0; i < HMAC_MD5_VECTORS.length; i++) {
         try {
            attr.put(IMac.MAC_KEY_MATERIAL, HMAC_MD5_VECTORS[i][0]);
            mac.init(attr);
            mac.update(HMAC_MD5_VECTORS[i][1], 0, 
HMAC_MD5_VECTORS[i][1].length);
            byte[] digest = mac.digest();
            harness.check(Util.areEqual(digest, HMAC_MD5_VECTORS[i][2]),
               "testHMacMD5");
         } catch (Exception x) {
            harness.debug(x);
            harness.fail("testOfHMacMD5 - " + String.valueOf(x));
         }
      }
   }

   /**
    * Check HMAC-SHA-1.
    *
    * @param harness The test harness.
    */
   private void testHMacSHA1(TestHarness harness) {
      IMac mac = MacFactory.getInstance("hmac-sha1");
      HashMap attr = new HashMap();
      for (int i = 0; i < HMAC_SHA1_VECTORS.length; i++) {
         try {
            attr.put(IMac.MAC_KEY_MATERIAL, HMAC_SHA1_VECTORS[i][0]);
            mac.init(attr);
            mac.update(HMAC_SHA1_VECTORS[i][1], 0, 
HMAC_MD5_VECTORS[i][1].length);
            byte[] digest = mac.digest();
            harness.check(Util.areEqual(digest, HMAC_SHA1_VECTORS[i][2]),
               "testHMacSHA1");
         } catch (Exception x) {
            harness.debug(x);
            harness.fail("testOfHMacSHA1 - " + String.valueOf(x));
         }
      }
   }
}

reply via email to

[Prev in Thread] Current Thread [Next in Thread]