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 }