001 /* Mac.java -- The message authentication code interface. 002 Copyright (C) 2004 Free Software Foundation, Inc. 003 004 This file is part of GNU Classpath. 005 006 GNU Classpath is free software; you can redistribute it and/or modify 007 it under the terms of the GNU General Public License as published by 008 the Free Software Foundation; either version 2, or (at your option) 009 any later version. 010 011 GNU Classpath is distributed in the hope that it will be useful, but 012 WITHOUT ANY WARRANTY; without even the implied warranty of 013 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 014 General Public License for more details. 015 016 You should have received a copy of the GNU General Public License 017 along with GNU Classpath; see the file COPYING. If not, write to the 018 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 019 02110-1301 USA. 020 021 Linking this library statically or dynamically with other modules is 022 making a combined work based on this library. Thus, the terms and 023 conditions of the GNU General Public License cover the whole 024 combination. 025 026 As a special exception, the copyright holders of this library give you 027 permission to link this library with independent modules to produce an 028 executable, regardless of the license terms of these independent 029 modules, and to copy and distribute the resulting executable under 030 terms of your choice, provided that you also meet, for each linked 031 independent module, the terms and conditions of the license of that 032 module. An independent module is a module which is not derived from 033 or based on this library. If you modify this library, you may extend 034 this exception to your version of the library, but you are not 035 obligated to do so. If you do not wish to do so, delete this 036 exception statement from your version. */ 037 038 039 package javax.crypto; 040 041 import gnu.java.security.Engine; 042 043 import java.lang.reflect.InvocationTargetException; 044 import java.nio.ByteBuffer; 045 import java.security.InvalidAlgorithmParameterException; 046 import java.security.InvalidKeyException; 047 import java.security.Key; 048 import java.security.NoSuchAlgorithmException; 049 import java.security.NoSuchProviderException; 050 import java.security.Provider; 051 import java.security.Security; 052 import 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 */ 073 public 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 }