From 28a24ea49d31754d3437916bacbd6207dbf35b37 Mon Sep 17 00:00:00 2001 From: patchzyy <64382339+patchzyy@users.noreply.github.com> Date: Mon, 28 Sep 2026 20:41:51 +0200 Subject: [PATCH] Always refresh Retro-WFC payload with fallback --- Launcher/WiiCompiled.Setup.Linux/Program.cs | 35 ++++++++++++---- .../ProductRepairService.cs | 3 +- Launcher/macos/setup.command | 40 +++++++++++++------ 3 files changed, 56 insertions(+), 22 deletions(-) diff --git a/Launcher/WiiCompiled.Setup.Linux/Program.cs b/Launcher/WiiCompiled.Setup.Linux/Program.cs index 9595398..f3af186 100644 --- a/Launcher/WiiCompiled.Setup.Linux/Program.cs +++ b/Launcher/WiiCompiled.Setup.Linux/Program.cs @@ -127,19 +127,40 @@ internal static class Program string? retroWfcOfflineDir = null; if (downloadPayload) { - // Reused if a previous install already downloaded and it's still valid - matches - // Windows's own reuse-if-valid behavior instead of re-downloading on every install. var cacheDir = Path.Combine(workspace, "generated", "retro-wfc-payload"); - reporter.Progress(InstallStages.Validate, "Preparing the Retro-WFC payload", 1); + reporter.Progress(InstallStages.Validate, + "Downloading the current Retro-WFC payload", 1); try { - RetroWfcPayload.ValidateStagedRetroWfcPayloadDirectory(cacheDir); - } - catch (InvalidDataException) - { + // A valid signature authenticates a payload, but does not prove it is the latest + // signed revision. Always ask the fixed endpoint for the current snapshot; the + // downloader verifies it before atomically replacing the cache. await RetroWfcPayload.DownloadRetroWfcPayloadAsync( RetroWfcPayload.CurrentRetroWfcPayloadUri, cacheDir, token); } + catch (Exception downloadFailure) when (!token.IsCancellationRequested && + downloadFailure is HttpRequestException or TimeoutException + or IOException) + { + // Offline installs may continue with a previously authenticated snapshot. Do not + // use this path for a newly downloaded payload that failed signature validation: + // that must remain a hard failure instead of hiding possible endpoint tampering. + try + { + RetroWfcPayload.ValidateStagedRetroWfcPayloadDirectory(cacheDir); + } + catch (Exception cacheFailure) when (cacheFailure is IOException or + UnauthorizedAccessException or InvalidDataException) + { + throw new InvalidOperationException( + "The current Retro-WFC payload could not be downloaded and no valid cached " + + $"payload is available ({cacheFailure.Message.TrimEnd('.')}).", downloadFailure); + } + + reporter.Diagnostic( + "The current Retro-WFC payload could not be downloaded; using the previously " + + $"verified cached payload instead ({downloadFailure.Message.TrimEnd('.')})."); + } retroWfcOfflineDir = cacheDir; } diff --git a/Launcher/WiiCompiled.Setup.Windows/ProductRepairService.cs b/Launcher/WiiCompiled.Setup.Windows/ProductRepairService.cs index 6ff4d94..815890e 100644 --- a/Launcher/WiiCompiled.Setup.Windows/ProductRepairService.cs +++ b/Launcher/WiiCompiled.Setup.Windows/ProductRepairService.cs @@ -110,8 +110,7 @@ internal sealed class ProductRepairService InputValidation.CurrentRetroWfcPayloadUri, payloadScratch, cancellationToken); } catch (Exception ex) when (!cancellationToken.IsCancellationRequested && - ex is HttpRequestException or IOException or InvalidDataException - or InvalidOperationException or OperationCanceledException) + ex is HttpRequestException or TimeoutException or IOException) { payloadSnapshot = RecoverInstalledRetroWfcPayload(toolkitFingerprint, Path.Combine(scratchRoot, "retro-wfc-payload-recovered"), ex, cancellationToken); diff --git a/Launcher/macos/setup.command b/Launcher/macos/setup.command index 09c2915..aac6346 100755 --- a/Launcher/macos/setup.command +++ b/Launcher/macos/setup.command @@ -77,26 +77,40 @@ if [[ -n "$retro_dir" ]]; then # verify its pinned signature before publishing it into the local cache. retro_wfc_dir="$support_root/RetroWfcPayload" retro_wfc_payload="$retro_wfc_dir/binary/payload.RMCPD00.bin" - if [[ -f "$retro_wfc_payload" ]] && ! "$translator" validate-retro-wfc-payload --directory "$retro_wfc_dir"; then - printf 'Discarding an invalid cached Retro-WFC payload...\n' >&2 - rm -f "$retro_wfc_payload" + cached_payload_valid=0 + if [[ -f "$retro_wfc_payload" ]]; then + if "$translator" validate-retro-wfc-payload --directory "$retro_wfc_dir"; then + cached_payload_valid=1 + else + printf 'Discarding an invalid cached Retro-WFC payload...\n' >&2 + rm -f "$retro_wfc_payload" + fi fi - if [[ ! -f "$retro_wfc_payload" ]]; then - printf 'Downloading the Retro-WFC payload needed for online play...\n' - mkdir -p "$retro_wfc_dir" - payload_stage=$(mktemp -d "$retro_wfc_dir/.payload-download.XXXXXX") - temporary_payload="$payload_stage/binary/payload.RMCPD00.bin" - mkdir -p "$(dirname "$temporary_payload")" - trap 'rm -rf "$payload_stage"' EXIT - /usr/bin/curl --fail --silent --show-error --connect-timeout 10 --max-time 30 \ - --retry 1 --output "$temporary_payload" \ - 'https://rwfc.net/api/wfc/payload?g=RMCPD00' || fail 'could not download the Retro-WFC payload needed for online play' + + # A signed cache may still be an older vulnerable revision, so always attempt to replace it + # with the current signed snapshot. A transport failure may fall back to the verified cache; + # a downloaded snapshot with an invalid signature remains a hard failure. + printf 'Downloading the current Retro-WFC payload needed for online play...\n' + mkdir -p "$retro_wfc_dir" + payload_stage=$(mktemp -d "$retro_wfc_dir/.payload-download.XXXXXX") + temporary_payload="$payload_stage/binary/payload.RMCPD00.bin" + mkdir -p "$(dirname "$temporary_payload")" + trap 'rm -rf "$payload_stage"' EXIT + if /usr/bin/curl --fail --silent --show-error --connect-timeout 10 --max-time 30 \ + --retry 1 --output "$temporary_payload" \ + 'https://rwfc.net/api/wfc/payload?g=RMCPD00'; then "$translator" validate-retro-wfc-payload --directory "$payload_stage" || \ fail 'downloaded Retro-WFC payload failed signature validation' mkdir -p "$retro_wfc_dir/binary" mv "$temporary_payload" "$retro_wfc_payload" rmdir "$payload_stage/binary" "$payload_stage" trap - EXIT + elif (( cached_payload_valid )); then + printf 'Could not download the current Retro-WFC payload; using the previously verified cached payload.\n' >&2 + rm -rf "$payload_stage" + trap - EXIT + else + fail 'could not download the current Retro-WFC payload and no valid cached payload is available' fi build_args+=(--profile both --base-output-dir "$products" --retro-rewind-package-dir "$retro_dir" --retro-wfc-offline-dir "$retro_wfc_dir") fi