001    /* Mac.java -- The message authentication code interface.
002       Copyright (C) 2004  Free Software Foundation, Inc.
003    
004    This file is part of GNU Classpath.
005    
006    GNU Classpath is free software; you can redistribute it and/or modify
007    it under the terms of the GNU General Public License as published by
008    the Free Software Foundation; either version 2, or (at your option)
009    any later version.
010    
011    GNU Classpath is distributed in the hope that it will be useful, but
012    WITHOUT ANY WARRANTY; without even the implied warranty of
013    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
014    General Public License for more details.
015    
016    You should have received a copy of the GNU General Public License
017    along with GNU Classpath; see the file COPYING.  If not, write to the
018    Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
019    02110-1301 USA.
020    
021    Linking this library statically or dynamically with other modules is
022    making a combined work based on this library.  Thus, the terms and
023    conditions of the GNU General Public License cover the whole
024    combination.
025    
026    As a special exception, the copyright holders of this library give you
027    permission to link this library with independent modules to produce an
028    executable, regardless of the license terms of these independent
029    modules, and to copy and distribute the resulting executable under
030    terms of your choice, provided that you also meet, for each linked
031    independent module, the terms and conditions of the license of that
032    module.  An independent module is a module which is not derived from
033    or based on this library.  If you modify this library, you may extend
034    this exception to your version of the library, but you are not
035    obligated to do so.  If you do not wish to do so, delete this
036    exception statement from your version. */
037    
038    
039    package javax.crypto;
040    
041    import gnu.java.security.Engine;
042    
043    import java.lang.reflect.InvocationTargetException;
044    import java.nio.ByteBuffer;
045    import java.security.InvalidAlgorithmParameterException;
046    import java.security.InvalidKeyException;
047    import java.security.Key;
048    import java.security.NoSuchAlgorithmException;
049    import java.security.NoSuchProviderException;
050    import java.security.Provider;
051    import java.security.Security;
052    import java.security.spec.AlgorithmParameterSpec;
053    
054    /**
055     * This class implements a "message authentication code" (MAC), a method
056     * to ensure the integrity of data transmitted between two parties who
057     * share a common secret key.
058     *
059     * <p>The best way to describe a MAC is as a <i>keyed one-way hash
060     * function</i>, which looks like:
061     *
062     * <blockquote><p><code>D = MAC(K, M)</code></blockquote>
063     *
064     * <p>where <code>K</code> is the key, <code>M</code> is the message,
065     * and <code>D</code> is the resulting digest. One party will usually
066     * send the concatenation <code>M || D</code> to the other party, who
067     * will then verify <code>D</code> by computing <code>D'</code> in a
068     * similar fashion. If <code>D == D'</code>, then the message is assumed
069     * to be authentic.
070     *
071     * @author Casey Marshall (csm@gnu.org)
072     */
073    public class Mac implements Cloneable
074    {
075    
076      // Fields.
077      // ------------------------------------------------------------------------
078    
079      private static final String SERVICE = "Mac";
080    
081      /** The underlying MAC implementation. */
082      private MacSpi macSpi;
083    
084      /** The provider we got our implementation from. */
085      private Provider provider;
086    
087      /** The name of the algorithm. */
088      private String algorithm;
089    
090      /** Whether or not we've been initialized. */
091      private boolean virgin;
092    
093      // Constructor.
094      // ------------------------------------------------------------------------
095    
096      /**
097       * Creates a new Mac instance.
098       *
099       * @param macSpi    The underlying MAC implementation.
100       * @param provider  The provider of this implementation.
101       * @param algorithm The name of this MAC algorithm.
102       */
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       */
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       */
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       */
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       */
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       */
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       */
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       */
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       */
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       */
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       */
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       */
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       */
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       */
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       */
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       */
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       */
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       */
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    }