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 }