1/*
2 * Copyright (c) 2003, 2013, 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 com.sun.crypto.provider;
27
28import java.util.Arrays;
29import java.nio.ByteBuffer;
30
31import javax.crypto.MacSpi;
32import javax.crypto.SecretKey;
33import javax.crypto.spec.SecretKeySpec;
34import javax.crypto.spec.PBEKeySpec;
35import javax.crypto.spec.PBEParameterSpec;
36import java.security.*;
37import java.security.spec.*;
38
39/**
40 * This is an implementation of the PBMAC1 algorithms as defined
41 * in PKCS#5 v2.1 standard.
42 */
43abstract class PBMAC1Core extends HmacCore {
44
45    // NOTE: this class inherits the Cloneable interface from HmacCore
46    // Need to override clone() if mutable fields are added.
47    private final String kdfAlgo;
48    private final String hashAlgo;
49    private final int blockLength; // in octets
50
51    /**
52     * Creates an instance of PBMAC1 according to the selected
53     * password-based key derivation function.
54     */
55    PBMAC1Core(String kdfAlgo, String hashAlgo, int blockLength)
56        throws NoSuchAlgorithmException {
57        super(hashAlgo, blockLength);
58        this.kdfAlgo = kdfAlgo;
59        this.hashAlgo = hashAlgo;
60        this.blockLength = blockLength;
61    }
62
63    private static PBKDF2Core getKDFImpl(String algo) {
64        PBKDF2Core kdf = null;
65        switch(algo) {
66        case "HmacSHA1":
67                kdf = new PBKDF2Core.HmacSHA1();
68                break;
69        case "HmacSHA224":
70                kdf = new PBKDF2Core.HmacSHA224();
71                break;
72        case "HmacSHA256":
73                kdf = new PBKDF2Core.HmacSHA256();
74                break;
75        case "HmacSHA384":
76                kdf = new PBKDF2Core.HmacSHA384();
77                break;
78        case "HmacSHA512":
79                kdf = new PBKDF2Core.HmacSHA512();
80                break;
81        default:
82                throw new ProviderException(
83                    "No MAC implementation for " + algo);
84        }
85        return kdf;
86    }
87
88    /**
89     * Initializes the HMAC with the given secret key and algorithm parameters.
90     *
91     * @param key the secret key.
92     * @param params the algorithm parameters.
93     *
94     * @exception InvalidKeyException if the given key is inappropriate for
95     * initializing this MAC.
96     * @exception InvalidAlgorithmParameterException if the given algorithm
97     * parameters are inappropriate for this MAC.
98     */
99    protected void engineInit(Key key, AlgorithmParameterSpec params)
100        throws InvalidKeyException, InvalidAlgorithmParameterException {
101        char[] passwdChars;
102        byte[] salt = null;
103        int iCount = 0;
104        if (key instanceof javax.crypto.interfaces.PBEKey) {
105            javax.crypto.interfaces.PBEKey pbeKey =
106                (javax.crypto.interfaces.PBEKey) key;
107            passwdChars = pbeKey.getPassword();
108            salt = pbeKey.getSalt(); // maybe null if unspecified
109            iCount = pbeKey.getIterationCount(); // maybe 0 if unspecified
110        } else if (key instanceof SecretKey) {
111            byte[] passwdBytes = key.getEncoded();
112            if ((passwdBytes == null) ||
113                !(key.getAlgorithm().regionMatches(true, 0, "PBE", 0, 3))) {
114                throw new InvalidKeyException("Missing password");
115            }
116            passwdChars = new char[passwdBytes.length];
117            for (int i=0; i<passwdChars.length; i++) {
118                passwdChars[i] = (char) (passwdBytes[i] & 0x7f);
119            }
120        } else {
121            throw new InvalidKeyException("SecretKey of PBE type required");
122        }
123        if (params == null) {
124            // should not auto-generate default values since current
125            // javax.crypto.Mac api does not have any method for caller to
126            // retrieve the generated defaults.
127            if ((salt == null) || (iCount == 0)) {
128                throw new InvalidAlgorithmParameterException
129                    ("PBEParameterSpec required for salt and iteration count");
130            }
131        } else if (!(params instanceof PBEParameterSpec)) {
132            throw new InvalidAlgorithmParameterException
133                ("PBEParameterSpec type required");
134        } else {
135            PBEParameterSpec pbeParams = (PBEParameterSpec) params;
136            // make sure the parameter values are consistent
137            if (salt != null) {
138                if (!Arrays.equals(salt, pbeParams.getSalt())) {
139                    throw new InvalidAlgorithmParameterException
140                        ("Inconsistent value of salt between key and params");
141                }
142            } else {
143                salt = pbeParams.getSalt();
144            }
145            if (iCount != 0) {
146                if (iCount != pbeParams.getIterationCount()) {
147                    throw new InvalidAlgorithmParameterException
148                        ("Different iteration count between key and params");
149                }
150            } else {
151                iCount = pbeParams.getIterationCount();
152            }
153        }
154        // For security purpose, we need to enforce a minimum length
155        // for salt; just require the minimum salt length to be 8-byte
156        // which is what PKCS#5 recommends and openssl does.
157        if (salt.length < 8) {
158            throw new InvalidAlgorithmParameterException
159                ("Salt must be at least 8 bytes long");
160        }
161        if (iCount <= 0) {
162            throw new InvalidAlgorithmParameterException
163                ("IterationCount must be a positive number");
164        }
165
166        PBEKeySpec pbeSpec =
167            new PBEKeySpec(passwdChars, salt, iCount, blockLength);
168            // password char[] was cloned in PBEKeySpec constructor,
169            // so we can zero it out here
170        java.util.Arrays.fill(passwdChars, ' ');
171
172        SecretKey s = null;
173        PBKDF2Core kdf = getKDFImpl(kdfAlgo);
174        try {
175            s = kdf.engineGenerateSecret(pbeSpec);
176
177        } catch (InvalidKeySpecException ikse) {
178            InvalidKeyException ike =
179                new InvalidKeyException("Cannot construct PBE key");
180            ike.initCause(ikse);
181            throw ike;
182        }
183        byte[] derivedKey = s.getEncoded();
184        SecretKey cipherKey = new SecretKeySpec(derivedKey, kdfAlgo);
185
186        super.engineInit(cipherKey, null);
187    }
188
189    public static final class HmacSHA1 extends PBMAC1Core {
190        public HmacSHA1() throws NoSuchAlgorithmException {
191            super("HmacSHA1", "SHA1", 64);
192        }
193    }
194
195    public static final class HmacSHA224 extends PBMAC1Core {
196        public HmacSHA224() throws NoSuchAlgorithmException {
197            super("HmacSHA224", "SHA-224", 64);
198        }
199    }
200
201    public static final class HmacSHA256 extends PBMAC1Core {
202        public HmacSHA256() throws NoSuchAlgorithmException {
203            super("HmacSHA256", "SHA-256", 64);
204        }
205    }
206
207    public static final class HmacSHA384 extends PBMAC1Core {
208        public HmacSHA384() throws NoSuchAlgorithmException {
209            super("HmacSHA384", "SHA-384", 128);
210        }
211    }
212
213    public static final class HmacSHA512 extends PBMAC1Core {
214        public HmacSHA512() throws NoSuchAlgorithmException {
215            super("HmacSHA512", "SHA-512", 128);
216        }
217    }
218}
219