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.security.AlgorithmParameters;
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;
29import java.util.Arrays;
30import org.apache.harmony.security.fortress.Engine;
31
32/**
33 * This class implements the functionality of an exemption mechanism such as
34 * <i>key recovery</i>, <i>key weakening</i>, or <i>key escrow</i>.
35 */
36public class ExemptionMechanism {
37
38    // Used to access common engine functionality
39    private static final Engine ENGINE = new Engine("ExemptionMechanism");
40
41    // Store used provider
42    private final Provider provider;
43
44    // Store used spi implementation
45    private final ExemptionMechanismSpi spiImpl;
46
47    // Store mechanism name
48    private final String mechanism;
49
50    // Store state (initialized or not)
51    private boolean isInit;
52
53    // Store initKey value
54    private Key initKey;
55
56    // Indicates if blob generated successfully
57    private boolean generated;
58
59    /**
60     * Creates a {@code ExemptionMechanism} instance.
61     *
62     * @param exmechSpi
63     *            the implementation delegate.
64     * @param provider
65     *            the associated provider.
66     * @param mechanism
67     *            the name of the mechanism.
68     */
69    protected ExemptionMechanism(ExemptionMechanismSpi exmechSpi,
70            Provider provider, String mechanism) {
71        this.mechanism = mechanism;
72        this.spiImpl = exmechSpi;
73        this.provider = provider;
74        isInit = false;
75    }
76
77    /**
78     * Returns the name of this {@code ExemptionMechanism}.
79     *
80     * @return the name of this {@code ExemptionMechanism}.
81     */
82    public final String getName() {
83        return mechanism;
84    }
85
86    /**
87     * Returns a new {@code ExemptionMechanism} instance that provides the
88     * specified exemption mechanism algorithm.
89     *
90     * @param algorithm
91     *            the name of the requested exemption mechanism.
92     * @return the new {@code ExemptionMechanism} instance.
93     * @throws NoSuchAlgorithmException
94     *             if the specified algorithm is not available by any provider.
95     * @throws NullPointerException
96     *             if the algorithm parameter is {@code null}.
97     */
98    public static final ExemptionMechanism getInstance(String algorithm)
99            throws NoSuchAlgorithmException {
100        if (algorithm == null) {
101            throw new NullPointerException("algorithm == null");
102        }
103        Engine.SpiAndProvider sap = ENGINE.getInstance(algorithm, null);
104        return new ExemptionMechanism((ExemptionMechanismSpi) sap.spi, sap.provider, algorithm);
105    }
106
107    /**
108     * Returns a new {@code ExemptionMechansm} instance that provides the
109     * specified exemption mechanism algorithm from the specified provider.
110     *
111     * @param algorithm
112     *            the name of the requested exemption mechanism.
113     * @param provider
114     *            the name of the provider that is providing the algorithm.
115     * @return the new {@code ExemptionMechanism} instance.
116     * @throws NoSuchAlgorithmException
117     *             if the specified algorithm is not provided by the specified
118     *             provider.
119     * @throws NoSuchProviderException
120     *             if the specified provider is not available.
121     * @throws NullPointerException
122     *             if the algorithm parameter is {@code null}.
123     * @throws IllegalArgumentException
124     *             if the provider parameter is {@code null}.
125     */
126    public static final ExemptionMechanism getInstance(String algorithm,
127            String provider) throws NoSuchAlgorithmException,
128            NoSuchProviderException {
129        if (provider == null) {
130            throw new IllegalArgumentException("provider == null");
131        }
132        Provider impProvider = Security.getProvider(provider);
133        if (impProvider == null) {
134            throw new NoSuchProviderException(provider);
135        }
136        if (algorithm == null) {
137            throw new NullPointerException("algorithm == null");
138        }
139        return getInstance(algorithm, impProvider);
140    }
141
142    /**
143     * Returns a new {@code ExemptionMechanism} instance that provides the
144     * specified exemption mechanism algorithm from the specified provider.
145     * The {@code provider} supplied does not have to be registered.
146     *
147     * @param algorithm
148     *            the name of the requested exemption mechanism.
149     * @param provider
150     *            the provider that is providing the algorithm.
151     * @return the new {@code ExemptionMechanism} instance.
152     * @throws NoSuchAlgorithmException
153     *             if the specified algorithm is not provided by the specified
154     *             provider.
155     * @throws NullPointerException
156     *             if the algorithm parameter is {@code null}.
157     * @throws IllegalArgumentException
158     *             if the provider parameter is {@code null}.
159     */
160    public static final ExemptionMechanism getInstance(String algorithm,
161            Provider provider) throws NoSuchAlgorithmException {
162        if (algorithm == null) {
163            throw new NullPointerException("algorithm == null");
164        }
165        if (provider == null) {
166            throw new IllegalArgumentException("provider == null");
167        }
168        Object spi = ENGINE.getInstance(algorithm, provider, null);
169        return new ExemptionMechanism((ExemptionMechanismSpi) spi, provider, algorithm);
170    }
171
172    /**
173     * Returns the provider of this {@code ExemptionMechanism} instance.
174     *
175     * @return the provider of this {@code ExemptionMechanism} instance.
176     */
177    public final Provider getProvider() {
178        return provider;
179    }
180
181    /**
182     * Returns whether the result blob for this {@code ExemptionMechanism}
183     * instance has been generated successfully and that the specified key is
184     * the same as the one that was used to initialize and generate.
185     *
186     * @param key
187     *            the key to verify.
188     * @return whether the result blob for this {@code ExemptionMechanism}
189     *         instance has been generated successfully.
190     * @throws ExemptionMechanismException
191     *             if an error occurs while determining whether the result blob
192     *             has been generated successfully.
193     */
194    public final boolean isCryptoAllowed(Key key)
195            throws ExemptionMechanismException {
196
197        if (generated
198                && (initKey.equals(key) || Arrays.equals(initKey.getEncoded(),
199                        key.getEncoded()))) {
200            return true;
201        }
202        return false;
203    }
204
205    /**
206     * Returns the size in bytes for the output buffer needed to hold the output
207     * of the next {@link #genExemptionBlob} call, given the specified {@code
208     * inputLen} (in bytes).
209     *
210     * @param inputLen
211     *            the specified input length (in bytes).
212     * @return the size in bytes for the output buffer.
213     * @throws IllegalStateException
214     *             if this {@code ExemptionMechanism} instance is not
215     *             initialized.
216     */
217    public final int getOutputSize(int inputLen) throws IllegalStateException {
218        if (!isInit) {
219            throw new IllegalStateException("ExemptionMechanism is not initialized");
220        }
221        return spiImpl.engineGetOutputSize(inputLen);
222    }
223
224    /**
225     * Initializes this {@code ExemptionMechanism} instance with the
226     * specified key.
227     *
228     * @param key
229     *            the key to initialize this instance with.
230     * @throws InvalidKeyException
231     *             if the key cannot be used to initialize this mechanism.
232     * @throws ExemptionMechanismException
233     *             if error(s) occur during initialization.
234     */
235    public final void init(Key key) throws InvalidKeyException,
236            ExemptionMechanismException {
237        generated = false;
238        spiImpl.engineInit(key);
239        initKey = key;
240        isInit = true;
241    }
242
243    /**
244     * Initializes this {@code ExemptionMechanism} instance with the
245     * specified key and algorithm parameters.
246     *
247     * @param key
248     *            the key to initialize this instance with.
249     * @param param
250     *            the parameters for this exemption mechanism algorithm.
251     * @throws InvalidKeyException
252     *             if the key cannot be used to initialize this mechanism.
253     * @throws InvalidAlgorithmParameterException
254     *             if the parameters cannot be used to initialize this
255     *             mechanism.
256     * @throws ExemptionMechanismException
257     *             if error(s) occur during initialization.
258     */
259    public final void init(Key key, AlgorithmParameters param)
260            throws InvalidKeyException, InvalidAlgorithmParameterException,
261            ExemptionMechanismException {
262        generated = false;
263        spiImpl.engineInit(key, param);
264        initKey = key;
265        isInit = true;
266    }
267
268    /**
269     * Initializes this {@code ExemptionMechanism} instance with the
270     * specified key and algorithm parameters.
271     *
272     * @param key
273     *            the key to initialize this instance with.
274     * @param param
275     *            the parameters for this exemption mechanism algorithm.
276     * @throws InvalidKeyException
277     *             if the key cannot be used to initialize this mechanism.
278     * @throws InvalidAlgorithmParameterException
279     *             the the parameters cannot be used to initialize this
280     *             mechanism.
281     * @throws ExemptionMechanismException
282     *             if error(s) occur during initialization.
283     */
284    public final void init(Key key, AlgorithmParameterSpec param)
285            throws InvalidKeyException, InvalidAlgorithmParameterException,
286            ExemptionMechanismException {
287        generated = false;
288        spiImpl.engineInit(key, param);
289        initKey = key;
290        isInit = true;
291    }
292
293    /**
294     * Generates the result key blob for this exemption mechanism.
295     *
296     * @return the result key blob for this exemption mechanism.
297     * @throws IllegalStateException
298     *             if this {@code ExemptionMechanism} instance is not
299     *             initialized.
300     * @throws ExemptionMechanismException
301     *             if error(s) occur during generation.
302     */
303    public final byte[] genExemptionBlob() throws IllegalStateException,
304            ExemptionMechanismException {
305        if (!isInit) {
306            throw new IllegalStateException("ExemptionMechanism is not initialized");
307        }
308        generated = false;
309        byte[] result = spiImpl.engineGenExemptionBlob();
310        generated = true;
311        return result;
312    }
313
314    /**
315     * Generates the result key blob for this exemption mechanism and stores it
316     * into the {@code output} buffer.
317     *
318     * @param output
319     *            the output buffer for the result key blob.
320     * @return the number of bytes written to the {@code output} buffer.
321     * @throws IllegalStateException
322     *             if this {@code ExemptionMechanism} instance is not
323     *             initialized.
324     * @throws ShortBufferException
325     *             if the provided buffer is too small for the result key blob.
326     * @throws ExemptionMechanismException
327     *             if error(s) occur during generation.
328     */
329    public final int genExemptionBlob(byte[] output)
330            throws IllegalStateException, ShortBufferException,
331            ExemptionMechanismException {
332        return genExemptionBlob(output, 0);
333    }
334
335    /**
336     * Generates the result key blob for this exemption mechanism and stores it
337     * into the {@code output} buffer at offset {@code outputOffset}.
338     *
339     * @param output
340     *            the output buffer for the result key blob.
341     * @param outputOffset
342     *            the offset in the output buffer to start.
343     * @return the number of bytes written to the {@code output} buffer.
344     * @throws IllegalStateException
345     *             if this {@code ExemptionMechanism} instance is not
346     *             initialized.
347     * @throws ShortBufferException
348     *             if the provided buffer is too small for the result key blob.
349     * @throws ExemptionMechanismException
350     *             if error(s) occur during generation.
351     */
352    public final int genExemptionBlob(byte[] output, int outputOffset)
353            throws IllegalStateException, ShortBufferException,
354            ExemptionMechanismException {
355        if (!isInit) {
356            throw new IllegalStateException("ExemptionMechanism is not initialized");
357        }
358        generated = false;
359        int len = spiImpl.engineGenExemptionBlob(output, outputOffset);
360        generated = true;
361        return len;
362    }
363
364    @Override protected void finalize() {
365        try {
366            super.finalize();
367        } catch (Throwable t) {
368            // for consistency with the RI, we must override Object.finalize() to
369            // remove the throws clause.
370            throw new AssertionError(t);
371        }
372    }
373}
374