CultLib/scripts/build-quic-native.ps1
Meta Vens c729dff2ee Pin the Windows MsQuic to the OpenSSL build, and both of its digests
Q9 ruled A. The Schannel MsQuic refuses QUIC_CREDENTIAL_TYPE_CERTIFICATE_PKCS12
with QUIC_STATUS_NOT_SUPPORTED, and PKCS12 is the only credential type a
provider has on both platforms, so a Windows provider could not open a
listener against the runtime this repo was shipping. The same bridge
binary passes all twenty smoke checks and the v1 gate against the OpenSSL
build, which is what Soul verified and what this now downloads.

Two digests, not one. The zip is pinned by FEE9A664 and 19,179,817 bytes;
the msquic.dll inside it by C17C6581 and 4,181,856 bytes. The earlier
report and the cut map's first record put the zip's digest on the DLL,
which is a pin that checks the archive and then ships a file nobody looked
at. Sizes ride along with both, because a digest that fails tells you less
than a digest and a length that fail together.

The CRT is linked statically. A Node or Unity host without the Visual C++
redistributable cannot load a DLL that imports vcruntime140.dll, and
Windows reports that as "the specified module could not be found" while
naming the module that was found. Confirmed on the rebuild: msquic.dll,
CRYPT32, ntdll, KERNEL32, and no CRT import at all. The DLL goes from
83,456 to 302,080 bytes, which is the price of the host not needing
anything installed.

The Unity plugin's committed msquic.dll is still the Schannel one and this
does not touch it; the next build-unity-package.ps1 run is what replaces
it, and that is not in this batch.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-16 22:04:27 +02:00

85 lines
4.4 KiB
PowerShell

param(
[string] $Configuration = "Release",
[string] $Architecture = "x64",
# Per-platform, so the Linux script's output sits beside this one's rather
# than on top of it. build-unity-package.ps1 and the Native.Tests csproj both
# pass an explicit -OutputDirectory, so neither is affected by this moving.
[string] $OutputDirectory = "artifacts\quic-native\win32-x64"
)
$ErrorActionPreference = "Stop"
if ($Architecture -ne "x64") {
throw "The first CultMesh native QUIC package supports x64 Windows desktop only."
}
$repoRoot = Split-Path -Parent $PSScriptRoot
$version = "2.5.9"
# OpenSSL, not Schannel (Q9 ruled A). The Schannel build of MsQuic refuses
# QUIC_CREDENTIAL_TYPE_CERTIFICATE_PKCS12 with QUIC_STATUS_NOT_SUPPORTED, which
# is the only credential type a provider has on both platforms, so a Windows
# provider could not open a listener at all. The identical bridge binary passes
# the whole v2 smoke and the v1 gate against this runtime.
#
# Two digests, because they answer different questions. The zip is what the
# download is pinned to; the DLL is what actually ships beside the bridge, and
# pinning only the archive leaves the file nobody checked.
$expectedPackageSha256 = "FEE9A664C0052DEEE66D40CED2E19BFE20BC423DE5AE5234CFC0CEBC22CA3F5E"
$expectedPackageBytes = 19179817
$expectedRuntimeSha256 = "C17C65813FE007EB486A5270D244F3659261355795B27ABB99BD19FF77539263"
$expectedRuntimeBytes = 4181856
$cacheRoot = Join-Path $repoRoot "artifacts\dependencies\msquic-openssl-$version"
$packagePath = Join-Path $cacheRoot "Microsoft.Native.Quic.MsQuic.OpenSSL.$version.zip"
$extractRoot = Join-Path $cacheRoot "package"
$nativeRoot = Join-Path $extractRoot "build\native"
$buildRoot = Join-Path $repoRoot "artifacts\quic-native-build\$Architecture\$Configuration"
$outputRoot = if ([IO.Path]::IsPathRooted($OutputDirectory)) {
$OutputDirectory
} else {
Join-Path $repoRoot $OutputDirectory
}
function Assert-Pinned {
param([string] $Path, [string] $ExpectedSha256, [long] $ExpectedBytes, [string] $What)
$actualBytes = (Get-Item -LiteralPath $Path).Length
$sha256 = [Security.Cryptography.SHA256]::Create()
try {
$stream = [IO.File]::OpenRead($Path)
try { $actualSha256 = [BitConverter]::ToString($sha256.ComputeHash($stream)).Replace("-", "") }
finally { $stream.Dispose() }
} finally { $sha256.Dispose() }
if ($actualSha256 -ne $ExpectedSha256 -or $actualBytes -ne $ExpectedBytes) {
throw ("$What does not match the pinned release: " +
"expected $ExpectedSha256 ($ExpectedBytes bytes), got $actualSha256 ($actualBytes bytes).")
}
}
New-Item -ItemType Directory -Force -Path $cacheRoot | Out-Null
if (-not (Test-Path -LiteralPath $packagePath)) {
& curl.exe -L --fail --silent --show-error `
-o $packagePath `
"https://www.nuget.org/api/v2/package/Microsoft.Native.Quic.MsQuic.OpenSSL/$version"
if ($LASTEXITCODE -ne 0) { throw "MsQuic package download failed with exit code $LASTEXITCODE" }
}
Assert-Pinned -Path $packagePath -ExpectedSha256 $expectedPackageSha256 `
-ExpectedBytes $expectedPackageBytes -What "The MsQuic NuGet package"
if (-not (Test-Path -LiteralPath (Join-Path $nativeRoot "include\msquic.h"))) {
if (Test-Path -LiteralPath $extractRoot) { Remove-Item -LiteralPath $extractRoot -Recurse -Force }
Expand-Archive -LiteralPath $packagePath -DestinationPath $extractRoot
}
Assert-Pinned -Path (Join-Path $nativeRoot "bin\x64\msquic.dll") `
-ExpectedSha256 $expectedRuntimeSha256 -ExpectedBytes $expectedRuntimeBytes -What "The MsQuic runtime DLL"
cmake -S (Join-Path $repoRoot "native\GameCult.Mesh.Quic.Native") -B $buildRoot -A x64 `
"-DMSQUIC_ROOT=$($nativeRoot.Replace('\', '/'))"
if ($LASTEXITCODE -ne 0) { throw "CultMesh native QUIC configure failed with exit code $LASTEXITCODE" }
cmake --build $buildRoot --config $Configuration
if ($LASTEXITCODE -ne 0) { throw "CultMesh native QUIC build failed with exit code $LASTEXITCODE" }
New-Item -ItemType Directory -Force -Path $outputRoot | Out-Null
Copy-Item -LiteralPath (Join-Path $buildRoot "bin\$Configuration\gamecult_mesh_quic_native.dll") `
-Destination $outputRoot -Force
Copy-Item -LiteralPath (Join-Path $nativeRoot "bin\x64\msquic.dll") -Destination $outputRoot -Force
Copy-Item -LiteralPath (Join-Path $extractRoot "LICENSE") -Destination (Join-Path $outputRoot "MSQUIC-LICENSE.txt") -Force
Write-Host "CultMesh native QUIC runtime: $outputRoot"
Get-ChildItem -LiteralPath $outputRoot -File | Select-Object Name,Length