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 boolean providerSupportsAlgorithm; 186 try { 187 providerSupportsAlgorithm = tryAlgorithm(null /* key */, provider, algorithm) != null; 188 } catch (InvalidKeyException e) { 189 throw new IllegalStateException("InvalidKeyException thrown when key == null", e); 190 } 191 if (!providerSupportsAlgorithm) { 192 if (provider == null) { 193 throw new NoSuchAlgorithmException("No provider found for " + algorithm); 194 } else { 195 throw new NoSuchAlgorithmException("Provider " + provider.getName() 196 + " does not provide " + algorithm); 197 } 198 } 199 return new Mac(null, provider, algorithm); 200 } 201 /** 202 * @throws InvalidKeyException if the specified key cannot be used to 203 * initialize this mac. 204 */ tryAlgorithm( Key key, Provider provider, String algorithm)205 private static Engine.SpiAndProvider tryAlgorithm( 206 Key key, Provider provider, String algorithm) throws InvalidKeyException { 207 if (provider != null) { 208 Provider.Service service = provider.getService(SERVICE, algorithm); 209 if (service == null) { 210 return null; 211 } 212 return tryAlgorithmWithProvider(service); 213 } 214 ArrayList<Provider.Service> services = ENGINE.getServices(algorithm); 215 if (services == null || services.isEmpty()) { 216 return null; 217 } 218 boolean keySupported = false; 219 for (Provider.Service service : services) { 220 if (key == null || service.supportsParameter(key)) { 221 keySupported = true; 222 Engine.SpiAndProvider sap = tryAlgorithmWithProvider(service); 223 if (sap != null) { 224 return sap; 225 } 226 } 227 } 228 if (!keySupported) { 229 throw new InvalidKeyException("No provider supports the provided key"); 230 } 231 return null; 232 } 233 tryAlgorithmWithProvider(Provider.Service service)234 private static Engine.SpiAndProvider tryAlgorithmWithProvider(Provider.Service service) { 235 try { 236 Engine.SpiAndProvider sap = ENGINE.getInstance(service, null); 237 if (sap.spi == null || sap.provider == null) { 238 return null; 239 } 240 if (!(sap.spi instanceof MacSpi)) { 241 return null; 242 } 243 return sap; 244 } catch (NoSuchAlgorithmException ignored) { 245 } 246 return null; 247 } 248 249 /** 250 * Makes sure a MacSpi that matches this type is selected. 251 * 252 * @throws InvalidKeyException if the specified key cannot be used to 253 * initialize this mac. 254 */ getSpi(Key key)255 private MacSpi getSpi(Key key) throws InvalidKeyException { 256 synchronized (initLock) { 257 if (spiImpl != null && provider != null && key == null) { 258 return spiImpl; 259 } 260 261 if (algorithm == null) { 262 return null; 263 } 264 265 final Engine.SpiAndProvider sap = tryAlgorithm(key, specifiedProvider, algorithm); 266 if (sap == null) { 267 throw new ProviderException("No provider for " + getAlgorithm()); 268 } 269 270 /* 271 * Set our Spi if we've never been initialized or if we have the Spi 272 * specified and have a null provider. 273 */ 274 if (spiImpl == null || provider != null) { 275 spiImpl = (MacSpi) sap.spi; 276 } 277 provider = sap.provider; 278 279 return spiImpl; 280 } 281 } 282 283 /** 284 * Convenience call when the Key is not available. 285 */ getSpi()286 private MacSpi getSpi() { 287 try { 288 return getSpi(null); 289 } catch (InvalidKeyException e) { 290 throw new IllegalStateException("InvalidKeyException thrown when key == null", e); 291 } 292 } 293 294 /** 295 * Returns the {@code MacSpi} backing this {@code Mac} or {@code null} if no {@code MacSpi} is 296 * backing this {@code Mac}. 297 * 298 * @hide 299 */ getCurrentSpi()300 public MacSpi getCurrentSpi() { 301 synchronized (initLock) { 302 return spiImpl; 303 } 304 } 305 306 /** 307 * Returns the length of this MAC (in bytes). 308 * 309 * @return the length of this MAC (in bytes). 310 */ getMacLength()311 public final int getMacLength() { 312 return getSpi().engineGetMacLength(); 313 } 314 315 /** 316 * Initializes this {@code Mac} instance with the specified key and 317 * algorithm parameters. 318 * 319 * @param key 320 * the key to initialize this algorithm. 321 * @param params 322 * the parameters for this algorithm. 323 * @throws InvalidKeyException 324 * if the specified key cannot be used to initialize this 325 * algorithm, or it is null. 326 * @throws InvalidAlgorithmParameterException 327 * if the specified parameters cannot be used to initialize this 328 * algorithm. 329 */ init(Key key, AlgorithmParameterSpec params)330 public final void init(Key key, AlgorithmParameterSpec params) 331 throws InvalidKeyException, InvalidAlgorithmParameterException { 332 if (key == null) { 333 throw new InvalidKeyException("key == null"); 334 } 335 getSpi(key).engineInit(key, params); 336 isInitMac = true; 337 } 338 339 /** 340 * Initializes this {@code Mac} instance with the specified key. 341 * 342 * @param key 343 * the key to initialize this algorithm. 344 * @throws InvalidKeyException 345 * if initialization fails because the provided key is {@code 346 * null}. 347 * @throws RuntimeException 348 * if the specified key cannot be used to initialize this 349 * algorithm. 350 */ init(Key key)351 public final void init(Key key) throws InvalidKeyException { 352 if (key == null) { 353 throw new InvalidKeyException("key == null"); 354 } 355 try { 356 getSpi(key).engineInit(key, null); 357 isInitMac = true; 358 } catch (InvalidAlgorithmParameterException e) { 359 throw new RuntimeException(e); 360 } 361 } 362 363 /** 364 * Updates this {@code Mac} instance with the specified byte. 365 * 366 * @param input 367 * the byte 368 * @throws IllegalStateException 369 * if this MAC is not initialized. 370 */ update(byte input)371 public final void update(byte input) throws IllegalStateException { 372 if (!isInitMac) { 373 throw new IllegalStateException(); 374 } 375 getSpi().engineUpdate(input); 376 } 377 378 /** 379 * Updates this {@code Mac} instance with the data from the specified buffer 380 * {@code input} from the specified {@code offset} and length {@code len}. 381 * 382 * @param input 383 * the buffer. 384 * @param offset 385 * the offset in the buffer. 386 * @param len 387 * the length of the data in the buffer. 388 * @throws IllegalStateException 389 * if this MAC is not initialized. 390 * @throws IllegalArgumentException 391 * if {@code offset} and {@code len} do not specified a valid 392 * chunk in {@code input} buffer. 393 */ update(byte[] input, int offset, int len)394 public final void update(byte[] input, int offset, int len) throws IllegalStateException { 395 if (!isInitMac) { 396 throw new IllegalStateException(); 397 } 398 if (input == null) { 399 return; 400 } 401 if ((offset < 0) || (len < 0) || ((offset + len) > input.length)) { 402 throw new IllegalArgumentException("Incorrect arguments." 403 + " input.length=" + input.length 404 + " offset=" + offset + ", len=" + len); 405 } 406 getSpi().engineUpdate(input, offset, len); 407 } 408 409 /** 410 * Copies the buffer provided as input for further processing. 411 * 412 * @param input 413 * the buffer. 414 * @throws IllegalStateException 415 * if this MAC is not initialized. 416 */ update(byte[] input)417 public final void update(byte[] input) throws IllegalStateException { 418 if (!isInitMac) { 419 throw new IllegalStateException(); 420 } 421 if (input != null) { 422 getSpi().engineUpdate(input, 0, input.length); 423 } 424 } 425 426 /** 427 * Updates this {@code Mac} instance with the data from the specified 428 * buffer, starting at {@link ByteBuffer#position()}, including the next 429 * {@link ByteBuffer#remaining()} bytes. 430 * 431 * @param input 432 * the buffer. 433 * @throws IllegalStateException 434 * if this MAC is not initialized. 435 */ update(ByteBuffer input)436 public final void update(ByteBuffer input) { 437 if (!isInitMac) { 438 throw new IllegalStateException(); 439 } 440 if (input != null) { 441 getSpi().engineUpdate(input); 442 } else { 443 throw new IllegalArgumentException("input == null"); 444 } 445 } 446 447 /** 448 * Computes the digest of this MAC based on the data previously specified in 449 * {@link #update} calls. 450 * <p> 451 * This {@code Mac} instance is reverted to its initial state and can be 452 * used to start the next MAC computation with the same parameters or 453 * initialized with different parameters. 454 * 455 * @return the generated digest. 456 * @throws IllegalStateException 457 * if this MAC is not initialized. 458 */ doFinal()459 public final byte[] doFinal() throws IllegalStateException { 460 if (!isInitMac) { 461 throw new IllegalStateException(); 462 } 463 return getSpi().engineDoFinal(); 464 } 465 466 /** 467 * Computes the digest of this MAC based on the data previously specified in 468 * {@link #update} calls and stores the digest in the specified {@code 469 * output} buffer at offset {@code outOffset}. 470 * <p> 471 * This {@code Mac} instance is reverted to its initial state and can be 472 * used to start the next MAC computation with the same parameters or 473 * initialized with different parameters. 474 * 475 * @param output 476 * the output buffer 477 * @param outOffset 478 * the offset in the output buffer 479 * @throws ShortBufferException 480 * if the specified output buffer is either too small for the 481 * digest to be stored, the specified output buffer is {@code 482 * null}, or the specified offset is negative or past the length 483 * of the output buffer. 484 * @throws IllegalStateException 485 * if this MAC is not initialized. 486 */ doFinal(byte[] output, int outOffset)487 public final void doFinal(byte[] output, int outOffset) 488 throws ShortBufferException, IllegalStateException { 489 if (!isInitMac) { 490 throw new IllegalStateException(); 491 } 492 if (output == null) { 493 throw new ShortBufferException("output == null"); 494 } 495 if ((outOffset < 0) || (outOffset >= output.length)) { 496 throw new ShortBufferException("Incorrect outOffset: " + outOffset); 497 } 498 MacSpi spi = getSpi(); 499 int t = spi.engineGetMacLength(); 500 if (t > (output.length - outOffset)) { 501 throw new ShortBufferException("Output buffer is short. Needed " + t + " bytes."); 502 } 503 byte[] result = spi.engineDoFinal(); 504 System.arraycopy(result, 0, output, outOffset, result.length); 505 506 } 507 508 /** 509 * Computes the digest of this MAC based on the data previously specified on 510 * {@link #update} calls and on the final bytes specified by {@code input} 511 * (or based on those bytes only). 512 * <p> 513 * This {@code Mac} instance is reverted to its initial state and can be 514 * used to start the next MAC computation with the same parameters or 515 * initialized with different parameters. 516 * 517 * @param input 518 * the final bytes. 519 * @return the generated digest. 520 * @throws IllegalStateException 521 * if this MAC is not initialized. 522 */ doFinal(byte[] input)523 public final byte[] doFinal(byte[] input) throws IllegalStateException { 524 if (!isInitMac) { 525 throw new IllegalStateException(); 526 } 527 MacSpi spi = getSpi(); 528 if (input != null) { 529 spi.engineUpdate(input, 0, input.length); 530 } 531 return spi.engineDoFinal(); 532 } 533 534 /** 535 * Resets this {@code Mac} instance to its initial state. 536 * <p> 537 * This {@code Mac} instance is reverted to its initial state and can be 538 * used to start the next MAC computation with the same parameters or 539 * initialized with different parameters. 540 */ reset()541 public final void reset() { 542 getSpi().engineReset(); 543 } 544 545 /** 546 * Clones this {@code Mac} instance and the underlying implementation. 547 * 548 * @return the cloned instance. 549 * @throws CloneNotSupportedException 550 * if the underlying implementation does not support cloning. 551 */ 552 @Override clone()553 public final Object clone() throws CloneNotSupportedException { 554 MacSpi newSpiImpl = null; 555 final MacSpi spi = getSpi(); 556 if (spi != null) { 557 newSpiImpl = (MacSpi) spi.clone(); 558 } 559 Mac mac = new Mac(newSpiImpl, this.provider, this.algorithm); 560 mac.isInitMac = this.isInitMac; 561 return mac; 562 } 563 } 564