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