Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions .github/workflows/ci-validation.yml
Original file line numberDiff line numberDiff line change
Expand Up@@ -142,9 +142,37 @@ jobs:
New-Item -ItemType Directory -Path $dest -Force | Out-Null
Copy-Item "$outDir\*" -Destination $dest -Recurse -Force

- name: Validate ARM64 driver package (CI policy)
if: matrix.platform == 'ARM64'
shell: pwsh
run: |
$ErrorActionPreference = "Stop"
./scripts/Test-Arm64DriverPackage.ps1 `
-PackagePath "artifacts/VDD/ARM64" `
-Policy CI

- name: Create ARM64 attestation CAB
if: matrix.platform == 'ARM64'
shell: pwsh
run: |
$ErrorActionPreference = "Stop"
$result = ./scripts/New-AttestationCab.ps1 `
-InputDirectory "artifacts/VDD/ARM64" `
-OutputDirectory "artifacts/VDD/ARM64" `
-CabFileName "MttVDD-ARM64-Attestation.cab"
Write-Output "Created attestation CAB at $($result.CabPath)"

- name: Upload artifacts
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: VDD-${{ matrix.platform }}-${{ env.BUILD_CONFIGURATION }}
path: artifacts/VDD/${{ matrix.platform }}/
if-no-files-found: error

- name: Upload ARM64 attestation CAB
if: matrix.platform == 'ARM64'
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: VDD-ARM64-Attestation-CAB
path: artifacts/VDD/ARM64/MttVDD-ARM64-Attestation.cab
if-no-files-found: error
121 changes: 121 additions & 0 deletions docs/windows-arm64-signing.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
# Windows ARM64 signing and maintainer handoff

This runbook describes how Virtual Display Driver maintainers can produce a **Microsoft-signed ARM64 preview** that installs on HVCI-enabled Windows on ARM systems (for example Surface Laptop 7 with Snapdragon X), and how to graduate that package to WHCP certification later.

## Background

- RealWarp and other consumers detect `MttVDD` through `Root\MttVDD` and `\\.\pipe\MTTVirtualDisplayPipe`.
- The upstream ARM64 release payload is architecturally correct (`NTARM64`, native `MttVDD.dll`), but SignPath/GlobalSign catalogs are rejected by the Driver Store on systems with Memory Integrity enabled (`0x800B0109` / untrusted root).
- `PnpLockdown=1` in `MttVDD.inf` is **installed-file protection** and should remain enabled; it is not a WHQL-only switch.

Related issues:

- [#465](https://github.com/VirtualDrivers/Virtual-Display-Driver/issues/465) — ARM64 package trust failure on install
- [#483](https://github.com/VirtualDrivers/Virtual-Display-Driver/issues/483) — post-install display attachment on ARM

## Repository automation

| Script | Purpose |
| --- | --- |
| `scripts/Test-Arm64DriverPackage.ps1` | Validates folder or CAB payloads (`CI` or `Release` policy) |
| `scripts/New-AttestationCab.ps1` | Builds a Partner Center-ready attestation CAB from ARM64 Release output |
| `scripts/Invoke-Arm64AcceptanceTest.ps1` | Surface acceptance harness (Secure Boot/HVCI + Release signature gate + optional install) |

GitHub Actions (`.github/workflows/ci-validation.yml`) builds ARM64 Release output, runs **CI** validation, creates the attestation CAB, round-trip validates the CAB, and uploads:

- `VDD-ARM64-Release` — raw build output
- `VDD-ARM64-Attestation-CAB` — submission CAB (unsigned; EV signing happens offline)

## Maintainer workflow

### 1. Build and validate locally or from CI

```powershell
# After ARM64 Release build output exists:
./scripts/Test-Arm64DriverPackage.ps1 `
-PackagePath "Virtual Display Driver (HDR)\ARM64\Release\MttVDD" `
-Policy CI

./scripts/New-AttestationCab.ps1 `
-InputDirectory "Virtual Display Driver (HDR)\ARM64\Release\MttVDD" `
-OutputDirectory "artifacts\VDD\ARM64"
```

Expected payload inside the CAB subfolder:

- `MttVDD.inf`
- `MttVDD.dll` (PE machine `0xAA64`)
- `MttVDD.pdb`
- `MttVDD.cat` (company catalog for verification; Microsoft replaces it)

Requirements from Microsoft attestation signing:

- Driver files must live in a **single subfolder** inside the CAB (never at CAB root).
- Subfolder name must be **fewer than 40 characters** and use no UNC paths during packaging.
- See [Attestation sign Windows drivers](https://learn.microsoft.com/windows-hardware/drivers/dashboard/code-signing-attestation).

### 2. EV-sign the CAB offline

Use the organization EV certificate and SignTool (SHA-256):

```cmd
SignTool sign /s MY /n "Company Name" /fd sha256 /tr http://timestamp.digicert.com /td sha256 /v MttVDD-ARM64-Attestation.cab
```

Do **not** store EV credentials in GitHub Actions.

### 3. Submit attestation preview in Partner Center

1. Open the [Partner Center hardware dashboard](https://partner.microsoft.com/dashboard/hardware/Search).
2. Choose **Submit new hardware**.
3. Upload the **EV-signed CAB**.
4. Leave test-signing options **unchecked** for the production-preview path on HVCI systems.
5. Request the Windows Desktop signatures needed for ARM64 user-mode driver attestation.
6. Download Microsoft’s returned package when processing completes.

Reference: [Driver signing options and best practices — attestation for testing scenarios](https://learn.microsoft.com/windows-hardware/drivers/dashboard/driver-signing-offerings#attestation-signed-drivers-for-testing-scenarios).

### 4. Validate the returned package (Release policy)

```powershell
./scripts/Test-Arm64DriverPackage.ps1 `
-PackagePath ".\downloads\MttVDD-ARM64-MicrosoftSigned.cab" `
-Policy Release
```

Release policy requires a catalog subject containing **Microsoft Windows Hardware Compatibility Publisher** and rejects SignPath-only catalogs.

Optional Surface preflight:

```powershell
./scripts/Invoke-Arm64AcceptanceTest.ps1 `
-PackagePath ".\downloads\MttVDD-ARM64-MicrosoftSigned.cab" `
-Mode Preflight
```

### 5. Publish an ARM64 preview release

1. Extract the Microsoft-signed package into a clearly named release asset, for example `VirtualDisplayDriver-ARM64.Driver.Only.zip`.
2. Mark the release as **ARM64 preview / attestation-signed** in release notes.
3. Link issues #465 and #483 and note that WHCP certification is the follow-up for broad retail/Windows Update distribution.

### 6. Graduate to WHCP (later)

Reuse the same validated ARM64 payload for HLK/WHCP submission when an ARM64 HLK lab is available. Attestation preview unblocks HVCI-enabled test machines; WHCP remains the supported public-release path.

## Acceptance criteria on Windows on ARM

Run with Secure Boot and Memory Integrity **enabled** (no test-signing boot configuration or trust-store workarounds):

1. **Static gate** — `Test-Arm64DriverPackage.ps1 -Policy Release` passes.
2. **Install gate** — `Invoke-Arm64AcceptanceTest.ps1 -Mode Install` installs `Root\MttVDD` with device status OK and no Code Integrity rejection.
3. **Display gate** — one free-tier virtual display attaches to extended topology (DisplayConfig / `EnumDisplayMonitors` / DXGI).
4. **RealWarp gate** — RealWarp 0.38.0 (x64 emulation) detects MttVDD, connects to `MTTVirtualDisplayPipe`, captures desktop, and renders on XREAL One / One Pro.

If signing succeeds but topology fails, treat that as issue #483 (IddCx attachment) rather than changing signing policy.

## Evidence captured on Surface (SignPath baseline)

The current public ARM64 package (`25.7.23`) fails Release validation because the catalog chains to **SignPath Foundation / GlobalSign**, not Microsoft WHCP. On HVCI-enabled systems this surfaces as Driver Store staging error **`0x800B0109`** (*A certificate chain processed, but terminated in a root certificate which is not trusted by the trust provider*).

That failure occurs **before** UMDF/IddCx load; replacing RealWarp’s bundled x64 Parsec driver with a **Microsoft-signed ARM64 MttVDD** package is the required production path.
210 changes: 210 additions & 0 deletions scripts/Invoke-Arm64AcceptanceTest.ps1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,210 @@
#Requires -Version 5.1
<#
.SYNOPSIS
Runs Surface acceptance checks for a Microsoft-signed ARM64 MttVDD package.

.DESCRIPTION
Preflight mode verifies Secure Boot and HVCI remain enabled and validates the package
with Release policy. Install mode performs NefCon-based installation and display
topology checks when Release validation succeeds.

.PARAMETER PackagePath
Directory or CAB containing the Microsoft-signed ARM64 driver package.

.PARAMETER Mode
Preflight - security and signature validation only (default)
Install - install driver and verify device/display topology when signature gate passes

.PARAMETER NefConPath
Optional path to nefconw.exe. Defaults to ARM64 binary extracted from the latest NefCon release.
#>
[CmdletBinding()]
param(
[Parameter(Mandatory = $true)]
[ValidateNotNullOrEmpty()]
[string]$PackagePath,

[ValidateSet('Preflight', 'Install')]
[string]$Mode = 'Preflight',

[string]$NefConPath
)

Set-StrictMode -Version Latest
$ErrorActionPreference = 'Stop'

function Test-SecurityGates {
$results = @()

$secureBoot = $false
try {
$secureBoot = Confirm-SecureBootUEFI -ErrorAction Stop
}
catch {
$reg = Get-ItemProperty -Path 'HKLM:\SYSTEM\CurrentControlSet\Control\SecureBoot\State' -Name UEFISecureBootEnabled -ErrorAction SilentlyContinue
$secureBoot = ($reg.UEFISecureBootEnabled -eq 1)
}

$results += [pscustomobject]@{
Check = 'SecureBoot'
Status = $(if ($secureBoot) { 'PASS' } else { 'FAIL' })
Detail = $(if ($secureBoot) { 'Secure Boot is enabled.' } else { 'Secure Boot is disabled or could not be verified.' })
}

$hvciEnabled = $false
try {
$ci = Get-CimInstance -ClassName Win32_DeviceGuard -Namespace root\Microsoft\Windows\DeviceGuard -ErrorAction Stop
$hvciEnabled = ($ci.SecurityServicesRunning -contains 1) -or ($ci.VirtualizationBasedSecurityStatus -ge 2)
}
catch {
$reg = Get-ItemProperty -Path 'HKLM:\SYSTEM\CurrentControlSet\Control\DeviceGuard\Scenarios\HypervisorEnforcedCodeIntegrity' -Name Enabled -ErrorAction SilentlyContinue
$hvciEnabled = ($reg.Enabled -eq 1)
}

$results += [pscustomobject]@{
Check = 'HVCI'
Status = $(if ($hvciEnabled) { 'PASS' } else { 'FAIL' })
Detail = $(if ($hvciEnabled) { 'Memory Integrity / HVCI appears enabled.' } else { 'Memory Integrity / HVCI does not appear enabled.' })
}

return $results
}

function Get-DriverPackageDirectory {
param(
[string]$Path
)

$item = Get-Item -LiteralPath $Path
if ($item.PSIsContainer) {
$inf = Get-ChildItem -LiteralPath $item.FullName -Filter 'MttVDD.inf' -Recurse -File | Select-Object -First 1
if (-not $inf) {
throw "MttVDD.inf not found under $Path"
}
return $inf.Directory.FullName
}

$expandRoot = Join-Path ([System.IO.Path]::GetTempPath()) ("vdd-arm64-accept-" + [guid]::NewGuid().ToString('N'))
New-Item -ItemType Directory -Path $expandRoot -Force | Out-Null
expand.exe $item.FullName -F:* $expandRoot | Out-Null
if ($LASTEXITCODE -ne 0) {
throw "Failed to expand CAB: $Path"
}

$inf = Get-ChildItem -LiteralPath $expandRoot -Filter 'MttVDD.inf' -Recurse -File | Select-Object -First 1
if (-not $inf) {
throw "MttVDD.inf not found in expanded CAB."
}

return $inf.Directory.FullName
}

function Ensure-NefCon {
param(
[string]$ExplicitPath
)

if ($ExplicitPath) {
if (-not (Test-Path -LiteralPath $ExplicitPath)) {
throw "NefCon not found at $ExplicitPath"
}
return (Resolve-Path -LiteralPath $ExplicitPath).Path
}

$tempDir = Join-Path $env:TEMP 'VDDAcceptanceNefCon'
$arm64Exe = Join-Path $tempDir 'ARM64\nefconw.exe'
if (-not (Test-Path -LiteralPath $arm64Exe)) {
New-Item -ItemType Directory -Path $tempDir -Force | Out-Null
$zipPath = Join-Path $tempDir 'nefcon.zip'
Invoke-WebRequest -Uri 'https://github.com/nefarius/nefcon/releases/download/v1.14.0/nefcon_v1.14.0.zip' -OutFile $zipPath -UseBasicParsing
Expand-Archive -LiteralPath $zipPath -DestinationPath $tempDir -Force
}

if (-not (Test-Path -LiteralPath $arm64Exe)) {
throw 'ARM64 nefconw.exe not found after extraction.'
}

return (Resolve-Path -LiteralPath $arm64Exe).Path
}

$validator = Join-Path $PSScriptRoot 'Test-Arm64DriverPackage.ps1'
if (-not (Test-Path -LiteralPath $validator)) {
throw "Validation script not found: $validator"
}

Write-Host '=== ARM64 MttVDD acceptance preflight ===' -ForegroundColor Cyan
$securityResults = Test-SecurityGates
$securityResults | ForEach-Object {
$color = if ($_.Status -eq 'PASS') { 'Green' } else { 'Red' }
Write-Host ("[{0}] {1} - {2}" -f $_.Status, $_.Check, $_.Detail) -ForegroundColor $color
}

if (@($securityResults | Where-Object { $_.Status -eq 'FAIL' }).Count -gt 0) {
throw 'Security gate failed. Acceptance testing requires Secure Boot and HVCI enabled.'
}

Write-Host '=== Release signature validation ===' -ForegroundColor Cyan
& $validator -PackagePath $PackagePath -Policy Release

if ($Mode -eq 'Preflight') {
Write-Host 'Preflight acceptance checks passed. Install mode can proceed once a Microsoft-signed package is available.' -ForegroundColor Green
return
}

$packageDir = Get-DriverPackageDirectory -Path $PackagePath
$nefcon = Ensure-NefCon -ExplicitPath $NefConPath
$infPath = Join-Path $packageDir 'MttVDD.inf'

Write-Host '=== Installing Root\MttVDD via NefCon ===' -ForegroundColor Cyan
Push-Location $packageDir
try {
& $nefcon install $infPath 'Root\MttVDD'
if ($LASTEXITCODE -ne 0) {
throw "nefconw install failed with exit code $LASTEXITCODE"
}
}
finally {
Pop-Location
}

Start-Sleep -Seconds 5

$pnpDevice = Get-PnpDevice -FriendlyName '*Virtual Display Driver*' -ErrorAction SilentlyContinue | Select-Object -First 1
if (-not $pnpDevice) {
$pnpDevice = Get-PnpDevice -InstanceId '*Root\MttVDD*' -ErrorAction SilentlyContinue | Select-Object -First 1
}

if (-not $pnpDevice -or $pnpDevice.Status -ne 'OK') {
throw 'MttVDD device not present or not healthy after install.'
}

Write-Host ("Device {0} status: {1}" -f $pnpDevice.InstanceId, $pnpDevice.Status) -ForegroundColor Green

try {
Add-Type @"
using System;
using System.Runtime.InteropServices;
public static class MonitorEnumAcceptance {
public delegate bool EnumMonitorsDelegate(IntPtr hMonitor, IntPtr hdcMonitor, ref RECT lprcMonitor, IntPtr dwData);
[StructLayout(LayoutKind.Sequential)]
public struct RECT { public int Left; public int Top; public int Right; public int Bottom; }
[DllImport("user32.dll")]
public static extern bool EnumDisplayMonitors(IntPtr hdc, IntPtr lprcClip, EnumMonitorsDelegate lpfnEnum, IntPtr dwData);
public static int CountMonitors() {
int count = 0;
EnumMonitorsDelegate callback = delegate (IntPtr hMonitor, IntPtr hdcMonitor, ref RECT lprcMonitor, IntPtr dwData) {
count++;
return true;
};
EnumDisplayMonitors(IntPtr.Zero, IntPtr.Zero, callback, IntPtr.Zero);
return count;
}
}
"@
$monitorCount = [MonitorEnumAcceptance]::CountMonitors()
Write-Host "EnumDisplayMonitors reported $monitorCount monitors." -ForegroundColor Green
}
catch {
Write-Warning "EnumDisplayMonitors probe unavailable: $($_.Exception.Message)"
}
Write-Host 'Install acceptance checks completed. Verify extended topology manually and test RealWarp 0.38.0 with XREAL One / One Pro.' -ForegroundColor Green
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions .github/workflows/ci-validation.yml
Original file line numberDiff line numberDiff line change
Expand Up@@ -142,9 +142,37 @@ jobs:
New-Item -ItemType Directory -Path $dest -Force | Out-Null
Copy-Item "$outDir\*" -Destination $dest -Recurse -Force

- name: Validate ARM64 driver package (CI policy)
if: matrix.platform == 'ARM64'
shell: pwsh
run: |
$ErrorActionPreference = "Stop"
./scripts/Test-Arm64DriverPackage.ps1 `
-PackagePath "artifacts/VDD/ARM64" `
-Policy CI

- name: Create ARM64 attestation CAB
if: matrix.platform == 'ARM64'
shell: pwsh
run: |
$ErrorActionPreference = "Stop"
$result = ./scripts/New-AttestationCab.ps1 `
-InputDirectory "artifacts/VDD/ARM64" `
-OutputDirectory "artifacts/VDD/ARM64" `
-CabFileName "MttVDD-ARM64-Attestation.cab"
Write-Output "Created attestation CAB at $($result.CabPath)"

- name: Upload artifacts
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: VDD-${{ matrix.platform }}-${{ env.BUILD_CONFIGURATION }}
path: artifacts/VDD/${{ matrix.platform }}/
if-no-files-found: error

- name: Upload ARM64 attestation CAB
if: matrix.platform == 'ARM64'
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: VDD-ARM64-Attestation-CAB
path: artifacts/VDD/ARM64/MttVDD-ARM64-Attestation.cab
if-no-files-found: error
121 changes: 121 additions & 0 deletions docs/windows-arm64-signing.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
# Windows ARM64 signing and maintainer handoff

This runbook describes how Virtual Display Driver maintainers can produce a **Microsoft-signed ARM64 preview** that installs on HVCI-enabled Windows on ARM systems (for example Surface Laptop 7 with Snapdragon X), and how to graduate that package to WHCP certification later.

## Background

- RealWarp and other consumers detect `MttVDD` through `Root\MttVDD` and `\\.\pipe\MTTVirtualDisplayPipe`.
- The upstream ARM64 release payload is architecturally correct (`NTARM64`, native `MttVDD.dll`), but SignPath/GlobalSign catalogs are rejected by the Driver Store on systems with Memory Integrity enabled (`0x800B0109` / untrusted root).
- `PnpLockdown=1` in `MttVDD.inf` is **installed-file protection** and should remain enabled; it is not a WHQL-only switch.

Related issues:

- [#465](https://github.com/VirtualDrivers/Virtual-Display-Driver/issues/465) — ARM64 package trust failure on install
- [#483](https://github.com/VirtualDrivers/Virtual-Display-Driver/issues/483) — post-install display attachment on ARM

## Repository automation

| Script | Purpose |
| --- | --- |
| `scripts/Test-Arm64DriverPackage.ps1` | Validates folder or CAB payloads (`CI` or `Release` policy) |
| `scripts/New-AttestationCab.ps1` | Builds a Partner Center-ready attestation CAB from ARM64 Release output |
| `scripts/Invoke-Arm64AcceptanceTest.ps1` | Surface acceptance harness (Secure Boot/HVCI + Release signature gate + optional install) |

GitHub Actions (`.github/workflows/ci-validation.yml`) builds ARM64 Release output, runs **CI** validation, creates the attestation CAB, round-trip validates the CAB, and uploads:

- `VDD-ARM64-Release` — raw build output
- `VDD-ARM64-Attestation-CAB` — submission CAB (unsigned; EV signing happens offline)

## Maintainer workflow

### 1. Build and validate locally or from CI

```powershell
# After ARM64 Release build output exists:
./scripts/Test-Arm64DriverPackage.ps1 `
-PackagePath "Virtual Display Driver (HDR)\ARM64\Release\MttVDD" `
-Policy CI

./scripts/New-AttestationCab.ps1 `
-InputDirectory "Virtual Display Driver (HDR)\ARM64\Release\MttVDD" `
-OutputDirectory "artifacts\VDD\ARM64"
```

Expected payload inside the CAB subfolder:

- `MttVDD.inf`
- `MttVDD.dll` (PE machine `0xAA64`)
- `MttVDD.pdb`
- `MttVDD.cat` (company catalog for verification; Microsoft replaces it)

Requirements from Microsoft attestation signing:

- Driver files must live in a **single subfolder** inside the CAB (never at CAB root).
- Subfolder name must be **fewer than 40 characters** and use no UNC paths during packaging.
- See [Attestation sign Windows drivers](https://learn.microsoft.com/windows-hardware/drivers/dashboard/code-signing-attestation).

### 2. EV-sign the CAB offline

Use the organization EV certificate and SignTool (SHA-256):

```cmd
SignTool sign /s MY /n "Company Name" /fd sha256 /tr http://timestamp.digicert.com /td sha256 /v MttVDD-ARM64-Attestation.cab
```

Do **not** store EV credentials in GitHub Actions.

### 3. Submit attestation preview in Partner Center

1. Open the [Partner Center hardware dashboard](https://partner.microsoft.com/dashboard/hardware/Search).
2. Choose **Submit new hardware**.
3. Upload the **EV-signed CAB**.
4. Leave test-signing options **unchecked** for the production-preview path on HVCI systems.
5. Request the Windows Desktop signatures needed for ARM64 user-mode driver attestation.
6. Download Microsoft’s returned package when processing completes.

Reference: [Driver signing options and best practices — attestation for testing scenarios](https://learn.microsoft.com/windows-hardware/drivers/dashboard/driver-signing-offerings#attestation-signed-drivers-for-testing-scenarios).

### 4. Validate the returned package (Release policy)

```powershell
./scripts/Test-Arm64DriverPackage.ps1 `
-PackagePath ".\downloads\MttVDD-ARM64-MicrosoftSigned.cab" `
-Policy Release
```

Release policy requires a catalog subject containing **Microsoft Windows Hardware Compatibility Publisher** and rejects SignPath-only catalogs.

Optional Surface preflight:

```powershell
./scripts/Invoke-Arm64AcceptanceTest.ps1 `
-PackagePath ".\downloads\MttVDD-ARM64-MicrosoftSigned.cab" `
-Mode Preflight
```

### 5. Publish an ARM64 preview release

1. Extract the Microsoft-signed package into a clearly named release asset, for example `VirtualDisplayDriver-ARM64.Driver.Only.zip`.
2. Mark the release as **ARM64 preview / attestation-signed** in release notes.
3. Link issues #465 and #483 and note that WHCP certification is the follow-up for broad retail/Windows Update distribution.

### 6. Graduate to WHCP (later)

Reuse the same validated ARM64 payload for HLK/WHCP submission when an ARM64 HLK lab is available. Attestation preview unblocks HVCI-enabled test machines; WHCP remains the supported public-release path.

## Acceptance criteria on Windows on ARM

Run with Secure Boot and Memory Integrity **enabled** (no test-signing boot configuration or trust-store workarounds):

1. **Static gate** — `Test-Arm64DriverPackage.ps1 -Policy Release` passes.
2. **Install gate** — `Invoke-Arm64AcceptanceTest.ps1 -Mode Install` installs `Root\MttVDD` with device status OK and no Code Integrity rejection.
3. **Display gate** — one free-tier virtual display attaches to extended topology (DisplayConfig / `EnumDisplayMonitors` / DXGI).
4. **RealWarp gate** — RealWarp 0.38.0 (x64 emulation) detects MttVDD, connects to `MTTVirtualDisplayPipe`, captures desktop, and renders on XREAL One / One Pro.

If signing succeeds but topology fails, treat that as issue #483 (IddCx attachment) rather than changing signing policy.

## Evidence captured on Surface (SignPath baseline)

The current public ARM64 package (`25.7.23`) fails Release validation because the catalog chains to **SignPath Foundation / GlobalSign**, not Microsoft WHCP. On HVCI-enabled systems this surfaces as Driver Store staging error **`0x800B0109`** (*A certificate chain processed, but terminated in a root certificate which is not trusted by the trust provider*).

That failure occurs **before** UMDF/IddCx load; replacing RealWarp’s bundled x64 Parsec driver with a **Microsoft-signed ARM64 MttVDD** package is the required production path.
210 changes: 210 additions & 0 deletions scripts/Invoke-Arm64AcceptanceTest.ps1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,210 @@
#Requires -Version 5.1
<#
.SYNOPSIS
Runs Surface acceptance checks for a Microsoft-signed ARM64 MttVDD package.

.DESCRIPTION
Preflight mode verifies Secure Boot and HVCI remain enabled and validates the package
with Release policy. Install mode performs NefCon-based installation and display
topology checks when Release validation succeeds.

.PARAMETER PackagePath
Directory or CAB containing the Microsoft-signed ARM64 driver package.

.PARAMETER Mode
Preflight - security and signature validation only (default)
Install - install driver and verify device/display topology when signature gate passes

.PARAMETER NefConPath
Optional path to nefconw.exe. Defaults to ARM64 binary extracted from the latest NefCon release.
#>
[CmdletBinding()]
param(
[Parameter(Mandatory = $true)]
[ValidateNotNullOrEmpty()]
[string]$PackagePath,

[ValidateSet('Preflight', 'Install')]
[string]$Mode = 'Preflight',

[string]$NefConPath
)

Set-StrictMode -Version Latest
$ErrorActionPreference = 'Stop'

function Test-SecurityGates {
$results = @()

$secureBoot = $false
try {
$secureBoot = Confirm-SecureBootUEFI -ErrorAction Stop
}
catch {
$reg = Get-ItemProperty -Path 'HKLM:\SYSTEM\CurrentControlSet\Control\SecureBoot\State' -Name UEFISecureBootEnabled -ErrorAction SilentlyContinue
$secureBoot = ($reg.UEFISecureBootEnabled -eq 1)
}

$results += [pscustomobject]@{
Check = 'SecureBoot'
Status = $(if ($secureBoot) { 'PASS' } else { 'FAIL' })
Detail = $(if ($secureBoot) { 'Secure Boot is enabled.' } else { 'Secure Boot is disabled or could not be verified.' })
}

$hvciEnabled = $false
try {
$ci = Get-CimInstance -ClassName Win32_DeviceGuard -Namespace root\Microsoft\Windows\DeviceGuard -ErrorAction Stop
$hvciEnabled = ($ci.SecurityServicesRunning -contains 1) -or ($ci.VirtualizationBasedSecurityStatus -ge 2)
}
catch {
$reg = Get-ItemProperty -Path 'HKLM:\SYSTEM\CurrentControlSet\Control\DeviceGuard\Scenarios\HypervisorEnforcedCodeIntegrity' -Name Enabled -ErrorAction SilentlyContinue
$hvciEnabled = ($reg.Enabled -eq 1)
}

$results += [pscustomobject]@{
Check = 'HVCI'
Status = $(if ($hvciEnabled) { 'PASS' } else { 'FAIL' })
Detail = $(if ($hvciEnabled) { 'Memory Integrity / HVCI appears enabled.' } else { 'Memory Integrity / HVCI does not appear enabled.' })
}

return $results
}

function Get-DriverPackageDirectory {
param(
[string]$Path
)

$item = Get-Item -LiteralPath $Path
if ($item.PSIsContainer) {
$inf = Get-ChildItem -LiteralPath $item.FullName -Filter 'MttVDD.inf' -Recurse -File | Select-Object -First 1
if (-not $inf) {
throw "MttVDD.inf not found under $Path"
}
return $inf.Directory.FullName
}

$expandRoot = Join-Path ([System.IO.Path]::GetTempPath()) ("vdd-arm64-accept-" + [guid]::NewGuid().ToString('N'))
New-Item -ItemType Directory -Path $expandRoot -Force | Out-Null
expand.exe $item.FullName -F:* $expandRoot | Out-Null
if ($LASTEXITCODE -ne 0) {
throw "Failed to expand CAB: $Path"
}

$inf = Get-ChildItem -LiteralPath $expandRoot -Filter 'MttVDD.inf' -Recurse -File | Select-Object -First 1
if (-not $inf) {
throw "MttVDD.inf not found in expanded CAB."
}

return $inf.Directory.FullName
}

function Ensure-NefCon {
param(
[string]$ExplicitPath
)

if ($ExplicitPath) {
if (-not (Test-Path -LiteralPath $ExplicitPath)) {
throw "NefCon not found at $ExplicitPath"
}
return (Resolve-Path -LiteralPath $ExplicitPath).Path
}

$tempDir = Join-Path $env:TEMP 'VDDAcceptanceNefCon'
$arm64Exe = Join-Path $tempDir 'ARM64\nefconw.exe'
if (-not (Test-Path -LiteralPath $arm64Exe)) {
New-Item -ItemType Directory -Path $tempDir -Force | Out-Null
$zipPath = Join-Path $tempDir 'nefcon.zip'
Invoke-WebRequest -Uri 'https://github.com/nefarius/nefcon/releases/download/v1.14.0/nefcon_v1.14.0.zip' -OutFile $zipPath -UseBasicParsing
Expand-Archive -LiteralPath $zipPath -DestinationPath $tempDir -Force
}

if (-not (Test-Path -LiteralPath $arm64Exe)) {
throw 'ARM64 nefconw.exe not found after extraction.'
}

return (Resolve-Path -LiteralPath $arm64Exe).Path
}

$validator = Join-Path $PSScriptRoot 'Test-Arm64DriverPackage.ps1'
if (-not (Test-Path -LiteralPath $validator)) {
throw "Validation script not found: $validator"
}

Write-Host '=== ARM64 MttVDD acceptance preflight ===' -ForegroundColor Cyan
$securityResults = Test-SecurityGates
$securityResults | ForEach-Object {
$color = if ($_.Status -eq 'PASS') { 'Green' } else { 'Red' }
Write-Host ("[{0}] {1} - {2}" -f $_.Status, $_.Check, $_.Detail) -ForegroundColor $color
}

if (@($securityResults | Where-Object { $_.Status -eq 'FAIL' }).Count -gt 0) {
throw 'Security gate failed. Acceptance testing requires Secure Boot and HVCI enabled.'
}

Write-Host '=== Release signature validation ===' -ForegroundColor Cyan
& $validator -PackagePath $PackagePath -Policy Release

if ($Mode -eq 'Preflight') {
Write-Host 'Preflight acceptance checks passed. Install mode can proceed once a Microsoft-signed package is available.' -ForegroundColor Green
return
}

$packageDir = Get-DriverPackageDirectory -Path $PackagePath
$nefcon = Ensure-NefCon -ExplicitPath $NefConPath
$infPath = Join-Path $packageDir 'MttVDD.inf'

Write-Host '=== Installing Root\MttVDD via NefCon ===' -ForegroundColor Cyan
Push-Location $packageDir
try {
& $nefcon install $infPath 'Root\MttVDD'
if ($LASTEXITCODE -ne 0) {
throw "nefconw install failed with exit code $LASTEXITCODE"
}
}
finally {
Pop-Location
}

Start-Sleep -Seconds 5

$pnpDevice = Get-PnpDevice -FriendlyName '*Virtual Display Driver*' -ErrorAction SilentlyContinue | Select-Object -First 1
if (-not $pnpDevice) {
$pnpDevice = Get-PnpDevice -InstanceId '*Root\MttVDD*' -ErrorAction SilentlyContinue | Select-Object -First 1
}

if (-not $pnpDevice -or $pnpDevice.Status -ne 'OK') {
throw 'MttVDD device not present or not healthy after install.'
}

Write-Host ("Device {0} status: {1}" -f $pnpDevice.InstanceId, $pnpDevice.Status) -ForegroundColor Green

try {
Add-Type @"
using System;
using System.Runtime.InteropServices;
public static class MonitorEnumAcceptance {
public delegate bool EnumMonitorsDelegate(IntPtr hMonitor, IntPtr hdcMonitor, ref RECT lprcMonitor, IntPtr dwData);
[StructLayout(LayoutKind.Sequential)]
public struct RECT { public int Left; public int Top; public int Right; public int Bottom; }
[DllImport("user32.dll")]
public static extern bool EnumDisplayMonitors(IntPtr hdc, IntPtr lprcClip, EnumMonitorsDelegate lpfnEnum, IntPtr dwData);
public static int CountMonitors() {
int count = 0;
EnumMonitorsDelegate callback = delegate (IntPtr hMonitor, IntPtr hdcMonitor, ref RECT lprcMonitor, IntPtr dwData) {
count++;
return true;
};
EnumDisplayMonitors(IntPtr.Zero, IntPtr.Zero, callback, IntPtr.Zero);
return count;
}
}
"@
$monitorCount = [MonitorEnumAcceptance]::CountMonitors()
Write-Host "EnumDisplayMonitors reported $monitorCount monitors." -ForegroundColor Green
}
catch {
Write-Warning "EnumDisplayMonitors probe unavailable: $($_.Exception.Message)"
}
Write-Host 'Install acceptance checks completed. Verify extended topology manually and test RealWarp 0.38.0 with XREAL One / One Pro.' -ForegroundColor Green
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions .github/workflows/ci-validation.yml
Original file line numberDiff line numberDiff line change
Expand Up@@ -142,9 +142,37 @@ jobs:
New-Item -ItemType Directory -Path $dest -Force | Out-Null
Copy-Item "$outDir\*" -Destination $dest -Recurse -Force

- name: Validate ARM64 driver package (CI policy)
if: matrix.platform == 'ARM64'
shell: pwsh
run: |
$ErrorActionPreference = "Stop"
./scripts/Test-Arm64DriverPackage.ps1 `
-PackagePath "artifacts/VDD/ARM64" `
-Policy CI

- name: Create ARM64 attestation CAB
if: matrix.platform == 'ARM64'
shell: pwsh
run: |
$ErrorActionPreference = "Stop"
$result = ./scripts/New-AttestationCab.ps1 `
-InputDirectory "artifacts/VDD/ARM64" `
-OutputDirectory "artifacts/VDD/ARM64" `
-CabFileName "MttVDD-ARM64-Attestation.cab"
Write-Output "Created attestation CAB at $($result.CabPath)"

- name: Upload artifacts
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: VDD-${{ matrix.platform }}-${{ env.BUILD_CONFIGURATION }}
path: artifacts/VDD/${{ matrix.platform }}/
if-no-files-found: error

- name: Upload ARM64 attestation CAB
if: matrix.platform == 'ARM64'
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: VDD-ARM64-Attestation-CAB
path: artifacts/VDD/ARM64/MttVDD-ARM64-Attestation.cab
if-no-files-found: error
121 changes: 121 additions & 0 deletions docs/windows-arm64-signing.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
# Windows ARM64 signing and maintainer handoff

This runbook describes how Virtual Display Driver maintainers can produce a **Microsoft-signed ARM64 preview** that installs on HVCI-enabled Windows on ARM systems (for example Surface Laptop 7 with Snapdragon X), and how to graduate that package to WHCP certification later.

## Background

- RealWarp and other consumers detect `MttVDD` through `Root\MttVDD` and `\\.\pipe\MTTVirtualDisplayPipe`.
- The upstream ARM64 release payload is architecturally correct (`NTARM64`, native `MttVDD.dll`), but SignPath/GlobalSign catalogs are rejected by the Driver Store on systems with Memory Integrity enabled (`0x800B0109` / untrusted root).
- `PnpLockdown=1` in `MttVDD.inf` is **installed-file protection** and should remain enabled; it is not a WHQL-only switch.

Related issues:

- [#465](https://github.com/VirtualDrivers/Virtual-Display-Driver/issues/465) — ARM64 package trust failure on install
- [#483](https://github.com/VirtualDrivers/Virtual-Display-Driver/issues/483) — post-install display attachment on ARM

## Repository automation

| Script | Purpose |
| --- | --- |
| `scripts/Test-Arm64DriverPackage.ps1` | Validates folder or CAB payloads (`CI` or `Release` policy) |
| `scripts/New-AttestationCab.ps1` | Builds a Partner Center-ready attestation CAB from ARM64 Release output |
| `scripts/Invoke-Arm64AcceptanceTest.ps1` | Surface acceptance harness (Secure Boot/HVCI + Release signature gate + optional install) |

GitHub Actions (`.github/workflows/ci-validation.yml`) builds ARM64 Release output, runs **CI** validation, creates the attestation CAB, round-trip validates the CAB, and uploads:

- `VDD-ARM64-Release` — raw build output
- `VDD-ARM64-Attestation-CAB` — submission CAB (unsigned; EV signing happens offline)

## Maintainer workflow

### 1. Build and validate locally or from CI

```powershell
# After ARM64 Release build output exists:
./scripts/Test-Arm64DriverPackage.ps1 `
-PackagePath "Virtual Display Driver (HDR)\ARM64\Release\MttVDD" `
-Policy CI

./scripts/New-AttestationCab.ps1 `
-InputDirectory "Virtual Display Driver (HDR)\ARM64\Release\MttVDD" `
-OutputDirectory "artifacts\VDD\ARM64"
```

Expected payload inside the CAB subfolder:

- `MttVDD.inf`
- `MttVDD.dll` (PE machine `0xAA64`)
- `MttVDD.pdb`
- `MttVDD.cat` (company catalog for verification; Microsoft replaces it)

Requirements from Microsoft attestation signing:

- Driver files must live in a **single subfolder** inside the CAB (never at CAB root).
- Subfolder name must be **fewer than 40 characters** and use no UNC paths during packaging.
- See [Attestation sign Windows drivers](https://learn.microsoft.com/windows-hardware/drivers/dashboard/code-signing-attestation).

### 2. EV-sign the CAB offline

Use the organization EV certificate and SignTool (SHA-256):

```cmd
SignTool sign /s MY /n "Company Name" /fd sha256 /tr http://timestamp.digicert.com /td sha256 /v MttVDD-ARM64-Attestation.cab
```

Do **not** store EV credentials in GitHub Actions.

### 3. Submit attestation preview in Partner Center

1. Open the [Partner Center hardware dashboard](https://partner.microsoft.com/dashboard/hardware/Search).
2. Choose **Submit new hardware**.
3. Upload the **EV-signed CAB**.
4. Leave test-signing options **unchecked** for the production-preview path on HVCI systems.
5. Request the Windows Desktop signatures needed for ARM64 user-mode driver attestation.
6. Download Microsoft’s returned package when processing completes.

Reference: [Driver signing options and best practices — attestation for testing scenarios](https://learn.microsoft.com/windows-hardware/drivers/dashboard/driver-signing-offerings#attestation-signed-drivers-for-testing-scenarios).

### 4. Validate the returned package (Release policy)

```powershell
./scripts/Test-Arm64DriverPackage.ps1 `
-PackagePath ".\downloads\MttVDD-ARM64-MicrosoftSigned.cab" `
-Policy Release
```

Release policy requires a catalog subject containing **Microsoft Windows Hardware Compatibility Publisher** and rejects SignPath-only catalogs.

Optional Surface preflight:

```powershell
./scripts/Invoke-Arm64AcceptanceTest.ps1 `
-PackagePath ".\downloads\MttVDD-ARM64-MicrosoftSigned.cab" `
-Mode Preflight
```

### 5. Publish an ARM64 preview release

1. Extract the Microsoft-signed package into a clearly named release asset, for example `VirtualDisplayDriver-ARM64.Driver.Only.zip`.
2. Mark the release as **ARM64 preview / attestation-signed** in release notes.
3. Link issues #465 and #483 and note that WHCP certification is the follow-up for broad retail/Windows Update distribution.

### 6. Graduate to WHCP (later)

Reuse the same validated ARM64 payload for HLK/WHCP submission when an ARM64 HLK lab is available. Attestation preview unblocks HVCI-enabled test machines; WHCP remains the supported public-release path.

## Acceptance criteria on Windows on ARM

Run with Secure Boot and Memory Integrity **enabled** (no test-signing boot configuration or trust-store workarounds):

1. **Static gate** — `Test-Arm64DriverPackage.ps1 -Policy Release` passes.
2. **Install gate** — `Invoke-Arm64AcceptanceTest.ps1 -Mode Install` installs `Root\MttVDD` with device status OK and no Code Integrity rejection.
3. **Display gate** — one free-tier virtual display attaches to extended topology (DisplayConfig / `EnumDisplayMonitors` / DXGI).
4. **RealWarp gate** — RealWarp 0.38.0 (x64 emulation) detects MttVDD, connects to `MTTVirtualDisplayPipe`, captures desktop, and renders on XREAL One / One Pro.

If signing succeeds but topology fails, treat that as issue #483 (IddCx attachment) rather than changing signing policy.

## Evidence captured on Surface (SignPath baseline)

The current public ARM64 package (`25.7.23`) fails Release validation because the catalog chains to **SignPath Foundation / GlobalSign**, not Microsoft WHCP. On HVCI-enabled systems this surfaces as Driver Store staging error **`0x800B0109`** (*A certificate chain processed, but terminated in a root certificate which is not trusted by the trust provider*).

That failure occurs **before** UMDF/IddCx load; replacing RealWarp’s bundled x64 Parsec driver with a **Microsoft-signed ARM64 MttVDD** package is the required production path.
210 changes: 210 additions & 0 deletions scripts/Invoke-Arm64AcceptanceTest.ps1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,210 @@
#Requires -Version 5.1
<#
.SYNOPSIS
Runs Surface acceptance checks for a Microsoft-signed ARM64 MttVDD package.

.DESCRIPTION
Preflight mode verifies Secure Boot and HVCI remain enabled and validates the package
with Release policy. Install mode performs NefCon-based installation and display
topology checks when Release validation succeeds.

.PARAMETER PackagePath
Directory or CAB containing the Microsoft-signed ARM64 driver package.

.PARAMETER Mode
Preflight - security and signature validation only (default)
Install - install driver and verify device/display topology when signature gate passes

.PARAMETER NefConPath
Optional path to nefconw.exe. Defaults to ARM64 binary extracted from the latest NefCon release.
#>
[CmdletBinding()]
param(
[Parameter(Mandatory = $true)]
[ValidateNotNullOrEmpty()]
[string]$PackagePath,

[ValidateSet('Preflight', 'Install')]
[string]$Mode = 'Preflight',

[string]$NefConPath
)

Set-StrictMode -Version Latest
$ErrorActionPreference = 'Stop'

function Test-SecurityGates {
$results = @()

$secureBoot = $false
try {
$secureBoot = Confirm-SecureBootUEFI -ErrorAction Stop
}
catch {
$reg = Get-ItemProperty -Path 'HKLM:\SYSTEM\CurrentControlSet\Control\SecureBoot\State' -Name UEFISecureBootEnabled -ErrorAction SilentlyContinue
$secureBoot = ($reg.UEFISecureBootEnabled -eq 1)
}

$results += [pscustomobject]@{
Check = 'SecureBoot'
Status = $(if ($secureBoot) { 'PASS' } else { 'FAIL' })
Detail = $(if ($secureBoot) { 'Secure Boot is enabled.' } else { 'Secure Boot is disabled or could not be verified.' })
}

$hvciEnabled = $false
try {
$ci = Get-CimInstance -ClassName Win32_DeviceGuard -Namespace root\Microsoft\Windows\DeviceGuard -ErrorAction Stop
$hvciEnabled = ($ci.SecurityServicesRunning -contains 1) -or ($ci.VirtualizationBasedSecurityStatus -ge 2)
}
catch {
$reg = Get-ItemProperty -Path 'HKLM:\SYSTEM\CurrentControlSet\Control\DeviceGuard\Scenarios\HypervisorEnforcedCodeIntegrity' -Name Enabled -ErrorAction SilentlyContinue
$hvciEnabled = ($reg.Enabled -eq 1)
}

$results += [pscustomobject]@{
Check = 'HVCI'
Status = $(if ($hvciEnabled) { 'PASS' } else { 'FAIL' })
Detail = $(if ($hvciEnabled) { 'Memory Integrity / HVCI appears enabled.' } else { 'Memory Integrity / HVCI does not appear enabled.' })
}

return $results
}

function Get-DriverPackageDirectory {
param(
[string]$Path
)

$item = Get-Item -LiteralPath $Path
if ($item.PSIsContainer) {
$inf = Get-ChildItem -LiteralPath $item.FullName -Filter 'MttVDD.inf' -Recurse -File | Select-Object -First 1
if (-not $inf) {
throw "MttVDD.inf not found under $Path"
}
return $inf.Directory.FullName
}

$expandRoot = Join-Path ([System.IO.Path]::GetTempPath()) ("vdd-arm64-accept-" + [guid]::NewGuid().ToString('N'))
New-Item -ItemType Directory -Path $expandRoot -Force | Out-Null
expand.exe $item.FullName -F:* $expandRoot | Out-Null
if ($LASTEXITCODE -ne 0) {
throw "Failed to expand CAB: $Path"
}

$inf = Get-ChildItem -LiteralPath $expandRoot -Filter 'MttVDD.inf' -Recurse -File | Select-Object -First 1
if (-not $inf) {
throw "MttVDD.inf not found in expanded CAB."
}

return $inf.Directory.FullName
}

function Ensure-NefCon {
param(
[string]$ExplicitPath
)

if ($ExplicitPath) {
if (-not (Test-Path -LiteralPath $ExplicitPath)) {
throw "NefCon not found at $ExplicitPath"
}
return (Resolve-Path -LiteralPath $ExplicitPath).Path
}

$tempDir = Join-Path $env:TEMP 'VDDAcceptanceNefCon'
$arm64Exe = Join-Path $tempDir 'ARM64\nefconw.exe'
if (-not (Test-Path -LiteralPath $arm64Exe)) {
New-Item -ItemType Directory -Path $tempDir -Force | Out-Null
$zipPath = Join-Path $tempDir 'nefcon.zip'
Invoke-WebRequest -Uri 'https://github.com/nefarius/nefcon/releases/download/v1.14.0/nefcon_v1.14.0.zip' -OutFile $zipPath -UseBasicParsing
Expand-Archive -LiteralPath $zipPath -DestinationPath $tempDir -Force
}

if (-not (Test-Path -LiteralPath $arm64Exe)) {
throw 'ARM64 nefconw.exe not found after extraction.'
}

return (Resolve-Path -LiteralPath $arm64Exe).Path
}

$validator = Join-Path $PSScriptRoot 'Test-Arm64DriverPackage.ps1'
if (-not (Test-Path -LiteralPath $validator)) {
throw "Validation script not found: $validator"
}

Write-Host '=== ARM64 MttVDD acceptance preflight ===' -ForegroundColor Cyan
$securityResults = Test-SecurityGates
$securityResults | ForEach-Object {
$color = if ($_.Status -eq 'PASS') { 'Green' } else { 'Red' }
Write-Host ("[{0}] {1} - {2}" -f $_.Status, $_.Check, $_.Detail) -ForegroundColor $color
}

if (@($securityResults | Where-Object { $_.Status -eq 'FAIL' }).Count -gt 0) {
throw 'Security gate failed. Acceptance testing requires Secure Boot and HVCI enabled.'
}

Write-Host '=== Release signature validation ===' -ForegroundColor Cyan
& $validator -PackagePath $PackagePath -Policy Release

if ($Mode -eq 'Preflight') {
Write-Host 'Preflight acceptance checks passed. Install mode can proceed once a Microsoft-signed package is available.' -ForegroundColor Green
return
}

$packageDir = Get-DriverPackageDirectory -Path $PackagePath
$nefcon = Ensure-NefCon -ExplicitPath $NefConPath
$infPath = Join-Path $packageDir 'MttVDD.inf'

Write-Host '=== Installing Root\MttVDD via NefCon ===' -ForegroundColor Cyan
Push-Location $packageDir
try {
& $nefcon install $infPath 'Root\MttVDD'
if ($LASTEXITCODE -ne 0) {
throw "nefconw install failed with exit code $LASTEXITCODE"
}
}
finally {
Pop-Location
}

Start-Sleep -Seconds 5

$pnpDevice = Get-PnpDevice -FriendlyName '*Virtual Display Driver*' -ErrorAction SilentlyContinue | Select-Object -First 1
if (-not $pnpDevice) {
$pnpDevice = Get-PnpDevice -InstanceId '*Root\MttVDD*' -ErrorAction SilentlyContinue | Select-Object -First 1
}

if (-not $pnpDevice -or $pnpDevice.Status -ne 'OK') {
throw 'MttVDD device not present or not healthy after install.'
}

Write-Host ("Device {0} status: {1}" -f $pnpDevice.InstanceId, $pnpDevice.Status) -ForegroundColor Green

try {
Add-Type @"
using System;
using System.Runtime.InteropServices;
public static class MonitorEnumAcceptance {
public delegate bool EnumMonitorsDelegate(IntPtr hMonitor, IntPtr hdcMonitor, ref RECT lprcMonitor, IntPtr dwData);
[StructLayout(LayoutKind.Sequential)]
public struct RECT { public int Left; public int Top; public int Right; public int Bottom; }
[DllImport("user32.dll")]
public static extern bool EnumDisplayMonitors(IntPtr hdc, IntPtr lprcClip, EnumMonitorsDelegate lpfnEnum, IntPtr dwData);
public static int CountMonitors() {
int count = 0;
EnumMonitorsDelegate callback = delegate (IntPtr hMonitor, IntPtr hdcMonitor, ref RECT lprcMonitor, IntPtr dwData) {
count++;
return true;
};
EnumDisplayMonitors(IntPtr.Zero, IntPtr.Zero, callback, IntPtr.Zero);
return count;
}
}
"@
$monitorCount = [MonitorEnumAcceptance]::CountMonitors()
Write-Host "EnumDisplayMonitors reported $monitorCount monitors." -ForegroundColor Green
}
catch {
Write-Warning "EnumDisplayMonitors probe unavailable: $($_.Exception.Message)"
}
Write-Host 'Install acceptance checks completed. Verify extended topology manually and test RealWarp 0.38.0 with XREAL One / One Pro.' -ForegroundColor Green
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions .github/workflows/ci-validation.yml
Original file line numberDiff line numberDiff line change
Expand Up@@ -142,9 +142,37 @@ jobs:
New-Item -ItemType Directory -Path $dest -Force | Out-Null
Copy-Item "$outDir\*" -Destination $dest -Recurse -Force

- name: Validate ARM64 driver package (CI policy)
if: matrix.platform == 'ARM64'
shell: pwsh
run: |
$ErrorActionPreference = "Stop"
./scripts/Test-Arm64DriverPackage.ps1 `
-PackagePath "artifacts/VDD/ARM64" `
-Policy CI

- name: Create ARM64 attestation CAB
if: matrix.platform == 'ARM64'
shell: pwsh
run: |
$ErrorActionPreference = "Stop"
$result = ./scripts/New-AttestationCab.ps1 `
-InputDirectory "artifacts/VDD/ARM64" `
-OutputDirectory "artifacts/VDD/ARM64" `
-CabFileName "MttVDD-ARM64-Attestation.cab"
Write-Output "Created attestation CAB at $($result.CabPath)"

- name: Upload artifacts
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: VDD-${{ matrix.platform }}-${{ env.BUILD_CONFIGURATION }}
path: artifacts/VDD/${{ matrix.platform }}/
if-no-files-found: error

- name: Upload ARM64 attestation CAB
if: matrix.platform == 'ARM64'
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: VDD-ARM64-Attestation-CAB
path: artifacts/VDD/ARM64/MttVDD-ARM64-Attestation.cab
if-no-files-found: error
121 changes: 121 additions & 0 deletions docs/windows-arm64-signing.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
# Windows ARM64 signing and maintainer handoff

This runbook describes how Virtual Display Driver maintainers can produce a **Microsoft-signed ARM64 preview** that installs on HVCI-enabled Windows on ARM systems (for example Surface Laptop 7 with Snapdragon X), and how to graduate that package to WHCP certification later.

## Background

- RealWarp and other consumers detect `MttVDD` through `Root\MttVDD` and `\\.\pipe\MTTVirtualDisplayPipe`.
- The upstream ARM64 release payload is architecturally correct (`NTARM64`, native `MttVDD.dll`), but SignPath/GlobalSign catalogs are rejected by the Driver Store on systems with Memory Integrity enabled (`0x800B0109` / untrusted root).
- `PnpLockdown=1` in `MttVDD.inf` is **installed-file protection** and should remain enabled; it is not a WHQL-only switch.

Related issues:

- [#465](https://github.com/VirtualDrivers/Virtual-Display-Driver/issues/465) — ARM64 package trust failure on install
- [#483](https://github.com/VirtualDrivers/Virtual-Display-Driver/issues/483) — post-install display attachment on ARM

## Repository automation

| Script | Purpose |
| --- | --- |
| `scripts/Test-Arm64DriverPackage.ps1` | Validates folder or CAB payloads (`CI` or `Release` policy) |
| `scripts/New-AttestationCab.ps1` | Builds a Partner Center-ready attestation CAB from ARM64 Release output |
| `scripts/Invoke-Arm64AcceptanceTest.ps1` | Surface acceptance harness (Secure Boot/HVCI + Release signature gate + optional install) |

GitHub Actions (`.github/workflows/ci-validation.yml`) builds ARM64 Release output, runs **CI** validation, creates the attestation CAB, round-trip validates the CAB, and uploads:

- `VDD-ARM64-Release` — raw build output
- `VDD-ARM64-Attestation-CAB` — submission CAB (unsigned; EV signing happens offline)

## Maintainer workflow

### 1. Build and validate locally or from CI

```powershell
# After ARM64 Release build output exists:
./scripts/Test-Arm64DriverPackage.ps1 `
-PackagePath "Virtual Display Driver (HDR)\ARM64\Release\MttVDD" `
-Policy CI

./scripts/New-AttestationCab.ps1 `
-InputDirectory "Virtual Display Driver (HDR)\ARM64\Release\MttVDD" `
-OutputDirectory "artifacts\VDD\ARM64"
```

Expected payload inside the CAB subfolder:

- `MttVDD.inf`
- `MttVDD.dll` (PE machine `0xAA64`)
- `MttVDD.pdb`
- `MttVDD.cat` (company catalog for verification; Microsoft replaces it)

Requirements from Microsoft attestation signing:

- Driver files must live in a **single subfolder** inside the CAB (never at CAB root).
- Subfolder name must be **fewer than 40 characters** and use no UNC paths during packaging.
- See [Attestation sign Windows drivers](https://learn.microsoft.com/windows-hardware/drivers/dashboard/code-signing-attestation).

### 2. EV-sign the CAB offline

Use the organization EV certificate and SignTool (SHA-256):

```cmd
SignTool sign /s MY /n "Company Name" /fd sha256 /tr http://timestamp.digicert.com /td sha256 /v MttVDD-ARM64-Attestation.cab
```

Do **not** store EV credentials in GitHub Actions.

### 3. Submit attestation preview in Partner Center

1. Open the [Partner Center hardware dashboard](https://partner.microsoft.com/dashboard/hardware/Search).
2. Choose **Submit new hardware**.
3. Upload the **EV-signed CAB**.
4. Leave test-signing options **unchecked** for the production-preview path on HVCI systems.
5. Request the Windows Desktop signatures needed for ARM64 user-mode driver attestation.
6. Download Microsoft’s returned package when processing completes.

Reference: [Driver signing options and best practices — attestation for testing scenarios](https://learn.microsoft.com/windows-hardware/drivers/dashboard/driver-signing-offerings#attestation-signed-drivers-for-testing-scenarios).

### 4. Validate the returned package (Release policy)

```powershell
./scripts/Test-Arm64DriverPackage.ps1 `
-PackagePath ".\downloads\MttVDD-ARM64-MicrosoftSigned.cab" `
-Policy Release
```

Release policy requires a catalog subject containing **Microsoft Windows Hardware Compatibility Publisher** and rejects SignPath-only catalogs.

Optional Surface preflight:

```powershell
./scripts/Invoke-Arm64AcceptanceTest.ps1 `
-PackagePath ".\downloads\MttVDD-ARM64-MicrosoftSigned.cab" `
-Mode Preflight
```

### 5. Publish an ARM64 preview release

1. Extract the Microsoft-signed package into a clearly named release asset, for example `VirtualDisplayDriver-ARM64.Driver.Only.zip`.
2. Mark the release as **ARM64 preview / attestation-signed** in release notes.
3. Link issues #465 and #483 and note that WHCP certification is the follow-up for broad retail/Windows Update distribution.

### 6. Graduate to WHCP (later)

Reuse the same validated ARM64 payload for HLK/WHCP submission when an ARM64 HLK lab is available. Attestation preview unblocks HVCI-enabled test machines; WHCP remains the supported public-release path.

## Acceptance criteria on Windows on ARM

Run with Secure Boot and Memory Integrity **enabled** (no test-signing boot configuration or trust-store workarounds):

1. **Static gate** — `Test-Arm64DriverPackage.ps1 -Policy Release` passes.
2. **Install gate** — `Invoke-Arm64AcceptanceTest.ps1 -Mode Install` installs `Root\MttVDD` with device status OK and no Code Integrity rejection.
3. **Display gate** — one free-tier virtual display attaches to extended topology (DisplayConfig / `EnumDisplayMonitors` / DXGI).
4. **RealWarp gate** — RealWarp 0.38.0 (x64 emulation) detects MttVDD, connects to `MTTVirtualDisplayPipe`, captures desktop, and renders on XREAL One / One Pro.

If signing succeeds but topology fails, treat that as issue #483 (IddCx attachment) rather than changing signing policy.

## Evidence captured on Surface (SignPath baseline)

The current public ARM64 package (`25.7.23`) fails Release validation because the catalog chains to **SignPath Foundation / GlobalSign**, not Microsoft WHCP. On HVCI-enabled systems this surfaces as Driver Store staging error **`0x800B0109`** (*A certificate chain processed, but terminated in a root certificate which is not trusted by the trust provider*).

That failure occurs **before** UMDF/IddCx load; replacing RealWarp’s bundled x64 Parsec driver with a **Microsoft-signed ARM64 MttVDD** package is the required production path.
210 changes: 210 additions & 0 deletions scripts/Invoke-Arm64AcceptanceTest.ps1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,210 @@
#Requires -Version 5.1
<#
.SYNOPSIS
Runs Surface acceptance checks for a Microsoft-signed ARM64 MttVDD package.

.DESCRIPTION
Preflight mode verifies Secure Boot and HVCI remain enabled and validates the package
with Release policy. Install mode performs NefCon-based installation and display
topology checks when Release validation succeeds.

.PARAMETER PackagePath
Directory or CAB containing the Microsoft-signed ARM64 driver package.

.PARAMETER Mode
Preflight - security and signature validation only (default)
Install - install driver and verify device/display topology when signature gate passes

.PARAMETER NefConPath
Optional path to nefconw.exe. Defaults to ARM64 binary extracted from the latest NefCon release.
#>
[CmdletBinding()]
param(
[Parameter(Mandatory = $true)]
[ValidateNotNullOrEmpty()]
[string]$PackagePath,

[ValidateSet('Preflight', 'Install')]
[string]$Mode = 'Preflight',

[string]$NefConPath
)

Set-StrictMode -Version Latest
$ErrorActionPreference = 'Stop'

function Test-SecurityGates {
$results = @()

$secureBoot = $false
try {
$secureBoot = Confirm-SecureBootUEFI -ErrorAction Stop
}
catch {
$reg = Get-ItemProperty -Path 'HKLM:\SYSTEM\CurrentControlSet\Control\SecureBoot\State' -Name UEFISecureBootEnabled -ErrorAction SilentlyContinue
$secureBoot = ($reg.UEFISecureBootEnabled -eq 1)
}

$results += [pscustomobject]@{
Check = 'SecureBoot'
Status = $(if ($secureBoot) { 'PASS' } else { 'FAIL' })
Detail = $(if ($secureBoot) { 'Secure Boot is enabled.' } else { 'Secure Boot is disabled or could not be verified.' })
}

$hvciEnabled = $false
try {
$ci = Get-CimInstance -ClassName Win32_DeviceGuard -Namespace root\Microsoft\Windows\DeviceGuard -ErrorAction Stop
$hvciEnabled = ($ci.SecurityServicesRunning -contains 1) -or ($ci.VirtualizationBasedSecurityStatus -ge 2)
}
catch {
$reg = Get-ItemProperty -Path 'HKLM:\SYSTEM\CurrentControlSet\Control\DeviceGuard\Scenarios\HypervisorEnforcedCodeIntegrity' -Name Enabled -ErrorAction SilentlyContinue
$hvciEnabled = ($reg.Enabled -eq 1)
}

$results += [pscustomobject]@{
Check = 'HVCI'
Status = $(if ($hvciEnabled) { 'PASS' } else { 'FAIL' })
Detail = $(if ($hvciEnabled) { 'Memory Integrity / HVCI appears enabled.' } else { 'Memory Integrity / HVCI does not appear enabled.' })
}

return $results
}

function Get-DriverPackageDirectory {
param(
[string]$Path
)

$item = Get-Item -LiteralPath $Path
if ($item.PSIsContainer) {
$inf = Get-ChildItem -LiteralPath $item.FullName -Filter 'MttVDD.inf' -Recurse -File | Select-Object -First 1
if (-not $inf) {
throw "MttVDD.inf not found under $Path"
}
return $inf.Directory.FullName
}

$expandRoot = Join-Path ([System.IO.Path]::GetTempPath()) ("vdd-arm64-accept-" + [guid]::NewGuid().ToString('N'))
New-Item -ItemType Directory -Path $expandRoot -Force | Out-Null
expand.exe $item.FullName -F:* $expandRoot | Out-Null
if ($LASTEXITCODE -ne 0) {
throw "Failed to expand CAB: $Path"
}

$inf = Get-ChildItem -LiteralPath $expandRoot -Filter 'MttVDD.inf' -Recurse -File | Select-Object -First 1
if (-not $inf) {
throw "MttVDD.inf not found in expanded CAB."
}

return $inf.Directory.FullName
}

function Ensure-NefCon {
param(
[string]$ExplicitPath
)

if ($ExplicitPath) {
if (-not (Test-Path -LiteralPath $ExplicitPath)) {
throw "NefCon not found at $ExplicitPath"
}
return (Resolve-Path -LiteralPath $ExplicitPath).Path
}

$tempDir = Join-Path $env:TEMP 'VDDAcceptanceNefCon'
$arm64Exe = Join-Path $tempDir 'ARM64\nefconw.exe'
if (-not (Test-Path -LiteralPath $arm64Exe)) {
New-Item -ItemType Directory -Path $tempDir -Force | Out-Null
$zipPath = Join-Path $tempDir 'nefcon.zip'
Invoke-WebRequest -Uri 'https://github.com/nefarius/nefcon/releases/download/v1.14.0/nefcon_v1.14.0.zip' -OutFile $zipPath -UseBasicParsing
Expand-Archive -LiteralPath $zipPath -DestinationPath $tempDir -Force
}

if (-not (Test-Path -LiteralPath $arm64Exe)) {
throw 'ARM64 nefconw.exe not found after extraction.'
}

return (Resolve-Path -LiteralPath $arm64Exe).Path
}

$validator = Join-Path $PSScriptRoot 'Test-Arm64DriverPackage.ps1'
if (-not (Test-Path -LiteralPath $validator)) {
throw "Validation script not found: $validator"
}

Write-Host '=== ARM64 MttVDD acceptance preflight ===' -ForegroundColor Cyan
$securityResults = Test-SecurityGates
$securityResults | ForEach-Object {
$color = if ($_.Status -eq 'PASS') { 'Green' } else { 'Red' }
Write-Host ("[{0}] {1} - {2}" -f $_.Status, $_.Check, $_.Detail) -ForegroundColor $color
}

if (@($securityResults | Where-Object { $_.Status -eq 'FAIL' }).Count -gt 0) {
throw 'Security gate failed. Acceptance testing requires Secure Boot and HVCI enabled.'
}

Write-Host '=== Release signature validation ===' -ForegroundColor Cyan
& $validator -PackagePath $PackagePath -Policy Release

if ($Mode -eq 'Preflight') {
Write-Host 'Preflight acceptance checks passed. Install mode can proceed once a Microsoft-signed package is available.' -ForegroundColor Green
return
}

$packageDir = Get-DriverPackageDirectory -Path $PackagePath
$nefcon = Ensure-NefCon -ExplicitPath $NefConPath
$infPath = Join-Path $packageDir 'MttVDD.inf'

Write-Host '=== Installing Root\MttVDD via NefCon ===' -ForegroundColor Cyan
Push-Location $packageDir
try {
& $nefcon install $infPath 'Root\MttVDD'
if ($LASTEXITCODE -ne 0) {
throw "nefconw install failed with exit code $LASTEXITCODE"
}
}
finally {
Pop-Location
}

Start-Sleep -Seconds 5

$pnpDevice = Get-PnpDevice -FriendlyName '*Virtual Display Driver*' -ErrorAction SilentlyContinue | Select-Object -First 1
if (-not $pnpDevice) {
$pnpDevice = Get-PnpDevice -InstanceId '*Root\MttVDD*' -ErrorAction SilentlyContinue | Select-Object -First 1
}

if (-not $pnpDevice -or $pnpDevice.Status -ne 'OK') {
throw 'MttVDD device not present or not healthy after install.'
}

Write-Host ("Device {0} status: {1}" -f $pnpDevice.InstanceId, $pnpDevice.Status) -ForegroundColor Green

try {
Add-Type @"
using System;
using System.Runtime.InteropServices;
public static class MonitorEnumAcceptance {
public delegate bool EnumMonitorsDelegate(IntPtr hMonitor, IntPtr hdcMonitor, ref RECT lprcMonitor, IntPtr dwData);
[StructLayout(LayoutKind.Sequential)]
public struct RECT { public int Left; public int Top; public int Right; public int Bottom; }
[DllImport("user32.dll")]
public static extern bool EnumDisplayMonitors(IntPtr hdc, IntPtr lprcClip, EnumMonitorsDelegate lpfnEnum, IntPtr dwData);
public static int CountMonitors() {
int count = 0;
EnumMonitorsDelegate callback = delegate (IntPtr hMonitor, IntPtr hdcMonitor, ref RECT lprcMonitor, IntPtr dwData) {
count++;
return true;
};
EnumDisplayMonitors(IntPtr.Zero, IntPtr.Zero, callback, IntPtr.Zero);
return count;
}
}
"@
$monitorCount = [MonitorEnumAcceptance]::CountMonitors()
Write-Host "EnumDisplayMonitors reported $monitorCount monitors." -ForegroundColor Green
}
catch {
Write-Warning "EnumDisplayMonitors probe unavailable: $($_.Exception.Message)"
}
Write-Host 'Install acceptance checks completed. Verify extended topology manually and test RealWarp 0.38.0 with XREAL One / One Pro.' -ForegroundColor Green
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions .github/workflows/ci-validation.yml
Original file line numberDiff line numberDiff line change
Expand Up@@ -142,9 +142,37 @@ jobs:
New-Item -ItemType Directory -Path $dest -Force | Out-Null
Copy-Item "$outDir\*" -Destination $dest -Recurse -Force

- name: Validate ARM64 driver package (CI policy)
if: matrix.platform == 'ARM64'
shell: pwsh
run: |
$ErrorActionPreference = "Stop"
./scripts/Test-Arm64DriverPackage.ps1 `
-PackagePath "artifacts/VDD/ARM64" `
-Policy CI

- name: Create ARM64 attestation CAB
if: matrix.platform == 'ARM64'
shell: pwsh
run: |
$ErrorActionPreference = "Stop"
$result = ./scripts/New-AttestationCab.ps1 `
-InputDirectory "artifacts/VDD/ARM64" `
-OutputDirectory "artifacts/VDD/ARM64" `
-CabFileName "MttVDD-ARM64-Attestation.cab"
Write-Output "Created attestation CAB at $($result.CabPath)"

- name: Upload artifacts
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: VDD-${{ matrix.platform }}-${{ env.BUILD_CONFIGURATION }}
path: artifacts/VDD/${{ matrix.platform }}/
if-no-files-found: error

- name: Upload ARM64 attestation CAB
if: matrix.platform == 'ARM64'
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: VDD-ARM64-Attestation-CAB
path: artifacts/VDD/ARM64/MttVDD-ARM64-Attestation.cab
if-no-files-found: error
121 changes: 121 additions & 0 deletions docs/windows-arm64-signing.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
# Windows ARM64 signing and maintainer handoff

This runbook describes how Virtual Display Driver maintainers can produce a **Microsoft-signed ARM64 preview** that installs on HVCI-enabled Windows on ARM systems (for example Surface Laptop 7 with Snapdragon X), and how to graduate that package to WHCP certification later.

## Background

- RealWarp and other consumers detect `MttVDD` through `Root\MttVDD` and `\\.\pipe\MTTVirtualDisplayPipe`.
- The upstream ARM64 release payload is architecturally correct (`NTARM64`, native `MttVDD.dll`), but SignPath/GlobalSign catalogs are rejected by the Driver Store on systems with Memory Integrity enabled (`0x800B0109` / untrusted root).
- `PnpLockdown=1` in `MttVDD.inf` is **installed-file protection** and should remain enabled; it is not a WHQL-only switch.

Related issues:

- [#465](https://github.com/VirtualDrivers/Virtual-Display-Driver/issues/465) — ARM64 package trust failure on install
- [#483](https://github.com/VirtualDrivers/Virtual-Display-Driver/issues/483) — post-install display attachment on ARM

## Repository automation

| Script | Purpose |
| --- | --- |
| `scripts/Test-Arm64DriverPackage.ps1` | Validates folder or CAB payloads (`CI` or `Release` policy) |
| `scripts/New-AttestationCab.ps1` | Builds a Partner Center-ready attestation CAB from ARM64 Release output |
| `scripts/Invoke-Arm64AcceptanceTest.ps1` | Surface acceptance harness (Secure Boot/HVCI + Release signature gate + optional install) |

GitHub Actions (`.github/workflows/ci-validation.yml`) builds ARM64 Release output, runs **CI** validation, creates the attestation CAB, round-trip validates the CAB, and uploads:

- `VDD-ARM64-Release` — raw build output
- `VDD-ARM64-Attestation-CAB` — submission CAB (unsigned; EV signing happens offline)

## Maintainer workflow

### 1. Build and validate locally or from CI

```powershell
# After ARM64 Release build output exists:
./scripts/Test-Arm64DriverPackage.ps1 `
-PackagePath "Virtual Display Driver (HDR)\ARM64\Release\MttVDD" `
-Policy CI

./scripts/New-AttestationCab.ps1 `
-InputDirectory "Virtual Display Driver (HDR)\ARM64\Release\MttVDD" `
-OutputDirectory "artifacts\VDD\ARM64"
```

Expected payload inside the CAB subfolder:

- `MttVDD.inf`
- `MttVDD.dll` (PE machine `0xAA64`)
- `MttVDD.pdb`
- `MttVDD.cat` (company catalog for verification; Microsoft replaces it)

Requirements from Microsoft attestation signing:

- Driver files must live in a **single subfolder** inside the CAB (never at CAB root).
- Subfolder name must be **fewer than 40 characters** and use no UNC paths during packaging.
- See [Attestation sign Windows drivers](https://learn.microsoft.com/windows-hardware/drivers/dashboard/code-signing-attestation).

### 2. EV-sign the CAB offline

Use the organization EV certificate and SignTool (SHA-256):

```cmd
SignTool sign /s MY /n "Company Name" /fd sha256 /tr http://timestamp.digicert.com /td sha256 /v MttVDD-ARM64-Attestation.cab
```

Do **not** store EV credentials in GitHub Actions.

### 3. Submit attestation preview in Partner Center

1. Open the [Partner Center hardware dashboard](https://partner.microsoft.com/dashboard/hardware/Search).
2. Choose **Submit new hardware**.
3. Upload the **EV-signed CAB**.
4. Leave test-signing options **unchecked** for the production-preview path on HVCI systems.
5. Request the Windows Desktop signatures needed for ARM64 user-mode driver attestation.
6. Download Microsoft’s returned package when processing completes.

Reference: [Driver signing options and best practices — attestation for testing scenarios](https://learn.microsoft.com/windows-hardware/drivers/dashboard/driver-signing-offerings#attestation-signed-drivers-for-testing-scenarios).

### 4. Validate the returned package (Release policy)

```powershell
./scripts/Test-Arm64DriverPackage.ps1 `
-PackagePath ".\downloads\MttVDD-ARM64-MicrosoftSigned.cab" `
-Policy Release
```

Release policy requires a catalog subject containing **Microsoft Windows Hardware Compatibility Publisher** and rejects SignPath-only catalogs.

Optional Surface preflight:

```powershell
./scripts/Invoke-Arm64AcceptanceTest.ps1 `
-PackagePath ".\downloads\MttVDD-ARM64-MicrosoftSigned.cab" `
-Mode Preflight
```

### 5. Publish an ARM64 preview release

1. Extract the Microsoft-signed package into a clearly named release asset, for example `VirtualDisplayDriver-ARM64.Driver.Only.zip`.
2. Mark the release as **ARM64 preview / attestation-signed** in release notes.
3. Link issues #465 and #483 and note that WHCP certification is the follow-up for broad retail/Windows Update distribution.

### 6. Graduate to WHCP (later)

Reuse the same validated ARM64 payload for HLK/WHCP submission when an ARM64 HLK lab is available. Attestation preview unblocks HVCI-enabled test machines; WHCP remains the supported public-release path.

## Acceptance criteria on Windows on ARM

Run with Secure Boot and Memory Integrity **enabled** (no test-signing boot configuration or trust-store workarounds):

1. **Static gate** — `Test-Arm64DriverPackage.ps1 -Policy Release` passes.
2. **Install gate** — `Invoke-Arm64AcceptanceTest.ps1 -Mode Install` installs `Root\MttVDD` with device status OK and no Code Integrity rejection.
3. **Display gate** — one free-tier virtual display attaches to extended topology (DisplayConfig / `EnumDisplayMonitors` / DXGI).
4. **RealWarp gate** — RealWarp 0.38.0 (x64 emulation) detects MttVDD, connects to `MTTVirtualDisplayPipe`, captures desktop, and renders on XREAL One / One Pro.

If signing succeeds but topology fails, treat that as issue #483 (IddCx attachment) rather than changing signing policy.

## Evidence captured on Surface (SignPath baseline)

The current public ARM64 package (`25.7.23`) fails Release validation because the catalog chains to **SignPath Foundation / GlobalSign**, not Microsoft WHCP. On HVCI-enabled systems this surfaces as Driver Store staging error **`0x800B0109`** (*A certificate chain processed, but terminated in a root certificate which is not trusted by the trust provider*).

That failure occurs **before** UMDF/IddCx load; replacing RealWarp’s bundled x64 Parsec driver with a **Microsoft-signed ARM64 MttVDD** package is the required production path.
210 changes: 210 additions & 0 deletions scripts/Invoke-Arm64AcceptanceTest.ps1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,210 @@
#Requires -Version 5.1
<#
.SYNOPSIS
Runs Surface acceptance checks for a Microsoft-signed ARM64 MttVDD package.

.DESCRIPTION
Preflight mode verifies Secure Boot and HVCI remain enabled and validates the package
with Release policy. Install mode performs NefCon-based installation and display
topology checks when Release validation succeeds.

.PARAMETER PackagePath
Directory or CAB containing the Microsoft-signed ARM64 driver package.

.PARAMETER Mode
Preflight - security and signature validation only (default)
Install - install driver and verify device/display topology when signature gate passes

.PARAMETER NefConPath
Optional path to nefconw.exe. Defaults to ARM64 binary extracted from the latest NefCon release.
#>
[CmdletBinding()]
param(
[Parameter(Mandatory = $true)]
[ValidateNotNullOrEmpty()]
[string]$PackagePath,

[ValidateSet('Preflight', 'Install')]
[string]$Mode = 'Preflight',

[string]$NefConPath
)

Set-StrictMode -Version Latest
$ErrorActionPreference = 'Stop'

function Test-SecurityGates {
$results = @()

$secureBoot = $false
try {
$secureBoot = Confirm-SecureBootUEFI -ErrorAction Stop
}
catch {
$reg = Get-ItemProperty -Path 'HKLM:\SYSTEM\CurrentControlSet\Control\SecureBoot\State' -Name UEFISecureBootEnabled -ErrorAction SilentlyContinue
$secureBoot = ($reg.UEFISecureBootEnabled -eq 1)
}

$results += [pscustomobject]@{
Check = 'SecureBoot'
Status = $(if ($secureBoot) { 'PASS' } else { 'FAIL' })
Detail = $(if ($secureBoot) { 'Secure Boot is enabled.' } else { 'Secure Boot is disabled or could not be verified.' })
}

$hvciEnabled = $false
try {
$ci = Get-CimInstance -ClassName Win32_DeviceGuard -Namespace root\Microsoft\Windows\DeviceGuard -ErrorAction Stop
$hvciEnabled = ($ci.SecurityServicesRunning -contains 1) -or ($ci.VirtualizationBasedSecurityStatus -ge 2)
}
catch {
$reg = Get-ItemProperty -Path 'HKLM:\SYSTEM\CurrentControlSet\Control\DeviceGuard\Scenarios\HypervisorEnforcedCodeIntegrity' -Name Enabled -ErrorAction SilentlyContinue
$hvciEnabled = ($reg.Enabled -eq 1)
}

$results += [pscustomobject]@{
Check = 'HVCI'
Status = $(if ($hvciEnabled) { 'PASS' } else { 'FAIL' })
Detail = $(if ($hvciEnabled) { 'Memory Integrity / HVCI appears enabled.' } else { 'Memory Integrity / HVCI does not appear enabled.' })
}

return $results
}

function Get-DriverPackageDirectory {
param(
[string]$Path
)

$item = Get-Item -LiteralPath $Path
if ($item.PSIsContainer) {
$inf = Get-ChildItem -LiteralPath $item.FullName -Filter 'MttVDD.inf' -Recurse -File | Select-Object -First 1
if (-not $inf) {
throw "MttVDD.inf not found under $Path"
}
return $inf.Directory.FullName
}

$expandRoot = Join-Path ([System.IO.Path]::GetTempPath()) ("vdd-arm64-accept-" + [guid]::NewGuid().ToString('N'))
New-Item -ItemType Directory -Path $expandRoot -Force | Out-Null
expand.exe $item.FullName -F:* $expandRoot | Out-Null
if ($LASTEXITCODE -ne 0) {
throw "Failed to expand CAB: $Path"
}

$inf = Get-ChildItem -LiteralPath $expandRoot -Filter 'MttVDD.inf' -Recurse -File | Select-Object -First 1
if (-not $inf) {
throw "MttVDD.inf not found in expanded CAB."
}

return $inf.Directory.FullName
}

function Ensure-NefCon {
param(
[string]$ExplicitPath
)

if ($ExplicitPath) {
if (-not (Test-Path -LiteralPath $ExplicitPath)) {
throw "NefCon not found at $ExplicitPath"
}
return (Resolve-Path -LiteralPath $ExplicitPath).Path
}

$tempDir = Join-Path $env:TEMP 'VDDAcceptanceNefCon'
$arm64Exe = Join-Path $tempDir 'ARM64\nefconw.exe'
if (-not (Test-Path -LiteralPath $arm64Exe)) {
New-Item -ItemType Directory -Path $tempDir -Force | Out-Null
$zipPath = Join-Path $tempDir 'nefcon.zip'
Invoke-WebRequest -Uri 'https://github.com/nefarius/nefcon/releases/download/v1.14.0/nefcon_v1.14.0.zip' -OutFile $zipPath -UseBasicParsing
Expand-Archive -LiteralPath $zipPath -DestinationPath $tempDir -Force
}

if (-not (Test-Path -LiteralPath $arm64Exe)) {
throw 'ARM64 nefconw.exe not found after extraction.'
}

return (Resolve-Path -LiteralPath $arm64Exe).Path
}

$validator = Join-Path $PSScriptRoot 'Test-Arm64DriverPackage.ps1'
if (-not (Test-Path -LiteralPath $validator)) {
throw "Validation script not found: $validator"
}

Write-Host '=== ARM64 MttVDD acceptance preflight ===' -ForegroundColor Cyan
$securityResults = Test-SecurityGates
$securityResults | ForEach-Object {
$color = if ($_.Status -eq 'PASS') { 'Green' } else { 'Red' }
Write-Host ("[{0}] {1} - {2}" -f $_.Status, $_.Check, $_.Detail) -ForegroundColor $color
}

if (@($securityResults | Where-Object { $_.Status -eq 'FAIL' }).Count -gt 0) {
throw 'Security gate failed. Acceptance testing requires Secure Boot and HVCI enabled.'
}

Write-Host '=== Release signature validation ===' -ForegroundColor Cyan
& $validator -PackagePath $PackagePath -Policy Release

if ($Mode -eq 'Preflight') {
Write-Host 'Preflight acceptance checks passed. Install mode can proceed once a Microsoft-signed package is available.' -ForegroundColor Green
return
}

$packageDir = Get-DriverPackageDirectory -Path $PackagePath
$nefcon = Ensure-NefCon -ExplicitPath $NefConPath
$infPath = Join-Path $packageDir 'MttVDD.inf'

Write-Host '=== Installing Root\MttVDD via NefCon ===' -ForegroundColor Cyan
Push-Location $packageDir
try {
& $nefcon install $infPath 'Root\MttVDD'
if ($LASTEXITCODE -ne 0) {
throw "nefconw install failed with exit code $LASTEXITCODE"
}
}
finally {
Pop-Location
}

Start-Sleep -Seconds 5

$pnpDevice = Get-PnpDevice -FriendlyName '*Virtual Display Driver*' -ErrorAction SilentlyContinue | Select-Object -First 1
if (-not $pnpDevice) {
$pnpDevice = Get-PnpDevice -InstanceId '*Root\MttVDD*' -ErrorAction SilentlyContinue | Select-Object -First 1
}

if (-not $pnpDevice -or $pnpDevice.Status -ne 'OK') {
throw 'MttVDD device not present or not healthy after install.'
}

Write-Host ("Device {0} status: {1}" -f $pnpDevice.InstanceId, $pnpDevice.Status) -ForegroundColor Green

try {
Add-Type @"
using System;
using System.Runtime.InteropServices;
public static class MonitorEnumAcceptance {
public delegate bool EnumMonitorsDelegate(IntPtr hMonitor, IntPtr hdcMonitor, ref RECT lprcMonitor, IntPtr dwData);
[StructLayout(LayoutKind.Sequential)]
public struct RECT { public int Left; public int Top; public int Right; public int Bottom; }
[DllImport("user32.dll")]
public static extern bool EnumDisplayMonitors(IntPtr hdc, IntPtr lprcClip, EnumMonitorsDelegate lpfnEnum, IntPtr dwData);
public static int CountMonitors() {
int count = 0;
EnumMonitorsDelegate callback = delegate (IntPtr hMonitor, IntPtr hdcMonitor, ref RECT lprcMonitor, IntPtr dwData) {
count++;
return true;
};
EnumDisplayMonitors(IntPtr.Zero, IntPtr.Zero, callback, IntPtr.Zero);
return count;
}
}
"@
$monitorCount = [MonitorEnumAcceptance]::CountMonitors()
Write-Host "EnumDisplayMonitors reported $monitorCount monitors." -ForegroundColor Green
}
catch {
Write-Warning "EnumDisplayMonitors probe unavailable: $($_.Exception.Message)"
}
Write-Host 'Install acceptance checks completed. Verify extended topology manually and test RealWarp 0.38.0 with XREAL One / One Pro.' -ForegroundColor Green
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions .github/workflows/ci-validation.yml
Original file line numberDiff line numberDiff line change
Expand Up@@ -142,9 +142,37 @@ jobs:
New-Item -ItemType Directory -Path $dest -Force | Out-Null
Copy-Item "$outDir\*" -Destination $dest -Recurse -Force

- name: Validate ARM64 driver package (CI policy)
if: matrix.platform == 'ARM64'
shell: pwsh
run: |
$ErrorActionPreference = "Stop"
./scripts/Test-Arm64DriverPackage.ps1 `
-PackagePath "artifacts/VDD/ARM64" `
-Policy CI

- name: Create ARM64 attestation CAB
if: matrix.platform == 'ARM64'
shell: pwsh
run: |
$ErrorActionPreference = "Stop"
$result = ./scripts/New-AttestationCab.ps1 `
-InputDirectory "artifacts/VDD/ARM64" `
-OutputDirectory "artifacts/VDD/ARM64" `
-CabFileName "MttVDD-ARM64-Attestation.cab"
Write-Output "Created attestation CAB at $($result.CabPath)"

- name: Upload artifacts
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: VDD-${{ matrix.platform }}-${{ env.BUILD_CONFIGURATION }}
path: artifacts/VDD/${{ matrix.platform }}/
if-no-files-found: error

- name: Upload ARM64 attestation CAB
if: matrix.platform == 'ARM64'
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: VDD-ARM64-Attestation-CAB
path: artifacts/VDD/ARM64/MttVDD-ARM64-Attestation.cab
if-no-files-found: error
121 changes: 121 additions & 0 deletions docs/windows-arm64-signing.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
# Windows ARM64 signing and maintainer handoff

This runbook describes how Virtual Display Driver maintainers can produce a **Microsoft-signed ARM64 preview** that installs on HVCI-enabled Windows on ARM systems (for example Surface Laptop 7 with Snapdragon X), and how to graduate that package to WHCP certification later.

## Background

- RealWarp and other consumers detect `MttVDD` through `Root\MttVDD` and `\\.\pipe\MTTVirtualDisplayPipe`.
- The upstream ARM64 release payload is architecturally correct (`NTARM64`, native `MttVDD.dll`), but SignPath/GlobalSign catalogs are rejected by the Driver Store on systems with Memory Integrity enabled (`0x800B0109` / untrusted root).
- `PnpLockdown=1` in `MttVDD.inf` is **installed-file protection** and should remain enabled; it is not a WHQL-only switch.

Related issues:

- [#465](https://github.com/VirtualDrivers/Virtual-Display-Driver/issues/465) — ARM64 package trust failure on install
- [#483](https://github.com/VirtualDrivers/Virtual-Display-Driver/issues/483) — post-install display attachment on ARM

## Repository automation

| Script | Purpose |
| --- | --- |
| `scripts/Test-Arm64DriverPackage.ps1` | Validates folder or CAB payloads (`CI` or `Release` policy) |
| `scripts/New-AttestationCab.ps1` | Builds a Partner Center-ready attestation CAB from ARM64 Release output |
| `scripts/Invoke-Arm64AcceptanceTest.ps1` | Surface acceptance harness (Secure Boot/HVCI + Release signature gate + optional install) |

GitHub Actions (`.github/workflows/ci-validation.yml`) builds ARM64 Release output, runs **CI** validation, creates the attestation CAB, round-trip validates the CAB, and uploads:

- `VDD-ARM64-Release` — raw build output
- `VDD-ARM64-Attestation-CAB` — submission CAB (unsigned; EV signing happens offline)

## Maintainer workflow

### 1. Build and validate locally or from CI

```powershell
# After ARM64 Release build output exists:
./scripts/Test-Arm64DriverPackage.ps1 `
-PackagePath "Virtual Display Driver (HDR)\ARM64\Release\MttVDD" `
-Policy CI

./scripts/New-AttestationCab.ps1 `
-InputDirectory "Virtual Display Driver (HDR)\ARM64\Release\MttVDD" `
-OutputDirectory "artifacts\VDD\ARM64"
```

Expected payload inside the CAB subfolder:

- `MttVDD.inf`
- `MttVDD.dll` (PE machine `0xAA64`)
- `MttVDD.pdb`
- `MttVDD.cat` (company catalog for verification; Microsoft replaces it)

Requirements from Microsoft attestation signing:

- Driver files must live in a **single subfolder** inside the CAB (never at CAB root).
- Subfolder name must be **fewer than 40 characters** and use no UNC paths during packaging.
- See [Attestation sign Windows drivers](https://learn.microsoft.com/windows-hardware/drivers/dashboard/code-signing-attestation).

### 2. EV-sign the CAB offline

Use the organization EV certificate and SignTool (SHA-256):

```cmd
SignTool sign /s MY /n "Company Name" /fd sha256 /tr http://timestamp.digicert.com /td sha256 /v MttVDD-ARM64-Attestation.cab
```

Do **not** store EV credentials in GitHub Actions.

### 3. Submit attestation preview in Partner Center

1. Open the [Partner Center hardware dashboard](https://partner.microsoft.com/dashboard/hardware/Search).
2. Choose **Submit new hardware**.
3. Upload the **EV-signed CAB**.
4. Leave test-signing options **unchecked** for the production-preview path on HVCI systems.
5. Request the Windows Desktop signatures needed for ARM64 user-mode driver attestation.
6. Download Microsoft’s returned package when processing completes.

Reference: [Driver signing options and best practices — attestation for testing scenarios](https://learn.microsoft.com/windows-hardware/drivers/dashboard/driver-signing-offerings#attestation-signed-drivers-for-testing-scenarios).

### 4. Validate the returned package (Release policy)

```powershell
./scripts/Test-Arm64DriverPackage.ps1 `
-PackagePath ".\downloads\MttVDD-ARM64-MicrosoftSigned.cab" `
-Policy Release
```

Release policy requires a catalog subject containing **Microsoft Windows Hardware Compatibility Publisher** and rejects SignPath-only catalogs.

Optional Surface preflight:

```powershell
./scripts/Invoke-Arm64AcceptanceTest.ps1 `
-PackagePath ".\downloads\MttVDD-ARM64-MicrosoftSigned.cab" `
-Mode Preflight
```

### 5. Publish an ARM64 preview release

1. Extract the Microsoft-signed package into a clearly named release asset, for example `VirtualDisplayDriver-ARM64.Driver.Only.zip`.
2. Mark the release as **ARM64 preview / attestation-signed** in release notes.
3. Link issues #465 and #483 and note that WHCP certification is the follow-up for broad retail/Windows Update distribution.

### 6. Graduate to WHCP (later)

Reuse the same validated ARM64 payload for HLK/WHCP submission when an ARM64 HLK lab is available. Attestation preview unblocks HVCI-enabled test machines; WHCP remains the supported public-release path.

## Acceptance criteria on Windows on ARM

Run with Secure Boot and Memory Integrity **enabled** (no test-signing boot configuration or trust-store workarounds):

1. **Static gate** — `Test-Arm64DriverPackage.ps1 -Policy Release` passes.
2. **Install gate** — `Invoke-Arm64AcceptanceTest.ps1 -Mode Install` installs `Root\MttVDD` with device status OK and no Code Integrity rejection.
3. **Display gate** — one free-tier virtual display attaches to extended topology (DisplayConfig / `EnumDisplayMonitors` / DXGI).
4. **RealWarp gate** — RealWarp 0.38.0 (x64 emulation) detects MttVDD, connects to `MTTVirtualDisplayPipe`, captures desktop, and renders on XREAL One / One Pro.

If signing succeeds but topology fails, treat that as issue #483 (IddCx attachment) rather than changing signing policy.

## Evidence captured on Surface (SignPath baseline)

The current public ARM64 package (`25.7.23`) fails Release validation because the catalog chains to **SignPath Foundation / GlobalSign**, not Microsoft WHCP. On HVCI-enabled systems this surfaces as Driver Store staging error **`0x800B0109`** (*A certificate chain processed, but terminated in a root certificate which is not trusted by the trust provider*).

That failure occurs **before** UMDF/IddCx load; replacing RealWarp’s bundled x64 Parsec driver with a **Microsoft-signed ARM64 MttVDD** package is the required production path.
210 changes: 210 additions & 0 deletions scripts/Invoke-Arm64AcceptanceTest.ps1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,210 @@
#Requires -Version 5.1
<#
.SYNOPSIS
Runs Surface acceptance checks for a Microsoft-signed ARM64 MttVDD package.

.DESCRIPTION
Preflight mode verifies Secure Boot and HVCI remain enabled and validates the package
with Release policy. Install mode performs NefCon-based installation and display
topology checks when Release validation succeeds.

.PARAMETER PackagePath
Directory or CAB containing the Microsoft-signed ARM64 driver package.

.PARAMETER Mode
Preflight - security and signature validation only (default)
Install - install driver and verify device/display topology when signature gate passes

.PARAMETER NefConPath
Optional path to nefconw.exe. Defaults to ARM64 binary extracted from the latest NefCon release.
#>
[CmdletBinding()]
param(
[Parameter(Mandatory = $true)]
[ValidateNotNullOrEmpty()]
[string]$PackagePath,

[ValidateSet('Preflight', 'Install')]
[string]$Mode = 'Preflight',

[string]$NefConPath
)

Set-StrictMode -Version Latest
$ErrorActionPreference = 'Stop'

function Test-SecurityGates {
$results = @()

$secureBoot = $false
try {
$secureBoot = Confirm-SecureBootUEFI -ErrorAction Stop
}
catch {
$reg = Get-ItemProperty -Path 'HKLM:\SYSTEM\CurrentControlSet\Control\SecureBoot\State' -Name UEFISecureBootEnabled -ErrorAction SilentlyContinue
$secureBoot = ($reg.UEFISecureBootEnabled -eq 1)
}

$results += [pscustomobject]@{
Check = 'SecureBoot'
Status = $(if ($secureBoot) { 'PASS' } else { 'FAIL' })
Detail = $(if ($secureBoot) { 'Secure Boot is enabled.' } else { 'Secure Boot is disabled or could not be verified.' })
}

$hvciEnabled = $false
try {
$ci = Get-CimInstance -ClassName Win32_DeviceGuard -Namespace root\Microsoft\Windows\DeviceGuard -ErrorAction Stop
$hvciEnabled = ($ci.SecurityServicesRunning -contains 1) -or ($ci.VirtualizationBasedSecurityStatus -ge 2)
}
catch {
$reg = Get-ItemProperty -Path 'HKLM:\SYSTEM\CurrentControlSet\Control\DeviceGuard\Scenarios\HypervisorEnforcedCodeIntegrity' -Name Enabled -ErrorAction SilentlyContinue
$hvciEnabled = ($reg.Enabled -eq 1)
}

$results += [pscustomobject]@{
Check = 'HVCI'
Status = $(if ($hvciEnabled) { 'PASS' } else { 'FAIL' })
Detail = $(if ($hvciEnabled) { 'Memory Integrity / HVCI appears enabled.' } else { 'Memory Integrity / HVCI does not appear enabled.' })
}

return $results
}

function Get-DriverPackageDirectory {
param(
[string]$Path
)

$item = Get-Item -LiteralPath $Path
if ($item.PSIsContainer) {
$inf = Get-ChildItem -LiteralPath $item.FullName -Filter 'MttVDD.inf' -Recurse -File | Select-Object -First 1
if (-not $inf) {
throw "MttVDD.inf not found under $Path"
}
return $inf.Directory.FullName
}

$expandRoot = Join-Path ([System.IO.Path]::GetTempPath()) ("vdd-arm64-accept-" + [guid]::NewGuid().ToString('N'))
New-Item -ItemType Directory -Path $expandRoot -Force | Out-Null
expand.exe $item.FullName -F:* $expandRoot | Out-Null
if ($LASTEXITCODE -ne 0) {
throw "Failed to expand CAB: $Path"
}

$inf = Get-ChildItem -LiteralPath $expandRoot -Filter 'MttVDD.inf' -Recurse -File | Select-Object -First 1
if (-not $inf) {
throw "MttVDD.inf not found in expanded CAB."
}

return $inf.Directory.FullName
}

function Ensure-NefCon {
param(
[string]$ExplicitPath
)

if ($ExplicitPath) {
if (-not (Test-Path -LiteralPath $ExplicitPath)) {
throw "NefCon not found at $ExplicitPath"
}
return (Resolve-Path -LiteralPath $ExplicitPath).Path
}

$tempDir = Join-Path $env:TEMP 'VDDAcceptanceNefCon'
$arm64Exe = Join-Path $tempDir 'ARM64\nefconw.exe'
if (-not (Test-Path -LiteralPath $arm64Exe)) {
New-Item -ItemType Directory -Path $tempDir -Force | Out-Null
$zipPath = Join-Path $tempDir 'nefcon.zip'
Invoke-WebRequest -Uri 'https://github.com/nefarius/nefcon/releases/download/v1.14.0/nefcon_v1.14.0.zip' -OutFile $zipPath -UseBasicParsing
Expand-Archive -LiteralPath $zipPath -DestinationPath $tempDir -Force
}

if (-not (Test-Path -LiteralPath $arm64Exe)) {
throw 'ARM64 nefconw.exe not found after extraction.'
}

return (Resolve-Path -LiteralPath $arm64Exe).Path
}

$validator = Join-Path $PSScriptRoot 'Test-Arm64DriverPackage.ps1'
if (-not (Test-Path -LiteralPath $validator)) {
throw "Validation script not found: $validator"
}

Write-Host '=== ARM64 MttVDD acceptance preflight ===' -ForegroundColor Cyan
$securityResults = Test-SecurityGates
$securityResults | ForEach-Object {
$color = if ($_.Status -eq 'PASS') { 'Green' } else { 'Red' }
Write-Host ("[{0}] {1} - {2}" -f $_.Status, $_.Check, $_.Detail) -ForegroundColor $color
}

if (@($securityResults | Where-Object { $_.Status -eq 'FAIL' }).Count -gt 0) {
throw 'Security gate failed. Acceptance testing requires Secure Boot and HVCI enabled.'
}

Write-Host '=== Release signature validation ===' -ForegroundColor Cyan
& $validator -PackagePath $PackagePath -Policy Release

if ($Mode -eq 'Preflight') {
Write-Host 'Preflight acceptance checks passed. Install mode can proceed once a Microsoft-signed package is available.' -ForegroundColor Green
return
}

$packageDir = Get-DriverPackageDirectory -Path $PackagePath
$nefcon = Ensure-NefCon -ExplicitPath $NefConPath
$infPath = Join-Path $packageDir 'MttVDD.inf'

Write-Host '=== Installing Root\MttVDD via NefCon ===' -ForegroundColor Cyan
Push-Location $packageDir
try {
& $nefcon install $infPath 'Root\MttVDD'
if ($LASTEXITCODE -ne 0) {
throw "nefconw install failed with exit code $LASTEXITCODE"
}
}
finally {
Pop-Location
}

Start-Sleep -Seconds 5

$pnpDevice = Get-PnpDevice -FriendlyName '*Virtual Display Driver*' -ErrorAction SilentlyContinue | Select-Object -First 1
if (-not $pnpDevice) {
$pnpDevice = Get-PnpDevice -InstanceId '*Root\MttVDD*' -ErrorAction SilentlyContinue | Select-Object -First 1
}

if (-not $pnpDevice -or $pnpDevice.Status -ne 'OK') {
throw 'MttVDD device not present or not healthy after install.'
}

Write-Host ("Device {0} status: {1}" -f $pnpDevice.InstanceId, $pnpDevice.Status) -ForegroundColor Green

try {
Add-Type @"
using System;
using System.Runtime.InteropServices;
public static class MonitorEnumAcceptance {
public delegate bool EnumMonitorsDelegate(IntPtr hMonitor, IntPtr hdcMonitor, ref RECT lprcMonitor, IntPtr dwData);
[StructLayout(LayoutKind.Sequential)]
public struct RECT { public int Left; public int Top; public int Right; public int Bottom; }
[DllImport("user32.dll")]
public static extern bool EnumDisplayMonitors(IntPtr hdc, IntPtr lprcClip, EnumMonitorsDelegate lpfnEnum, IntPtr dwData);
public static int CountMonitors() {
int count = 0;
EnumMonitorsDelegate callback = delegate (IntPtr hMonitor, IntPtr hdcMonitor, ref RECT lprcMonitor, IntPtr dwData) {
count++;
return true;
};
EnumDisplayMonitors(IntPtr.Zero, IntPtr.Zero, callback, IntPtr.Zero);
return count;
}
}
"@
$monitorCount = [MonitorEnumAcceptance]::CountMonitors()
Write-Host "EnumDisplayMonitors reported $monitorCount monitors." -ForegroundColor Green
}
catch {
Write-Warning "EnumDisplayMonitors probe unavailable: $($_.Exception.Message)"
}
Write-Host 'Install acceptance checks completed. Verify extended topology manually and test RealWarp 0.38.0 with XREAL One / One Pro.' -ForegroundColor Green
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions .github/workflows/ci-validation.yml
Original file line numberDiff line numberDiff line change
Expand Up@@ -142,9 +142,37 @@ jobs:
New-Item -ItemType Directory -Path $dest -Force | Out-Null
Copy-Item "$outDir\*" -Destination $dest -Recurse -Force

- name: Validate ARM64 driver package (CI policy)
if: matrix.platform == 'ARM64'
shell: pwsh
run: |
$ErrorActionPreference = "Stop"
./scripts/Test-Arm64DriverPackage.ps1 `
-PackagePath "artifacts/VDD/ARM64" `
-Policy CI

- name: Create ARM64 attestation CAB
if: matrix.platform == 'ARM64'
shell: pwsh
run: |
$ErrorActionPreference = "Stop"
$result = ./scripts/New-AttestationCab.ps1 `
-InputDirectory "artifacts/VDD/ARM64" `
-OutputDirectory "artifacts/VDD/ARM64" `
-CabFileName "MttVDD-ARM64-Attestation.cab"
Write-Output "Created attestation CAB at $($result.CabPath)"

- name: Upload artifacts
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: VDD-${{ matrix.platform }}-${{ env.BUILD_CONFIGURATION }}
path: artifacts/VDD/${{ matrix.platform }}/
if-no-files-found: error

- name: Upload ARM64 attestation CAB
if: matrix.platform == 'ARM64'
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: VDD-ARM64-Attestation-CAB
path: artifacts/VDD/ARM64/MttVDD-ARM64-Attestation.cab
if-no-files-found: error
121 changes: 121 additions & 0 deletions docs/windows-arm64-signing.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
# Windows ARM64 signing and maintainer handoff

This runbook describes how Virtual Display Driver maintainers can produce a **Microsoft-signed ARM64 preview** that installs on HVCI-enabled Windows on ARM systems (for example Surface Laptop 7 with Snapdragon X), and how to graduate that package to WHCP certification later.

## Background

- RealWarp and other consumers detect `MttVDD` through `Root\MttVDD` and `\\.\pipe\MTTVirtualDisplayPipe`.
- The upstream ARM64 release payload is architecturally correct (`NTARM64`, native `MttVDD.dll`), but SignPath/GlobalSign catalogs are rejected by the Driver Store on systems with Memory Integrity enabled (`0x800B0109` / untrusted root).
- `PnpLockdown=1` in `MttVDD.inf` is **installed-file protection** and should remain enabled; it is not a WHQL-only switch.

Related issues:

- [#465](https://github.com/VirtualDrivers/Virtual-Display-Driver/issues/465) — ARM64 package trust failure on install
- [#483](https://github.com/VirtualDrivers/Virtual-Display-Driver/issues/483) — post-install display attachment on ARM

## Repository automation

| Script | Purpose |
| --- | --- |
| `scripts/Test-Arm64DriverPackage.ps1` | Validates folder or CAB payloads (`CI` or `Release` policy) |
| `scripts/New-AttestationCab.ps1` | Builds a Partner Center-ready attestation CAB from ARM64 Release output |
| `scripts/Invoke-Arm64AcceptanceTest.ps1` | Surface acceptance harness (Secure Boot/HVCI + Release signature gate + optional install) |

GitHub Actions (`.github/workflows/ci-validation.yml`) builds ARM64 Release output, runs **CI** validation, creates the attestation CAB, round-trip validates the CAB, and uploads:

- `VDD-ARM64-Release` — raw build output
- `VDD-ARM64-Attestation-CAB` — submission CAB (unsigned; EV signing happens offline)

## Maintainer workflow

### 1. Build and validate locally or from CI

```powershell
# After ARM64 Release build output exists:
./scripts/Test-Arm64DriverPackage.ps1 `
-PackagePath "Virtual Display Driver (HDR)\ARM64\Release\MttVDD" `
-Policy CI

./scripts/New-AttestationCab.ps1 `
-InputDirectory "Virtual Display Driver (HDR)\ARM64\Release\MttVDD" `
-OutputDirectory "artifacts\VDD\ARM64"
```

Expected payload inside the CAB subfolder:

- `MttVDD.inf`
- `MttVDD.dll` (PE machine `0xAA64`)
- `MttVDD.pdb`
- `MttVDD.cat` (company catalog for verification; Microsoft replaces it)

Requirements from Microsoft attestation signing:

- Driver files must live in a **single subfolder** inside the CAB (never at CAB root).
- Subfolder name must be **fewer than 40 characters** and use no UNC paths during packaging.
- See [Attestation sign Windows drivers](https://learn.microsoft.com/windows-hardware/drivers/dashboard/code-signing-attestation).

### 2. EV-sign the CAB offline

Use the organization EV certificate and SignTool (SHA-256):

```cmd
SignTool sign /s MY /n "Company Name" /fd sha256 /tr http://timestamp.digicert.com /td sha256 /v MttVDD-ARM64-Attestation.cab
```

Do **not** store EV credentials in GitHub Actions.

### 3. Submit attestation preview in Partner Center

1. Open the [Partner Center hardware dashboard](https://partner.microsoft.com/dashboard/hardware/Search).
2. Choose **Submit new hardware**.
3. Upload the **EV-signed CAB**.
4. Leave test-signing options **unchecked** for the production-preview path on HVCI systems.
5. Request the Windows Desktop signatures needed for ARM64 user-mode driver attestation.
6. Download Microsoft’s returned package when processing completes.

Reference: [Driver signing options and best practices — attestation for testing scenarios](https://learn.microsoft.com/windows-hardware/drivers/dashboard/driver-signing-offerings#attestation-signed-drivers-for-testing-scenarios).

### 4. Validate the returned package (Release policy)

```powershell
./scripts/Test-Arm64DriverPackage.ps1 `
-PackagePath ".\downloads\MttVDD-ARM64-MicrosoftSigned.cab" `
-Policy Release
```

Release policy requires a catalog subject containing **Microsoft Windows Hardware Compatibility Publisher** and rejects SignPath-only catalogs.

Optional Surface preflight:

```powershell
./scripts/Invoke-Arm64AcceptanceTest.ps1 `
-PackagePath ".\downloads\MttVDD-ARM64-MicrosoftSigned.cab" `
-Mode Preflight
```

### 5. Publish an ARM64 preview release

1. Extract the Microsoft-signed package into a clearly named release asset, for example `VirtualDisplayDriver-ARM64.Driver.Only.zip`.
2. Mark the release as **ARM64 preview / attestation-signed** in release notes.
3. Link issues #465 and #483 and note that WHCP certification is the follow-up for broad retail/Windows Update distribution.

### 6. Graduate to WHCP (later)

Reuse the same validated ARM64 payload for HLK/WHCP submission when an ARM64 HLK lab is available. Attestation preview unblocks HVCI-enabled test machines; WHCP remains the supported public-release path.

## Acceptance criteria on Windows on ARM

Run with Secure Boot and Memory Integrity **enabled** (no test-signing boot configuration or trust-store workarounds):

1. **Static gate** — `Test-Arm64DriverPackage.ps1 -Policy Release` passes.
2. **Install gate** — `Invoke-Arm64AcceptanceTest.ps1 -Mode Install` installs `Root\MttVDD` with device status OK and no Code Integrity rejection.
3. **Display gate** — one free-tier virtual display attaches to extended topology (DisplayConfig / `EnumDisplayMonitors` / DXGI).
4. **RealWarp gate** — RealWarp 0.38.0 (x64 emulation) detects MttVDD, connects to `MTTVirtualDisplayPipe`, captures desktop, and renders on XREAL One / One Pro.

If signing succeeds but topology fails, treat that as issue #483 (IddCx attachment) rather than changing signing policy.

## Evidence captured on Surface (SignPath baseline)

The current public ARM64 package (`25.7.23`) fails Release validation because the catalog chains to **SignPath Foundation / GlobalSign**, not Microsoft WHCP. On HVCI-enabled systems this surfaces as Driver Store staging error **`0x800B0109`** (*A certificate chain processed, but terminated in a root certificate which is not trusted by the trust provider*).

That failure occurs **before** UMDF/IddCx load; replacing RealWarp’s bundled x64 Parsec driver with a **Microsoft-signed ARM64 MttVDD** package is the required production path.
210 changes: 210 additions & 0 deletions scripts/Invoke-Arm64AcceptanceTest.ps1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,210 @@
#Requires -Version 5.1
<#
.SYNOPSIS
Runs Surface acceptance checks for a Microsoft-signed ARM64 MttVDD package.

.DESCRIPTION
Preflight mode verifies Secure Boot and HVCI remain enabled and validates the package
with Release policy. Install mode performs NefCon-based installation and display
topology checks when Release validation succeeds.

.PARAMETER PackagePath
Directory or CAB containing the Microsoft-signed ARM64 driver package.

.PARAMETER Mode
Preflight - security and signature validation only (default)
Install - install driver and verify device/display topology when signature gate passes

.PARAMETER NefConPath
Optional path to nefconw.exe. Defaults to ARM64 binary extracted from the latest NefCon release.
#>
[CmdletBinding()]
param(
[Parameter(Mandatory = $true)]
[ValidateNotNullOrEmpty()]
[string]$PackagePath,

[ValidateSet('Preflight', 'Install')]
[string]$Mode = 'Preflight',

[string]$NefConPath
)

Set-StrictMode -Version Latest
$ErrorActionPreference = 'Stop'

function Test-SecurityGates {
$results = @()

$secureBoot = $false
try {
$secureBoot = Confirm-SecureBootUEFI -ErrorAction Stop
}
catch {
$reg = Get-ItemProperty -Path 'HKLM:\SYSTEM\CurrentControlSet\Control\SecureBoot\State' -Name UEFISecureBootEnabled -ErrorAction SilentlyContinue
$secureBoot = ($reg.UEFISecureBootEnabled -eq 1)
}

$results += [pscustomobject]@{
Check = 'SecureBoot'
Status = $(if ($secureBoot) { 'PASS' } else { 'FAIL' })
Detail = $(if ($secureBoot) { 'Secure Boot is enabled.' } else { 'Secure Boot is disabled or could not be verified.' })
}

$hvciEnabled = $false
try {
$ci = Get-CimInstance -ClassName Win32_DeviceGuard -Namespace root\Microsoft\Windows\DeviceGuard -ErrorAction Stop
$hvciEnabled = ($ci.SecurityServicesRunning -contains 1) -or ($ci.VirtualizationBasedSecurityStatus -ge 2)
}
catch {
$reg = Get-ItemProperty -Path 'HKLM:\SYSTEM\CurrentControlSet\Control\DeviceGuard\Scenarios\HypervisorEnforcedCodeIntegrity' -Name Enabled -ErrorAction SilentlyContinue
$hvciEnabled = ($reg.Enabled -eq 1)
}

$results += [pscustomobject]@{
Check = 'HVCI'
Status = $(if ($hvciEnabled) { 'PASS' } else { 'FAIL' })
Detail = $(if ($hvciEnabled) { 'Memory Integrity / HVCI appears enabled.' } else { 'Memory Integrity / HVCI does not appear enabled.' })
}

return $results
}

function Get-DriverPackageDirectory {
param(
[string]$Path
)

$item = Get-Item -LiteralPath $Path
if ($item.PSIsContainer) {
$inf = Get-ChildItem -LiteralPath $item.FullName -Filter 'MttVDD.inf' -Recurse -File | Select-Object -First 1
if (-not $inf) {
throw "MttVDD.inf not found under $Path"
}
return $inf.Directory.FullName
}

$expandRoot = Join-Path ([System.IO.Path]::GetTempPath()) ("vdd-arm64-accept-" + [guid]::NewGuid().ToString('N'))
New-Item -ItemType Directory -Path $expandRoot -Force | Out-Null
expand.exe $item.FullName -F:* $expandRoot | Out-Null
if ($LASTEXITCODE -ne 0) {
throw "Failed to expand CAB: $Path"
}

$inf = Get-ChildItem -LiteralPath $expandRoot -Filter 'MttVDD.inf' -Recurse -File | Select-Object -First 1
if (-not $inf) {
throw "MttVDD.inf not found in expanded CAB."
}

return $inf.Directory.FullName
}

function Ensure-NefCon {
param(
[string]$ExplicitPath
)

if ($ExplicitPath) {
if (-not (Test-Path -LiteralPath $ExplicitPath)) {
throw "NefCon not found at $ExplicitPath"
}
return (Resolve-Path -LiteralPath $ExplicitPath).Path
}

$tempDir = Join-Path $env:TEMP 'VDDAcceptanceNefCon'
$arm64Exe = Join-Path $tempDir 'ARM64\nefconw.exe'
if (-not (Test-Path -LiteralPath $arm64Exe)) {
New-Item -ItemType Directory -Path $tempDir -Force | Out-Null
$zipPath = Join-Path $tempDir 'nefcon.zip'
Invoke-WebRequest -Uri 'https://github.com/nefarius/nefcon/releases/download/v1.14.0/nefcon_v1.14.0.zip' -OutFile $zipPath -UseBasicParsing
Expand-Archive -LiteralPath $zipPath -DestinationPath $tempDir -Force
}

if (-not (Test-Path -LiteralPath $arm64Exe)) {
throw 'ARM64 nefconw.exe not found after extraction.'
}

return (Resolve-Path -LiteralPath $arm64Exe).Path
}

$validator = Join-Path $PSScriptRoot 'Test-Arm64DriverPackage.ps1'
if (-not (Test-Path -LiteralPath $validator)) {
throw "Validation script not found: $validator"
}

Write-Host '=== ARM64 MttVDD acceptance preflight ===' -ForegroundColor Cyan
$securityResults = Test-SecurityGates
$securityResults | ForEach-Object {
$color = if ($_.Status -eq 'PASS') { 'Green' } else { 'Red' }
Write-Host ("[{0}] {1} - {2}" -f $_.Status, $_.Check, $_.Detail) -ForegroundColor $color
}

if (@($securityResults | Where-Object { $_.Status -eq 'FAIL' }).Count -gt 0) {
throw 'Security gate failed. Acceptance testing requires Secure Boot and HVCI enabled.'
}

Write-Host '=== Release signature validation ===' -ForegroundColor Cyan
& $validator -PackagePath $PackagePath -Policy Release

if ($Mode -eq 'Preflight') {
Write-Host 'Preflight acceptance checks passed. Install mode can proceed once a Microsoft-signed package is available.' -ForegroundColor Green
return
}

$packageDir = Get-DriverPackageDirectory -Path $PackagePath
$nefcon = Ensure-NefCon -ExplicitPath $NefConPath
$infPath = Join-Path $packageDir 'MttVDD.inf'

Write-Host '=== Installing Root\MttVDD via NefCon ===' -ForegroundColor Cyan
Push-Location $packageDir
try {
& $nefcon install $infPath 'Root\MttVDD'
if ($LASTEXITCODE -ne 0) {
throw "nefconw install failed with exit code $LASTEXITCODE"
}
}
finally {
Pop-Location
}

Start-Sleep -Seconds 5

$pnpDevice = Get-PnpDevice -FriendlyName '*Virtual Display Driver*' -ErrorAction SilentlyContinue | Select-Object -First 1
if (-not $pnpDevice) {
$pnpDevice = Get-PnpDevice -InstanceId '*Root\MttVDD*' -ErrorAction SilentlyContinue | Select-Object -First 1
}

if (-not $pnpDevice -or $pnpDevice.Status -ne 'OK') {
throw 'MttVDD device not present or not healthy after install.'
}

Write-Host ("Device {0} status: {1}" -f $pnpDevice.InstanceId, $pnpDevice.Status) -ForegroundColor Green

try {
Add-Type @"
using System;
using System.Runtime.InteropServices;
public static class MonitorEnumAcceptance {
public delegate bool EnumMonitorsDelegate(IntPtr hMonitor, IntPtr hdcMonitor, ref RECT lprcMonitor, IntPtr dwData);
[StructLayout(LayoutKind.Sequential)]
public struct RECT { public int Left; public int Top; public int Right; public int Bottom; }
[DllImport("user32.dll")]
public static extern bool EnumDisplayMonitors(IntPtr hdc, IntPtr lprcClip, EnumMonitorsDelegate lpfnEnum, IntPtr dwData);
public static int CountMonitors() {
int count = 0;
EnumMonitorsDelegate callback = delegate (IntPtr hMonitor, IntPtr hdcMonitor, ref RECT lprcMonitor, IntPtr dwData) {
count++;
return true;
};
EnumDisplayMonitors(IntPtr.Zero, IntPtr.Zero, callback, IntPtr.Zero);
return count;
}
}
"@
$monitorCount = [MonitorEnumAcceptance]::CountMonitors()
Write-Host "EnumDisplayMonitors reported $monitorCount monitors." -ForegroundColor Green
}
catch {
Write-Warning "EnumDisplayMonitors probe unavailable: $($_.Exception.Message)"
}
Write-Host 'Install acceptance checks completed. Verify extended topology manually and test RealWarp 0.38.0 with XREAL One / One Pro.' -ForegroundColor Green
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions .github/workflows/ci-validation.yml
Original file line numberDiff line numberDiff line change
Expand Up@@ -142,9 +142,37 @@ jobs:
New-Item -ItemType Directory -Path $dest -Force | Out-Null
Copy-Item "$outDir\*" -Destination $dest -Recurse -Force

- name: Validate ARM64 driver package (CI policy)
if: matrix.platform == 'ARM64'
shell: pwsh
run: |
$ErrorActionPreference = "Stop"
./scripts/Test-Arm64DriverPackage.ps1 `
-PackagePath "artifacts/VDD/ARM64" `
-Policy CI

- name: Create ARM64 attestation CAB
if: matrix.platform == 'ARM64'
shell: pwsh
run: |
$ErrorActionPreference = "Stop"
$result = ./scripts/New-AttestationCab.ps1 `
-InputDirectory "artifacts/VDD/ARM64" `
-OutputDirectory "artifacts/VDD/ARM64" `
-CabFileName "MttVDD-ARM64-Attestation.cab"
Write-Output "Created attestation CAB at $($result.CabPath)"

- name: Upload artifacts
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: VDD-${{ matrix.platform }}-${{ env.BUILD_CONFIGURATION }}
path: artifacts/VDD/${{ matrix.platform }}/
if-no-files-found: error

- name: Upload ARM64 attestation CAB
if: matrix.platform == 'ARM64'
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: VDD-ARM64-Attestation-CAB
path: artifacts/VDD/ARM64/MttVDD-ARM64-Attestation.cab
if-no-files-found: error
121 changes: 121 additions & 0 deletions docs/windows-arm64-signing.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
# Windows ARM64 signing and maintainer handoff

This runbook describes how Virtual Display Driver maintainers can produce a **Microsoft-signed ARM64 preview** that installs on HVCI-enabled Windows on ARM systems (for example Surface Laptop 7 with Snapdragon X), and how to graduate that package to WHCP certification later.

## Background

- RealWarp and other consumers detect `MttVDD` through `Root\MttVDD` and `\\.\pipe\MTTVirtualDisplayPipe`.
- The upstream ARM64 release payload is architecturally correct (`NTARM64`, native `MttVDD.dll`), but SignPath/GlobalSign catalogs are rejected by the Driver Store on systems with Memory Integrity enabled (`0x800B0109` / untrusted root).
- `PnpLockdown=1` in `MttVDD.inf` is **installed-file protection** and should remain enabled; it is not a WHQL-only switch.

Related issues:

- [#465](https://github.com/VirtualDrivers/Virtual-Display-Driver/issues/465) — ARM64 package trust failure on install
- [#483](https://github.com/VirtualDrivers/Virtual-Display-Driver/issues/483) — post-install display attachment on ARM

## Repository automation

| Script | Purpose |
| --- | --- |
| `scripts/Test-Arm64DriverPackage.ps1` | Validates folder or CAB payloads (`CI` or `Release` policy) |
| `scripts/New-AttestationCab.ps1` | Builds a Partner Center-ready attestation CAB from ARM64 Release output |
| `scripts/Invoke-Arm64AcceptanceTest.ps1` | Surface acceptance harness (Secure Boot/HVCI + Release signature gate + optional install) |

GitHub Actions (`.github/workflows/ci-validation.yml`) builds ARM64 Release output, runs **CI** validation, creates the attestation CAB, round-trip validates the CAB, and uploads:

- `VDD-ARM64-Release` — raw build output
- `VDD-ARM64-Attestation-CAB` — submission CAB (unsigned; EV signing happens offline)

## Maintainer workflow

### 1. Build and validate locally or from CI

```powershell
# After ARM64 Release build output exists:
./scripts/Test-Arm64DriverPackage.ps1 `
-PackagePath "Virtual Display Driver (HDR)\ARM64\Release\MttVDD" `
-Policy CI

./scripts/New-AttestationCab.ps1 `
-InputDirectory "Virtual Display Driver (HDR)\ARM64\Release\MttVDD" `
-OutputDirectory "artifacts\VDD\ARM64"
```

Expected payload inside the CAB subfolder:

- `MttVDD.inf`
- `MttVDD.dll` (PE machine `0xAA64`)
- `MttVDD.pdb`
- `MttVDD.cat` (company catalog for verification; Microsoft replaces it)

Requirements from Microsoft attestation signing:

- Driver files must live in a **single subfolder** inside the CAB (never at CAB root).
- Subfolder name must be **fewer than 40 characters** and use no UNC paths during packaging.
- See [Attestation sign Windows drivers](https://learn.microsoft.com/windows-hardware/drivers/dashboard/code-signing-attestation).

### 2. EV-sign the CAB offline

Use the organization EV certificate and SignTool (SHA-256):

```cmd
SignTool sign /s MY /n "Company Name" /fd sha256 /tr http://timestamp.digicert.com /td sha256 /v MttVDD-ARM64-Attestation.cab
```

Do **not** store EV credentials in GitHub Actions.

### 3. Submit attestation preview in Partner Center

1. Open the [Partner Center hardware dashboard](https://partner.microsoft.com/dashboard/hardware/Search).
2. Choose **Submit new hardware**.
3. Upload the **EV-signed CAB**.
4. Leave test-signing options **unchecked** for the production-preview path on HVCI systems.
5. Request the Windows Desktop signatures needed for ARM64 user-mode driver attestation.
6. Download Microsoft’s returned package when processing completes.

Reference: [Driver signing options and best practices — attestation for testing scenarios](https://learn.microsoft.com/windows-hardware/drivers/dashboard/driver-signing-offerings#attestation-signed-drivers-for-testing-scenarios).

### 4. Validate the returned package (Release policy)

```powershell
./scripts/Test-Arm64DriverPackage.ps1 `
-PackagePath ".\downloads\MttVDD-ARM64-MicrosoftSigned.cab" `
-Policy Release
```

Release policy requires a catalog subject containing **Microsoft Windows Hardware Compatibility Publisher** and rejects SignPath-only catalogs.

Optional Surface preflight:

```powershell
./scripts/Invoke-Arm64AcceptanceTest.ps1 `
-PackagePath ".\downloads\MttVDD-ARM64-MicrosoftSigned.cab" `
-Mode Preflight
```

### 5. Publish an ARM64 preview release

1. Extract the Microsoft-signed package into a clearly named release asset, for example `VirtualDisplayDriver-ARM64.Driver.Only.zip`.
2. Mark the release as **ARM64 preview / attestation-signed** in release notes.
3. Link issues #465 and #483 and note that WHCP certification is the follow-up for broad retail/Windows Update distribution.

### 6. Graduate to WHCP (later)

Reuse the same validated ARM64 payload for HLK/WHCP submission when an ARM64 HLK lab is available. Attestation preview unblocks HVCI-enabled test machines; WHCP remains the supported public-release path.

## Acceptance criteria on Windows on ARM

Run with Secure Boot and Memory Integrity **enabled** (no test-signing boot configuration or trust-store workarounds):

1. **Static gate** — `Test-Arm64DriverPackage.ps1 -Policy Release` passes.
2. **Install gate** — `Invoke-Arm64AcceptanceTest.ps1 -Mode Install` installs `Root\MttVDD` with device status OK and no Code Integrity rejection.
3. **Display gate** — one free-tier virtual display attaches to extended topology (DisplayConfig / `EnumDisplayMonitors` / DXGI).
4. **RealWarp gate** — RealWarp 0.38.0 (x64 emulation) detects MttVDD, connects to `MTTVirtualDisplayPipe`, captures desktop, and renders on XREAL One / One Pro.

If signing succeeds but topology fails, treat that as issue #483 (IddCx attachment) rather than changing signing policy.

## Evidence captured on Surface (SignPath baseline)

The current public ARM64 package (`25.7.23`) fails Release validation because the catalog chains to **SignPath Foundation / GlobalSign**, not Microsoft WHCP. On HVCI-enabled systems this surfaces as Driver Store staging error **`0x800B0109`** (*A certificate chain processed, but terminated in a root certificate which is not trusted by the trust provider*).

That failure occurs **before** UMDF/IddCx load; replacing RealWarp’s bundled x64 Parsec driver with a **Microsoft-signed ARM64 MttVDD** package is the required production path.
210 changes: 210 additions & 0 deletions scripts/Invoke-Arm64AcceptanceTest.ps1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,210 @@
#Requires -Version 5.1
<#
.SYNOPSIS
Runs Surface acceptance checks for a Microsoft-signed ARM64 MttVDD package.

.DESCRIPTION
Preflight mode verifies Secure Boot and HVCI remain enabled and validates the package
with Release policy. Install mode performs NefCon-based installation and display
topology checks when Release validation succeeds.

.PARAMETER PackagePath
Directory or CAB containing the Microsoft-signed ARM64 driver package.

.PARAMETER Mode
Preflight - security and signature validation only (default)
Install - install driver and verify device/display topology when signature gate passes

.PARAMETER NefConPath
Optional path to nefconw.exe. Defaults to ARM64 binary extracted from the latest NefCon release.
#>
[CmdletBinding()]
param(
[Parameter(Mandatory = $true)]
[ValidateNotNullOrEmpty()]
[string]$PackagePath,

[ValidateSet('Preflight', 'Install')]
[string]$Mode = 'Preflight',

[string]$NefConPath
)

Set-StrictMode -Version Latest
$ErrorActionPreference = 'Stop'

function Test-SecurityGates {
$results = @()

$secureBoot = $false
try {
$secureBoot = Confirm-SecureBootUEFI -ErrorAction Stop
}
catch {
$reg = Get-ItemProperty -Path 'HKLM:\SYSTEM\CurrentControlSet\Control\SecureBoot\State' -Name UEFISecureBootEnabled -ErrorAction SilentlyContinue
$secureBoot = ($reg.UEFISecureBootEnabled -eq 1)
}

$results += [pscustomobject]@{
Check = 'SecureBoot'
Status = $(if ($secureBoot) { 'PASS' } else { 'FAIL' })
Detail = $(if ($secureBoot) { 'Secure Boot is enabled.' } else { 'Secure Boot is disabled or could not be verified.' })
}

$hvciEnabled = $false
try {
$ci = Get-CimInstance -ClassName Win32_DeviceGuard -Namespace root\Microsoft\Windows\DeviceGuard -ErrorAction Stop
$hvciEnabled = ($ci.SecurityServicesRunning -contains 1) -or ($ci.VirtualizationBasedSecurityStatus -ge 2)
}
catch {
$reg = Get-ItemProperty -Path 'HKLM:\SYSTEM\CurrentControlSet\Control\DeviceGuard\Scenarios\HypervisorEnforcedCodeIntegrity' -Name Enabled -ErrorAction SilentlyContinue
$hvciEnabled = ($reg.Enabled -eq 1)
}

$results += [pscustomobject]@{
Check = 'HVCI'
Status = $(if ($hvciEnabled) { 'PASS' } else { 'FAIL' })
Detail = $(if ($hvciEnabled) { 'Memory Integrity / HVCI appears enabled.' } else { 'Memory Integrity / HVCI does not appear enabled.' })
}

return $results
}

function Get-DriverPackageDirectory {
param(
[string]$Path
)

$item = Get-Item -LiteralPath $Path
if ($item.PSIsContainer) {
$inf = Get-ChildItem -LiteralPath $item.FullName -Filter 'MttVDD.inf' -Recurse -File | Select-Object -First 1
if (-not $inf) {
throw "MttVDD.inf not found under $Path"
}
return $inf.Directory.FullName
}

$expandRoot = Join-Path ([System.IO.Path]::GetTempPath()) ("vdd-arm64-accept-" + [guid]::NewGuid().ToString('N'))
New-Item -ItemType Directory -Path $expandRoot -Force | Out-Null
expand.exe $item.FullName -F:* $expandRoot | Out-Null
if ($LASTEXITCODE -ne 0) {
throw "Failed to expand CAB: $Path"
}

$inf = Get-ChildItem -LiteralPath $expandRoot -Filter 'MttVDD.inf' -Recurse -File | Select-Object -First 1
if (-not $inf) {
throw "MttVDD.inf not found in expanded CAB."
}

return $inf.Directory.FullName
}

function Ensure-NefCon {
param(
[string]$ExplicitPath
)

if ($ExplicitPath) {
if (-not (Test-Path -LiteralPath $ExplicitPath)) {
throw "NefCon not found at $ExplicitPath"
}
return (Resolve-Path -LiteralPath $ExplicitPath).Path
}

$tempDir = Join-Path $env:TEMP 'VDDAcceptanceNefCon'
$arm64Exe = Join-Path $tempDir 'ARM64\nefconw.exe'
if (-not (Test-Path -LiteralPath $arm64Exe)) {
New-Item -ItemType Directory -Path $tempDir -Force | Out-Null
$zipPath = Join-Path $tempDir 'nefcon.zip'
Invoke-WebRequest -Uri 'https://github.com/nefarius/nefcon/releases/download/v1.14.0/nefcon_v1.14.0.zip' -OutFile $zipPath -UseBasicParsing
Expand-Archive -LiteralPath $zipPath -DestinationPath $tempDir -Force
}

if (-not (Test-Path -LiteralPath $arm64Exe)) {
throw 'ARM64 nefconw.exe not found after extraction.'
}

return (Resolve-Path -LiteralPath $arm64Exe).Path
}

$validator = Join-Path $PSScriptRoot 'Test-Arm64DriverPackage.ps1'
if (-not (Test-Path -LiteralPath $validator)) {
throw "Validation script not found: $validator"
}

Write-Host '=== ARM64 MttVDD acceptance preflight ===' -ForegroundColor Cyan
$securityResults = Test-SecurityGates
$securityResults | ForEach-Object {
$color = if ($_.Status -eq 'PASS') { 'Green' } else { 'Red' }
Write-Host ("[{0}] {1} - {2}" -f $_.Status, $_.Check, $_.Detail) -ForegroundColor $color
}

if (@($securityResults | Where-Object { $_.Status -eq 'FAIL' }).Count -gt 0) {
throw 'Security gate failed. Acceptance testing requires Secure Boot and HVCI enabled.'
}

Write-Host '=== Release signature validation ===' -ForegroundColor Cyan
& $validator -PackagePath $PackagePath -Policy Release

if ($Mode -eq 'Preflight') {
Write-Host 'Preflight acceptance checks passed. Install mode can proceed once a Microsoft-signed package is available.' -ForegroundColor Green
return
}

$packageDir = Get-DriverPackageDirectory -Path $PackagePath
$nefcon = Ensure-NefCon -ExplicitPath $NefConPath
$infPath = Join-Path $packageDir 'MttVDD.inf'

Write-Host '=== Installing Root\MttVDD via NefCon ===' -ForegroundColor Cyan
Push-Location $packageDir
try {
& $nefcon install $infPath 'Root\MttVDD'
if ($LASTEXITCODE -ne 0) {
throw "nefconw install failed with exit code $LASTEXITCODE"
}
}
finally {
Pop-Location
}

Start-Sleep -Seconds 5

$pnpDevice = Get-PnpDevice -FriendlyName '*Virtual Display Driver*' -ErrorAction SilentlyContinue | Select-Object -First 1
if (-not $pnpDevice) {
$pnpDevice = Get-PnpDevice -InstanceId '*Root\MttVDD*' -ErrorAction SilentlyContinue | Select-Object -First 1
}

if (-not $pnpDevice -or $pnpDevice.Status -ne 'OK') {
throw 'MttVDD device not present or not healthy after install.'
}

Write-Host ("Device {0} status: {1}" -f $pnpDevice.InstanceId, $pnpDevice.Status) -ForegroundColor Green

try {
Add-Type @"
using System;
using System.Runtime.InteropServices;
public static class MonitorEnumAcceptance {
public delegate bool EnumMonitorsDelegate(IntPtr hMonitor, IntPtr hdcMonitor, ref RECT lprcMonitor, IntPtr dwData);
[StructLayout(LayoutKind.Sequential)]
public struct RECT { public int Left; public int Top; public int Right; public int Bottom; }
[DllImport("user32.dll")]
public static extern bool EnumDisplayMonitors(IntPtr hdc, IntPtr lprcClip, EnumMonitorsDelegate lpfnEnum, IntPtr dwData);
public static int CountMonitors() {
int count = 0;
EnumMonitorsDelegate callback = delegate (IntPtr hMonitor, IntPtr hdcMonitor, ref RECT lprcMonitor, IntPtr dwData) {
count++;
return true;
};
EnumDisplayMonitors(IntPtr.Zero, IntPtr.Zero, callback, IntPtr.Zero);
return count;
}
}
"@
$monitorCount = [MonitorEnumAcceptance]::CountMonitors()
Write-Host "EnumDisplayMonitors reported $monitorCount monitors." -ForegroundColor Green
}
catch {
Write-Warning "EnumDisplayMonitors probe unavailable: $($_.Exception.Message)"
}
Write-Host 'Install acceptance checks completed. Verify extended topology manually and test RealWarp 0.38.0 with XREAL One / One Pro.' -ForegroundColor Green
Loading