From 2a0b4885fe5294b6bbe3775f255bc958e42bf525 Mon Sep 17 00:00:00 2001 From: cproudlock Date: Thu, 21 May 2026 14:35:29 -0400 Subject: [PATCH] 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\\ with . 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 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) --- .../09-Setup-Keyence.ps1 | 35 ++++++ .../common/Patch-KeyenceFipsConfigs.ps1 | 114 ++++++++++++++++++ 2 files changed, 149 insertions(+) create mode 100644 playbook/shopfloor-setup/gea-shopfloor-keyence/common/Patch-KeyenceFipsConfigs.ps1 diff --git a/playbook/shopfloor-setup/gea-shopfloor-keyence/09-Setup-Keyence.ps1 b/playbook/shopfloor-setup/gea-shopfloor-keyence/09-Setup-Keyence.ps1 index 8bf93c6..a1c7c9d 100644 --- a/playbook/shopfloor-setup/gea-shopfloor-keyence/09-Setup-Keyence.ps1 +++ b/playbook/shopfloor-setup/gea-shopfloor-keyence/09-Setup-Keyence.ps1 @@ -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\\ with +# . +# +# 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) # ============================================================================ diff --git a/playbook/shopfloor-setup/gea-shopfloor-keyence/common/Patch-KeyenceFipsConfigs.ps1 b/playbook/shopfloor-setup/gea-shopfloor-keyence/common/Patch-KeyenceFipsConfigs.ps1 new file mode 100644 index 0000000..b9e8ccf --- /dev/null +++ b/playbook/shopfloor-setup/gea-shopfloor-keyence/common/Patch-KeyenceFipsConfigs.ps1 @@ -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: + + + + as a sibling under . Preserves any existing + 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 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."