1/*
2 * Copyright (c) 2003, 2008, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.  Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26package sun.security.pkcs11;
27
28import java.security.*;
29import java.security.spec.AlgorithmParameterSpec;
30
31import javax.crypto.*;
32
33import static sun.security.pkcs11.TemplateManager.*;
34import sun.security.pkcs11.wrapper.*;
35import static sun.security.pkcs11.wrapper.PKCS11Constants.*;
36
37/**
38 * KeyGenerator implementation class. This class currently supports
39 * DES, DESede, AES, ARCFOUR, and Blowfish.
40 *
41 * @author  Andreas Sterbenz
42 * @since   1.5
43 */
44final class P11KeyGenerator extends KeyGeneratorSpi {
45
46    // token instance
47    private final Token token;
48
49    // algorithm name
50    private final String algorithm;
51
52    // mechanism id
53    private long mechanism;
54
55    // raw key size in bits, e.g. 64 for DES. Always valid.
56    private int keySize;
57
58    // bits of entropy in the key, e.g. 56 for DES. Always valid.
59    private int significantKeySize;
60
61    // keyType (CKK_*), needed for TemplateManager call only.
62    private long keyType;
63
64    // for determining if both 112 and 168 bits of DESede key lengths
65    // are supported.
66    private boolean supportBothKeySizes;
67
68    /**
69     * Utility method for checking if the specified key size is valid
70     * and within the supported range. Return the significant key size
71     * upon successful validation.
72     * @param keyGenMech the PKCS#11 key generation mechanism.
73     * @param keySize the to-be-checked key size for this mechanism.
74     * @param token token which provides this mechanism.
75     * @return the significant key size (in bits) corresponding to the
76     * specified key size.
77     * @throws InvalidParameterException if the specified key size is invalid.
78     * @throws ProviderException if this mechanism isn't supported by SunPKCS11
79     * or underlying native impl.
80     */
81    static int checkKeySize(long keyGenMech, int keySize, Token token)
82        throws InvalidAlgorithmParameterException, ProviderException {
83        int sigKeySize;
84        switch ((int)keyGenMech) {
85            case (int)CKM_DES_KEY_GEN:
86                if ((keySize != 64) && (keySize != 56)) {
87                    throw new InvalidAlgorithmParameterException
88                            ("DES key length must be 56 bits");
89                }
90                sigKeySize = 56;
91                break;
92            case (int)CKM_DES2_KEY_GEN:
93            case (int)CKM_DES3_KEY_GEN:
94                if ((keySize == 112) || (keySize == 128)) {
95                    sigKeySize = 112;
96                } else if ((keySize == 168) || (keySize == 192)) {
97                    sigKeySize = 168;
98                } else {
99                    throw new InvalidAlgorithmParameterException
100                            ("DESede key length must be 112, or 168 bits");
101                }
102                break;
103            default:
104                // Handle all variable-key-length algorithms here
105                CK_MECHANISM_INFO info = null;
106                try {
107                    info = token.getMechanismInfo(keyGenMech);
108                } catch (PKCS11Exception p11e) {
109                    // Should never happen
110                    throw new ProviderException
111                            ("Cannot retrieve mechanism info", p11e);
112                }
113                if (info == null) {
114                    // XXX Unable to retrieve the supported key length from
115                    // the underlying native impl. Skip the checking for now.
116                    return keySize;
117                }
118                // PKCS#11 defines these to be in number of bytes except for
119                // RC4 which is in bits. However, some PKCS#11 impls still use
120                // bytes for all mechs, e.g. NSS. We try to detect this
121                // inconsistency if the minKeySize seems unreasonably small.
122                int minKeySize = (int)info.ulMinKeySize;
123                int maxKeySize = (int)info.ulMaxKeySize;
124                if (keyGenMech != CKM_RC4_KEY_GEN || minKeySize < 8) {
125                    minKeySize = (int)info.ulMinKeySize << 3;
126                    maxKeySize = (int)info.ulMaxKeySize << 3;
127                }
128                // Explicitly disallow keys shorter than 40-bits for security
129                if (minKeySize < 40) minKeySize = 40;
130                if (keySize < minKeySize || keySize > maxKeySize) {
131                    throw new InvalidAlgorithmParameterException
132                            ("Key length must be between " + minKeySize +
133                            " and " + maxKeySize + " bits");
134                }
135                if (keyGenMech == CKM_AES_KEY_GEN) {
136                    if ((keySize != 128) && (keySize != 192) &&
137                        (keySize != 256)) {
138                        throw new InvalidAlgorithmParameterException
139                                ("AES key length must be " + minKeySize +
140                                (maxKeySize >= 192? ", 192":"") +
141                                (maxKeySize >= 256? ", or 256":"") + " bits");
142                    }
143                }
144                sigKeySize = keySize;
145        }
146        return sigKeySize;
147    }
148
149    P11KeyGenerator(Token token, String algorithm, long mechanism)
150            throws PKCS11Exception {
151        super();
152        this.token = token;
153        this.algorithm = algorithm;
154        this.mechanism = mechanism;
155
156        if (this.mechanism == CKM_DES3_KEY_GEN) {
157            /* Given the current lookup order specified in SunPKCS11.java,
158               if CKM_DES2_KEY_GEN is used to construct this object, it
159               means that CKM_DES3_KEY_GEN is disabled or unsupported.
160            */
161            supportBothKeySizes =
162                (token.provider.config.isEnabled(CKM_DES2_KEY_GEN) &&
163                 (token.getMechanismInfo(CKM_DES2_KEY_GEN) != null));
164        }
165        setDefaultKeySize();
166    }
167
168    // set default keysize and also initialize keyType
169    private void setDefaultKeySize() {
170        switch ((int)mechanism) {
171        case (int)CKM_DES_KEY_GEN:
172            keySize = 64;
173            keyType = CKK_DES;
174            break;
175        case (int)CKM_DES2_KEY_GEN:
176            keySize = 128;
177            keyType = CKK_DES2;
178            break;
179        case (int)CKM_DES3_KEY_GEN:
180            keySize = 192;
181            keyType = CKK_DES3;
182            break;
183        case (int)CKM_AES_KEY_GEN:
184            keySize = 128;
185            keyType = CKK_AES;
186            break;
187        case (int)CKM_RC4_KEY_GEN:
188            keySize = 128;
189            keyType = CKK_RC4;
190            break;
191        case (int)CKM_BLOWFISH_KEY_GEN:
192            keySize = 128;
193            keyType = CKK_BLOWFISH;
194            break;
195        default:
196            throw new ProviderException("Unknown mechanism " + mechanism);
197        }
198        try {
199            significantKeySize = checkKeySize(mechanism, keySize, token);
200        } catch (InvalidAlgorithmParameterException iape) {
201            throw new ProviderException("Unsupported default key size", iape);
202        }
203    }
204
205    // see JCE spec
206    protected void engineInit(SecureRandom random) {
207        token.ensureValid();
208        setDefaultKeySize();
209    }
210
211    // see JCE spec
212    protected void engineInit(AlgorithmParameterSpec params,
213            SecureRandom random) throws InvalidAlgorithmParameterException {
214        throw new InvalidAlgorithmParameterException
215                ("AlgorithmParameterSpec not supported");
216    }
217
218    // see JCE spec
219    protected void engineInit(int keySize, SecureRandom random) {
220        token.ensureValid();
221        int newSignificantKeySize;
222        try {
223            newSignificantKeySize = checkKeySize(mechanism, keySize, token);
224        } catch (InvalidAlgorithmParameterException iape) {
225            throw (InvalidParameterException)
226                    (new InvalidParameterException().initCause(iape));
227        }
228        if ((mechanism == CKM_DES2_KEY_GEN) ||
229            (mechanism == CKM_DES3_KEY_GEN))  {
230            long newMechanism = (newSignificantKeySize == 112 ?
231                CKM_DES2_KEY_GEN : CKM_DES3_KEY_GEN);
232            if (mechanism != newMechanism) {
233                if (supportBothKeySizes) {
234                    mechanism = newMechanism;
235                    // Adjust keyType to reflect the mechanism change
236                    keyType = (mechanism == CKM_DES2_KEY_GEN ?
237                        CKK_DES2 : CKK_DES3);
238                } else {
239                    throw new InvalidParameterException
240                            ("Only " + significantKeySize +
241                             "-bit DESede is supported");
242                }
243            }
244        }
245        this.keySize = keySize;
246        this.significantKeySize = newSignificantKeySize;
247    }
248
249    // see JCE spec
250    protected SecretKey engineGenerateKey() {
251        Session session = null;
252        try {
253            session = token.getObjSession();
254            CK_ATTRIBUTE[] attributes;
255            switch ((int)keyType) {
256            case (int)CKK_DES:
257            case (int)CKK_DES2:
258            case (int)CKK_DES3:
259                // fixed length, do not specify CKA_VALUE_LEN
260                attributes = new CK_ATTRIBUTE[] {
261                    new CK_ATTRIBUTE(CKA_CLASS, CKO_SECRET_KEY),
262                };
263                break;
264            default:
265                attributes = new CK_ATTRIBUTE[] {
266                    new CK_ATTRIBUTE(CKA_CLASS, CKO_SECRET_KEY),
267                    new CK_ATTRIBUTE(CKA_VALUE_LEN, keySize >> 3),
268                };
269                break;
270            }
271            attributes = token.getAttributes
272                (O_GENERATE, CKO_SECRET_KEY, keyType, attributes);
273            long keyID = token.p11.C_GenerateKey
274                (session.id(), new CK_MECHANISM(mechanism), attributes);
275            return P11Key.secretKey
276                (session, keyID, algorithm, significantKeySize, attributes);
277        } catch (PKCS11Exception e) {
278            throw new ProviderException("Could not generate key", e);
279        } finally {
280            token.releaseSession(session);
281        }
282    }
283
284}
285