public/Unprotect-GzBlob.ps1


function Unprotect-GzBlob() {
    [CmdletBinding()]
    Param(
        [Parameter(Position = 0, ValueFromPipeline = $true)]
        [Byte[]] $EncryptedBlob,

        [Parameter(ParameterSetName = "PrivateKey")]
        [Byte[]] $PrivateKey,

        [Parameter(ParameterSetName = "Password")]
        [SecureString] $Password,

        [Parameter(ParameterSetName = "Keys")]
        [Byte[]] $Key,

        [Parameter(ParameterSetName = "Keys")]
        [Byte[]] $AuthenticationKey,

        [Byte[]] $InsecureInfo
    )

    PROCESS {
        $Headers = $InsecureInfo
        if(!$Headers) {
            $Headers = New-Object 'Byte[]' -ArgumentList 0
        }
    
        $options = Get-GzProtectDataOption
    
        $saltLength = 0
        $AuthenticationSaltLength = 0
        if($Password -or $PrivateKey) {
            if($Password) {
                $PrivateKey = ConvertTo-UnprotectedBytes $Password
            } 
        
    
            $saltLength = $AuthenticationSaltLength = ($options.SaltSize / 8)
            $salt = New-Object 'Byte[]' -ArgumentList $saltLength
            $AuthenticationSalt = New-Object 'Byte[]' -ArgumentList $saltLength
    
            [Array]::Copy($EncryptedBlob, $Headers.Length, $salt, 0, $salt.Length)
            [Array]::Copy($EncryptedBlob, $Headers.Length + $salt.Length, $AuthenticationSalt, 0, $AuthenticationSalt.Length)
    
            $generator = New-Object System.Security.Cryptography.Rfc2898DeriveBytes `
                -ArgumentList $PrivateKey, ($salt), ($options.Iterations)
    
            $Key = $generator.GetBytes($options.KeySize / 8)
            $generator.Dispose()
    
            $generator = New-Object System.Security.Cryptography.Rfc2898DeriveBytes `
                -ArgumentList $PrivateKey, ($AuthenticationSalt), ($options.Iterations)
    
            $AuthenticationKey = $generator.GetBytes($options.KeySize / 8)
    
            $generator.Dispose()
            [Array]::Clear($salt, 0, $salt.Length)
        }
    
        if(!$Key -or $Key.Length -eq 0) {
            Throw [ArgumentException] "Key must not be null or empty"
        }
    
        if(!$AuthenticationKey -or $AuthenticationKey.Length -eq 0) {
            Throw [ArgumentException] "AuthenticationKey must not be null or empty"
        }
    
    
        $hmac = New-Object System.Security.Cryptography.HMACSHA256
        $hmac.Key = $AuthenticationKey
        $hash = New-Object 'Byte[]' -ArgumentList ($hmac.HashSize / 8)
    
        $chunk = $EncryptedBlob.Length - $hash.Length
        $message = New-Object 'Byte[]' -ArgumentList ($chunk)
    
      
        [Array]::Copy($EncryptedBlob, 0, $message, 0, $chunk)
        
        $computedHash = $hmac.ComputeHash($message)
        $ivSize = $options.BlockSize / 8
    
        if($EncryptedBlob.Length -lt ($hash.Length + $Headers.Length + $ivSize)) {
            return $null;
        }
    
        [Array]::Copy($EncryptedBlob, $EncryptedBlob.Length - $hash.Length, $hash, 0, $hash.Length)
    
      
        $compareResult = 0;
        for($i = 0; $i -lt $hash.Length; $i++) {
            $compareResult = $compareResult -bor ($hash[$i] -bxor $computedHash[$i])
        }
        
        if($compareResult -ne 0) {
            return $null;
        }
       
        $aes = [System.Security.Cryptography.Aes]::Create()
        $aes.KeySize = $options.KeySize
        $aes.BlockSize = $options.BlockSize
        $aes.Mode = "CBC"
        $aes.Padding = "PKCS7"
    
        $iv = New-Object 'Byte[]' -ArgumentList $ivSize
        $headerLength = ($Headers.Length + $salt.Length + $AuthenticationSalt.Length)
        [Array]::Copy($EncryptedBlob, $headerLength, $iv, 0, $iv.Length)
        $headerLength += $iv.Length
    
        $decryptor = $aes.CreateDecryptor($key, $iv)
        $ms = New-Object System.IO.MemoryStream
        $cryptoStream = New-Object System.Security.Cryptography.CryptoStream($ms, $decryptor, "Write")
        $binaryWriter = New-Object System.IO.BinaryWriter($cryptoStream)
       
    
        try {
            $binaryWriter.Write($EncryptedBlob, $headerLength, $EncryptedBlob.Length - $headerLength - $hash.Length)
            $cryptoStream.FlushFinalBlock()
            
            $ms.Flush()
           
            return $ms.ToArray()
        } finally {
            if($Password -or $PrivateKey) {
                [Array]::Clear($key, 0, $key.Length)
                [Array]::Clear($AuthenticationKey, 0, $AuthenticationKey.Length)
            }
           
            [Array]::Clear($iv, 0, $iv.Length)
            $binaryWriter.Dispose()
            $cryptoStream.Dispose()
            $ms.Dispose()
             $decryptor.Dispose();
        }
    }
}