Source for javax.crypto.Mac

   1: /* Mac.java -- The message authentication code interface.
   2:    Copyright (C) 2004  Free Software Foundation, Inc.
   3: 
   4: This file is 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, or (at your option)
   9: 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; see the file COPYING.  If not, write to the
  18: Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
  19: 02110-1301 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 javax.crypto;
  40: 
  41: import gnu.java.security.Engine;
  42: 
  43: import java.lang.reflect.InvocationTargetException;
  44: import java.nio.ByteBuffer;
  45: import java.security.InvalidAlgorithmParameterException;
  46: import java.security.InvalidKeyException;
  47: import java.security.Key;
  48: import java.security.NoSuchAlgorithmException;
  49: import java.security.NoSuchProviderException;
  50: import java.security.Provider;
  51: import java.security.Security;
  52: import java.security.spec.AlgorithmParameterSpec;
  53: 
  54: /**
  55:  * This class implements a "message authentication code" (MAC), a method
  56:  * to ensure the integrity of data transmitted between two parties who
  57:  * share a common secret key.
  58:  *
  59:  * <p>The best way to describe a MAC is as a <i>keyed one-way hash
  60:  * function</i>, which looks like:
  61:  *
  62:  * <blockquote><p><code>D = MAC(K, M)</code></blockquote>
  63:  *
  64:  * <p>where <code>K</code> is the key, <code>M</code> is the message,
  65:  * and <code>D</code> is the resulting digest. One party will usually
  66:  * send the concatenation <code>M || D</code> to the other party, who
  67:  * will then verify <code>D</code> by computing <code>D'</code> in a
  68:  * similar fashion. If <code>D == D'</code>, then the message is assumed
  69:  * to be authentic.
  70:  *
  71:  * @author Casey Marshall (csm@gnu.org)
  72:  */
  73: public class Mac implements Cloneable
  74: {
  75: 
  76:   // Fields.
  77:   // ------------------------------------------------------------------------
  78: 
  79:   private static final String SERVICE = "Mac";
  80: 
  81:   /** The underlying MAC implementation. */
  82:   private MacSpi macSpi;
  83: 
  84:   /** The provider we got our implementation from. */
  85:   private Provider provider;
  86: 
  87:   /** The name of the algorithm. */
  88:   private String algorithm;
  89: 
  90:   /** Whether or not we've been initialized. */
  91:   private boolean virgin;
  92: 
  93:   // Constructor.
  94:   // ------------------------------------------------------------------------
  95: 
  96:   /**
  97:    * Creates a new Mac instance.
  98:    *
  99:    * @param macSpi    The underlying MAC implementation.
 100:    * @param provider  The provider of this implementation.
 101:    * @param algorithm The name of this MAC algorithm.
 102:    */
 103:   protected Mac(MacSpi macSpi, Provider provider, String algorithm)
 104:   {
 105:     this.macSpi = macSpi;
 106:     this.provider = provider;
 107:     this.algorithm = algorithm;
 108:     virgin = true;
 109:   }
 110: 
 111:   /**
 112:    * Create an instance of the named algorithm from the first provider with an
 113:    * appropriate implementation.
 114:    * 
 115:    * @param algorithm The name of the algorithm.
 116:    * @return An appropriate Mac instance, if the specified algorithm is
 117:    *         implemented by a provider.
 118:    * @throws NoSuchAlgorithmException If no implementation of the named
 119:    *           algorithm is installed.
 120:    * @throws IllegalArgumentException if <code>algorithm</code> is
 121:    *           <code>null</code> or is an empty string.
 122:    */
 123:   public static final Mac getInstance(String algorithm)
 124:       throws NoSuchAlgorithmException
 125:   {
 126:     Provider[] p = Security.getProviders();
 127:     NoSuchAlgorithmException lastException = null;
 128:     for (int i = 0; i < p.length; i++)
 129:       try
 130:         {
 131:           return getInstance(algorithm, p[i]);
 132:         }
 133:       catch (NoSuchAlgorithmException x)
 134:         {
 135:           lastException = x;
 136:         }
 137:       if (lastException != null)
 138:         throw lastException;
 139:       throw new NoSuchAlgorithmException(algorithm);
 140:   }
 141: 
 142:   /**
 143:    * Create an instance of the named algorithm from the named provider.
 144:    * 
 145:    * @param algorithm The name of the algorithm.
 146:    * @param provider The name of the provider.
 147:    * @return An appropriate Mac instance, if the specified algorithm is
 148:    *         implemented by the named provider.
 149:    * @throws NoSuchAlgorithmException If the named provider has no
 150:    *           implementation of the algorithm.
 151:    * @throws NoSuchProviderException If the named provider does not exist.
 152:    * @throws IllegalArgumentException if either <code>algorithm</code> or
 153:    *           <code>provider</code> is <code>null</code>, or if
 154:    *           <code>algorithm</code> is an empty string.
 155:    */
 156:   public static final Mac getInstance(String algorithm, String provider)
 157:       throws NoSuchAlgorithmException, NoSuchProviderException
 158:   {
 159:     if (provider == null)
 160:       throw new IllegalArgumentException("provider MUST NOT be null");
 161:     Provider p = Security.getProvider(provider);
 162:     if (p == null)
 163:       throw new NoSuchProviderException(provider);
 164:     return getInstance(algorithm, p);
 165:   }
 166: 
 167:   /**
 168:    * Create an instance of the named algorithm from a provider.
 169:    * 
 170:    * @param algorithm The name of the algorithm.
 171:    * @param provider The provider.
 172:    * @return An appropriate Mac instance, if the specified algorithm is
 173:    *         implemented by the provider.
 174:    * @throws NoSuchAlgorithmException If the provider has no implementation of
 175:    *           the algorithm.
 176:    * @throws IllegalArgumentException if either <code>algorithm</code> or
 177:    *           <code>provider</code> is <code>null</code>, or if
 178:    *           <code>algorithm</code> is an empty string.
 179:    */
 180:   public static final Mac getInstance(String algorithm, Provider provider)
 181:       throws NoSuchAlgorithmException
 182:   {
 183:     StringBuilder sb = new StringBuilder("Mac algorithm [")
 184:         .append(algorithm).append("] from provider[")
 185:         .append(provider).append("] could not be created");
 186:     Throwable cause;
 187:     try
 188:       {
 189:         Object spi = Engine.getInstance(SERVICE, algorithm, provider);
 190:         return new Mac((MacSpi) spi, provider, algorithm);
 191:       }
 192:     catch (InvocationTargetException x)
 193:       {
 194:         cause = x.getCause();
 195:         if (cause instanceof NoSuchAlgorithmException)
 196:           throw (NoSuchAlgorithmException) cause;
 197:         if (cause == null)
 198:           cause = x;
 199:       }
 200:     catch (ClassCastException x)
 201:       {
 202:         cause = x;
 203:       }
 204:     NoSuchAlgorithmException x = new NoSuchAlgorithmException(sb.toString());
 205:     x.initCause(cause);
 206:     throw x;
 207:   }
 208: 
 209:   /**
 210:    * Finishes the computation of a MAC and returns the digest.
 211:    *
 212:    * <p>After this method succeeds, it may be used again as just after a
 213:    * call to <code>init</code>, and can compute another MAC using the
 214:    * same key and parameters.
 215:    *
 216:    * @return The message authentication code.
 217:    * @throws java.lang.IllegalStateException If this instnace has not
 218:    *         been initialized.
 219:    */
 220:   public final byte[] doFinal() throws IllegalStateException
 221:   {
 222:     if (virgin)
 223:       {
 224:         throw new IllegalStateException("not initialized");
 225:       }
 226:     byte[] digest = macSpi.engineDoFinal();
 227:     reset();
 228:     return digest;
 229:   }
 230: 
 231:   /**
 232:    * Finishes the computation of a MAC with a final byte array (or
 233:    * computes a MAC over those bytes only) and returns the digest.
 234:    *
 235:    * <p>After this method succeeds, it may be used again as just after a
 236:    * call to <code>init</code>, and can compute another MAC using the
 237:    * same key and parameters.
 238:    *
 239:    * @param input The bytes to add.
 240:    * @return The message authentication code.
 241:    * @throws java.lang.IllegalStateException If this instnace has not
 242:    *         been initialized.
 243:    */
 244:   public final byte[] doFinal(byte[] input) throws IllegalStateException
 245:   {
 246:     update(input);
 247:     byte[] digest = macSpi.engineDoFinal();
 248:     reset();
 249:     return digest;
 250:   }
 251: 
 252:   /**
 253:    * Finishes the computation of a MAC and places the result into the
 254:    * given array.
 255:    *
 256:    * <p>After this method succeeds, it may be used again as just after a
 257:    * call to <code>init</code>, and can compute another MAC using the
 258:    * same key and parameters.
 259:    *
 260:    * @param output    The destination for the result.
 261:    * @param outOffset The index in the output array to start.
 262:    * @return The message authentication code.
 263:    * @throws java.lang.IllegalStateException If this instnace has not
 264:    *         been initialized.
 265:    * @throws javax.crypto.ShortBufferException If <code>output</code> is
 266:    *         not large enough to hold the result.
 267:    */
 268:   public final void doFinal(byte[] output, int outOffset)
 269:   throws IllegalStateException, ShortBufferException
 270:   {
 271:     if (virgin)
 272:       {
 273:         throw new IllegalStateException("not initialized");
 274:       }
 275:     if (output.length - outOffset < getMacLength())
 276:       {
 277:         throw new ShortBufferException();
 278:       }
 279:     byte[] mac = macSpi.engineDoFinal();
 280:     System.arraycopy(mac, 0, output, outOffset, getMacLength());
 281:     reset();
 282:   }
 283: 
 284:   /**
 285:    * Returns the name of this MAC algorithm.
 286:    *
 287:    * @return The MAC name.
 288:    */
 289:   public final String getAlgorithm()
 290:   {
 291:     return algorithm;
 292:   }
 293: 
 294:   /**
 295:    * Get the size of the MAC. This is the size of the array returned by
 296:    * {@link #doFinal()} and {@link #doFinal(byte[])}, and the minimum
 297:    * number of bytes that must be available in the byte array passed to
 298:    * {@link #doFinal(byte[],int)}.
 299:    *
 300:    * @return The MAC length.
 301:    */
 302:   public final int getMacLength()
 303:   {
 304:     return macSpi.engineGetMacLength();
 305:   }
 306: 
 307:   /**
 308:    * Get the provider of the underlying implementation.
 309:    *
 310:    * @return The provider.
 311:    */
 312:   public final Provider getProvider()
 313:   {
 314:     return provider;
 315:   }
 316: 
 317:   /**
 318:    * Initialize this MAC with a key and no parameters.
 319:    *
 320:    * @param key The key to initialize this instance with.
 321:    * @throws java.security.InvalidKeyException If the key is
 322:    *         unacceptable.
 323:    */
 324:   public final void init(Key key) throws InvalidKeyException
 325:   {
 326:     try
 327:       {
 328:         init(key, null);
 329:       }
 330:     catch (InvalidAlgorithmParameterException iape)
 331:       {
 332:         throw new IllegalArgumentException(algorithm + " needs parameters");
 333:       }
 334:   }
 335: 
 336:   /**
 337:    * Initialize this MAC with a key and parameters.
 338:    *
 339:    * @param key    The key to initialize this instance with.
 340:    * @param params The algorithm-specific parameters.
 341:    * @throws java.security.InvalidAlgorithmParameterException If the
 342:    *         algorithm parameters are unacceptable.
 343:    * @throws java.security.InvalidKeyException If the key is
 344:    *         unacceptable.
 345:    */
 346:   public final void init(Key key, AlgorithmParameterSpec params)
 347:     throws InvalidAlgorithmParameterException, InvalidKeyException
 348:   {
 349:     macSpi.engineInit(key, params);
 350:     virgin = false;                      // w00t!
 351:   }
 352: 
 353:   /**
 354:    * Reset this instance. A call to this method returns this instance
 355:    * back to the state it was in just after it was initialized.
 356:    */
 357:   public final void reset()
 358:   {
 359:     macSpi.engineReset();
 360:   }
 361: 
 362:   /**
 363:    * Update the computation with a single byte.
 364:    *
 365:    * @param input The next byte.
 366:    * @throws java.lang.IllegalStateException If this instance has not
 367:    *         been initialized.
 368:    */
 369:   public final void update(byte input) throws IllegalStateException
 370:   {
 371:     if (virgin)
 372:       {
 373:         throw new IllegalStateException("not initialized");
 374:       }
 375:     macSpi.engineUpdate(input);
 376:   }
 377: 
 378:   /**
 379:    * Update the computation with a byte array.
 380:    *
 381:    * @param input The next bytes.
 382:    * @throws java.lang.IllegalStateException If this instance has not
 383:    *         been initialized.
 384:    */
 385:   public final void update(byte[] input) throws IllegalStateException
 386:   {
 387:     update(input, 0, input.length);
 388:   }
 389: 
 390:   /**
 391:    * Update the computation with a portion of a byte array.
 392:    *
 393:    * @param input  The next bytes.
 394:    * @param offset The index in <code>input</code> to start.
 395:    * @param length The number of bytes to update.
 396:    * @throws java.lang.IllegalStateException If this instance has not
 397:    *         been initialized.
 398:    */
 399:   public final void update(byte[] input, int offset, int length)
 400:     throws IllegalStateException
 401:   {
 402:     if (virgin)
 403:       {
 404:         throw new IllegalStateException("not initialized");
 405:       }
 406:     macSpi.engineUpdate(input, offset, length);
 407:   }
 408: 
 409:   /**
 410:    * Update this MAC with the remaining bytes in the given buffer
 411:    * @param buffer The input buffer.
 412:    * @since 1.5
 413:    */
 414:   public final void update (final ByteBuffer buffer)
 415:   {
 416:     if (virgin)
 417:       throw new IllegalStateException ("not initialized");
 418:     macSpi.engineUpdate(buffer);
 419:   }
 420:   
 421:   /**
 422:    * Clone this instance, if the underlying implementation supports it.
 423:    *
 424:    * @return A clone of this instance.
 425:    * @throws java.lang.CloneNotSupportedException If the underlying
 426:    *         implementation is not cloneable.
 427:    */
 428:   public final Object clone() throws CloneNotSupportedException
 429:   {
 430:     Mac result = new Mac((MacSpi) macSpi.clone(), provider, algorithm);
 431:     result.virgin = virgin;
 432:     return result;
 433:   }
 434: }