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 */
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
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 */
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 */
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 */
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 */
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 */
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
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 */
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 */
412 public final int getMacLength() {
413 chooseFirstProvider();
414 return spi.engineGetMacLength();
415 }
416
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 */
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 */
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 */
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 */
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 */
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 */
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 */
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 */
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 */
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 */
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 */
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 }
--- EOF ---