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 */ Mac(MacSpi macSpi, Provider provider, String algorithm)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 */ getInstance(String algorithm)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 */ getInstance(String algorithm, String provider)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 */ getInstance(String algorithm, Provider provider)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 */ doFinal()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 */ doFinal(byte[] input)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 */ doFinal(byte[] output, int outOffset)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 */ getAlgorithm()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 */ getMacLength()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 */ getProvider()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 */ init(Key key)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 */ init(Key key, AlgorithmParameterSpec params)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 */ reset()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 */ update(byte input)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 */ update(byte[] input)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 */ update(byte[] input, int offset, int length)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 */ update(final ByteBuffer buffer)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 */ clone()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 } 435