• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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      */
Mac(MacSpi macSpi, Provider provider, String algorithm)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 
Mac(String algorithm)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      */
getAlgorithm()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      */
getInstance(String algorithm)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      */
getInstance(String algorithm, String provider)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      */
getInstance(String algorithm, Provider provider)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      */
chooseFirstProvider()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 
chooseProvider(Key key, AlgorithmParameterSpec params)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      */
getProvider()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      */
getMacLength()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      */
init(Key key)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      */
init(Key key, AlgorithmParameterSpec params)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      */
update(byte input)482     public final void update(byte input) throws IllegalStateException {
483         chooseFirstProvider();
484         if (initialized == false) {
485             throw new IllegalStateException("MAC not initialized");
486         }
487         spi.engineUpdate(input);
488     }
489 
490     /**
491      * Processes the given array of bytes.
492      *
493      * @param input the array of bytes to be processed.
494      *
495      * @exception IllegalStateException if this <code>Mac</code> has not been
496      * initialized.
497      */
update(byte[] input)498     public final void update(byte[] input) throws IllegalStateException {
499         chooseFirstProvider();
500         if (initialized == false) {
501             throw new IllegalStateException("MAC not initialized");
502         }
503         if (input != null) {
504             spi.engineUpdate(input, 0, input.length);
505         }
506     }
507 
508     /**
509      * Processes the first <code>len</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      */
update(byte[] input, int offset, int len)519     public final void update(byte[] input, int offset, int len)
520             throws IllegalStateException {
521         chooseFirstProvider();
522         if (initialized == false) {
523             throw new IllegalStateException("MAC not initialized");
524         }
525 
526         if (input != null) {
527             if ((offset < 0) || (len > (input.length - offset)) || (len < 0))
528                 throw new IllegalArgumentException("Bad arguments");
529             spi.engineUpdate(input, offset, len);
530         }
531     }
532 
533     /**
534      * Processes <code>input.remaining()</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      */
update(ByteBuffer input)545     public final void update(ByteBuffer input) {
546         chooseFirstProvider();
547         if (initialized == false) {
548             throw new IllegalStateException("MAC not initialized");
549         }
550         if (input == null) {
551             throw new IllegalArgumentException("Buffer must not be null");
552         }
553         spi.engineUpdate(input);
554     }
555 
556     /**
557      * Finishes the MAC operation.
558      *
559      * <p>A call to this method resets this <code>Mac</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      */
doFinal()575     public final byte[] doFinal() throws IllegalStateException {
576         chooseFirstProvider();
577         if (initialized == false) {
578             throw new IllegalStateException("MAC not initialized");
579         }
580         byte[] mac = spi.engineDoFinal();
581         spi.engineReset();
582         return mac;
583     }
584 
585     /**
586      * Finishes the MAC operation.
587      *
588      * <p>A call to this method resets this <code>Mac</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      */
doFinal(byte[] output, int outOffset)611     public final void doFinal(byte[] output, int outOffset)
612         throws ShortBufferException, IllegalStateException
613     {
614         chooseFirstProvider();
615         if (initialized == false) {
616             throw new IllegalStateException("MAC not initialized");
617         }
618         int macLen = getMacLength();
619         if (output == null || output.length-outOffset < macLen) {
620             throw new ShortBufferException
621                 ("Cannot store MAC in output buffer");
622         }
623         byte[] mac = doFinal();
624         System.arraycopy(mac, 0, output, outOffset, macLen);
625         return;
626     }
627 
628     /**
629      * Processes the given array of bytes and finishes the MAC operation.
630      *
631      * <p>A call to this method resets this <code>Mac</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      */
doFinal(byte[] input)648     public final byte[] doFinal(byte[] input) throws IllegalStateException
649     {
650         chooseFirstProvider();
651         if (initialized == false) {
652             throw new IllegalStateException("MAC not initialized");
653         }
654         update(input);
655         return doFinal();
656     }
657 
658     /**
659      * Resets this <code>Mac</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      */
reset()672     public final void reset() {
673         chooseFirstProvider();
674         spi.engineReset();
675     }
676 
677     /**
678      * Returns a clone if the provider implementation is cloneable.
679      *
680      * @return a clone if the provider implementation is cloneable.
681      *
682      * @exception CloneNotSupportedException if this is called on a
683      * delegate that does not support <code>Cloneable</code>.
684      */
clone()685     public final Object clone() throws CloneNotSupportedException {
686         chooseFirstProvider();
687         Mac that = (Mac)super.clone();
688         that.spi = (MacSpi)this.spi.clone();
689         return that;
690     }
691 
692     /**
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      */
getCurrentSpi()698     public MacSpi getCurrentSpi() {
699         return spi;
700     }
701 }
702