Keyence VR-3000 G2: imaging-time FIPS opt-out for .exe.configs

Under Intune-enforced LSA FIPS policy, Profilometer / VRAnalyzer /
VRInspection apps crash at device init when MD5CryptoServiceProvider's
ctor is called to verify the probe EEPROM calibration (see keyence3000.txt
+ .png in pxe-images for the dialog + stack).

Patch each .exe.config under C:\Program Files\KEYENCE\<model>\ with
<runtime><enforceFIPSPolicy enabled="false"/></runtime>. Scope is app-CLR
only; OS-wide Lsa FIPS policy stays enforced. CMMC posture: scoped
exception, non-CUI integrity hash, documented in SSP. Each affected bay's
hostname must be on InfoSec's FIPS-exception list before imaging.

09-Setup-Keyence.ps1 gates the patch behind model=vr3000 only. vr5000 /
vr6000 bays do not auto-apply. Verified on win11 VM via qga: 29 configs
across vr5000+vr6000 layouts (vr3000 install was incomplete on VM),
patched + idempotent on re-run, existing <runtime> children preserved.
Also verified on a real PC: 27 patched + 2 skipped (Keyence pre-shipped
the element in two configs), 0 errors.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
cproudlock
2026-05-21 14:35:29 -04:00
parent 37357eee43
commit 2a0b4885fe
2 changed files with 149 additions and 0 deletions

View File

@@ -184,6 +184,41 @@ if ($dxPath) {
Write-KeyenceLog "DXSETUP.exe not found under either Program Files - DirectX install skipped" 'WARN'
}
# ============================================================================
# Step 1.6: FIPS opt-out for Keyence .NET apps (VR-3000 G2 only)
# ============================================================================
# REQUIREMENT BEFORE IMAGING: the bay's hostname must be reported to GE
# InfoSec and added to the documented FIPS-exception list. Do not image a
# VR-3000 G2 bay through this path without that ticket on record.
#
# Why: Keyence VR-series Viewer / Analyzer / Inspection apps read EEPROM
# calibration from the probe and verify it via MD5CryptoServiceProvider.
# Under Intune-enforced LSA FIPS policy that ctor throws (see
# keyence3000.txt / .png in pxe-images for the dialog + stack trace).
# Patch every .exe.config under C:\Program Files\KEYENCE\<model>\ with
# <runtime><enforceFIPSPolicy enabled="false"/></runtime>.
#
# Scope: app-level CLR only. Does NOT touch OS-wide Lsa policy. Intune /
# Defender baselines on the Lsa key remain enforced. CMMC posture: scoped
# exception, non-CUI integrity hash, documented in SSP. Currently only
# approved for VR-3000 G2 model; vr5000 / vr6000 bays do not auto-apply.
if ($model -eq 'vr3000') {
$fipsPatch = Join-Path $PSScriptRoot 'common\Patch-KeyenceFipsConfigs.ps1'
if (Test-Path -LiteralPath $fipsPatch) {
Write-KeyenceLog "Running FIPS opt-out patch (VR-3000 G2): $fipsPatch"
Write-KeyenceLog " REMINDER: confirm bay hostname is on InfoSec FIPS-exception list before production cutover"
try {
& $fipsPatch 2>&1 | ForEach-Object { Write-KeyenceLog " $_" }
} catch {
Write-KeyenceLog " FIPS patch failed: $_" 'WARN'
}
} else {
Write-KeyenceLog "Patch-KeyenceFipsConfigs.ps1 not found at $fipsPatch - FIPS opt-out skipped" 'WARN'
}
} else {
Write-KeyenceLog "FIPS opt-out patch not applied: model=$model (only vr3000 currently approved)"
}
# ============================================================================
# Step 2: OpenText auto-start at login (HostExplorer "WJ Shopfloor" session)
# ============================================================================

View File

@@ -0,0 +1,114 @@
<#
.SYNOPSIS
Patches all Keyence VR-series .exe.config files to opt the app out of .NET
FIPS policy enforcement so the EEPROM MD5 calibration check does not throw
'This implementation is not part of the Windows Platform FIPS validated
cryptographic algorithms.'
Currently approved only for VR-3000 G2. Before running on a production
bay, report the PC hostname to GE InfoSec and confirm the bay is on
the documented FIPS-exception list. Imaging-time auto-apply (in
09-Setup-Keyence.ps1) gates this script behind model=vr3000.
.DESCRIPTION
Scans C:\Program Files\KEYENCE and C:\Program Files (x86)\KEYENCE for any
.exe.config under VR-3000 G2, VR-5000, VR-6000 (and any other Keyence model
that ships in the same root). Injects:
<runtime>
<enforceFIPSPolicy enabled="false"/>
</runtime>
as a sibling under <configuration>. Preserves any existing <runtime>
children (generatePublisherEvidence, useLegacyJit, etc).
Idempotent. Re-running is a no-op for already-patched configs.
This patch is SCOPED to the Keyence app's CLR. It does NOT disable the
OS-wide LSA FIPS policy. Intune / Defender baseline policies that enforce
the LSA key remain in effect.
.PARAMETER InstallRoots
Override the default search roots. By default both C:\Program Files\KEYENCE
and C:\Program Files (x86)\KEYENCE are scanned.
.PARAMETER WhatIf
Show what would be patched without writing any files.
.EXAMPLE
.\Patch-KeyenceFipsConfigs.ps1
.\Patch-KeyenceFipsConfigs.ps1 -WhatIf
.\Patch-KeyenceFipsConfigs.ps1 -InstallRoots 'D:\KEYENCE'
.NOTES
Affected EXEs (Profilometer3.exe, VRAnalyzer*.exe, VRInspection_*.exe, etc.)
must be restarted after the patch for the new config to take effect.
#>
[CmdletBinding(SupportsShouldProcess)]
param(
[string[]]$InstallRoots = @(
'C:\Program Files\KEYENCE',
'C:\Program Files (x86)\KEYENCE'
)
)
$ErrorActionPreference = 'Continue'
$patched = 0
$skipped = 0
$errors = 0
foreach ($root in $InstallRoots) {
if (-not (Test-Path -LiteralPath $root)) {
Write-Host "Root not present, skipping: $root"
continue
}
Write-Host "Scanning: $root"
$configs = Get-ChildItem -LiteralPath $root -Filter '*.exe.config' -Recurse -ErrorAction SilentlyContinue
foreach ($cfg in $configs) {
try {
[xml]$xml = Get-Content -LiteralPath $cfg.FullName
$cfgRoot = $xml.configuration
if (-not $cfgRoot) {
Write-Warning " [SKIP] No <configuration> root: $($cfg.FullName)"
$errors++
continue
}
$rt = $cfgRoot.SelectSingleNode('runtime')
if (-not $rt) {
$rt = $xml.CreateElement('runtime')
$cfgRoot.AppendChild($rt) | Out-Null
}
$existing = $rt.SelectSingleNode("enforceFIPSPolicy[@enabled='false']")
if ($existing) {
Write-Host " [SKIP] already patched: $($cfg.FullName)"
$skipped++
continue
}
# Remove any other enforceFIPSPolicy variants (enabled=true or no attr).
$rt.SelectNodes('enforceFIPSPolicy') | ForEach-Object { $rt.RemoveChild($_) | Out-Null }
$fips = $xml.CreateElement('enforceFIPSPolicy')
$fips.SetAttribute('enabled', 'false')
$rt.AppendChild($fips) | Out-Null
if ($PSCmdlet.ShouldProcess($cfg.FullName, 'Inject enforceFIPSPolicy')) {
$xml.Save($cfg.FullName)
Write-Host " [OK] patched: $($cfg.FullName)"
$patched++
} else {
Write-Host " [WHATIF] would patch: $($cfg.FullName)"
}
} catch {
Write-Warning " [ERR] $($cfg.FullName): $_"
$errors++
}
}
}
Write-Host ""
Write-Host "Summary: patched=$patched skipped=$skipped errors=$errors"
Write-Host ""
Write-Host "Restart Keyence apps (Profilometer3.exe, VRAnalyzer*.exe, VRLauncher.exe,"
Write-Host "VRInspection_*.exe, etc.) for the new config to take effect. No reboot needed."