author | chegar |
Mon, 14 Dec 2015 19:24:33 +0000 | |
changeset 34687 | d302ed125dc9 |
parent 32649 | 2ee9017c7597 |
child 43008 | c6c74a38f1ad |
permissions | -rw-r--r-- |
2 | 1 |
/* |
24263
f95477ce56e4
8042449: Issue for negative byte major record version
xuelei
parents:
16913
diff
changeset
|
2 |
* Copyright (c) 1996, 2014, Oracle and/or its affiliates. All rights reserved. |
2 | 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 |
|
5506 | 7 |
* published by the Free Software Foundation. Oracle designates this |
2 | 8 |
* particular file as subject to the "Classpath" exception as provided |
5506 | 9 |
* by Oracle in the LICENSE file that accompanied this code. |
2 | 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 |
* |
|
5506 | 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. |
|
2 | 24 |
*/ |
25 |
||
26 |
package sun.security.ssl; |
|
27 |
||
28 |
import java.io.*; |
|
29 |
import java.nio.*; |
|
30904 | 30 |
import java.util.*; |
2 | 31 |
|
32 |
import javax.crypto.BadPaddingException; |
|
33 |
||
34 |
import javax.net.ssl.*; |
|
35 |
||
34687
d302ed125dc9
8144995: Move sun.misc.HexDumpEncoder to sun.security.util
chegar
parents:
32649
diff
changeset
|
36 |
import sun.security.util.HexDumpEncoder; |
2 | 37 |
|
38 |
||
39 |
/** |
|
30904 | 40 |
* {@code InputRecord} takes care of the management of SSL/TLS/DTLS input |
41 |
* records, including buffering, decryption, handshake messages marshal, etc. |
|
2 | 42 |
* |
43 |
* @author David Brownell |
|
44 |
*/ |
|
30904 | 45 |
class InputRecord implements Record, Closeable { |
46 |
||
47 |
/* Class and subclass dynamic debugging support */ |
|
48 |
static final Debug debug = Debug.getInstance("ssl"); |
|
2 | 49 |
|
30904 | 50 |
Authenticator readAuthenticator; |
51 |
CipherBox readCipher; |
|
52 |
||
53 |
HandshakeHash handshakeHash; |
|
54 |
boolean isClosed; |
|
2 | 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. |
|
30904 | 59 |
ProtocolVersion helloVersion; |
2 | 60 |
|
30904 | 61 |
// fragment size |
62 |
int fragmentSize; |
|
2 | 63 |
|
64 |
InputRecord() { |
|
30904 | 65 |
this.readCipher = CipherBox.NULL; |
66 |
this.readAuthenticator = null; // Please override this assignment. |
|
67 |
this.helloVersion = ProtocolVersion.DEFAULT_HELLO; |
|
68 |
this.fragmentSize = Record.maxDataSize; |
|
2 | 69 |
} |
70 |
||
71 |
void setHelloVersion(ProtocolVersion helloVersion) { |
|
72 |
this.helloVersion = helloVersion; |
|
73 |
} |
|
74 |
||
75 |
ProtocolVersion getHelloVersion() { |
|
76 |
return helloVersion; |
|
77 |
} |
|
78 |
||
79 |
/* |
|
30904 | 80 |
* Set instance for the computation of handshake hashes. |
81 |
* |
|
2 | 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) { |
|
30904 | 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 |
||
2 | 104 |
this.handshakeHash = handshakeHash; |
105 |
} |
|
106 |
||
30904 | 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 |
|
32649
2ee9017c7597
8136583: Core libraries should use blessed modifier order
martin
parents:
30904
diff
changeset
|
126 |
public synchronized void close() throws IOException { |
30904 | 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(); |
|
2 | 211 |
} |
212 |
||
30904 | 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 |
||
16113 | 384 |
BadPaddingException reservedBPE = null; |
16913 | 385 |
int tagLen = |
386 |
(authenticator instanceof MAC) ? ((MAC)authenticator).MAClen() : 0; |
|
30904 | 387 |
int cipheredLength = bb.remaining(); |
388 |
int srcPos = bb.position(); |
|
16113 | 389 |
if (!box.isNullCipher()) { |
16913 | 390 |
try { |
391 |
// apply explicit nonce for AEAD/CBC cipher suites if needed |
|
30904 | 392 |
int nonceSize = box.applyExplicitNonce( |
393 |
authenticator, contentType, bb, sequence); |
|
16113 | 394 |
|
16913 | 395 |
// decrypt the content |
396 |
if (box.isAEADMode()) { |
|
30904 | 397 |
// DON'T decrypt the nonce_explicit for AEAD mode |
398 |
bb.position(srcPos + nonceSize); |
|
16913 | 399 |
} // The explicit IV for CBC mode can be decrypted. |
400 |
||
16113 | 401 |
// Note that the CipherBox.decrypt() does not change |
402 |
// the capacity of the buffer. |
|
30904 | 403 |
box.decrypt(bb, tagLen); |
404 |
// We don't actually remove the nonce. |
|
405 |
bb.position(srcPos + nonceSize); |
|
16113 | 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 |
} |
|
2 | 417 |
} |
418 |
||
16913 | 419 |
// Requires message authentication code for null, stream and block |
420 |
// cipher suites. |
|
30904 | 421 |
if ((authenticator instanceof MAC) && (tagLen != 0)) { |
16913 | 422 |
MAC signer = (MAC)authenticator; |
30904 | 423 |
int contentLen = bb.remaining() - tagLen; |
16113 | 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 |
|
30904 | 435 |
contentLen = cipheredLength - tagLen; |
436 |
bb.limit(srcPos + cipheredLength); |
|
16113 | 437 |
} |
438 |
||
439 |
// Run MAC computation and comparison on the payload. |
|
30904 | 440 |
// |
441 |
// MAC data would be stripped off during the check. |
|
442 |
if (checkMacTags(contentType, bb, signer, sequence, false)) { |
|
16113 | 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 |
|
30904 | 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); |
|
16113 | 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. |
|
30904 | 468 |
checkMacTags(contentType, temporary, signer, sequence, true); |
16113 | 469 |
} |
2 | 470 |
} |
471 |
||
16113 | 472 |
// Is it a failover? |
473 |
if (reservedBPE != null) { |
|
474 |
throw reservedBPE; |
|
475 |
} |
|
30904 | 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; |
|
16113 | 533 |
} |
2 | 534 |
|
16113 | 535 |
/* |
536 |
* Run MAC computation and comparison |
|
537 |
* |
|
538 |
* Please DON'T change the content of the byte buffer parameter! |
|
539 |
*/ |
|
30904 | 540 |
private static boolean checkMacTags(byte contentType, byte[] buffer, |
16113 | 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. |
|
2 | 548 |
throw new RuntimeException("Internal MAC error"); |
549 |
} |
|
550 |
||
16113 | 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 |
|
2 | 574 |
} |
575 |
} |
|
16113 | 576 |
|
577 |
return results; |
|
2 | 578 |
} |
579 |
||
16113 | 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 |
*/ |
|
30904 | 586 |
private static int calculateRemainingLen( |
16113 | 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)) - |
|
30904 | 604 |
Math.ceil(usedLen/(1.0d * blockLen))) * blockLen; |
2 | 605 |
} |
30904 | 606 |
} |
2 | 607 |