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