Mac.java revision cec4dd4b1d33f78997603d0f89c0d0e56e64dbcd
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
18package javax.crypto;
19
20import java.nio.ByteBuffer;
21import java.security.InvalidAlgorithmParameterException;
22import java.security.InvalidKeyException;
23import java.security.Key;
24import java.security.NoSuchAlgorithmException;
25import java.security.NoSuchProviderException;
26import java.security.Provider;
27import java.security.Security;
28import java.security.spec.AlgorithmParameterSpec;
29
30import org.apache.harmony.crypto.internal.nls.Messages;
31import 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 */
38public class Mac implements Cloneable {
39
40    //Used to access common engine functionality
41    private static final Engine engine = new Engine("Mac"); //$NON-NLS-1$
42
43    // Store used provider
44    private final Provider provider;
45
46    // Store used spi implementation
47    private final MacSpi spiImpl;
48
49    // Store used algorithm name
50    private final String algorithm;
51
52    // Store Mac state (initialized or not initialized)
53    private boolean isInitMac;
54
55    /**
56     * Creates a new {@code Mac} instance.
57     *
58     * @param macSpi
59     *            the implementation delegate.
60     * @param provider
61     *            the implementation provider.
62     * @param algorithm
63     *            the name of the MAC algorithm.
64     */
65    protected Mac(MacSpi macSpi, Provider provider, String algorithm) {
66        this.provider = provider;
67        this.algorithm = algorithm;
68        this.spiImpl = macSpi;
69        this.isInitMac = false;
70    }
71
72    /**
73     * Returns the name of the MAC algorithm.
74     *
75     * @return the name of the MAC algorithm.
76     */
77    public final String getAlgorithm() {
78        return algorithm;
79    }
80
81    /**
82     * Returns the provider of this {@code Mac} instance.
83     *
84     * @return the provider of this {@code Mac} instance.
85     */
86    public final Provider getProvider() {
87        return provider;
88    }
89
90    /**
91     * Creates a new {@code Mac} instance that provides the specified MAC
92     * algorithm.
93     *
94     * @param algorithm
95     *            the name of the requested MAC algorithm.
96     * @return the new {@code Mac} instance.
97     * @throws NoSuchAlgorithmException
98     *             if the specified algorithm is not available by any provider.
99     * @throws NullPointerException
100     *             if {@code algorithm} is {@code null} (instead of
101     *             NoSuchAlgorithmException as in 1.4 release).
102     */
103    public static final Mac getInstance(String algorithm)
104            throws NoSuchAlgorithmException {
105        if (algorithm == null) {
106            throw new NullPointerException(Messages.getString("crypto.02")); //$NON-NLS-1$
107        }
108        synchronized (engine) {
109            engine.getInstance(algorithm, null);
110            return new Mac((MacSpi) engine.spi, engine.provider, algorithm);
111        }
112    }
113
114    /**
115     * Creates a new {@code Mac} instance that provides the specified MAC
116     * algorithm from the specified provider.
117     *
118     * @param algorithm
119     *            the name of the requested MAC algorithm.
120     * @param provider
121     *            the name of the provider that is providing the algorithm.
122     * @return the new {@code Mac} instance.
123     * @throws NoSuchAlgorithmException
124     *             if the specified algorithm is not provided by the specified
125     *             provider.
126     * @throws NoSuchProviderException
127     *             if the specified provider is not available.
128     * @throws IllegalArgumentException
129     *             if the specified provider name is {@code null} or empty.
130     * @throws NullPointerException
131     *             if {@code algorithm} is {@code null} (instead of
132     *             NoSuchAlgorithmException as in 1.4 release).
133     */
134    public static final Mac getInstance(String algorithm, String provider)
135            throws NoSuchAlgorithmException, NoSuchProviderException {
136        if ((provider == null) || (provider.length() == 0)) {
137            throw new IllegalArgumentException(Messages.getString("crypto.03")); //$NON-NLS-1$
138        }
139        Provider impProvider = Security.getProvider(provider);
140        if (impProvider == null) {
141            throw new NoSuchProviderException(provider);
142        }
143        return getInstance(algorithm, impProvider);
144    }
145
146    /**
147     * Creates a new {@code Mac} instance that provides the specified MAC
148     * algorithm from the specified provider.
149     *
150     * @param algorithm
151     *            the name of the requested MAC algorithm.
152     * @param provider
153     *            the provider that is providing the algorithm.
154     * @return the new {@code Mac} instance.
155     * @throws NoSuchAlgorithmException
156     *             if the specified algorithm is not provided by the specified
157     *             provider.
158     * @throws IllegalArgumentException
159     *             if {@code provider} is {@code null}.
160     * @throws NullPointerException
161     *             if {@code algorithm} is {@code null} (instead of
162     *             NoSuchAlgorithmException as in 1.4 release).
163     */
164    public static final Mac getInstance(String algorithm, Provider provider)
165            throws NoSuchAlgorithmException {
166        if (provider == null) {
167            throw new IllegalArgumentException(Messages.getString("crypto.04")); //$NON-NLS-1$
168        }
169        if (algorithm == null) {
170            throw new NullPointerException(Messages.getString("crypto.02")); //$NON-NLS-1$
171        }
172        synchronized (engine) {
173            engine.getInstance(algorithm, provider, null);
174            return new Mac((MacSpi) engine.spi, provider, algorithm);
175        }
176    }
177
178    /**
179     * Returns the length of this MAC (in bytes).
180     *
181     * @return the length of this MAC (in bytes).
182     */
183    public final int getMacLength() {
184        return spiImpl.engineGetMacLength();
185    }
186
187    /**
188     * Initializes this {@code Mac} instance with the specified key and
189     * algorithm parameters.
190     *
191     * @param key
192     *            the key to initialize this algorithm.
193     * @param params
194     *            the parameters for this algorithm.
195     * @throws InvalidKeyException
196     *             if the specified key cannot be used to initialize this
197     *             algorithm, or it is null.
198     * @throws InvalidAlgorithmParameterException
199     *             if the specified parameters cannot be used to initialize this
200     *             algorithm.
201     */
202    public final void init(Key key, AlgorithmParameterSpec params)
203            throws InvalidKeyException, InvalidAlgorithmParameterException {
204        if (key == null) {
205            throw new InvalidKeyException(Messages.getString("crypto.05")); //$NON-NLS-1$
206        }
207        spiImpl.engineInit(key, params);
208        isInitMac = true;
209    }
210
211    /**
212     * Initializes this {@code Mac} instance with the specified key.
213     *
214     * @param key
215     *            the key to initialize this algorithm.
216     * @throws InvalidKeyException
217     *             if initialization fails because the provided key is {@code
218     *             null}.
219     * @throws RuntimeException
220     *             if the specified key cannot be used to initialize this
221     *             algorithm.
222     */
223    public final void init(Key key) throws InvalidKeyException {
224        if (key == null) {
225            throw new InvalidKeyException(Messages.getString("crypto.05")); //$NON-NLS-1$
226        }
227        try {
228            spiImpl.engineInit(key, null);
229            isInitMac = true;
230        } catch (InvalidAlgorithmParameterException e) {
231            throw new RuntimeException(e);
232        }
233    }
234
235    /**
236     * Updates this {@code Mac} instance with the specified byte.
237     *
238     * @param input
239     *            the byte
240     * @throws IllegalStateException
241     *             if this MAC is not initialized.
242     */
243    public final void update(byte input) throws IllegalStateException {
244        if (!isInitMac) {
245            throw new IllegalStateException(Messages.getString("crypto.01"));
246        }
247        spiImpl.engineUpdate(input);
248    }
249
250    /**
251     * Updates this {@code Mac} instance with the data from the specified buffer
252     * {@code input} from the specified {@code offset} and length {@code len}.
253     *
254     * @param input
255     *            the buffer.
256     * @param offset
257     *            the offset in the buffer.
258     * @param len
259     *            the length of the data in the buffer.
260     * @throws IllegalStateException
261     *             if this MAC is not initialized.
262     * @throws IllegalArgumentException
263     *             if {@code offset} and {@code len} do not specified a valid
264     *             chunk in {@code input} buffer.
265     */
266    public final void update(byte[] input, int offset, int len)
267            throws IllegalStateException {
268        if (!isInitMac) {
269            throw new IllegalStateException(Messages.getString("crypto.01"));
270        }
271        if (input == null) {
272            return;
273        }
274        if ((offset < 0) || (len < 0) || ((offset + len) > input.length)) {
275            throw new IllegalArgumentException(Messages.getString("crypto.06")); //$NON-NLS-1$
276        }
277        spiImpl.engineUpdate(input, offset, len);
278    }
279
280    /**
281     * Copies the buffer provided as input for further processing.
282     *
283     * @param input
284     *            the buffer.
285     * @throws IllegalStateException
286     *             if this MAC is not initialized.
287     */
288    public final void update(byte[] input) throws IllegalStateException {
289        if (!isInitMac) {
290            throw new IllegalStateException(Messages.getString("crypto.01"));
291        }
292        if (input != null) {
293            spiImpl.engineUpdate(input, 0, input.length);
294        }
295    }
296
297    /**
298     * Updates this {@code Mac} instance with the data from the specified
299     * buffer, starting at {@link ByteBuffer#position()}, including the next
300     * {@link ByteBuffer#remaining()} bytes.
301     *
302     * @param input
303     *            the buffer.
304     * @throws IllegalStateException
305     *             if this MAC is not initialized.
306     */
307    public final void update(ByteBuffer input) {
308        if (!isInitMac) {
309            throw new IllegalStateException(Messages.getString("crypto.01"));
310        }
311        if (input != null) {
312            spiImpl.engineUpdate(input);
313        } else {
314            throw new IllegalArgumentException(Messages.getString("crypto.07")); //$NON-NLS-1$
315        }
316    }
317
318    /**
319     * Computes the digest of this MAC based on the data previously specified in
320     * {@link #update} calls.
321     * <p>
322     * This {@code Mac} instance is reverted to its initial state and can be
323     * used to start the next MAC computation with the same parameters or
324     * initialized with different parameters.
325     *
326     * @return the generated digest.
327     * @throws IllegalStateException
328     *             if this MAC is not initialized.
329     */
330    public final byte[] doFinal() throws IllegalStateException {
331        if (!isInitMac) {
332            throw new IllegalStateException(Messages.getString("crypto.01"));
333        }
334        return spiImpl.engineDoFinal();
335    }
336
337    /**
338     * Computes the digest of this MAC based on the data previously specified in
339     * {@link #update} calls and stores the digest in the specified {@code
340     * output} buffer at offset {@code outOffset}.
341     * <p>
342     * This {@code Mac} instance is reverted to its initial state and can be
343     * used to start the next MAC computation with the same parameters or
344     * initialized with different parameters.
345     *
346     * @param output
347     *            the output buffer
348     * @param outOffset
349     *            the offset in the output buffer
350     * @throws ShortBufferException
351     *             if the specified output buffer is either too small for the
352     *             digest to be stored, the specified output buffer is {@code
353     *             null}, or the specified offset is negative or past the length
354     *             of the output buffer.
355     * @throws IllegalStateException
356     *             if this MAC is not initialized.
357     */
358    public final void doFinal(byte[] output, int outOffset)
359            throws ShortBufferException, IllegalStateException {
360        if (!isInitMac) {
361            throw new IllegalStateException(Messages.getString("crypto.01"));
362        }
363        if (output == null) {
364            throw new ShortBufferException(Messages.getString("crypto.08")); //$NON-NLS-1$
365        }
366        if ((outOffset < 0) || (outOffset >= output.length)) {
367            throw new ShortBufferException(Messages.getString("crypto.09", //$NON-NLS-1$
368                    Integer.toString(outOffset)));
369        }
370        int t = spiImpl.engineGetMacLength();
371        if (t > (output.length - outOffset)) {
372            throw new ShortBufferException(
373                    Messages.getString("crypto.0A", //$NON-NLS-1$
374                            Integer.toString(t)));
375        }
376        byte[] result = spiImpl.engineDoFinal();
377        System.arraycopy(result, 0, output, outOffset, result.length);
378
379    }
380
381    /**
382     * Computes the digest of this MAC based on the data previously specified on
383     * {@link #update} calls and on the final bytes specified by {@code input}
384     * (or based on those bytes only).
385     * <p>
386     * This {@code Mac} instance is reverted to its initial state and can be
387     * used to start the next MAC computation with the same parameters or
388     * initialized with different parameters.
389     *
390     * @param input
391     *            the final bytes.
392     * @return the generated digest.
393     * @throws IllegalStateException
394     *             if this MAC is not initialized.
395     */
396    public final byte[] doFinal(byte[] input) throws IllegalStateException {
397        if (!isInitMac) {
398            throw new IllegalStateException(Messages.getString("crypto.0B")); //$NON-NLS-1$
399        }
400        if (input != null) {
401            spiImpl.engineUpdate(input, 0, input.length);
402        }
403        return spiImpl.engineDoFinal();
404    }
405
406    /**
407     * Resets this {@code Mac} instance to its initial state.
408     * <p>
409     * This {@code Mac} instance is reverted to its initial state and can be
410     * used to start the next MAC computation with the same parameters or
411     * initialized with different parameters.
412     */
413    public final void reset() {
414        spiImpl.engineReset();
415    }
416
417    /**
418     * Clones this {@code Mac} instance and the underlying implementation.
419     *
420     * @return the cloned instance.
421     * @throws CloneNotSupportedException
422     *             if the underlying implementation does not support cloning.
423     */
424    @Override
425    public final Object clone() throws CloneNotSupportedException {
426        MacSpi newSpiImpl = (MacSpi)spiImpl.clone();
427        Mac mac = new Mac(newSpiImpl, this.provider, this.algorithm);
428        mac.isInitMac = this.isInitMac;
429        return mac;
430    }
431}