1 module hunt.net.secure.conscrypt.NativeSsl;
2 
3 // dfmt off
4 version(WITH_HUNT_SECURITY):
5 // dfmt on
6 
7 import hunt.net.Exceptions;
8 import hunt.net.KeyCertOptions;
9 import hunt.net.secure.conscrypt.AbstractSessionContext;
10 import hunt.net.secure.conscrypt.AddressUtils;
11 import hunt.net.secure.conscrypt.NativeCrypto;
12 import hunt.net.secure.conscrypt.OpenSSLKey;
13 import hunt.net.secure.conscrypt.SSLParametersImpl;
14 import hunt.net.secure.conscrypt.SSLUtils;
15 
16 // import hunt.net.ssl.X509KeyManager;
17 
18 // import hunt.security.cert.X509Certificate;
19 // import hunt.security.Key;
20 // import hunt.security.x500.X500Principal;
21 
22 import hunt.collection;
23 import hunt.logging;
24 import hunt.Exceptions;
25 
26 import std.array;
27 import std.container.array;
28 
29 import deimos.openssl.ssl;
30 
31 
32 /**
33  * A utility wrapper that abstracts operations on the underlying native SSL instance.
34  */
35 final class NativeSsl {
36     private SSLParametersImpl parameters;
37     private SSLHandshakeCallbacks handshakeCallbacks;
38     // private AliasChooser aliasChooser;
39     // private PSKCallbacks pskCallbacks;
40     // private X509Certificate[] localCertificates;
41     // private ReadWriteLock lock = new ReentrantReadWriteLock();
42     private long ssl;
43 
44     // private this(long ssl, SSLParametersImpl parameters,
45     //         SSLHandshakeCallbacks handshakeCallbacks, AliasChooser aliasChooser,
46     //         PSKCallbacks pskCallbacks) {
47     //     this.ssl = ssl;
48     //     this.parameters = parameters;
49     //     this.handshakeCallbacks = handshakeCallbacks;
50     //     this.aliasChooser = aliasChooser;
51     //     this.pskCallbacks = pskCallbacks;
52     // }
53     private this(long ssl, SSLParametersImpl parameters,
54             SSLHandshakeCallbacks handshakeCallbacks) {
55         this.ssl = ssl;
56         this.parameters = parameters;
57         this.handshakeCallbacks = handshakeCallbacks;
58     }    
59 
60     static NativeSsl newInstance(SSLParametersImpl parameters,
61             SSLHandshakeCallbacks handshakeCallbacks) {
62                 
63         AbstractSessionContext ctx = parameters.getSessionContext();
64         version(HUNT_NET_DEBUG) warning("UseClientMode: ", parameters.getUseClientMode());
65         
66         // KeyCertOptions certOptions = parameters.getKeyCertOptions();
67 
68         // string caFile = certOptions.getCaFile();
69         // if(!caFile.empty()) {
70         //     ctx.useCaCertificate(caFile, certOptions.getCaPassword());
71         // }
72 
73         // string certFile = certOptions.getCertFile();
74         // string keyFile = certOptions.getKeyFile();
75         // if(!certFile.empty() && !keyFile.empty()) {
76         //     ctx.useCertificate(certFile, keyFile, certOptions.getCertPassword(), certOptions.getKeyPassword());
77         // }
78 
79         long ssl_ctx = ctx.sslCtxNativePointer;
80         long ssl = NativeCrypto.SSL_new(ssl_ctx);
81 
82         return new NativeSsl(ssl, parameters, handshakeCallbacks); // , chooser, pskCallbacks
83     }
84 
85     BioWrapper newBio() {
86         try {
87             return new BioWrapper(this);
88         } catch (SSLException e) {
89             throw new RuntimeException(e);
90         }
91     }
92 
93     void offerToResumeSession(long sslSessionNativePointer) {
94         NativeCrypto.SSL_set_session(ssl, sslSessionNativePointer);
95     }
96 
97     byte[] getSessionId() {
98         return NativeCrypto.SSL_session_id(ssl);
99     }
100 
101     long getTime() {
102         return NativeCrypto.SSL_get_time(ssl);
103     }
104 
105     long getTimeout() {
106         return NativeCrypto.SSL_get_timeout(ssl);
107     }
108 
109     void setTimeout(long millis) {
110         NativeCrypto.SSL_set_timeout(ssl, millis);
111     }
112 
113     string getCipherSuite() {
114         return NativeCrypto.cipherSuiteToJava(NativeCrypto.SSL_get_current_cipher(ssl));
115     }
116 
117     // X509Certificate[] getPeerCertificates() {
118     //     ubyte[][] encoded = null;
119     //     version(Have_boringssl) encoded = NativeCrypto.SSL_get0_peer_certificates(ssl);
120 
121     //     return encoded is null ? null : SSLUtils.decodeX509CertificateChain(encoded);
122     // }
123 
124     // X509Certificate[] getLocalCertificates() {
125     //     return localCertificates;
126     // }
127 
128     byte[] getPeerCertificateOcspData() {
129         return NativeCrypto.SSL_get_ocsp_response(ssl);
130     }
131 
132     // byte[] getTlsUnique() {
133     //     return NativeCrypto.SSL_get_tls_unique(ssl);
134     // }
135 
136     // void setTokenBindingParams(int... params) {
137     //     NativeCrypto.SSL_set_token_binding_params(ssl, params);
138     // }
139 
140     // int getTokenBindingParams() {
141     //     return NativeCrypto.SSL_get_token_binding_params(ssl);
142     // }
143 
144     // byte[] exportKeyingMaterial(string label, byte[] context, int length) {
145     //     if (label is null) {
146     //         throw new NullPointerException("Label is null");
147     //     }
148     //     byte[] labelBytes = label.getBytes(Charset.forName("US-ASCII"));
149     //     return NativeCrypto.SSL_export_keying_material(ssl, labelBytes, context, length);
150     // }
151 
152     byte[] getPeerTlsSctData() {
153         return NativeCrypto.SSL_get_signed_cert_timestamp_list(ssl);
154     }
155 
156     /**
157      * @see NativeCrypto.SSLHandshakeCallbacks#clientPSKKeyRequested(string, byte[], byte[])
158      */
159     // @SuppressWarnings("deprecation") // PSKKeyManager is deprecated, but in our own package
160     int clientPSKKeyRequested(string identityHint, byte[] identityBytesOut, byte[] key) {
161 
162 implementationMissing(false);
163 return 0;
164         // PSKKeyManager pskKeyManager = parameters.getPSKKeyManager();
165         // if (pskKeyManager is null) {
166         //     return 0;
167         // }
168 
169         // string identity = pskCallbacks.chooseClientPSKIdentity(pskKeyManager, identityHint);
170         // // Store identity in NULL-terminated modified UTF-8 representation into ientityBytesOut
171         // byte[] identityBytes;
172         // if (identity is null) {
173         //     identity = "";
174         //     identityBytes = EmptyArray.BYTE;
175         // } else if (identity.isEmpty()) {
176         //     identityBytes = EmptyArray.BYTE;
177         // } else {
178         //     try {
179         //         identityBytes = identity.getBytes("UTF-8");
180         //     } catch (UnsupportedEncodingException e) {
181         //         throw new RuntimeException("UTF-8 encoding not supported", e);
182         //     }
183         // }
184         // if (identityBytes.length + 1 > identityBytesOut.length) {
185         //     // Insufficient space in the output buffer
186         //     return 0;
187         // }
188         // if (identityBytes.length > 0) {
189         //     System.arraycopy(identityBytes, 0, identityBytesOut, 0, identityBytes.length);
190         // }
191         // identityBytesOut[identityBytes.length] = 0;
192 
193         // SecretKey secretKey = pskCallbacks.getPSKKey(pskKeyManager, identityHint, identity);
194         // byte[] secretKeyBytes = secretKey.getEncoded();
195         // if (secretKeyBytes is null) {
196         //     return 0;
197         // } else if (secretKeyBytes.length > key.length) {
198         //     // Insufficient space in the output buffer
199         //     return 0;
200         // }
201         // System.arraycopy(secretKeyBytes, 0, key, 0, secretKeyBytes.length);
202         // return secretKeyBytes.length;
203     }
204 
205     /**
206      * @see NativeCrypto.SSLHandshakeCallbacks#serverPSKKeyRequested(string, string, byte[])
207      */
208     // @SuppressWarnings("deprecation") // PSKKeyManager is deprecated, but in our own package
209     int serverPSKKeyRequested(string identityHint, string identity, byte[] key) {
210 
211 implementationMissing(false);
212 return 0;
213         // PSKKeyManager pskKeyManager = parameters.getPSKKeyManager();
214         // if (pskKeyManager is null) {
215         //     return 0;
216         // }
217         // SecretKey secretKey = pskCallbacks.getPSKKey(pskKeyManager, identityHint, identity);
218         // byte[] secretKeyBytes = secretKey.getEncoded();
219         // if (secretKeyBytes is null) {
220         //     return 0;
221         // } else if (secretKeyBytes.length > key.length) {
222         //     return 0;
223         // }
224         // System.arraycopy(secretKeyBytes, 0, key, 0, secretKeyBytes.length);
225         // return secretKeyBytes.length;
226     }
227 
228     void chooseClientCertificate(byte[] keyTypeBytes, byte[][] asn1DerEncodedPrincipals) {
229         
230         implementationMissing(false);
231         // Set<string> keyTypesSet = SSLUtils.getSupportedClientKeyTypes(keyTypeBytes);
232         // string[] keyTypes = keyTypesSet.toArray(new string[keyTypesSet.size()]);
233 
234         // X500Principal[] issuers;
235         // if (asn1DerEncodedPrincipals is null) {
236         //     issuers = null;
237         // } else {
238         //     issuers = new X500Principal[asn1DerEncodedPrincipals.length];
239         //     for (int i = 0; i < asn1DerEncodedPrincipals.length; i++) {
240         //         issuers[i] = new X500Principal(asn1DerEncodedPrincipals[i]);
241         //     }
242         // }
243         // X509KeyManager keyManager = parameters.getX509KeyManager();
244         // string name = (keyManager !is null)
245         //         ? aliasChooser.chooseClientAlias(keyManager, issuers, keyTypes)
246         //         : null;
247         // setCertificate(name);
248     }
249 
250     // void setCertificate(string name) {
251     //     if (name.empty) {
252     //         warning("The certificate name is empty");
253     //         return;
254     //     }
255     //     tracef("Certificate: %s", name);
256     //     X509KeyManager keyManager = parameters.getX509KeyManager();
257     //     if (keyManager is null) {
258     //         return;
259     //     }
260     //     PrivateKey privateKey = keyManager.getPrivateKey(name);
261     //     if (privateKey is null) {
262     //         return;
263     //     }
264     //     localCertificates = keyManager.getCertificateChain(name);
265     //     if (localCertificates is null) {
266     //         return;
267     //     }
268     //     size_t numLocalCerts = localCertificates.length;
269     //     PublicKey publicKey = (numLocalCerts > 0) ? localCertificates[0].getPublicKey() : null;
270 
271     //     // Encode the local certificates.
272     //     byte[][] encodedLocalCerts = new byte[][numLocalCerts];
273     //     for (size_t i = 0; i < numLocalCerts; ++i) {
274     //         encodedLocalCerts[i] = localCertificates[i].getEncoded();
275     //     }
276 
277     //     // Convert the key so we can access a native reference.
278     //     OpenSSLKey key;
279     //     try {
280     //         key = OpenSSLKey.fromPrivateKeyForTLSStackOnly(privateKey, publicKey);
281     //     } catch (InvalidKeyException e) {
282     //         throw new SSLException(e.msg);
283     //     }
284 
285     //     // Set the local certs and private key.
286     //     version(Have_boringssl) NativeCrypto.setLocalCertsAndPrivateKey(ssl, encodedLocalCerts, key.getNativeRef());
287     //     version(Have_hunt_openssl) {
288     //         implementationMissing(false);
289     //     }
290     // }
291 
292     string getVersion() {
293         return NativeCrypto.SSL_get_version(ssl);
294     }
295 
296     string getRequestedServerName() {
297         return NativeCrypto.SSL_get_servername(ssl);
298     }
299 
300     byte[] getTlsChannelId() {
301         return NativeCrypto.SSL_get_tls_channel_id(ssl);
302     }
303 
304     void initialize(string hostname) { // , OpenSSLKey channelIdPrivateKey
305         version(HUNT_NET_DEBUG) tracef("host: %s", hostname);
306 
307         bool enableSessionCreation = parameters.getEnableSessionCreation();
308         if (!enableSessionCreation) {
309             NativeCrypto.SSL_set_session_creation_enabled(ssl, false);
310         }
311 
312         // Allow servers to trigger renegotiation. Some inadvisable server
313         // configurations cause them to attempt to renegotiate during
314         // certain protocols.
315         NativeCrypto.SSL_accept_renegotiations(ssl);
316 
317         if (isClient()) {
318             version(HUNT_DEBUG) trace("Initializing TLS client...");
319             NativeCrypto.SSL_set_connect_state(ssl);
320 
321             // Configure OCSP and CT extensions for client
322             version(Have_boringssl) {
323                 NativeCrypto.SSL_enable_ocsp_stapling(ssl);
324                 if (parameters.isCTVerificationEnabled(hostname))
325                     NativeCrypto.SSL_enable_signed_cert_timestamps(ssl);
326             }
327         } else {
328             version(HUNT_DEBUG) trace("Initializing TLS server...");
329             NativeCrypto.SSL_set_accept_state(ssl);
330 
331             // Configure OCSP for server
332             if (parameters.getOCSPResponse() !is null) {
333                 version(Have_boringssl) NativeCrypto.SSL_enable_ocsp_stapling(ssl);
334             }
335         }
336 
337         if (parameters.getEnabledProtocols().length == 0 && parameters.isEnabledProtocolsFiltered) {
338             throw new SSLHandshakeException("No enabled protocols; "
339                     ~ NativeCrypto.OBSOLETE_PROTOCOL_SSLV3
340                     ~ " is no longer supported and was filtered from the list");
341         }
342         // NativeCrypto.setEnabledProtocols(ssl, parameters.enabledProtocols);
343         // NativeCrypto.setEnabledCipherSuites(ssl, parameters.enabledCipherSuites);
344 
345         if (parameters.applicationProtocols.length > 0) {
346             NativeCrypto.setApplicationProtocols(ssl, isClient(), parameters.applicationProtocols);
347         }
348         if (!isClient() && parameters.applicationProtocolSelector !is null) {
349             NativeCrypto.setApplicationProtocolSelector(ssl, parameters.applicationProtocolSelector);
350         }
351 
352         // setup server certificates and private keys.
353         // clients will receive a call back to request certificates.
354         if (!isClient()) {
355             Array!string keyTypes;
356             foreach (long sslCipherNativePointer ; NativeCrypto.SSL_get_ciphers(ssl)) {
357                 string keyType = SSLUtils.getServerX509KeyType(sslCipherNativePointer);
358                 if (!keyType.empty()) 
359                     keyTypes.insertBack(keyType);
360             }
361 
362             // X509KeyManager keyManager = parameters.getX509KeyManager();
363             // if (keyManager !is null) {
364             //     foreach (string keyType ; keyTypes) {
365             //         try {
366             //             setCertificate(aliasChooser.chooseServerAlias(keyManager, keyType));
367             //         } catch (CertificateEncodingException e) {
368             //             throw new IOException(e.msg);
369             //         }
370             //     }
371             // } else {
372             //     warning("keyManager is null");
373             // }
374 
375             NativeCrypto.SSL_set_options(ssl, SSL_OP_CIPHER_SERVER_PREFERENCE);
376 
377             if (parameters.sctExtension !is null) {
378                 NativeCrypto.SSL_set_signed_cert_timestamp_list(ssl, parameters.sctExtension);
379             }
380 
381             if (parameters.ocspResponse !is null) {
382                 NativeCrypto.SSL_set_ocsp_response(ssl, parameters.ocspResponse);
383             }
384         }
385 
386         // FIXME: Needing refactor or cleanup -@zxp at 8/3/2018, 11:32:59 AM
387         // 
388         // enablePSKKeyManagerIfRequested(); 
389 
390         if (parameters.useSessionTickets) {
391             NativeCrypto.SSL_clear_options(ssl, SSL_OP_NO_TICKET);
392         } else {
393             NativeCrypto.SSL_set_options(
394                     ssl, NativeCrypto.SSL_get_options(ssl) | SSL_OP_NO_TICKET);
395         }
396 
397         if (parameters.getUseSni() && AddressUtils.isValidSniHostname(hostname)) {
398             NativeCrypto.SSL_set_tlsext_host_name(ssl, hostname);
399         }
400 
401         // BEAST attack mitigation (1/n-1 record splitting for CBC cipher suites
402         // with TLSv1 and SSLv3).
403         version(Have_boringssl) NativeCrypto.SSL_set_mode(ssl, SSL_MODE_CBC_RECORD_SPLITTING);
404 
405         setCertificateValidation();
406         // TODO: Tasks pending completion -@zhangxueping at 2019-12-17T14:56:02+08:00
407         // 
408         // warning("channelIdEnabled: ", parameters.channelIdEnabled);
409         // if (parameters.channelIdEnabled) {
410         //     setTlsChannelId(channelIdPrivateKey);
411         // }
412     }
413 
414     // // TODO(nathanmittler): Remove once after we switch to the engine socket.
415     // void doHandshake(FileDescriptor fd, int timeoutMillis)
416     //         throws CertificateException, IOException {
417     //     lock.readLock().lock();
418     //     try {
419     //         if (isClosed() || fd is null || !fd.valid()) {
420     //             throw new SocketException("Socket is closed");
421     //         }
422     //         NativeCrypto.SSL_do_handshake(ssl, fd, handshakeCallbacks, timeoutMillis);
423     //     } finally {
424     //         lock.readLock().unlock();
425     //     }
426     // }
427 
428     int doHandshake() {
429         // lock.readLock().lock();
430         try {
431             return NativeCrypto.ENGINE_SSL_do_handshake(ssl, handshakeCallbacks);
432         } catch(Exception ex) {
433             warning(ex.msg);
434             return 0;
435         } finally {
436             // lock.readLock().unlock();
437             
438         }
439     }
440 
441     // // TODO(nathanmittler): Remove once after we switch to the engine socket.
442     // int read(FileDescriptor fd, byte[] buf, int offset, int len, int timeoutMillis)
443     //         {
444     //     lock.readLock().lock();
445     //     try {
446     //         if (isClosed() || fd is null || !fd.valid()) {
447     //             throw new SocketException("Socket is closed");
448     //         }
449     //         return NativeCrypto
450     //                 .SSL_read(ssl, fd, handshakeCallbacks, buf, offset, len, timeoutMillis);
451     //     } finally {
452     //         lock.readLock().unlock();
453     //     }
454     // }
455 
456     // // TODO(nathanmittler): Remove once after we switch to the engine socket.
457     // void write(FileDescriptor fd, byte[] buf, int offset, int len, int timeoutMillis)
458     //         {
459     //     lock.readLock().lock();
460     //     try {
461     //         if (isClosed() || fd is null || !fd.valid()) {
462     //             throw new SocketException("Socket is closed");
463     //         }
464     //         NativeCrypto
465     //                 .SSL_write(ssl, fd, handshakeCallbacks, buf, offset, len, timeoutMillis);
466     //     } finally {
467     //         lock.readLock().unlock();
468     //     }
469     // }
470 
471     // @SuppressWarnings("deprecation") // PSKKeyManager is deprecated, but in our own package
472     // private void enablePSKKeyManagerIfRequested() {
473     //     // Enable Pre-Shared Key (PSK) key exchange if requested
474     //     PSKKeyManager pskKeyManager = parameters.getPSKKeyManager();
475     //     if (pskKeyManager !is null) {
476     //         bool pskEnabled = false;
477     //         for (string enabledCipherSuite : parameters.enabledCipherSuites) {
478     //             if ((enabledCipherSuite !is null) && (enabledCipherSuite.contains("PSK"))) {
479     //                 pskEnabled = true;
480     //                 break;
481     //             }
482     //         }
483     //         if (pskEnabled) {
484     //             if (isClient()) {
485     //                 NativeCrypto.set_SSL_psk_client_callback_enabled(ssl, true);
486     //             } else {
487     //                 NativeCrypto.set_SSL_psk_server_callback_enabled(ssl, true);
488     //                 string identityHint = pskCallbacks.chooseServerPSKIdentityHint(pskKeyManager);
489     //                 NativeCrypto.SSL_use_psk_identity_hint(ssl, identityHint);
490     //             }
491     //         }
492     //     }
493     // }
494 
495     // private void setTlsChannelId(OpenSSLKey channelIdPrivateKey) {
496 
497     //     if (parameters.getUseClientMode()) {
498     //         // Client-side TLS Channel ID
499     //         if (channelIdPrivateKey is null) {
500     //             throw new SSLHandshakeException("Invalid TLS channel ID key specified");
501     //         }
502     //         NativeCrypto.SSL_set1_tls_channel_id(ssl, channelIdPrivateKey.getNativeRef());
503     //     } else {
504     //         // Server-side TLS Channel ID
505     //         NativeCrypto.SSL_enable_tls_channel_id(ssl);
506     //     }
507     // }
508 
509     private void setCertificateValidation() {
510         // https://stackoverflow.com/questions/40165088/when-to-call-ssl-set-verify-vs-ssl-ctx-set-verify
511         // https://www.openssl.org/docs/man1.1.0/man3/SSL_CTX_set_verify.html
512         // setup peer certificate verification
513         version(HUNT_NET_DEBUG) warningf("Setup peer certificate verification: isClient=%s", isClient());
514 
515         if (isClient()) {
516             KeyCertOptions certOptions = parameters.getKeyCertOptions();
517             if(certOptions !is null) {
518                 string caFile = certOptions.getCaFile();
519                 if(!caFile.empty()) {
520                     NativeCrypto.SSL_set_verify(ssl, SSL_VERIFY_PEER);
521                     AbstractSessionContext ctx = parameters.getSessionContext();
522                     ctx.useCaCertificate(caFile, certOptions.getCaPassword());
523                 }
524 
525                 string certFile = certOptions.getCertFile();
526                 string keyFile = certOptions.getKeyFile();
527                 if(!certFile.empty() && !keyFile.empty()) {
528                     NativeCrypto.SSL_use_certificate_file(ssl, certFile);
529                     NativeCrypto.SSL_use_PrivateKey_file(ssl, keyFile);
530                     // TODO: enable password
531 
532                     if(!NativeCrypto.SSL_check_private_key(ssl)) {
533                         warningf("Private key (%s) does not match the certificate public key: %s", keyFile, certFile);
534                     }
535                 }
536             }
537 
538         } else {
539             // TODO: Tasks pending completion -@zxp at 8/8/2019, 6:34:06 PM
540             // 
541             // needing client auth takes priority...
542             bool certRequested;
543             if (parameters.getNeedClientAuth()) {
544                 NativeCrypto.SSL_set_verify(ssl, SSL_VERIFY_PEER
545                                 | SSL_VERIFY_FAIL_IF_NO_PEER_CERT);
546                 certRequested = true;
547                 // ... over just wanting it...
548             } else if (parameters.getWantClientAuth()) {
549                 NativeCrypto.SSL_set_verify(ssl, SSL_VERIFY_PEER);
550                 certRequested = true;
551                 // ... and we must disable verification if we don't want client auth.
552             } else {
553                 NativeCrypto.SSL_set_verify(ssl, SSL_VERIFY_NONE);
554                 certRequested = false;
555             }
556 
557             version(HUNT_NET_DEBUG) warningf("certRequested: %s", certRequested);
558 
559             if (certRequested) {
560             //     X509TrustManager trustManager = parameters.getX509TrustManager();
561             //     X509Certificate[] issuers = trustManager.getAcceptedIssuers();
562             //     if (issuers !is null && issuers.length != 0) {
563             //         byte[][] issuersBytes;
564             //         try {
565             //             issuersBytes = SSLUtils.encodeSubjectX509Principals(issuers);
566             //         } catch (CertificateEncodingException e) {
567             //             throw new SSLException("Problem encoding principals", e);
568             //         }
569             //         NativeCrypto.SSL_set_client_CA_list(ssl, issuersBytes);
570             //     }
571             }
572             
573         }
574     }
575 
576     // void interrupt() {
577     //     NativeCrypto.SSL_interrupt(ssl);
578     // }
579 
580     // // TODO(nathanmittler): Remove once after we switch to the engine socket.
581     // void shutdown(FileDescriptor fd) {
582     //     NativeCrypto.SSL_shutdown(ssl, fd, handshakeCallbacks);
583     // }
584 
585     void shutdown() {
586         NativeCrypto.ENGINE_SSL_shutdown(ssl, handshakeCallbacks);
587     }
588 
589     bool wasShutdownReceived() {
590         return (NativeCrypto.SSL_get_shutdown(ssl) & SSL_RECEIVED_SHUTDOWN) != 0;
591     }
592 
593     bool wasShutdownSent() {
594         return (NativeCrypto.SSL_get_shutdown(ssl) & SSL_SENT_SHUTDOWN) != 0;
595     }
596 
597     int readDirectByteBuffer(long destAddress, int destLength) {
598         // lock.readLock().lock();
599         try {
600             return NativeCrypto.ENGINE_SSL_read_direct(
601                     ssl, destAddress, destLength, handshakeCallbacks);
602         } finally {
603             // lock.readLock().unlock();
604         }
605     }
606 
607     int writeDirectByteBuffer(long sourceAddress, int sourceLength) {
608         // lock.readLock().lock();
609         try {
610             return NativeCrypto.ENGINE_SSL_write_direct(
611                     ssl, sourceAddress, sourceLength, handshakeCallbacks);
612         } finally {
613             // lock.readLock().unlock();
614         }
615     }
616 
617     // void forceRead() {
618     //     lock.readLock().lock();
619     //     try {
620     //         NativeCrypto.ENGINE_SSL_force_read(ssl, handshakeCallbacks);
621     //     } finally {
622     //         lock.readLock().unlock();
623     //     }
624     // }
625 
626     int getPendingReadableBytes() {
627         return NativeCrypto.SSL_pending_readable_bytes(ssl);
628     }
629 
630     int getMaxSealOverhead() {
631         version(Have_boringssl) return NativeCrypto.SSL_max_seal_overhead(ssl);
632         version(Have_hunt_openssl) {
633             // implementationMissing(false);
634             return 0;
635         }
636     }
637 
638     void close() {
639         // lock.writeLock().lock();
640         try {
641             if (!isClosed()) {
642                 long toFree = ssl;
643                 ssl = 0L;
644                 NativeCrypto.SSL_free(toFree);
645             }
646         } finally {
647             // lock.writeLock().unlock();
648         }
649     }
650 
651     bool isClosed() {
652         return ssl == 0L;
653     }
654 
655     int getError(int result) {
656         return NativeCrypto.SSL_get_error(ssl, result);
657     }
658 
659     byte[] getApplicationProtocol() {
660         return NativeCrypto.getApplicationProtocol(ssl);
661     }
662 
663     private bool isClient() {
664         return parameters.getUseClientMode();
665     }
666 
667     // protected void finalize() {
668     //     try {
669     //         close();
670     //     } finally {
671     //         // super.finalize();
672     //     }
673     // }
674 }
675 
676 
677 /**
678 * A utility wrapper that abstracts operations on the underlying native BIO instance.
679 */
680 class BioWrapper {
681     private long bio;
682     private NativeSsl nativeSsl;
683 
684     private this(NativeSsl nativeSsl) {
685         this.nativeSsl = nativeSsl;
686         this.bio = NativeCrypto.SSL_BIO_new(nativeSsl.ssl);
687     }
688 
689     int getPendingWrittenBytes() {
690         if (bio != 0) {
691             return NativeCrypto.SSL_pending_written_bytes_in_BIO(bio);
692         } else {
693             return 0;
694         }
695     }
696 
697     int writeDirectByteBuffer(long address, int length) {
698         return NativeCrypto.ENGINE_SSL_write_BIO_direct(
699                 nativeSsl.ssl, bio, address, length, nativeSsl.handshakeCallbacks);
700     }
701 
702     int readDirectByteBuffer(long destAddress, int destLength) {
703         return NativeCrypto.ENGINE_SSL_read_BIO_direct(
704                 nativeSsl.ssl, bio, destAddress, destLength, nativeSsl.handshakeCallbacks);
705     }
706 
707     void close() {
708         long toFree = bio;
709         bio = 0L;
710         NativeCrypto.BIO_free_all(toFree);
711     }
712 }