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