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     *
146     * @param algorithm
147     *            the name of the requested exemption mechanism.
148     * @param provider
149     *            the provider that is providing the algorithm.
150     * @return the new {@code ExemptionMechanism} instance.
151     * @throws NoSuchAlgorithmException
152     *             if the specified algorithm is not provided by the specified
153     *             provider.
154     * @throws NullPointerException
155     *             if the algorithm parameter is {@code null}.
156     * @throws IllegalArgumentException
157     *             if the provider parameter is {@code null}.
158     */
159    public static final ExemptionMechanism getInstance(String algorithm,
160            Provider provider) throws NoSuchAlgorithmException {
161        if (algorithm == null) {
162            throw new NullPointerException("algorithm == null");
163        }
164        if (provider == null) {
165            throw new IllegalArgumentException("provider == null");
166        }
167        Object spi = ENGINE.getInstance(algorithm, provider, null);
168        return new ExemptionMechanism((ExemptionMechanismSpi) spi, provider, algorithm);
169    }
170
171    /**
172     * Returns the provider of this {@code ExemptionMechanism} instance.
173     *
174     * @return the provider of this {@code ExemptionMechanism} instance.
175     */
176    public final Provider getProvider() {
177        return provider;
178    }
179
180    /**
181     * Returns whether the result blob for this {@code ExemptionMechanism}
182     * instance has been generated successfully and that the specified key is
183     * the same as the one that was used to initialize and generate.
184     *
185     * @param key
186     *            the key to verify.
187     * @return whether the result blob for this {@code ExemptionMechanism}
188     *         instance has been generated successfully.
189     * @throws ExemptionMechanismException
190     *             if an error occurs while determining whether the result blob
191     *             has been generated successfully.
192     */
193    public final boolean isCryptoAllowed(Key key)
194            throws ExemptionMechanismException {
195
196        if (generated
197                && (initKey.equals(key) || Arrays.equals(initKey.getEncoded(),
198                        key.getEncoded()))) {
199            return true;
200        }
201        return false;
202    }
203
204    /**
205     * Returns the size in bytes for the output buffer needed to hold the output
206     * of the next {@link #genExemptionBlob} call, given the specified {@code
207     * inputLen} (in bytes).
208     *
209     * @param inputLen
210     *            the specified input length (in bytes).
211     * @return the size in bytes for the output buffer.
212     * @throws IllegalStateException
213     *             if this {@code ExemptionMechanism} instance is not
214     *             initialized.
215     */
216    public final int getOutputSize(int inputLen) throws IllegalStateException {
217        if (!isInit) {
218            throw new IllegalStateException("ExemptionMechanism is not initialized");
219        }
220        return spiImpl.engineGetOutputSize(inputLen);
221    }
222
223    /**
224     * Initializes this {@code ExemptionMechanism} instance with the
225     * specified key.
226     *
227     * @param key
228     *            the key to initialize this instance with.
229     * @throws InvalidKeyException
230     *             if the key cannot be used to initialize this mechanism.
231     * @throws ExemptionMechanismException
232     *             if error(s) occur during initialization.
233     */
234    public final void init(Key key) throws InvalidKeyException,
235            ExemptionMechanismException {
236        generated = false;
237        spiImpl.engineInit(key);
238        initKey = key;
239        isInit = true;
240    }
241
242    /**
243     * Initializes this {@code ExemptionMechanism} instance with the
244     * specified key and algorithm parameters.
245     *
246     * @param key
247     *            the key to initialize this instance with.
248     * @param param
249     *            the parameters for this exemption mechanism algorithm.
250     * @throws InvalidKeyException
251     *             if the key cannot be used to initialize this mechanism.
252     * @throws InvalidAlgorithmParameterException
253     *             if the parameters cannot be used to initialize this
254     *             mechanism.
255     * @throws ExemptionMechanismException
256     *             if error(s) occur during initialization.
257     */
258    public final void init(Key key, AlgorithmParameters param)
259            throws InvalidKeyException, InvalidAlgorithmParameterException,
260            ExemptionMechanismException {
261        generated = false;
262        spiImpl.engineInit(key, param);
263        initKey = key;
264        isInit = true;
265    }
266
267    /**
268     * Initializes this {@code ExemptionMechanism} instance with the
269     * specified key and algorithm parameters.
270     *
271     * @param key
272     *            the key to initialize this instance with.
273     * @param param
274     *            the parameters for this exemption mechanism algorithm.
275     * @throws InvalidKeyException
276     *             if the key cannot be used to initialize this mechanism.
277     * @throws InvalidAlgorithmParameterException
278     *             the the parameters cannot be used to initialize this
279     *             mechanism.
280     * @throws ExemptionMechanismException
281     *             if error(s) occur during initialization.
282     */
283    public final void init(Key key, AlgorithmParameterSpec param)
284            throws InvalidKeyException, InvalidAlgorithmParameterException,
285            ExemptionMechanismException {
286        generated = false;
287        spiImpl.engineInit(key, param);
288        initKey = key;
289        isInit = true;
290    }
291
292    /**
293     * Generates the result key blob for this exemption mechanism.
294     *
295     * @return the result key blob for this exemption mechanism.
296     * @throws IllegalStateException
297     *             if this {@code ExemptionMechanism} instance is not
298     *             initialized.
299     * @throws ExemptionMechanismException
300     *             if error(s) occur during generation.
301     */
302    public final byte[] genExemptionBlob() throws IllegalStateException,
303            ExemptionMechanismException {
304        if (!isInit) {
305            throw new IllegalStateException("ExemptionMechanism is not initialized");
306        }
307        generated = false;
308        byte[] result = spiImpl.engineGenExemptionBlob();
309        generated = true;
310        return result;
311    }
312
313    /**
314     * Generates the result key blob for this exemption mechanism and stores it
315     * into the {@code output} buffer.
316     *
317     * @param output
318     *            the output buffer for the result key blob.
319     * @return the number of bytes written to the {@code output} buffer.
320     * @throws IllegalStateException
321     *             if this {@code ExemptionMechanism} instance is not
322     *             initialized.
323     * @throws ShortBufferException
324     *             if the provided buffer is too small for the result key blob.
325     * @throws ExemptionMechanismException
326     *             if error(s) occur during generation.
327     */
328    public final int genExemptionBlob(byte[] output)
329            throws IllegalStateException, ShortBufferException,
330            ExemptionMechanismException {
331        return genExemptionBlob(output, 0);
332    }
333
334    /**
335     * Generates the result key blob for this exemption mechanism and stores it
336     * into the {@code output} buffer at offset {@code outputOffset}.
337     *
338     * @param output
339     *            the output buffer for the result key blob.
340     * @param outputOffset
341     *            the offset in the output buffer to start.
342     * @return the number of bytes written to the {@code output} buffer.
343     * @throws IllegalStateException
344     *             if this {@code ExemptionMechanism} instance is not
345     *             initialized.
346     * @throws ShortBufferException
347     *             if the provided buffer is too small for the result key blob.
348     * @throws ExemptionMechanismException
349     *             if error(s) occur during generation.
350     */
351    public final int genExemptionBlob(byte[] output, int outputOffset)
352            throws IllegalStateException, ShortBufferException,
353            ExemptionMechanismException {
354        if (!isInit) {
355            throw new IllegalStateException("ExemptionMechanism is not initialized");
356        }
357        generated = false;
358        int len = spiImpl.engineGenExemptionBlob(output, outputOffset);
359        generated = true;
360        return len;
361    }
362
363    /**
364     * Override to clear any key state in the instance.
365     */
366    @Override protected void finalize() {
367        try {
368            super.finalize();
369        } catch (Throwable t) {
370            // for consistency with the RI, we must override Object.finalize() to
371            // remove the throws clause.
372            throw new AssertionError(t);
373        }
374    }
375}
376