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
    File Encryption/Decryption Examples
 
    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 > Invoke-KeyCipher -mode encrypt -key ricoTush /my/file/in/directory -lineBufferSize 80 # This saves to default path which is %HOMEDRIVE%\USERS\PUBLIC\DOCUMENTS
 
    Password Hashing Examples
 
    PS > Invoke-KeyCipher hash secret mypassword
 
    PS > Invoke-KeyCipher unhash secret mypassword
 
    Encrypting Multiple Files
 
    PS > $files = $(ls C:\Users\ERIC\Downloads\Video\Strike* | ? {$($_.length / 1mb) -lt 200} | %{ $_.FullName })
     
    PS > $files | %{Write-Progress -Activity "Encrypting.." -Status $_.Split('\')[$_.Split('\').Length - 1] ; Invoke-KeyCipher encrypt mysecret $_ -lineBufferSize 10 }
 
    Decrypting Multiple Files
 
    PS > $files | %{Write-Progress -Activity "Decrypting.." -Status $_.Split('\')[$_.Split('\').Length - 1] ; Invoke-KeyCipher decrypt mysecret $_ -lineBufferSize 10 -retainAllFiles | Out-Null}
 
.INPUTS
    [System.String] mode (encrypt | decrypt | hash | unhash)
    [System.String] key (secret)
    [System.String] inputFilePath (location of file *fullpathName)
    [System.String] outFilePath (location of save dir *only directory)
    [Integer] lineBufferSize (for *partial encryption only)
    [Switch] fullEncryption (for *full Encryption)
    [Switch] retainAllFiles (used when *encrypting/decrypting multiple files)
 
.NOTES
 
    The Module is only capable of encrypting and decrypting files less than 500 Mb at a reasonable time of approximately 5 - 10 minutes.
    It is advisable to use the -lineBufferSize flag for large files of more than 10 mb and -fullEncryption for files less than 10 mb.
     
    For this version (v 0.1.3) the default encode / decode utility is base64.exe which is more capable compared to certutil.exe which was used in previous versions.
 
    Author: Eric Mutua
    Date: 22.04.2020
    Version: 0.1.3
#>


    [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 = $(
                        [string]$(Join-Path $env:PUBLIC $("\Documents\"+$(@($inFilePath.split('\'))[@($inFilePath.split('\')).count - 1])).split('.')[0])
                        ),

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

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

    begin {

        $ErrorActionPreference = "Silently Continue"

        # Setting OutFilePath param
        $defaultOutPath = $([string]$(Join-Path $env:PUBLIC $("\Documents\"+$(@($inFilePath.split('\'))[@($inFilePath.split('\')).count - 1])).split('.')[0]));
                             
        # Checking mode parameter
        if($($mode -eq "hash") -or $($mode -eq "unhash")){
            $outFilePath = '-'
        }
        else
        { 
            if($outFilePath -eq $defaultOutPath){
                if(-not $(Test-Path $defaultOutPath)){
                    mkdir $defaultOutPath | Out-Null
                }
            }    
                
        }
        

        $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
            }
        }
         
        # Instantiating other constants
        $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 = ""
        $isEncipherFilePartial = $($encipherFilePath.Split('enc')[1] -eq $($pt_ext.Split('enc')[1]+'.'+$inFileExt))
        $version = "0.1.3" 


        # 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\)
        }
         
        if(Test-Path $(Join-Path $env:ProgramFiles\WindowsPowerShell\Modules Invoke_KeyCipher)){
            $moduleInstallationPath = $(Join-Path $env:ProgramFiles\WindowsPowerShell\Modules Invoke_KeyCipher\$version\)
        }

        # Setting and testing the base64util path
        $isBase64Util = $(Test-Path $(Join-Path $moduleInstallationPath 'base64.exe'))
        Set-Alias base64 $(Join-Path $moduleInstallationPath 'base64.exe')
    }

    process
    {
        # Checking whether the Log File exist
        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($isBase64Util, $LogPath) #($isBase64Util, $inFilePath, $LogPath)
                encipherFile($base64EncodeFilePath, $LogPath) #($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, $isEncipherFilePartial) #($decipherFilePath, $encipherFilePath, $key, $LogPath, $isPython, $pt_ext, $inFileExt, $isEncipherFilePartial)
                $decryptionStatus = base64Decode($decipherFilePath, $base64DecodeFilePath) #($decipherFilePath, $base64DecodeFilePath, $LogPath)

                if($($decryptionStatus -ne 'Nul') -or $($decryptionStatus) -ne ''){
                    Write-Host $($("[+] ")+$decryptionStatus) -ForegroundColor Cyan
                }
                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($isBase64Util)
    {    

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

# Decode function

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

    if($isBase64Util)
    {
        if($isdecipherFilePath)
        {
            # Decoded as a line

             base64 decode $decipherFilePath $base64DecodeFilePath | Out-Null
             
            # Checking whether base64 was succesfull

            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 "[+] Verifying & Saving Decrypted file ..." -ForegroundColor Gray
                if(-not $retainAllFiles){
                    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 (base64.exe).`n" >> $LogPath

    }
}

function encipherFile()
{

    $isBase64EncodeFilePath = $(Test-Path $base64EncodeFilePath) 

    if($isBase64EncodeFilePath){

        if($isPython)
        {    
            Write-host "[+] File Enciphering ..." -ForegroundColor Gray
            
            $base64EncBuffer = $(Get-Content $base64EncodeFilePath)
            $sizeBs64Buffer = $($base64EncBuffer.Length / 1mb)
            $lineCountBs64Buffer = $base64EncBuffer.Count
            $charCountBs64Buffer = $($base64EncBuffer | Measure-Object -Character).Characters
            
            # Checking if input file is fit for full or partial encryption
            
            if ($(Get-Variable -Name fullEncryption).IsValidValue($fullEncryption)){
                $isPartialEncryption = $false
            } 
            if($(Get-Variable -Name lineBufferSize).IsValidValue($lineBufferSize) -and $($lineBufferSize -gt 1)){
                $isPartialEncryption = $true
            }
            
            # Partial File Encryption
            if ($isPartialEncryption -and ($sizeBs64Buffer -gt 1))
            {
                
                if ($lineCountBs64Buffer -eq 1){
                    # Encryption is done character by character
                    $line = $base64EncBuffer.Substring(0, $lineBufferSize)
                    
                    # Encrypt byte after byte
                    $enc_line = $(python $(Join-Path $moduleInstallationPath 'KeyCipher_stream_encrypter.py') --encrypt $key $line -m)

                    # Append the unecrypted text
                    $unencryptedLine = $base64EncBuffer.SubString($lineBufferSize, $([int]$($charCountBs64Buffer - $lineBufferSize)))

                    # Saving Everything to File
                    $($enc_line+$unencryptedLine) >  $($encipherFilePath.Replace('enc', $pt_ext));
                } 
                
            
                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
                
                if ($lineCountBs64Buffer -eq 1)
                {
                    $line = $base64EncBuffer;
                
                    # Encryption is done byte after byte
                    $enc_line = $(python $(Join-Path $moduleInstallationPath 'KeyCipher_stream_encrypter.py') --encrypt $key $line -m)
                    
                    # Saving Everything to File
                    $enc_line > $encipherFilePath
                    
                }

                Write-host "[+] Verifying and Saving Encrypted File..." -ForegroundColor Gray
            
                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)
            $charCountEncipherBuffer = $($encipherFileBuffer | Measure-Object -Character).Characters
            
            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){

                $line = $encipherFileBuffer.SubString(0, $lineBufferSize); 
                
                $dec_line = $(python $(Join-Path $moduleInstallationPath 'KeyCipher_stream_encrypter.py') --decrypt $key $line -m)  
                
                $unecryptedLine = $encipherFileBuffer.SubString($lineBufferSize, $([int]$($charCountEncipherBuffer - $lineBufferSize))) 

                # Saving Everything to File
                $($dec_line+$unecryptedLine) > $decipherFilePath
                
                # Clean up
                Write-host "[+] Removing temporary files..." -ForegroundColor Gray
                if(Test-Path $base64DecodeFilePath){Remove-Item $base64DecodeFilePath}
            }
            else
            {

            $line = $encipherFileBuffer;
            
            $dec_line = $(python $(Join-Path $moduleInstallationPath 'KeyCipher_stream_encrypter.py') --decrypt $key $line -m) 

            # Saving Everything to File
            $dec_line > $decipherFilePath                       
            
            # 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
    }

}