Invoke_KeyCipher.psm1

function Invoke-KeyCipher(){
<#
.SYNOPSIS
    Invoke-KeyCipher [mode (encrypt | derypt)] [key secret] [inFilePath | string_stream] [outFilePath path_to_save]
.DESCRIPTION
    Encrypt / Decrypt files
.FUNCTIONALITY
    Invoke KeyCipher enciphering / deciphering python script
.EXAMPLE
    Invoke-KeyCipher decrypt pa55w0rd .\input\File.ext .\out\File.ext
 
    PS > Invoke-KeyCipher encrypt pa55w0rd .\input\File.ext
 
    PS > Invoke-KeyCipher encrypt my_Secret_key 5ecret@pa55w0rd
 
    PS > $(ls "C:\Users\ERIC\Desktop\WindowsUpdate.log") | Invoke-KeyCipher -mode encrypt -key ricoTush -lineBufferSize 9
 
    PS > $(ls "C:\Users\ERIC\Desktop\WindowsUpdate.log") | Invoke-KeyCipher -mode encrypt -key ricoTush -fullEncryption $true
.NOTES
    Author: Eric Mutua
    Date: 22.04.2019
    Version: 0.1.0
#>


    [CmdLetBinding()]
    Param(
        [parameter(position = 1, mandatory = $true)]
        [String] $mode,

        [parameter(position = 2, mandatory = $true)]
        [String] $key,

        [parameter(position = 3, 
                   mandatory = $true, 
                   ValueFromPipelineByPropertyName = $true
        )]
        [Alias('FullName')]
        [String] $inFilePath,

        [parameter(
            position = 4, 
            mandatory = $false, 
            ValueFromPipelineByPropertyName = $true
        )]
        [Alias('DirectoryName')]
        [String] 
        $outFilePath = $(     
                             # Checking mode parameter
                             if($($mode -eq "hash") -or $($mode -eq "unhash")){
                                 return '-'
                             }
                             else
                             { 
                             # The Default Output path
                             $defaultOutPath = $([string]$(Join-Path $env:PUBLIC $("\Documents\"+$(@($inFilePath.split('\'))[@($inFilePath.split('\')).count - 1])).split('.')[0]));
                            
                             if(-not $(Test-Path $defaultOutPath)){
                                mkdir $defaultOutPath
                             }

                             return $([System.String]$defaultOutPath)
                            }
                        ),

        [parameter(position = 5, mandatory = $false)]
        [int] $lineBufferSize = 10,

        [parameter(position = 6, mandatory = $false)]
        [switch] $fullEncription = $false
    )

    begin {

        $ErrorActionPreference = "Silently Continue"
        
        $isCertUtil = $(Test-Path $env:windir\System32\certutil.exe)

        Set-Alias certutil $(Join-path $env:windir "\System32\certutil.exe")

        $inputFileName = $(@($inFilePath.split('\'))[@($inFilePath.split('\')).count - 1])
        $inFileExt = $($inputFileName.split('.')[$inputFileName.Split('.').Length -1])

        # Temporary Directory
        if($outFilePath -ne '-'){
            if(-not $(Test-Path $env:TEMP\$($inputFileName.split('.')[0]))){
                mkdir $env:TEMP\$($inputFileName.split('.')[0]) | Out-Null
            }
        }
         
        $ErrorActionPreference = "SilentlyContinue"
        $base64EncodeFilePath = $(Join-path $env:TEMP\$($inputFileName.split('.')[0]) "Encoded.bs64enc") 
        $bsDecodeName = $inputFileName.replace($inFileExt, $($("dec64.")+$inFileExt))
        $base64DecodeFilePath = $(Join-path $outFilePath $bsDecodeName)

        $pt_ext = $('enc-')+$([string]$lineBufferSize)

        $possibleEncipherPath = $(Join-path $env:TEMP\$($inputFileName.split('.')[0]) $inputFileName.replace($inFileExt, $($($pt_ext+'.')+$inFileExt)))
        

        if(Test-Path $possibleEncipherPath){
            $encipherFilePath = $(Join-path $env:TEMP\$($inputFileName.split('.')[0]) $inputFileName.replace($inFileExt, $($($pt_ext+'.')+$inFileExt))) 
        }
        else 
        {
            $encipherFilePath = $(Join-path $env:TEMP\$($inputFileName.split('.')[0]) $inputFileName.replace($inFileExt, $('enc.')+$inFileExt)) 
        }
        
        $decipherFilePath = $(Join-path $env:TEMP\$($inputFileName.split('.')[0]) "Deciphered.dec")     
        $isPython = $($($env:PATH | Select-String 'python27').Matches.Success)
        $LogPath = ""
        $version = $([string]$(Find-Module Invoke_KeyCipher).Version)

        # Setting the module installation path

        if(Test-Path $(Join-Path $PSHOME\Modules Invoke_KeyCipher)){
            $moduleInstallationPath = $(Join-Path $PSHOME\Modules Invoke_KeyCipher)
        }

        if(Test-Path $(Join-Path ${env:ProgramFiles(x86)}\WindowsPowershell\Modules Invoke_KeyCipher)){
            $moduleInstallationPath = $(Join-Path ${env:ProgramFiles(x86)}\WindowsPowershell\Modules Invoke_KeyCipher\$version\)
        }
         

    }

    process
    {
            # Check if $base64EncodeFilePath is complete to take care of pipeline values
            # For pipeline values only
            # Begin Section for pipelined input parameters
            if(-not $base64EncodeFilePath.Contains($(@($inFilePath.split('\'))[@($inFilePath.split('\')).count - 1]))){
                $ErrorActionPreference = "Silently Continue"
                $isCertUtil = $(Test-Path $env:windir\System32\certutil.exe)
                Set-Alias certutil $(Join-path $env:windir "\System32\certutil.exe")

                $iFileName = $(@($inFilePath.split('\'))[@($inFilePath.split('\')).count - 1])
                
                # Make the temp directory
                if($outFilePath -ne '-'){
                    if(-not $(Test-Path $env:TEMP\$($iFileName.split('.')[0]))){
                        mkdir $env:TEMP\$($iFileName.split('.')[0]) | Out-Null
                    }
                }
                
                $p_ext = $('enc-')+$([string]$lineBufferSize)
                $iFileExt = $($iFileName.split('.')[$iFileName.Split('.').Length - 1])
                $bsDecodeName = $iFileName.replace($iFileExt, $($("dec64.")+$iFileExt))
                
                # Set paths
                $base64EncodeFilePath = $(Join-path $env:TEMP\$($iFileName.split('.')[0]) "Encoded.bs64enc")
                $psbEncipherPath = $(Join-path $env:TEMP\$($iFileName.split('.')[0]) $iFileName.replace($iFileExt, $($($p_ext+'.')+$iFileExt)))
                
                if(Test-Path $psbEncipherPath){
                    $encipherFilePath = $(Join-path $env:TEMP\$($iFileName.split('.')[0]) $iFileName.replace($iFileExt, $($($p_ext+'.')+$iFileExt))) 
                }
                else 
                {
                    $encipherFilePath = $(Join-path $env:TEMP\$($iFileName.split('.')[0]) $iFileName.replace($iFileExt, $('enc.')+$iFileExt)) 
                }

                $decipherFilePath = $(Join-path $env:TEMP\$($iFileName.split('.')[0]) "Deciphered.dec")
                $base64DecodeFilePath = $(Join-path $outFilePath $bsDecodeName)
                $isPython = $($($env:PATH | Select-String 'python').Matches.Success)
                $LogPath = ""
                $isEncipherFilePartial = $($encipherFilePath.Split('enc')[1] -eq $($p_ext.Split('enc')[1]+'.'+$iFileExt))
                $inputFileName = $iFileName;
                $pt_ext = $p_ext;
                $inFileExt = $iFileExt;
            }

            # End Begin Section for pipelined values

        if( -not $(Test-Path $(Join-path $env:TEMP "\KeyCipher"))){
            mkdir  $(Join-path $env:TEMP "\KeyCipher") | Out-Null
        }

        # Instantiate Log file
        $LogPath = $(Join-path $env:TEMP "\KeyCipher\KeyCipher.log")

        switch ($mode) {
            $("encrypt")
            {              
                # Encrypting Files
                Write-Host "[+] Beginning File Encryption ..." -ForegroundColor Green

                base64Encode($isCertUtil, $inFilePath, $LogPath)
                encipherFile($base64EncodeFilePath, $encipherFilePath, $key, $LogPath, $isPython, $pt_ext)
                
                Write-Host "[+] Done Encrypting" -ForegroundColor Green
                
            }
            $("decrypt")
             { 
                # Decrypting File
                Write-Host "[+] Beginning File Decryption ..." -ForegroundColor Cyan

                decipherFile($decipherFilePath, $encipherFilePath, $key, $LogPath, $isPython, $pt_ext, $inFileExt, $isEncipherFilePartial) 
                $decryptionStatus = base64Decode($decipherFilePath, $base64DecodeFilePath, $LogPath)

                if($($decryptionStatus -ne 'Nul') -or $($decryptionStatus) -ne ''){
                    Write-Host $($("[+] ")+$decryptionStatus) -ForegroundColor Magenta
                }
                else
                {
                    Write-Host "[+] Done Decrypting" -ForegroundColor Magenta
                }
             }
             $("hash")
             {
                    # Password hashing
                    Write-Host "[+] Beginning Password hashing ..." -ForegroundColor Green

                    $keyCipherStream = $(Join-Path $moduleInstallationPath 'KeyCipher_stream_encrypter.py')

                    if($isPython -and $(Test-Path $keyCipherStream)){
                        if($inFilePath.Contains('/') -or $inFilePath.Contains('\')){
                            Write-Host "[!] Warning: The string you are trying to encrypt could be a path" -ForegroundColor Yellow 
                        }
    
                        $passHash = $(python $keyCipherStream --encrypt $key $inFilePath -m)
                        Set-Content -Path $(Join-Path $env:TEMP passHash) -Value $passHash
                            
                        return "[Hash] "+$passHash+"`n[+] Done" 
                    }
             }
             $("unhash")
             {
                #Password unhashing
                Write-Host "[+] Begninnig Password unhashing ..." -ForegroundColor Cyan
    
                    $keyCipherStream = $(Join-Path $moduleInstallationPath 'KeyCipher_stream_encrypter.py')
                    if($isPython -and $(Test-Path $keyCipherStream)){
                        $unhashedPass = $(Get-Content -Path $(Join-Path $env:TEMP passHash))
                        if(Test-Path $(Join-Path $env:TEMP passHash)){Remove-Item $(Join-Path $env:TEMP passHash)}
                        return "[Password] "+$(python $keyCipherStream --decrypt $key $unhashedPass -m)+"`n[+] Done"
                        
                    }    
             }        
             
            Default {Write-Host "[!] Please Refer to the help for appropriate mode" -ForegroundColor Yellow}
        }

    }

    end
    {
        
    }
}

# Encode function
function base64Encode(){

    
    if($isCertUtil)
    {    

        certutil -encode $inFilePath $base64EncodeFilePath 
                
    }
    else
    {
        "["+$(Get-Date)+"][base64Encode] :: ERROR :: File Not Found (certutil.exe).`n" >> $LogPath

        # Alternative for certutil.exe
        Out-File -InputObject $(python $(Join-Path $moduleInstallationPath '/base64.py') -e $inFilePath) -Path $base64EncodeFilePath
    }
}

# Decode function

function base64Decode()
{
    $isdecipherFilePath = $(Test-path $decipherFilePath)

    if($isCertUtil)
    {
        if($isdecipherFilePath)
        {
            certutil -decode $decipherFilePath $base64DecodeFilePath | Out-Null

            if($? -ne $true){

                "["+$(Get-Date)+"][base64Decode] :: ERROR :: Incorrect Key attempted decryption.`n" >> $LogPath

                return "Wrong Key! You are not authorised to Decrypt File"
            }
            else{
               
                Write-host "[+] Removing temporary files..." -ForegroundColor Gray

                if(Test-Path $(Join-path $outFilePath $inputFileName.replace($inFileExt, $($($pt_ext+'.')+$inFileExt)))){Remove-Item $(Join-path $outFilePath $inputFileName.replace($inFileExt, $($($pt_ext+'.')+$inFileExt)))}
                if(Test-Path $(Join-path $outFilePath $inputFileName.replace($inFileExt, $('enc.')+$inFileExt))){Remove-Item $(Join-path $outFilePath $inputFileName.replace($inFileExt, $('enc.')+$inFileExt))}
                if(Test-Path $env:TEMP\$($inputFileName.split('.')[0])){Remove-Item -Recurse $env:TEMP\$($inputFileName.split('.')[0]) }
                    
                return "Done"
            }

        }
        else
        {
            "["+$(Get-Date)+"][base64Decode] :: ERROR :: File Not Found ("+ $decipherFilePath +").`n" >> $LogPath
            
            return 'Nul'
        }
    }
    else
    {
        "["+$(Get-Date)+"][base64Decode] :: ERROR :: File Not Found (certutil.exe).`n" >> $LogPath

        # Alternative for certutil.exe
        Out-File -InputObject $(python $(Join-Path $moduleInstallationPath '/base64.py') -d $decipherFilePath) -Path $base64DecodeFilePath
            
        if(Test-Path $base64DecodeFilePath){Copy-Item $base64DecodeFilePath $outFilePath}
        if(Test-Path $(Join-path $outFilePath $inputFileName.replace($inFileExt, $($($pt_ext+'.')+$inFileExt)))){Remove-Item $(Join-path $outFilePath $inputFileName.replace($inFileExt, $($($pt_ext+'.')+$inFileExt)))}
        if(Test-Path $(Join-path $outFilePath $inputFileName.replace($inFileExt, $('enc.')+$inFileExt))){Remove-Item $(Join-path $outFilePath $inputFileName.replace($inFileExt, $('enc.')+$inFileExt))}
        if(Test-Path $env:TEMP\$($inputFileName.split('.')[0])){Remove-Item -Recurse $env:TEMP\$($inputFileName.split('.')[0]) }
    
        return "Done"
    }
}

function encipherFile()
{

    $isBase64EncodeFilePath = $(Test-Path $base64EncodeFilePath) 

    if($isBase64EncodeFilePath){

        if($isPython)
        {    
            Write-host "[+] File Enciphering ..." -ForegroundColor Gray
            
            $base64EncBuffer = $(Get-Content $base64EncodeFilePath)
            $sizeBase64Buffer = $base64EncBuffer.Count
            
            # Partial File Encryption
            if ($($sizeBase64Buffer -gt $lineBufferSize) -and $(-not $fullEncription)){

                $innerLineBuffer = $($base64EncBuffer | Select-Object -First $lineBufferSize)
                $_divisor = [int]$innerLineBuffer.Count / 10
                forEach($innerLine in $innerLineBuffer){
                    
                    $actual_pcnt = [int]$innerLineBuffer.indexOf($innerLine) / $_divisor
                    
                    # Show Progress
                    Show-ProgressBar($actual_pcnt, "Encrypting ")
            
                    $enc_line = $(python $(Join-Path $moduleInstallationPath 'KeyCipher_stream_encrypter.py') --encrypt $key $innerLine -m)

                    Out-File -FilePath $($encipherFilePath.Replace('enc', $pt_ext)) -Append  -InputObject $enc_line -Encoding string
                } 
                
                # Append the unecrypted text
                $unencryptedLines = $($base64EncBuffer | Select-Object -Last $($sizeBase64Buffer - $lineBufferSize))
                
                Out-File -FilePath $($encipherFilePath.Replace('enc', $pt_ext)) -Append  -InputObject $unencryptedLines -Encoding string
                
                Write-host "[+] Verifying and Saving Encrypted File..." -ForegroundColor Gray
                if(Test-Path $base64EncodeFilePath){Remove-Item $base64EncodeFilePath}
                if(Test-Path $($encipherFilePath.Replace('enc', $pt_ext))){Copy-Item $($encipherFilePath.Replace('enc', $pt_ext)) $outFilePath}
        
            }
            else 
            {
                # Full file Encryption
                $divisor = [int]$sizeBase64Buffer / 10

                forEach($line in $base64EncBuffer)
                {
                    
                    $actual_pcnt = [int]$base64EncBuffer.indexOf($line) / $divisor

                    # Show Progress
                    Show-ProgressBar($actual_pcnt, "Encrypting ")
                                                
                    # Encryption is done line by line
                    $enc_line = $(python $(Join-Path $moduleInstallationPath 'KeyCipher_stream_encrypter.py') --encrypt $key $line -m)

                    Out-File -FilePath $encipherFilePath -Append  -InputObject $enc_line -Encoding string

                }

                Write-host "[+] Verifying and Saving Encrypted File..." -ForegroundColor Gray
                if(Test-Path $base64EncodeFilePath){Remove-Item $base64EncodeFilePath}
                #Debug
                Write-Host $encipherFilePath, $outFilePath
                if(Test-Path $encipherFilePath){Copy-Item $encipherFilePath $outFilePath}
            }
        }
        else
        {
            "["+$(Get-Date)+"][encipherFile] :: ERROR :: keymode not set or Python not found.`n" >> $LogPath
        }

    }
    else
    {
        "["+$(Get-Date)+"][encipherFile] :: ERROR :: File Not Found ("+ $base64EncodeFilePath +").`n" >> $LogPath
    }
}

function decipherFile(){

    $isEncipherFilePath = $(Test-Path $encipherFilePath)
        
    if($isEncipherFilePath){

        if($isPython)
        {    
            Write-host "[+] File Deciphering ..." -ForegroundColor Gray
            
            $encipherFileBuffer = $(Get-Content $encipherFilePath)
            $sizeEncipherBuffer = $encipherFileBuffer.Count
            
            
            if(-not $(Get-Variable -Name isEncipherFilePartial).IsValidValue($isEncipherFilePartial)){
                $isEncipherFilePartial = $($encipherFilePath.Split('enc')[1] -eq $($pt_ext.Split('enc')[1]+'.'+$inFileExt))
            }
            
            # For partial File Decryption
            if($isEncipherFilePartial){

                $partialEncFileBuffer = $($encipherFileBuffer | Select-Object -First $lineBufferSize)

                $dvs = [int]$partialEncFileBuffer.Count / 10

                forEach($line in $partialEncFileBuffer){
                    $actual_pcnt = [int]$partialEncFileBuffer.indexOf($line) / $dvs

                    # Show Progress
                    Show-ProgressBar($actual_pcnt, "Decrypting ")
                    $dec_line = $(python $(Join-Path $moduleInstallationPath 'KeyCipher_stream_encrypter.py') --decrypt $key $line -m)  
                                               
                    Out-File -FilePath $decipherFilePath -Append  -InputObject $dec_line -Encoding string

                }

                $base64FileBufferRem = $($encipherFileBuffer | Select-Object -Last $($sizeEncipherBuffer - $lineBufferSize))
                Out-File -FilePath $decipherFilePath -Append  -InputObject $base64FileBufferRem -Encoding string

                # Clean up
                Write-host "[+] Removing temporary files..." -ForegroundColor Gray
                if(Test-Path $base64DecodeFilePath){Remove-Item $base64DecodeFilePath}
            }
            else
            {

            $divisor = [int]$sizeEncipherBuffer / 10

            # For full File Decryption
            forEach($line in $encipherFileBuffer)
            {
                $actual_pcnt = [int]$encipherFileBuffer.indexOf($line) / $divisor
                
                # Show Progress
                Show-ProgressBar($actual_pcnt, "Decrypting ")
                $dec_line = $(python $(Join-Path $moduleInstallationPath 'KeyCipher_stream_encrypter.py') --decrypt $key $line -m)  
                                               
                Out-File -FilePath $decipherFilePath -Append  -InputObject $dec_line -Encoding string

            }
            
            # Clean up
            Write-host "[+] Removing temporary files..." -ForegroundColor Gray
            if(Test-Path $base64DecodeFilePath){Remove-Item $base64DecodeFilePath}
            }
        }
        else
        {                         
            "["+$(Get-Date)+"][decipherFile] :: ERROR :: keymode not set or Python not found.`n" >> $LogPath
        }
    }
    else
    {
        "["+$(Get-Date)+"][decipherFile]:: ERROR :: File Not Found ("+ $encipherFilePath +").`n" >> $LogPath
    }

}

function Show-ProgressBar($params){
    
    $act_pcnt = $params[0]
    $action = $params[1]

    $show_pcnt = [int]$($($act_pcnt) * 10)

    Write-Progress -Activity $($action+$([string]$inputFileName))`
                   -Status $([string]$show_pcnt+"% Complete")`
                   -PercentComplete $($act_pcnt * 10)
}