• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2017 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package org.conscrypt;
18 
19 import static org.conscrypt.Preconditions.checkArgument;
20 import static org.conscrypt.Preconditions.checkNotNull;
21 
22 import java.io.FileDescriptor;
23 import java.io.IOException;
24 import java.io.InputStream;
25 import java.io.OutputStream;
26 import java.net.InetAddress;
27 import java.net.InetSocketAddress;
28 import java.net.Socket;
29 import java.net.SocketAddress;
30 import java.net.SocketException;
31 import java.nio.channels.SocketChannel;
32 import java.security.PrivateKey;
33 import java.util.ArrayList;
34 import java.util.List;
35 import javax.net.ssl.HandshakeCompletedEvent;
36 import javax.net.ssl.HandshakeCompletedListener;
37 import javax.net.ssl.SSLException;
38 import javax.net.ssl.SSLSession;
39 import javax.net.ssl.SSLSocket;
40 
41 /**
42  * Abstract base class for all Conscrypt {@link SSLSocket} classes.
43  */
44 abstract class AbstractConscryptSocket extends SSLSocket {
45     final Socket socket;
46     private final boolean autoClose;
47 
48     /**
49      * The peer's DNS hostname if it was supplied during creation. Note that
50      * this may be a raw IP address, so it should be checked before use with
51      * extensions that don't use it like Server Name Indication (SNI).
52      */
53     private String peerHostname;
54 
55     /**
56      * The peer's port if it was supplied during creation. Should only be set if
57      * {@link #peerHostname} is also set.
58      */
59     private final int peerPort;
60 
61     private final PeerInfoProvider peerInfoProvider = new PeerInfoProvider() {
62         @Override
63         String getHostname() {
64             return AbstractConscryptSocket.this.getHostname();
65         }
66 
67         @Override
68         String getHostnameOrIP() {
69             return AbstractConscryptSocket.this.getHostnameOrIP();
70         }
71 
72         @Override
73         int getPort() {
74             return AbstractConscryptSocket.this.getPort();
75         }
76     };
77 
78     private final List<HandshakeCompletedListener> listeners =
79             new ArrayList<HandshakeCompletedListener>(2);
80 
81     /**
82      * Local cache of timeout to avoid getsockopt on every read and
83      * write for non-wrapped sockets. Note that this is not used when delegating
84      * to another socket.
85      */
86     private int readTimeoutMilliseconds;
87 
AbstractConscryptSocket()88     AbstractConscryptSocket() throws IOException {
89         this.socket = this;
90         this.peerHostname = null;
91         this.peerPort = -1;
92         this.autoClose = false;
93     }
94 
AbstractConscryptSocket(String hostname, int port)95     AbstractConscryptSocket(String hostname, int port) throws IOException {
96         super(hostname, port);
97         this.socket = this;
98         this.peerHostname = hostname;
99         this.peerPort = port;
100         this.autoClose = false;
101     }
102 
AbstractConscryptSocket(InetAddress address, int port)103     AbstractConscryptSocket(InetAddress address, int port) throws IOException {
104         super(address, port);
105         this.socket = this;
106         this.peerHostname = null;
107         this.peerPort = -1;
108         this.autoClose = false;
109     }
110 
AbstractConscryptSocket(String hostname, int port, InetAddress clientAddress, int clientPort)111     AbstractConscryptSocket(String hostname, int port, InetAddress clientAddress, int clientPort)
112             throws IOException {
113         super(hostname, port, clientAddress, clientPort);
114         this.socket = this;
115         this.peerHostname = hostname;
116         this.peerPort = port;
117         this.autoClose = false;
118     }
119 
AbstractConscryptSocket(InetAddress address, int port, InetAddress clientAddress, int clientPort)120     AbstractConscryptSocket(InetAddress address, int port, InetAddress clientAddress,
121             int clientPort) throws IOException {
122         super(address, port, clientAddress, clientPort);
123         this.socket = this;
124         this.peerHostname = null;
125         this.peerPort = -1;
126         this.autoClose = false;
127     }
128 
AbstractConscryptSocket(Socket socket, String hostname, int port, boolean autoClose)129     AbstractConscryptSocket(Socket socket, String hostname, int port, boolean autoClose)
130             throws IOException {
131         this.socket = checkNotNull(socket, "socket");
132         this.peerHostname = hostname;
133         this.peerPort = port;
134         this.autoClose = autoClose;
135     }
136 
137     @Override
connect(SocketAddress endpoint)138     public final void connect(SocketAddress endpoint) throws IOException {
139         connect(endpoint, 0);
140     }
141 
142     /**
143      * Try to extract the peer's hostname if it's available from the endpoint address.
144      */
145     @Override
connect(SocketAddress endpoint, int timeout)146     public final void connect(SocketAddress endpoint, int timeout) throws IOException {
147         if (peerHostname == null && endpoint instanceof InetSocketAddress) {
148             peerHostname =
149                     Platform.getHostStringFromInetSocketAddress((InetSocketAddress) endpoint);
150         }
151 
152         if (isDelegating()) {
153             socket.connect(endpoint, timeout);
154         } else {
155             super.connect(endpoint, timeout);
156         }
157     }
158 
159     @Override
bind(SocketAddress bindpoint)160     public void bind(SocketAddress bindpoint) throws IOException {
161         if (isDelegating()) {
162             socket.bind(bindpoint);
163         } else {
164             super.bind(bindpoint);
165         }
166     }
167 
168     @Override
169     @SuppressWarnings("UnsynchronizedOverridesSynchronized")
close()170     public void close() throws IOException {
171         if (isDelegating()) {
172             if (autoClose && !socket.isClosed()) {
173                 socket.close();
174             }
175         } else {
176             if (!super.isClosed()) {
177                 super.close();
178             }
179         }
180     }
181 
182     @Override
getInetAddress()183     public InetAddress getInetAddress() {
184         if (isDelegating()) {
185             return socket.getInetAddress();
186         }
187         return super.getInetAddress();
188     }
189 
190     @Override
getLocalAddress()191     public InetAddress getLocalAddress() {
192         if (isDelegating()) {
193             return socket.getLocalAddress();
194         }
195         return super.getLocalAddress();
196     }
197 
198     @Override
getLocalPort()199     public int getLocalPort() {
200         if (isDelegating()) {
201             return socket.getLocalPort();
202         }
203         return super.getLocalPort();
204     }
205 
206     @Override
getRemoteSocketAddress()207     public SocketAddress getRemoteSocketAddress() {
208         if (isDelegating()) {
209             return socket.getRemoteSocketAddress();
210         }
211         return super.getRemoteSocketAddress();
212     }
213 
214     @Override
getLocalSocketAddress()215     public SocketAddress getLocalSocketAddress() {
216         if (isDelegating()) {
217             return socket.getLocalSocketAddress();
218         }
219         return super.getLocalSocketAddress();
220     }
221 
222     @Override
getPort()223     public final int getPort() {
224         if (isDelegating()) {
225             return socket.getPort();
226         }
227 
228         if (peerPort != -1) {
229             // Return the port that has been explicitly set in the constructor.
230             return peerPort;
231         }
232         return super.getPort();
233     }
234 
235     @Override
addHandshakeCompletedListener(HandshakeCompletedListener listener)236     public void addHandshakeCompletedListener(HandshakeCompletedListener listener) {
237         checkArgument(listener != null, "Provided listener is null");
238         listeners.add(listener);
239     }
240 
241     @Override
removeHandshakeCompletedListener(HandshakeCompletedListener listener)242     public void removeHandshakeCompletedListener(HandshakeCompletedListener listener) {
243         checkArgument(listener != null, "Provided listener is null");
244         if (!listeners.remove(listener)) {
245             throw new IllegalArgumentException("Provided listener is not registered");
246         }
247     }
248 
249     /* @Override */
getFileDescriptor$()250     public FileDescriptor getFileDescriptor$() {
251         if (isDelegating()) {
252             return Platform.getFileDescriptor(socket);
253         }
254         return Platform.getFileDescriptorFromSSLSocket(this);
255     }
256 
257     @Override
258     @SuppressWarnings("UnsynchronizedOverridesSynchronized")
setSoTimeout(int readTimeoutMilliseconds)259     public final void setSoTimeout(int readTimeoutMilliseconds) throws SocketException {
260         if (isDelegating()) {
261             socket.setSoTimeout(readTimeoutMilliseconds);
262         } else {
263             super.setSoTimeout(readTimeoutMilliseconds);
264             this.readTimeoutMilliseconds = readTimeoutMilliseconds;
265         }
266     }
267 
268     @Override
269     @SuppressWarnings("UnsynchronizedOverridesSynchronized")
getSoTimeout()270     public final int getSoTimeout() throws SocketException {
271         if (isDelegating()) {
272             return socket.getSoTimeout();
273         }
274         return readTimeoutMilliseconds;
275     }
276 
277     @Override
sendUrgentData(int data)278     public final void sendUrgentData(int data) throws IOException {
279         throw new SocketException("Method sendUrgentData() is not supported.");
280     }
281 
282     @Override
setOOBInline(boolean on)283     public final void setOOBInline(boolean on) throws SocketException {
284         throw new SocketException("Method setOOBInline() is not supported.");
285     }
286 
287     @Override
getOOBInline()288     public boolean getOOBInline() throws SocketException {
289         return false;
290     }
291 
292     @Override
getChannel()293     public SocketChannel getChannel() {
294         // TODO(nmittler): Support channels?
295         return null;
296     }
297 
298     @Override
getInputStream()299     public InputStream getInputStream() throws IOException {
300         if (isDelegating()) {
301             return socket.getInputStream();
302         }
303         return super.getInputStream();
304     }
305 
306     @Override
getOutputStream()307     public OutputStream getOutputStream() throws IOException {
308         if (isDelegating()) {
309             return socket.getOutputStream();
310         }
311         return super.getOutputStream();
312     }
313 
314     @Override
setTcpNoDelay(boolean on)315     public void setTcpNoDelay(boolean on) throws SocketException {
316         if (isDelegating()) {
317             socket.setTcpNoDelay(on);
318         } else {
319             super.setTcpNoDelay(on);
320         }
321     }
322 
323     @Override
getTcpNoDelay()324     public boolean getTcpNoDelay() throws SocketException {
325         if (isDelegating()) {
326             return socket.getTcpNoDelay();
327         }
328         return super.getTcpNoDelay();
329     }
330 
331     @Override
setSoLinger(boolean on, int linger)332     public void setSoLinger(boolean on, int linger) throws SocketException {
333         if (isDelegating()) {
334             socket.setSoLinger(on, linger);
335         } else {
336             super.setSoLinger(on, linger);
337         }
338     }
339 
340     @Override
getSoLinger()341     public int getSoLinger() throws SocketException {
342         if (isDelegating()) {
343             return socket.getSoLinger();
344         }
345         return super.getSoLinger();
346     }
347 
348     @Override
349     @SuppressWarnings("UnsynchronizedOverridesSynchronized")
setSendBufferSize(int size)350     public void setSendBufferSize(int size) throws SocketException {
351         if (isDelegating()) {
352             socket.setSendBufferSize(size);
353         } else {
354             super.setSendBufferSize(size);
355         }
356     }
357 
358     @Override
359     @SuppressWarnings("UnsynchronizedOverridesSynchronized")
getSendBufferSize()360     public int getSendBufferSize() throws SocketException {
361         if (isDelegating()) {
362             return socket.getSendBufferSize();
363         }
364         return super.getSendBufferSize();
365     }
366 
367     @Override
368     @SuppressWarnings("UnsynchronizedOverridesSynchronized")
setReceiveBufferSize(int size)369     public void setReceiveBufferSize(int size) throws SocketException {
370         if (isDelegating()) {
371             socket.setReceiveBufferSize(size);
372         } else {
373             super.setReceiveBufferSize(size);
374         }
375     }
376 
377     @Override
378     @SuppressWarnings("UnsynchronizedOverridesSynchronized")
getReceiveBufferSize()379     public int getReceiveBufferSize() throws SocketException {
380         if (isDelegating()) {
381             return socket.getReceiveBufferSize();
382         }
383         return super.getReceiveBufferSize();
384     }
385 
386     @Override
setKeepAlive(boolean on)387     public void setKeepAlive(boolean on) throws SocketException {
388         if (isDelegating()) {
389             socket.setKeepAlive(on);
390         } else {
391             super.setKeepAlive(on);
392         }
393     }
394 
395     @Override
getKeepAlive()396     public boolean getKeepAlive() throws SocketException {
397         if (isDelegating()) {
398             return socket.getKeepAlive();
399         }
400         return super.getKeepAlive();
401     }
402 
403     @Override
setTrafficClass(int tc)404     public void setTrafficClass(int tc) throws SocketException {
405         if (isDelegating()) {
406             socket.setTrafficClass(tc);
407         } else {
408             super.setTrafficClass(tc);
409         }
410     }
411 
412     @Override
getTrafficClass()413     public int getTrafficClass() throws SocketException {
414         if (isDelegating()) {
415             return socket.getTrafficClass();
416         }
417         return super.getTrafficClass();
418     }
419 
420     @Override
setReuseAddress(boolean on)421     public void setReuseAddress(boolean on) throws SocketException {
422         if (isDelegating()) {
423             socket.setReuseAddress(on);
424         } else {
425             super.setReuseAddress(on);
426         }
427     }
428 
429     @Override
getReuseAddress()430     public boolean getReuseAddress() throws SocketException {
431         if (isDelegating()) {
432             return socket.getReuseAddress();
433         }
434         return super.getReuseAddress();
435     }
436 
437     @Override
shutdownInput()438     public void shutdownInput() throws IOException {
439         if (isDelegating()) {
440             socket.shutdownInput();
441         } else {
442             super.shutdownInput();
443         }
444     }
445 
446     @Override
shutdownOutput()447     public void shutdownOutput() throws IOException {
448         if (isDelegating()) {
449             socket.shutdownOutput();
450         } else {
451             super.shutdownOutput();
452         }
453     }
454 
455     @Override
isConnected()456     public boolean isConnected() {
457         if (isDelegating()) {
458             return socket.isConnected();
459         }
460         return super.isConnected();
461     }
462 
463     @Override
isBound()464     public boolean isBound() {
465         if (isDelegating()) {
466             return socket.isBound();
467         }
468         return super.isBound();
469     }
470 
471     @Override
isClosed()472     public boolean isClosed() {
473         if (isDelegating()) {
474             return socket.isClosed();
475         }
476         return super.isClosed();
477     }
478 
479     @Override
isInputShutdown()480     public boolean isInputShutdown() {
481         if (isDelegating()) {
482             return socket.isInputShutdown();
483         }
484         return super.isInputShutdown();
485     }
486 
487     @Override
isOutputShutdown()488     public boolean isOutputShutdown() {
489         if (isDelegating()) {
490             return socket.isOutputShutdown();
491         }
492         return super.isOutputShutdown();
493     }
494 
495     @Override
setPerformancePreferences(int connectionTime, int latency, int bandwidth)496     public void setPerformancePreferences(int connectionTime, int latency, int bandwidth) {
497         if (isDelegating()) {
498             socket.setPerformancePreferences(connectionTime, latency, bandwidth);
499         } else {
500             super.setPerformancePreferences(connectionTime, latency, bandwidth);
501         }
502     }
503 
504     @Override
toString()505     public String toString() {
506         StringBuilder builder = new StringBuilder("SSL socket over ");
507         if (isDelegating()) {
508             builder.append(socket.toString());
509         } else {
510             builder.append(super.toString());
511         }
512         return builder.toString();
513     }
514 
515     /**
516      * Returns the hostname that was supplied during socket creation. No DNS resolution is
517      * attempted before returning the hostname.
518      */
getHostname()519     String getHostname() {
520         return peerHostname;
521     }
522 
523     /**
524      * This method enables Server Name Indication
525      *
526      * @param hostname the desired SNI hostname, or null to disable
527      */
setHostname(String hostname)528     void setHostname(String hostname) {
529         peerHostname = hostname;
530     }
531 
532     /**
533      * For the purposes of an SSLSession, we want a way to represent the supplied hostname
534      * or the IP address in a textual representation. We do not want to perform reverse DNS
535      * lookups on this address.
536      */
getHostnameOrIP()537     String getHostnameOrIP() {
538         if (peerHostname != null) {
539             return peerHostname;
540         }
541 
542         InetAddress peerAddress = getInetAddress();
543         if (peerAddress != null) {
544             return Platform.getOriginalHostNameFromInetAddress(peerAddress);
545         }
546 
547         return null;
548     }
549 
550     /**
551      * Note write timeouts are not part of the javax.net.ssl.SSLSocket API
552      */
setSoWriteTimeout(int writeTimeoutMilliseconds)553     void setSoWriteTimeout(int writeTimeoutMilliseconds) throws SocketException {
554         throw new SocketException("Method setSoWriteTimeout() is not supported.");
555     }
556 
557     /**
558      * Note write timeouts are not part of the javax.net.ssl.SSLSocket API
559      */
getSoWriteTimeout()560     int getSoWriteTimeout() throws SocketException {
561         return 0;
562     }
563 
564     /**
565      * Set the handshake timeout on this socket.  This timeout is specified in
566      * milliseconds and will be used only during the handshake process.
567      */
setHandshakeTimeout(int handshakeTimeoutMilliseconds)568     void setHandshakeTimeout(int handshakeTimeoutMilliseconds) throws SocketException {
569         throw new SocketException("Method setHandshakeTimeout() is not supported.");
570     }
571 
checkOpen()572     final void checkOpen() throws SocketException {
573         if (isClosed()) {
574             throw new SocketException("Socket is closed");
575         }
576     }
577 
peerInfoProvider()578     final PeerInfoProvider peerInfoProvider() {
579         return peerInfoProvider;
580     }
581 
582     /**
583      * Called by {@link #notifyHandshakeCompletedListeners()} to get the currently active session.
584      * Unlike {@link #getSession()}, this method must not block.
585      */
getActiveSession()586     abstract SSLSession getActiveSession();
587 
setApplicationProtocolSelector(ApplicationProtocolSelectorAdapter selector)588     abstract void setApplicationProtocolSelector(ApplicationProtocolSelectorAdapter selector);
589 
notifyHandshakeCompletedListeners()590     final void notifyHandshakeCompletedListeners() {
591         List<HandshakeCompletedListener> listenersCopy = new ArrayList<>(listeners);
592         if (!listenersCopy.isEmpty()) {
593             // notify the listeners
594             HandshakeCompletedEvent event = new HandshakeCompletedEvent(this, getActiveSession());
595             for (HandshakeCompletedListener listener : listenersCopy) {
596                 try {
597                     listener.handshakeCompleted(event);
598                 } catch (RuntimeException e) {
599                     // The RI runs the handlers in a separate thread,
600                     // which we do not. But we try to preserve their
601                     // behavior of logging a problem and not killing
602                     // the handshaking thread just because a listener
603                     // has a problem.
604                     Thread thread = Thread.currentThread();
605                     thread.getUncaughtExceptionHandler().uncaughtException(thread, e);
606                 }
607             }
608         }
609     }
610 
isDelegating()611     private boolean isDelegating() {
612         // Checking for null to handle the case of calling virtual methods in the super class
613         // constructor.
614         return socket != null && socket != this;
615     }
616     /* @Override */
617     @SuppressWarnings("MissingOverride") // For compilation with Java 6.
getHandshakeSession()618     public abstract SSLSession getHandshakeSession();
619 
620     /**
621      * This method enables session ticket support.
622      *
623      * @param useSessionTickets True to enable session tickets
624      */
setUseSessionTickets(boolean useSessionTickets)625     abstract void setUseSessionTickets(boolean useSessionTickets);
626 
627     /**
628      * Enables/disables TLS Channel ID for this server socket.
629      *
630      * <p>This method needs to be invoked before the handshake starts.
631      *
632      * @throws IllegalStateException if this is a client socket or if the handshake has already
633      *         started.
634      */
setChannelIdEnabled(boolean enabled)635     abstract void setChannelIdEnabled(boolean enabled);
636 
637     /**
638      * Gets the TLS Channel ID for this server socket. Channel ID is only available once the
639      * handshake completes.
640      *
641      * @return channel ID or {@code null} if not available.
642      *
643      * @throws IllegalStateException if this is a client socket or if the handshake has not yet
644      *         completed.
645      * @throws SSLException if channel ID is available but could not be obtained.
646      */
getChannelId()647     abstract byte[] getChannelId() throws SSLException;
648 
649     /**
650      * Sets the {@link PrivateKey} to be used for TLS Channel ID by this client socket.
651      *
652      * <p>This method needs to be invoked before the handshake starts.
653      *
654      * @param privateKey private key (enables TLS Channel ID) or {@code null} for no key (disables
655      *        TLS Channel ID). The private key must be an Elliptic Curve (EC) key based on the NIST
656      *        P-256 curve (aka SECG secp256r1 or ANSI X9.62 prime256v1).
657      *
658      * @throws IllegalStateException if this is a server socket or if the handshake has already
659      *         started.
660      */
setChannelIdPrivateKey(PrivateKey privateKey)661     abstract void setChannelIdPrivateKey(PrivateKey privateKey);
662 
663     /**
664      * Returns null always for backward compatibility.
665      * @deprecated NPN is not supported
666      */
667     @Deprecated
getNpnSelectedProtocol()668     byte[] getNpnSelectedProtocol() {
669         return null;
670     }
671 
672     /**
673      * This method does nothing and is kept for backward compatibility.
674      * @deprecated NPN is not supported
675      */
676     @Deprecated
setNpnProtocols(byte[] npnProtocols)677     void setNpnProtocols(byte[] npnProtocols) {}
678 
679     /**
680      * Returns the protocol agreed upon by client and server, or {@code null} if
681      * no protocol was agreed upon.
682      *
683      * @deprecated use {@link #getApplicationProtocol()} instead.
684      */
685     @Deprecated
getAlpnSelectedProtocol()686     abstract byte[] getAlpnSelectedProtocol();
687 
688     /**
689      * Sets the list of ALPN protocols. This method internally converts the protocols to their
690      * wire-format form.
691      *
692      * @param alpnProtocols the list of ALPN protocols
693      * @deprecated use {@link #setApplicationProtocols(String[])} instead.
694      */
695     @Deprecated
setAlpnProtocols(String[] alpnProtocols)696     abstract void setAlpnProtocols(String[] alpnProtocols);
697 
698     /**
699      * Alternate version of {@link #setAlpnProtocols(String[])} that directly sets the list of
700      * ALPN in the wire-format form used by BoringSSL (length-prefixed 8-bit strings).
701      * Requires that all strings be encoded with US-ASCII.
702      *
703      * @param alpnProtocols the encoded form of the ALPN protocol list
704      * @deprecated Use {@link #setApplicationProtocols(String[])} instead.
705      */
706     @Deprecated
setAlpnProtocols(byte[] alpnProtocols)707     abstract void setAlpnProtocols(byte[] alpnProtocols);
708 
709     /**
710      * Sets the list of ALPN protocols.
711      *
712      * @param protocols the list of ALPN protocols
713      */
714     @SuppressWarnings("MissingOverride") // For compiling pre Java 9.
setApplicationProtocols(String[] protocols)715     abstract void setApplicationProtocols(String[] protocols);
716 
717     /**
718      * Returns the list of supported ALPN protocols.
719      */
720     @SuppressWarnings("MissingOverride") // For compiling pre Java 9.
getApplicationProtocols()721     abstract String[] getApplicationProtocols();
722 
723     @SuppressWarnings("MissingOverride") // For compiling pre Java 9.
getApplicationProtocol()724     public abstract String getApplicationProtocol();
725 
726     @SuppressWarnings("MissingOverride") // For compiling pre Java 9.
getHandshakeApplicationProtocol()727     public abstract String getHandshakeApplicationProtocol();
728 
729     /**
730      * Sets an application-provided ALPN protocol selector. If provided, this will override
731      * the list of protocols set by {@link #setApplicationProtocols(String[])}.
732      */
setApplicationProtocolSelector(ApplicationProtocolSelector selector)733     abstract void setApplicationProtocolSelector(ApplicationProtocolSelector selector);
734 
735     /**
736      * Returns the tls-unique channel binding value for this connection, per RFC 5929.  This
737      * will return {@code null} if there is no such value available, such as if the handshake
738      * has not yet completed or this connection is closed.
739      */
getTlsUnique()740     abstract byte[] getTlsUnique();
741 
742     /**
743      * Exports a value derived from the TLS master secret as described in RFC 5705.
744      *
745      * @param label the label to use in calculating the exported value.  This must be
746      * an ASCII-only string.
747      * @param context the application-specific context value to use in calculating the
748      * exported value.  This may be {@code null} to use no application context, which is
749      * treated differently than an empty byte array.
750      * @param length the number of bytes of keying material to return.
751      * @return a value of the specified length, or {@code null} if the handshake has not yet
752      * completed or the connection has been closed.
753      * @throws SSLException if the value could not be exported.
754      */
exportKeyingMaterial(String label, byte[] context, int length)755     abstract byte[] exportKeyingMaterial(String label, byte[] context, int length)
756             throws SSLException;
757 }
758