001/* Mac.java -- The message authentication code interface. 002 Copyright (C) 2004 Free Software Foundation, Inc. 003 004This file is part of GNU Classpath. 005 006GNU Classpath is free software; you can redistribute it and/or modify 007it under the terms of the GNU General Public License as published by 008the Free Software Foundation; either version 2, or (at your option) 009any later version. 010 011GNU Classpath is distributed in the hope that it will be useful, but 012WITHOUT ANY WARRANTY; without even the implied warranty of 013MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 014General Public License for more details. 015 016You should have received a copy of the GNU General Public License 017along with GNU Classpath; see the file COPYING. If not, write to the 018Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 01902110-1301 USA. 020 021Linking this library statically or dynamically with other modules is 022making a combined work based on this library. Thus, the terms and 023conditions of the GNU General Public License cover the whole 024combination. 025 026As a special exception, the copyright holders of this library give you 027permission to link this library with independent modules to produce an 028executable, regardless of the license terms of these independent 029modules, and to copy and distribute the resulting executable under 030terms of your choice, provided that you also meet, for each linked 031independent module, the terms and conditions of the license of that 032module. An independent module is a module which is not derived from 033or based on this library. If you modify this library, you may extend 034this exception to your version of the library, but you are not 035obligated to do so. If you do not wish to do so, delete this 036exception statement from your version. */ 037 038 039package javax.crypto; 040 041import gnu.java.security.Engine; 042 043import java.lang.reflect.InvocationTargetException; 044import java.nio.ByteBuffer; 045import java.security.InvalidAlgorithmParameterException; 046import java.security.InvalidKeyException; 047import java.security.Key; 048import java.security.NoSuchAlgorithmException; 049import java.security.NoSuchProviderException; 050import java.security.Provider; 051import java.security.Security; 052import java.security.spec.AlgorithmParameterSpec; 053 054/** 055 * This class implements a "message authentication code" (MAC), a method 056 * to ensure the integrity of data transmitted between two parties who 057 * share a common secret key. 058 * 059 * <p>The best way to describe a MAC is as a <i>keyed one-way hash 060 * function</i>, which looks like: 061 * 062 * <blockquote><p><code>D = MAC(K, M)</code></blockquote> 063 * 064 * <p>where <code>K</code> is the key, <code>M</code> is the message, 065 * and <code>D</code> is the resulting digest. One party will usually 066 * send the concatenation <code>M || D</code> to the other party, who 067 * will then verify <code>D</code> by computing <code>D'</code> in a 068 * similar fashion. If <code>D == D'</code>, then the message is assumed 069 * to be authentic. 070 * 071 * @author Casey Marshall (csm@gnu.org) 072 */ 073public class Mac implements Cloneable 074{ 075 076 // Fields. 077 // ------------------------------------------------------------------------ 078 079 private static final String SERVICE = "Mac"; 080 081 /** The underlying MAC implementation. */ 082 private MacSpi macSpi; 083 084 /** The provider we got our implementation from. */ 085 private Provider provider; 086 087 /** The name of the algorithm. */ 088 private String algorithm; 089 090 /** Whether or not we've been initialized. */ 091 private boolean virgin; 092 093 // Constructor. 094 // ------------------------------------------------------------------------ 095 096 /** 097 * Creates a new Mac instance. 098 * 099 * @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}