Add hash check to updater

This commit is contained in:
Natsumi
2023-06-18 10:03:05 +12:00
parent 21e8b30380
commit cc3f6f7ecd
4 changed files with 89 additions and 35 deletions

View File

@@ -428,20 +428,6 @@ namespace VRCX
broadcastSocket.SendTo(byteBuffer, endPoint); broadcastSocket.SendTo(byteBuffer, endPoint);
} }
/// <summary>
/// Downloads the VRCX update executable from the specified URL and saves it to the AppData directory.
/// </summary>
/// <param name="url">The URL of the VRCX update to download.</param>
public void DownloadVRCXUpdate(string url)
{
var Location = Path.Combine(Program.AppDataDirectory, "update.exe");
using (var client = new WebClient())
{
client.Headers.Add("user-agent", Program.Version);
client.DownloadFile(new Uri(url), Location);
}
}
/// <summary> /// <summary>
/// Restarts the VRCX application for an update by launching a new process with the "/Upgrade" argument and exiting the current process. /// Restarts the VRCX application for an update by launching a new process with the "/Upgrade" argument and exiting the current process.
/// </summary> /// </summary>

View File

@@ -26,6 +26,7 @@ namespace VRCX
public static string DownloadTempLocation; public static string DownloadTempLocation;
public static string DownloadDestinationLocation; public static string DownloadDestinationLocation;
public static string DownloadHashLocation;
public static int DownloadProgress; public static int DownloadProgress;
public static int DownloadSize; public static int DownloadSize;
public static bool DownloadCanceled; public static bool DownloadCanceled;
@@ -104,18 +105,25 @@ namespace VRCX
}; };
} }
public void DownloadFile(string url, int size) // old asset bundle cacher downloader method reused for updating, it's not pretty
public void DownloadFile(string fileUrl, string hashUrl, int size)
{ {
client = new WebClient();
client.Headers.Add("user-agent", Program.Version);
DownloadProgress = 0; DownloadProgress = 0;
DownloadSize = size; DownloadSize = size;
DownloadCanceled = false; DownloadCanceled = false;
DownloadTempLocation = Path.Combine(Program.AppDataDirectory, "tempDownload.exe"); DownloadTempLocation = Path.Combine(Program.AppDataDirectory, "tempDownload.exe");
DownloadDestinationLocation = Path.Combine(Program.AppDataDirectory, "update.exe"); DownloadDestinationLocation = Path.Combine(Program.AppDataDirectory, "update.exe");
client = new WebClient(); DownloadHashLocation = Path.Combine(Program.AppDataDirectory, "sha256sum.txt");
client.Headers.Add("user-agent", Program.Version); if (File.Exists(DownloadHashLocation))
File.Delete(DownloadHashLocation);
if (!string.IsNullOrEmpty(hashUrl))
client.DownloadFile(new Uri(hashUrl), DownloadHashLocation);
client.DownloadProgressChanged += new DownloadProgressChangedEventHandler(DownloadProgressCallback); client.DownloadProgressChanged += new DownloadProgressChangedEventHandler(DownloadProgressCallback);
client.DownloadFileCompleted += new AsyncCompletedEventHandler(DownloadCompletedCallback); client.DownloadFileCompleted += new AsyncCompletedEventHandler(DownloadCompletedCallback);
client.DownloadFileAsync(new System.Uri(url), DownloadTempLocation); client.DownloadFileAsync(new Uri(fileUrl), DownloadTempLocation);
} }
public void CancelDownload() public void CancelDownload()
@@ -163,6 +171,27 @@ namespace VRCX
DownloadProgress = -15; DownloadProgress = -15;
return; return;
} }
if (File.Exists(DownloadHashLocation))
{
logger.Info("Updater: Checking hash");
var lines = File.ReadAllLines(DownloadHashLocation);
var hash = lines.Length > 0 ? lines[0].Split(' ') : new[] { "" };
using (var sha256 = SHA256.Create())
using (var stream = File.OpenRead(DownloadTempLocation))
{
var hashBytes = sha256.ComputeHash(stream);
var hashString = BitConverter.ToString(hashBytes).Replace("-", "").ToLowerInvariant();
if (hashString != hash[0])
{
logger.Error($"Updater: Hash check failed file:{hashString} remote:{hash[0]}");
// can't delete file yet because it's in use
DownloadProgress = -14;
return;
}
}
logger.Info("Updater: Hash check passed");
}
if (File.Exists(DownloadDestinationLocation)) if (File.Exists(DownloadDestinationLocation))
File.Delete(DownloadDestinationLocation); File.Delete(DownloadDestinationLocation);
File.Move(DownloadTempLocation, DownloadDestinationLocation); File.Move(DownloadTempLocation, DownloadDestinationLocation);

View File

@@ -20,6 +20,12 @@ namespace VRCX
{ {
if (Process.GetProcessesByName("VRCX_Setup").Length > 0) if (Process.GetProcessesByName("VRCX_Setup").Length > 0)
Environment.Exit(0); Environment.Exit(0);
var setupHash = Path.Combine(Program.AppDataDirectory, "sha256sum.txt");
if (File.Exists(setupHash))
File.Delete(setupHash);
var tempDownload = Path.Combine(Program.AppDataDirectory, "tempDownload.exe");
if (File.Exists(tempDownload))
File.Delete(tempDownload);
if (File.Exists(VRCX_Setup_Executable)) if (File.Exists(VRCX_Setup_Executable))
File.Delete(VRCX_Setup_Executable); File.Delete(VRCX_Setup_Executable);
if (File.Exists(Update_Executable)) if (File.Exists(Update_Executable))

View File

@@ -21952,9 +21952,10 @@ speechSynthesis.getVoices();
this.downloadQueue.delete(ref.id); this.downloadQueue.delete(ref.id);
this.downloadQueueTable.data = Array.from(this.downloadQueue.values()); this.downloadQueueTable.data = Array.from(this.downloadQueue.values());
var url = this.downloadCurrent.updateZipUrl; var fileUrl = this.downloadCurrent.updateSetupUrl;
var hashUrl = this.downloadCurrent.updateHashUrl;
var size = this.downloadCurrent.size; var size = this.downloadCurrent.size;
await AssetBundleCacher.DownloadFile(url, size); await AssetBundleCacher.DownloadFile(fileUrl, hashUrl, size);
this.downloadFileProgress(); this.downloadFileProgress();
}; };
@@ -21993,9 +21994,16 @@ speechSynthesis.getVoices();
}); });
this.downloadFileComplete('Canceled'); this.downloadFileComplete('Canceled');
return; return;
case -14:
this.$message({
message: 'Download failed, hash mismatch',
type: 'error'
});
this.downloadFileComplete('Failed');
return;
case -15: case -15:
this.$message({ this.$message({
message: 'Download failed', message: 'Download failed, size mismatch',
type: 'error' type: 'error'
}); });
this.downloadFileComplete('Failed'); this.downloadFileComplete('Failed');
@@ -22803,7 +22811,8 @@ speechSynthesis.getVoices();
}; };
$app.methods.downloadVRCXUpdate = function ( $app.methods.downloadVRCXUpdate = function (
updateZipUrl, updateSetupUrl,
updateHashUrl,
size, size,
name, name,
type, type,
@@ -22816,7 +22825,8 @@ speechSynthesis.getVoices();
this.downloadQueue.set('VRCXUpdate', { this.downloadQueue.set('VRCXUpdate', {
ref, ref,
type, type,
updateZipUrl, updateSetupUrl,
updateHashUrl,
size, size,
autoInstall autoInstall
}); });
@@ -22829,17 +22839,27 @@ speechSynthesis.getVoices();
$app.methods.installVRCXUpdate = function () { $app.methods.installVRCXUpdate = function () {
for (var release of this.VRCXUpdateDialog.releases) { for (var release of this.VRCXUpdateDialog.releases) {
if (release.name === this.VRCXUpdateDialog.release) { if (release.name === this.VRCXUpdateDialog.release) {
var downloadUrl = '';
var hashUrl = '';
var size = 0;
for (var asset of release.assets) { for (var asset of release.assets) {
if (asset.state !== 'uploaded') {
continue;
}
if ( if (
(asset.content_type === 'application/x-msdownload' || asset.content_type === 'application/x-msdownload' ||
asset.content_type === asset.content_type === 'application/x-msdos-program'
'application/x-msdos-program') &&
asset.state === 'uploaded'
) { ) {
var downloadUrl = asset.browser_download_url; downloadUrl = asset.browser_download_url;
var size = asset.size; size = asset.size;
break; break;
} }
if (
asset.name === 'SHA256SUMS.txt' &&
asset.content_type === 'text/plain'
) {
hashUrl = asset.browser_download_url;
}
} }
if (!downloadUrl) { if (!downloadUrl) {
return; return;
@@ -22849,6 +22869,7 @@ speechSynthesis.getVoices();
var autoInstall = false; var autoInstall = false;
this.downloadVRCXUpdate( this.downloadVRCXUpdate(
downloadUrl, downloadUrl,
hashUrl,
size, size,
name, name,
type, type,
@@ -22960,17 +22981,27 @@ speechSynthesis.getVoices();
// update already downloaded // update already downloaded
this.VRCXUpdateDialog.updatePendingIsLatest = true; this.VRCXUpdateDialog.updatePendingIsLatest = true;
} else if (name > this.appVersion) { } else if (name > this.appVersion) {
var downloadUrl = '';
var hashUrl = '';
var size = 0;
for (var asset of json.assets) { for (var asset of json.assets) {
if (asset.state !== 'uploaded') {
continue;
}
if ( if (
(asset.content_type === 'application/x-msdownload' || asset.content_type === 'application/x-msdownload' ||
asset.content_type === asset.content_type === 'application/x-msdos-program'
'application/x-msdos-program') &&
asset.state === 'uploaded'
) { ) {
var downloadUrl = asset.browser_download_url; downloadUrl = asset.browser_download_url;
var size = asset.size; size = asset.size;
break; break;
} }
if (
asset.name === 'SHA256SUMS.txt' &&
asset.content_type === 'text/plain'
) {
hashUrl = asset.browser_download_url;
}
} }
if (!downloadUrl) { if (!downloadUrl) {
return; return;
@@ -22986,6 +23017,7 @@ speechSynthesis.getVoices();
var autoInstall = false; var autoInstall = false;
this.downloadVRCXUpdate( this.downloadVRCXUpdate(
downloadUrl, downloadUrl,
hashUrl,
size, size,
name, name,
type, type,
@@ -22995,6 +23027,7 @@ speechSynthesis.getVoices();
var autoInstall = true; var autoInstall = true;
this.downloadVRCXUpdate( this.downloadVRCXUpdate(
downloadUrl, downloadUrl,
hashUrl,
size, size,
name, name,
type, type,