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 import sun.security.util.Debug;
     38 import sun.security.jca.*;
     39 import 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&ndash;8</td>
     69  *         </tr>
     70  *         <tr>
     71  *             <td>DESedeMAC/CFB8</td>
     72  *             <td>1&ndash;8</td>
     73  *         </tr>
     74  *         <tr>
     75  *             <td>DESedeMAC64</td>
     76  *             <td>1&ndash;8</td>
     77  *         </tr>
     78  *         <tr>
     79  *             <td>DESMAC</td>
     80  *             <td>1&ndash;8</td>
     81  *         </tr>
     82  *         <tr>
     83  *             <td>DESMAC/CFB8</td>
     84  *             <td>1&ndash;8</td>
     85  *         </tr>
     86  *         <tr>
     87  *             <td>DESwithISO9797</td>
     88  *             <td>1&ndash;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&ndash;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&ndash;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 
    139 public 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