Public/Test-HibpPwnedPassword.ps1

function Test-HibpPwnedPassword {
    <#
        .SYNOPSIS
            Checks if a password has been exposed in a data breach.

        .DESCRIPTION
            Checks if a password has been exposed in a data breach by querying the Pwned Passwords API using the k-Anonymity model.
            The function hashes the password with SHA-1, sends the first 5 characters of the hash to the API, and then checks the returned list of hash suffixes for a match.
            This function does NOT send your plain text password over the internet.

        .PARAMETER Password
            The password to check, as a SecureString because Microsoft doesn't like it when you publish to the Gallery with clear text.

        .EXAMPLE
            Test-HibpPwnedPassword -Password (Read-Host -AsSecureString)

            Prompts for a password securely and checks if it has been pwned.

        .EXAMPLE
            'password' | ConvertTo-SecureString -AsPlainText -Force | Test-HibpPwnedPassword

            Checks the password 'password' to see if it has been pwned.

        .LINK
            https://haveibeenpwned.com/API/v3#PwnedPasswords
    #>


    [CmdletBinding()]
    Param(
        [Parameter(Mandatory = $true, Position = 0, ValueFromPipeline)]
        [System.Security.SecureString]$Password
    )

    begin {
        $sha1 = [System.Security.Cryptography.SHA1]::Create()
    }

    process {
        $bstr = [System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($Password)
        $plainTextPassword = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto($bstr)
        [System.Runtime.InteropServices.Marshal]::ZeroFreeBSTR($bstr)

        $passwordBytes = [System.Text.Encoding]::UTF8.GetBytes($plainTextPassword)
        $hashBytes = $sha1.ComputeHash($passwordBytes)
        $hashString = ($hashBytes | ForEach-Object { $_.ToString('X2') }) -join ''

        $prefix = $hashString.Substring(0, 5)
        $suffix = $hashString.Substring(5)

        $uri = 'https://api.pwnedpasswords.com/range/{0}' -f $prefix
        $headers = @{
            'Add-Padding' = 'true'
        }

        try {
            $response = Invoke-RestMethod -Uri $uri -Method Get -Headers $headers -ErrorAction Stop

            $pwnedSuffixes = $response.Split([Environment]::NewLine) | ForEach-Object { $_.Split(':')[0] }

            if ($pwnedSuffixes -contains $suffix) {
                $true
            }
            else {
                $false
            }
        }
        catch {
            Write-Error "Failed to query the Pwned Passwords API: $_"
            Write-Output $false
        }
    }

    end {
        $sha1.Dispose()
    }
}