1 /* 2 * Copyright (c) 1998, 2017, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package javax.crypto; 27 28 import java.util.*; 29 30 import java.security.*; 31 import java.security.Provider.Service; 32 import java.security.spec.AlgorithmParameterSpec; 33 34 import java.nio.ByteBuffer; 35 36 import sun.security.util.Debug; 37 import sun.security.jca.*; 38 import sun.security.jca.GetInstance.Instance; 39 40 /** 41 * This class provides the functionality of a "Message Authentication Code" 42 * (MAC) algorithm. 43 * 44 * <p> A MAC provides a way to check 45 * the integrity of information transmitted over or stored in an unreliable 46 * medium, based on a secret key. Typically, message 47 * authentication codes are used between two parties that share a secret 48 * key in order to validate information transmitted between these 49 * parties. 50 * 51 * <p> A MAC mechanism that is based on cryptographic hash functions is 52 * referred to as HMAC. HMAC can be used with any cryptographic hash function, 53 * e.g., SHA256 or SHA384, in combination with a secret shared key. HMAC is 54 * specified in RFC 2104. 55 * 56 * <p> Every implementation of the Java platform is required to support 57 * the following standard {@code Mac} algorithms: 58 * <ul> 59 * <li>{@code HmacMD5}</li> 60 * <li>{@code HmacSHA1}</li> 61 * <li>{@code HmacSHA256}</li> 62 * </ul> 63 * These algorithms are described in the 64 * <a href="{@docRoot}/../specs/security/standard-names.html#mac-algorithms"> 65 * Mac section</a> of the 66 * Java Security Standard Algorithm Names Specification. 67 * Consult the release documentation for your implementation to see if any 68 * other algorithms are supported. 69 * 70 * @author Jan Luehe 71 * 72 * @since 1.4 73 */ 74 75 public class Mac implements Cloneable { 76 77 private static final Debug debug = 78 Debug.getInstance("jca", "Mac"); 79 80 private static final Debug pdebug = 81 Debug.getInstance("provider", "Provider"); 82 private static final boolean skipDebug = 83 Debug.isOn("engine=") && !Debug.isOn("mac"); 84 85 // The provider 86 private Provider provider; 87 88 // The provider implementation (delegate) 89 private MacSpi spi; 90 91 // The name of the MAC algorithm. 92 private final String algorithm; 93 94 // Has this object been initialized? 95 private boolean initialized = false; 96 97 // next service to try in provider selection 98 // null once provider is selected 99 private Service firstService; 100 101 // remaining services to try in provider selection 102 // null once provider is selected 103 private Iterator<Service> serviceIterator; 104 105 private final Object lock; 106 107 /** 108 * Creates a MAC object. 109 * 110 * @param macSpi the delegate 111 * @param provider the provider 112 * @param algorithm the algorithm 113 */ Mac(MacSpi macSpi, Provider provider, String algorithm)114 protected Mac(MacSpi macSpi, Provider provider, String algorithm) { 115 this.spi = macSpi; 116 this.provider = provider; 117 this.algorithm = algorithm; 118 serviceIterator = null; 119 lock = null; 120 } 121 Mac(Service s, Iterator<Service> t, String algorithm)122 private Mac(Service s, Iterator<Service> t, String algorithm) { 123 firstService = s; 124 serviceIterator = t; 125 this.algorithm = algorithm; 126 lock = new Object(); 127 } 128 129 /** 130 * Returns the algorithm name of this {@code Mac} object. 131 * 132 * <p>This is the same name that was specified in one of the 133 * {@code getInstance} calls that created this 134 * {@code Mac} object. 135 * 136 * @return the algorithm name of this {@code Mac} object. 137 */ getAlgorithm()138 public final String getAlgorithm() { 139 return this.algorithm; 140 } 141 142 /** 143 * Returns a {@code Mac} object that implements the 144 * specified MAC algorithm. 145 * 146 * <p> This method traverses the list of registered security Providers, 147 * starting with the most preferred Provider. 148 * A new Mac object encapsulating the 149 * MacSpi implementation from the first 150 * Provider that supports the specified algorithm is returned. 151 * 152 * <p> Note that the list of registered providers may be retrieved via 153 * the {@link Security#getProviders() Security.getProviders()} method. 154 * 155 * @implNote 156 * The JDK Reference Implementation additionally uses the 157 * {@code jdk.security.provider.preferred} 158 * {@link Security#getProperty(String) Security} property to determine 159 * the preferred provider order for the specified algorithm. This 160 * may be different than the order of providers returned by 161 * {@link Security#getProviders() Security.getProviders()}. 162 * 163 * @param algorithm the standard name of the requested MAC algorithm. 164 * See the Mac section in the <a href= 165 * "{@docRoot}/../specs/security/standard-names.html#mac-algorithms"> 166 * Java Security Standard Algorithm Names Specification</a> 167 * for information about standard algorithm names. 168 * 169 * @return the new {@code Mac} object 170 * 171 * @throws NoSuchAlgorithmException if no {@code Provider} supports a 172 * {@code MacSpi} implementation for the specified algorithm 173 * 174 * @throws NullPointerException if {@code algorithm} is {@code null} 175 * 176 * @see java.security.Provider 177 */ getInstance(String algorithm)178 public static final Mac getInstance(String algorithm) 179 throws NoSuchAlgorithmException { 180 Objects.requireNonNull(algorithm, "null algorithm name"); 181 List<Service> services = GetInstance.getServices("Mac", algorithm); 182 // make sure there is at least one service from a signed provider 183 Iterator<Service> t = services.iterator(); 184 while (t.hasNext()) { 185 Service s = t.next(); 186 if (JceSecurity.canUseProvider(s.getProvider()) == false) { 187 continue; 188 } 189 return new Mac(s, t, algorithm); 190 } 191 throw new NoSuchAlgorithmException 192 ("Algorithm " + algorithm + " not available"); 193 } 194 195 /** 196 * Returns a {@code Mac} object that implements the 197 * specified MAC algorithm. 198 * 199 * <p> A new Mac object encapsulating the 200 * MacSpi implementation from the specified provider 201 * is returned. The specified provider must be registered 202 * in the security provider list. 203 * 204 * <p> Note that the list of registered providers may be retrieved via 205 * the {@link Security#getProviders() Security.getProviders()} method. 206 * 207 * @param algorithm the standard name of the requested MAC algorithm. 208 * See the Mac section in the <a href= 209 * "{@docRoot}/../specs/security/standard-names.html#mac-algorithms"> 210 * Java Security Standard Algorithm Names Specification</a> 211 * for information about standard algorithm names. 212 * 213 * @param provider the name of the provider. 214 * 215 * @return the new {@code Mac} object 216 * 217 * @throws IllegalArgumentException if the {@code provider} 218 * is {@code null} or empty 219 * 220 * @throws NoSuchAlgorithmException if a {@code MacSpi} 221 * implementation for the specified algorithm is not 222 * available from the specified provider 223 * 224 * @throws NoSuchProviderException if the specified provider is not 225 * registered in the security provider list 226 * 227 * @throws NullPointerException if {@code algorithm} is {@code null} 228 * 229 * @see java.security.Provider 230 */ getInstance(String algorithm, String provider)231 public static final Mac getInstance(String algorithm, String provider) 232 throws NoSuchAlgorithmException, NoSuchProviderException { 233 Objects.requireNonNull(algorithm, "null algorithm name"); 234 Instance instance = JceSecurity.getInstance 235 ("Mac", MacSpi.class, algorithm, provider); 236 return new Mac((MacSpi)instance.impl, instance.provider, algorithm); 237 } 238 239 /** 240 * Returns a {@code Mac} object that implements the 241 * specified MAC algorithm. 242 * 243 * <p> A new Mac object encapsulating the 244 * MacSpi implementation from the specified Provider 245 * object is returned. Note that the specified Provider object 246 * does not have to be registered in the provider list. 247 * 248 * @param algorithm the standard name of the requested MAC algorithm. 249 * See the Mac section in the <a href= 250 * "{@docRoot}/../specs/security/standard-names.html#mac-algorithms"> 251 * Java Security Standard Algorithm Names Specification</a> 252 * for information about standard algorithm names. 253 * 254 * @param provider the provider. 255 * 256 * @return the new {@code Mac} object 257 * 258 * @throws IllegalArgumentException if the {@code provider} is 259 * {@code null} 260 * 261 * @throws NoSuchAlgorithmException if a {@code MacSpi} 262 * implementation for the specified algorithm is not available 263 * from the specified {@code Provider} object 264 * 265 * @throws NullPointerException if {@code algorithm} is {@code null} 266 * 267 * @see java.security.Provider 268 */ getInstance(String algorithm, Provider provider)269 public static final Mac getInstance(String algorithm, Provider provider) 270 throws NoSuchAlgorithmException { 271 Objects.requireNonNull(algorithm, "null algorithm name"); 272 Instance instance = JceSecurity.getInstance 273 ("Mac", MacSpi.class, algorithm, provider); 274 return new Mac((MacSpi)instance.impl, instance.provider, algorithm); 275 } 276 277 // max number of debug warnings to print from chooseFirstProvider() 278 private static int warnCount = 10; 279 280 /** 281 * Choose the Spi from the first provider available. Used if 282 * delayed provider selection is not possible because init() 283 * is not the first method called. 284 */ chooseFirstProvider()285 void chooseFirstProvider() { 286 if ((spi != null) || (serviceIterator == null)) { 287 return; 288 } 289 synchronized (lock) { 290 if (spi != null) { 291 return; 292 } 293 if (debug != null) { 294 int w = --warnCount; 295 if (w >= 0) { 296 debug.println("Mac.init() not first method " 297 + "called, disabling delayed provider selection"); 298 if (w == 0) { 299 debug.println("Further warnings of this type will " 300 + "be suppressed"); 301 } 302 new Exception("Call trace").printStackTrace(); 303 } 304 } 305 Exception lastException = null; 306 while ((firstService != null) || serviceIterator.hasNext()) { 307 Service s; 308 if (firstService != null) { 309 s = firstService; 310 firstService = null; 311 } else { 312 s = serviceIterator.next(); 313 } 314 if (JceSecurity.canUseProvider(s.getProvider()) == false) { 315 continue; 316 } 317 try { 318 Object obj = s.newInstance(null); 319 if (obj instanceof MacSpi == false) { 320 continue; 321 } 322 spi = (MacSpi)obj; 323 provider = s.getProvider(); 324 // not needed any more 325 firstService = null; 326 serviceIterator = null; 327 return; 328 } catch (NoSuchAlgorithmException e) { 329 lastException = e; 330 } 331 } 332 ProviderException e = new ProviderException 333 ("Could not construct MacSpi instance"); 334 if (lastException != null) { 335 e.initCause(lastException); 336 } 337 throw e; 338 } 339 } 340 chooseProvider(Key key, AlgorithmParameterSpec params)341 private void chooseProvider(Key key, AlgorithmParameterSpec params) 342 throws InvalidKeyException, InvalidAlgorithmParameterException { 343 synchronized (lock) { 344 if (spi != null) { 345 spi.engineInit(key, params); 346 return; 347 } 348 Exception lastException = null; 349 while ((firstService != null) || serviceIterator.hasNext()) { 350 Service s; 351 if (firstService != null) { 352 s = firstService; 353 firstService = null; 354 } else { 355 s = serviceIterator.next(); 356 } 357 // if provider says it does not support this key, ignore it 358 if (s.supportsParameter(key) == false) { 359 continue; 360 } 361 if (JceSecurity.canUseProvider(s.getProvider()) == false) { 362 continue; 363 } 364 try { 365 MacSpi spi = (MacSpi)s.newInstance(null); 366 spi.engineInit(key, params); 367 provider = s.getProvider(); 368 this.spi = spi; 369 firstService = null; 370 serviceIterator = null; 371 return; 372 } catch (Exception e) { 373 // NoSuchAlgorithmException from newInstance() 374 // InvalidKeyException from init() 375 // RuntimeException (ProviderException) from init() 376 if (lastException == null) { 377 lastException = e; 378 } 379 } 380 } 381 // no working provider found, fail 382 if (lastException instanceof InvalidKeyException) { 383 throw (InvalidKeyException)lastException; 384 } 385 if (lastException instanceof InvalidAlgorithmParameterException) { 386 throw (InvalidAlgorithmParameterException)lastException; 387 } 388 if (lastException instanceof RuntimeException) { 389 throw (RuntimeException)lastException; 390 } 391 String kName = (key != null) ? key.getClass().getName() : "(null)"; 392 throw new InvalidKeyException 393 ("No installed provider supports this key: " 394 + kName, lastException); 395 } 396 } 397 398 /** 399 * Returns the provider of this {@code Mac} object. 400 * 401 * @return the provider of this {@code Mac} object. 402 */ getProvider()403 public final Provider getProvider() { 404 chooseFirstProvider(); 405 return this.provider; 406 } 407 408 /** 409 * Returns the length of the MAC in bytes. 410 * 411 * @return the MAC length in bytes. 412 */ getMacLength()413 public final int getMacLength() { 414 chooseFirstProvider(); 415 return spi.engineGetMacLength(); 416 } 417 getProviderName()418 private String getProviderName() { 419 return (provider == null) ? "(no provider)" : provider.getName(); 420 } 421 422 /** 423 * Initializes this {@code Mac} object with the given key. 424 * 425 * @param key the key. 426 * 427 * @exception InvalidKeyException if the given key is inappropriate for 428 * initializing this MAC. 429 */ init(Key key)430 public final void init(Key key) throws InvalidKeyException { 431 try { 432 if (spi != null) { 433 spi.engineInit(key, null); 434 } else { 435 chooseProvider(key, null); 436 } 437 } catch (InvalidAlgorithmParameterException e) { 438 throw new InvalidKeyException("init() failed", e); 439 } 440 initialized = true; 441 442 if (!skipDebug && pdebug != null) { 443 pdebug.println("Mac." + algorithm + " algorithm from: " + 444 getProviderName()); 445 } 446 } 447 448 /** 449 * Initializes this {@code Mac} object with the given key and 450 * algorithm parameters. 451 * 452 * @param key the key. 453 * @param params the algorithm parameters. 454 * 455 * @exception InvalidKeyException if the given key is inappropriate for 456 * initializing this MAC. 457 * @exception InvalidAlgorithmParameterException if the given algorithm 458 * parameters are inappropriate for this MAC. 459 */ init(Key key, AlgorithmParameterSpec params)460 public final void init(Key key, AlgorithmParameterSpec params) 461 throws InvalidKeyException, InvalidAlgorithmParameterException { 462 if (spi != null) { 463 spi.engineInit(key, params); 464 } else { 465 chooseProvider(key, params); 466 } 467 initialized = true; 468 469 if (!skipDebug && pdebug != null) { 470 pdebug.println("Mac." + algorithm + " algorithm from: " + 471 getProviderName()); 472 } 473 } 474 475 /** 476 * Processes the given byte. 477 * 478 * @param input the input byte to be processed. 479 * 480 * @exception IllegalStateException if this {@code Mac} has not been 481 * initialized. 482 */ update(byte input)483 public final void update(byte input) throws IllegalStateException { 484 chooseFirstProvider(); 485 if (initialized == false) { 486 throw new IllegalStateException("MAC not initialized"); 487 } 488 spi.engineUpdate(input); 489 } 490 491 /** 492 * Processes the given array of bytes. 493 * 494 * @param input the array of bytes to be processed. 495 * 496 * @exception IllegalStateException if this {@code Mac} has not been 497 * initialized. 498 */ update(byte[] input)499 public final void update(byte[] input) throws IllegalStateException { 500 chooseFirstProvider(); 501 if (initialized == false) { 502 throw new IllegalStateException("MAC not initialized"); 503 } 504 if (input != null) { 505 spi.engineUpdate(input, 0, input.length); 506 } 507 } 508 509 /** 510 * Processes the first {@code len} bytes in {@code input}, 511 * starting at {@code offset} inclusive. 512 * 513 * @param input the input buffer. 514 * @param offset the offset in {@code input} where the input starts. 515 * @param len the number of bytes to process. 516 * 517 * @exception IllegalStateException if this {@code Mac} has not been 518 * initialized. 519 */ update(byte[] input, int offset, int len)520 public final void update(byte[] input, int offset, int len) 521 throws IllegalStateException { 522 chooseFirstProvider(); 523 if (initialized == false) { 524 throw new IllegalStateException("MAC not initialized"); 525 } 526 527 if (input != null) { 528 if ((offset < 0) || (len > (input.length - offset)) || (len < 0)) 529 throw new IllegalArgumentException("Bad arguments"); 530 spi.engineUpdate(input, offset, len); 531 } 532 } 533 534 /** 535 * Processes {@code input.remaining()} bytes in the ByteBuffer 536 * {@code input}, starting at {@code input.position()}. 537 * Upon return, the buffer's position will be equal to its limit; 538 * its limit will not have changed. 539 * 540 * @param input the ByteBuffer 541 * 542 * @exception IllegalStateException if this {@code Mac} has not been 543 * initialized. 544 * @since 1.5 545 */ update(ByteBuffer input)546 public final void update(ByteBuffer input) { 547 chooseFirstProvider(); 548 if (initialized == false) { 549 throw new IllegalStateException("MAC not initialized"); 550 } 551 if (input == null) { 552 throw new IllegalArgumentException("Buffer must not be null"); 553 } 554 spi.engineUpdate(input); 555 } 556 557 /** 558 * Finishes the MAC operation. 559 * 560 * <p>A call to this method resets this {@code Mac} object to the 561 * state it was in when previously initialized via a call to 562 * {@code init(Key)} or 563 * {@code init(Key, AlgorithmParameterSpec)}. 564 * That is, the object is reset and available to generate another MAC from 565 * the same key, if desired, via new calls to {@code update} and 566 * {@code doFinal}. 567 * (In order to reuse this {@code Mac} object with a different key, 568 * it must be reinitialized via a call to {@code init(Key)} or 569 * {@code init(Key, AlgorithmParameterSpec)}. 570 * 571 * @return the MAC result. 572 * 573 * @exception IllegalStateException if this {@code Mac} has not been 574 * initialized. 575 */ doFinal()576 public final byte[] doFinal() throws IllegalStateException { 577 chooseFirstProvider(); 578 if (initialized == false) { 579 throw new IllegalStateException("MAC not initialized"); 580 } 581 byte[] mac = spi.engineDoFinal(); 582 spi.engineReset(); 583 return mac; 584 } 585 586 /** 587 * Finishes the MAC operation. 588 * 589 * <p>A call to this method resets this {@code Mac} object to the 590 * state it was in when previously initialized via a call to 591 * {@code init(Key)} or 592 * {@code init(Key, AlgorithmParameterSpec)}. 593 * That is, the object is reset and available to generate another MAC from 594 * the same key, if desired, via new calls to {@code update} and 595 * {@code doFinal}. 596 * (In order to reuse this {@code Mac} object with a different key, 597 * it must be reinitialized via a call to {@code init(Key)} or 598 * {@code init(Key, AlgorithmParameterSpec)}. 599 * 600 * <p>The MAC result is stored in {@code output}, starting at 601 * {@code outOffset} inclusive. 602 * 603 * @param output the buffer where the MAC result is stored 604 * @param outOffset the offset in {@code output} where the MAC is 605 * stored 606 * 607 * @exception ShortBufferException if the given output buffer is too small 608 * to hold the result 609 * @exception IllegalStateException if this {@code Mac} has not been 610 * initialized. 611 */ doFinal(byte[] output, int outOffset)612 public final void doFinal(byte[] output, int outOffset) 613 throws ShortBufferException, IllegalStateException 614 { 615 chooseFirstProvider(); 616 if (initialized == false) { 617 throw new IllegalStateException("MAC not initialized"); 618 } 619 int macLen = getMacLength(); 620 if (output == null || output.length-outOffset < macLen) { 621 throw new ShortBufferException 622 ("Cannot store MAC in output buffer"); 623 } 624 byte[] mac = doFinal(); 625 System.arraycopy(mac, 0, output, outOffset, macLen); 626 return; 627 } 628 629 /** 630 * Processes the given array of bytes and finishes the MAC operation. 631 * 632 * <p>A call to this method resets this {@code Mac} object to the 633 * state it was in when previously initialized via a call to 634 * {@code init(Key)} or 635 * {@code init(Key, AlgorithmParameterSpec)}. 636 * That is, the object is reset and available to generate another MAC from 637 * the same key, if desired, via new calls to {@code update} and 638 * {@code doFinal}. 639 * (In order to reuse this {@code Mac} object with a different key, 640 * it must be reinitialized via a call to {@code init(Key)} or 641 * {@code init(Key, AlgorithmParameterSpec)}. 642 * 643 * @param input data in bytes 644 * @return the MAC result. 645 * 646 * @exception IllegalStateException if this {@code Mac} has not been 647 * initialized. 648 */ doFinal(byte[] input)649 public final byte[] doFinal(byte[] input) throws IllegalStateException 650 { 651 chooseFirstProvider(); 652 if (initialized == false) { 653 throw new IllegalStateException("MAC not initialized"); 654 } 655 update(input); 656 return doFinal(); 657 } 658 659 /** 660 * Resets this {@code Mac} object. 661 * 662 * <p>A call to this method resets this {@code Mac} object to the 663 * state it was in when previously initialized via a call to 664 * {@code init(Key)} or 665 * {@code init(Key, AlgorithmParameterSpec)}. 666 * That is, the object is reset and available to generate another MAC from 667 * the same key, if desired, via new calls to {@code update} and 668 * {@code doFinal}. 669 * (In order to reuse this {@code Mac} object with a different key, 670 * it must be reinitialized via a call to {@code init(Key)} or 671 * {@code init(Key, AlgorithmParameterSpec)}. 672 */ reset()673 public final void reset() { 674 chooseFirstProvider(); 675 spi.engineReset(); 676 } 677 678 /** 679 * Returns a clone if the provider implementation is cloneable. 680 * 681 * @return a clone if the provider implementation is cloneable. 682 * 683 * @exception CloneNotSupportedException if this is called on a 684 * delegate that does not support {@code Cloneable}. 685 */ clone()686 public final Object clone() throws CloneNotSupportedException { 687 chooseFirstProvider(); 688 Mac that = (Mac)super.clone(); 689 that.spi = (MacSpi)this.spi.clone(); 690 return that; 691 } 692 } 693