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