2.10.7: changelog; release.ps1 signs releases
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,5 +1,15 @@
|
||||
# Changelog
|
||||
|
||||
## 2.10.7
|
||||
|
||||
**Sicherheit**
|
||||
- Daten von Servern und Mitspielern werden auf gültige Längen geprüft – eine gefälschte Längenangabe ließ das Spiel
|
||||
vorher mit „Out of Memory“ abstürzen
|
||||
- Mitspieler können nicht mehr beliebige Spiel-Zustände bei anderen auslösen (z.B. am eigenen Spieler oder an
|
||||
Systemen wie Tod/Speichern) – nur noch Klicks auf klickbare Dinge und echte Schraub-/Montage-Schritte
|
||||
- Uhrzeit/Wetter und Meldungen im Stil von Server-/Admin-Nachrichten („* …“) nur noch vom Admin bzw. Host
|
||||
- Release ist mit dem MSCO-Release-Schlüssel signiert
|
||||
|
||||
## 2.10.6
|
||||
|
||||
- Neuer Konsolenbefehl **oilcheck**: zeigt, woher Öl ausläuft – die Leck-Werte des Motors (Ölwanne, Ölfilter,
|
||||
|
||||
@@ -11,7 +11,7 @@ namespace MSCO
|
||||
/// Version der Mod. Kleine Updates: nur die dritte Stelle erhöhen (2.9.1, 2.9.2 ...) - sie bleiben mit
|
||||
/// Servern und Mitspielern derselben ersten zwei Stellen kompatibel.
|
||||
/// </summary>
|
||||
public const string ModVersion = "2.10.6";
|
||||
public const string ModVersion = "2.10.7";
|
||||
/// <summary>
|
||||
/// Netzwerk-Version (die ersten beiden Stellen). Nur erhöhen, wenn sich das Protokoll inkompatibel ändert - dann
|
||||
/// auch GameServer.ModVersion im Server.
|
||||
|
||||
+60
-3
@@ -6,11 +6,18 @@
|
||||
# %AppData%\MSCO\release-token.dpapi (Windows DPAPI, nur dein Windows-Konto kann ihn lesen - anlegen mit
|
||||
# "release.ps1 -SaveToken"), sonst als Umgebungsvariable MSCO_RELEASE_TOKEN. Ohne Token wird nichts hochgeladen.
|
||||
# MSCO_NO_RELEASE=1 schaltet das Hochladen ab (Test-Builds).
|
||||
#
|
||||
# Signatur: jede Datei wird mit dem privaten Release-Schlüssel signiert (RSA-3072, SHA-256) - der Launcher und das Setup
|
||||
# installieren nur, was zu ihrem eingebauten öffentlichen Schlüssel passt. Selbst wer den Keymaster oder den Token
|
||||
# übernimmt, kann so keine fremde Datei als MSCO-Update verteilen. Der Schlüssel liegt DPAPI-verschlüsselt in
|
||||
# %AppData%\MSCO\release-signing-key.dpapi (einmal anlegen: "release.ps1 -NewSigningKey"; SICHERN: "-ExportSigningKey").
|
||||
# Gibt es die Version auf dem Keymaster schon, passiert nichts - also einfach die Version erhöhen und bauen.
|
||||
param(
|
||||
[ValidateSet("mod", "launcher", "installer")][string]$Product,
|
||||
[string]$File,
|
||||
[switch]$SaveToken,
|
||||
[switch]$NewSigningKey,
|
||||
[string]$ExportSigningKey = "",
|
||||
[string]$Version = "",
|
||||
[string]$Changelog = ""
|
||||
)
|
||||
@@ -36,7 +43,39 @@ if ($SaveToken) {
|
||||
Write-Host "Release token saved encrypted in $tokenDpapi"
|
||||
exit 0
|
||||
}
|
||||
if (-not $Product -or -not $File) { Write-Host "Usage: release.ps1 -Product mod|launcher|installer -File <file> | release.ps1 -SaveToken"; exit 1 }
|
||||
$keyEntropy = [Text.Encoding]::UTF8.GetBytes("MSCO release signing key v1")
|
||||
$keyFile = Join-Path $env:APPDATA "MSCO\release-signing-key.dpapi"
|
||||
|
||||
# Neues Schlüsselpaar (nur einmal - der öffentliche Schlüssel ist in Launcher und Setup eingebaut)
|
||||
if ($NewSigningKey) {
|
||||
if (Test-Path $keyFile) { Write-Host "There already is a signing key ($keyFile) - not replaced"; exit 1 }
|
||||
$rsa = New-Object System.Security.Cryptography.RSACng 3072
|
||||
$private = $rsa.ToXmlString($true)
|
||||
New-Item -ItemType Directory -Force (Split-Path $keyFile) | Out-Null
|
||||
[IO.File]::WriteAllBytes($keyFile, [Security.Cryptography.ProtectedData]::Protect([Text.Encoding]::UTF8.GetBytes($private), $keyEntropy, $scope))
|
||||
Write-Host "Signing key saved encrypted in $keyFile"
|
||||
Write-Host "PUBLIC KEY (put into Launcher/Setup):"
|
||||
Write-Host $rsa.ToXmlString($false)
|
||||
exit 0
|
||||
}
|
||||
# Sicherung des privaten Schlüssels mit eigenem Passwort (AES-256, PBKDF2) - z.B. für einen USB-Stick/Passwort-Manager
|
||||
if ($ExportSigningKey) {
|
||||
if (-not (Test-Path $keyFile)) { Write-Host "No signing key"; exit 1 }
|
||||
$private = [Security.Cryptography.ProtectedData]::Unprotect([IO.File]::ReadAllBytes($keyFile), $keyEntropy, $scope)
|
||||
$pw = Read-Host "Password for the backup" -AsSecureString
|
||||
$plainPw = [Runtime.InteropServices.Marshal]::PtrToStringAuto([Runtime.InteropServices.Marshal]::SecureStringToBSTR($pw))
|
||||
if ($plainPw.Length -lt 12) { Write-Host "Use at least 12 characters"; exit 1 }
|
||||
$salt = New-Object byte[] 16; (New-Object Security.Cryptography.RNGCryptoServiceProvider).GetBytes($salt)
|
||||
$kdf = New-Object Security.Cryptography.Rfc2898DeriveBytes($plainPw, $salt, 300000, [Security.Cryptography.HashAlgorithmName]::SHA256)
|
||||
$aes = [Security.Cryptography.Aes]::Create(); $aes.Key = $kdf.GetBytes(32); $aes.GenerateIV()
|
||||
$enc = $aes.CreateEncryptor().TransformFinalBlock($private, 0, $private.Length)
|
||||
$out = "MSCO-SIGNING-KEY-BACKUP v1`n" + [Convert]::ToBase64String($salt) + "`n" + [Convert]::ToBase64String($aes.IV) + "`n" + [Convert]::ToBase64String($enc)
|
||||
[IO.File]::WriteAllText($ExportSigningKey, $out)
|
||||
Write-Host "Backup written to $ExportSigningKey (AES-256, PBKDF2-SHA256 300000) - keep it and the password safe"
|
||||
exit 0
|
||||
}
|
||||
|
||||
if (-not $Product -or -not $File) { Write-Host "Usage: release.ps1 -Product mod|launcher|installer -File <file> | -SaveToken | -NewSigningKey | -ExportSigningKey <file>"; exit 1 }
|
||||
|
||||
if ($env:MSCO_NO_RELEASE -eq "1") { Write-Host "MSCO release: MSCO_NO_RELEASE=1 - not uploading."; exit 0 }
|
||||
$token = $env:MSCO_RELEASE_TOKEN
|
||||
@@ -82,6 +121,25 @@ if (-not $Changelog) {
|
||||
}
|
||||
}
|
||||
|
||||
# Signieren: "MSCO-RELEASE|<produkt>|<version>|<sha256>" - so passt die Signatur nur zu genau dieser Datei, diesem
|
||||
# Produkt und dieser Version (keine alte Version als neue, keine Mod-DLL als Launcher)
|
||||
$bytes = [System.IO.File]::ReadAllBytes((Resolve-Path $File))
|
||||
$sha = [BitConverter]::ToString([Security.Cryptography.SHA256]::Create().ComputeHash($bytes)).Replace("-", "").ToLowerInvariant()
|
||||
$signature = ""
|
||||
if (Test-Path $keyFile) {
|
||||
try {
|
||||
$private = [Text.Encoding]::UTF8.GetString([Security.Cryptography.ProtectedData]::Unprotect([IO.File]::ReadAllBytes($keyFile), $keyEntropy, $scope))
|
||||
$rsa = New-Object System.Security.Cryptography.RSACng
|
||||
$rsa.FromXmlString($private)
|
||||
$message = [Text.Encoding]::UTF8.GetBytes("MSCO-RELEASE|$Product|$Version|$sha")
|
||||
$signature = [Convert]::ToBase64String($rsa.SignData($message, [Security.Cryptography.HashAlgorithmName]::SHA256, [Security.Cryptography.RSASignaturePadding]::Pkcs1))
|
||||
$private = $null
|
||||
} catch { Write-Host "MSCO release: signing failed ($($_.Exception.Message)) - not uploading"; exit 0 }
|
||||
} else {
|
||||
Write-Host "MSCO release: no signing key ($keyFile) - the launcher will refuse this release. Not uploading."
|
||||
exit 0
|
||||
}
|
||||
|
||||
Add-Type -AssemblyName System.Net.Http
|
||||
$client = New-Object System.Net.Http.HttpClient
|
||||
$client.Timeout = [TimeSpan]::FromMinutes(10)
|
||||
@@ -92,10 +150,9 @@ $boundary = "----MSCO" + [Guid]::NewGuid().ToString("N")
|
||||
$utf8 = New-Object System.Text.UTF8Encoding($false)
|
||||
$body = New-Object System.IO.MemoryStream
|
||||
function Write-Text([string]$text) { $b = $utf8.GetBytes($text); $body.Write($b, 0, $b.Length) }
|
||||
foreach ($field in @(@("product", $Product), @("version", $Version), @("changelog", $Changelog))) {
|
||||
foreach ($field in @(@("product", $Product), @("version", $Version), @("changelog", $Changelog), @("signature", $signature))) {
|
||||
Write-Text "--$boundary`r`nContent-Disposition: form-data; name=`"$($field[0])`"`r`nContent-Type: text/plain; charset=utf-8`r`n`r`n$($field[1])`r`n"
|
||||
}
|
||||
$bytes = [System.IO.File]::ReadAllBytes((Resolve-Path $File))
|
||||
Write-Text "--$boundary`r`nContent-Disposition: form-data; name=`"file`"; filename=`"$([System.IO.Path]::GetFileName($File))`"`r`nContent-Type: application/octet-stream`r`n`r`n"
|
||||
$body.Write($bytes, 0, $bytes.Length)
|
||||
Write-Text "`r`n--$boundary--`r`n"
|
||||
|
||||
Reference in New Issue
Block a user