Source code

Revision control

Copy as Markdown

Other Tools

Test Info: Warnings

  • This test gets skipped with pattern: os == 'win' && os_version == '11.26100' && arch == 'x86_64' && msix OR os == 'win' && os_version == '11.26200' && arch == 'x86_64' && msix
  • Manifest: netwerk/test/unit/xpcshell.toml
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
// Tests that when a server sends a fatal decrypt_error alert on a TLS PSK
// resumption attempt where no early data was offered (server did not advertise
// max_early_data_size), Firefox automatically retries with a full handshake.
//
// FaultyServer fires at epoch 2 read (server reading the client's Finished)
// when it detects a stateless PSK resumption without early data
// (ss->statelessResume). The server's write epoch has already advanced past
// epoch 2 by this point, so the alert arrives as
// SSL_ERROR_RX_UNEXPECTED_RECORD_TYPE on the client rather than
// SSL_ERROR_DECRYPT_ERROR_ALERT.
//
// This exercises the Do0RTT(false) path added in Bug 2033073: Firefox sets
// mResumptionAttempted = true when resumptionTokenPresent is true but no early
// data was sent, and ShouldRestartOnResumptionError() then allows the retry.
//
// Companion to test_retry_decrypt_error.js (0-RTT / early-data path).
"use strict";
const { HttpServer } = ChromeUtils.importESModule(
);
const { AppConstants } = ChromeUtils.importESModule(
"resource://gre/modules/AppConstants.sys.mjs"
);
const kHost = "psk-no-early-data-on-resume.example.com";
var httpServer = null;
let resumeCallbackCount = 0;
add_setup(
{
skip_if: () => AppConstants.MOZ_SYSTEM_NSS,
},
async () => {
httpServer = new HttpServer();
// FaultyServer calls back on /callback/2 (epoch 2) for this host.
httpServer.registerPathHandler("/callback/2", () => {
resumeCallbackCount++;
});
httpServer.start(-1);
registerCleanupFunction(async () => {
await httpServer.stop();
});
// Pass false so FaultyServer does NOT set MOZ_TLS_SERVER_0RTT — the
// session ticket issued on the first connection will have
// max_early_data_size = 0, ensuring Firefox never sends early data.
await asyncSetupFaultyServer(httpServer, { use0RTT: false });
Services.prefs.setCharPref("network.dns.localDomains", kHost);
registerCleanupFunction(() => {
Services.prefs.clearUserPref("network.dns.localDomains");
});
}
);
add_task(
{
skip_if: () => AppConstants.MOZ_SYSTEM_NSS,
},
async function test_retry_on_psk_no_early_data() {
// First connection: no cached session ticket → full TLS 1.3 handshake.
// The server issues a NewSessionTicket WITHOUT max_early_data_size, so
// Firefox will offer the PSK on resumption but will NOT send early data.
// FaultyServer does NOT fire here (no PSK, no statelessResume).
{
let beforeCallbacks = resumeCallbackCount;
let chan = makeChan(`https://${kHost}:8443/`);
let [, buf] = await channelOpenPromise(chan, CL_ALLOW_UNKNOWN_CL);
ok(buf, "first connection succeeded");
equal(
resumeCallbackCount,
beforeCallbacks,
"FaultyServer did not fire on fresh handshake"
);
}
// Second connection: Firefox finds the cached session ticket and includes
// it as a PSK in the ClientHello — but sends NO early data because the
// ticket has no max_early_data_size. FaultyServer detects the stateless
// PSK resumption at epoch 2 read and sends a fatal alert. Firefox must
// set mResumptionAttempted = true via Do0RTT(false), recognise the error
// via ShouldRestartOnResumptionError(), evict the stale token, and retry
// with a fresh handshake that succeeds.
{
let beforeCallbacks = resumeCallbackCount;
let chan = makeChan(`https://${kHost}:8443/`);
let [, buf] = await channelOpenPromise(chan, CL_ALLOW_UNKNOWN_CL);
ok(buf, "second connection succeeded after PSK rejection retry");
equal(
resumeCallbackCount,
beforeCallbacks + 1,
"FaultyServer fired exactly once (on the PSK resumption attempt)"
);
}
}
);