PSPGP.psm1

# Get library name, from the PSM1 file name
$LibraryName = 'PSPGP'
$Library = "$LibraryName.dll"
$Class = "$LibraryName.Initialize"

$AssemblyFolders = Get-ChildItem -Path $PSScriptRoot\Lib -Directory -ErrorAction SilentlyContinue

# Lets find which libraries we need to load
$Default = $false
$Core = $false
$Standard = $false
foreach ($A in $AssemblyFolders.Name) {
    if ($A -eq 'Default') {
        $Default = $true
    } elseif ($A -eq 'Core') {
        $Core = $true
    } elseif ($A -eq 'Standard') {
        $Standard = $true
    }
}
if ($Standard -and $Core -and $Default) {
    $FrameworkNet = 'Default'
    $Framework = 'Standard'
} elseif ($Standard -and $Core) {
    $Framework = 'Standard'
    $FrameworkNet = 'Standard'
} elseif ($Core -and $Default) {
    $Framework = 'Core'
    $FrameworkNet = 'Default'
} elseif ($Standard -and $Default) {
    $Framework = 'Standard'
    $FrameworkNet = 'Default'
} elseif ($Standard) {
    $Framework = 'Standard'
    $FrameworkNet = 'Standard'
} elseif ($Core) {
    $Framework = 'Core'
    $FrameworkNet = ''
} elseif ($Default) {
    $Framework = ''
    $FrameworkNet = 'Default'
} else {
    Write-Error -Message 'No assemblies found'
}
if ($PSEdition -eq 'Core') {
    $LibFolder = $Framework
} else {
    $LibFolder = $FrameworkNet
}

try {
    $ImportModule = Get-Command -Name Import-Module -Module Microsoft.PowerShell.Core

    if (-not ($Class -as [type])) {
        & $ImportModule ([IO.Path]::Combine($PSScriptRoot, 'Lib', $LibFolder, $Library)) -ErrorAction Stop
    } else {
        $Type = "$Class" -as [Type]
        & $importModule -Force -Assembly ($Type.Assembly)
    }
} catch {
    if ($ErrorActionPreference -eq 'Stop') {
        throw
    } else {
        Write-Warning -Message "Importing module $Library failed. Fix errors before continuing. Error: $($_.Exception.Message)"
        # we will continue, but it's not a good idea to do so
        # return
    }
}
# Dot source all libraries by loading external file
. $PSScriptRoot\PSPGP.Libraries.ps1

function New-PGPKey {
    [cmdletBinding(DefaultParameterSetName = 'ClearText')]
    param(
        [parameter(Mandatory, ParameterSetName = 'Strength')]
        [parameter(Mandatory, ParameterSetName = 'StrengthCredential')]
        [parameter(Mandatory, ParameterSetName = 'ClearText')]
        [parameter(Mandatory, ParameterSetName = 'Credential')]
        [string] $FilePathPublic,
        [parameter(Mandatory, ParameterSetName = 'Strength')]
        [parameter(Mandatory, ParameterSetName = 'StrengthCredential')]
        [parameter(Mandatory, ParameterSetName = 'ClearText')]
        [parameter(Mandatory, ParameterSetName = 'Credential')]
        [string] $FilePathPrivate,
        [parameter(ParameterSetName = 'Strength')]
        [parameter(ParameterSetName = 'ClearText')]
        [string] $UserName,
        [parameter(ParameterSetName = 'Strength')]
        [parameter(ParameterSetName = 'ClearText')]
        [string] $Password,
        [parameter(Mandatory, ParameterSetName = 'StrengthCredential')]
        [parameter(Mandatory, ParameterSetName = 'Credential')]
        [pscredential] $Credential,
        [parameter(Mandatory, ParameterSetName = 'Strength')]
        [parameter(Mandatory, ParameterSetName = 'StrengthCredential')]
        [int] $Strength,
        [parameter(Mandatory, ParameterSetName = 'Strength')]
        [parameter(Mandatory, ParameterSetName = 'StrengthCredential')]
        [int] $Certainty,
        [parameter(ParameterSetName = 'Strength')]
        [parameter(ParameterSetName = 'StrengthCredential')]
        [switch] $EmitVersion,

        [alias('HashAlgorithmTag')][Org.BouncyCastle.Bcpg.HashAlgorithmTag] $HashAlgorithm,
        [Org.BouncyCastle.Bcpg.CompressionAlgorithmTag] $CompressionAlgorithm,
        [PgpCore.Enums.PGPFileType] $FileType,
        [Int32] $PgpSignatureType,
        [Org.BouncyCastle.Bcpg.PublicKeyAlgorithmTag] $PublicKeyAlgorithm,
        [Org.BouncyCastle.Bcpg.SymmetricKeyAlgorithmTag] $SymmetricKeyAlgorithm
    )
    try {
        $PGP = [PgpCore.PGP]::new()
    } catch {
        if ($PSBoundParameters.ErrorAction -eq 'Stop') {
            throw
        } else {
            Write-Warning -Message "New-PGPKey - Creating keys genarated erorr: $($_.Exception.Message)"
            return
        }
    }
    if ($Credential) {
        $UserName = $Credential.UserName
        $Password = $Credential.GetNetworkCredential().Password
    }

    if ($PSBoundParameters.ContainsKey('HashAlgorithm')) {
        $PGP.HashAlgorithmTag = $HashAlgorithm
    }
    if ($PSBoundParameters.ContainsKey('CompressionAlgorithm')) {
        $PGP.CompressionAlgorithm = $CompressionAlgorithm
    }
    if ($PSBoundParameters.ContainsKey('FileType')) {
        $PGP.FileType = $FileType
    }
    if ($PSBoundParameters.ContainsKey('PgpSignatureType')) {
        $PGP.PgpSignatureType = $PgpSignatureType
    }
    if ($PSBoundParameters.ContainsKey('PublicKeyAlgorithm')) {
        $PGP.PublicKeyAlgorithm = $PublicKeyAlgorithm
    }
    if ($PSBoundParameters.ContainsKey('SymmetricKeyAlgorithm')) {
        $PGP.SymmetricKeyAlgorithm = $SymmetricKeyAlgorithm
    }

    $ResolvedPublicKey = $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($FilePathPublic)
    $ResolvedPrivateKey = $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($FilePathPrivate)

    try {
        if ($Strength) {
            $PGP.GenerateKey($ResolvedPublicKey, $ResolvedPrivateKey, $UserName, $Password, $Strength, $Certainty, $EmitVersion.IsPresent)
        } else {
            $PGP.GenerateKey($ResolvedPublicKey, $ResolvedPrivateKey, $UserName, $Password)
        }
    } catch {
        if ($PSBoundParameters.ErrorAction -eq 'Stop') {
            throw
        } else {
            Write-Warning -Message "New-PGPKey - Creating keys genarated error: $($_.Exception.Message)"
            return
        }
    }

    #void GenerateKey(string publicKeyFilePath, string privateKeyFilePath, string username, string password, int strength, int certainty, bool emitVersion)
    #void GenerateKey(System.IO.Stream publicKeyStream, System.IO.Stream privateKeyStream, string username, string password, int strength, int certainty, bool armor, bool emitVersion)
}

function Protect-PGP {
    [cmdletBinding(DefaultParameterSetName = 'File')]
    param(
        [Parameter(Mandatory, ParameterSetName = 'Folder')]
        [Parameter(Mandatory, ParameterSetName = 'File')]
        [Parameter(Mandatory, ParameterSetName = 'String')]
        [string[]] $FilePathPublic,

        [Parameter(Mandatory, ParameterSetName = 'Folder')][string] $FolderPath,
        [Parameter(ParameterSetName = 'Folder')][string] $OutputFolderPath,

        [Parameter(Mandatory, ParameterSetName = 'File')][string] $FilePath,
        [Parameter(ParameterSetName = 'File')][string] $OutFilePath,

        [Parameter(Mandatory, ParameterSetName = 'String')][string] $String,

        [System.IO.FileInfo] $SignKey,
        [string] $SignPassword,
        [alias('HashAlgorithmTag')][Org.BouncyCastle.Bcpg.HashAlgorithmTag] $HashAlgorithm,
        [Org.BouncyCastle.Bcpg.CompressionAlgorithmTag] $CompressionAlgorithm,
        [PgpCore.Enums.PGPFileType] $FileType,
        [Int32] $PgpSignatureType,
        [Org.BouncyCastle.Bcpg.PublicKeyAlgorithmTag] $PublicKeyAlgorithm,
        [Org.BouncyCastle.Bcpg.SymmetricKeyAlgorithmTag] $SymmetricKeyAlgorithm

    )
    $PublicKeys = [System.Collections.Generic.List[System.IO.FileInfo]]::new()
    foreach ($FilePathPubc in $FilePathPublic) {
        $ResolvedPublicKey = $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($FilePathPubc)
        if (Test-Path -LiteralPath $ResolvedPublicKey) {
            $PublicKeys.Add([System.IO.FileInfo]::new($ResolvedPublicKey))
        } else {
            if ($PSBoundParameters.ErrorAction -eq 'Stop') {
                throw
            } else {
                Write-Warning -Message "Protect-PGP - Public key doesn't exists $($ResolvedPublicKey): $($_.Exception.Message)"
                return
            }
        }
    }
    try {
        if ($SignKey) {
            $EncryptionKeys = [PgpCore.EncryptionKeys]::new($PublicKeys, $SignKey, $SignPassword)
        } else {
            $EncryptionKeys = [PgpCore.EncryptionKeys]::new($PublicKeys)
        }
        $PGP = [PgpCore.PGP]::new($EncryptionKeys)
    } catch {
        if ($PSBoundParameters.ErrorAction -eq 'Stop') {
            throw
        } else {
            Write-Warning -Message "Protect-PGP - Can't encrypt files because: $($_.Exception.Message)"
            return
        }
    }

    if ($PSBoundParameters.ContainsKey('HashAlgorithm')) {
        $PGP.HashAlgorithmTag = $HashAlgorithm
    }
    if ($PSBoundParameters.ContainsKey('CompressionAlgorithm')) {
        $PGP.CompressionAlgorithm = $CompressionAlgorithm
    }
    if ($PSBoundParameters.ContainsKey('FileType')) {
        $PGP.FileType = $FileType
    }
    if ($PSBoundParameters.ContainsKey('PgpSignatureType')) {
        $PGP.PgpSignatureType = $PgpSignatureType
    }
    if ($PSBoundParameters.ContainsKey('PublicKeyAlgorithm')) {
        $PGP.PublicKeyAlgorithm = $PublicKeyAlgorithm
    }
    if ($PSBoundParameters.ContainsKey('SymmetricKeyAlgorithm')) {
        $PGP.SymmetricKeyAlgorithm = $SymmetricKeyAlgorithm
    }

    if ($FolderPath) {
        $ResolvedFolderPath = $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($FolderPath)
        foreach ($File in Get-ChildItem -LiteralPath $ResolvedFolderPath -Recurse:$Recursive) {
            try {
                if ($OutputFolderPath) {
                    $ResolvedOutputFolder = $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($OutputFolderPath)
                    $OutputFile = [io.Path]::Combine($ResolvedOutputFolder, "$($File.Name).pgp")
                    if ($SignKey) {
                        $PGP.EncryptFileAndSign($File.FullName, $Outputfile)
                    } else {
                        $PGP.EncryptFile($File.FullName, $OutputFile)
                    }
                } else {
                    if ($SignKey) {
                        $PGP.EncryptFileAndSign($File.FullName, "$($File.FullName).pgp")
                    } else {
                        $PGP.EncryptFile($File.FullName, "$($File.FullName).pgp")
                    }
                }
            } catch {
                if ($PSBoundParameters.ErrorAction -eq 'Stop') {
                    throw
                } else {
                    Write-Warning -Message "Protect-PGP - Can't encrypt file $($File.FullName): $($_.Exception.Message)"
                    return
                }
            }
        }
    } elseif ($FilePath) {
        try {
            $ResolvedFilePath = $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($FilePath)
            if ($OutFilePath) {
                $ResolvedOutFilePath = $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($OutFilePath)
                if ($SignKey) {
                    $PGP.EncryptFileAndSign($ResolvedFilePath, $ResolvedOutFilePath)
                } else {
                    $PGP.EncryptFile($ResolvedFilePath, $ResolvedOutFilePath)
                }
            } else {
                if ($SignKey) {
                    $PGP.EncryptFileAndSign($ResolvedFilePath, "$($ResolvedFilePath).pgp")
                } else {
                    $PGP.EncryptFile($ResolvedFilePath, "$($ResolvedFilePath).pgp")
                }
            }
        } catch {
            if ($PSBoundParameters.ErrorAction -eq 'Stop') {
                throw
            } else {
                Write-Warning -Message "Protect-PGP - Can't encrypt file $($FilePath): $($_.Exception.Message)"
                return
            }
        }
    } elseif ($String) {
        try {
            if ($SignKey) {
                $PGP.EncryptArmoredStringAndSign($String)
            } else {
                $PGP.EncryptArmoredString($String)
            }
        } catch {
            if ($PSBoundParameters.ErrorAction -eq 'Stop') {
                throw
            } else {
                Write-Warning -Message "Protect-PGP - Can't encrypt string: $($_.Exception.Message)"
            }
        }
    }
}

function Test-PGP {
    [cmdletBinding(DefaultParameterSetName = 'File')]
    param(
        [Parameter(Mandatory, ParameterSetName = 'Folder')]
        [Parameter(Mandatory, ParameterSetName = 'File')]
        [Parameter(Mandatory, ParameterSetName = 'String')]
        [string] $FilePathPublic,

        [Parameter(Mandatory, ParameterSetName = 'Folder')][string] $FolderPath,
        [Parameter(ParameterSetName = 'Folder')][string] $OutputFolderPath,

        [Parameter(Mandatory, ParameterSetName = 'File')][string] $FilePath,
        [Parameter(ParameterSetName = 'File')][string] $OutFilePath,

        [Parameter(Mandatory, ParameterSetName = 'String')][string] $String
    )

    $ResolvedPublicKey = $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($FilePathPublic)
    if (Test-Path -LiteralPath $ResolvedPublicKey) {
        $PublicKey = [System.IO.FileInfo]::new($ResolvedPublicKey)
    } else {
        if ($PSBoundParameters.ErrorAction -eq 'Stop') {
            throw
        } else {
            Write-Warning -Message "Test-PGP - Public key doesn't exists $($ResolvedPublicKey): $($_.Exception.Message)"
            return
        }
    }
    try {
        $EncryptionKeys = [PgpCore.EncryptionKeys]::new($PublicKey)
        $PGP = [PgpCore.PGP]::new($EncryptionKeys)
    } catch {
        if ($PSBoundParameters.ErrorAction -eq 'Stop') {
            throw
        } else {
            Write-Warning -Message "Test-PGP - Can't test files because: $($_.Exception.Message)"
            return
        }
    }
    if ($FolderPath) {
        $ResolvedFolderPath = $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($FolderPath)
        foreach ($File in Get-ChildItem -LiteralPath $ResolvedFolderPath -Recurse:$Recursive) {
            try {
                $Output = $PGP.VerifyFile($File.FullName)
                $ErrorMessage = ''
            } catch {
                $Output = $false
                if ($PSBoundParameters.ErrorAction -eq 'Stop') {
                    throw
                } else {
                    Write-Warning -Message "Test-PGP - Can't test file $($File.FuleName): $($_.Exception.Message)"
                    $ErrorMessage = $($_.Exception.Message)
                }
            }
            [PSCustomObject] @{
                FilePath = $File.FullName
                Status   = $Output
                Error    = $ErrorMessage
            }
        }
    } elseif ($FilePath) {
        $ResolvedFilePath = $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($FilePath)
        try {
            $Output = $PGP.VerifyFile($ResolvedFilePath)
        } catch {
            $Output = $false
            if ($PSBoundParameters.ErrorAction -eq 'Stop') {
                throw
            } else {
                Write-Warning -Message "Test-PGP - Can't test file $($ResolvedFilePath): $($_.Exception.Message)"
                $ErrorMessage = $($_.Exception.Message)
            }
        }
        [PSCustomObject] @{
            FilePath = $ResolvedFilePath
            Status   = $Output
            Error    = $ErrorMessage
        }
    } elseif ($String) {
        try {
            $PGP.VerifyArmoredString($String)
        } catch {
            if ($PSBoundParameters.ErrorAction -eq 'Stop') {
                throw
            } else {
                Write-Warning -Message "Test-PGP - Can't test string: $($_.Exception.Message)"
            }
        }
    }
}
function Unprotect-PGP {
    [cmdletBinding(DefaultParameterSetName = 'FolderClearText')]
    param(
        [Parameter(Mandatory, ParameterSetName = 'FolderCredential')]
        [Parameter(Mandatory, ParameterSetName = 'FolderClearText')]
        [Parameter(Mandatory, ParameterSetName = 'FileCredential')]
        [Parameter(Mandatory, ParameterSetName = 'FileClearText')]
        [Parameter(Mandatory, ParameterSetName = 'StringClearText')]
        [Parameter(Mandatory, ParameterSetName = 'StringCredential')]
        [string] $FilePathPrivate,

        [Parameter(ParameterSetName = 'FolderClearText')]
        [Parameter(ParameterSetName = 'FileClearText')]
        [Parameter(ParameterSetName = 'StringClearText')]
        [string] $Password,

        [Parameter(Mandatory, ParameterSetName = 'FileCredential')]
        [Parameter(Mandatory, ParameterSetName = 'FolderCredential')]
        [Parameter(Mandatory, ParameterSetName = 'StringCredential')]
        [pscredential] $Credential,

        [Parameter(Mandatory, ParameterSetName = 'FolderCredential')]
        [Parameter(Mandatory, ParameterSetName = 'FolderClearText')]
        [string] $FolderPath,

        [Parameter(Mandatory, ParameterSetName = 'FolderCredential')]
        [Parameter(Mandatory, ParameterSetName = 'FolderClearText')]
        [string] $OutputFolderPath,

        [Parameter(Mandatory, ParameterSetName = 'FileCredential')]
        [Parameter(Mandatory, ParameterSetName = 'FileClearText')]
        [string] $FilePath,

        [Parameter(Mandatory, ParameterSetName = 'FileCredential')]
        [Parameter(Mandatory, ParameterSetName = 'FileClearText')]
        [string] $OutFilePath,

        [Parameter(Mandatory, ParameterSetName = 'StringClearText')]
        [Parameter(Mandatory, ParameterSetName = 'StringCredential')]
        [string] $String
    )



    if ($Credential) {
        $Password = $Credential.GetNetworkCredential().Password
    }

    $ResolvedPrivateFile = $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($FilePathPrivate)
    if (-not (Test-Path -LiteralPath $ResolvedPrivateFile)) {
        Write-Warning -Message "Unprotect-PGP - Remove PGP encryption failed because private key file doesn't exists."
        return
    }
    $PrivateKey = Get-Content -LiteralPath $ResolvedPrivateFile -Raw
    try {
        $EncryptionKeys = [PgpCore.EncryptionKeys]::new($PrivateKey, $Password)

        $PGP = [PgpCore.PGP]::new($EncryptionKeys)
    } catch {
        if ($PSBoundParameters.ErrorAction -eq 'Stop') {
            throw
        } else {
            Write-Warning -Message "Unprotect-PGP - Can't decrypt files because: $($_.Exception.Message)"
            return
        }
    }
    if ($FolderPath) {
        $ResolvedFolderPath = $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($FolderPath)
        foreach ($File in Get-ChildItem -LiteralPath $ResolvedFolderPath -Recurse:$Recursive) {
            try {
                if ($OutputFolderPath) {
                    $ResolvedOutputFolder = $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($OutputFolderPath)
                    $OutputFile = [io.Path]::Combine($ResolvedOutputFolder, "$($File.Name.Replace('.pgp',''))")
                    $PGP.DecryptFile($File.FullName, $OutputFile)
                } else {
                    $PGP.DecryptFile($File.FullName, "$($File.FullName)")
                }
            } catch {
                if ($PSBoundParameters.ErrorAction -eq 'Stop') {
                    throw
                } else {
                    Write-Warning -Message "Unprotect-PGP - Remove PGP encryption from $($File.FullName) failed: $($_.Exception.Message)"
                    return
                }
            }
        }
    } elseif ($FilePath) {
        try {
            $ResolvedFilePath = $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($FilePath)
            if ($OutFilePath) {
                $ResolvedOutFilePath = $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($OutFilePath)
                $PGP.DecryptFile($ResolvedFilePath, $ResolvedOutFilePath)
            } else {
                $PGP.DecryptFile($ResolvedFilePath, "$($FilePath.Replace('.pgp',''))")
            }
        } catch {
            if ($PSBoundParameters.ErrorAction -eq 'Stop') {
                throw
            } else {
                Write-Warning -Message "Unprotect-PGP - Remove PGP encryption from $($FilePath) failed: $($_.Exception.Message)"
                return
            }
        }
    } elseif ($String) {
        try {
            $PGP.DecryptArmoredString($String)
        } catch {
            if ($PSBoundParameters.ErrorAction -eq 'Stop') {
                throw
            } else {
                Write-Warning -Message "Unprotect-PGP - Remove PGP encryption from string failed: $($_.Exception.Message)"
                return
            }
        }
    }
}



if ($PSVersionTable.PSEdition -eq 'Desktop' -and (Get-ItemProperty "HKLM:\SOFTWARE\Microsoft\NET Framework Setup\NDP\v4\Full").Release -lt 461808) {
    Write-Warning "This module requires .NET Framework 4.7.2 or later."; return 
} 

# Export functions and aliases as required
Export-ModuleMember -Function @('New-PGPKey', 'Protect-PGP', 'Test-PGP', 'Unprotect-PGP') -Alias @()