Mac.java revision cec4dd4b1d33f78997603d0f89c0d0e56e64dbcd
1/* 2 * Licensed to the Apache Software Foundation (ASF) under one or more 3 * contributor license agreements. See the NOTICE file distributed with 4 * this work for additional information regarding copyright ownership. 5 * The ASF licenses this file to You under the Apache License, Version 2.0 6 * (the "License"); you may not use this file except in compliance with 7 * the License. You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 18package javax.crypto; 19 20import java.nio.ByteBuffer; 21import java.security.InvalidAlgorithmParameterException; 22import java.security.InvalidKeyException; 23import java.security.Key; 24import java.security.NoSuchAlgorithmException; 25import java.security.NoSuchProviderException; 26import java.security.Provider; 27import java.security.Security; 28import java.security.spec.AlgorithmParameterSpec; 29 30import org.apache.harmony.crypto.internal.nls.Messages; 31import org.apache.harmony.security.fortress.Engine; 32 33 34/** 35 * This class provides the public API for <i>Message Authentication Code</i> 36 * (MAC) algorithms. 37 */ 38public class Mac implements Cloneable { 39 40 //Used to access common engine functionality 41 private static final Engine engine = new Engine("Mac"); //$NON-NLS-1$ 42 43 // Store used provider 44 private final Provider provider; 45 46 // Store used spi implementation 47 private final MacSpi spiImpl; 48 49 // Store used algorithm name 50 private final String algorithm; 51 52 // Store Mac state (initialized or not initialized) 53 private boolean isInitMac; 54 55 /** 56 * Creates a new {@code Mac} instance. 57 * 58 * @param macSpi 59 * the implementation delegate. 60 * @param provider 61 * the implementation provider. 62 * @param algorithm 63 * the name of the MAC algorithm. 64 */ 65 protected Mac(MacSpi macSpi, Provider provider, String algorithm) { 66 this.provider = provider; 67 this.algorithm = algorithm; 68 this.spiImpl = macSpi; 69 this.isInitMac = false; 70 } 71 72 /** 73 * Returns the name of the MAC algorithm. 74 * 75 * @return the name of the MAC algorithm. 76 */ 77 public final String getAlgorithm() { 78 return algorithm; 79 } 80 81 /** 82 * Returns the provider of this {@code Mac} instance. 83 * 84 * @return the provider of this {@code Mac} instance. 85 */ 86 public final Provider getProvider() { 87 return provider; 88 } 89 90 /** 91 * Creates a new {@code Mac} instance that provides the specified MAC 92 * algorithm. 93 * 94 * @param algorithm 95 * the name of the requested MAC algorithm. 96 * @return the new {@code Mac} instance. 97 * @throws NoSuchAlgorithmException 98 * if the specified algorithm is not available by any provider. 99 * @throws NullPointerException 100 * if {@code algorithm} is {@code null} (instead of 101 * NoSuchAlgorithmException as in 1.4 release). 102 */ 103 public static final Mac getInstance(String algorithm) 104 throws NoSuchAlgorithmException { 105 if (algorithm == null) { 106 throw new NullPointerException(Messages.getString("crypto.02")); //$NON-NLS-1$ 107 } 108 synchronized (engine) { 109 engine.getInstance(algorithm, null); 110 return new Mac((MacSpi) engine.spi, engine.provider, algorithm); 111 } 112 } 113 114 /** 115 * Creates a new {@code Mac} instance that provides the specified MAC 116 * algorithm from the specified provider. 117 * 118 * @param algorithm 119 * the name of the requested MAC algorithm. 120 * @param provider 121 * the name of the provider that is providing the algorithm. 122 * @return the new {@code Mac} instance. 123 * @throws NoSuchAlgorithmException 124 * if the specified algorithm is not provided by the specified 125 * provider. 126 * @throws NoSuchProviderException 127 * if the specified provider is not available. 128 * @throws IllegalArgumentException 129 * if the specified provider name is {@code null} or empty. 130 * @throws NullPointerException 131 * if {@code algorithm} is {@code null} (instead of 132 * NoSuchAlgorithmException as in 1.4 release). 133 */ 134 public static final Mac getInstance(String algorithm, String provider) 135 throws NoSuchAlgorithmException, NoSuchProviderException { 136 if ((provider == null) || (provider.length() == 0)) { 137 throw new IllegalArgumentException(Messages.getString("crypto.03")); //$NON-NLS-1$ 138 } 139 Provider impProvider = Security.getProvider(provider); 140 if (impProvider == null) { 141 throw new NoSuchProviderException(provider); 142 } 143 return getInstance(algorithm, impProvider); 144 } 145 146 /** 147 * Creates a new {@code Mac} instance that provides the specified MAC 148 * algorithm from the specified provider. 149 * 150 * @param algorithm 151 * the name of the requested MAC algorithm. 152 * @param provider 153 * the provider that is providing the algorithm. 154 * @return the new {@code Mac} instance. 155 * @throws NoSuchAlgorithmException 156 * if the specified algorithm is not provided by the specified 157 * provider. 158 * @throws IllegalArgumentException 159 * if {@code provider} is {@code null}. 160 * @throws NullPointerException 161 * if {@code algorithm} is {@code null} (instead of 162 * NoSuchAlgorithmException as in 1.4 release). 163 */ 164 public static final Mac getInstance(String algorithm, Provider provider) 165 throws NoSuchAlgorithmException { 166 if (provider == null) { 167 throw new IllegalArgumentException(Messages.getString("crypto.04")); //$NON-NLS-1$ 168 } 169 if (algorithm == null) { 170 throw new NullPointerException(Messages.getString("crypto.02")); //$NON-NLS-1$ 171 } 172 synchronized (engine) { 173 engine.getInstance(algorithm, provider, null); 174 return new Mac((MacSpi) engine.spi, provider, algorithm); 175 } 176 } 177 178 /** 179 * Returns the length of this MAC (in bytes). 180 * 181 * @return the length of this MAC (in bytes). 182 */ 183 public final int getMacLength() { 184 return spiImpl.engineGetMacLength(); 185 } 186 187 /** 188 * Initializes this {@code Mac} instance with the specified key and 189 * algorithm parameters. 190 * 191 * @param key 192 * the key to initialize this algorithm. 193 * @param params 194 * the parameters for this algorithm. 195 * @throws InvalidKeyException 196 * if the specified key cannot be used to initialize this 197 * algorithm, or it is null. 198 * @throws InvalidAlgorithmParameterException 199 * if the specified parameters cannot be used to initialize this 200 * algorithm. 201 */ 202 public final void init(Key key, AlgorithmParameterSpec params) 203 throws InvalidKeyException, InvalidAlgorithmParameterException { 204 if (key == null) { 205 throw new InvalidKeyException(Messages.getString("crypto.05")); //$NON-NLS-1$ 206 } 207 spiImpl.engineInit(key, params); 208 isInitMac = true; 209 } 210 211 /** 212 * Initializes this {@code Mac} instance with the specified key. 213 * 214 * @param key 215 * the key to initialize this algorithm. 216 * @throws InvalidKeyException 217 * if initialization fails because the provided key is {@code 218 * null}. 219 * @throws RuntimeException 220 * if the specified key cannot be used to initialize this 221 * algorithm. 222 */ 223 public final void init(Key key) throws InvalidKeyException { 224 if (key == null) { 225 throw new InvalidKeyException(Messages.getString("crypto.05")); //$NON-NLS-1$ 226 } 227 try { 228 spiImpl.engineInit(key, null); 229 isInitMac = true; 230 } catch (InvalidAlgorithmParameterException e) { 231 throw new RuntimeException(e); 232 } 233 } 234 235 /** 236 * Updates this {@code Mac} instance with the specified byte. 237 * 238 * @param input 239 * the byte 240 * @throws IllegalStateException 241 * if this MAC is not initialized. 242 */ 243 public final void update(byte input) throws IllegalStateException { 244 if (!isInitMac) { 245 throw new IllegalStateException(Messages.getString("crypto.01")); 246 } 247 spiImpl.engineUpdate(input); 248 } 249 250 /** 251 * Updates this {@code Mac} instance with the data from the specified buffer 252 * {@code input} from the specified {@code offset} and length {@code len}. 253 * 254 * @param input 255 * the buffer. 256 * @param offset 257 * the offset in the buffer. 258 * @param len 259 * the length of the data in the buffer. 260 * @throws IllegalStateException 261 * if this MAC is not initialized. 262 * @throws IllegalArgumentException 263 * if {@code offset} and {@code len} do not specified a valid 264 * chunk in {@code input} buffer. 265 */ 266 public final void update(byte[] input, int offset, int len) 267 throws IllegalStateException { 268 if (!isInitMac) { 269 throw new IllegalStateException(Messages.getString("crypto.01")); 270 } 271 if (input == null) { 272 return; 273 } 274 if ((offset < 0) || (len < 0) || ((offset + len) > input.length)) { 275 throw new IllegalArgumentException(Messages.getString("crypto.06")); //$NON-NLS-1$ 276 } 277 spiImpl.engineUpdate(input, offset, len); 278 } 279 280 /** 281 * Copies the buffer provided as input for further processing. 282 * 283 * @param input 284 * the buffer. 285 * @throws IllegalStateException 286 * if this MAC is not initialized. 287 */ 288 public final void update(byte[] input) throws IllegalStateException { 289 if (!isInitMac) { 290 throw new IllegalStateException(Messages.getString("crypto.01")); 291 } 292 if (input != null) { 293 spiImpl.engineUpdate(input, 0, input.length); 294 } 295 } 296 297 /** 298 * Updates this {@code Mac} instance with the data from the specified 299 * buffer, starting at {@link ByteBuffer#position()}, including the next 300 * {@link ByteBuffer#remaining()} bytes. 301 * 302 * @param input 303 * the buffer. 304 * @throws IllegalStateException 305 * if this MAC is not initialized. 306 */ 307 public final void update(ByteBuffer input) { 308 if (!isInitMac) { 309 throw new IllegalStateException(Messages.getString("crypto.01")); 310 } 311 if (input != null) { 312 spiImpl.engineUpdate(input); 313 } else { 314 throw new IllegalArgumentException(Messages.getString("crypto.07")); //$NON-NLS-1$ 315 } 316 } 317 318 /** 319 * Computes the digest of this MAC based on the data previously specified in 320 * {@link #update} calls. 321 * <p> 322 * This {@code Mac} instance is reverted to its initial state and can be 323 * used to start the next MAC computation with the same parameters or 324 * initialized with different parameters. 325 * 326 * @return the generated digest. 327 * @throws IllegalStateException 328 * if this MAC is not initialized. 329 */ 330 public final byte[] doFinal() throws IllegalStateException { 331 if (!isInitMac) { 332 throw new IllegalStateException(Messages.getString("crypto.01")); 333 } 334 return spiImpl.engineDoFinal(); 335 } 336 337 /** 338 * Computes the digest of this MAC based on the data previously specified in 339 * {@link #update} calls and stores the digest in the specified {@code 340 * output} buffer at offset {@code outOffset}. 341 * <p> 342 * This {@code Mac} instance is reverted to its initial state and can be 343 * used to start the next MAC computation with the same parameters or 344 * initialized with different parameters. 345 * 346 * @param output 347 * the output buffer 348 * @param outOffset 349 * the offset in the output buffer 350 * @throws ShortBufferException 351 * if the specified output buffer is either too small for the 352 * digest to be stored, the specified output buffer is {@code 353 * null}, or the specified offset is negative or past the length 354 * of the output buffer. 355 * @throws IllegalStateException 356 * if this MAC is not initialized. 357 */ 358 public final void doFinal(byte[] output, int outOffset) 359 throws ShortBufferException, IllegalStateException { 360 if (!isInitMac) { 361 throw new IllegalStateException(Messages.getString("crypto.01")); 362 } 363 if (output == null) { 364 throw new ShortBufferException(Messages.getString("crypto.08")); //$NON-NLS-1$ 365 } 366 if ((outOffset < 0) || (outOffset >= output.length)) { 367 throw new ShortBufferException(Messages.getString("crypto.09", //$NON-NLS-1$ 368 Integer.toString(outOffset))); 369 } 370 int t = spiImpl.engineGetMacLength(); 371 if (t > (output.length - outOffset)) { 372 throw new ShortBufferException( 373 Messages.getString("crypto.0A", //$NON-NLS-1$ 374 Integer.toString(t))); 375 } 376 byte[] result = spiImpl.engineDoFinal(); 377 System.arraycopy(result, 0, output, outOffset, result.length); 378 379 } 380 381 /** 382 * Computes the digest of this MAC based on the data previously specified on 383 * {@link #update} calls and on the final bytes specified by {@code input} 384 * (or based on those bytes only). 385 * <p> 386 * This {@code Mac} instance is reverted to its initial state and can be 387 * used to start the next MAC computation with the same parameters or 388 * initialized with different parameters. 389 * 390 * @param input 391 * the final bytes. 392 * @return the generated digest. 393 * @throws IllegalStateException 394 * if this MAC is not initialized. 395 */ 396 public final byte[] doFinal(byte[] input) throws IllegalStateException { 397 if (!isInitMac) { 398 throw new IllegalStateException(Messages.getString("crypto.0B")); //$NON-NLS-1$ 399 } 400 if (input != null) { 401 spiImpl.engineUpdate(input, 0, input.length); 402 } 403 return spiImpl.engineDoFinal(); 404 } 405 406 /** 407 * Resets this {@code Mac} instance to its initial state. 408 * <p> 409 * This {@code Mac} instance is reverted to its initial state and can be 410 * used to start the next MAC computation with the same parameters or 411 * initialized with different parameters. 412 */ 413 public final void reset() { 414 spiImpl.engineReset(); 415 } 416 417 /** 418 * Clones this {@code Mac} instance and the underlying implementation. 419 * 420 * @return the cloned instance. 421 * @throws CloneNotSupportedException 422 * if the underlying implementation does not support cloning. 423 */ 424 @Override 425 public final Object clone() throws CloneNotSupportedException { 426 MacSpi newSpiImpl = (MacSpi)spiImpl.clone(); 427 Mac mac = new Mac(newSpiImpl, this.provider, this.algorithm); 428 mac.isInitMac = this.isInitMac; 429 return mac; 430 } 431}