Source for gnu.javax.crypto.mac.OMAC

   1: /* OMAC.java -- 
   2:    Copyright (C) 2004, 2006 Free Software Foundation, Inc.
   3: 
   4: This file is a part of GNU Classpath.
   5: 
   6: GNU Classpath is free software; you can redistribute it and/or modify
   7: it under the terms of the GNU General Public License as published by
   8: the Free Software Foundation; either version 2 of the License, or (at
   9: your option) any later version.
  10: 
  11: GNU Classpath is distributed in the hope that it will be useful, but
  12: WITHOUT ANY WARRANTY; without even the implied warranty of
  13: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  14: General Public License for more details.
  15: 
  16: You should have received a copy of the GNU General Public License
  17: along with GNU Classpath; if not, write to the Free Software
  18: Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
  19: USA
  20: 
  21: Linking this library statically or dynamically with other modules is
  22: making a combined work based on this library.  Thus, the terms and
  23: conditions of the GNU General Public License cover the whole
  24: combination.
  25: 
  26: As a special exception, the copyright holders of this library give you
  27: permission to link this library with independent modules to produce an
  28: executable, regardless of the license terms of these independent
  29: modules, and to copy and distribute the resulting executable under
  30: terms of your choice, provided that you also meet, for each linked
  31: independent module, the terms and conditions of the license of that
  32: module.  An independent module is a module which is not derived from
  33: or based on this library.  If you modify this library, you may extend
  34: this exception to your version of the library, but you are not
  35: obligated to do so.  If you do not wish to do so, delete this
  36: exception statement from your version.  */
  37: 
  38: 
  39: package gnu.javax.crypto.mac;
  40: 
  41: import gnu.java.security.Configuration;
  42: import gnu.java.security.Registry;
  43: import gnu.java.security.util.Util;
  44: import gnu.javax.crypto.cipher.CipherFactory;
  45: import gnu.javax.crypto.cipher.IBlockCipher;
  46: import gnu.javax.crypto.mode.IMode;
  47: 
  48: import java.security.InvalidKeyException;
  49: import java.util.Arrays;
  50: import java.util.HashMap;
  51: import java.util.Map;
  52: import java.util.logging.Logger;
  53: 
  54: /**
  55:  * The One-Key CBC MAC, OMAC. This message authentication code is based on a
  56:  * block cipher in CBC mode.
  57:  * <p>
  58:  * References:
  59:  * <ol>
  60:  * <li>Tetsu Iwata and Kaoru Kurosawa, <i><a
  61:  * href="http://crypt.cis.ibaraki.ac.jp/omac/docs/omac.pdf">OMAC: One-Key CBC
  62:  * MAC</a></i>.</li>
  63:  * </ol>
  64:  */
  65: public class OMAC
  66:     implements IMac
  67: {
  68:   private static final Logger log = Logger.getLogger(OMAC.class.getName());
  69:   private static final byte C1 = (byte) 0x87;
  70:   private static final byte C2 = 0x1b;
  71:   // Test key for OMAC-AES-128
  72:   private static final byte[] KEY0 =
  73:       Util.toBytesFromString("2b7e151628aed2a6abf7158809cf4f3c");
  74:   // Test MAC for zero-length input.
  75:   private static final byte[] DIGEST0 =
  76:       Util.toBytesFromString("bb1d6929e95937287fa37d129b756746");
  77:   private static Boolean valid;
  78:   private final IBlockCipher cipher;
  79:   private final String name;
  80:   private IMode mode;
  81:   private int blockSize;
  82:   private int outputSize;
  83:   private byte[] Lu, Lu2;
  84:   private byte[] M;
  85:   private byte[] Y;
  86:   private boolean init;
  87:   private int index;
  88: 
  89:   public OMAC(IBlockCipher cipher)
  90:   {
  91:     this.cipher = cipher;
  92:     this.name = "OMAC-" + cipher.name();
  93:   }
  94: 
  95:   public Object clone()
  96:   {
  97:     return new OMAC(cipher);
  98:   }
  99: 
 100:   public String name()
 101:   {
 102:     return name;
 103:   }
 104: 
 105:   public int macSize()
 106:   {
 107:     return outputSize;
 108:   }
 109: 
 110:   public void init(Map attrib) throws InvalidKeyException
 111:   {
 112:     HashMap attrib2 = new HashMap();
 113:     attrib2.put(IBlockCipher.KEY_MATERIAL, attrib.get(MAC_KEY_MATERIAL));
 114:     cipher.reset();
 115:     cipher.init(attrib2);
 116:     blockSize = cipher.currentBlockSize();
 117:     Integer os = (Integer) attrib.get(TRUNCATED_SIZE);
 118:     if (os != null)
 119:       {
 120:         outputSize = os.intValue();
 121:         if (outputSize < 0 || outputSize > blockSize)
 122:           throw new IllegalArgumentException("truncated size out of range");
 123:       }
 124:     else
 125:       outputSize = blockSize;
 126: 
 127:     byte[] L = new byte[blockSize];
 128:     cipher.encryptBlock(L, 0, L, 0);
 129:     if (Configuration.DEBUG)
 130:       log.fine("L = " + Util.toString(L).toLowerCase());
 131:     if (Lu != null)
 132:       {
 133:         Arrays.fill(Lu, (byte) 0);
 134:         if (Lu.length != blockSize)
 135:           Lu = new byte[blockSize];
 136:       }
 137:     else
 138:       Lu = new byte[blockSize];
 139:     if (Lu2 != null)
 140:       {
 141:         Arrays.fill(Lu2, (byte) 0);
 142:         if (Lu2.length != blockSize)
 143:           Lu2 = new byte[blockSize];
 144:       }
 145:     else
 146:       Lu2 = new byte[blockSize];
 147: 
 148:     boolean msb = (L[0] & 0x80) != 0;
 149:     for (int i = 0; i < blockSize; i++)
 150:       {
 151:         Lu[i] = (byte)(L[i] << 1 & 0xFF);
 152:         if (i + 1 < blockSize)
 153:           Lu[i] |= (byte)((L[i + 1] & 0x80) >> 7);
 154:       }
 155:     if (msb)
 156:       {
 157:         if (blockSize == 16)
 158:           Lu[Lu.length - 1] ^= C1;
 159:         else if (blockSize == 8)
 160:           Lu[Lu.length - 1] ^= C2;
 161:         else
 162:           throw new IllegalArgumentException("unsupported cipher block size: "
 163:                                              + blockSize);
 164:       }
 165:     if (Configuration.DEBUG)
 166:       log.fine("Lu = " + Util.toString(Lu).toLowerCase());
 167:     msb = (Lu[0] & 0x80) != 0;
 168:     for (int i = 0; i < blockSize; i++)
 169:       {
 170:         Lu2[i] = (byte)(Lu[i] << 1 & 0xFF);
 171:         if (i + 1 < blockSize)
 172:           Lu2[i] |= (byte)((Lu[i + 1] & 0x80) >> 7);
 173:       }
 174:     if (msb)
 175:       {
 176:         if (blockSize == 16)
 177:           Lu2[Lu2.length - 1] ^= C1;
 178:         else
 179:           Lu2[Lu2.length - 1] ^= C2;
 180:       }
 181:     if (Configuration.DEBUG)
 182:       log.fine("Lu2 = " + Util.toString(Lu2).toLowerCase());
 183:     if (M != null)
 184:       {
 185:         Arrays.fill(M, (byte) 0);
 186:         if (M.length != blockSize)
 187:           M = new byte[blockSize];
 188:       }
 189:     else
 190:       M = new byte[blockSize];
 191:     if (Y != null)
 192:       {
 193:         Arrays.fill(Y, (byte) 0);
 194:         if (Y.length != blockSize)
 195:           Y = new byte[blockSize];
 196:       }
 197:     else
 198:       Y = new byte[blockSize];
 199: 
 200:     index = 0;
 201:     init = true;
 202:   }
 203: 
 204:   public void update(byte b)
 205:   {
 206:     if (! init)
 207:       throw new IllegalStateException("not initialized");
 208:     if (index == M.length)
 209:       {
 210:         process();
 211:         index = 0;
 212:       }
 213:     M[index++] = b;
 214:   }
 215: 
 216:   public void update(byte[] buf, int off, int len)
 217:   {
 218:     if (! init)
 219:       throw new IllegalStateException("not initialized");
 220:     if (off < 0 || len < 0 || off + len > buf.length)
 221:       throw new IndexOutOfBoundsException("size=" + buf.length + "; off=" + off
 222:                                           + "; len=" + len);
 223:     for (int i = 0; i < len;)
 224:       {
 225:         if (index == blockSize)
 226:           {
 227:             process();
 228:             index = 0;
 229:           }
 230:         int count = Math.min(blockSize - index, len - i);
 231:         System.arraycopy(buf, off + i, M, index, count);
 232:         index += count;
 233:         i += count;
 234:       }
 235:   }
 236: 
 237:   public byte[] digest()
 238:   {
 239:     byte[] b = new byte[outputSize];
 240:     digest(b, 0);
 241:     return b;
 242:   }
 243: 
 244:   public void digest(byte[] out, int off)
 245:   {
 246:     if (! init)
 247:       throw new IllegalStateException("not initialized");
 248:     if (off < 0 || off + outputSize > out.length)
 249:       throw new IndexOutOfBoundsException("size=" + out.length + "; off=" + off
 250:                                           + "; len=" + outputSize);
 251:     byte[] T = new byte[blockSize];
 252:     byte[] L = Lu;
 253:     if (index < blockSize)
 254:       {
 255:         M[index++] = (byte) 0x80;
 256:         while (index < blockSize)
 257:           M[index++] = 0;
 258:         L = Lu2;
 259:       }
 260:     for (int i = 0; i < blockSize; i++)
 261:       T[i] = (byte)(M[i] ^ Y[i] ^ L[i]);
 262:     cipher.encryptBlock(T, 0, T, 0);
 263:     System.arraycopy(T, 0, out, off, outputSize);
 264:     reset();
 265:   }
 266: 
 267:   public void reset()
 268:   {
 269:     index = 0;
 270:     if (Y != null)
 271:       Arrays.fill(Y, (byte) 0);
 272:     if (M != null)
 273:       Arrays.fill(M, (byte) 0);
 274:   }
 275: 
 276:   public boolean selfTest()
 277:   {
 278:     OMAC mac = new OMAC(CipherFactory.getInstance(Registry.AES_CIPHER));
 279:     mac.reset();
 280:     Map attr = new HashMap();
 281:     attr.put(MAC_KEY_MATERIAL, KEY0);
 282:     byte[] digest = null;
 283:     try
 284:       {
 285:         mac.init(attr);
 286:         digest = mac.digest();
 287:       }
 288:     catch (Exception x)
 289:       {
 290:         return false;
 291:       }
 292:     if (digest == null)
 293:       return false;
 294:     return Arrays.equals(DIGEST0, digest);
 295:   }
 296: 
 297:   private void process()
 298:   {
 299:     for (int i = 0; i < blockSize; i++)
 300:       M[i] = (byte)(M[i] ^ Y[i]);
 301:     cipher.encryptBlock(M, 0, Y, 0);
 302:   }
 303: }