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