1/* 2 * Copyright (C) 2014 The Android Open Source Project 3 * Copyright (c) 1998, 2011, Oracle and/or its affiliates. All rights reserved. 4 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 5 * 6 * This code is free software; you can redistribute it and/or modify it 7 * under the terms of the GNU General Public License version 2 only, as 8 * published by the Free Software Foundation. Oracle designates this 9 * particular file as subject to the "Classpath" exception as provided 10 * by Oracle in the LICENSE file that accompanied this code. 11 * 12 * This code is distributed in the hope that it will be useful, but WITHOUT 13 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 14 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 15 * version 2 for more details (a copy is included in the LICENSE file that 16 * accompanied this code). 17 * 18 * You should have received a copy of the GNU General Public License version 19 * 2 along with this work; if not, write to the Free Software Foundation, 20 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 21 * 22 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 23 * or visit www.oracle.com if you need additional information or have any 24 * questions. 25 */ 26 27package javax.crypto; 28 29import java.util.*; 30 31import java.security.*; 32import java.security.Provider.Service; 33import java.security.spec.AlgorithmParameterSpec; 34 35import java.nio.ByteBuffer; 36 37import sun.security.util.Debug; 38import sun.security.jca.*; 39import sun.security.jca.GetInstance.Instance; 40 41/** 42 * This class provides the functionality of a "Message Authentication Code" 43 * (MAC) algorithm. 44 * 45 * <p> A MAC provides a way to check 46 * the integrity of information transmitted over or stored in an unreliable 47 * medium, based on a secret key. Typically, message 48 * authentication codes are used between two parties that share a secret 49 * key in order to validate information transmitted between these 50 * parties. 51 * 52 * <p> A MAC mechanism that is based on cryptographic hash functions is 53 * referred to as HMAC. HMAC can be used with any cryptographic hash function, 54 * e.g., MD5 or SHA-1, in combination with a secret shared key. HMAC is 55 * specified in RFC 2104. 56 * 57 * <p> Android provides the following <code>Mac</code> algorithms 58 * <table> 59 * <thead> 60 * <tr> 61 * <th>Name</th> 62 * <th>Supported (API Levels)</th> 63 * </tr> 64 * </thead> 65 * <tbody> 66 * <tr> 67 * <td>DESedeMAC</td> 68 * <td>1–8</td> 69 * </tr> 70 * <tr> 71 * <td>DESedeMAC/CFB8</td> 72 * <td>1–8</td> 73 * </tr> 74 * <tr> 75 * <td>DESedeMAC64</td> 76 * <td>1–8</td> 77 * </tr> 78 * <tr> 79 * <td>DESMAC</td> 80 * <td>1–8</td> 81 * </tr> 82 * <tr> 83 * <td>DESMAC/CFB8</td> 84 * <td>1–8</td> 85 * </tr> 86 * <tr> 87 * <td>DESwithISO9797</td> 88 * <td>1–8</td> 89 * </tr> 90 * <tr> 91 * <td>HmacMD5</td> 92 * <td>1+</td> 93 * </tr> 94 * <tr> 95 * <td>HmacSHA1</td> 96 * <td>1+</td> 97 * </tr> 98 * <tr> 99 * <td>HmacSHA224</td> 100 * <td>1–8, 22+</td> 101 * </tr> 102 * <tr> 103 * <td>HmacSHA256</td> 104 * <td>1+</td> 105 * </tr> 106 * <tr> 107 * <td>HmacSHA384</td> 108 * <td>1+</td> 109 * </tr> 110 * <tr> 111 * <td>HmacSHA512</td> 112 * <td>1+</td> 113 * </tr> 114 * <tr> 115 * <td>ISO9797ALG3MAC</td> 116 * <td>1–8</td> 117 * </tr> 118 * <tr> 119 * <td>PBEwithHmacSHA</td> 120 * <td>1+</td> 121 * </tr> 122 * <tr> 123 * <td>PBEwithHmacSHA1</td> 124 * <td>1+</td> 125 * </tr> 126 * </tbody> 127 * </table> 128 * 129 * These algorithms are described in the 130 * <a href="{@docRoot}openjdk-redirect.html?v=8&path=/technotes/guides/security/StandardNames.html#Mac"> 131 * Mac section</a> of the 132 * Java Cryptography Architecture Standard Algorithm Name Documentation. 133 * 134 * @author Jan Luehe 135 * 136 * @since 1.4 137 */ 138 139public class Mac implements Cloneable { 140 141 private static final Debug debug = 142 Debug.getInstance("jca", "Mac"); 143 144 // The provider 145 private Provider provider; 146 147 // The provider implementation (delegate) 148 private MacSpi spi; 149 150 // The name of the MAC algorithm. 151 private final String algorithm; 152 153 // Has this object been initialized? 154 private boolean initialized = false; 155 156 private final Object lock; 157 158 /** 159 * Creates a MAC object. 160 * 161 * @param macSpi the delegate 162 * @param provider the provider 163 * @param algorithm the algorithm 164 */ 165 protected Mac(MacSpi macSpi, Provider provider, String algorithm) { 166 this.spi = macSpi; 167 this.provider = provider; 168 this.algorithm = algorithm; 169 lock = null; 170 } 171 172 private Mac(String algorithm) { 173 this.algorithm = algorithm; 174 lock = new Object(); 175 } 176 177 /** 178 * Returns the algorithm name of this <code>Mac</code> object. 179 * 180 * <p>This is the same name that was specified in one of the 181 * <code>getInstance</code> calls that created this 182 * <code>Mac</code> object. 183 * 184 * @return the algorithm name of this <code>Mac</code> object. 185 */ 186 public final String getAlgorithm() { 187 return this.algorithm; 188 } 189 190 /** 191 * Returns a <code>Mac</code> object that implements the 192 * specified MAC algorithm. 193 * 194 * <p> This method traverses the list of registered security Providers, 195 * starting with the most preferred Provider. 196 * A new Mac object encapsulating the 197 * MacSpi implementation from the first 198 * Provider that supports the specified algorithm is returned. 199 * 200 * <p> Note that the list of registered providers may be retrieved via 201 * the {@link Security#getProviders() Security.getProviders()} method. 202 * 203 * @param algorithm the standard name of the requested MAC algorithm. 204 * See the Mac section in the <a href= 205 * "{@docRoot}openjdk-redirect.html?v=8&path=/technotes/guides/security/StandardNames.html#Mac"> 206 * Java Cryptography Architecture Standard Algorithm Name Documentation</a> 207 * for information about standard algorithm names. 208 * 209 * @return the new <code>Mac</code> object. 210 * 211 * @exception NoSuchAlgorithmException if no Provider supports a 212 * MacSpi implementation for the 213 * specified algorithm. 214 * 215 * @see java.security.Provider 216 */ 217 public static final Mac getInstance(String algorithm) 218 throws NoSuchAlgorithmException { 219 List services = GetInstance.getServices("Mac", algorithm); 220 // make sure there is at least one service from a signed provider 221 Iterator t = services.iterator(); 222 while (t.hasNext()) { 223 Service s = (Service)t.next(); 224 if (JceSecurity.canUseProvider(s.getProvider()) == false) { 225 continue; 226 } 227 return new Mac(algorithm); 228 } 229 throw new NoSuchAlgorithmException 230 ("Algorithm " + algorithm + " not available"); 231 } 232 233 /** 234 * Returns a <code>Mac</code> object that implements the 235 * specified MAC algorithm. 236 * 237 * <p> A new Mac object encapsulating the 238 * MacSpi implementation from the specified provider 239 * is returned. The specified provider must be registered 240 * in the security provider list. 241 * 242 * <p> Note that the list of registered providers may be retrieved via 243 * the {@link Security#getProviders() Security.getProviders()} method. 244 * 245 * @param algorithm the standard name of the requested MAC algorithm. 246 * See the Mac section in the <a href= 247 * "{@docRoot}openjdk-redirect.html?v=8&path=/technotes/guides/security/StandardNames.html#Mac"> 248 * Java Cryptography Architecture Standard Algorithm Name Documentation</a> 249 * for information about standard algorithm names. 250 * 251 * @param provider the name of the provider. 252 * 253 * @return the new <code>Mac</code> object. 254 * 255 * @exception NoSuchAlgorithmException if a MacSpi 256 * implementation for the specified algorithm is not 257 * available from the specified provider. 258 * 259 * @exception NoSuchProviderException if the specified provider is not 260 * registered in the security provider list. 261 * 262 * @exception IllegalArgumentException if the <code>provider</code> 263 * is null or empty. 264 * 265 * @see java.security.Provider 266 */ 267 public static final Mac getInstance(String algorithm, String provider) 268 throws NoSuchAlgorithmException, NoSuchProviderException { 269 Instance instance = JceSecurity.getInstance 270 ("Mac", MacSpi.class, algorithm, provider); 271 return new Mac((MacSpi)instance.impl, instance.provider, algorithm); 272 } 273 274 /** 275 * Returns a <code>Mac</code> object that implements the 276 * specified MAC algorithm. 277 * 278 * <p> A new Mac object encapsulating the 279 * MacSpi implementation from the specified Provider 280 * object is returned. Note that the specified Provider object 281 * does not have to be registered in the provider list. 282 * 283 * @param algorithm the standard name of the requested MAC algorithm. 284 * See the Mac section in the <a href= 285 * "{@docRoot}openjdk-redirect.html?v=8&path=/technotes/guides/security/StandardNames.html#Mac"> 286 * Java Cryptography Architecture Standard Algorithm Name Documentation</a> 287 * for information about standard algorithm names. 288 * 289 * @param provider the provider. 290 * 291 * @return the new <code>Mac</code> object. 292 * 293 * @exception NoSuchAlgorithmException if a MacSpi 294 * implementation for the specified algorithm is not available 295 * from the specified Provider object. 296 * 297 * @exception IllegalArgumentException if the <code>provider</code> 298 * is null. 299 * 300 * @see java.security.Provider 301 */ 302 public static final Mac getInstance(String algorithm, Provider provider) 303 throws NoSuchAlgorithmException { 304 Instance instance = JceSecurity.getInstance 305 ("Mac", MacSpi.class, algorithm, provider); 306 return new Mac((MacSpi)instance.impl, instance.provider, algorithm); 307 } 308 309 // max number of debug warnings to print from chooseFirstProvider() 310 private static int warnCount = 10; 311 312 /** 313 * Choose the Spi from the first provider available. Used if 314 * delayed provider selection is not possible because init() 315 * is not the first method called. 316 */ 317 void chooseFirstProvider() { 318 if (spi != null || lock == null) { 319 return; 320 } 321 synchronized (lock) { 322 if (spi != null) { 323 return; 324 } 325 if (debug != null) { 326 int w = --warnCount; 327 if (w >= 0) { 328 debug.println("Mac.init() not first method " 329 + "called, disabling delayed provider selection"); 330 if (w == 0) { 331 debug.println("Further warnings of this type will " 332 + "be suppressed"); 333 } 334 new Exception("Call trace").printStackTrace(); 335 } 336 } 337 Exception lastException = null; 338 for (Service s : GetInstance.getServices("Mac", algorithm)) { 339 if (JceSecurity.canUseProvider(s.getProvider()) == false) { 340 continue; 341 } 342 try { 343 Object obj = s.newInstance(null); 344 if (obj instanceof MacSpi == false) { 345 continue; 346 } 347 spi = (MacSpi)obj; 348 provider = s.getProvider(); 349 return; 350 } catch (NoSuchAlgorithmException e) { 351 lastException = e; 352 } 353 } 354 ProviderException e = new ProviderException 355 ("Could not construct MacSpi instance"); 356 if (lastException != null) { 357 e.initCause(lastException); 358 } 359 throw e; 360 } 361 } 362 363 private void chooseProvider(Key key, AlgorithmParameterSpec params) 364 throws InvalidKeyException, InvalidAlgorithmParameterException { 365 synchronized (lock) { 366 if (spi != null && (key == null || lock == null)) { 367 spi.engineInit(key, params); 368 return; 369 } 370 Exception lastException = null; 371 for (Service s : GetInstance.getServices("Mac", algorithm)) { 372 // if provider says it does not support this key, ignore it 373 if (s.supportsParameter(key) == false) { 374 continue; 375 } 376 if (JceSecurity.canUseProvider(s.getProvider()) == false) { 377 continue; 378 } 379 try { 380 MacSpi spi = (MacSpi)s.newInstance(null); 381 spi.engineInit(key, params); 382 provider = s.getProvider(); 383 this.spi = spi; 384 return; 385 } catch (Exception e) { 386 // NoSuchAlgorithmException from newInstance() 387 // InvalidKeyException from init() 388 // RuntimeException (ProviderException) from init() 389 if (lastException == null) { 390 lastException = e; 391 } 392 } 393 } 394 // no working provider found, fail 395 if (lastException instanceof InvalidKeyException) { 396 throw (InvalidKeyException)lastException; 397 } 398 if (lastException instanceof InvalidAlgorithmParameterException) { 399 throw (InvalidAlgorithmParameterException)lastException; 400 } 401 if (lastException instanceof RuntimeException) { 402 throw (RuntimeException)lastException; 403 } 404 String kName = (key != null) ? key.getClass().getName() : "(null)"; 405 throw new InvalidKeyException 406 ("No installed provider supports this key: " 407 + kName, lastException); 408 } 409 } 410 411 /** 412 * Returns the provider of this <code>Mac</code> object. 413 * 414 * @return the provider of this <code>Mac</code> object. 415 */ 416 public final Provider getProvider() { 417 chooseFirstProvider(); 418 return this.provider; 419 } 420 421 /** 422 * Returns the length of the MAC in bytes. 423 * 424 * @return the MAC length in bytes. 425 */ 426 public final int getMacLength() { 427 chooseFirstProvider(); 428 return spi.engineGetMacLength(); 429 } 430 431 /** 432 * Initializes this <code>Mac</code> object with the given key. 433 * 434 * @param key the key. 435 * 436 * @exception InvalidKeyException if the given key is inappropriate for 437 * initializing this MAC. 438 */ 439 public final void init(Key key) throws InvalidKeyException { 440 try { 441 if (spi != null && (key == null || lock == null)) { 442 spi.engineInit(key, null); 443 } else { 444 chooseProvider(key, null); 445 } 446 } catch (InvalidAlgorithmParameterException e) { 447 throw new InvalidKeyException("init() failed", e); 448 } 449 initialized = true; 450 } 451 452 /** 453 * Initializes this <code>Mac</code> object with the given key and 454 * algorithm parameters. 455 * 456 * @param key the key. 457 * @param params the algorithm parameters. 458 * 459 * @exception InvalidKeyException if the given key is inappropriate for 460 * initializing this MAC. 461 * @exception InvalidAlgorithmParameterException if the given algorithm 462 * parameters are inappropriate for this MAC. 463 */ 464 public final void init(Key key, AlgorithmParameterSpec params) 465 throws InvalidKeyException, InvalidAlgorithmParameterException { 466 if (spi != null && (key == null || lock == null)) { 467 spi.engineInit(key, params); 468 } else { 469 chooseProvider(key, params); 470 } 471 initialized = true; 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</code> has not been 480 * initialized. 481 */ 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</code> has not been 496 * initialized. 497 */ 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</code> bytes in <code>input</code>, 510 * starting at <code>offset</code> inclusive. 511 * 512 * @param input the input buffer. 513 * @param offset the offset in <code>input</code> where the input starts. 514 * @param len the number of bytes to process. 515 * 516 * @exception IllegalStateException if this <code>Mac</code> has not been 517 * initialized. 518 */ 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()</code> bytes in the ByteBuffer 535 * <code>input</code>, starting at <code>input.position()</code>. 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</code> has not been 542 * initialized. 543 * @since 1.5 544 */ 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</code> object to the 560 * state it was in when previously initialized via a call to 561 * <code>init(Key)</code> or 562 * <code>init(Key, AlgorithmParameterSpec)</code>. 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</code> and 565 * <code>doFinal</code>. 566 * (In order to reuse this <code>Mac</code> object with a different key, 567 * it must be reinitialized via a call to <code>init(Key)</code> or 568 * <code>init(Key, AlgorithmParameterSpec)</code>. 569 * 570 * @return the MAC result. 571 * 572 * @exception IllegalStateException if this <code>Mac</code> has not been 573 * initialized. 574 */ 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</code> object to the 589 * state it was in when previously initialized via a call to 590 * <code>init(Key)</code> or 591 * <code>init(Key, AlgorithmParameterSpec)</code>. 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</code> and 594 * <code>doFinal</code>. 595 * (In order to reuse this <code>Mac</code> object with a different key, 596 * it must be reinitialized via a call to <code>init(Key)</code> or 597 * <code>init(Key, AlgorithmParameterSpec)</code>. 598 * 599 * <p>The MAC result is stored in <code>output</code>, starting at 600 * <code>outOffset</code> inclusive. 601 * 602 * @param output the buffer where the MAC result is stored 603 * @param outOffset the offset in <code>output</code> 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</code> has not been 609 * initialized. 610 */ 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</code> object to the 632 * state it was in when previously initialized via a call to 633 * <code>init(Key)</code> or 634 * <code>init(Key, AlgorithmParameterSpec)</code>. 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</code> and 637 * <code>doFinal</code>. 638 * (In order to reuse this <code>Mac</code> object with a different key, 639 * it must be reinitialized via a call to <code>init(Key)</code> or 640 * <code>init(Key, AlgorithmParameterSpec)</code>. 641 * 642 * @param input data in bytes 643 * @return the MAC result. 644 * 645 * @exception IllegalStateException if this <code>Mac</code> has not been 646 * initialized. 647 */ 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</code> object. 660 * 661 * <p>A call to this method resets this <code>Mac</code> object to the 662 * state it was in when previously initialized via a call to 663 * <code>init(Key)</code> or 664 * <code>init(Key, AlgorithmParameterSpec)</code>. 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</code> and 667 * <code>doFinal</code>. 668 * (In order to reuse this <code>Mac</code> object with a different key, 669 * it must be reinitialized via a call to <code>init(Key)</code> or 670 * <code>init(Key, AlgorithmParameterSpec)</code>. 671 */ 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</code>. 684 */ 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 /** 693 * Returns the {@code MacSpi} backing this {@code Mac} or {@code null} if no {@code MacSpi} is 694 * backing this {@code Mac}. 695 * 696 * @hide 697 */ 698 public MacSpi getCurrentSpi() { 699 return spi; 700 } 701} 702