blob: 4a8f3721d1d73beaf2f7b0b8211f7ff7bf204ebb [file] [log] [blame]
J. Duke319a3b92007-12-01 00:00:00 +00001/*
2 * Copyright 1998-2007 Sun Microsystems, Inc. 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. Sun designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
22 * CA 95054 USA or visit www.sun.com if you need additional information or
23 * have any questions.
24 */
25
26package javax.crypto;
27
28import java.util.*;
29
30import java.security.*;
31import java.security.Provider.Service;
32import java.security.spec.AlgorithmParameterSpec;
33
34import java.nio.ByteBuffer;
35
36import sun.security.util.Debug;
37import sun.security.jca.*;
38import sun.security.jca.GetInstance.Instance;
39
40/**
41 * This class provides the functionality of a "Message Authentication Code"
42 * (MAC) algorithm.
43 *
44 * <p> A MAC provides a way to check
45 * the integrity of information transmitted over or stored in an unreliable
46 * medium, based on a secret key. Typically, message
47 * authentication codes are used between two parties that share a secret
48 * key in order to validate information transmitted between these
49 * parties.
50 *
51 * <p> A MAC mechanism that is based on cryptographic hash functions is
52 * referred to as HMAC. HMAC can be used with any cryptographic hash function,
53 * e.g., MD5 or SHA-1, in combination with a secret shared key. HMAC is
54 * specified in RFC 2104.
55 *
56 * @author Jan Luehe
57 *
58 * @since 1.4
59 */
60
61public class Mac implements Cloneable {
62
63 private static final Debug debug =
64 Debug.getInstance("jca", "Mac");
65
66 // The provider
67 private Provider provider;
68
69 // The provider implementation (delegate)
70 private MacSpi spi;
71
72 // The name of the MAC algorithm.
73 private final String algorithm;
74
75 // Has this object been initialized?
76 private boolean initialized = false;
77
78 // next service to try in provider selection
79 // null once provider is selected
80 private Service firstService;
81
82 // remaining services to try in provider selection
83 // null once provider is selected
84 private Iterator serviceIterator;
85
86 private final Object lock;
87
88 /**
89 * Creates a MAC object.
90 *
91 * @param macSpi the delegate
92 * @param provider the provider
93 * @param algorithm the algorithm
94 */
95 protected Mac(MacSpi macSpi, Provider provider, String algorithm) {
96 this.spi = macSpi;
97 this.provider = provider;
98 this.algorithm = algorithm;
99 serviceIterator = null;
100 lock = null;
101 }
102
103 private Mac(Service s, Iterator t, String algorithm) {
104 firstService = s;
105 serviceIterator = t;
106 this.algorithm = algorithm;
107 lock = new Object();
108 }
109
110 /**
111 * Returns the algorithm name of this <code>Mac</code> object.
112 *
113 * <p>This is the same name that was specified in one of the
114 * <code>getInstance</code> calls that created this
115 * <code>Mac</code> object.
116 *
117 * @return the algorithm name of this <code>Mac</code> object.
118 */
119 public final String getAlgorithm() {
120 return this.algorithm;
121 }
122
123 /**
124 * Returns a <code>Mac</code> object that implements the
125 * specified MAC algorithm.
126 *
127 * <p> This method traverses the list of registered security Providers,
128 * starting with the most preferred Provider.
129 * A new Mac object encapsulating the
130 * MacSpi implementation from the first
131 * Provider that supports the specified algorithm is returned.
132 *
133 * <p> Note that the list of registered providers may be retrieved via
134 * the {@link Security#getProviders() Security.getProviders()} method.
135 *
136 * @param algorithm the standard name of the requested MAC algorithm.
137 * See Appendix A in the <a href=
138 * "{@docRoot}/../technotes/guides/security/crypto/CryptoSpec.html#AppA">
139 * Java Cryptography Architecture Reference Guide</a>
140 * for information about standard algorithm names.
141 *
142 * @return the new <code>Mac</code> object.
143 *
144 * @exception NoSuchAlgorithmException if no Provider supports a
145 * MacSpi implementation for the
146 * specified algorithm.
147 *
148 * @see java.security.Provider
149 */
150 public static final Mac getInstance(String algorithm)
151 throws NoSuchAlgorithmException {
152 List services = GetInstance.getServices("Mac", algorithm);
153 // make sure there is at least one service from a signed provider
154 Iterator t = services.iterator();
155 while (t.hasNext()) {
156 Service s = (Service)t.next();
157 if (JceSecurity.canUseProvider(s.getProvider()) == false) {
158 continue;
159 }
160 return new Mac(s, t, algorithm);
161 }
162 throw new NoSuchAlgorithmException
163 ("Algorithm " + algorithm + " not available");
164 }
165
166 /**
167 * Returns a <code>Mac</code> object that implements the
168 * specified MAC algorithm.
169 *
170 * <p> A new Mac object encapsulating the
171 * MacSpi implementation from the specified provider
172 * is returned. The specified provider must be registered
173 * in the security provider list.
174 *
175 * <p> Note that the list of registered providers may be retrieved via
176 * the {@link Security#getProviders() Security.getProviders()} method.
177 *
178 * @param algorithm the standard name of the requested MAC algorithm.
179 * See Appendix A in the <a href=
180 * "{@docRoot}/../technotes/guides/security/crypto/CryptoSpec.html#AppA">
181 * Java Cryptography Architecture Reference Guide</a>
182 * for information about standard algorithm names.
183 *
184 * @param provider the name of the provider.
185 *
186 * @return the new <code>Mac</code> object.
187 *
188 * @exception NoSuchAlgorithmException if a MacSpi
189 * implementation for the specified algorithm is not
190 * available from the specified provider.
191 *
192 * @exception NoSuchProviderException if the specified provider is not
193 * registered in the security provider list.
194 *
195 * @exception IllegalArgumentException if the <code>provider</code>
196 * is null or empty.
197 *
198 * @see java.security.Provider
199 */
200 public static final Mac getInstance(String algorithm, String provider)
201 throws NoSuchAlgorithmException, NoSuchProviderException {
202 Instance instance = JceSecurity.getInstance
203 ("Mac", MacSpi.class, algorithm, provider);
204 return new Mac((MacSpi)instance.impl, instance.provider, algorithm);
205 }
206
207 /**
208 * Returns a <code>Mac</code> object that implements the
209 * specified MAC algorithm.
210 *
211 * <p> A new Mac object encapsulating the
212 * MacSpi implementation from the specified Provider
213 * object is returned. Note that the specified Provider object
214 * does not have to be registered in the provider list.
215 *
216 * @param algorithm the standard name of the requested MAC algorithm.
217 * See Appendix A in the <a href=
218 * "{@docRoot}/../technotes/guides/security/crypto/CryptoSpec.html#AppA">
219 * Java Cryptography Architecture Reference Guide</a>
220 * for information about standard algorithm names.
221 *
222 * @param provider the provider.
223 *
224 * @return the new <code>Mac</code> object.
225 *
226 * @exception NoSuchAlgorithmException if a MacSpi
227 * implementation for the specified algorithm is not available
228 * from the specified Provider object.
229 *
230 * @exception IllegalArgumentException if the <code>provider</code>
231 * is null.
232 *
233 * @see java.security.Provider
234 */
235 public static final Mac getInstance(String algorithm, Provider provider)
236 throws NoSuchAlgorithmException {
237 Instance instance = JceSecurity.getInstance
238 ("Mac", MacSpi.class, algorithm, provider);
239 return new Mac((MacSpi)instance.impl, instance.provider, algorithm);
240 }
241
242 // max number of debug warnings to print from chooseFirstProvider()
243 private static int warnCount = 10;
244
245 /**
246 * Choose the Spi from the first provider available. Used if
247 * delayed provider selection is not possible because init()
248 * is not the first method called.
249 */
250 void chooseFirstProvider() {
251 if ((spi != null) || (serviceIterator == null)) {
252 return;
253 }
254 synchronized (lock) {
255 if (spi != null) {
256 return;
257 }
258 if (debug != null) {
259 int w = --warnCount;
260 if (w >= 0) {
261 debug.println("Mac.init() not first method "
262 + "called, disabling delayed provider selection");
263 if (w == 0) {
264 debug.println("Further warnings of this type will "
265 + "be suppressed");
266 }
267 new Exception("Call trace").printStackTrace();
268 }
269 }
270 Exception lastException = null;
271 while ((firstService != null) || serviceIterator.hasNext()) {
272 Service s;
273 if (firstService != null) {
274 s = firstService;
275 firstService = null;
276 } else {
277 s = (Service)serviceIterator.next();
278 }
279 if (JceSecurity.canUseProvider(s.getProvider()) == false) {
280 continue;
281 }
282 try {
283 Object obj = s.newInstance(null);
284 if (obj instanceof MacSpi == false) {
285 continue;
286 }
287 spi = (MacSpi)obj;
288 provider = s.getProvider();
289 // not needed any more
290 firstService = null;
291 serviceIterator = null;
292 return;
293 } catch (NoSuchAlgorithmException e) {
294 lastException = e;
295 }
296 }
297 ProviderException e = new ProviderException
298 ("Could not construct MacSpi instance");
299 if (lastException != null) {
300 e.initCause(lastException);
301 }
302 throw e;
303 }
304 }
305
306 private void chooseProvider(Key key, AlgorithmParameterSpec params)
307 throws InvalidKeyException, InvalidAlgorithmParameterException {
308 synchronized (lock) {
309 if (spi != null) {
310 spi.engineInit(key, params);
311 return;
312 }
313 Exception lastException = null;
314 while ((firstService != null) || serviceIterator.hasNext()) {
315 Service s;
316 if (firstService != null) {
317 s = firstService;
318 firstService = null;
319 } else {
320 s = (Service)serviceIterator.next();
321 }
322 // if provider says it does not support this key, ignore it
323 if (s.supportsParameter(key) == false) {
324 continue;
325 }
326 if (JceSecurity.canUseProvider(s.getProvider()) == false) {
327 continue;
328 }
329 try {
330 MacSpi spi = (MacSpi)s.newInstance(null);
331 spi.engineInit(key, params);
332 provider = s.getProvider();
333 this.spi = spi;
334 firstService = null;
335 serviceIterator = null;
336 return;
337 } catch (Exception e) {
338 // NoSuchAlgorithmException from newInstance()
339 // InvalidKeyException from init()
340 // RuntimeException (ProviderException) from init()
341 if (lastException == null) {
342 lastException = e;
343 }
344 }
345 }
346 // no working provider found, fail
347 if (lastException instanceof InvalidKeyException) {
348 throw (InvalidKeyException)lastException;
349 }
350 if (lastException instanceof InvalidAlgorithmParameterException) {
351 throw (InvalidAlgorithmParameterException)lastException;
352 }
353 if (lastException instanceof RuntimeException) {
354 throw (RuntimeException)lastException;
355 }
356 String kName = (key != null) ? key.getClass().getName() : "(null)";
357 throw new InvalidKeyException
358 ("No installed provider supports this key: "
359 + kName, lastException);
360 }
361 }
362
363 /**
364 * Returns the provider of this <code>Mac</code> object.
365 *
366 * @return the provider of this <code>Mac</code> object.
367 */
368 public final Provider getProvider() {
369 chooseFirstProvider();
370 return this.provider;
371 }
372
373 /**
374 * Returns the length of the MAC in bytes.
375 *
376 * @return the MAC length in bytes.
377 */
378 public final int getMacLength() {
379 chooseFirstProvider();
380 return spi.engineGetMacLength();
381 }
382
383 /**
384 * Initializes this <code>Mac</code> object with the given key.
385 *
386 * @param key the key.
387 *
388 * @exception InvalidKeyException if the given key is inappropriate for
389 * initializing this MAC.
390 */
391 public final void init(Key key) throws InvalidKeyException {
392 try {
393 if (spi != null) {
394 spi.engineInit(key, null);
395 } else {
396 chooseProvider(key, null);
397 }
398 } catch (InvalidAlgorithmParameterException e) {
399 throw new InvalidKeyException("init() failed", e);
400 }
401 initialized = true;
402 }
403
404 /**
405 * Initializes this <code>Mac</code> object with the given key and
406 * algorithm parameters.
407 *
408 * @param key the key.
409 * @param params the algorithm parameters.
410 *
411 * @exception InvalidKeyException if the given key is inappropriate for
412 * initializing this MAC.
413 * @exception InvalidAlgorithmParameterException if the given algorithm
414 * parameters are inappropriate for this MAC.
415 */
416 public final void init(Key key, AlgorithmParameterSpec params)
417 throws InvalidKeyException, InvalidAlgorithmParameterException {
418 if (spi != null) {
419 spi.engineInit(key, params);
420 } else {
421 chooseProvider(key, params);
422 }
423 initialized = true;
424 }
425
426 /**
427 * Processes the given byte.
428 *
429 * @param input the input byte to be processed.
430 *
431 * @exception IllegalStateException if this <code>Mac</code> has not been
432 * initialized.
433 */
434 public final void update(byte input) throws IllegalStateException {
435 chooseFirstProvider();
436 if (initialized == false) {
437 throw new IllegalStateException("MAC not initialized");
438 }
439 spi.engineUpdate(input);
440 }
441
442 /**
443 * Processes the given array of bytes.
444 *
445 * @param input the array of bytes to be processed.
446 *
447 * @exception IllegalStateException if this <code>Mac</code> has not been
448 * initialized.
449 */
450 public final void update(byte[] input) throws IllegalStateException {
451 chooseFirstProvider();
452 if (initialized == false) {
453 throw new IllegalStateException("MAC not initialized");
454 }
455 if (input != null) {
456 spi.engineUpdate(input, 0, input.length);
457 }
458 }
459
460 /**
461 * Processes the first <code>len</code> bytes in <code>input</code>,
462 * starting at <code>offset</code> inclusive.
463 *
464 * @param input the input buffer.
465 * @param offset the offset in <code>input</code> where the input starts.
466 * @param len the number of bytes to process.
467 *
468 * @exception IllegalStateException if this <code>Mac</code> has not been
469 * initialized.
470 */
471 public final void update(byte[] input, int offset, int len)
472 throws IllegalStateException {
473 chooseFirstProvider();
474 if (initialized == false) {
475 throw new IllegalStateException("MAC not initialized");
476 }
477
478 if (input != null) {
479 if ((offset < 0) || (len > (input.length - offset)) || (len < 0))
480 throw new IllegalArgumentException("Bad arguments");
481 spi.engineUpdate(input, offset, len);
482 }
483 }
484
485 /**
486 * Processes <code>input.remaining()</code> bytes in the ByteBuffer
487 * <code>input</code>, starting at <code>input.position()</code>.
488 * Upon return, the buffer's position will be equal to its limit;
489 * its limit will not have changed.
490 *
491 * @param input the ByteBuffer
492 *
493 * @exception IllegalStateException if this <code>Mac</code> has not been
494 * initialized.
495 * @since 1.5
496 */
497 public final void update(ByteBuffer input) {
498 chooseFirstProvider();
499 if (initialized == false) {
500 throw new IllegalStateException("MAC not initialized");
501 }
502 if (input == null) {
503 throw new IllegalArgumentException("Buffer must not be null");
504 }
505 spi.engineUpdate(input);
506 }
507
508 /**
509 * Finishes the MAC operation.
510 *
511 * <p>A call to this method resets this <code>Mac</code> object to the
512 * state it was in when previously initialized via a call to
513 * <code>init(Key)</code> or
514 * <code>init(Key, AlgorithmParameterSpec)</code>.
515 * That is, the object is reset and available to generate another MAC from
516 * the same key, if desired, via new calls to <code>update</code> and
517 * <code>doFinal</code>.
518 * (In order to reuse this <code>Mac</code> object with a different key,
519 * it must be reinitialized via a call to <code>init(Key)</code> or
520 * <code>init(Key, AlgorithmParameterSpec)</code>.
521 *
522 * @return the MAC result.
523 *
524 * @exception IllegalStateException if this <code>Mac</code> has not been
525 * initialized.
526 */
527 public final byte[] doFinal() throws IllegalStateException {
528 chooseFirstProvider();
529 if (initialized == false) {
530 throw new IllegalStateException("MAC not initialized");
531 }
532 byte[] mac = spi.engineDoFinal();
533 spi.engineReset();
534 return mac;
535 }
536
537 /**
538 * Finishes the MAC operation.
539 *
540 * <p>A call to this method resets this <code>Mac</code> object to the
541 * state it was in when previously initialized via a call to
542 * <code>init(Key)</code> or
543 * <code>init(Key, AlgorithmParameterSpec)</code>.
544 * That is, the object is reset and available to generate another MAC from
545 * the same key, if desired, via new calls to <code>update</code> and
546 * <code>doFinal</code>.
547 * (In order to reuse this <code>Mac</code> object with a different key,
548 * it must be reinitialized via a call to <code>init(Key)</code> or
549 * <code>init(Key, AlgorithmParameterSpec)</code>.
550 *
551 * <p>The MAC result is stored in <code>output</code>, starting at
552 * <code>outOffset</code> inclusive.
553 *
554 * @param output the buffer where the MAC result is stored
555 * @param outOffset the offset in <code>output</code> where the MAC is
556 * stored
557 *
558 * @exception ShortBufferException if the given output buffer is too small
559 * to hold the result
560 * @exception IllegalStateException if this <code>Mac</code> has not been
561 * initialized.
562 */
563 public final void doFinal(byte[] output, int outOffset)
564 throws ShortBufferException, IllegalStateException
565 {
566 chooseFirstProvider();
567 if (initialized == false) {
568 throw new IllegalStateException("MAC not initialized");
569 }
570 int macLen = getMacLength();
571 if (output == null || output.length-outOffset < macLen) {
572 throw new ShortBufferException
573 ("Cannot store MAC in output buffer");
574 }
575 byte[] mac = doFinal();
576 System.arraycopy(mac, 0, output, outOffset, macLen);
577 return;
578 }
579
580 /**
581 * Processes the given array of bytes and finishes the MAC operation.
582 *
583 * <p>A call to this method resets this <code>Mac</code> object to the
584 * state it was in when previously initialized via a call to
585 * <code>init(Key)</code> or
586 * <code>init(Key, AlgorithmParameterSpec)</code>.
587 * That is, the object is reset and available to generate another MAC from
588 * the same key, if desired, via new calls to <code>update</code> and
589 * <code>doFinal</code>.
590 * (In order to reuse this <code>Mac</code> object with a different key,
591 * it must be reinitialized via a call to <code>init(Key)</code> or
592 * <code>init(Key, AlgorithmParameterSpec)</code>.
593 *
594 * @param input data in bytes
595 * @return the MAC result.
596 *
597 * @exception IllegalStateException if this <code>Mac</code> has not been
598 * initialized.
599 */
600 public final byte[] doFinal(byte[] input) throws IllegalStateException
601 {
602 chooseFirstProvider();
603 if (initialized == false) {
604 throw new IllegalStateException("MAC not initialized");
605 }
606 update(input);
607 return doFinal();
608 }
609
610 /**
611 * Resets this <code>Mac</code> object.
612 *
613 * <p>A call to this method resets this <code>Mac</code> object to the
614 * state it was in when previously initialized via a call to
615 * <code>init(Key)</code> or
616 * <code>init(Key, AlgorithmParameterSpec)</code>.
617 * That is, the object is reset and available to generate another MAC from
618 * the same key, if desired, via new calls to <code>update</code> and
619 * <code>doFinal</code>.
620 * (In order to reuse this <code>Mac</code> object with a different key,
621 * it must be reinitialized via a call to <code>init(Key)</code> or
622 * <code>init(Key, AlgorithmParameterSpec)</code>.
623 */
624 public final void reset() {
625 chooseFirstProvider();
626 spi.engineReset();
627 }
628
629 /**
630 * Returns a clone if the provider implementation is cloneable.
631 *
632 * @return a clone if the provider implementation is cloneable.
633 *
634 * @exception CloneNotSupportedException if this is called on a
635 * delegate that does not support <code>Cloneable</code>.
636 */
637 public final Object clone() throws CloneNotSupportedException {
638 chooseFirstProvider();
639 Mac that = (Mac)super.clone();
640 that.spi = (MacSpi)this.spi.clone();
641 return that;
642 }
643}