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/AlpnExtension.java
41159 views
1
/*
2
* Copyright (c) 2015, 2021, 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.IOException;
29
import java.nio.ByteBuffer;
30
import java.nio.charset.Charset;
31
import java.security.AccessController;
32
import java.security.PrivilegedAction;
33
import java.security.Security;
34
import java.util.Arrays;
35
import java.util.Collections;
36
import java.util.LinkedList;
37
import java.util.List;
38
import javax.net.ssl.SSLEngine;
39
import javax.net.ssl.SSLProtocolException;
40
import javax.net.ssl.SSLSocket;
41
import sun.security.ssl.SSLExtension.ExtensionConsumer;
42
import sun.security.ssl.SSLExtension.SSLExtensionSpec;
43
import sun.security.ssl.SSLHandshake.HandshakeMessage;
44
45
/**
46
* Pack of the "application_layer_protocol_negotiation" extensions [RFC 7301].
47
*/
48
final class AlpnExtension {
49
static final HandshakeProducer chNetworkProducer = new CHAlpnProducer();
50
static final ExtensionConsumer chOnLoadConsumer = new CHAlpnConsumer();
51
static final HandshakeAbsence chOnLoadAbsence = new CHAlpnAbsence();
52
53
static final HandshakeProducer shNetworkProducer = new SHAlpnProducer();
54
static final ExtensionConsumer shOnLoadConsumer = new SHAlpnConsumer();
55
static final HandshakeAbsence shOnLoadAbsence = new SHAlpnAbsence();
56
57
// Note: we reuse ServerHello operations for EncryptedExtensions for now.
58
// Please be careful about any code or specification changes in the future.
59
static final HandshakeProducer eeNetworkProducer = new SHAlpnProducer();
60
static final ExtensionConsumer eeOnLoadConsumer = new SHAlpnConsumer();
61
static final HandshakeAbsence eeOnLoadAbsence = new SHAlpnAbsence();
62
63
static final SSLStringizer alpnStringizer = new AlpnStringizer();
64
65
// Encoding Charset to convert between String and byte[]
66
static final Charset alpnCharset;
67
68
static {
69
@SuppressWarnings("removal")
70
String alpnCharsetString = AccessController.doPrivileged(
71
(PrivilegedAction<String>) ()
72
-> Security.getProperty("jdk.tls.alpnCharset"));
73
if ((alpnCharsetString == null)
74
|| (alpnCharsetString.length() == 0)) {
75
alpnCharsetString = "ISO_8859_1";
76
}
77
alpnCharset = Charset.forName(alpnCharsetString);
78
}
79
80
/**
81
* The "application_layer_protocol_negotiation" extension.
82
*
83
* See RFC 7301 for the specification of this extension.
84
*/
85
static final class AlpnSpec implements SSLExtensionSpec {
86
final List<String> applicationProtocols;
87
88
private AlpnSpec(String[] applicationProtocols) {
89
this.applicationProtocols = List.of(applicationProtocols);
90
}
91
92
private AlpnSpec(HandshakeContext hc,
93
ByteBuffer buffer) throws IOException {
94
// ProtocolName protocol_name_list<2..2^16-1>, RFC 7301.
95
if (buffer.remaining() < 2) {
96
throw hc.conContext.fatal(Alert.DECODE_ERROR,
97
new SSLProtocolException(
98
"Invalid application_layer_protocol_negotiation: " +
99
"insufficient data (length=" + buffer.remaining() + ")"));
100
}
101
102
int listLen = Record.getInt16(buffer);
103
if (listLen < 2 || listLen != buffer.remaining()) {
104
throw hc.conContext.fatal(Alert.DECODE_ERROR,
105
new SSLProtocolException(
106
"Invalid application_layer_protocol_negotiation: " +
107
"incorrect list length (length=" + listLen + ")"));
108
}
109
110
List<String> protocolNames = new LinkedList<>();
111
while (buffer.hasRemaining()) {
112
// opaque ProtocolName<1..2^8-1>, RFC 7301.
113
byte[] bytes = Record.getBytes8(buffer);
114
if (bytes.length == 0) {
115
throw hc.conContext.fatal(Alert.DECODE_ERROR,
116
new SSLProtocolException(
117
"Invalid application_layer_protocol_negotiation " +
118
"extension: empty application protocol name"));
119
}
120
121
String appProtocol = new String(bytes, alpnCharset);
122
protocolNames.add(appProtocol);
123
}
124
125
this.applicationProtocols =
126
Collections.unmodifiableList(protocolNames);
127
}
128
129
@Override
130
public String toString() {
131
return applicationProtocols.toString();
132
}
133
}
134
135
private static final class AlpnStringizer implements SSLStringizer {
136
@Override
137
public String toString(HandshakeContext hc, ByteBuffer buffer) {
138
try {
139
return (new AlpnSpec(hc, buffer)).toString();
140
} catch (IOException ioe) {
141
// For debug logging only, so please swallow exceptions.
142
return ioe.getMessage();
143
}
144
}
145
}
146
147
/**
148
* Network data producer of the extension in a ClientHello
149
* handshake message.
150
*/
151
private static final class CHAlpnProducer implements HandshakeProducer {
152
static final int MAX_AP_LENGTH = 255;
153
static final int MAX_AP_LIST_LENGTH = 65535;
154
155
// Prevent instantiation of this class.
156
private CHAlpnProducer() {
157
// blank
158
}
159
160
@Override
161
public byte[] produce(ConnectionContext context,
162
HandshakeMessage message) throws IOException {
163
// The producing happens in client side only.
164
ClientHandshakeContext chc = (ClientHandshakeContext)context;
165
166
// Is it a supported and enabled extension?
167
if (!chc.sslConfig.isAvailable(SSLExtension.CH_ALPN)) {
168
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
169
SSLLogger.info(
170
"Ignore client unavailable extension: " +
171
SSLExtension.CH_ALPN.name);
172
}
173
174
chc.applicationProtocol = "";
175
chc.conContext.applicationProtocol = "";
176
return null;
177
}
178
179
String[] laps = chc.sslConfig.applicationProtocols;
180
if ((laps == null) || (laps.length == 0)) {
181
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
182
SSLLogger.info(
183
"No available application protocols");
184
}
185
return null;
186
}
187
188
// Produce the extension: first find the overall length
189
int listLength = 0; // ProtocolNameList length
190
for (String ap : laps) {
191
int length = ap.getBytes(alpnCharset).length;
192
if (length == 0) {
193
// log the configuration problem
194
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
195
SSLLogger.severe(
196
"Application protocol name cannot be empty");
197
}
198
199
throw chc.conContext.fatal(Alert.ILLEGAL_PARAMETER,
200
"Application protocol name cannot be empty");
201
}
202
203
if (length <= MAX_AP_LENGTH) {
204
// opaque ProtocolName<1..2^8-1>, RFC 7301.
205
listLength += (length + 1);
206
} else {
207
// log the configuration problem
208
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
209
SSLLogger.severe(
210
"Application protocol name (" + ap +
211
") exceeds the size limit (" +
212
MAX_AP_LENGTH + " bytes)");
213
}
214
215
throw chc.conContext.fatal(Alert.ILLEGAL_PARAMETER,
216
"Application protocol name (" + ap +
217
") exceeds the size limit (" +
218
MAX_AP_LENGTH + " bytes)");
219
}
220
221
if (listLength > MAX_AP_LIST_LENGTH) {
222
// log the configuration problem
223
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
224
SSLLogger.severe(
225
"The configured application protocols (" +
226
Arrays.toString(laps) +
227
") exceed the size limit (" +
228
MAX_AP_LIST_LENGTH + " bytes)");
229
}
230
231
throw chc.conContext.fatal(Alert.ILLEGAL_PARAMETER,
232
"The configured application protocols (" +
233
Arrays.toString(laps) +
234
") exceed the size limit (" +
235
MAX_AP_LIST_LENGTH + " bytes)");
236
}
237
}
238
239
// ProtocolName protocol_name_list<2..2^16-1>, RFC 7301.
240
byte[] extData = new byte[listLength + 2];
241
ByteBuffer m = ByteBuffer.wrap(extData);
242
Record.putInt16(m, listLength);
243
244
// opaque ProtocolName<1..2^8-1>;
245
for (String ap : laps) {
246
Record.putBytes8(m, ap.getBytes(alpnCharset));
247
}
248
249
// Update the context.
250
chc.handshakeExtensions.put(SSLExtension.CH_ALPN,
251
new AlpnSpec(chc.sslConfig.applicationProtocols));
252
253
return extData;
254
}
255
}
256
257
/**
258
* Network data consumer of the extension in a ClientHello
259
* handshake message.
260
*/
261
private static final class CHAlpnConsumer implements ExtensionConsumer {
262
// Prevent instantiation of this class.
263
private CHAlpnConsumer() {
264
// blank
265
}
266
267
@Override
268
public void consume(ConnectionContext context,
269
HandshakeMessage message, ByteBuffer buffer) throws IOException {
270
// The consuming happens in server side only.
271
ServerHandshakeContext shc = (ServerHandshakeContext)context;
272
273
// Is it a supported and enabled extension?
274
if (!shc.sslConfig.isAvailable(SSLExtension.CH_ALPN)) {
275
shc.applicationProtocol = "";
276
shc.conContext.applicationProtocol = "";
277
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
278
SSLLogger.info(
279
"Ignore server unavailable extension: " +
280
SSLExtension.CH_ALPN.name);
281
}
282
return; // ignore the extension
283
}
284
285
// Is the extension enabled?
286
boolean noAPSelector;
287
if (shc.conContext.transport instanceof SSLEngine) {
288
noAPSelector = (shc.sslConfig.engineAPSelector == null);
289
} else {
290
noAPSelector = (shc.sslConfig.socketAPSelector == null);
291
}
292
293
boolean noAlpnProtocols =
294
shc.sslConfig.applicationProtocols == null ||
295
shc.sslConfig.applicationProtocols.length == 0;
296
if (noAPSelector && noAlpnProtocols) {
297
shc.applicationProtocol = "";
298
shc.conContext.applicationProtocol = "";
299
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
300
SSLLogger.fine(
301
"Ignore server unenabled extension: " +
302
SSLExtension.CH_ALPN.name);
303
}
304
return; // ignore the extension
305
}
306
307
// Parse the extension.
308
AlpnSpec spec = new AlpnSpec(shc, buffer);
309
310
// Update the context.
311
if (noAPSelector) { // noAlpnProtocols is false
312
List<String> protocolNames = spec.applicationProtocols;
313
boolean matched = false;
314
// Use server application protocol preference order.
315
for (String ap : shc.sslConfig.applicationProtocols) {
316
if (protocolNames.contains(ap)) {
317
shc.applicationProtocol = ap;
318
shc.conContext.applicationProtocol = ap;
319
matched = true;
320
break;
321
}
322
}
323
324
if (!matched) {
325
throw shc.conContext.fatal(Alert.NO_APPLICATION_PROTOCOL,
326
"No matching application layer protocol values");
327
}
328
} // Otherwise, applicationProtocol will be set by the
329
// application selector callback later.
330
331
shc.handshakeExtensions.put(SSLExtension.CH_ALPN, spec);
332
333
// No impact on session resumption.
334
//
335
// [RFC 7301] Unlike many other TLS extensions, this extension
336
// does not establish properties of the session, only of the
337
// connection. When session resumption or session tickets are
338
// used, the previous contents of this extension are irrelevant,
339
// and only the values in the new handshake messages are
340
// considered.
341
}
342
}
343
344
/**
345
* The absence processing if the extension is not present in
346
* a ClientHello handshake message.
347
*/
348
private static final class CHAlpnAbsence implements HandshakeAbsence {
349
@Override
350
public void absent(ConnectionContext context,
351
HandshakeMessage message) throws IOException {
352
// The producing happens in server side only.
353
ServerHandshakeContext shc = (ServerHandshakeContext)context;
354
355
// Please don't use the previous negotiated application protocol.
356
shc.applicationProtocol = "";
357
shc.conContext.applicationProtocol = "";
358
}
359
}
360
361
/**
362
* Network data producer of the extension in the ServerHello
363
* handshake message.
364
*/
365
private static final class SHAlpnProducer implements HandshakeProducer {
366
// Prevent instantiation of this class.
367
private SHAlpnProducer() {
368
// blank
369
}
370
371
@Override
372
public byte[] produce(ConnectionContext context,
373
HandshakeMessage message) throws IOException {
374
// The producing happens in client side only.
375
ServerHandshakeContext shc = (ServerHandshakeContext)context;
376
377
// In response to ALPN request only
378
AlpnSpec requestedAlps =
379
(AlpnSpec)shc.handshakeExtensions.get(SSLExtension.CH_ALPN);
380
if (requestedAlps == null) {
381
// Ignore, this extension was not requested and accepted.
382
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
383
SSLLogger.fine(
384
"Ignore unavailable extension: " +
385
SSLExtension.SH_ALPN.name);
386
}
387
388
shc.applicationProtocol = "";
389
shc.conContext.applicationProtocol = "";
390
return null;
391
}
392
393
List<String> alps = requestedAlps.applicationProtocols;
394
if (shc.conContext.transport instanceof SSLEngine) {
395
if (shc.sslConfig.engineAPSelector != null) {
396
SSLEngine engine = (SSLEngine)shc.conContext.transport;
397
shc.applicationProtocol =
398
shc.sslConfig.engineAPSelector.apply(engine, alps);
399
if ((shc.applicationProtocol == null) ||
400
(!shc.applicationProtocol.isEmpty() &&
401
!alps.contains(shc.applicationProtocol))) {
402
throw shc.conContext.fatal(
403
Alert.NO_APPLICATION_PROTOCOL,
404
"No matching application layer protocol values");
405
}
406
}
407
} else {
408
if (shc.sslConfig.socketAPSelector != null) {
409
SSLSocket socket = (SSLSocket)shc.conContext.transport;
410
shc.applicationProtocol =
411
shc.sslConfig.socketAPSelector.apply(socket, alps);
412
if ((shc.applicationProtocol == null) ||
413
(!shc.applicationProtocol.isEmpty() &&
414
!alps.contains(shc.applicationProtocol))) {
415
throw shc.conContext.fatal(
416
Alert.NO_APPLICATION_PROTOCOL,
417
"No matching application layer protocol values");
418
}
419
}
420
}
421
422
if ((shc.applicationProtocol == null) ||
423
(shc.applicationProtocol.isEmpty())) {
424
// Ignore, no negotiated application layer protocol.
425
shc.applicationProtocol = "";
426
shc.conContext.applicationProtocol = "";
427
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
428
SSLLogger.warning(
429
"Ignore, no negotiated application layer protocol");
430
}
431
432
return null;
433
}
434
435
// opaque ProtocolName<1..2^8-1>, RFC 7301.
436
byte[] bytes = shc.applicationProtocol.getBytes(alpnCharset);
437
int listLen = bytes.length + 1; // 1: length byte
438
439
// ProtocolName protocol_name_list<2..2^16-1>, RFC 7301.
440
byte[] extData = new byte[listLen + 2]; // 2: list length
441
ByteBuffer m = ByteBuffer.wrap(extData);
442
Record.putInt16(m, listLen);
443
Record.putBytes8(m, bytes);
444
445
// Update the context.
446
shc.conContext.applicationProtocol = shc.applicationProtocol;
447
448
// Clean or register the extension
449
//
450
// No further use of the request and respond extension any more.
451
shc.handshakeExtensions.remove(SSLExtension.CH_ALPN);
452
453
return extData;
454
}
455
}
456
457
/**
458
* Network data consumer of the extension in the ServerHello
459
* handshake message.
460
*/
461
private static final class SHAlpnConsumer implements ExtensionConsumer {
462
// Prevent instantiation of this class.
463
private SHAlpnConsumer() {
464
// blank
465
}
466
467
@Override
468
public void consume(ConnectionContext context,
469
HandshakeMessage message, ByteBuffer buffer) throws IOException {
470
// The producing happens in client side only.
471
ClientHandshakeContext chc = (ClientHandshakeContext)context;
472
473
// In response to ALPN request only
474
AlpnSpec requestedAlps =
475
(AlpnSpec)chc.handshakeExtensions.get(SSLExtension.CH_ALPN);
476
if (requestedAlps == null ||
477
requestedAlps.applicationProtocols == null ||
478
requestedAlps.applicationProtocols.isEmpty()) {
479
throw chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE,
480
"Unexpected " + SSLExtension.CH_ALPN.name + " extension");
481
}
482
483
// Parse the extension.
484
AlpnSpec spec = new AlpnSpec(chc, buffer);
485
486
// Only one application protocol is allowed.
487
if (spec.applicationProtocols.size() != 1) {
488
throw chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE,
489
"Invalid " + SSLExtension.CH_ALPN.name + " extension: " +
490
"Only one application protocol name " +
491
"is allowed in ServerHello message");
492
}
493
494
// The respond application protocol must be one of the requested.
495
if (!requestedAlps.applicationProtocols.containsAll(
496
spec.applicationProtocols)) {
497
throw chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE,
498
"Invalid " + SSLExtension.CH_ALPN.name + " extension: " +
499
"Only client specified application protocol " +
500
"is allowed in ServerHello message");
501
}
502
503
// Update the context.
504
chc.applicationProtocol = spec.applicationProtocols.get(0);
505
chc.conContext.applicationProtocol = chc.applicationProtocol;
506
507
// Clean or register the extension
508
//
509
// No further use of the request and respond extension any more.
510
chc.handshakeExtensions.remove(SSLExtension.CH_ALPN);
511
}
512
}
513
514
/**
515
* The absence processing if the extension is not present in
516
* the ServerHello handshake message.
517
*/
518
private static final class SHAlpnAbsence implements HandshakeAbsence {
519
@Override
520
public void absent(ConnectionContext context,
521
HandshakeMessage message) throws IOException {
522
// The producing happens in client side only.
523
ClientHandshakeContext chc = (ClientHandshakeContext)context;
524
525
// Please don't use the previous negotiated application protocol.
526
chc.applicationProtocol = "";
527
chc.conContext.applicationProtocol = "";
528
}
529
}
530
}
531
532