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.ProviderException; 28 import java.security.Security; 29 import java.security.spec.AlgorithmParameterSpec; 30 import java.util.ArrayList; 31 import 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 public class Mac implements Cloneable { 39 40 // The service name. 41 private static final String SERVICE = "Mac"; 42 43 //Used to access common engine functionality 44 private static final Engine ENGINE = new Engine(SERVICE); 45 46 // Store used provider 47 private Provider provider; 48 49 // Provider that was requested during creation. 50 private final Provider specifiedProvider; 51 52 // Store used spi implementation 53 private MacSpi spiImpl; 54 55 // Store used algorithm name 56 private final String algorithm; 57 58 /** 59 * Lock held while the SPI is initializing. 60 */ 61 private final Object initLock = new Object(); 62 63 // Store Mac state (initialized or not initialized) 64 private boolean isInitMac; 65 66 /** 67 * Creates a new {@code Mac} instance. 68 * 69 * @param macSpi 70 * the implementation delegate. 71 * @param provider 72 * the implementation provider. 73 * @param algorithm 74 * the name of the MAC algorithm. 75 */ Mac(MacSpi macSpi, Provider provider, String algorithm)76 protected Mac(MacSpi macSpi, Provider provider, String algorithm) { 77 this.specifiedProvider = provider; 78 this.algorithm = algorithm; 79 this.spiImpl = macSpi; 80 this.isInitMac = false; 81 } 82 83 /** 84 * Returns the name of the MAC algorithm. 85 * 86 * @return the name of the MAC algorithm. 87 */ getAlgorithm()88 public final String getAlgorithm() { 89 return algorithm; 90 } 91 92 /** 93 * Returns the provider of this {@code Mac} instance. 94 * 95 * @return the provider of this {@code Mac} instance. 96 */ getProvider()97 public final Provider getProvider() { 98 getSpi(); 99 return provider; 100 } 101 102 /** 103 * Creates a new {@code Mac} instance that provides the specified MAC 104 * algorithm. 105 * 106 * @param algorithm 107 * the name of the requested MAC algorithm. 108 * @return the new {@code Mac} instance. 109 * @throws NoSuchAlgorithmException 110 * if the specified algorithm is not available by any provider. 111 * @throws NullPointerException 112 * if {@code algorithm} is {@code null} (instead of 113 * NoSuchAlgorithmException as in 1.4 release). 114 */ getInstance(String algorithm)115 public static final Mac getInstance(String algorithm) 116 throws NoSuchAlgorithmException { 117 return getMac(algorithm, null); 118 } 119 120 /** 121 * Creates a new {@code Mac} instance that provides the specified MAC 122 * algorithm from the specified provider. 123 * 124 * @param algorithm 125 * the name of the requested MAC algorithm. 126 * @param provider 127 * the name of the provider that is providing the algorithm. 128 * @return the new {@code Mac} instance. 129 * @throws NoSuchAlgorithmException 130 * if the specified algorithm is not provided by the specified 131 * provider. 132 * @throws NoSuchProviderException 133 * if the specified provider is not available. 134 * @throws IllegalArgumentException 135 * if the specified provider name is {@code null} or empty. 136 * @throws NullPointerException 137 * if {@code algorithm} is {@code null} (instead of 138 * NoSuchAlgorithmException as in 1.4 release). 139 */ getInstance(String algorithm, String provider)140 public static final Mac getInstance(String algorithm, String provider) 141 throws NoSuchAlgorithmException, NoSuchProviderException { 142 if (provider == null || provider.isEmpty()) { 143 throw new IllegalArgumentException("Provider is null or empty"); 144 } 145 Provider impProvider = Security.getProvider(provider); 146 if (impProvider == null) { 147 throw new NoSuchProviderException(provider); 148 } 149 return getMac(algorithm, impProvider); 150 } 151 152 /** 153 * Creates a new {@code Mac} instance that provides the specified MAC 154 * algorithm from the specified provider. The {@code provider} supplied 155 * does not have to be registered. 156 * 157 * @param algorithm 158 * the name of the requested MAC algorithm. 159 * @param provider 160 * the provider that is providing the algorithm. 161 * @return the new {@code Mac} instance. 162 * @throws NoSuchAlgorithmException 163 * if the specified algorithm is not provided by the specified 164 * provider. 165 * @throws IllegalArgumentException 166 * if {@code provider} is {@code null}. 167 * @throws NullPointerException 168 * if {@code algorithm} is {@code null} (instead of 169 * NoSuchAlgorithmException as in 1.4 release). 170 */ getInstance(String algorithm, Provider provider)171 public static final Mac getInstance(String algorithm, Provider provider) 172 throws NoSuchAlgorithmException { 173 if (provider == null) { 174 throw new IllegalArgumentException("provider == null"); 175 } 176 return getMac(algorithm, provider); 177 } 178 getMac(String algorithm, Provider provider)179 private static Mac getMac(String algorithm, Provider provider) 180 throws NoSuchAlgorithmException { 181 if (algorithm == null) { 182 throw new NullPointerException("algorithm == null"); 183 } 184 185 if (tryAlgorithm(null, provider, algorithm) == null) { 186 if (provider == null) { 187 throw new NoSuchAlgorithmException("No provider found for " + algorithm); 188 } else { 189 throw new NoSuchAlgorithmException("Provider " + provider.getName() 190 + " does not provide " + algorithm); 191 } 192 } 193 return new Mac(null, provider, algorithm); 194 } 195 tryAlgorithm(Key key, Provider provider, String algorithm)196 private static Engine.SpiAndProvider tryAlgorithm(Key key, Provider provider, String algorithm) { 197 if (provider != null) { 198 Provider.Service service = provider.getService(SERVICE, algorithm); 199 if (service == null) { 200 return null; 201 } 202 return tryAlgorithmWithProvider(key, service); 203 } 204 ArrayList<Provider.Service> services = ENGINE.getServices(algorithm); 205 if (services == null) { 206 return null; 207 } 208 for (Provider.Service service : services) { 209 Engine.SpiAndProvider sap = tryAlgorithmWithProvider(key, service); 210 if (sap != null) { 211 return sap; 212 } 213 } 214 return null; 215 } 216 tryAlgorithmWithProvider(Key key, Provider.Service service)217 private static Engine.SpiAndProvider tryAlgorithmWithProvider(Key key, Provider.Service service) { 218 try { 219 if (key != null && !service.supportsParameter(key)) { 220 return null; 221 } 222 223 Engine.SpiAndProvider sap = ENGINE.getInstance(service, null); 224 if (sap.spi == null || sap.provider == null) { 225 return null; 226 } 227 if (!(sap.spi instanceof MacSpi)) { 228 return null; 229 } 230 return sap; 231 } catch (NoSuchAlgorithmException ignored) { 232 } 233 return null; 234 } 235 236 /** 237 * Makes sure a MacSpi that matches this type is selected. 238 */ getSpi(Key key)239 private MacSpi getSpi(Key key) { 240 synchronized (initLock) { 241 if (spiImpl != null && provider != null && key == null) { 242 return spiImpl; 243 } 244 245 if (algorithm == null) { 246 return null; 247 } 248 249 final Engine.SpiAndProvider sap = tryAlgorithm(key, specifiedProvider, algorithm); 250 if (sap == null) { 251 throw new ProviderException("No provider for " + getAlgorithm()); 252 } 253 254 /* 255 * Set our Spi if we've never been initialized or if we have the Spi 256 * specified and have a null provider. 257 */ 258 if (spiImpl == null || provider != null) { 259 spiImpl = (MacSpi) sap.spi; 260 } 261 provider = sap.provider; 262 263 return spiImpl; 264 } 265 } 266 267 /** 268 * Convenience call when the Key is not available. 269 */ getSpi()270 private MacSpi getSpi() { 271 return getSpi(null); 272 } 273 274 /** 275 * Returns the length of this MAC (in bytes). 276 * 277 * @return the length of this MAC (in bytes). 278 */ getMacLength()279 public final int getMacLength() { 280 return getSpi().engineGetMacLength(); 281 } 282 283 /** 284 * Initializes this {@code Mac} instance with the specified key and 285 * algorithm parameters. 286 * 287 * @param key 288 * the key to initialize this algorithm. 289 * @param params 290 * the parameters for this algorithm. 291 * @throws InvalidKeyException 292 * if the specified key cannot be used to initialize this 293 * algorithm, or it is null. 294 * @throws InvalidAlgorithmParameterException 295 * if the specified parameters cannot be used to initialize this 296 * algorithm. 297 */ init(Key key, AlgorithmParameterSpec params)298 public final void init(Key key, AlgorithmParameterSpec params) 299 throws InvalidKeyException, InvalidAlgorithmParameterException { 300 if (key == null) { 301 throw new InvalidKeyException("key == null"); 302 } 303 getSpi(key).engineInit(key, params); 304 isInitMac = true; 305 } 306 307 /** 308 * Initializes this {@code Mac} instance with the specified key. 309 * 310 * @param key 311 * the key to initialize this algorithm. 312 * @throws InvalidKeyException 313 * if initialization fails because the provided key is {@code 314 * null}. 315 * @throws RuntimeException 316 * if the specified key cannot be used to initialize this 317 * algorithm. 318 */ init(Key key)319 public final void init(Key key) throws InvalidKeyException { 320 if (key == null) { 321 throw new InvalidKeyException("key == null"); 322 } 323 try { 324 getSpi(key).engineInit(key, null); 325 isInitMac = true; 326 } catch (InvalidAlgorithmParameterException e) { 327 throw new RuntimeException(e); 328 } 329 } 330 331 /** 332 * Updates this {@code Mac} instance with the specified byte. 333 * 334 * @param input 335 * the byte 336 * @throws IllegalStateException 337 * if this MAC is not initialized. 338 */ update(byte input)339 public final void update(byte input) throws IllegalStateException { 340 if (!isInitMac) { 341 throw new IllegalStateException(); 342 } 343 getSpi().engineUpdate(input); 344 } 345 346 /** 347 * Updates this {@code Mac} instance with the data from the specified buffer 348 * {@code input} from the specified {@code offset} and length {@code len}. 349 * 350 * @param input 351 * the buffer. 352 * @param offset 353 * the offset in the buffer. 354 * @param len 355 * the length of the data in the buffer. 356 * @throws IllegalStateException 357 * if this MAC is not initialized. 358 * @throws IllegalArgumentException 359 * if {@code offset} and {@code len} do not specified a valid 360 * chunk in {@code input} buffer. 361 */ update(byte[] input, int offset, int len)362 public final void update(byte[] input, int offset, int len) throws IllegalStateException { 363 if (!isInitMac) { 364 throw new IllegalStateException(); 365 } 366 if (input == null) { 367 return; 368 } 369 if ((offset < 0) || (len < 0) || ((offset + len) > input.length)) { 370 throw new IllegalArgumentException("Incorrect arguments." 371 + " input.length=" + input.length 372 + " offset=" + offset + ", len=" + len); 373 } 374 getSpi().engineUpdate(input, offset, len); 375 } 376 377 /** 378 * Copies the buffer provided as input for further processing. 379 * 380 * @param input 381 * the buffer. 382 * @throws IllegalStateException 383 * if this MAC is not initialized. 384 */ update(byte[] input)385 public final void update(byte[] input) throws IllegalStateException { 386 if (!isInitMac) { 387 throw new IllegalStateException(); 388 } 389 if (input != null) { 390 getSpi().engineUpdate(input, 0, input.length); 391 } 392 } 393 394 /** 395 * Updates this {@code Mac} instance with the data from the specified 396 * buffer, starting at {@link ByteBuffer#position()}, including the next 397 * {@link ByteBuffer#remaining()} bytes. 398 * 399 * @param input 400 * the buffer. 401 * @throws IllegalStateException 402 * if this MAC is not initialized. 403 */ update(ByteBuffer input)404 public final void update(ByteBuffer input) { 405 if (!isInitMac) { 406 throw new IllegalStateException(); 407 } 408 if (input != null) { 409 getSpi().engineUpdate(input); 410 } else { 411 throw new IllegalArgumentException("input == null"); 412 } 413 } 414 415 /** 416 * Computes the digest of this MAC based on the data previously specified in 417 * {@link #update} calls. 418 * <p> 419 * This {@code Mac} instance is reverted to its initial state and can be 420 * used to start the next MAC computation with the same parameters or 421 * initialized with different parameters. 422 * 423 * @return the generated digest. 424 * @throws IllegalStateException 425 * if this MAC is not initialized. 426 */ doFinal()427 public final byte[] doFinal() throws IllegalStateException { 428 if (!isInitMac) { 429 throw new IllegalStateException(); 430 } 431 return getSpi().engineDoFinal(); 432 } 433 434 /** 435 * Computes the digest of this MAC based on the data previously specified in 436 * {@link #update} calls and stores the digest in the specified {@code 437 * output} buffer at offset {@code outOffset}. 438 * <p> 439 * This {@code Mac} instance is reverted to its initial state and can be 440 * used to start the next MAC computation with the same parameters or 441 * initialized with different parameters. 442 * 443 * @param output 444 * the output buffer 445 * @param outOffset 446 * the offset in the output buffer 447 * @throws ShortBufferException 448 * if the specified output buffer is either too small for the 449 * digest to be stored, the specified output buffer is {@code 450 * null}, or the specified offset is negative or past the length 451 * of the output buffer. 452 * @throws IllegalStateException 453 * if this MAC is not initialized. 454 */ doFinal(byte[] output, int outOffset)455 public final void doFinal(byte[] output, int outOffset) 456 throws ShortBufferException, IllegalStateException { 457 if (!isInitMac) { 458 throw new IllegalStateException(); 459 } 460 if (output == null) { 461 throw new ShortBufferException("output == null"); 462 } 463 if ((outOffset < 0) || (outOffset >= output.length)) { 464 throw new ShortBufferException("Incorrect outOffset: " + outOffset); 465 } 466 MacSpi spi = getSpi(); 467 int t = spi.engineGetMacLength(); 468 if (t > (output.length - outOffset)) { 469 throw new ShortBufferException("Output buffer is short. Needed " + t + " bytes."); 470 } 471 byte[] result = spi.engineDoFinal(); 472 System.arraycopy(result, 0, output, outOffset, result.length); 473 474 } 475 476 /** 477 * Computes the digest of this MAC based on the data previously specified on 478 * {@link #update} calls and on the final bytes specified by {@code input} 479 * (or based on those bytes only). 480 * <p> 481 * This {@code Mac} instance is reverted to its initial state and can be 482 * used to start the next MAC computation with the same parameters or 483 * initialized with different parameters. 484 * 485 * @param input 486 * the final bytes. 487 * @return the generated digest. 488 * @throws IllegalStateException 489 * if this MAC is not initialized. 490 */ doFinal(byte[] input)491 public final byte[] doFinal(byte[] input) throws IllegalStateException { 492 if (!isInitMac) { 493 throw new IllegalStateException(); 494 } 495 MacSpi spi = getSpi(); 496 if (input != null) { 497 spi.engineUpdate(input, 0, input.length); 498 } 499 return spi.engineDoFinal(); 500 } 501 502 /** 503 * Resets this {@code Mac} instance to its initial state. 504 * <p> 505 * This {@code Mac} instance is reverted to its initial state and can be 506 * used to start the next MAC computation with the same parameters or 507 * initialized with different parameters. 508 */ reset()509 public final void reset() { 510 getSpi().engineReset(); 511 } 512 513 /** 514 * Clones this {@code Mac} instance and the underlying implementation. 515 * 516 * @return the cloned instance. 517 * @throws CloneNotSupportedException 518 * if the underlying implementation does not support cloning. 519 */ 520 @Override clone()521 public final Object clone() throws CloneNotSupportedException { 522 MacSpi newSpiImpl = null; 523 final MacSpi spi = getSpi(); 524 if (spi != null) { 525 newSpiImpl = (MacSpi) spi.clone(); 526 } 527 Mac mac = new Mac(newSpiImpl, this.provider, this.algorithm); 528 mac.isInitMac = this.isInitMac; 529 return mac; 530 } 531 } 532