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>
This commit is contained in:
parent
8c418961c4
commit
c729dff2ee
4 changed files with 54 additions and 19 deletions
|
|
@ -1,7 +1,7 @@
|
|||
cmake_minimum_required(VERSION 3.24)
|
||||
project(gamecult_mesh_quic_native LANGUAGES CXX)
|
||||
|
||||
# Two platforms, one source. Windows takes MsQuic from the pinned Schannel NuGet
|
||||
# Two platforms, one source. Windows takes MsQuic from the pinned OpenSSL NuGet
|
||||
# layout (MSQUIC_ROOT); Linux takes it from the extracted libmsquic deb, whose
|
||||
# headers and library land in unrelated places, so they are named separately.
|
||||
if(NOT WIN32 AND NOT (CMAKE_SYSTEM_NAME STREQUAL "Linux"))
|
||||
|
|
@ -16,8 +16,13 @@ target_include_directories(gamecult_mesh_quic_native PUBLIC include)
|
|||
|
||||
if(WIN32)
|
||||
if(NOT DEFINED MSQUIC_ROOT)
|
||||
message(FATAL_ERROR "MSQUIC_ROOT must point at the extracted Microsoft.Native.Quic.MsQuic.Schannel package build/native directory")
|
||||
message(FATAL_ERROR "MSQUIC_ROOT must point at the extracted Microsoft.Native.Quic.MsQuic.OpenSSL package build/native directory")
|
||||
endif()
|
||||
# Static CRT. A Node or Unity host without the Visual C++ redistributable
|
||||
# installed cannot load a DLL that imports vcruntime140.dll, and the failure
|
||||
# arrives as "the specified module could not be found" with nothing naming the
|
||||
# missing runtime. The bridge carries its own instead.
|
||||
set_property(TARGET gamecult_mesh_quic_native PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreaded")
|
||||
target_compile_definitions(gamecult_mesh_quic_native PRIVATE WIN32_LEAN_AND_MEAN NOMINMAX)
|
||||
target_include_directories(gamecult_mesh_quic_native PRIVATE "${MSQUIC_ROOT}/include")
|
||||
target_link_directories(gamecult_mesh_quic_native PRIVATE "${MSQUIC_ROOT}/lib/x64")
|
||||
|
|
|
|||
|
|
@ -69,8 +69,17 @@ Windows x64, from the repo root:
|
|||
|
||||
powershell -File scripts/build-quic-native.ps1
|
||||
|
||||
MsQuic comes from a pinned NuGet package verified by SHA-256, and the build uses
|
||||
MSVC with `/Brepro` so a rebuild reproduces the committed Unity plugin DLL.
|
||||
MsQuic comes from `Microsoft.Native.Quic.MsQuic.OpenSSL` 2.5.9, pinned twice: the
|
||||
NuGet zip by digest and size, and the `msquic.dll` inside it by digest and size,
|
||||
because the archive is what the download is checked against and the DLL is what
|
||||
actually ships beside the bridge. The OpenSSL flavour rather than Schannel
|
||||
because Schannel's MsQuic refuses `QUIC_CREDENTIAL_TYPE_CERTIFICATE_PKCS12`,
|
||||
which is the only credential type a provider has on both platforms.
|
||||
|
||||
The build uses MSVC with `/Brepro` so a rebuild reproduces the committed Unity
|
||||
plugin DLL, and links the CRT statically (`/MT`). A Node or Unity host without
|
||||
the Visual C++ redistributable cannot load a DLL that imports `vcruntime140.dll`,
|
||||
and the failure it reports names no missing runtime; the bridge carries its own.
|
||||
|
||||
Linux x64, on a Debian 13 host:
|
||||
|
||||
|
|
|
|||
|
|
@ -14,9 +14,21 @@ if ($Architecture -ne "x64") {
|
|||
|
||||
$repoRoot = Split-Path -Parent $PSScriptRoot
|
||||
$version = "2.5.9"
|
||||
$expectedPackageSha256 = "9877A919E1AA73AA4800F1E8A06B6539021F376228910138EB04540CD956617B"
|
||||
$cacheRoot = Join-Path $repoRoot "artifacts\dependencies\msquic-schannel-$version"
|
||||
$packagePath = Join-Path $cacheRoot "Microsoft.Native.Quic.MsQuic.Schannel.$version.zip"
|
||||
# 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"
|
||||
|
|
@ -26,27 +38,36 @@ $outputRoot = if ([IO.Path]::IsPathRooted($OutputDirectory)) {
|
|||
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.Schannel/$version"
|
||||
"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" }
|
||||
}
|
||||
$sha256 = [Security.Cryptography.SHA256]::Create()
|
||||
try {
|
||||
$packageBytes = [IO.File]::ReadAllBytes($packagePath)
|
||||
$packageSha256 = [BitConverter]::ToString($sha256.ComputeHash($packageBytes)).Replace("-", "")
|
||||
} finally {
|
||||
$sha256.Dispose()
|
||||
}
|
||||
if ($packageSha256 -ne $expectedPackageSha256) {
|
||||
throw "MsQuic package digest does not match the pinned release."
|
||||
}
|
||||
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('\', '/'))"
|
||||
|
|
|
|||
|
|
@ -193,4 +193,4 @@ $manifest = Get-Content -LiteralPath (Join-Path $outputRoot "package.json") -Raw
|
|||
Write-Host "CultLib Unity package: $outputRoot"
|
||||
Write-Host "Package: $($manifest.name)@$($manifest.version)"
|
||||
Write-Host "Managed assemblies: $($expectedAssemblies.Count)"
|
||||
Write-Host "Native realtime: MsQuic Schannel 2.5.9 (Windows x64)"
|
||||
Write-Host "Native realtime: MsQuic OpenSSL 2.5.9 (Windows x64)"
|
||||
|
|
|
|||
Loading…
Reference in a new issue