1 /*
2  * Copyright (c) 1996, 2019, 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 
26 package sun.security.ssl;
27 
28 import java.io.ByteArrayOutputStream;
29 import java.io.Closeable;
30 import java.io.IOException;
31 import java.io.OutputStream;
32 import java.nio.ByteBuffer;
33 import java.util.concurrent.locks.ReentrantLock;
34 import sun.security.ssl.SSLCipher.SSLWriteCipher;
35 
36 /**
37  * {@code OutputRecord} takes care of the management of SSL/(D)TLS
38  * output records, including buffering, encryption, handshake
39  * messages marshal, etc.
40  *
41  * @author David Brownell
42  */
43 abstract class OutputRecord
44         extends ByteArrayOutputStream implements Record, Closeable {
45     SSLWriteCipher              writeCipher;
46     // Needed for KeyUpdate, used after Handshake.Finished
47     TransportContext            tc;
48 
49     final HandshakeHash         handshakeHash;
50     boolean                     firstMessage;
51 
52     // current protocol version, sent as record version
53     ProtocolVersion             protocolVersion;
54 
55     // version for the ClientHello message. Only relevant if this is a
56     // client handshake record. If set to ProtocolVersion.SSL20Hello,
57     // the V3 client hello is converted to V2 format.
58     ProtocolVersion             helloVersion;
59 
60     // Is it the first application record to write?
61     boolean                     isFirstAppOutputRecord = true;
62 
63     // packet size
64     int                         packetSize;
65 
66     // fragment size
67     private int                 fragmentSize;
68 
69     // closed or not?
70     volatile boolean            isClosed;
71 
72     final ReentrantLock recordLock = new ReentrantLock();
73 
74     /*
75      * Mappings from V3 cipher suite encodings to their pure V2 equivalents.
76      * This is taken from the SSL V3 specification, Appendix E.
77      */
78     private static final int[] V3toV2CipherMap1 =
79         {-1, -1, -1, 0x02, 0x01, -1, 0x04, 0x05, -1, 0x06, 0x07};
80     private static final int[] V3toV2CipherMap3 =
81         {-1, -1, -1, 0x80, 0x80, -1, 0x80, 0x80, -1, 0x40, 0xC0};
82     private static final byte[] HANDSHAKE_MESSAGE_KEY_UPDATE =
83         {SSLHandshake.KEY_UPDATE.id, 0x00, 0x00, 0x01, 0x00};
84 
OutputRecord(HandshakeHash handshakeHash, SSLWriteCipher writeCipher)85     OutputRecord(HandshakeHash handshakeHash, SSLWriteCipher writeCipher) {
86         this.writeCipher = writeCipher;
87         this.firstMessage = true;
88         this.fragmentSize = Record.maxDataSize;
89 
90         this.handshakeHash = handshakeHash;
91 
92         // Please set packetSize and protocolVersion in the implementation.
93     }
94 
setVersion(ProtocolVersion protocolVersion)95     void setVersion(ProtocolVersion protocolVersion) {
96         recordLock.lock();
97         try {
98             this.protocolVersion = protocolVersion;
99         } finally {
100             recordLock.unlock();
101         }
102     }
103 
104     /*
105      * Updates helloVersion of this record.
106      */
setHelloVersion(ProtocolVersion helloVersion)107     void setHelloVersion(ProtocolVersion helloVersion) {
108         recordLock.lock();
109         try {
110             this.helloVersion = helloVersion;
111         } finally {
112             recordLock.unlock();
113         }
114     }
115 
116     /*
117      * Return true iff the record is empty -- to avoid doing the work
118      * of sending empty records over the network.
119      */
isEmpty()120     boolean isEmpty() {
121         return false;
122     }
123 
seqNumIsHuge()124     boolean seqNumIsHuge() {
125         recordLock.lock();
126         try {
127             return (writeCipher.authenticator != null) &&
128                         writeCipher.authenticator.seqNumIsHuge();
129         } finally {
130             recordLock.unlock();
131         }
132     }
133 
134     // SSLEngine and SSLSocket
encodeAlert(byte level, byte description)135     abstract void encodeAlert(byte level, byte description) throws IOException;
136 
137     // SSLEngine and SSLSocket
encodeHandshake(byte[] buffer, int offset, int length)138     abstract void encodeHandshake(byte[] buffer,
139             int offset, int length) throws IOException;
140 
141     // SSLEngine and SSLSocket
encodeChangeCipherSpec()142     abstract void encodeChangeCipherSpec() throws IOException;
143 
144     // apply to SSLEngine only
encode( ByteBuffer[] srcs, int srcsOffset, int srcsLength, ByteBuffer[] dsts, int dstsOffset, int dstsLength)145     Ciphertext encode(
146         ByteBuffer[] srcs, int srcsOffset, int srcsLength,
147         ByteBuffer[] dsts, int dstsOffset, int dstsLength) throws IOException {
148 
149         throw new UnsupportedOperationException();
150     }
151 
152     // apply to SSLEngine only
encodeV2NoCipher()153     void encodeV2NoCipher() throws IOException {
154         throw new UnsupportedOperationException();
155     }
156 
157     // apply to SSLSocket only
deliver( byte[] source, int offset, int length)158     void deliver(
159             byte[] source, int offset, int length) throws IOException {
160         throw new UnsupportedOperationException();
161     }
162 
163     // apply to SSLSocket only
setDeliverStream(OutputStream outputStream)164     void setDeliverStream(OutputStream outputStream) {
165         throw new UnsupportedOperationException();
166     }
167 
168     // Change write ciphers, may use change_cipher_spec record.
changeWriteCiphers(SSLWriteCipher writeCipher, boolean useChangeCipherSpec)169     void changeWriteCiphers(SSLWriteCipher writeCipher,
170             boolean useChangeCipherSpec) throws IOException {
171         recordLock.lock();
172         try {
173             if (isClosed()) {
174                 if (SSLLogger.isOn && SSLLogger.isOn("ssl")) {
175                     SSLLogger.warning("outbound has closed, ignore outbound " +
176                         "change_cipher_spec message");
177                 }
178                 return;
179             }
180 
181             if (useChangeCipherSpec) {
182                 encodeChangeCipherSpec();
183             }
184 
185             /*
186              * Dispose of any intermediate state in the underlying cipher.
187              * For PKCS11 ciphers, this will release any attached sessions,
188              * and thus make finalization faster.
189              *
190              * Since MAC's doFinal() is called for every SSL/TLS packet, it's
191              * not necessary to do the same with MAC's.
192              */
193             writeCipher.dispose();
194 
195             this.writeCipher = writeCipher;
196             this.isFirstAppOutputRecord = true;
197         } finally {
198             recordLock.unlock();
199         }
200     }
201 
202     // Change write ciphers using key_update handshake message.
changeWriteCiphers(SSLWriteCipher writeCipher, byte keyUpdateRequest)203     void changeWriteCiphers(SSLWriteCipher writeCipher,
204             byte keyUpdateRequest) throws IOException {
205         recordLock.lock();
206         try {
207             if (isClosed()) {
208                 if (SSLLogger.isOn && SSLLogger.isOn("ssl")) {
209                     SSLLogger.warning("outbound has closed, ignore outbound " +
210                         "key_update handshake message");
211                 }
212                 return;
213             }
214 
215             // encode the handshake message, KeyUpdate
216             byte[] hm = HANDSHAKE_MESSAGE_KEY_UPDATE.clone();
217             hm[hm.length - 1] = keyUpdateRequest;
218             encodeHandshake(hm, 0, hm.length);
219             flush();
220 
221             // Dispose of any intermediate state in the underlying cipher.
222             writeCipher.dispose();
223 
224             this.writeCipher = writeCipher;
225             this.isFirstAppOutputRecord = true;
226         } finally {
227             recordLock.unlock();
228         }
229     }
230 
changePacketSize(int packetSize)231     void changePacketSize(int packetSize) {
232         recordLock.lock();
233         try {
234             this.packetSize = packetSize;
235         } finally {
236             recordLock.unlock();
237         }
238     }
239 
changeFragmentSize(int fragmentSize)240     void changeFragmentSize(int fragmentSize) {
241         recordLock.lock();
242         try {
243             this.fragmentSize = fragmentSize;
244         } finally {
245             recordLock.unlock();
246         }
247     }
248 
getMaxPacketSize()249     int getMaxPacketSize() {
250         recordLock.lock();
251         try {
252             return packetSize;
253         } finally {
254             recordLock.unlock();
255         }
256     }
257 
258     // apply to DTLS SSLEngine
initHandshaker()259     void initHandshaker() {
260         // blank
261     }
262 
263     // apply to DTLS SSLEngine
finishHandshake()264     void finishHandshake() {
265         // blank
266     }
267 
268     // apply to DTLS SSLEngine
launchRetransmission()269     void launchRetransmission() {
270         // blank
271     }
272 
273     @Override
close()274     public void close() throws IOException {
275         recordLock.lock();
276         try {
277             if (isClosed) {
278                 return;
279             }
280 
281             isClosed = true;
282             writeCipher.dispose();
283         } finally {
284             recordLock.unlock();
285         }
286     }
287 
isClosed()288     boolean isClosed() {
289         return isClosed;
290     }
291 
292     //
293     // shared helpers
294     //
295 
296     private static final class T13PaddingHolder {
297         private static final byte[] zeros = new byte[16];
298     }
299 
calculateFragmentSize(int fragmentLimit)300     int calculateFragmentSize(int fragmentLimit) {
301         if (fragmentSize > 0) {
302             fragmentLimit = Math.min(fragmentLimit, fragmentSize);
303         }
304 
305         if (protocolVersion.useTLS13PlusSpec()) {
306             // No negative integer checking as the fragment capacity should
307             // have been ensured.
308             return fragmentLimit -  T13PaddingHolder.zeros.length - 1;
309         }
310 
311         return fragmentLimit;
312     }
313 
314     // Encrypt a fragment and wrap up a record.
315     //
316     // To be consistent with the spec of SSLEngine.wrap() methods, the
317     // destination ByteBuffer's position is updated to reflect the amount
318     // of data produced.  The limit remains the same.
encrypt( SSLWriteCipher encCipher, byte contentType, ByteBuffer destination, int headerOffset, int dstLim, int headerSize, ProtocolVersion protocolVersion)319     static long encrypt(
320             SSLWriteCipher encCipher, byte contentType, ByteBuffer destination,
321             int headerOffset, int dstLim, int headerSize,
322             ProtocolVersion protocolVersion) {
323         boolean isDTLS = protocolVersion.isDTLS;
324         if (isDTLS) {
325             if (protocolVersion.useTLS13PlusSpec()) {
326                 return d13Encrypt(encCipher,
327                         contentType, destination, headerOffset,
328                         dstLim, headerSize, protocolVersion);
329             } else {
330                 return d10Encrypt(encCipher,
331                         contentType, destination, headerOffset,
332                         dstLim, headerSize, protocolVersion);
333             }
334         } else {
335             if (protocolVersion.useTLS13PlusSpec()) {
336                 return t13Encrypt(encCipher,
337                         contentType, destination, headerOffset,
338                         dstLim, headerSize, protocolVersion);
339             } else {
340                 return t10Encrypt(encCipher,
341                         contentType, destination, headerOffset,
342                         dstLim, headerSize, protocolVersion);
343             }
344         }
345     }
346 
d13Encrypt( SSLWriteCipher encCipher, byte contentType, ByteBuffer destination, int headerOffset, int dstLim, int headerSize, ProtocolVersion protocolVersion)347     private static long d13Encrypt(
348             SSLWriteCipher encCipher, byte contentType, ByteBuffer destination,
349             int headerOffset, int dstLim, int headerSize,
350             ProtocolVersion protocolVersion) {
351         throw new UnsupportedOperationException("Not supported yet.");
352     }
353 
d10Encrypt( SSLWriteCipher encCipher, byte contentType, ByteBuffer destination, int headerOffset, int dstLim, int headerSize, ProtocolVersion protocolVersion)354     private static long d10Encrypt(
355             SSLWriteCipher encCipher, byte contentType, ByteBuffer destination,
356             int headerOffset, int dstLim, int headerSize,
357             ProtocolVersion protocolVersion) {
358         byte[] sequenceNumber = encCipher.authenticator.sequenceNumber();
359         encCipher.encrypt(contentType, destination);
360 
361         // Finish out the record header.
362         int fragLen = destination.limit() - headerOffset - headerSize;
363 
364         destination.put(headerOffset, contentType);         // content type
365         destination.put(headerOffset + 1, protocolVersion.major);
366         destination.put(headerOffset + 2, protocolVersion.minor);
367 
368         // epoch and sequence_number
369         destination.put(headerOffset + 3, sequenceNumber[0]);
370         destination.put(headerOffset + 4, sequenceNumber[1]);
371         destination.put(headerOffset + 5, sequenceNumber[2]);
372         destination.put(headerOffset + 6, sequenceNumber[3]);
373         destination.put(headerOffset + 7, sequenceNumber[4]);
374         destination.put(headerOffset + 8, sequenceNumber[5]);
375         destination.put(headerOffset + 9, sequenceNumber[6]);
376         destination.put(headerOffset + 10, sequenceNumber[7]);
377 
378         // fragment length
379         destination.put(headerOffset + 11, (byte)(fragLen >> 8));
380         destination.put(headerOffset + 12, (byte)fragLen);
381 
382         // Update destination position to reflect the amount of data produced.
383         destination.position(destination.limit());
384 
385         return Authenticator.toLong(sequenceNumber);
386     }
387 
t13Encrypt( SSLWriteCipher encCipher, byte contentType, ByteBuffer destination, int headerOffset, int dstLim, int headerSize, ProtocolVersion protocolVersion)388     private static long t13Encrypt(
389             SSLWriteCipher encCipher, byte contentType, ByteBuffer destination,
390             int headerOffset, int dstLim, int headerSize,
391             ProtocolVersion protocolVersion) {
392         if (!encCipher.isNullCipher()) {
393             // inner plaintext, using zero length padding.
394             int endOfPt = destination.limit();
395             int startOfPt = destination.position();
396             destination.position(endOfPt);
397             destination.limit(endOfPt + 1 + T13PaddingHolder.zeros.length);
398             destination.put(contentType);
399             destination.put(T13PaddingHolder.zeros);
400             destination.position(startOfPt);
401         }
402 
403         // use the right TLSCiphertext.opaque_type and legacy_record_version
404         ProtocolVersion pv = protocolVersion;
405         if (!encCipher.isNullCipher()) {
406             pv = ProtocolVersion.TLS12;
407             contentType = ContentType.APPLICATION_DATA.id;
408         } else if (protocolVersion.useTLS13PlusSpec()) {
409             pv = ProtocolVersion.TLS12;
410         }
411 
412         byte[] sequenceNumber = encCipher.authenticator.sequenceNumber();
413         encCipher.encrypt(contentType, destination);
414 
415         // Finish out the record header.
416         int fragLen = destination.limit() - headerOffset - headerSize;
417         destination.put(headerOffset, contentType);
418         destination.put(headerOffset + 1, pv.major);
419         destination.put(headerOffset + 2, pv.minor);
420 
421         // fragment length
422         destination.put(headerOffset + 3, (byte)(fragLen >> 8));
423         destination.put(headerOffset + 4, (byte)fragLen);
424 
425         // Update destination position to reflect the amount of data produced.
426         destination.position(destination.limit());
427 
428         return Authenticator.toLong(sequenceNumber);
429     }
430 
t10Encrypt( SSLWriteCipher encCipher, byte contentType, ByteBuffer destination, int headerOffset, int dstLim, int headerSize, ProtocolVersion protocolVersion)431     private static long t10Encrypt(
432             SSLWriteCipher encCipher, byte contentType, ByteBuffer destination,
433             int headerOffset, int dstLim, int headerSize,
434             ProtocolVersion protocolVersion) {
435         byte[] sequenceNumber = encCipher.authenticator.sequenceNumber();
436         encCipher.encrypt(contentType, destination);
437 
438         // Finish out the record header.
439         int fragLen = destination.limit() - headerOffset - headerSize;
440 
441         destination.put(headerOffset, contentType);         // content type
442         destination.put(headerOffset + 1, protocolVersion.major);
443         destination.put(headerOffset + 2, protocolVersion.minor);
444 
445         // fragment length
446         destination.put(headerOffset + 3, (byte)(fragLen >> 8));
447         destination.put(headerOffset + 4, (byte)fragLen);
448 
449         // Update destination position to reflect the amount of data produced.
450         destination.position(destination.limit());
451 
452         return Authenticator.toLong(sequenceNumber);
453     }
454 
455     // Encrypt a fragment and wrap up a record.
456     //
457     // Uses the internal expandable buf variable and the current
458     // protocolVersion variable.
encrypt( SSLWriteCipher encCipher, byte contentType, int headerSize)459     long encrypt(
460             SSLWriteCipher encCipher, byte contentType, int headerSize) {
461         if (protocolVersion.useTLS13PlusSpec()) {
462             return t13Encrypt(encCipher, contentType, headerSize);
463         } else {
464             return t10Encrypt(encCipher, contentType, headerSize);
465         }
466     }
467 
t13Encrypt( SSLWriteCipher encCipher, byte contentType, int headerSize)468     private long t13Encrypt(
469             SSLWriteCipher encCipher, byte contentType, int headerSize) {
470         if (!encCipher.isNullCipher()) {
471             // inner plaintext
472             write(contentType);
473             write(T13PaddingHolder.zeros, 0, T13PaddingHolder.zeros.length);
474         }
475 
476         byte[] sequenceNumber = encCipher.authenticator.sequenceNumber();
477         int position = headerSize;
478         int contentLen = count - position;
479 
480         // ensure the capacity
481         int requiredPacketSize =
482                 encCipher.calculatePacketSize(contentLen, headerSize);
483         if (requiredPacketSize > buf.length) {
484             byte[] newBuf = new byte[requiredPacketSize];
485             System.arraycopy(buf, 0, newBuf, 0, count);
486             buf = newBuf;
487         }
488 
489         // use the right TLSCiphertext.opaque_type and legacy_record_version
490         ProtocolVersion pv = protocolVersion;
491         if (!encCipher.isNullCipher()) {
492             pv = ProtocolVersion.TLS12;
493             contentType = ContentType.APPLICATION_DATA.id;
494         } else {
495             pv = ProtocolVersion.TLS12;
496         }
497 
498         ByteBuffer destination = ByteBuffer.wrap(buf, position, contentLen);
499         count = headerSize + encCipher.encrypt(contentType, destination);
500 
501         // Fill out the header, write it and the message.
502         int fragLen = count - headerSize;
503 
504         buf[0] = contentType;
505         buf[1] = pv.major;
506         buf[2] = pv.minor;
507         buf[3] = (byte)((fragLen >> 8) & 0xFF);
508         buf[4] = (byte)(fragLen & 0xFF);
509 
510         return Authenticator.toLong(sequenceNumber);
511     }
512 
t10Encrypt( SSLWriteCipher encCipher, byte contentType, int headerSize)513     private long t10Encrypt(
514             SSLWriteCipher encCipher, byte contentType, int headerSize) {
515         byte[] sequenceNumber = encCipher.authenticator.sequenceNumber();
516         int position = headerSize + writeCipher.getExplicitNonceSize();
517         int contentLen = count - position;
518 
519         // ensure the capacity
520         int requiredPacketSize =
521                 encCipher.calculatePacketSize(contentLen, headerSize);
522         if (requiredPacketSize > buf.length) {
523             byte[] newBuf = new byte[requiredPacketSize];
524             System.arraycopy(buf, 0, newBuf, 0, count);
525             buf = newBuf;
526         }
527         ByteBuffer destination = ByteBuffer.wrap(buf, position, contentLen);
528         count = headerSize + encCipher.encrypt(contentType, destination);
529 
530         // Fill out the header, write it and the message.
531         int fragLen = count - headerSize;
532         buf[0] = contentType;
533         buf[1] = protocolVersion.major;
534         buf[2] = protocolVersion.minor;
535         buf[3] = (byte)((fragLen >> 8) & 0xFF);
536         buf[4] = (byte)(fragLen & 0xFF);
537 
538         return Authenticator.toLong(sequenceNumber);
539     }
540 
encodeV2ClientHello( byte[] fragment, int offset, int length)541     static ByteBuffer encodeV2ClientHello(
542             byte[] fragment, int offset, int length) throws IOException {
543         int v3SessIdLenOffset = offset + 34;      //  2: client_version
544                                                   // 32: random
545 
546         int v3SessIdLen = fragment[v3SessIdLenOffset];
547         int v3CSLenOffset = v3SessIdLenOffset + 1 + v3SessIdLen;
548         int v3CSLen = ((fragment[v3CSLenOffset] & 0xff) << 8) +
549                        (fragment[v3CSLenOffset + 1] & 0xff);
550         int cipherSpecs = v3CSLen / 2;        // 2: cipher spec size
551 
552         // Estimate the max V2ClientHello message length
553         //
554         // 11: header size
555         // (cipherSpecs * 6): cipher_specs
556         //    6: one cipher suite may need 6 bytes, see V3toV2CipherSuite.
557         // 3: placeholder for the TLS_EMPTY_RENEGOTIATION_INFO_SCSV
558         //    signaling cipher suite
559         // 32: challenge size
560         int v2MaxMsgLen = 11 + (cipherSpecs * 6) + 3 + 32;
561 
562         // Create a ByteBuffer backed by an accessible byte array.
563         byte[] dstBytes = new byte[v2MaxMsgLen];
564         ByteBuffer dstBuf = ByteBuffer.wrap(dstBytes);
565 
566         /*
567          * Copy over the cipher specs. We don't care about actually
568          * translating them for use with an actual V2 server since
569          * we only talk V3.  Therefore, just copy over the V3 cipher
570          * spec values with a leading 0.
571          */
572         int v3CSOffset = v3CSLenOffset + 2;   // skip length field
573         int v2CSLen = 0;
574 
575         dstBuf.position(11);
576         boolean containsRenegoInfoSCSV = false;
577         for (int i = 0; i < cipherSpecs; i++) {
578             byte byte1, byte2;
579 
580             byte1 = fragment[v3CSOffset++];
581             byte2 = fragment[v3CSOffset++];
582             v2CSLen += V3toV2CipherSuite(dstBuf, byte1, byte2);
583             if (!containsRenegoInfoSCSV &&
584                     byte1 == (byte)0x00 && byte2 == (byte)0xFF) {
585                 containsRenegoInfoSCSV = true;
586             }
587         }
588 
589         if (!containsRenegoInfoSCSV) {
590             v2CSLen += V3toV2CipherSuite(dstBuf, (byte)0x00, (byte)0xFF);
591         }
592 
593         /*
594          * Copy in the nonce.
595          */
596         dstBuf.put(fragment, (offset + 2), 32);
597 
598         /*
599          * Build the first part of the V3 record header from the V2 one
600          * that's now buffered up.  (Lengths are fixed up later).
601          */
602         int msgLen = dstBuf.position() - 2;   // Exclude the legth field itself
603         dstBuf.position(0);
604         dstBuf.put((byte)(0x80 | ((msgLen >>> 8) & 0xFF)));  // pos: 0
605         dstBuf.put((byte)(msgLen & 0xFF));                   // pos: 1
606         dstBuf.put(SSLHandshake.CLIENT_HELLO.id);            // pos: 2
607         dstBuf.put(fragment[offset]);         // major version, pos: 3
608         dstBuf.put(fragment[offset + 1]);     // minor version, pos: 4
609         dstBuf.put((byte)(v2CSLen >>> 8));                   // pos: 5
610         dstBuf.put((byte)(v2CSLen & 0xFF));                  // pos: 6
611         dstBuf.put((byte)0x00);           // session_id_length, pos: 7
612         dstBuf.put((byte)0x00);                              // pos: 8
613         dstBuf.put((byte)0x00);           // challenge_length,  pos: 9
614         dstBuf.put((byte)32);                                // pos: 10
615 
616         dstBuf.position(0);
617         dstBuf.limit(msgLen + 2);
618 
619         return dstBuf;
620     }
621 
V3toV2CipherSuite(ByteBuffer dstBuf, byte byte1, byte byte2)622     private static int V3toV2CipherSuite(ByteBuffer dstBuf,
623             byte byte1, byte byte2) {
624         dstBuf.put((byte)0);
625         dstBuf.put(byte1);
626         dstBuf.put(byte2);
627 
628         if (((byte2 & 0xff) > 0xA) || (V3toV2CipherMap1[byte2] == -1)) {
629             return 3;
630         }
631 
632         dstBuf.put((byte)V3toV2CipherMap1[byte2]);
633         dstBuf.put((byte)0);
634         dstBuf.put((byte)V3toV2CipherMap3[byte2]);
635 
636         return 6;
637     }
638 }
639