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