• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  *  Licensed to the Apache Software Foundation (ASF) under one or more
3  *  contributor license agreements.  See the NOTICE file distributed with
4  *  this work for additional information regarding copyright ownership.
5  *  The ASF licenses this file to You under the Apache License, Version 2.0
6  *  (the "License"); you may not use this file except in compliance with
7  *  the License.  You may obtain a copy of the License at
8  *
9  *     http://www.apache.org/licenses/LICENSE-2.0
10  *
11  *  Unless required by applicable law or agreed to in writing, software
12  *  distributed under the License is distributed on an "AS IS" BASIS,
13  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  *  See the License for the specific language governing permissions and
15  *  limitations under the License.
16  */
17 
18 package javax.crypto;
19 
20 import java.nio.ByteBuffer;
21 import java.security.InvalidAlgorithmParameterException;
22 import java.security.InvalidKeyException;
23 import java.security.Key;
24 import java.security.NoSuchAlgorithmException;
25 import java.security.NoSuchProviderException;
26 import java.security.Provider;
27 import java.security.ProviderException;
28 import java.security.Security;
29 import java.security.spec.AlgorithmParameterSpec;
30 import java.util.ArrayList;
31 import org.apache.harmony.security.fortress.Engine;
32 
33 
34 /**
35  * This class provides the public API for <i>Message Authentication Code</i>
36  * (MAC) algorithms.
37  */
38 public class Mac implements Cloneable {
39 
40     // The service name.
41     private static final String SERVICE = "Mac";
42 
43     //Used to access common engine functionality
44     private static final Engine ENGINE = new Engine(SERVICE);
45 
46     // Store used provider
47     private Provider provider;
48 
49     // Provider that was requested during creation.
50     private final Provider specifiedProvider;
51 
52     // Store used spi implementation
53     private MacSpi spiImpl;
54 
55     // Store used algorithm name
56     private final String algorithm;
57 
58     /**
59      * Lock held while the SPI is initializing.
60      */
61     private final Object initLock = new Object();
62 
63     // Store Mac state (initialized or not initialized)
64     private boolean isInitMac;
65 
66     /**
67      * Creates a new {@code Mac} instance.
68      *
69      * @param macSpi
70      *            the implementation delegate.
71      * @param provider
72      *            the implementation provider.
73      * @param algorithm
74      *            the name of the MAC algorithm.
75      */
Mac(MacSpi macSpi, Provider provider, String algorithm)76     protected Mac(MacSpi macSpi, Provider provider, String algorithm) {
77         this.specifiedProvider = provider;
78         this.algorithm = algorithm;
79         this.spiImpl = macSpi;
80         this.isInitMac = false;
81     }
82 
83     /**
84      * Returns the name of the MAC algorithm.
85      *
86      * @return the name of the MAC algorithm.
87      */
getAlgorithm()88     public final String getAlgorithm() {
89         return algorithm;
90     }
91 
92     /**
93      * Returns the provider of this {@code Mac} instance.
94      *
95      * @return the provider of this {@code Mac} instance.
96      */
getProvider()97     public final Provider getProvider() {
98         getSpi();
99         return provider;
100     }
101 
102     /**
103      * Creates a new {@code Mac} instance that provides the specified MAC
104      * algorithm.
105      *
106      * @param algorithm
107      *            the name of the requested MAC algorithm.
108      * @return the new {@code Mac} instance.
109      * @throws NoSuchAlgorithmException
110      *             if the specified algorithm is not available by any provider.
111      * @throws NullPointerException
112      *             if {@code algorithm} is {@code null} (instead of
113      *             NoSuchAlgorithmException as in 1.4 release).
114      */
getInstance(String algorithm)115     public static final Mac getInstance(String algorithm)
116             throws NoSuchAlgorithmException {
117         return getMac(algorithm, null);
118     }
119 
120     /**
121      * Creates a new {@code Mac} instance that provides the specified MAC
122      * algorithm from the specified provider.
123      *
124      * @param algorithm
125      *            the name of the requested MAC algorithm.
126      * @param provider
127      *            the name of the provider that is providing the algorithm.
128      * @return the new {@code Mac} instance.
129      * @throws NoSuchAlgorithmException
130      *             if the specified algorithm is not provided by the specified
131      *             provider.
132      * @throws NoSuchProviderException
133      *             if the specified provider is not available.
134      * @throws IllegalArgumentException
135      *             if the specified provider name is {@code null} or empty.
136      * @throws NullPointerException
137      *             if {@code algorithm} is {@code null} (instead of
138      *             NoSuchAlgorithmException as in 1.4 release).
139      */
getInstance(String algorithm, String provider)140     public static final Mac getInstance(String algorithm, String provider)
141             throws NoSuchAlgorithmException, NoSuchProviderException {
142         if (provider == null || provider.isEmpty()) {
143             throw new IllegalArgumentException("Provider is null or empty");
144         }
145         Provider impProvider = Security.getProvider(provider);
146         if (impProvider == null) {
147             throw new NoSuchProviderException(provider);
148         }
149         return getMac(algorithm, impProvider);
150     }
151 
152     /**
153      * Creates a new {@code Mac} instance that provides the specified MAC
154      * algorithm from the specified provider. The {@code provider} supplied
155      * does not have to be registered.
156      *
157      * @param algorithm
158      *            the name of the requested MAC algorithm.
159      * @param provider
160      *            the provider that is providing the algorithm.
161      * @return the new {@code Mac} instance.
162      * @throws NoSuchAlgorithmException
163      *             if the specified algorithm is not provided by the specified
164      *             provider.
165      * @throws IllegalArgumentException
166      *             if {@code provider} is {@code null}.
167      * @throws NullPointerException
168      *             if {@code algorithm} is {@code null} (instead of
169      *             NoSuchAlgorithmException as in 1.4 release).
170      */
getInstance(String algorithm, Provider provider)171     public static final Mac getInstance(String algorithm, Provider provider)
172             throws NoSuchAlgorithmException {
173         if (provider == null) {
174             throw new IllegalArgumentException("provider == null");
175         }
176         return getMac(algorithm, provider);
177     }
178 
getMac(String algorithm, Provider provider)179     private static Mac getMac(String algorithm, Provider provider)
180             throws NoSuchAlgorithmException {
181         if (algorithm == null) {
182             throw new NullPointerException("algorithm == null");
183         }
184 
185         boolean providerSupportsAlgorithm;
186         try {
187             providerSupportsAlgorithm = tryAlgorithm(null /* key */, provider, algorithm) != null;
188         } catch (InvalidKeyException e) {
189             throw new IllegalStateException("InvalidKeyException thrown when key == null", e);
190         }
191         if (!providerSupportsAlgorithm) {
192             if (provider == null) {
193                 throw new NoSuchAlgorithmException("No provider found for " + algorithm);
194             } else {
195                 throw new NoSuchAlgorithmException("Provider " + provider.getName()
196                         + " does not provide " + algorithm);
197             }
198         }
199         return new Mac(null, provider, algorithm);
200     }
201     /**
202       * @throws InvalidKeyException if the specified key cannot be used to
203       *             initialize this mac.
204       */
tryAlgorithm( Key key, Provider provider, String algorithm)205     private static Engine.SpiAndProvider tryAlgorithm(
206             Key key, Provider provider, String algorithm) throws InvalidKeyException {
207         if (provider != null) {
208             Provider.Service service = provider.getService(SERVICE, algorithm);
209             if (service == null) {
210                 return null;
211             }
212             return tryAlgorithmWithProvider(service);
213         }
214         ArrayList<Provider.Service> services = ENGINE.getServices(algorithm);
215         if (services == null || services.isEmpty()) {
216             return null;
217         }
218         boolean keySupported = false;
219         for (Provider.Service service : services) {
220             if (key == null || service.supportsParameter(key)) {
221                 keySupported = true;
222                 Engine.SpiAndProvider sap = tryAlgorithmWithProvider(service);
223                 if (sap != null) {
224                     return sap;
225                 }
226             }
227         }
228         if (!keySupported) {
229             throw new InvalidKeyException("No provider supports the provided key");
230         }
231         return null;
232     }
233 
tryAlgorithmWithProvider(Provider.Service service)234     private static Engine.SpiAndProvider tryAlgorithmWithProvider(Provider.Service service) {
235         try {
236             Engine.SpiAndProvider sap = ENGINE.getInstance(service, null);
237             if (sap.spi == null || sap.provider == null) {
238                 return null;
239             }
240             if (!(sap.spi instanceof MacSpi)) {
241                 return null;
242             }
243             return sap;
244         } catch (NoSuchAlgorithmException ignored) {
245         }
246         return null;
247     }
248 
249     /**
250      * Makes sure a MacSpi that matches this type is selected.
251      *
252      * @throws InvalidKeyException if the specified key cannot be used to
253      *             initialize this mac.
254      */
getSpi(Key key)255     private MacSpi getSpi(Key key) throws InvalidKeyException {
256         synchronized (initLock) {
257             if (spiImpl != null && provider != null && key == null) {
258                 return spiImpl;
259             }
260 
261             if (algorithm == null) {
262                 return null;
263             }
264 
265             final Engine.SpiAndProvider sap = tryAlgorithm(key, specifiedProvider, algorithm);
266             if (sap == null) {
267                 throw new ProviderException("No provider for " + getAlgorithm());
268             }
269 
270             /*
271              * Set our Spi if we've never been initialized or if we have the Spi
272              * specified and have a null provider.
273              */
274             if (spiImpl == null || provider != null) {
275                 spiImpl = (MacSpi) sap.spi;
276             }
277             provider = sap.provider;
278 
279             return spiImpl;
280         }
281     }
282 
283     /**
284      * Convenience call when the Key is not available.
285      */
getSpi()286     private MacSpi getSpi() {
287         try {
288             return getSpi(null);
289         } catch (InvalidKeyException e) {
290             throw new IllegalStateException("InvalidKeyException thrown when key == null", e);
291         }
292     }
293 
294     /**
295      * Returns the {@code MacSpi} backing this {@code Mac} or {@code null} if no {@code MacSpi} is
296      * backing this {@code Mac}.
297      *
298      * @hide
299      */
getCurrentSpi()300     public MacSpi getCurrentSpi() {
301         synchronized (initLock) {
302             return spiImpl;
303         }
304     }
305 
306     /**
307      * Returns the length of this MAC (in bytes).
308      *
309      * @return the length of this MAC (in bytes).
310      */
getMacLength()311     public final int getMacLength() {
312         return getSpi().engineGetMacLength();
313     }
314 
315     /**
316      * Initializes this {@code Mac} instance with the specified key and
317      * algorithm parameters.
318      *
319      * @param key
320      *            the key to initialize this algorithm.
321      * @param params
322      *            the parameters for this algorithm.
323      * @throws InvalidKeyException
324      *             if the specified key cannot be used to initialize this
325      *             algorithm, or it is null.
326      * @throws InvalidAlgorithmParameterException
327      *             if the specified parameters cannot be used to initialize this
328      *             algorithm.
329      */
init(Key key, AlgorithmParameterSpec params)330     public final void init(Key key, AlgorithmParameterSpec params)
331             throws InvalidKeyException, InvalidAlgorithmParameterException {
332         if (key == null) {
333             throw new InvalidKeyException("key == null");
334         }
335         getSpi(key).engineInit(key, params);
336         isInitMac = true;
337     }
338 
339     /**
340      * Initializes this {@code Mac} instance with the specified key.
341      *
342      * @param key
343      *            the key to initialize this algorithm.
344      * @throws InvalidKeyException
345      *             if initialization fails because the provided key is {@code
346      *             null}.
347      * @throws RuntimeException
348      *             if the specified key cannot be used to initialize this
349      *             algorithm.
350      */
init(Key key)351     public final void init(Key key) throws InvalidKeyException {
352         if (key == null) {
353             throw new InvalidKeyException("key == null");
354         }
355         try {
356             getSpi(key).engineInit(key, null);
357             isInitMac = true;
358         } catch (InvalidAlgorithmParameterException e) {
359             throw new RuntimeException(e);
360         }
361     }
362 
363     /**
364      * Updates this {@code Mac} instance with the specified byte.
365      *
366      * @param input
367      *            the byte
368      * @throws IllegalStateException
369      *             if this MAC is not initialized.
370      */
update(byte input)371     public final void update(byte input) throws IllegalStateException {
372         if (!isInitMac) {
373             throw new IllegalStateException();
374         }
375         getSpi().engineUpdate(input);
376     }
377 
378     /**
379      * Updates this {@code Mac} instance with the data from the specified buffer
380      * {@code input} from the specified {@code offset} and length {@code len}.
381      *
382      * @param input
383      *            the buffer.
384      * @param offset
385      *            the offset in the buffer.
386      * @param len
387      *            the length of the data in the buffer.
388      * @throws IllegalStateException
389      *             if this MAC is not initialized.
390      * @throws IllegalArgumentException
391      *             if {@code offset} and {@code len} do not specified a valid
392      *             chunk in {@code input} buffer.
393      */
update(byte[] input, int offset, int len)394     public final void update(byte[] input, int offset, int len) throws IllegalStateException {
395         if (!isInitMac) {
396             throw new IllegalStateException();
397         }
398         if (input == null) {
399             return;
400         }
401         if ((offset < 0) || (len < 0) || ((offset + len) > input.length)) {
402             throw new IllegalArgumentException("Incorrect arguments."
403                                                + " input.length=" + input.length
404                                                + " offset=" + offset + ", len=" + len);
405         }
406         getSpi().engineUpdate(input, offset, len);
407     }
408 
409     /**
410      * Copies the buffer provided as input for further processing.
411      *
412      * @param input
413      *            the buffer.
414      * @throws IllegalStateException
415      *             if this MAC is not initialized.
416      */
update(byte[] input)417     public final void update(byte[] input) throws IllegalStateException {
418         if (!isInitMac) {
419             throw new IllegalStateException();
420         }
421         if (input != null) {
422             getSpi().engineUpdate(input, 0, input.length);
423         }
424     }
425 
426     /**
427      * Updates this {@code Mac} instance with the data from the specified
428      * buffer, starting at {@link ByteBuffer#position()}, including the next
429      * {@link ByteBuffer#remaining()} bytes.
430      *
431      * @param input
432      *            the buffer.
433      * @throws IllegalStateException
434      *             if this MAC is not initialized.
435      */
update(ByteBuffer input)436     public final void update(ByteBuffer input) {
437         if (!isInitMac) {
438             throw new IllegalStateException();
439         }
440         if (input != null) {
441             getSpi().engineUpdate(input);
442         } else {
443             throw new IllegalArgumentException("input == null");
444         }
445     }
446 
447     /**
448      * Computes the digest of this MAC based on the data previously specified in
449      * {@link #update} calls.
450      * <p>
451      * This {@code Mac} instance is reverted to its initial state and can be
452      * used to start the next MAC computation with the same parameters or
453      * initialized with different parameters.
454      *
455      * @return the generated digest.
456      * @throws IllegalStateException
457      *             if this MAC is not initialized.
458      */
doFinal()459     public final byte[] doFinal() throws IllegalStateException {
460         if (!isInitMac) {
461             throw new IllegalStateException();
462         }
463         return getSpi().engineDoFinal();
464     }
465 
466     /**
467      * Computes the digest of this MAC based on the data previously specified in
468      * {@link #update} calls and stores the digest in the specified {@code
469      * output} buffer at offset {@code outOffset}.
470      * <p>
471      * This {@code Mac} instance is reverted to its initial state and can be
472      * used to start the next MAC computation with the same parameters or
473      * initialized with different parameters.
474      *
475      * @param output
476      *            the output buffer
477      * @param outOffset
478      *            the offset in the output buffer
479      * @throws ShortBufferException
480      *             if the specified output buffer is either too small for the
481      *             digest to be stored, the specified output buffer is {@code
482      *             null}, or the specified offset is negative or past the length
483      *             of the output buffer.
484      * @throws IllegalStateException
485      *             if this MAC is not initialized.
486      */
doFinal(byte[] output, int outOffset)487     public final void doFinal(byte[] output, int outOffset)
488             throws ShortBufferException, IllegalStateException {
489         if (!isInitMac) {
490             throw new IllegalStateException();
491         }
492         if (output == null) {
493             throw new ShortBufferException("output == null");
494         }
495         if ((outOffset < 0) || (outOffset >= output.length)) {
496             throw new ShortBufferException("Incorrect outOffset: " + outOffset);
497         }
498         MacSpi spi = getSpi();
499         int t = spi.engineGetMacLength();
500         if (t > (output.length - outOffset)) {
501             throw new ShortBufferException("Output buffer is short. Needed " + t + " bytes.");
502         }
503         byte[] result = spi.engineDoFinal();
504         System.arraycopy(result, 0, output, outOffset, result.length);
505 
506     }
507 
508     /**
509      * Computes the digest of this MAC based on the data previously specified on
510      * {@link #update} calls and on the final bytes specified by {@code input}
511      * (or based on those bytes only).
512      * <p>
513      * This {@code Mac} instance is reverted to its initial state and can be
514      * used to start the next MAC computation with the same parameters or
515      * initialized with different parameters.
516      *
517      * @param input
518      *            the final bytes.
519      * @return the generated digest.
520      * @throws IllegalStateException
521      *             if this MAC is not initialized.
522      */
doFinal(byte[] input)523     public final byte[] doFinal(byte[] input) throws IllegalStateException {
524         if (!isInitMac) {
525             throw new IllegalStateException();
526         }
527         MacSpi spi = getSpi();
528         if (input != null) {
529             spi.engineUpdate(input, 0, input.length);
530         }
531         return spi.engineDoFinal();
532     }
533 
534     /**
535      * Resets this {@code Mac} instance to its initial state.
536      * <p>
537      * This {@code Mac} instance is reverted to its initial state and can be
538      * used to start the next MAC computation with the same parameters or
539      * initialized with different parameters.
540      */
reset()541     public final void reset() {
542         getSpi().engineReset();
543     }
544 
545     /**
546      * Clones this {@code Mac} instance and the underlying implementation.
547      *
548      * @return the cloned instance.
549      * @throws CloneNotSupportedException
550      *             if the underlying implementation does not support cloning.
551      */
552     @Override
clone()553     public final Object clone() throws CloneNotSupportedException {
554         MacSpi newSpiImpl = null;
555         final MacSpi spi = getSpi();
556         if (spi != null) {
557             newSpiImpl = (MacSpi) spi.clone();
558         }
559         Mac mac = new Mac(newSpiImpl, this.provider, this.algorithm);
560         mac.isInitMac = this.isInitMac;
561         return mac;
562     }
563 }
564