Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/mobile
Path: blob/master/src/java.base/share/classes/sun/security/ssl/HandshakeHash.java
41159 views
1
/*
2
* Copyright (c) 2003, 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.IOException;
30
import java.nio.ByteBuffer;
31
import java.security.MessageDigest;
32
import java.security.NoSuchAlgorithmException;
33
import java.util.Arrays;
34
import java.util.LinkedList;
35
import javax.crypto.SecretKey;
36
import sun.security.util.MessageDigestSpi2;
37
38
final class HandshakeHash {
39
private TranscriptHash transcriptHash;
40
private LinkedList<byte[]> reserves; // one handshake message per entry
41
private boolean hasBeenUsed;
42
43
HandshakeHash() {
44
this.transcriptHash = new CacheOnlyHash();
45
this.reserves = new LinkedList<>();
46
this.hasBeenUsed = false;
47
}
48
49
// fix the negotiated protocol version and cipher suite
50
void determine(ProtocolVersion protocolVersion,
51
CipherSuite cipherSuite) {
52
if (!(transcriptHash instanceof CacheOnlyHash)) {
53
throw new IllegalStateException(
54
"Not expected instance of transcript hash");
55
}
56
57
CacheOnlyHash coh = (CacheOnlyHash)transcriptHash;
58
if (protocolVersion.useTLS13PlusSpec()) {
59
transcriptHash = new T13HandshakeHash(cipherSuite);
60
} else if (protocolVersion.useTLS12PlusSpec()) {
61
transcriptHash = new T12HandshakeHash(cipherSuite);
62
} else if (protocolVersion.useTLS10PlusSpec()) {
63
transcriptHash = new T10HandshakeHash(cipherSuite);
64
} else {
65
transcriptHash = new S30HandshakeHash(cipherSuite);
66
}
67
68
byte[] reserved = coh.baos.toByteArray();
69
if (reserved.length != 0) {
70
transcriptHash.update(reserved, 0, reserved.length);
71
}
72
}
73
74
HandshakeHash copy() {
75
if (transcriptHash instanceof CacheOnlyHash) {
76
HandshakeHash result = new HandshakeHash();
77
result.transcriptHash = ((CacheOnlyHash)transcriptHash).copy();
78
result.reserves = new LinkedList<>(reserves);
79
result.hasBeenUsed = hasBeenUsed;
80
return result;
81
} else {
82
throw new IllegalStateException("Hash does not support copying");
83
}
84
}
85
86
void receive(byte[] input) {
87
reserves.add(Arrays.copyOf(input, input.length));
88
}
89
90
void receive(ByteBuffer input, int length) {
91
if (input.hasArray()) {
92
int from = input.position() + input.arrayOffset();
93
int to = from + length;
94
reserves.add(Arrays.copyOfRange(input.array(), from, to));
95
} else {
96
int inPos = input.position();
97
byte[] holder = new byte[length];
98
input.get(holder);
99
input.position(inPos);
100
reserves.add(Arrays.copyOf(holder, holder.length));
101
}
102
}
103
void receive(ByteBuffer input) {
104
receive(input, input.remaining());
105
}
106
107
// For HelloRetryRequest only! Please use this method very carefully!
108
void push(byte[] input) {
109
reserves.push(Arrays.copyOf(input, input.length));
110
}
111
112
// For PreSharedKey to modify the state of the PSK binder hash
113
byte[] removeLastReceived() {
114
return reserves.removeLast();
115
}
116
117
void deliver(byte[] input) {
118
update();
119
transcriptHash.update(input, 0, input.length);
120
}
121
122
void deliver(byte[] input, int offset, int length) {
123
update();
124
transcriptHash.update(input, offset, length);
125
}
126
127
void deliver(ByteBuffer input) {
128
update();
129
if (input.hasArray()) {
130
transcriptHash.update(input.array(),
131
input.position() + input.arrayOffset(), input.remaining());
132
} else {
133
int inPos = input.position();
134
byte[] holder = new byte[input.remaining()];
135
input.get(holder);
136
input.position(inPos);
137
transcriptHash.update(holder, 0, holder.length);
138
}
139
}
140
141
// Use one handshake message if it has not been used.
142
void utilize() {
143
if (hasBeenUsed) {
144
return;
145
}
146
if (reserves.size() != 0) {
147
byte[] holder = reserves.remove();
148
transcriptHash.update(holder, 0, holder.length);
149
hasBeenUsed = true;
150
}
151
}
152
153
// Consume one handshake message if it has not been consumed.
154
void consume() {
155
if (hasBeenUsed) {
156
hasBeenUsed = false;
157
return;
158
}
159
if (reserves.size() != 0) {
160
byte[] holder = reserves.remove();
161
transcriptHash.update(holder, 0, holder.length);
162
}
163
}
164
165
void update() {
166
while (reserves.size() != 0) {
167
byte[] holder = reserves.remove();
168
transcriptHash.update(holder, 0, holder.length);
169
}
170
hasBeenUsed = false;
171
}
172
173
byte[] digest() {
174
// Note that the reserve handshake message may be not a part of
175
// the expected digest.
176
return transcriptHash.digest();
177
}
178
179
void finish() {
180
this.transcriptHash = new CacheOnlyHash();
181
this.reserves = new LinkedList<>();
182
this.hasBeenUsed = false;
183
}
184
185
// Optional
186
byte[] archived() {
187
// Note that the reserve handshake message may be not a part of
188
// the expected digest.
189
return transcriptHash.archived();
190
}
191
192
// Optional, TLS 1.0/1.1 only
193
byte[] digest(String algorithm) {
194
T10HandshakeHash hh = (T10HandshakeHash)transcriptHash;
195
return hh.digest(algorithm);
196
}
197
198
// Optional, SSL 3.0 only
199
byte[] digest(String algorithm, SecretKey masterSecret) {
200
S30HandshakeHash hh = (S30HandshakeHash)transcriptHash;
201
return hh.digest(algorithm, masterSecret);
202
}
203
204
// Optional, SSL 3.0 only
205
byte[] digest(boolean useClientLabel, SecretKey masterSecret) {
206
S30HandshakeHash hh = (S30HandshakeHash)transcriptHash;
207
return hh.digest(useClientLabel, masterSecret);
208
}
209
210
public boolean isHashable(byte handshakeType) {
211
return handshakeType != SSLHandshake.HELLO_REQUEST.id &&
212
handshakeType != SSLHandshake.HELLO_VERIFY_REQUEST.id;
213
}
214
215
interface TranscriptHash {
216
void update(byte[] input, int offset, int length);
217
byte[] digest();
218
byte[] archived(); // optional
219
}
220
221
// For cache only.
222
private static final class CacheOnlyHash implements TranscriptHash {
223
private final ByteArrayOutputStream baos;
224
225
CacheOnlyHash() {
226
this.baos = new ByteArrayOutputStream();
227
}
228
229
@Override
230
public void update(byte[] input, int offset, int length) {
231
baos.write(input, offset, length);
232
}
233
234
@Override
235
public byte[] digest() {
236
throw new IllegalStateException(
237
"Not expected call to handshake hash digest");
238
}
239
240
@Override
241
public byte[] archived() {
242
return baos.toByteArray();
243
}
244
245
CacheOnlyHash copy() {
246
CacheOnlyHash result = new CacheOnlyHash();
247
try {
248
baos.writeTo(result.baos);
249
} catch (IOException ex) {
250
throw new RuntimeException("unable to clone hash state");
251
}
252
return result;
253
}
254
}
255
256
static final class S30HandshakeHash implements TranscriptHash {
257
static final byte[] MD5_pad1 = genPad(0x36, 48);
258
static final byte[] MD5_pad2 = genPad(0x5c, 48);
259
260
static final byte[] SHA_pad1 = genPad(0x36, 40);
261
static final byte[] SHA_pad2 = genPad(0x5c, 40);
262
263
private static final byte[] SSL_CLIENT = { 0x43, 0x4C, 0x4E, 0x54 };
264
private static final byte[] SSL_SERVER = { 0x53, 0x52, 0x56, 0x52 };
265
266
private final MessageDigest mdMD5;
267
private final MessageDigest mdSHA;
268
private final TranscriptHash md5;
269
private final TranscriptHash sha;
270
private final ByteArrayOutputStream baos;
271
272
S30HandshakeHash(CipherSuite cipherSuite) {
273
try {
274
this.mdMD5 = MessageDigest.getInstance("MD5");
275
this.mdSHA = MessageDigest.getInstance("SHA");
276
} catch (NoSuchAlgorithmException nsae) {
277
throw new RuntimeException(
278
"Hash algorithm MD5 or SHA is not available", nsae);
279
}
280
281
boolean hasArchived = false;
282
if (mdMD5 instanceof Cloneable) {
283
md5 = new CloneableHash(mdMD5);
284
} else {
285
hasArchived = true;
286
md5 = new NonCloneableHash(mdMD5);
287
}
288
if (mdSHA instanceof Cloneable) {
289
sha = new CloneableHash(mdSHA);
290
} else {
291
hasArchived = true;
292
sha = new NonCloneableHash(mdSHA);
293
}
294
295
if (hasArchived) {
296
this.baos = null;
297
} else {
298
this.baos = new ByteArrayOutputStream();
299
}
300
}
301
302
@Override
303
public void update(byte[] input, int offset, int length) {
304
md5.update(input, offset, length);
305
sha.update(input, offset, length);
306
if (baos != null) {
307
baos.write(input, offset, length);
308
}
309
}
310
311
@Override
312
public byte[] digest() {
313
byte[] digest = new byte[36];
314
System.arraycopy(md5.digest(), 0, digest, 0, 16);
315
System.arraycopy(sha.digest(), 0, digest, 16, 20);
316
317
return digest;
318
}
319
320
@Override
321
public byte[] archived() {
322
if (baos != null) {
323
return baos.toByteArray();
324
} else if (md5 instanceof NonCloneableHash) {
325
return md5.archived();
326
} else {
327
return sha.archived();
328
}
329
}
330
331
byte[] digest(boolean useClientLabel, SecretKey masterSecret) {
332
MessageDigest md5Clone = cloneMd5();
333
MessageDigest shaClone = cloneSha();
334
335
if (useClientLabel) {
336
md5Clone.update(SSL_CLIENT);
337
shaClone.update(SSL_CLIENT);
338
} else {
339
md5Clone.update(SSL_SERVER);
340
shaClone.update(SSL_SERVER);
341
}
342
343
updateDigest(md5Clone, MD5_pad1, MD5_pad2, masterSecret);
344
updateDigest(shaClone, SHA_pad1, SHA_pad2, masterSecret);
345
346
byte[] digest = new byte[36];
347
System.arraycopy(md5Clone.digest(), 0, digest, 0, 16);
348
System.arraycopy(shaClone.digest(), 0, digest, 16, 20);
349
350
return digest;
351
}
352
353
byte[] digest(String algorithm, SecretKey masterSecret) {
354
if ("RSA".equalsIgnoreCase(algorithm)) {
355
MessageDigest md5Clone = cloneMd5();
356
MessageDigest shaClone = cloneSha();
357
updateDigest(md5Clone, MD5_pad1, MD5_pad2, masterSecret);
358
updateDigest(shaClone, SHA_pad1, SHA_pad2, masterSecret);
359
360
byte[] digest = new byte[36];
361
System.arraycopy(md5Clone.digest(), 0, digest, 0, 16);
362
System.arraycopy(shaClone.digest(), 0, digest, 16, 20);
363
364
return digest;
365
} else {
366
MessageDigest shaClone = cloneSha();
367
updateDigest(shaClone, SHA_pad1, SHA_pad2, masterSecret);
368
return shaClone.digest();
369
}
370
}
371
372
private static byte[] genPad(int b, int count) {
373
byte[] padding = new byte[count];
374
Arrays.fill(padding, (byte)b);
375
return padding;
376
}
377
378
private MessageDigest cloneMd5() {
379
MessageDigest md5Clone;
380
if (mdMD5 instanceof Cloneable) {
381
try {
382
md5Clone = (MessageDigest)mdMD5.clone();
383
} catch (CloneNotSupportedException ex) { // unlikely
384
throw new RuntimeException(
385
"MessageDigest does no support clone operation");
386
}
387
} else {
388
try {
389
md5Clone = MessageDigest.getInstance("MD5");
390
} catch (NoSuchAlgorithmException nsae) {
391
throw new RuntimeException(
392
"Hash algorithm MD5 is not available", nsae);
393
}
394
md5Clone.update(md5.archived());
395
}
396
397
return md5Clone;
398
}
399
400
private MessageDigest cloneSha() {
401
MessageDigest shaClone;
402
if (mdSHA instanceof Cloneable) {
403
try {
404
shaClone = (MessageDigest)mdSHA.clone();
405
} catch (CloneNotSupportedException ex) { // unlikely
406
throw new RuntimeException(
407
"MessageDigest does no support clone operation");
408
}
409
} else {
410
try {
411
shaClone = MessageDigest.getInstance("SHA");
412
} catch (NoSuchAlgorithmException nsae) {
413
throw new RuntimeException(
414
"Hash algorithm SHA is not available", nsae);
415
}
416
shaClone.update(sha.archived());
417
}
418
419
return shaClone;
420
}
421
422
private static void updateDigest(MessageDigest md,
423
byte[] pad1, byte[] pad2, SecretKey masterSecret) {
424
byte[] keyBytes = "RAW".equals(masterSecret.getFormat())
425
? masterSecret.getEncoded() : null;
426
if (keyBytes != null) {
427
md.update(keyBytes);
428
} else {
429
digestKey(md, masterSecret);
430
}
431
md.update(pad1);
432
byte[] temp = md.digest();
433
434
if (keyBytes != null) {
435
md.update(keyBytes);
436
} else {
437
digestKey(md, masterSecret);
438
}
439
md.update(pad2);
440
md.update(temp);
441
}
442
443
private static void digestKey(MessageDigest md, SecretKey key) {
444
try {
445
if (md instanceof MessageDigestSpi2) {
446
((MessageDigestSpi2)md).engineUpdate(key);
447
} else {
448
throw new Exception(
449
"Digest does not support implUpdate(SecretKey)");
450
}
451
} catch (Exception e) {
452
throw new RuntimeException(
453
"Could not obtain encoded key and "
454
+ "MessageDigest cannot digest key", e);
455
}
456
}
457
}
458
459
// TLS 1.0 and TLS 1.1
460
static final class T10HandshakeHash implements TranscriptHash {
461
private final TranscriptHash md5;
462
private final TranscriptHash sha;
463
private final ByteArrayOutputStream baos;
464
465
T10HandshakeHash(CipherSuite cipherSuite) {
466
MessageDigest mdMD5;
467
MessageDigest mdSHA;
468
try {
469
mdMD5 = MessageDigest.getInstance("MD5");
470
mdSHA = MessageDigest.getInstance("SHA");
471
} catch (NoSuchAlgorithmException nsae) {
472
throw new RuntimeException(
473
"Hash algorithm MD5 or SHA is not available", nsae);
474
}
475
476
boolean hasArchived = false;
477
if (mdMD5 instanceof Cloneable) {
478
md5 = new CloneableHash(mdMD5);
479
} else {
480
hasArchived = true;
481
md5 = new NonCloneableHash(mdMD5);
482
}
483
if (mdSHA instanceof Cloneable) {
484
sha = new CloneableHash(mdSHA);
485
} else {
486
hasArchived = true;
487
sha = new NonCloneableHash(mdSHA);
488
}
489
490
if (hasArchived) {
491
this.baos = null;
492
} else {
493
this.baos = new ByteArrayOutputStream();
494
}
495
}
496
497
@Override
498
public void update(byte[] input, int offset, int length) {
499
md5.update(input, offset, length);
500
sha.update(input, offset, length);
501
if (baos != null) {
502
baos.write(input, offset, length);
503
}
504
}
505
506
@Override
507
public byte[] digest() {
508
byte[] digest = new byte[36];
509
System.arraycopy(md5.digest(), 0, digest, 0, 16);
510
System.arraycopy(sha.digest(), 0, digest, 16, 20);
511
512
return digest;
513
}
514
515
byte[] digest(String algorithm) {
516
if ("RSA".equalsIgnoreCase(algorithm)) {
517
return digest();
518
} else {
519
return sha.digest();
520
}
521
}
522
523
@Override
524
public byte[] archived() {
525
if (baos != null) {
526
return baos.toByteArray();
527
} else if (md5 instanceof NonCloneableHash) {
528
return md5.archived();
529
} else {
530
return sha.archived();
531
}
532
}
533
}
534
535
static final class T12HandshakeHash implements TranscriptHash {
536
private final TranscriptHash transcriptHash;
537
private final ByteArrayOutputStream baos;
538
539
T12HandshakeHash(CipherSuite cipherSuite) {
540
MessageDigest md;
541
try {
542
md = MessageDigest.getInstance(cipherSuite.hashAlg.name);
543
} catch (NoSuchAlgorithmException nsae) {
544
throw new RuntimeException(
545
"Hash algorithm " +
546
cipherSuite.hashAlg.name + " is not available", nsae);
547
}
548
549
if (md instanceof Cloneable) {
550
transcriptHash = new CloneableHash(md);
551
this.baos = new ByteArrayOutputStream();
552
} else {
553
transcriptHash = new NonCloneableHash(md);
554
this.baos = null;
555
}
556
}
557
558
@Override
559
public void update(byte[] input, int offset, int length) {
560
transcriptHash.update(input, offset, length);
561
if (baos != null) {
562
baos.write(input, offset, length);
563
}
564
}
565
566
@Override
567
public byte[] digest() {
568
return transcriptHash.digest();
569
}
570
571
@Override
572
public byte[] archived() {
573
if (baos != null) {
574
return baos.toByteArray();
575
} else {
576
return transcriptHash.archived();
577
}
578
}
579
}
580
581
static final class T13HandshakeHash implements TranscriptHash {
582
private final TranscriptHash transcriptHash;
583
584
T13HandshakeHash(CipherSuite cipherSuite) {
585
MessageDigest md;
586
try {
587
md = MessageDigest.getInstance(cipherSuite.hashAlg.name);
588
} catch (NoSuchAlgorithmException nsae) {
589
throw new RuntimeException(
590
"Hash algorithm " +
591
cipherSuite.hashAlg.name + " is not available", nsae);
592
}
593
594
if (md instanceof Cloneable) {
595
transcriptHash = new CloneableHash(md);
596
} else {
597
transcriptHash = new NonCloneableHash(md);
598
}
599
}
600
601
@Override
602
public void update(byte[] input, int offset, int length) {
603
transcriptHash.update(input, offset, length);
604
}
605
606
@Override
607
public byte[] digest() {
608
return transcriptHash.digest();
609
}
610
611
@Override
612
public byte[] archived() {
613
// This method is not necessary in T13
614
throw new UnsupportedOperationException(
615
"TLS 1.3 does not require archived.");
616
}
617
}
618
619
static final class CloneableHash implements TranscriptHash {
620
private final MessageDigest md;
621
622
CloneableHash(MessageDigest md) {
623
this.md = md;
624
}
625
626
@Override
627
public void update(byte[] input, int offset, int length) {
628
md.update(input, offset, length);
629
}
630
631
@Override
632
public byte[] digest() {
633
try {
634
return ((MessageDigest)md.clone()).digest();
635
} catch (CloneNotSupportedException ex) {
636
// unlikely
637
return new byte[0];
638
}
639
}
640
641
@Override
642
public byte[] archived() {
643
throw new UnsupportedOperationException("Not supported yet.");
644
}
645
}
646
647
static final class NonCloneableHash implements TranscriptHash {
648
private final MessageDigest md;
649
private final ByteArrayOutputStream baos = new ByteArrayOutputStream();
650
651
NonCloneableHash(MessageDigest md) {
652
this.md = md;
653
}
654
655
@Override
656
public void update(byte[] input, int offset, int length) {
657
baos.write(input, offset, length);
658
}
659
660
@Override
661
public byte[] digest() {
662
byte[] bytes = baos.toByteArray();
663
md.reset();
664
return md.digest(bytes);
665
}
666
667
@Override
668
public byte[] archived() {
669
return baos.toByteArray();
670
}
671
}
672
}
673
674