Source for gnu.javax.crypto.mac.OMAC

   1: /* OMAC.java --
   2:    Copyright (C) 2004, 2006, 2010 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 = Configuration.DEBUG ?
  69:                         Logger.getLogger(OMAC.class.getName()) : null;
  70:   private static final byte C1 = (byte) 0x87;
  71:   private static final byte C2 = 0x1b;
  72:   // Test key for OMAC-AES-128
  73:   private static final byte[] KEY0 =
  74:       Util.toBytesFromString("2b7e151628aed2a6abf7158809cf4f3c");
  75:   // Test MAC for zero-length input.
  76:   private static final byte[] DIGEST0 =
  77:       Util.toBytesFromString("bb1d6929e95937287fa37d129b756746");
  78:   private static Boolean valid;
  79:   private final IBlockCipher cipher;
  80:   private final String name;
  81:   private IMode mode;
  82:   private int blockSize;
  83:   private int outputSize;
  84:   private byte[] Lu, Lu2;
  85:   private byte[] M;
  86:   private byte[] Y;
  87:   private boolean init;
  88:   private int index;
  89: 
  90:   public OMAC(IBlockCipher cipher)
  91:   {
  92:     this.cipher = cipher;
  93:     this.name = "OMAC-" + cipher.name();
  94:   }
  95: 
  96:   public Object clone()
  97:   {
  98:     return new OMAC(cipher);
  99:   }
 100: 
 101:   public String name()
 102:   {
 103:     return name;
 104:   }
 105: 
 106:   public int macSize()
 107:   {
 108:     return outputSize;
 109:   }
 110: 
 111:   public void init(Map attrib) throws InvalidKeyException
 112:   {
 113:     HashMap attrib2 = new HashMap();
 114:     attrib2.put(IBlockCipher.KEY_MATERIAL, attrib.get(MAC_KEY_MATERIAL));
 115:     cipher.reset();
 116:     cipher.init(attrib2);
 117:     blockSize = cipher.currentBlockSize();
 118:     Integer os = (Integer) attrib.get(TRUNCATED_SIZE);
 119:     if (os != null)
 120:       {
 121:         outputSize = os.intValue();
 122:         if (outputSize < 0 || outputSize > blockSize)
 123:           throw new IllegalArgumentException("truncated size out of range");
 124:       }
 125:     else
 126:       outputSize = blockSize;
 127: 
 128:     byte[] L = new byte[blockSize];
 129:     cipher.encryptBlock(L, 0, L, 0);
 130:     if (Configuration.DEBUG)
 131:       log.fine("L = " + Util.toString(L).toLowerCase());
 132:     if (Lu != null)
 133:       {
 134:         Arrays.fill(Lu, (byte) 0);
 135:         if (Lu.length != blockSize)
 136:           Lu = new byte[blockSize];
 137:       }
 138:     else
 139:       Lu = new byte[blockSize];
 140:     if (Lu2 != null)
 141:       {
 142:         Arrays.fill(Lu2, (byte) 0);
 143:         if (Lu2.length != blockSize)
 144:           Lu2 = new byte[blockSize];
 145:       }
 146:     else
 147:       Lu2 = new byte[blockSize];
 148: 
 149:     boolean msb = (L[0] & 0x80) != 0;
 150:     for (int i = 0; i < blockSize; i++)
 151:       {
 152:         Lu[i] = (byte)(L[i] << 1 & 0xFF);
 153:         if (i + 1 < blockSize)
 154:           Lu[i] |= (byte)((L[i + 1] & 0x80) >> 7);
 155:       }
 156:     if (msb)
 157:       {
 158:         if (blockSize == 16)
 159:           Lu[Lu.length - 1] ^= C1;
 160:         else if (blockSize == 8)
 161:           Lu[Lu.length - 1] ^= C2;
 162:         else
 163:           throw new IllegalArgumentException("unsupported cipher block size: "
 164:                                              + blockSize);
 165:       }
 166:     if (Configuration.DEBUG)
 167:       log.fine("Lu = " + Util.toString(Lu).toLowerCase());
 168:     msb = (Lu[0] & 0x80) != 0;
 169:     for (int i = 0; i < blockSize; i++)
 170:       {
 171:         Lu2[i] = (byte)(Lu[i] << 1 & 0xFF);
 172:         if (i + 1 < blockSize)
 173:           Lu2[i] |= (byte)((Lu[i + 1] & 0x80) >> 7);
 174:       }
 175:     if (msb)
 176:       {
 177:         if (blockSize == 16)
 178:           Lu2[Lu2.length - 1] ^= C1;
 179:         else
 180:           Lu2[Lu2.length - 1] ^= C2;
 181:       }
 182:     if (Configuration.DEBUG)
 183:       log.fine("Lu2 = " + Util.toString(Lu2).toLowerCase());
 184:     if (M != null)
 185:       {
 186:         Arrays.fill(M, (byte) 0);
 187:         if (M.length != blockSize)
 188:           M = new byte[blockSize];
 189:       }
 190:     else
 191:       M = new byte[blockSize];
 192:     if (Y != null)
 193:       {
 194:         Arrays.fill(Y, (byte) 0);
 195:         if (Y.length != blockSize)
 196:           Y = new byte[blockSize];
 197:       }
 198:     else
 199:       Y = new byte[blockSize];
 200: 
 201:     index = 0;
 202:     init = true;
 203:   }
 204: 
 205:   public void update(byte b)
 206:   {
 207:     if (! init)
 208:       throw new IllegalStateException("not initialized");
 209:     if (index == M.length)
 210:       {
 211:         process();
 212:         index = 0;
 213:       }
 214:     M[index++] = b;
 215:   }
 216: 
 217:   public void update(byte[] buf, int off, int len)
 218:   {
 219:     if (! init)
 220:       throw new IllegalStateException("not initialized");
 221:     if (off < 0 || len < 0 || off + len > buf.length)
 222:       throw new IndexOutOfBoundsException("size=" + buf.length + "; off=" + off
 223:                                           + "; len=" + len);
 224:     for (int i = 0; i < len;)
 225:       {
 226:         if (index == blockSize)
 227:           {
 228:             process();
 229:             index = 0;
 230:           }
 231:         int count = Math.min(blockSize - index, len - i);
 232:         System.arraycopy(buf, off + i, M, index, count);
 233:         index += count;
 234:         i += count;
 235:       }
 236:   }
 237: 
 238:   public byte[] digest()
 239:   {
 240:     byte[] b = new byte[outputSize];
 241:     digest(b, 0);
 242:     return b;
 243:   }
 244: 
 245:   public void digest(byte[] out, int off)
 246:   {
 247:     if (! init)
 248:       throw new IllegalStateException("not initialized");
 249:     if (off < 0 || off + outputSize > out.length)
 250:       throw new IndexOutOfBoundsException("size=" + out.length + "; off=" + off
 251:                                           + "; len=" + outputSize);
 252:     byte[] T = new byte[blockSize];
 253:     byte[] L = Lu;
 254:     if (index < blockSize)
 255:       {
 256:         M[index++] = (byte) 0x80;
 257:         while (index < blockSize)
 258:           M[index++] = 0;
 259:         L = Lu2;
 260:       }
 261:     for (int i = 0; i < blockSize; i++)
 262:       T[i] = (byte)(M[i] ^ Y[i] ^ L[i]);
 263:     cipher.encryptBlock(T, 0, T, 0);
 264:     System.arraycopy(T, 0, out, off, outputSize);
 265:     reset();
 266:   }
 267: 
 268:   public void reset()
 269:   {
 270:     index = 0;
 271:     if (Y != null)
 272:       Arrays.fill(Y, (byte) 0);
 273:     if (M != null)
 274:       Arrays.fill(M, (byte) 0);
 275:   }
 276: 
 277:   public boolean selfTest()
 278:   {
 279:     OMAC mac = new OMAC(CipherFactory.getInstance(Registry.AES_CIPHER));
 280:     mac.reset();
 281:     Map attr = new HashMap();
 282:     attr.put(MAC_KEY_MATERIAL, KEY0);
 283:     byte[] digest = null;
 284:     try
 285:       {
 286:         mac.init(attr);
 287:         digest = mac.digest();
 288:       }
 289:     catch (Exception x)
 290:       {
 291:         return false;
 292:       }
 293:     if (digest == null)
 294:       return false;
 295:     return Arrays.equals(DIGEST0, digest);
 296:   }
 297: 
 298:   private void process()
 299:   {
 300:     for (int i = 0; i < blockSize; i++)
 301:       M[i] = (byte)(M[i] ^ Y[i]);
 302:     cipher.encryptBlock(M, 0, Y, 0);
 303:   }
 304: }