private/Invoke-WinPEDriverPackageDownload.ps1

#Requires -PSEdition Core

function Invoke-WinPEDriverPackageDownload {
    <#
    .SYNOPSIS
        Downloads and verifies a WinPE driver package file
 
    .DESCRIPTION
        Downloads a file with curl.exe, supports resumable transfers, and optionally verifies
        its SHA256 hash. Unless Force is specified, an existing destination is reused when no
        hash is supplied or when its hash matches. The function can also copy another file from
        the destination directory when that file has the expected hash.
 
        The function rejects empty responses, checksum mismatches, HTML responses, HTTP 202
        responses, and recognized web application firewall challenge pages. Failed or invalid
        downloads produce terminating errors and remove the destination and temporary response
        header file when applicable.
 
    .PARAMETER Uri
        Specifies the non-empty URI of the file to download.
 
    .PARAMETER DestinationPath
        Specifies the non-empty destination path, including the file name. The parent directory
        is created when it does not exist.
 
    .PARAMETER SearchUri
        Specifies an optional URI to send as the HTTP referer.
 
    .PARAMETER ExpectedSHA256
        Specifies the expected SHA256 hash. Whitespace is ignored and comparison is
        case-insensitive.
 
    .PARAMETER Force
        Downloads the file even when an existing destination or alternate cache file could be
        reused.
 
    .EXAMPLE
        PS> Invoke-WinPEDriverPackageDownload -Uri 'https://example.com/winpe-driver.cab' -DestinationPath 'C:\Drivers\winpe-driver.cab'
 
        Downloads the package unless the destination file already exists.
 
    .EXAMPLE
        PS> Invoke-WinPEDriverPackageDownload -Uri 'https://example.com/winpe-driver.cab' -DestinationPath 'C:\Drivers\winpe-driver.cab' -ExpectedSHA256 '0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF' -Force
 
        Downloads the package even when a matching cached file exists and verifies its hash.
 
    .INPUTS
        None. This function does not accept pipeline input.
 
    .OUTPUTS
        System.IO.FileInfo. Returns the downloaded, existing, or reused destination file.
 
    .NOTES
        Author: David Segura
        Company: Recast Software
        Version: 1.0.0
        Date: 2026-08-28
 
        Requires curl.exe, network access for uncached files, and a writable destination
        directory. This helper does not support WhatIf or Confirm.
    #>

    [CmdletBinding()]
    [OutputType([System.IO.FileInfo])]
    param (
        [Parameter(Mandatory)]
        [ValidateNotNullOrEmpty()]
        [string]$Uri,

        [Parameter(Mandatory)]
        [ValidateNotNullOrEmpty()]
        [string]$DestinationPath,

        [Parameter()]
        [string]$SearchUri,

        [Parameter()]
        [string]$ExpectedSHA256,

        [Parameter()]
        [switch]$Force
    )

    $downloadDir = Split-Path -Path $DestinationPath -Parent
    $destinationLeaf = Split-Path -Path $DestinationPath -Leaf
    $normalizedExpectedSHA256 = $null
    if (-not (Test-Path -Path $downloadDir)) {
        New-Item -ItemType Directory -Path $downloadDir -Force | Out-Null
    }

    if (-not [string]::IsNullOrWhiteSpace($ExpectedSHA256)) {
        $normalizedExpectedSHA256 = ($ExpectedSHA256 -replace '\s+', '').ToUpperInvariant()
    }

    $getVerifiedCachedFile = {
        param([string]$Path)

        if (-not (Test-Path -Path $Path -PathType Leaf)) {
            return $null
        }

        if (-not $normalizedExpectedSHA256) {
            return Get-Item -Path $Path
        }

        $actualHash = (Get-FileHash -Path $Path -Algorithm SHA256).Hash.ToUpperInvariant()
        if ($actualHash -eq $normalizedExpectedSHA256) {
            return Get-Item -Path $Path
        }

        return $null
    }

    # Skip if file already exists and hash matches
    if (-not $Force) {
        $verifiedCachedFile = & $getVerifiedCachedFile $DestinationPath
        if ($verifiedCachedFile) {
            if ($normalizedExpectedSHA256) {
                Write-Verbose "[$($MyInvocation.MyCommand.Name)] '$destinationLeaf' already downloaded and SHA256 verified. Skipping."
            }
            else {
                Write-Verbose "[$($MyInvocation.MyCommand.Name)] '$destinationLeaf' already exists (no checksum to verify). Skipping."
            }

            return $verifiedCachedFile
        }

        if ($normalizedExpectedSHA256) {
            $alternateCachedFile = Get-ChildItem -Path $downloadDir -File -ErrorAction SilentlyContinue |
                Where-Object { $_.FullName -ne $DestinationPath } |
                ForEach-Object {
                    $cachedFile = & $getVerifiedCachedFile $_.FullName
                    if ($cachedFile) {
                        $cachedFile
                    }
                } |
                Select-Object -First 1

            if ($alternateCachedFile) {
                Write-Verbose "[$($MyInvocation.MyCommand.Name)] Reusing cached file '$($alternateCachedFile.Name)' for '$destinationLeaf'."
                Copy-Item -Path $alternateCachedFile.FullName -Destination $DestinationPath -Force
                return Get-Item -Path $DestinationPath
            }

            if (Test-Path -Path $DestinationPath -PathType Leaf) {
                Write-Warning "[$(Get-Date -Format s)] SHA256 mismatch for '$destinationLeaf'. Re-downloading."
            }
        }
    }

    Write-HostDateTimeDarkGray -Message "Source: $Uri"
    Write-HostDateTimeDarkGray -Message "Output: $DestinationPath"
    Write-Verbose "[$($MyInvocation.MyCommand.Name)] Downloading '$(Split-Path $Uri -Leaf)'"

    $curlHeaderPath = Join-Path $downloadDir "$destinationLeaf.headers.tmp"
    if (Test-Path -Path $curlHeaderPath -PathType Leaf) {
        Remove-Item -Path $curlHeaderPath -Force -ErrorAction SilentlyContinue
    }

    $curlArgs = @(
        '--location', '--fail', '--retry', '5', '--continue-at', '-',
        '--user-agent', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/134.0.0.0 Safari/537.36'
    )
    if ($SearchUri) {
        $curlArgs += '--referer', $SearchUri
    }
    $curlArgs += '--dump-header', $curlHeaderPath
    $curlArgs += '--output', $DestinationPath, $Uri

    curl.exe @curlArgs
    if ($LASTEXITCODE -ne 0) {
        $PSCmdlet.ThrowTerminatingError(
            [System.Management.Automation.ErrorRecord]::new(
                [System.IO.IOException]::new("curl.exe failed (exit $LASTEXITCODE) downloading '$Uri'"),
                'DownloadFailed',
                [System.Management.Automation.ErrorCategory]::ConnectionError,
                $Uri
            )
        )
    }

    $downloadStatusCode = $null
    $downloadContentType = $null
    if (Test-Path -Path $curlHeaderPath -PathType Leaf) {
        $headerLines = Get-Content -Path $curlHeaderPath -ErrorAction SilentlyContinue
        $statusLine = $headerLines |
            Where-Object { $_ -match '^HTTP/\d(?:\.\d)?\s+(\d{3})' } |
            Select-Object -Last 1
        if ($statusLine -and $statusLine -match '^HTTP/\d(?:\.\d)?\s+(\d{3})') {
            $downloadStatusCode = [int]$Matches[1]
        }

        $contentTypeLine = $headerLines |
            Where-Object { $_ -match '^(?i)Content-Type\s*:' } |
            Select-Object -Last 1
        if ($contentTypeLine) {
            $downloadContentType = ($contentTypeLine -replace '^(?i)Content-Type\s*:\s*', '').Trim().ToLowerInvariant()
        }
    }

    $challengeMarkerDetected = $false
    if (Test-Path -Path $DestinationPath -PathType Leaf) {
        $challengeProbe = Get-Content -Path $DestinationPath -Raw -ErrorAction SilentlyContinue
        if ($challengeProbe -and (
                $challengeProbe -match 'AwsWafIntegration' -or
                $challengeProbe -match 'challenge\.js' -or
                $challengeProbe -match "verify that you're not a robot" -or
                $challengeProbe -match 'JavaScript is disabled'
            )) {
            $challengeMarkerDetected = $true
        }
    }

    if ($challengeMarkerDetected -or
        $downloadStatusCode -eq 202 -or
        ($downloadContentType -and $downloadContentType -like 'text/html*')) {
        Remove-Item -Path $DestinationPath -Force -ErrorAction SilentlyContinue
        Remove-Item -Path $curlHeaderPath -Force -ErrorAction SilentlyContinue
        $PSCmdlet.ThrowTerminatingError(
            [System.Management.Automation.ErrorRecord]::new(
                [System.IO.IOException]::new("Download blocked by Intel AWS WAF challenge for '$destinationLeaf'."),
                'WafChallenge',
                [System.Management.Automation.ErrorCategory]::ConnectionError,
                $Uri
            )
        )
    }

    if ((Get-Item -Path $DestinationPath -ErrorAction SilentlyContinue).Length -eq 0) {
        Remove-Item -Path $DestinationPath -Force -ErrorAction SilentlyContinue
        Remove-Item -Path $curlHeaderPath -Force -ErrorAction SilentlyContinue
        $PSCmdlet.ThrowTerminatingError(
            [System.Management.Automation.ErrorRecord]::new(
                [System.IO.IOException]::new("Downloaded file is empty from '$Uri'. The URL may have changed."),
                'EmptyDownload',
                [System.Management.Automation.ErrorCategory]::ConnectionError,
                $Uri
            )
        )
    }

    if ($normalizedExpectedSHA256) {
        $actualHash = (Get-FileHash -Path $DestinationPath -Algorithm SHA256).Hash.ToUpperInvariant()
        if ($actualHash -ne $normalizedExpectedSHA256) {
            Remove-Item -Path $DestinationPath -Force -ErrorAction SilentlyContinue
            Remove-Item -Path $curlHeaderPath -Force -ErrorAction SilentlyContinue
            $PSCmdlet.ThrowTerminatingError(
                [System.Management.Automation.ErrorRecord]::new(
                    [System.IO.IOException]::new("SHA256 mismatch for '$destinationLeaf'. Expected: $normalizedExpectedSHA256, Actual: $actualHash"),
                    'ChecksumMismatch',
                    [System.Management.Automation.ErrorCategory]::SecurityError,
                    $DestinationPath
                )
            )
        }
        Write-Verbose "[$($MyInvocation.MyCommand.Name)] SHA256 verified for '$destinationLeaf'"
    }

    Remove-Item -Path $curlHeaderPath -Force -ErrorAction SilentlyContinue

    Get-Item -Path $DestinationPath
}