1 /* Mac.java -- The message authentication code interface.
2    Copyright (C) 2004  Free Software Foundation, Inc.
3 
4 This file is part of GNU Classpath.
5 
6 GNU Classpath is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2, or (at your option)
9 any later version.
10 
11 GNU Classpath is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 General Public License for more details.
15 
16 You should have received a copy of the GNU General Public License
17 along with GNU Classpath; see the file COPYING.  If not, write to the
18 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 02110-1301 USA.
20 
21 Linking this library statically or dynamically with other modules is
22 making a combined work based on this library.  Thus, the terms and
23 conditions of the GNU General Public License cover the whole
24 combination.
25 
26 As a special exception, the copyright holders of this library give you
27 permission to link this library with independent modules to produce an
28 executable, regardless of the license terms of these independent
29 modules, and to copy and distribute the resulting executable under
30 terms of your choice, provided that you also meet, for each linked
31 independent module, the terms and conditions of the license of that
32 module.  An independent module is a module which is not derived from
33 or based on this library.  If you modify this library, you may extend
34 this exception to your version of the library, but you are not
35 obligated to do so.  If you do not wish to do so, delete this
36 exception statement from your version. */
37 
38 
39 package javax.crypto;
40 
41 import gnu.java.security.Engine;
42 
43 import java.lang.reflect.InvocationTargetException;
44 import java.nio.ByteBuffer;
45 import java.security.InvalidAlgorithmParameterException;
46 import java.security.InvalidKeyException;
47 import java.security.Key;
48 import java.security.NoSuchAlgorithmException;
49 import java.security.NoSuchProviderException;
50 import java.security.Provider;
51 import java.security.Security;
52 import java.security.spec.AlgorithmParameterSpec;
53 
54 /**
55  * This class implements a "message authentication code" (MAC), a method
56  * to ensure the integrity of data transmitted between two parties who
57  * share a common secret key.
58  *
59  * <p>The best way to describe a MAC is as a <i>keyed one-way hash
60  * function</i>, which looks like:
61  *
62  * <blockquote><p><code>D = MAC(K, M)</code></blockquote>
63  *
64  * <p>where <code>K</code> is the key, <code>M</code> is the message,
65  * and <code>D</code> is the resulting digest. One party will usually
66  * send the concatenation <code>M || D</code> to the other party, who
67  * will then verify <code>D</code> by computing <code>D'</code> in a
68  * similar fashion. If <code>D == D'</code>, then the message is assumed
69  * to be authentic.
70  *
71  * @author Casey Marshall (csm@gnu.org)
72  */
73 public class Mac implements Cloneable
74 {
75 
76   // Fields.
77   // ------------------------------------------------------------------------
78 
79   private static final String SERVICE = "Mac";
80 
81   /** The underlying MAC implementation. */
82   private MacSpi macSpi;
83 
84   /** The provider we got our implementation from. */
85   private Provider provider;
86 
87   /** The name of the algorithm. */
88   private String algorithm;
89 
90   /** Whether or not we've been initialized. */
91   private boolean virgin;
92 
93   // Constructor.
94   // ------------------------------------------------------------------------
95 
96   /**
97    * Creates a new Mac instance.
98    *
99    * @param macSpi    The underlying MAC implementation.
100    * @param provider  The provider of this implementation.
101    * @param algorithm The name of this MAC algorithm.
102    */
Mac(MacSpi macSpi, Provider provider, String algorithm)103   protected Mac(MacSpi macSpi, Provider provider, String algorithm)
104   {
105     this.macSpi = macSpi;
106     this.provider = provider;
107     this.algorithm = algorithm;
108     virgin = true;
109   }
110 
111   /**
112    * Create an instance of the named algorithm from the first provider with an
113    * appropriate implementation.
114    *
115    * @param algorithm The name of the algorithm.
116    * @return An appropriate Mac instance, if the specified algorithm is
117    *         implemented by a provider.
118    * @throws NoSuchAlgorithmException If no implementation of the named
119    *           algorithm is installed.
120    * @throws IllegalArgumentException if <code>algorithm</code> is
121    *           <code>null</code> or is an empty string.
122    */
getInstance(String algorithm)123   public static final Mac getInstance(String algorithm)
124       throws NoSuchAlgorithmException
125   {
126     Provider[] p = Security.getProviders();
127     NoSuchAlgorithmException lastException = null;
128     for (int i = 0; i < p.length; i++)
129       try
130         {
131           return getInstance(algorithm, p[i]);
132         }
133       catch (NoSuchAlgorithmException x)
134         {
135           lastException = x;
136         }
137       if (lastException != null)
138         throw lastException;
139       throw new NoSuchAlgorithmException(algorithm);
140   }
141 
142   /**
143    * Create an instance of the named algorithm from the named provider.
144    *
145    * @param algorithm The name of the algorithm.
146    * @param provider The name of the provider.
147    * @return An appropriate Mac instance, if the specified algorithm is
148    *         implemented by the named provider.
149    * @throws NoSuchAlgorithmException If the named provider has no
150    *           implementation of the algorithm.
151    * @throws NoSuchProviderException If the named provider does not exist.
152    * @throws IllegalArgumentException if either <code>algorithm</code> or
153    *           <code>provider</code> is <code>null</code>, or if
154    *           <code>algorithm</code> is an empty string.
155    */
getInstance(String algorithm, String provider)156   public static final Mac getInstance(String algorithm, String provider)
157       throws NoSuchAlgorithmException, NoSuchProviderException
158   {
159     if (provider == null)
160       throw new IllegalArgumentException("provider MUST NOT be null");
161     Provider p = Security.getProvider(provider);
162     if (p == null)
163       throw new NoSuchProviderException(provider);
164     return getInstance(algorithm, p);
165   }
166 
167   /**
168    * Create an instance of the named algorithm from a provider.
169    *
170    * @param algorithm The name of the algorithm.
171    * @param provider The provider.
172    * @return An appropriate Mac instance, if the specified algorithm is
173    *         implemented by the provider.
174    * @throws NoSuchAlgorithmException If the provider has no implementation of
175    *           the algorithm.
176    * @throws IllegalArgumentException if either <code>algorithm</code> or
177    *           <code>provider</code> is <code>null</code>, or if
178    *           <code>algorithm</code> is an empty string.
179    */
getInstance(String algorithm, Provider provider)180   public static final Mac getInstance(String algorithm, Provider provider)
181       throws NoSuchAlgorithmException
182   {
183     StringBuilder sb = new StringBuilder("Mac algorithm [")
184         .append(algorithm).append("] from provider[")
185         .append(provider).append("] could not be created");
186     Throwable cause;
187     try
188       {
189         Object spi = Engine.getInstance(SERVICE, algorithm, provider);
190         return new Mac((MacSpi) spi, provider, algorithm);
191       }
192     catch (InvocationTargetException x)
193       {
194         cause = x.getCause();
195         if (cause instanceof NoSuchAlgorithmException)
196           throw (NoSuchAlgorithmException) cause;
197         if (cause == null)
198           cause = x;
199       }
200     catch (ClassCastException x)
201       {
202         cause = x;
203       }
204     NoSuchAlgorithmException x = new NoSuchAlgorithmException(sb.toString());
205     x.initCause(cause);
206     throw x;
207   }
208 
209   /**
210    * Finishes the computation of a MAC and returns the digest.
211    *
212    * <p>After this method succeeds, it may be used again as just after a
213    * call to <code>init</code>, and can compute another MAC using the
214    * same key and parameters.
215    *
216    * @return The message authentication code.
217    * @throws java.lang.IllegalStateException If this instnace has not
218    *         been initialized.
219    */
doFinal()220   public final byte[] doFinal() throws IllegalStateException
221   {
222     if (virgin)
223       {
224         throw new IllegalStateException("not initialized");
225       }
226     byte[] digest = macSpi.engineDoFinal();
227     reset();
228     return digest;
229   }
230 
231   /**
232    * Finishes the computation of a MAC with a final byte array (or
233    * computes a MAC over those bytes only) and returns the digest.
234    *
235    * <p>After this method succeeds, it may be used again as just after a
236    * call to <code>init</code>, and can compute another MAC using the
237    * same key and parameters.
238    *
239    * @param input The bytes to add.
240    * @return The message authentication code.
241    * @throws java.lang.IllegalStateException If this instnace has not
242    *         been initialized.
243    */
doFinal(byte[] input)244   public final byte[] doFinal(byte[] input) throws IllegalStateException
245   {
246     update(input);
247     byte[] digest = macSpi.engineDoFinal();
248     reset();
249     return digest;
250   }
251 
252   /**
253    * Finishes the computation of a MAC and places the result into the
254    * given array.
255    *
256    * <p>After this method succeeds, it may be used again as just after a
257    * call to <code>init</code>, and can compute another MAC using the
258    * same key and parameters.
259    *
260    * @param output    The destination for the result.
261    * @param outOffset The index in the output array to start.
262    * @return The message authentication code.
263    * @throws java.lang.IllegalStateException If this instnace has not
264    *         been initialized.
265    * @throws javax.crypto.ShortBufferException If <code>output</code> is
266    *         not large enough to hold the result.
267    */
doFinal(byte[] output, int outOffset)268   public final void doFinal(byte[] output, int outOffset)
269   throws IllegalStateException, ShortBufferException
270   {
271     if (virgin)
272       {
273         throw new IllegalStateException("not initialized");
274       }
275     if (output.length - outOffset < getMacLength())
276       {
277         throw new ShortBufferException();
278       }
279     byte[] mac = macSpi.engineDoFinal();
280     System.arraycopy(mac, 0, output, outOffset, getMacLength());
281     reset();
282   }
283 
284   /**
285    * Returns the name of this MAC algorithm.
286    *
287    * @return The MAC name.
288    */
getAlgorithm()289   public final String getAlgorithm()
290   {
291     return algorithm;
292   }
293 
294   /**
295    * Get the size of the MAC. This is the size of the array returned by
296    * {@link #doFinal()} and {@link #doFinal(byte[])}, and the minimum
297    * number of bytes that must be available in the byte array passed to
298    * {@link #doFinal(byte[],int)}.
299    *
300    * @return The MAC length.
301    */
getMacLength()302   public final int getMacLength()
303   {
304     return macSpi.engineGetMacLength();
305   }
306 
307   /**
308    * Get the provider of the underlying implementation.
309    *
310    * @return The provider.
311    */
getProvider()312   public final Provider getProvider()
313   {
314     return provider;
315   }
316 
317   /**
318    * Initialize this MAC with a key and no parameters.
319    *
320    * @param key The key to initialize this instance with.
321    * @throws java.security.InvalidKeyException If the key is
322    *         unacceptable.
323    */
init(Key key)324   public final void init(Key key) throws InvalidKeyException
325   {
326     try
327       {
328         init(key, null);
329       }
330     catch (InvalidAlgorithmParameterException iape)
331       {
332         throw new IllegalArgumentException(algorithm + " needs parameters");
333       }
334   }
335 
336   /**
337    * Initialize this MAC with a key and parameters.
338    *
339    * @param key    The key to initialize this instance with.
340    * @param params The algorithm-specific parameters.
341    * @throws java.security.InvalidAlgorithmParameterException If the
342    *         algorithm parameters are unacceptable.
343    * @throws java.security.InvalidKeyException If the key is
344    *         unacceptable.
345    */
init(Key key, AlgorithmParameterSpec params)346   public final void init(Key key, AlgorithmParameterSpec params)
347     throws InvalidAlgorithmParameterException, InvalidKeyException
348   {
349     macSpi.engineInit(key, params);
350     virgin = false;                      // w00t!
351   }
352 
353   /**
354    * Reset this instance. A call to this method returns this instance
355    * back to the state it was in just after it was initialized.
356    */
reset()357   public final void reset()
358   {
359     macSpi.engineReset();
360   }
361 
362   /**
363    * Update the computation with a single byte.
364    *
365    * @param input The next byte.
366    * @throws java.lang.IllegalStateException If this instance has not
367    *         been initialized.
368    */
update(byte input)369   public final void update(byte input) throws IllegalStateException
370   {
371     if (virgin)
372       {
373         throw new IllegalStateException("not initialized");
374       }
375     macSpi.engineUpdate(input);
376   }
377 
378   /**
379    * Update the computation with a byte array.
380    *
381    * @param input The next bytes.
382    * @throws java.lang.IllegalStateException If this instance has not
383    *         been initialized.
384    */
update(byte[] input)385   public final void update(byte[] input) throws IllegalStateException
386   {
387     update(input, 0, input.length);
388   }
389 
390   /**
391    * Update the computation with a portion of a byte array.
392    *
393    * @param input  The next bytes.
394    * @param offset The index in <code>input</code> to start.
395    * @param length The number of bytes to update.
396    * @throws java.lang.IllegalStateException If this instance has not
397    *         been initialized.
398    */
update(byte[] input, int offset, int length)399   public final void update(byte[] input, int offset, int length)
400     throws IllegalStateException
401   {
402     if (virgin)
403       {
404         throw new IllegalStateException("not initialized");
405       }
406     macSpi.engineUpdate(input, offset, length);
407   }
408 
409   /**
410    * Update this MAC with the remaining bytes in the given buffer
411    * @param buffer The input buffer.
412    * @since 1.5
413    */
update(final ByteBuffer buffer)414   public final void update (final ByteBuffer buffer)
415   {
416     if (virgin)
417       throw new IllegalStateException ("not initialized");
418     macSpi.engineUpdate(buffer);
419   }
420 
421   /**
422    * Clone this instance, if the underlying implementation supports it.
423    *
424    * @return A clone of this instance.
425    * @throws java.lang.CloneNotSupportedException If the underlying
426    *         implementation is not cloneable.
427    */
clone()428   public final Object clone() throws CloneNotSupportedException
429   {
430     Mac result = new Mac((MacSpi) macSpi.clone(), provider, algorithm);
431     result.virgin = virgin;
432     return result;
433   }
434 }
435