test_tls_server.js (8394B)
1 /* Any copyright is dedicated to the Public Domain. 2 http://creativecommons.org/publicdomain/zero/1.0/ */ 3 4 "use strict"; 5 6 // Need profile dir to store the key / cert 7 do_get_profile(); 8 // Ensure PSM is initialized 9 Cc["@mozilla.org/psm;1"].getService(Ci.nsISupports); 10 11 const { MockRegistrar } = ChromeUtils.importESModule( 12 "resource://testing-common/MockRegistrar.sys.mjs" 13 ); 14 const certOverrideService = Cc[ 15 "@mozilla.org/security/certoverride;1" 16 ].getService(Ci.nsICertOverrideService); 17 const socketTransportService = Cc[ 18 "@mozilla.org/network/socket-transport-service;1" 19 ].getService(Ci.nsISocketTransportService); 20 21 const prefs = Services.prefs; 22 23 function areCertsEqual(certA, certB) { 24 let derA = certA.getRawDER(); 25 let derB = certB.getRawDER(); 26 if (derA.length != derB.length) { 27 return false; 28 } 29 for (let i = 0; i < derA.length; i++) { 30 if (derA[i] != derB[i]) { 31 return false; 32 } 33 } 34 return true; 35 } 36 37 function startServer( 38 cert, 39 expectingPeerCert, 40 clientCertificateConfig, 41 expectedVersion, 42 expectedVersionStr 43 ) { 44 let tlsServer = Cc["@mozilla.org/network/tls-server-socket;1"].createInstance( 45 Ci.nsITLSServerSocket 46 ); 47 tlsServer.init(-1, true, -1); 48 tlsServer.serverCert = cert; 49 50 let input, output; 51 52 let listener = { 53 onSocketAccepted(socket, transport) { 54 info("Accept TLS client connection"); 55 let connectionInfo = transport.securityCallbacks.getInterface( 56 Ci.nsITLSServerConnectionInfo 57 ); 58 connectionInfo.setSecurityObserver(listener); 59 input = transport.openInputStream(0, 0, 0); 60 output = transport.openOutputStream(0, 0, 0); 61 }, 62 onHandshakeDone(socket, status) { 63 info("TLS handshake done"); 64 if (expectingPeerCert) { 65 ok(!!status.peerCert, "Has peer cert"); 66 ok( 67 areCertsEqual(status.peerCert, cert), 68 "Peer cert matches expected cert" 69 ); 70 } else { 71 ok(!status.peerCert, "No peer cert (as expected)"); 72 } 73 74 equal( 75 status.tlsVersionUsed, 76 expectedVersion, 77 "Using " + expectedVersionStr 78 ); 79 let expectedCipher; 80 if (expectedVersion >= 772) { 81 expectedCipher = "TLS_AES_128_GCM_SHA256"; 82 } else { 83 expectedCipher = "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256"; 84 } 85 equal(status.cipherName, expectedCipher, "Using expected cipher"); 86 equal(status.keyLength, 128, "Using 128-bit key"); 87 equal(status.macLength, 128, "Using 128-bit MAC"); 88 89 input.asyncWait( 90 { 91 onInputStreamReady(input1) { 92 NetUtil.asyncCopy(input1, output); 93 }, 94 }, 95 0, 96 0, 97 Services.tm.currentThread 98 ); 99 }, 100 onStopListening() { 101 info("onStopListening"); 102 input.close(); 103 output.close(); 104 }, 105 }; 106 107 tlsServer.setSessionTickets(false); 108 tlsServer.setRequestClientCertificate(clientCertificateConfig); 109 110 tlsServer.asyncListen(listener); 111 112 return tlsServer; 113 } 114 115 function storeCertOverride(port, cert) { 116 certOverrideService.rememberValidityOverride( 117 "127.0.0.1", 118 port, 119 {}, 120 cert, 121 true 122 ); 123 } 124 125 function startClient(port, sendClientCert, expectingAlert, tlsVersion) { 126 gClientAuthDialogService.selectCertificate = sendClientCert; 127 let SSL_ERROR_BASE = Ci.nsINSSErrorsService.NSS_SSL_ERROR_BASE; 128 let SSL_ERROR_BAD_CERT_ALERT = SSL_ERROR_BASE + 17; 129 let SSL_ERROR_RX_CERTIFICATE_REQUIRED_ALERT = SSL_ERROR_BASE + 181; 130 let transport = socketTransportService.createTransport( 131 ["ssl"], 132 "127.0.0.1", 133 port, 134 null, 135 null 136 ); 137 let input; 138 let output; 139 140 let inputDeferred = Promise.withResolvers(); 141 let outputDeferred = Promise.withResolvers(); 142 143 let handler = { 144 onTransportStatus(transport1, status) { 145 if (status === Ci.nsISocketTransport.STATUS_CONNECTED_TO) { 146 output.asyncWait(handler, 0, 0, Services.tm.currentThread); 147 } 148 }, 149 150 onInputStreamReady(input1) { 151 try { 152 let data = NetUtil.readInputStreamToString(input1, input1.available()); 153 equal(data, "HELLO", "Echoed data received"); 154 input1.close(); 155 output.close(); 156 ok(!expectingAlert, "No cert alert expected"); 157 inputDeferred.resolve(); 158 } catch (e) { 159 let errorCode = -1 * (e.result & 0xffff); 160 if (expectingAlert) { 161 if ( 162 tlsVersion == Ci.nsITLSClientStatus.TLS_VERSION_1_2 && 163 errorCode == SSL_ERROR_BAD_CERT_ALERT 164 ) { 165 info("Got bad cert alert as expected for tls 1.2"); 166 input1.close(); 167 output.close(); 168 inputDeferred.resolve(); 169 return; 170 } 171 if ( 172 tlsVersion == Ci.nsITLSClientStatus.TLS_VERSION_1_3 && 173 errorCode == SSL_ERROR_RX_CERTIFICATE_REQUIRED_ALERT 174 ) { 175 info("Got cert required alert as expected for tls 1.3"); 176 input1.close(); 177 output.close(); 178 inputDeferred.resolve(); 179 return; 180 } 181 } 182 inputDeferred.reject(e); 183 } 184 }, 185 186 onOutputStreamReady(output1) { 187 try { 188 output1.write("HELLO", 5); 189 info("Output to server written"); 190 outputDeferred.resolve(); 191 input = transport.openInputStream(0, 0, 0); 192 input.asyncWait(handler, 0, 0, Services.tm.currentThread); 193 } catch (e) { 194 let errorCode = -1 * (e.result & 0xffff); 195 if (errorCode == SSL_ERROR_BAD_CERT_ALERT) { 196 info("Server doesn't like client cert"); 197 } 198 outputDeferred.reject(e); 199 } 200 }, 201 }; 202 203 transport.setEventSink(handler, Services.tm.currentThread); 204 output = transport.openOutputStream(0, 0, 0); 205 206 return Promise.all([inputDeferred.promise, outputDeferred.promise]); 207 } 208 209 // Replace the UI dialog that prompts the user to pick a client certificate. 210 const gClientAuthDialogService = { 211 _selectCertificate: false, 212 213 set selectCertificate(value) { 214 this._selectCertificate = value; 215 }, 216 217 chooseCertificate(hostname, certArray, loadContext, caNames, callback) { 218 if (this._selectCertificate) { 219 callback.certificateChosen(certArray[0], false); 220 } else { 221 callback.certificateChose(null, false); 222 } 223 }, 224 225 QueryInterface: ChromeUtils.generateQI([Ci.nsIClientAuthDialogService]), 226 }; 227 228 const ClientAuthDialogServiceContractID = 229 "@mozilla.org/security/ClientAuthDialogService;1"; 230 MockRegistrar.register( 231 ClientAuthDialogServiceContractID, 232 gClientAuthDialogService 233 ); 234 235 const tests = [ 236 { 237 expectingPeerCert: true, 238 clientCertificateConfig: Ci.nsITLSServerSocket.REQUIRE_ALWAYS, 239 sendClientCert: true, 240 expectingAlert: false, 241 }, 242 { 243 expectingPeerCert: true, 244 clientCertificateConfig: Ci.nsITLSServerSocket.REQUIRE_ALWAYS, 245 sendClientCert: false, 246 expectingAlert: true, 247 }, 248 { 249 expectingPeerCert: true, 250 clientCertificateConfig: Ci.nsITLSServerSocket.REQUEST_ALWAYS, 251 sendClientCert: true, 252 expectingAlert: false, 253 }, 254 { 255 expectingPeerCert: false, 256 clientCertificateConfig: Ci.nsITLSServerSocket.REQUEST_ALWAYS, 257 sendClientCert: false, 258 expectingAlert: false, 259 }, 260 { 261 expectingPeerCert: false, 262 clientCertificateConfig: Ci.nsITLSServerSocket.REQUEST_NEVER, 263 sendClientCert: true, 264 expectingAlert: false, 265 }, 266 { 267 expectingPeerCert: false, 268 clientCertificateConfig: Ci.nsITLSServerSocket.REQUEST_NEVER, 269 sendClientCert: false, 270 expectingAlert: false, 271 }, 272 ]; 273 274 const versions = [ 275 { 276 prefValue: 3, 277 version: Ci.nsITLSClientStatus.TLS_VERSION_1_2, 278 versionStr: "TLS 1.2", 279 }, 280 { 281 prefValue: 4, 282 version: Ci.nsITLSClientStatus.TLS_VERSION_1_3, 283 versionStr: "TLS 1.3", 284 }, 285 ]; 286 287 add_task(async function () { 288 let cert = getTestServerCertificate(); 289 ok(!!cert, "Got self-signed cert"); 290 for (let v of versions) { 291 prefs.setIntPref("security.tls.version.max", v.prefValue); 292 for (let t of tests) { 293 let server = startServer( 294 cert, 295 t.expectingPeerCert, 296 t.clientCertificateConfig, 297 v.version, 298 v.versionStr 299 ); 300 storeCertOverride(server.port, cert); 301 await startClient( 302 server.port, 303 t.sendClientCert, 304 t.expectingAlert, 305 v.version 306 ); 307 server.close(); 308 } 309 } 310 }); 311 312 registerCleanupFunction(function () { 313 prefs.clearUserPref("security.tls.version.max"); 314 });