1 module hunt.net.secure.conscrypt.AbstractSessionContext;
2 
3 // dfmt off
4 version(WITH_HUNT_SECURITY):
5 // dfmt on
6 
7 import hunt.net.ssl.SSLSession;
8 import hunt.net.ssl.SSLSessionContext;
9 
10 import hunt.net.secure.conscrypt.ByteArray;
11 import hunt.net.secure.conscrypt.NativeCrypto;
12 import hunt.net.secure.conscrypt.NativeSslSession;
13 // import hunt.net.KeyCertOptions;
14 
15 import hunt.collection;
16 import hunt.Exceptions;
17 import hunt.logging;
18 
19 import deimos.openssl.ssl;
20 
21 /**
22  * Supports SSL session caches.
23  */
24 abstract class AbstractSessionContext : SSLSessionContext {
25 
26     /**
27      * Maximum lifetime of a session (in seconds) after which it's considered invalid and should not
28      * be used to for new connections.
29      */
30     private enum int DEFAULT_SESSION_TIMEOUT_SECONDS = 8 * 60 * 60;
31 
32     private int maximumSize;
33     private int timeout = DEFAULT_SESSION_TIMEOUT_SECONDS;
34 
35     package long sslCtxNativePointer; 
36 
37     // private final Map<ByteArray, NativeSslSession> sessions =
38     //         new LinkedHashMap<ByteArray, NativeSslSession>() {
39     //             override
40     //             protected bool removeEldestEntry(
41     //                     Map.Entry<ByteArray, NativeSslSession> eldest) {
42     //                 // NOTE: does not take into account any session that may have become
43     //                 // invalid.
44     //                 if (maximumSize > 0 && size() > maximumSize) {
45     //                     // Let the subclass know.
46     //                     onBeforeRemoveSession(eldest.getValue());
47     //                     return true;
48     //                 }
49     //                 return false;
50     //             }
51     //         };
52 
53     /**
54      * Constructs a new session context.
55      *
56      * @param maximumSize of cache
57      */
58     this(int maximumSize) {
59         this.maximumSize = maximumSize;
60         sslCtxNativePointer = NativeCrypto.SSL_CTX_new();
61     }
62 
63     // this(int maximumSize, KeyCertOptions options) {
64     //     this.maximumSize = maximumSize;
65     //     sslCtxNativePointer = NativeCrypto.SSL_CTX_new();
66         
67     //     version(HUNT_NET_DEBUG) {
68     //         trace("using certificate: " ~ certificate);
69     //         trace("using privatekey: " ~ privatekey);
70     //     }
71     //     // NativeCrypto.SSL_set_verify(sslCtxNativePointer, SSL_VERIFY_PEER|SSL_VERIFY_FAIL_IF_NO_PEER_CERT);
72     //     // NativeCrypto.SSL_CTX_use_certificate_file(sslCtxNativePointer, certificate);
73     //     // NativeCrypto.SSL_CTX_use_PrivateKey_file(sslCtxNativePointer, privatekey);
74     // }
75 
76     void setVerify(int mode) {
77         // SSL_VERIFY_PEER|SSL_VERIFY_FAIL_IF_NO_PEER_CERT
78         NativeCrypto.SSL_CTX_set_verify(sslCtxNativePointer, mode);
79     }
80 
81     void useCaCertificate(string caFile, string password="") {
82         version(HUNT_NET_DEBUG) {
83             trace("using CA file: " ~ caFile);
84         }  
85         NativeCrypto.SSL_CTX_load_verify_locations(sslCtxNativePointer, caFile, null);
86     }
87 
88     void useCertificate(string certificate, string privateKey, string certPassword="", string keyPassword="") {
89         // FIXME: Needing refactor or cleanup -@zhangxueping at 2019-12-16T18:22:25+08:00
90         // using the password
91         version(HUNT_NET_DEBUG) {
92             trace("using certificate: " ~ certificate);
93             trace("using privatekey: " ~ privateKey);
94         }        
95         NativeCrypto.SSL_CTX_use_certificate_file(sslCtxNativePointer, certificate);
96         NativeCrypto.SSL_CTX_use_PrivateKey_file(sslCtxNativePointer, privateKey);
97 
98 
99         if(!NativeCrypto.SSL_CTX_check_private_key(sslCtxNativePointer)) {
100             warningf("Private key (%s) does not match the certificate public key: %s", privateKey, certificate);
101         }
102     }
103 
104     /**
105      * This method is provided for API-compatibility only, not intended for use. No guarantees
106      * are made WRT performance.
107      */
108     override
109     final Enumeration!(byte[]) getIds() {
110         // Make a copy of the IDs.
111         // Iterator<NativeSslSession> iter;
112         // synchronized (sessions) {
113         //     iter = Arrays.asList(sessions.values().toArray(new NativeSslSession[sessions.size()]))
114         //             .iterator();
115         // }
116         // return new Enumeration!(byte[])() {
117         //     private NativeSslSession next;
118 
119         //     override
120         //     bool hasMoreElements() {
121         //         if (next !is null) {
122         //             return true;
123         //         }
124         //         while (iter.hasNext()) {
125         //             NativeSslSession session = iter.next();
126         //             if (session.isValid()) {
127         //                 next = session;
128         //                 return true;
129         //             }
130         //         }
131         //         next = null;
132         //         return false;
133         //     }
134 
135         //     override
136         //     byte[] nextElement() {
137         //         if (hasMoreElements()) {
138         //             byte[] id = next.getId();
139         //             next = null;
140         //             return id;
141         //         }
142         //         throw new NoSuchElementException();
143         //     }
144         // };
145         implementationMissing();
146         return null;
147     }
148 
149     /**
150      * This is provided for API-compatibility only, not intended for use. No guarantees are
151      * made WRT performance or the validity of the returned session.
152      */
153     override
154     final SSLSession getSession(byte[] sessionId) {
155         if (sessionId is null) {
156             throw new NullPointerException("sessionId");
157         }
158         // ByteArray key = new ByteArray(sessionId);
159         // NativeSslSession session;
160         // synchronized (sessions) {
161         //     session = sessions.get(key);
162         // }
163         // if (session !is null && session.isValid()) {
164         //     return session.toSSLSession();
165         // }
166 
167         implementationMissing();
168         return null;
169     }
170 
171     override
172     final int getSessionCacheSize() {
173         return maximumSize;
174     }
175 
176     override
177     final int getSessionTimeout() {
178         return timeout;
179     }
180 
181     override
182     final void setSessionTimeout(int seconds) {
183         if (seconds < 0) {
184             throw new IllegalArgumentException("seconds < 0");
185         }
186         implementationMissing();
187         // synchronized (sessions) {
188         //     // Set the timeout on this context.
189         //     timeout = seconds;
190         //     // setSessionTimeout(0) is defined to remove the timeout, but passing 0
191         //     // to SSL_CTX_set_timeout in BoringSSL sets it to the default timeout instead.
192         //     // Pass INT_MAX seconds (68 years), since that's equivalent for practical purposes.
193         //     if (seconds > 0) {
194         //         NativeCrypto.SSL_CTX_set_timeout(sslCtxNativePointer, this, seconds);
195         //     } else {
196         //         NativeCrypto.SSL_CTX_set_timeout(sslCtxNativePointer, this, int.max);
197         //     }
198 
199         //     Iterator<NativeSslSession> i = sessions.values().iterator();
200         //     while (i.hasNext()) {
201         //         NativeSslSession session = i.next();
202         //         // SSLSession's know their context and consult the
203         //         // timeout as part of their validity condition.
204         //         if (!session.isValid()) {
205         //             // Let the subclass know.
206         //             onBeforeRemoveSession(session);
207         //             i.remove();
208         //         }
209         //     }
210         // }
211     }
212 
213     override
214     final void setSessionCacheSize(int size) {
215         if (size < 0) {
216             throw new IllegalArgumentException("size < 0");
217         }
218 
219         int oldMaximum = maximumSize;
220         maximumSize = size;
221 
222         // Trim cache to size if necessary.
223         if (size < oldMaximum) {
224             trimToSize();
225         }
226     }
227 
228     protected void finalize() {
229         // try {
230         //     NativeCrypto.SSL_CTX_free(sslCtxNativePointer, this);
231         // } finally {
232         //     super.finalize();
233         // }
234     }
235 
236     /**
237      * Adds the given session to the cache.
238      */
239     final void cacheSession(NativeSslSession session) {
240         byte[] id = session.getId();
241         if (id is null || id.length == 0) {
242             return;
243         }
244 
245         // Let the subclass know.
246         onBeforeAddSession(session);
247 
248         // ByteArray key = new ByteArray(id);
249         // synchronized (sessions) {
250         //     sessions.put(key, session);
251         // }
252         implementationMissing();
253     }
254 
255     /**
256      * Called for server sessions only. Retrieves the session by its ID. Overridden by
257      * {@link ServerSessionContext} to
258      */
259     final NativeSslSession getSessionFromCache(byte[] sessionId) {
260         if (sessionId is null) {
261             return null;
262         }
263 
264 
265         implementationMissing();
266         return null;
267         // First, look in the in-memory cache.
268         // NativeSslSession session;
269         // synchronized (sessions) {
270         //     session = sessions.get(new ByteArray(sessionId));
271         // }
272         // if (session !is null && session.isValid()) {
273         //     return session;
274         // }
275 
276         // // Not found in-memory - look it up in the persistent cache.
277         // return getSessionFromPersistentCache(sessionId);
278     }
279 
280     /**
281      * Called when the given session is about to be added. Used by {@link ClientSessionContext} to
282      * update its host-and-port based cache.
283      *
284      * <p>Visible for extension only, not intended to be called directly.
285      */
286     abstract void onBeforeAddSession(NativeSslSession session);
287 
288     /**
289      * Called when a session is about to be removed. Used by {@link ClientSessionContext}
290      * to update its host-and-port based cache.
291      *
292      * <p>Visible for extension only, not intended to be called directly.
293      */
294     abstract void onBeforeRemoveSession(NativeSslSession session);
295 
296     /**
297      * Called for server sessions only. Retrieves the session by ID from the persistent cache.
298      *
299      * <p>Visible for extension only, not intended to be called directly.
300      */
301     abstract NativeSslSession getSessionFromPersistentCache(byte[] sessionId);
302 
303     /**
304      * Makes sure cache size is < maximumSize.
305      */
306     private void trimToSize() {
307 
308         implementationMissing();
309         // synchronized (sessions) {
310         //     int size = sessions.size();
311         //     if (size > maximumSize) {
312         //         int removals = size - maximumSize;
313         //         Iterator<NativeSslSession> i = sessions.values().iterator();
314         //         while (removals-- > 0) {
315         //             NativeSslSession session = i.next();
316         //             onBeforeRemoveSession(session);
317         //             i.remove();
318         //         }
319         //     }
320         // }
321     }
322 }