InputRecord.java revision 12745:f068a4ffddd2
1/*
2 * Copyright (c) 1996, 2014, 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.ssl;
27
28import java.io.*;
29import java.nio.*;
30import java.util.*;
31
32import javax.crypto.BadPaddingException;
33
34import javax.net.ssl.*;
35
36import sun.misc.HexDumpEncoder;
37
38
39/**
40 * {@code InputRecord} takes care of the management of SSL/TLS/DTLS input
41 * records, including buffering, decryption, handshake messages marshal, etc.
42 *
43 * @author David Brownell
44 */
45class InputRecord implements Record, Closeable {
46
47    /* Class and subclass dynamic debugging support */
48    static final Debug debug = Debug.getInstance("ssl");
49
50    Authenticator       readAuthenticator;
51    CipherBox           readCipher;
52
53    HandshakeHash       handshakeHash;
54    boolean             isClosed;
55
56    // The ClientHello version to accept. If set to ProtocolVersion.SSL20Hello
57    // and the first message we read is a ClientHello in V2 format, we convert
58    // it to V3. Otherwise we throw an exception when encountering a V2 hello.
59    ProtocolVersion     helloVersion;
60
61    // fragment size
62    int                 fragmentSize;
63
64    InputRecord() {
65        this.readCipher = CipherBox.NULL;
66        this.readAuthenticator = null;      // Please override this assignment.
67        this.helloVersion = ProtocolVersion.DEFAULT_HELLO;
68        this.fragmentSize = Record.maxDataSize;
69    }
70
71    void setHelloVersion(ProtocolVersion helloVersion) {
72        this.helloVersion = helloVersion;
73    }
74
75    ProtocolVersion getHelloVersion() {
76        return helloVersion;
77    }
78
79    /*
80     * Set instance for the computation of handshake hashes.
81     *
82     * For handshaking, we need to be able to hash every byte above the
83     * record marking layer.  This is where we're guaranteed to see those
84     * bytes, so this is where we can hash them ... especially in the
85     * case of hashing the initial V2 message!
86     */
87    void setHandshakeHash(HandshakeHash handshakeHash) {
88        if (handshakeHash != null) {
89            byte[] reserved = null;
90            if (this.handshakeHash != null) {
91                reserved = this.handshakeHash.getAllHandshakeMessages();
92            }
93            if ((reserved != null) && (reserved.length != 0)) {
94                handshakeHash.update(reserved, 0, reserved.length);
95
96               if (debug != null && Debug.isOn("data")) {
97                    Debug.printHex(
98                        "[reserved] handshake hash: len = " + reserved.length,
99                        reserved);
100               }
101            }
102        }
103
104        this.handshakeHash = handshakeHash;
105    }
106
107    boolean seqNumIsHuge() {
108        return (readAuthenticator != null) &&
109                        readAuthenticator.seqNumIsHuge();
110    }
111
112    boolean isEmpty() {
113        return false;
114    }
115
116    // apply to DTLS SSLEngine
117    void expectingFinishFlight() {
118        // blank
119    }
120
121    /**
122     * Prevent any more data from being read into this record,
123     * and flag the record as holding no data.
124     */
125    @Override
126    public synchronized void close() throws IOException {
127        if (!isClosed) {
128            isClosed = true;
129            readCipher.dispose();
130        }
131    }
132
133    // apply to SSLSocket and SSLEngine
134    void changeReadCiphers(
135            Authenticator readAuthenticator, CipherBox readCipher) {
136
137        /*
138         * Dispose of any intermediate state in the underlying cipher.
139         * For PKCS11 ciphers, this will release any attached sessions,
140         * and thus make finalization faster.
141         *
142         * Since MAC's doFinal() is called for every SSL/TLS packet, it's
143         * not necessary to do the same with MAC's.
144         */
145        readCipher.dispose();
146
147        this.readAuthenticator = readAuthenticator;
148        this.readCipher = readCipher;
149    }
150
151    // change fragment size
152    void changeFragmentSize(int fragmentSize) {
153        this.fragmentSize = fragmentSize;
154    }
155
156    /*
157     * Check if there is enough inbound data in the ByteBuffer to make
158     * a inbound packet.
159     *
160     * @return -1 if there are not enough bytes to tell (small header),
161     */
162    // apply to SSLEngine only
163    int bytesInCompletePacket(ByteBuffer buf) throws SSLException {
164        throw new UnsupportedOperationException();
165    }
166
167    // apply to SSLSocket only
168    int bytesInCompletePacket(InputStream is) throws IOException {
169        throw new UnsupportedOperationException();
170    }
171
172    /**
173     * Return true if the specified record protocol version is out of the
174     * range of the possible supported versions.
175     */
176    void checkRecordVersion(ProtocolVersion version,
177            boolean allowSSL20Hello) throws SSLException {
178        // blank
179    }
180
181    // apply to DTLS SSLEngine only
182    Plaintext acquirePlaintext()
183            throws IOException, BadPaddingException {
184        throw new UnsupportedOperationException();
185    }
186
187    // read, decrypt and decompress the network record.
188    //
189    // apply to SSLEngine only
190    Plaintext decode(ByteBuffer netData)
191            throws IOException, BadPaddingException {
192        throw new UnsupportedOperationException();
193    }
194
195    // apply to SSLSocket only
196    Plaintext decode(InputStream is, ByteBuffer destination)
197            throws IOException, BadPaddingException {
198        throw new UnsupportedOperationException();
199    }
200
201    // apply to SSLSocket only
202    void setDeliverStream(OutputStream outputStream) {
203        throw new UnsupportedOperationException();
204    }
205
206    // calculate plaintext fragment size
207    //
208    // apply to SSLEngine only
209    int estimateFragmentSize(int packetSize) {
210        throw new UnsupportedOperationException();
211    }
212
213    //
214    // shared helpers
215    //
216
217    // Not apply to DTLS
218    static ByteBuffer convertToClientHello(ByteBuffer packet) {
219
220        int srcPos = packet.position();
221        int srcLim = packet.limit();
222
223        byte firstByte = packet.get();
224        byte secondByte = packet.get();
225        int recordLen = (((firstByte & 0x7F) << 8) | (secondByte & 0xFF)) + 2;
226
227        packet.position(srcPos + 3);        // the V2ClientHello record header
228
229        byte majorVersion = packet.get();
230        byte minorVersion = packet.get();
231
232        int cipherSpecLen = ((packet.get() & 0xFF) << 8) +
233                             (packet.get() & 0xFF);
234        int sessionIdLen  = ((packet.get() & 0xFF) << 8) +
235                             (packet.get() & 0xFF);
236        int nonceLen      = ((packet.get() & 0xFF) << 8) +
237                             (packet.get() & 0xFF);
238
239        // Required space for the target SSLv3 ClientHello message.
240        //  5: record header size
241        //  4: handshake header size
242        //  2: ClientHello.client_version
243        // 32: ClientHello.random
244        //  1: length byte of ClientHello.session_id
245        //  2: empty ClientHello.compression_methods
246        int requiredSize = 46 + sessionIdLen + ((cipherSpecLen * 2 ) / 3 );
247        byte[] converted = new byte[requiredSize];
248
249        /*
250         * Build the first part of the V3 record header from the V2 one
251         * that's now buffered up.  (Lengths are fixed up later).
252         */
253        // Note: need not to set the header actually.
254        converted[0] = ct_handshake;
255        converted[1] = majorVersion;
256        converted[2] = minorVersion;
257        // header [3..4] for handshake message length
258        // required size is 5;
259
260        /*
261         * Store the generic V3 handshake header:  4 bytes
262         */
263        converted[5] = 1;    // HandshakeMessage.ht_client_hello
264        // buf [6..8] for length of ClientHello (int24)
265        // required size += 4;
266
267        /*
268         * ClientHello header starts with SSL version
269         */
270        converted[9] = majorVersion;
271        converted[10] = minorVersion;
272        // required size += 2;
273        int pointer = 11;
274
275        /*
276         * Copy Random value/nonce ... if less than the 32 bytes of
277         * a V3 "Random", right justify and zero pad to the left.  Else
278         * just take the last 32 bytes.
279         */
280        int offset = srcPos + 11 + cipherSpecLen + sessionIdLen;
281
282        if (nonceLen < 32) {
283            for (int i = 0; i < (32 - nonceLen); i++) {
284                converted[pointer++] = 0;
285            }
286            packet.position(offset);
287            packet.get(converted, pointer, nonceLen);
288
289            pointer += nonceLen;
290        } else {
291            packet.position(offset + nonceLen - 32);
292            packet.get(converted, pointer, 32);
293
294            pointer += 32;
295        }
296
297        /*
298         * Copy session ID (only one byte length!)
299         */
300        offset -= sessionIdLen;
301        converted[pointer++] = (byte)(sessionIdLen & 0xFF);
302        packet.position(offset);
303        packet.get(converted, pointer, sessionIdLen);
304
305        /*
306         * Copy and translate cipher suites ... V2 specs with first byte zero
307         * are really V3 specs (in the last 2 bytes), just copy those and drop
308         * the other ones.  Preference order remains unchanged.
309         *
310         * Example:  Netscape Navigator 3.0 (exportable) says:
311         *
312         * 0/3,     SSL_RSA_EXPORT_WITH_RC4_40_MD5
313         * 0/6,     SSL_RSA_EXPORT_WITH_RC2_CBC_40_MD5
314         *
315         * Microsoft Internet Explorer 3.0 (exportable) supports only
316         *
317         * 0/3,     SSL_RSA_EXPORT_WITH_RC4_40_MD5
318         */
319        int j;
320
321        offset -= cipherSpecLen;
322        packet.position(offset);
323
324        j = pointer + 2;
325        for (int i = 0; i < cipherSpecLen; i += 3) {
326            if (packet.get() != 0) {
327                // Ignore version 2.0 specifix cipher suite.  Clients
328                // should also include the version 3.0 equivalent in
329                // the V2ClientHello message.
330                packet.get();           // ignore the 2nd byte
331                packet.get();           // ignore the 3rd byte
332                continue;
333            }
334
335            converted[j++] = packet.get();
336            converted[j++] = packet.get();
337        }
338
339        j -= pointer + 2;
340        converted[pointer++] = (byte)((j >>> 8) & 0xFF);
341        converted[pointer++] = (byte)(j & 0xFF);
342        pointer += j;
343
344        /*
345         * Append compression methods (default/null only)
346         */
347        converted[pointer++] = 1;
348        converted[pointer++] = 0;      // Session.compression_null
349
350        /*
351         * Fill in lengths of the messages we synthesized (nested:
352         * V3 handshake message within V3 record).
353         */
354        // Note: need not to set the header actually.
355        int fragLen = pointer - 5;                      // TLSPlaintext.length
356        converted[3] = (byte)((fragLen >>> 8) & 0xFF);
357        converted[4] = (byte)(fragLen & 0xFF);
358
359        /*
360         * Handshake.length, length of ClientHello message
361         */
362        fragLen = pointer - 9;                          // Handshake.length
363        converted[6] = (byte)((fragLen >>> 16) & 0xFF);
364        converted[7] = (byte)((fragLen >>> 8) & 0xFF);
365        converted[8] = (byte)(fragLen & 0xFF);
366
367        // consume the full record
368        packet.position(srcPos + recordLen);
369
370        // Need no header bytes.
371        return ByteBuffer.wrap(converted, 5, pointer - 5);  // 5: header size
372    }
373
374    static ByteBuffer decrypt(Authenticator authenticator, CipherBox box,
375            byte contentType, ByteBuffer bb) throws BadPaddingException {
376
377        return decrypt(authenticator, box, contentType, bb, null);
378    }
379
380    static ByteBuffer decrypt(Authenticator authenticator,
381            CipherBox box, byte contentType, ByteBuffer bb,
382            byte[] sequence) throws BadPaddingException {
383
384        BadPaddingException reservedBPE = null;
385        int tagLen =
386            (authenticator instanceof MAC) ? ((MAC)authenticator).MAClen() : 0;
387        int cipheredLength = bb.remaining();
388        int srcPos = bb.position();
389        if (!box.isNullCipher()) {
390            try {
391                // apply explicit nonce for AEAD/CBC cipher suites if needed
392                int nonceSize = box.applyExplicitNonce(
393                        authenticator, contentType, bb, sequence);
394
395                // decrypt the content
396                if (box.isAEADMode()) {
397                    // DON'T decrypt the nonce_explicit for AEAD mode
398                    bb.position(srcPos + nonceSize);
399                }   // The explicit IV for CBC mode can be decrypted.
400
401                // Note that the CipherBox.decrypt() does not change
402                // the capacity of the buffer.
403                box.decrypt(bb, tagLen);
404                // We don't actually remove the nonce.
405                bb.position(srcPos + nonceSize);
406            } catch (BadPaddingException bpe) {
407                // RFC 2246 states that decryption_failed should be used
408                // for this purpose. However, that allows certain attacks,
409                // so we just send bad record MAC. We also need to make
410                // sure to always check the MAC to avoid a timing attack
411                // for the same issue. See paper by Vaudenay et al and the
412                // update in RFC 4346/5246.
413                //
414                // Failover to message authentication code checking.
415                reservedBPE = bpe;
416            }
417        }
418
419        // Requires message authentication code for null, stream and block
420        // cipher suites.
421        if ((authenticator instanceof MAC) && (tagLen != 0)) {
422            MAC signer = (MAC)authenticator;
423            int contentLen = bb.remaining() - tagLen;
424
425            // Note that although it is not necessary, we run the same MAC
426            // computation and comparison on the payload for both stream
427            // cipher and CBC block cipher.
428            if (contentLen < 0) {
429                // negative data length, something is wrong
430                if (reservedBPE == null) {
431                    reservedBPE = new BadPaddingException("bad record");
432                }
433
434                // set offset of the dummy MAC
435                contentLen = cipheredLength - tagLen;
436                bb.limit(srcPos + cipheredLength);
437            }
438
439            // Run MAC computation and comparison on the payload.
440            //
441            // MAC data would be stripped off during the check.
442            if (checkMacTags(contentType, bb, signer, sequence, false)) {
443                if (reservedBPE == null) {
444                    reservedBPE = new BadPaddingException("bad record MAC");
445                }
446            }
447
448            // Run MAC computation and comparison on the remainder.
449            //
450            // It is only necessary for CBC block cipher.  It is used to get a
451            // constant time of MAC computation and comparison on each record.
452            if (box.isCBCMode()) {
453                int remainingLen = calculateRemainingLen(
454                                        signer, cipheredLength, contentLen);
455
456                // NOTE: remainingLen may be bigger (less than 1 block of the
457                // hash algorithm of the MAC) than the cipheredLength.
458                //
459                // Is it possible to use a static buffer, rather than allocate
460                // it dynamically?
461                remainingLen += signer.MAClen();
462                ByteBuffer temporary = ByteBuffer.allocate(remainingLen);
463
464                // Won't need to worry about the result on the remainder. And
465                // then we won't need to worry about what's actual data to
466                // check MAC tag on.  We start the check from the header of the
467                // buffer so that we don't need to construct a new byte buffer.
468                checkMacTags(contentType, temporary, signer, sequence, true);
469            }
470        }
471
472        // Is it a failover?
473        if (reservedBPE != null) {
474            throw reservedBPE;
475        }
476
477        return bb.slice();
478    }
479
480    /*
481     * Run MAC computation and comparison
482     *
483     */
484    private static boolean checkMacTags(byte contentType, ByteBuffer bb,
485            MAC signer, byte[] sequence, boolean isSimulated) {
486
487        int tagLen = signer.MAClen();
488        int position = bb.position();
489        int lim = bb.limit();
490        int macOffset = lim - tagLen;
491
492        bb.limit(macOffset);
493        byte[] hash = signer.compute(contentType, bb, sequence, isSimulated);
494        if (hash == null || tagLen != hash.length) {
495            // Something is wrong with MAC implementation.
496            throw new RuntimeException("Internal MAC error");
497        }
498
499        bb.position(macOffset);
500        bb.limit(lim);
501        try {
502            int[] results = compareMacTags(bb, hash);
503            return (results[0] != 0);
504        } finally {
505            // reset to the data
506            bb.position(position);
507            bb.limit(macOffset);
508        }
509    }
510
511    /*
512     * A constant-time comparison of the MAC tags.
513     *
514     * Please DON'T change the content of the ByteBuffer parameter!
515     */
516    private static int[] compareMacTags(ByteBuffer bb, byte[] tag) {
517
518        // An array of hits is used to prevent Hotspot optimization for
519        // the purpose of a constant-time check.
520        int[] results = {0, 0};     // {missed #, matched #}
521
522        // The caller ensures there are enough bytes available in the buffer.
523        // So we won't need to check the remaining of the buffer.
524        for (int i = 0; i < tag.length; i++) {
525            if (bb.get() != tag[i]) {
526                results[0]++;       // mismatched bytes
527            } else {
528                results[1]++;       // matched bytes
529            }
530        }
531
532        return results;
533    }
534
535    /*
536     * Run MAC computation and comparison
537     *
538     * Please DON'T change the content of the byte buffer parameter!
539     */
540    private static boolean checkMacTags(byte contentType, byte[] buffer,
541            int offset, int contentLen, MAC signer, boolean isSimulated) {
542
543        int tagLen = signer.MAClen();
544        byte[] hash = signer.compute(
545                contentType, buffer, offset, contentLen, isSimulated);
546        if (hash == null || tagLen != hash.length) {
547            // Something is wrong with MAC implementation.
548            throw new RuntimeException("Internal MAC error");
549        }
550
551        int[] results = compareMacTags(buffer, offset + contentLen, hash);
552        return (results[0] != 0);
553    }
554
555    /*
556     * A constant-time comparison of the MAC tags.
557     *
558     * Please DON'T change the content of the byte buffer parameter!
559     */
560    private static int[] compareMacTags(
561            byte[] buffer, int offset, byte[] tag) {
562
563        // An array of hits is used to prevent Hotspot optimization for
564        // the purpose of a constant-time check.
565        int[] results = {0, 0};    // {missed #, matched #}
566
567        // The caller ensures there are enough bytes available in the buffer.
568        // So we won't need to check the length of the buffer.
569        for (int i = 0; i < tag.length; i++) {
570            if (buffer[offset + i] != tag[i]) {
571                results[0]++;       // mismatched bytes
572            } else {
573                results[1]++;       // matched bytes
574            }
575        }
576
577        return results;
578    }
579
580    /*
581     * Calculate the length of a dummy buffer to run MAC computation
582     * and comparison on the remainder.
583     *
584     * The caller MUST ensure that the fullLen is not less than usedLen.
585     */
586    private static int calculateRemainingLen(
587            MAC signer, int fullLen, int usedLen) {
588
589        int blockLen = signer.hashBlockLen();
590        int minimalPaddingLen = signer.minimalPaddingLen();
591
592        // (blockLen - minimalPaddingLen) is the maximum message size of
593        // the last block of hash function operation. See FIPS 180-4, or
594        // MD5 specification.
595        fullLen += 13 - (blockLen - minimalPaddingLen);
596        usedLen += 13 - (blockLen - minimalPaddingLen);
597
598        // Note: fullLen is always not less than usedLen, and blockLen
599        // is always bigger than minimalPaddingLen, so we don't worry
600        // about negative values. 0x01 is added to the result to ensure
601        // that the return value is positive.  The extra one byte does
602        // not impact the overall MAC compression function evaluations.
603        return 0x01 + (int)(Math.ceil(fullLen/(1.0d * blockLen)) -
604                Math.ceil(usedLen/(1.0d * blockLen))) * blockLen;
605    }
606}
607
608