1 module hunt.net.secure.conscrypt.ActiveSession;
2 
3 // dfmt off
4 version(WITH_HUNT_SECURITY):
5 // dfmt on
6 
7 import hunt.net.secure.conscrypt.AbstractSessionContext;
8 import hunt.net.secure.conscrypt.ConscryptSession;
9 import hunt.net.secure.conscrypt.NativeSsl;
10 import hunt.net.secure.conscrypt.NativeConstants;
11 import hunt.net.secure.conscrypt.SSLNullSession;
12 import hunt.net.secure.conscrypt.SSLUtils;
13 
14 import hunt.net.ssl.SSLSessionContext;
15 
16 // import hunt.security.cert.Certificate;
17 // import hunt.security.cert.X509Certificate;
18 // import hunt.security.Principal;
19 
20 import hunt.net.ssl.SSLSession;
21 import hunt.net.Exceptions;
22 
23 import hunt.collection;
24 
25 import hunt.util.DateTime;
26 import hunt.Exceptions;
27 
28 import hunt.logging;
29 
30 import std.datetime;
31 
32 /**
33  * A session that is dedicated a single connection and operates directly on the underlying
34  * {@code SSL}.
35  */
36 final class ActiveSession : ConscryptSession {
37     private NativeSsl ssl;
38     private AbstractSessionContext sessionContext;
39     private byte[] id;
40     private long creationTime;
41     private string protocol;
42     private string peerHost;
43     private int peerPort = -1;
44     private long lastAccessedTime = 0;
45     // private X509Certificate[] peerCertificateChain;
46     // private X509Certificate[] localCertificates;
47     // private X509Certificate[] peerCertificates;
48     private byte[] peerCertificateOcspData;
49     private byte[] peerTlsSctData;
50 
51     this(NativeSsl ssl, AbstractSessionContext sessionContext) {
52         this.ssl = ssl;
53         this.sessionContext = sessionContext;
54     }
55 
56     override
57     byte[] getId() {
58         if (id is null) {
59             synchronized (ssl) {
60                 id = ssl.getSessionId();
61             }
62         }
63         return id !is null ? id.dup : [];
64     }
65 
66     /**
67      * Indicates that this session's ID may have changed and should be re-cached.
68      */
69     void resetId() {
70         id = null;
71     }
72 
73     override
74     SSLSessionContext getSessionContext() {
75         return isValid() ? sessionContext : null;
76     }
77 
78     override
79     long getCreationTime() {
80         if (creationTime == 0) {
81             synchronized (ssl) {
82                 creationTime = ssl.getTime();
83             }
84         }
85         return creationTime;
86     }
87 
88     /**
89      * Returns the last time this SSL session was accessed. Accessing
90      * here is to mean that a new connection with the same SSL context data was
91      * established.
92      *
93      * @return the session's last access time in milliseconds since the epoch
94      */
95     // TODO(nathanmittler): Does lastAccessedTime need to account for session reuse?
96     override
97     long getLastAccessedTime() {
98         return lastAccessedTime == 0 ? getCreationTime() : lastAccessedTime;
99     }
100 
101     void setLastAccessedTime(long accessTimeMillis) {
102         lastAccessedTime = accessTimeMillis;
103     }
104 
105     /**
106      * Returns the OCSP stapled response. Returns a copy of the internal arrays.
107      *
108      * The method signature matches
109      * <a
110      * href="http://download.java.net/java/jdk9/docs/api/javax/net/ssl/ExtendedSSLSession.html#getStatusResponses--">Java
111      * 9</a>.
112      *
113      * @see <a href="https://tools.ietf.org/html/rfc6066">RFC 6066</a>
114      * @see <a href="https://tools.ietf.org/html/rfc6961">RFC 6961</a>
115      */
116     override
117     List!(byte[]) getStatusResponses() {
118         if (peerCertificateOcspData is null) {
119             return new EmptyList!(byte[])();
120         }
121 
122         return Collections.singletonList(peerCertificateOcspData.dup);
123     }
124 
125     /**
126      * Returns the signed certificate timestamp (SCT) received from the peer. Returns a
127      * copy of the internal array.
128      *
129      * @see <a href="https://tools.ietf.org/html/rfc6962">RFC 6962</a>
130      */
131     override
132     byte[] getPeerSignedCertificateTimestamp() {
133         if (peerTlsSctData is null) {
134             return null;
135         }
136         return peerTlsSctData.dup;
137     }
138 
139     override
140     string getRequestedServerName() {
141         synchronized (ssl) {
142             return ssl.getRequestedServerName();
143         }
144     }
145 
146     override
147     void invalidate() {
148         synchronized (ssl) {
149             ssl.setTimeout(0L);
150         }
151     }
152 
153     override
154     bool isValid() {
155         synchronized (ssl) {
156             long creationTimeMillis = ssl.getTime();
157             long timeoutMillis = ssl.getTimeout();
158             long now = convert!(TimeUnit.HectoNanosecond, TimeUnit.Millisecond)(Clock.currStdTime);
159             return (now - timeoutMillis) < creationTimeMillis;
160         }
161     }
162 
163     override
164     void putValue(string name, Object value) {
165         throw new UnsupportedOperationException(
166                 "All calls to this method should be intercepted by ProvidedSessionDecorator.");
167     }
168 
169     override
170     Object getValue(string name) {
171         throw new UnsupportedOperationException(
172                 "All calls to this method should be intercepted by ProvidedSessionDecorator.");
173     }
174 
175     override
176     void removeValue(string name) {
177         throw new UnsupportedOperationException(
178                 "All calls to this method should be intercepted by ProvidedSessionDecorator.");
179     }
180 
181     override
182     string[] getValueNames() {
183         throw new UnsupportedOperationException(
184                 "All calls to this method should be intercepted by ProvidedSessionDecorator.");
185     }
186 
187     // override
188     // Certificate[] getPeerCertificates() {
189     //     checkPeerCertificatesPresent();
190     //     return cast(Certificate[])peerCertificates.dup;
191     // }
192 
193     // override
194     // Certificate[] getLocalCertificates() {
195     //     return localCertificates is null ? null : cast(Certificate[])localCertificates.dup;
196     // }
197 
198     /**
199      * Returns the certificate(s) of the peer in this SSL session
200      * used in the handshaking phase of the connection.
201      * Please notice hat this method is superseded by
202      * <code>getPeerCertificates()</code>.
203      * @return an array of X509 certificates (the peer's one first and then
204      *         eventually that of the certification authority) or null if no
205      *         certificate were used during the SSL connection.
206      * @throws SSLPeerUnverifiedException if either a non-X.509 certificate
207      *         was used (i.e. Kerberos certificates) or the peer could not
208      *         be verified.
209      */
210     // override
211     // X509Certificate[] getPeerCertificateChain()
212     //         {
213     //     checkPeerCertificatesPresent();
214     //     // TODO(nathanmittler): Should we clone?
215     //     X509Certificate[] result = peerCertificateChain;
216     //     if (result is null) {
217     //         // single-check idiom
218     //         peerCertificateChain = result = SSLUtils.toCertificateChain(peerCertificates);
219     //     }
220     //     return result;
221     // }
222 
223     // override
224     // Principal getPeerPrincipal() {
225     //     checkPeerCertificatesPresent();
226     //     return peerCertificates[0].getSubjectX500Principal();
227     // }
228 
229     // override
230     // Principal getLocalPrincipal() {
231     //     if (localCertificates !is null && localCertificates.length > 0) {
232     //         return localCertificates[0].getSubjectX500Principal();
233     //     } else {
234     //         return null;
235     //     }
236     // }
237 
238     override
239     string getCipherSuite() {
240         // Always get the Cipher from the SSL directly since it may have changed during a
241         // renegotiation.
242         string cipher;
243         synchronized (ssl) {
244             cipher = ssl.getCipherSuite();
245         }
246         return cipher is null ? SSLNullSession.INVALID_CIPHER : cipher;
247     }
248 
249     override
250     string getProtocol() {
251         string protocol = this.protocol;
252         if (protocol is null) {
253             synchronized (ssl) {
254                 protocol = ssl.getVersion();
255             }
256             this.protocol = protocol;
257         }
258         return protocol;
259     }
260 
261     override
262     string getPeerHost() {
263         return peerHost;
264     }
265 
266     override
267     int getPeerPort() {
268         return peerPort;
269     }
270 
271     override
272     int getPacketBufferSize() {
273         return NativeConstants.SSL3_RT_MAX_PACKET_SIZE;
274     }
275 
276     override
277     int getApplicationBufferSize() {
278         return NativeConstants.SSL3_RT_MAX_PLAIN_LENGTH;
279     }
280 
281     /**
282      * Configures the peer information once it has been received by the handshake.
283      */
284     // void onPeerCertificatesReceived(
285     //         string peerHost, int peerPort, X509Certificate[] peerCertificates) {
286     //     configurePeer(peerHost, peerPort, peerCertificates);
287     // }
288 
289     // private void configurePeer(string peerHost, int peerPort, X509Certificate[] peerCertificates) {
290     //     this.peerHost = peerHost;
291     //     this.peerPort = peerPort;
292     //     this.peerCertificates = peerCertificates;
293 
294     //     version(Have_boringssl) {
295     //     synchronized (ssl) {
296     //         this.peerCertificateOcspData = ssl.getPeerCertificateOcspData();
297     //         this.peerTlsSctData = ssl.getPeerTlsSctData();
298     //     }
299     //     }
300     // }
301 
302     /**
303      * Updates the cached peer certificate after the handshake has completed
304      * (or entered False Start).
305      */
306     void onPeerCertificateAvailable(string peerHost, int peerPort) {
307         version(HUNT_NET_DEBUG) {
308             implementationMissing(false);
309             version(HUNT_NET_DEBUG) infof("peerHost: %s, peerPort: %d", peerHost, peerPort);
310         }
311         // synchronized (ssl) {
312         //     id = null;
313         //     this.localCertificates = ssl.getLocalCertificates();
314         //     if (this.peerCertificates is null) {
315         //         // When resuming a session, the cert_verify_callback (which calls
316         //         // onPeerCertificatesReceived) isn't called by BoringSSL during the handshake
317         //         // because it presumes the certs were verified in the previous connection on that
318         //         // session, leaving us without the peer certificates.  If that happens, fetch them
319         //         // explicitly.
320         //         configurePeer(peerHost, peerPort, ssl.getPeerCertificates());
321         //     }
322         // }
323     }
324 
325     /**
326      * Throw SSLPeerUnverifiedException on null or empty peerCertificates array
327      */
328     // private void checkPeerCertificatesPresent() {
329     //     if (peerCertificates is null || peerCertificates.length == 0) {
330     //         throw new SSLPeerUnverifiedException("No peer certificates");
331     //     }
332     // }
333 
334     // private void notifyUnbound(Object value, string name) {
335     //     if (value instanceof SSLSessionBindingListener) {
336     //         ((SSLSessionBindingListener) value)
337     //                 .valueUnbound(new SSLSessionBindingEvent(this, name));
338     //     }
339     // }
340 }