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