1/*
2 * Copyright (c) 2005, 2009, 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.nio.ByteBuffer;
29
30import javax.crypto.MacSpi;
31import javax.crypto.SecretKey;
32import java.security.*;
33import java.security.spec.AlgorithmParameterSpec;
34
35import static com.sun.crypto.provider.TlsPrfGenerator.genPad;
36
37/**
38 * This file contains the code for the SslMacMD5 and SslMacSHA1 implementations.
39 * The SSL 3.0 MAC is a variation of the HMAC algorithm.
40 *
41 * Note that we don't implement Cloneable as that is not needed for SSL.
42 *
43 * @author  Andreas Sterbenz
44 * @since   1.6
45 */
46final class SslMacCore {
47
48    private final MessageDigest md;
49    private final byte[] pad1, pad2;
50
51    private boolean first;       // Is this the first data to be processed?
52    private byte[] secret;
53
54    /**
55     * Standard constructor, creates a new SslMacCore instance instantiating
56     * a MessageDigest of the specified name.
57     */
58    SslMacCore(String digestAlgorithm, byte[] pad1, byte[] pad2)
59            throws NoSuchAlgorithmException {
60        md = MessageDigest.getInstance(digestAlgorithm);
61        this.pad1 = pad1;
62        this.pad2 = pad2;
63        first = true;
64    }
65
66    /**
67     * Returns the length of the Mac in bytes.
68     *
69     * @return the Mac length in bytes.
70     */
71    int getDigestLength() {
72        return md.getDigestLength();
73    }
74
75    /**
76     * Initializes the Mac with the given secret key and algorithm parameters.
77     *
78     * @param key the secret key.
79     * @param params the algorithm parameters.
80     *
81     * @exception InvalidKeyException if the given key is inappropriate for
82     * initializing this MAC.
83     * @exception InvalidAlgorithmParameterException if the given algorithm
84     * parameters are inappropriate for this MAC.
85     */
86    void init(Key key, AlgorithmParameterSpec params)
87            throws InvalidKeyException, InvalidAlgorithmParameterException {
88
89        if (params != null) {
90            throw new InvalidAlgorithmParameterException
91                ("SslMac does not use parameters");
92        }
93
94        if (!(key instanceof SecretKey)) {
95            throw new InvalidKeyException("Secret key expected");
96        }
97
98        secret = key.getEncoded();
99        if (secret == null || secret.length == 0) {
100            throw new InvalidKeyException("Missing key data");
101        }
102
103        reset();
104    }
105
106    /**
107     * Processes the given byte.
108     *
109     * @param input the input byte to be processed.
110     */
111    void update(byte input) {
112        if (first == true) {
113            // compute digest for 1st pass; start with inner pad
114            md.update(secret);
115            md.update(pad1);
116            first = false;
117        }
118
119        // add the passed byte to the inner digest
120        md.update(input);
121    }
122
123    /**
124     * Processes the first <code>len</code> bytes in <code>input</code>,
125     * starting at <code>offset</code>.
126     *
127     * @param input the input buffer.
128     * @param offset the offset in <code>input</code> where the input starts.
129     * @param len the number of bytes to process.
130     */
131    void update(byte input[], int offset, int len) {
132        if (first == true) {
133            // compute digest for 1st pass; start with inner pad
134            md.update(secret);
135            md.update(pad1);
136            first = false;
137        }
138
139        // add the selected part of an array of bytes to the inner digest
140        md.update(input, offset, len);
141    }
142
143    void update(ByteBuffer input) {
144        if (first == true) {
145            // compute digest for 1st pass; start with inner pad
146            md.update(secret);
147            md.update(pad1);
148            first = false;
149        }
150
151        md.update(input);
152    }
153
154    /**
155     * Completes the Mac computation and resets the Mac for further use,
156     * maintaining the secret key that the Mac was initialized with.
157     *
158     * @return the Mac result.
159     */
160    byte[] doFinal() {
161        if (first == true) {
162            // compute digest for 1st pass; start with inner pad
163            md.update(secret);
164            md.update(pad1);
165        } else {
166            first = true;
167        }
168
169        try {
170            // finish the inner digest
171            byte[] tmp = md.digest();
172
173            // compute digest for 2nd pass; start with outer pad
174            md.update(secret);
175            md.update(pad2);
176            // add result of 1st hash
177            md.update(tmp);
178
179            md.digest(tmp, 0, tmp.length);
180            return tmp;
181        } catch (DigestException e) {
182            // should never occur
183            throw new ProviderException(e);
184        }
185    }
186
187    /**
188     * Resets the Mac for further use, maintaining the secret key that the
189     * Mac was initialized with.
190     */
191    void reset() {
192        if (first == false) {
193            md.reset();
194            first = true;
195        }
196    }
197
198    // nested static class for the SslMacMD5 implementation
199    public static final class SslMacMD5 extends MacSpi {
200        private final SslMacCore core;
201        public SslMacMD5() throws NoSuchAlgorithmException {
202            core = new SslMacCore("MD5", md5Pad1, md5Pad2);
203        }
204        protected int engineGetMacLength() {
205            return core.getDigestLength();
206        }
207        protected void engineInit(Key key, AlgorithmParameterSpec params)
208                throws InvalidKeyException, InvalidAlgorithmParameterException {
209            core.init(key, params);
210        }
211        protected void engineUpdate(byte input) {
212            core.update(input);
213        }
214        protected void engineUpdate(byte input[], int offset, int len) {
215            core.update(input, offset, len);
216        }
217        protected void engineUpdate(ByteBuffer input) {
218            core.update(input);
219        }
220        protected byte[] engineDoFinal() {
221            return core.doFinal();
222        }
223        protected void engineReset() {
224            core.reset();
225        }
226
227        static final byte[] md5Pad1 = genPad((byte)0x36, 48);
228        static final byte[] md5Pad2 = genPad((byte)0x5c, 48);
229    }
230
231    // nested static class for the SslMacMD5 implementation
232    public static final class SslMacSHA1 extends MacSpi {
233        private final SslMacCore core;
234        public SslMacSHA1() throws NoSuchAlgorithmException {
235            core = new SslMacCore("SHA", shaPad1, shaPad2);
236        }
237        protected int engineGetMacLength() {
238            return core.getDigestLength();
239        }
240        protected void engineInit(Key key, AlgorithmParameterSpec params)
241                throws InvalidKeyException, InvalidAlgorithmParameterException {
242            core.init(key, params);
243        }
244        protected void engineUpdate(byte input) {
245            core.update(input);
246        }
247        protected void engineUpdate(byte input[], int offset, int len) {
248            core.update(input, offset, len);
249        }
250        protected void engineUpdate(ByteBuffer input) {
251            core.update(input);
252        }
253        protected byte[] engineDoFinal() {
254            return core.doFinal();
255        }
256        protected void engineReset() {
257            core.reset();
258        }
259
260        static final byte[] shaPad1 = genPad((byte)0x36, 40);
261        static final byte[] shaPad2 = genPad((byte)0x5c, 40);
262    }
263
264}
265