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