Wax/Trace triad: switch to SHA256 hashes (FIPS-compliant) + separate hash-failure path from copy-failure path

Backup-FormtracepakSettings observed 17 Errors on a real shopfloor PC
(G5PRTW04ESF / WJF00159 capture) - all of the form:
  WARNING: Failed to copy ...App.ini: Exception calling ".ctor" with "0"
  argument(s): "This implementation is not part of the Windows Platform
  FIPS validated cryptographic algorithms."

Cause: Windows FIPS policy is enabled on West Jefferson shopfloor PCs.
The per-file Get-FileHash -Algorithm MD5 call throws a hard .NET exception
that bypasses -ErrorAction SilentlyContinue (the throw is from the MD5
constructor, not the cmdlet's parameter binder). That exception was caught
by the broad try/catch around both Copy-Item + manifest add, producing a
misleading "Failed to copy" message even though Copy-Item already succeeded.
Net effect: files copied fine, but manifest rows were missing for those
files (Install would fall back to its bulk-copy path).

Two fixes:
- Switch the hash algorithm from MD5 to SHA256 in both Backup (manifest
  row capture) and Install (Restore-FileItem hash-skip compare). SHA256
  is Get-FileHash's default and is FIPS-compliant. Old MD5-hashed backups
  remain restorable because Install computes hashes fresh from disk at
  restore time and does not read the Hash column from file_manifest.csv.
- Split the broad try/catch in Backup's Copy-ToStaging into two
  try/catches: the first wraps only Copy-Item (real copy failure -> Errors
  counter + skip the file), the second wraps only Get-FileHash (hash
  failure -> log warning, manifest row gets a null Hash and is still
  recorded). A hash failure no longer pretends the copy failed.
- Install's hash compare is wrapped in try/catch too so a hash exception
  falls through to overwrite-mode rather than crashing the restore.

Smoke tested on win11 VM: SHA256 round-trip works (64-char hashes in
file_manifest.csv), Backup reports 0 Errors, Install hash-skip path
correctly skips Identical files on second-run idempotency check.
This commit is contained in:
cproudlock
2026-05-24 09:23:53 -04:00
parent fce6680c6f
commit 821e3179d1
2 changed files with 35 additions and 11 deletions

View File

@@ -221,18 +221,32 @@ function Copy-ToStaging {
}
Copy-Item -Path $f.FullName -Destination $destFull -Force
$counters[$CounterKey]++
$manifestRows.Add([PSCustomObject]@{
RelativePath = $destRelPath
OriginalPath = $f.FullName
SizeBytes = $f.Length
LastModified = $f.LastWriteTime.ToString('o')
Hash = (Get-FileHash $f.FullName -Algorithm MD5 -ErrorAction SilentlyContinue).Hash
})
} catch {
Write-Warning " Failed to copy $($f.FullName): $_"
$counters.Errors++
continue
}
# Hash computation runs in its own try so a hash failure (e.g. FIPS
# policy banning MD5, missing file, permission) does NOT roll back
# the copy we just succeeded at. SHA256 is the default and is
# FIPS-compliant; older Backup runs may have written MD5 hashes
# into file_manifest.csv but Install never reads those - it computes
# fresh hashes at restore time for the [SKIP] Identical check.
$hash = $null
try {
$hash = (Get-FileHash -LiteralPath $f.FullName -Algorithm SHA256 -ErrorAction Stop).Hash
} catch {
Write-Warning " Hash skipped for $($f.FullName): $_"
}
$manifestRows.Add([PSCustomObject]@{
RelativePath = $destRelPath
OriginalPath = $f.FullName
SizeBytes = $f.Length
LastModified = $f.LastWriteTime.ToString('o')
Hash = $hash
})
}
}

View File

@@ -212,9 +212,19 @@ function Restore-FileItem {
}
if ((Test-Path $DestFile) -and -not $Force) {
$srcHash = (Get-FileHash $SourceFile -Algorithm MD5).Hash
$destHash = (Get-FileHash $DestFile -Algorithm MD5).Hash
if ($srcHash -eq $destHash) {
# SHA256 not MD5: MD5 throws under Windows FIPS policy
# ("This implementation is not part of the Windows Platform FIPS
# validated cryptographic algorithms"). SHA256 is FIPS-compliant
# and is Get-FileHash's default.
$srcHash = $null
$destHash = $null
try {
$srcHash = (Get-FileHash -LiteralPath $SourceFile -Algorithm SHA256 -ErrorAction Stop).Hash
$destHash = (Get-FileHash -LiteralPath $DestFile -Algorithm SHA256 -ErrorAction Stop).Hash
} catch {
Write-Warning " Hash compare failed for ${DestFile}: $_ - falling through to overwrite"
}
if ($srcHash -and $destHash -and $srcHash -eq $destHash) {
Write-Host " [SKIP] Identical: $DestFile" -ForegroundColor DarkGray
$counters.Skipped++
return