1 /** 2 * Botan-powered SSL. 3 * 4 * License: 5 * This Source Code Form is subject to the terms of 6 * the Mozilla Public License, v. 2.0. If a copy of 7 * the MPL was not distributed with this file, You 8 * can obtain one at http://mozilla.org/MPL/2.0/. 9 * 10 * Authors: 11 * Vladimir Panteleev <vladimir@thecybershadow.net> 12 */ 13 14 module ae.net.ssl.botan; 15 16 import botan.math.bigint.bigint; 17 import botan.rng.auto_rng; 18 import botan.tls.client; 19 import botan.tls.server; 20 21 import ae.net.asockets; 22 import ae.net.ssl; 23 24 debug = BotanSSL; 25 debug(BotanSSL) import std.stdio : stderr; 26 27 /// Botan implementation of SSLProvider. 28 class BotanSSLProvider : SSLProvider 29 { 30 override SSLContext createContext(SSLContext.Kind kind) 31 { 32 return new BotanSSLContext(kind); 33 } 34 35 override SSLAdapter createAdapter(SSLContext context, IConnection next) 36 { 37 auto ctx = cast(BotanSSLContext)context; 38 assert(ctx, "Not a BotanSSLContext"); 39 return new BotanSSLAdapter(ctx, next); 40 } 41 } 42 43 /// Implementation of TLSCredentialsManager with the default behavior. 44 class DefaultTLSCredentialsManager : TLSCredentialsManager 45 { 46 override Vector!CertificateStore trustedCertificateAuthorities(in string type, in string context) 47 { 48 return super.trustedCertificateAuthorities(type, context); 49 } 50 51 override void verifyCertificateChain(in string type, in string purported_hostname, const ref Vector!X509Certificate cert_chain) 52 { 53 return super.verifyCertificateChain(type, purported_hostname, cert_chain); 54 } 55 56 override Vector!X509Certificate certChain(const ref Vector!string cert_key_types, in string type, in string context) 57 { 58 return super.certChain(cert_key_types, type, context); 59 } 60 61 override Vector!X509Certificate certChainSingleType(in string cert_key_type, in string type, in string context) 62 { 63 return super.certChainSingleType(cert_key_type, type, context); 64 } 65 66 override PrivateKey privateKeyFor(in X509Certificate cert, in string type, in string context) 67 { 68 return super.privateKeyFor(cert, type, context); 69 } 70 71 override bool attemptSrp(in string type, in string context) 72 { 73 return super.attemptSrp(type, context); 74 } 75 76 override string srpIdentifier(in string type, in string context) 77 { 78 return super.srpIdentifier(type, context); 79 } 80 81 override string srpPassword(in string type, 82 in string context, 83 in string identifier) 84 { 85 return super.srpPassword(type, context, identifier); 86 } 87 88 override bool srpVerifier(in string type, 89 in string context, 90 in string identifier, 91 ref string group_name, 92 ref BigInt verifier, 93 ref Vector!ubyte salt, 94 bool generate_fake_on_unknown) 95 { 96 return super.srpVerifier(type, context, identifier, group_name, verifier, salt, generate_fake_on_unknown); 97 } 98 99 override string pskIdentityHint(in string type, in string context) 100 { 101 return super.pskIdentityHint(type, context); 102 } 103 104 override string pskIdentity(in string type, in string context, in string identity_hint) 105 { 106 return super.pskIdentity(type, context, identity_hint); 107 } 108 109 override bool hasPsk() 110 { 111 return super.hasPsk(); 112 } 113 114 override SymmetricKey psk(in string type, in string context, in string identity) 115 { 116 return super.psk(type, context, identity); 117 } 118 } 119 120 class AETLSCredentialsManager : DefaultTLSCredentialsManager 121 { 122 /// E.g.: `() => readText(".../ca-bundle.crt")` 123 static string delegate() trustedRootCABundleProvider = null; 124 static CertificateStore trustedrootCABundleStore; 125 126 SSLContext.Verify verify; 127 128 override Vector!CertificateStore trustedCertificateAuthorities(in string type, in string context) 129 { 130 if (!trustedrootCABundleStore) 131 { 132 auto memStore = new CertificateStoreInMemory(); 133 import std.string : split; 134 auto bundleText = trustedRootCABundleProvider(); 135 foreach (certText; bundleText.split("\n=")[1..$]) 136 memStore.addCertificate(X509Certificate(cast(DataSource) DataSourceMemory(certText))); 137 trustedrootCABundleStore = memStore; 138 } 139 140 return Vector!CertificateStore(trustedrootCABundleStore); 141 } 142 143 override void verifyCertificateChain(in string type, in string purported_hostname, const ref Vector!X509Certificate cert_chain) 144 { 145 if (verify == SSLContext.Verify.none) 146 return; 147 if (verify == SSLContext.Verify.verify && cert_chain.empty) 148 return; 149 super.verifyCertificateChain(type, purported_hostname, cert_chain); 150 } 151 } 152 153 class AETLSPolicy : TLSPolicy 154 { 155 } 156 157 class BotanSSLContext : SSLContext 158 { 159 Kind kind; 160 Verify verify; 161 162 TLSSessionManager sessions; 163 RandomNumberGenerator rng; 164 TLSPolicy policy; 165 166 this(Kind kind) 167 { 168 this.kind = kind; 169 this.rng = new AutoSeededRNG; 170 this.sessions = new TLSSessionManagerInMemory(rng); 171 this.policy = new AETLSPolicy(); 172 } 173 174 override void setCipherList(string[] ciphers) 175 { 176 assert(false, "TODO"); 177 } 178 179 override void enableDH(int bits) 180 { 181 assert(false, "TODO"); 182 } 183 184 override void enableECDH() 185 { 186 assert(false, "TODO"); 187 } 188 189 override void setCertificate(string path) 190 { 191 assert(false, "TODO"); 192 } 193 194 override void setPrivateKey(string path) 195 { 196 assert(false, "TODO"); 197 } 198 199 override void setPeerVerify(Verify verify) 200 { 201 this.verify = verify; 202 } 203 204 override void setPeerRootCertificate(string path) 205 { 206 assert(false, "TODO"); 207 } 208 209 override void setFlags(int flags) 210 { 211 assert(false, "TODO"); 212 } 213 } 214 215 static this() 216 { 217 ssl = new BotanSSLProvider(); 218 } 219 220 static this() 221 { 222 // Needed for OCSP validation 223 import botan.utils.http_util.http_util : tcp_message_handler; 224 tcp_message_handler = 225 (in string hostname, string message) 226 { 227 import std.socket : TcpSocket, InternetAddress; 228 auto s = new TcpSocket(new InternetAddress(hostname, 80)); 229 import std.array; 230 // stderr.writeln("OCSP send:", message); 231 // import std.file; std.file.write("ocsp-req", message); 232 while (message.length) 233 { 234 auto sent = s.send(message); 235 message = message[sent .. $]; 236 } 237 string reply; 238 char[4096] buf; 239 while (true) 240 { 241 auto received = s.receive(buf); 242 if (received > 0) 243 reply ~= buf[0 .. received]; 244 else 245 break; 246 } 247 // stderr.writeln("OCSP:", [reply]); 248 s.close(); 249 return reply; 250 }; 251 } 252 253 // *************************************************************************** 254 255 class BotanSSLAdapter : SSLAdapter 256 { 257 BotanSSLContext context; 258 TLSChannel channel; 259 AETLSCredentialsManager creds; 260 TLSServerInformation serverInfo; 261 262 this(BotanSSLContext context, IConnection next) 263 { 264 this.context = context; 265 this.creds = new AETLSCredentialsManager(); 266 this.creds.verify = context.verify; 267 super(next); 268 269 if (next.state == ConnectionState.connected) 270 initialize(); 271 } 272 273 override void onConnect() 274 { 275 initialize(); 276 } 277 278 private final void initialize() 279 { 280 final switch (context.kind) 281 { 282 case SSLContext.Kind.client: 283 channel = new TLSClient( 284 &botanSocketOutput, 285 &botanClientData, 286 &botanAlert, 287 &botanHandshake, 288 context.sessions, 289 creds, 290 context.policy, 291 context.rng, 292 serverInfo, 293 ); 294 break; 295 case SSLContext.Kind.server: 296 assert(false, "TODO"); 297 // break; 298 } 299 } 300 301 override void onReadData(Data data) 302 { 303 bool wasActive = channel.isActive(); 304 channel.receivedData(cast(ubyte*)data.ptr, data.length); 305 if (!wasActive && channel.isActive()) 306 super.onConnect(); 307 } 308 309 override void send(Data[] data, int priority) 310 { 311 foreach (datum; data) 312 channel.send(cast(ubyte*)datum.ptr, datum.length); 313 } 314 315 override void setHostName(string hostname, ushort port = 0, string service = null) 316 { 317 serverInfo = TLSServerInformation(hostname, service, port); 318 } 319 320 override SSLCertificate getHostCertificate() 321 { 322 assert(false, "TODO"); 323 } 324 325 override SSLCertificate getPeerCertificate() 326 { 327 assert(false, "TODO"); 328 } 329 330 void botanSocketOutput(in ubyte[] data) 331 { 332 next.send(Data(data, true)); 333 } 334 335 void botanClientData(in ubyte[] data) 336 { 337 super.onReadData(Data(data)); 338 } 339 340 void botanAlert(in TLSAlert alert, in ubyte[] data) 341 { 342 if (alert.isFatal) 343 super.disconnect("Fatal TLS alert: " ~ alert.typeString, DisconnectType.error); 344 } 345 346 bool botanHandshake(in TLSSession session) 347 { 348 debug(BotanSSL) stderr.writeln("Handshake done!"); 349 return true; 350 } 351 } 352 353 class BotanSSLCertificate : SSLCertificate 354 { 355 } 356 357 // *************************************************************************** 358 359 unittest 360 { 361 import std.file : readText; 362 AETLSCredentialsManager.trustedRootCABundleProvider = () => readText("/home/vladimir/Downloads/ca-bundle.crt"); 363 364 void testServer(string host, ushort port) 365 { 366 auto c = new TcpConnection; 367 auto ctx = ssl.createContext(SSLContext.Kind.client); 368 auto s = ssl.createAdapter(ctx, c); 369 Data allData; 370 371 s.handleConnect = 372 { 373 debug(BotanSSL) stderr.writeln("Connected!"); 374 s.send(Data("GET /d/nettest/testUrl1 HTTP/1.0\r\nHost: thecybershadow.net\r\n\r\n")); 375 }; 376 s.handleReadData = (Data data) 377 { 378 debug(BotanSSL) { stderr.write(cast(string)data.contents); stderr.flush(); } 379 allData ~= data; 380 }; 381 s.handleDisconnect = (string reason, DisconnectType type) 382 { 383 debug(BotanSSL) { stderr.writeln(reason); } 384 assert(type == DisconnectType.graceful); 385 import std.algorithm.searching : endsWith; 386 assert((cast(string)allData.contents).endsWith("Hello world\n")); 387 }; 388 s.setHostName("thecybershadow.net"); 389 c.connect(host, port); 390 socketManager.loop(); 391 } 392 393 testServer("thecybershadow.net", 443); 394 } 395 396 // version (unittest) import ae.net.ssl.test; 397 // unittest { testSSL(new BotanSSLProvider); }