23 * questions. |
23 * questions. |
24 */ |
24 */ |
25 |
25 |
26 package sun.security.ssl; |
26 package sun.security.ssl; |
27 |
27 |
28 import java.io.*; |
28 import java.io.Closeable; |
29 import java.nio.*; |
29 import java.io.IOException; |
30 import java.util.*; |
30 import java.io.InputStream; |
31 |
31 import java.io.OutputStream; |
|
32 import java.nio.BufferUnderflowException; |
|
33 import java.nio.ByteBuffer; |
32 import javax.crypto.BadPaddingException; |
34 import javax.crypto.BadPaddingException; |
33 |
35 import sun.security.ssl.SSLCipher.SSLReadCipher; |
34 import javax.net.ssl.*; |
|
35 |
|
36 import sun.security.util.HexDumpEncoder; |
|
37 |
|
38 |
36 |
39 /** |
37 /** |
40 * {@code InputRecord} takes care of the management of SSL/TLS/DTLS input |
38 * {@code InputRecord} takes care of the management of SSL/TLS/DTLS input |
41 * records, including buffering, decryption, handshake messages marshal, etc. |
39 * records, including buffering, decryption, handshake messages marshal, etc. |
42 * |
40 * |
43 * @author David Brownell |
41 * @author David Brownell |
44 */ |
42 */ |
45 class InputRecord implements Record, Closeable { |
43 abstract class InputRecord implements Record, Closeable { |
46 |
44 SSLReadCipher readCipher; |
47 /* Class and subclass dynamic debugging support */ |
45 // Needed for KeyUpdate, used after Handshake.Finished |
48 static final Debug debug = Debug.getInstance("ssl"); |
46 TransportContext tc; |
49 |
47 |
50 Authenticator readAuthenticator; |
48 final HandshakeHash handshakeHash; |
51 CipherBox readCipher; |
|
52 |
|
53 HandshakeHash handshakeHash; |
|
54 boolean isClosed; |
49 boolean isClosed; |
55 |
50 |
56 // The ClientHello version to accept. If set to ProtocolVersion.SSL20Hello |
51 // 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 |
52 // 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. |
53 // it to V3. Otherwise we throw an exception when encountering a V2 hello. |
59 ProtocolVersion helloVersion; |
54 ProtocolVersion helloVersion; |
60 |
55 |
61 // fragment size |
56 // fragment size |
62 int fragmentSize; |
57 int fragmentSize; |
63 |
58 |
64 InputRecord() { |
59 InputRecord(HandshakeHash handshakeHash, SSLReadCipher readCipher) { |
65 this.readCipher = CipherBox.NULL; |
60 this.readCipher = readCipher; |
66 this.readAuthenticator = null; // Please override this assignment. |
61 this.helloVersion = ProtocolVersion.TLS10; |
67 this.helloVersion = ProtocolVersion.DEFAULT_HELLO; |
62 this.handshakeHash = handshakeHash; |
|
63 this.isClosed = false; |
68 this.fragmentSize = Record.maxDataSize; |
64 this.fragmentSize = Record.maxDataSize; |
69 } |
65 } |
70 |
66 |
71 void setHelloVersion(ProtocolVersion helloVersion) { |
67 void setHelloVersion(ProtocolVersion helloVersion) { |
72 this.helloVersion = helloVersion; |
68 this.helloVersion = helloVersion; |
73 } |
69 } |
74 |
70 |
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() { |
71 boolean seqNumIsHuge() { |
108 return (readAuthenticator != null) && |
72 return (readCipher.authenticator != null) && |
109 readAuthenticator.seqNumIsHuge(); |
73 readCipher.authenticator.seqNumIsHuge(); |
110 } |
74 } |
111 |
75 |
112 boolean isEmpty() { |
76 boolean isEmpty() { |
113 return false; |
77 return false; |
114 } |
78 } |
115 |
79 |
116 // apply to DTLS SSLEngine |
80 // apply to DTLS SSLEngine |
117 void expectingFinishFlight() { |
81 void expectingFinishFlight() { |
|
82 // blank |
|
83 } |
|
84 |
|
85 // apply to DTLS SSLEngine |
|
86 void finishHandshake() { |
118 // blank |
87 // blank |
119 } |
88 } |
120 |
89 |
121 /** |
90 /** |
122 * Prevent any more data from being read into this record, |
91 * Prevent any more data from being read into this record, |
158 * a inbound packet. |
129 * a inbound packet. |
159 * |
130 * |
160 * @return -1 if there are not enough bytes to tell (small header), |
131 * @return -1 if there are not enough bytes to tell (small header), |
161 */ |
132 */ |
162 // apply to SSLEngine only |
133 // apply to SSLEngine only |
163 int bytesInCompletePacket(ByteBuffer buf) throws SSLException { |
134 int bytesInCompletePacket( |
164 throw new UnsupportedOperationException(); |
135 ByteBuffer[] srcs, int srcsOffset, int srcsLength) throws IOException { |
|
136 |
|
137 throw new UnsupportedOperationException("Not supported yet."); |
165 } |
138 } |
166 |
139 |
167 // apply to SSLSocket only |
140 // apply to SSLSocket only |
168 int bytesInCompletePacket(InputStream is) throws IOException { |
141 int bytesInCompletePacket() throws IOException { |
169 throw new UnsupportedOperationException(); |
142 throw new UnsupportedOperationException(); |
170 } |
143 } |
171 |
144 |
172 /** |
145 // apply to SSLSocket only |
173 * Return true if the specified record protocol version is out of the |
146 void setReceiverStream(InputStream inputStream) { |
174 * range of the possible supported versions. |
147 throw new UnsupportedOperationException(); |
175 */ |
|
176 void checkRecordVersion(ProtocolVersion version, |
|
177 boolean allowSSL20Hello) throws SSLException { |
|
178 // blank |
|
179 } |
148 } |
180 |
149 |
181 // apply to DTLS SSLEngine only |
150 // apply to DTLS SSLEngine only |
182 Plaintext acquirePlaintext() |
151 Plaintext acquirePlaintext() |
183 throws IOException, BadPaddingException { |
152 throws IOException, BadPaddingException { |
184 throw new UnsupportedOperationException(); |
153 throw new UnsupportedOperationException(); |
185 } |
154 } |
186 |
155 |
187 // read, decrypt and decompress the network record. |
156 // read, decrypt and decompress the network record. |
188 // |
157 // |
189 // apply to SSLEngine only |
158 abstract Plaintext[] decode(ByteBuffer[] srcs, int srcsOffset, |
190 Plaintext decode(ByteBuffer netData) |
159 int srcsLength) throws IOException, BadPaddingException; |
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 |
160 |
201 // apply to SSLSocket only |
161 // apply to SSLSocket only |
202 void setDeliverStream(OutputStream outputStream) { |
162 void setDeliverStream(OutputStream outputStream) { |
203 throw new UnsupportedOperationException(); |
163 throw new UnsupportedOperationException(); |
204 } |
164 } |
370 |
328 |
371 // Need no header bytes. |
329 // Need no header bytes. |
372 return ByteBuffer.wrap(converted, 5, pointer - 5); // 5: header size |
330 return ByteBuffer.wrap(converted, 5, pointer - 5); // 5: header size |
373 } |
331 } |
374 |
332 |
375 static ByteBuffer decrypt(Authenticator authenticator, CipherBox box, |
333 // Extract an SSL/(D)TLS record from the specified source buffers. |
376 byte contentType, ByteBuffer bb) throws BadPaddingException { |
334 static ByteBuffer extract( |
377 |
335 ByteBuffer[] buffers, int offset, int length, int headerSize) { |
378 return decrypt(authenticator, box, contentType, bb, null); |
336 |
379 } |
337 boolean hasFullHeader = false; |
380 |
338 int contentLen = -1; |
381 static ByteBuffer decrypt(Authenticator authenticator, |
339 for (int i = offset, j = 0; |
382 CipherBox box, byte contentType, ByteBuffer bb, |
340 i < (offset + length) && j < headerSize; i++) { |
383 byte[] sequence) throws BadPaddingException { |
341 int remains = buffers[i].remaining(); |
384 |
342 int pos = buffers[i].position(); |
385 BadPaddingException reservedBPE = null; |
343 for (int k = 0; k < remains && j < headerSize; j++, k++) { |
386 int tagLen = |
344 byte b = buffers[i].get(pos + k); |
387 (authenticator instanceof MAC) ? ((MAC)authenticator).MAClen() : 0; |
345 if (j == (headerSize - 2)) { |
388 int cipheredLength = bb.remaining(); |
346 contentLen = ((b & 0xFF) << 8); |
389 int srcPos = bb.position(); |
347 } else if (j == (headerSize -1)) { |
390 if (!box.isNullCipher()) { |
348 contentLen |= (b & 0xFF); |
391 try { |
349 hasFullHeader = true; |
392 // apply explicit nonce for AEAD/CBC cipher suites if needed |
350 break; |
393 int nonceSize = box.applyExplicitNonce( |
|
394 authenticator, contentType, bb, sequence); |
|
395 |
|
396 // decrypt the content |
|
397 if (box.isAEADMode()) { |
|
398 // DON'T decrypt the nonce_explicit for AEAD mode |
|
399 bb.position(srcPos + nonceSize); |
|
400 } // The explicit IV for CBC mode can be decrypted. |
|
401 |
|
402 // Note that the CipherBox.decrypt() does not change |
|
403 // the capacity of the buffer. |
|
404 box.decrypt(bb, tagLen); |
|
405 // We don't actually remove the nonce. |
|
406 bb.position(srcPos + nonceSize); |
|
407 } catch (BadPaddingException bpe) { |
|
408 // RFC 2246 states that decryption_failed should be used |
|
409 // for this purpose. However, that allows certain attacks, |
|
410 // so we just send bad record MAC. We also need to make |
|
411 // sure to always check the MAC to avoid a timing attack |
|
412 // for the same issue. See paper by Vaudenay et al and the |
|
413 // update in RFC 4346/5246. |
|
414 // |
|
415 // Failover to message authentication code checking. |
|
416 reservedBPE = bpe; |
|
417 } |
|
418 } |
|
419 |
|
420 // Requires message authentication code for null, stream and block |
|
421 // cipher suites. |
|
422 if ((authenticator instanceof MAC) && (tagLen != 0)) { |
|
423 MAC signer = (MAC)authenticator; |
|
424 int contentLen = bb.remaining() - tagLen; |
|
425 |
|
426 // Note that although it is not necessary, we run the same MAC |
|
427 // computation and comparison on the payload for both stream |
|
428 // cipher and CBC block cipher. |
|
429 if (contentLen < 0) { |
|
430 // negative data length, something is wrong |
|
431 if (reservedBPE == null) { |
|
432 reservedBPE = new BadPaddingException("bad record"); |
|
433 } |
351 } |
434 |
352 } |
435 // set offset of the dummy MAC |
353 } |
436 contentLen = cipheredLength - tagLen; |
354 |
437 bb.limit(srcPos + cipheredLength); |
355 if (!hasFullHeader) { |
438 } |
356 throw new BufferUnderflowException(); |
439 |
357 } |
440 // Run MAC computation and comparison on the payload. |
358 |
441 // |
359 int packetLen = headerSize + contentLen; |
442 // MAC data would be stripped off during the check. |
360 int remains = 0; |
443 if (checkMacTags(contentType, bb, signer, sequence, false)) { |
361 for (int i = offset; i < offset + length; i++) { |
444 if (reservedBPE == null) { |
362 remains += buffers[i].remaining(); |
445 reservedBPE = new BadPaddingException("bad record MAC"); |
363 if (remains >= packetLen) { |
446 } |
364 break; |
447 } |
365 } |
448 |
366 } |
449 // Run MAC computation and comparison on the remainder. |
367 |
450 // |
368 if (remains < packetLen) { |
451 // It is only necessary for CBC block cipher. It is used to get a |
369 throw new BufferUnderflowException(); |
452 // constant time of MAC computation and comparison on each record. |
370 } |
453 if (box.isCBCMode()) { |
371 |
454 int remainingLen = calculateRemainingLen( |
372 byte[] packet = new byte[packetLen]; |
455 signer, cipheredLength, contentLen); |
373 int packetOffset = 0; |
456 |
374 int packetSpaces = packetLen; |
457 // NOTE: remainingLen may be bigger (less than 1 block of the |
375 for (int i = offset; i < offset + length; i++) { |
458 // hash algorithm of the MAC) than the cipheredLength. |
376 if (buffers[i].hasRemaining()) { |
459 // |
377 int len = Math.min(packetSpaces, buffers[i].remaining()); |
460 // Is it possible to use a static buffer, rather than allocate |
378 buffers[i].get(packet, packetOffset, len); |
461 // it dynamically? |
379 packetOffset += len; |
462 remainingLen += signer.MAClen(); |
380 packetSpaces -= len; |
463 ByteBuffer temporary = ByteBuffer.allocate(remainingLen); |
381 } |
464 |
382 |
465 // Won't need to worry about the result on the remainder. And |
383 if (packetSpaces <= 0) { |
466 // then we won't need to worry about what's actual data to |
384 break; |
467 // check MAC tag on. We start the check from the header of the |
385 } |
468 // buffer so that we don't need to construct a new byte buffer. |
386 } |
469 checkMacTags(contentType, temporary, signer, sequence, true); |
387 |
470 } |
388 return ByteBuffer.wrap(packet); |
471 } |
|
472 |
|
473 // Is it a failover? |
|
474 if (reservedBPE != null) { |
|
475 throw reservedBPE; |
|
476 } |
|
477 |
|
478 return bb.slice(); |
|
479 } |
|
480 |
|
481 /* |
|
482 * Run MAC computation and comparison |
|
483 * |
|
484 */ |
|
485 private static boolean checkMacTags(byte contentType, ByteBuffer bb, |
|
486 MAC signer, byte[] sequence, boolean isSimulated) { |
|
487 |
|
488 int tagLen = signer.MAClen(); |
|
489 int position = bb.position(); |
|
490 int lim = bb.limit(); |
|
491 int macOffset = lim - tagLen; |
|
492 |
|
493 bb.limit(macOffset); |
|
494 byte[] hash = signer.compute(contentType, bb, sequence, isSimulated); |
|
495 if (hash == null || tagLen != hash.length) { |
|
496 // Something is wrong with MAC implementation. |
|
497 throw new RuntimeException("Internal MAC error"); |
|
498 } |
|
499 |
|
500 bb.position(macOffset); |
|
501 bb.limit(lim); |
|
502 try { |
|
503 int[] results = compareMacTags(bb, hash); |
|
504 return (results[0] != 0); |
|
505 } finally { |
|
506 // reset to the data |
|
507 bb.position(position); |
|
508 bb.limit(macOffset); |
|
509 } |
|
510 } |
|
511 |
|
512 /* |
|
513 * A constant-time comparison of the MAC tags. |
|
514 * |
|
515 * Please DON'T change the content of the ByteBuffer parameter! |
|
516 */ |
|
517 private static int[] compareMacTags(ByteBuffer bb, byte[] tag) { |
|
518 |
|
519 // An array of hits is used to prevent Hotspot optimization for |
|
520 // the purpose of a constant-time check. |
|
521 int[] results = {0, 0}; // {missed #, matched #} |
|
522 |
|
523 // The caller ensures there are enough bytes available in the buffer. |
|
524 // So we won't need to check the remaining of the buffer. |
|
525 for (int i = 0; i < tag.length; i++) { |
|
526 if (bb.get() != tag[i]) { |
|
527 results[0]++; // mismatched bytes |
|
528 } else { |
|
529 results[1]++; // matched bytes |
|
530 } |
|
531 } |
|
532 |
|
533 return results; |
|
534 } |
|
535 |
|
536 /* |
|
537 * Run MAC computation and comparison |
|
538 * |
|
539 * Please DON'T change the content of the byte buffer parameter! |
|
540 */ |
|
541 private static boolean checkMacTags(byte contentType, byte[] buffer, |
|
542 int offset, int contentLen, MAC signer, boolean isSimulated) { |
|
543 |
|
544 int tagLen = signer.MAClen(); |
|
545 byte[] hash = signer.compute( |
|
546 contentType, buffer, offset, contentLen, isSimulated); |
|
547 if (hash == null || tagLen != hash.length) { |
|
548 // Something is wrong with MAC implementation. |
|
549 throw new RuntimeException("Internal MAC error"); |
|
550 } |
|
551 |
|
552 int[] results = compareMacTags(buffer, offset + contentLen, hash); |
|
553 return (results[0] != 0); |
|
554 } |
|
555 |
|
556 /* |
|
557 * A constant-time comparison of the MAC tags. |
|
558 * |
|
559 * Please DON'T change the content of the byte buffer parameter! |
|
560 */ |
|
561 private static int[] compareMacTags( |
|
562 byte[] buffer, int offset, byte[] tag) { |
|
563 |
|
564 // An array of hits is used to prevent Hotspot optimization for |
|
565 // the purpose of a constant-time check. |
|
566 int[] results = {0, 0}; // {missed #, matched #} |
|
567 |
|
568 // The caller ensures there are enough bytes available in the buffer. |
|
569 // So we won't need to check the length of the buffer. |
|
570 for (int i = 0; i < tag.length; i++) { |
|
571 if (buffer[offset + i] != tag[i]) { |
|
572 results[0]++; // mismatched bytes |
|
573 } else { |
|
574 results[1]++; // matched bytes |
|
575 } |
|
576 } |
|
577 |
|
578 return results; |
|
579 } |
|
580 |
|
581 /* |
|
582 * Calculate the length of a dummy buffer to run MAC computation |
|
583 * and comparison on the remainder. |
|
584 * |
|
585 * The caller MUST ensure that the fullLen is not less than usedLen. |
|
586 */ |
|
587 private static int calculateRemainingLen( |
|
588 MAC signer, int fullLen, int usedLen) { |
|
589 |
|
590 int blockLen = signer.hashBlockLen(); |
|
591 int minimalPaddingLen = signer.minimalPaddingLen(); |
|
592 |
|
593 // (blockLen - minimalPaddingLen) is the maximum message size of |
|
594 // the last block of hash function operation. See FIPS 180-4, or |
|
595 // MD5 specification. |
|
596 fullLen += 13 - (blockLen - minimalPaddingLen); |
|
597 usedLen += 13 - (blockLen - minimalPaddingLen); |
|
598 |
|
599 // Note: fullLen is always not less than usedLen, and blockLen |
|
600 // is always bigger than minimalPaddingLen, so we don't worry |
|
601 // about negative values. 0x01 is added to the result to ensure |
|
602 // that the return value is positive. The extra one byte does |
|
603 // not impact the overall MAC compression function evaluations. |
|
604 return 0x01 + (int)(Math.ceil(fullLen/(1.0d * blockLen)) - |
|
605 Math.ceil(usedLen/(1.0d * blockLen))) * blockLen; |
|
606 } |
389 } |
607 } |
390 } |
608 |
|