# RescuePC Protected Module - DO NOT EDIT
# Source code is maintained separately in the development repository.
#Requires -Version 5.1

$script:PackConfig = @{
    ManifestFileName = "pack-manifest.json"
    SignatureFileName = "pack-signature.json"
    HashAlgorithm = "SHA256"
    SignatureAlgorithm = "RSA-SHA256"
    MinKeySize = 2048
    MaxSignatureAgeDays = 365
    TrustedPublicKeyPath = "$env:LOCALAPPDATA\RescuePC\Keys\pack-public.pem"
    EmbeddedPublicKey = $null
}

function Get-FileHashSHA256 {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory)]
        [string]$FilePath
    )

    if (-not (Test-Path $FilePath)) {
        throw "File not found: $FilePath"
    }

    $hash = Get-FileHash -Path $FilePath -Algorithm SHA256
    return $hash.Hash.ToLower()
}

function Get-DirectoryManifest {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory)]
        [string]$Path,

        [string[]]$Include = @("*.ps1", "*.psm1", "*.psd1", "*.json", "*.xml"),

        [string[]]$Exclude = @("*-manifest.json", "*-signature.json", "*-signature.sig", "*.log")
    )

    if (-not (Test-Path $Path)) {
        throw "Directory not found: $Path"
    }

    $manifest = @{
        Version = "1.0"
        GeneratedAt = (Get-Date).ToUniversalTime().ToString("o")
        GeneratedBy = $env:COMPUTERNAME
        RootPath = (Resolve-Path $Path).Path
        Files = @()
        TotalFiles = 0
        TotalSizeBytes = 0
    }

    $files = Get-ChildItem -Path $Path -Recurse -File | Where-Object {
        $file = $_
        $included = $false
        foreach ($pattern in $Include) {
            if ($file.Name -like $pattern) {
                $included = $true
                break
            }
        }

        if ($included) {
            foreach ($pattern in $Exclude) {
                if ($file.Name -like $pattern) {
                    $included = $false
                    break
                }
            }
        }

        return $included
    }

    foreach ($file in $files) {
        $relativePath = $file.FullName.Substring($manifest.RootPath.Length + 1).Replace("\", "/")
        $hash = Get-FileHashSHA256 -FilePath $file.FullName

        $manifest.Files += @{
            Path = $relativePath
            Hash = $hash
            SizeBytes = $file.Length
            LastModified = $file.LastWriteTimeUtc.ToString("o")
        }

        $manifest.TotalFiles++
        $manifest.TotalSizeBytes += $file.Length
    }

    $manifest.Files = $manifest.Files | Sort-Object { $_.Path }

    $allHashes = ($manifest.Files | ForEach-Object { $_.Hash }) -join ""
    $sha256 = [System.Security.Cryptography.SHA256]::Create()
    $manifestHashBytes = $sha256.ComputeHash([System.Text.Encoding]::UTF8.GetBytes($allHashes))
    $manifest.ManifestHash = [BitConverter]::ToString($manifestHashBytes).Replace("-", "").ToLower()

    return $manifest
}

function New-PackManifest {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory)]
        [string]$PackPath,

        [Parameter(Mandatory)]
        [string]$PackName,

        [string]$PackVersion = "1.0.0",

        [string]$Description = ""
    )

    $manifest = Get-DirectoryManifest -Path $PackPath

    $manifest.PackName = $PackName
    $manifest.PackVersion = $PackVersion
    $manifest.Description = $Description
    $manifest.Publisher = "RescuePC Repairs"
    $manifest.MinPowerShellVersion = "5.1"

    $manifestPath = Join-Path $PackPath $script:PackConfig.ManifestFileName
    $manifest | ConvertTo-Json -Depth 10 | Out-File -FilePath $manifestPath -Encoding UTF8

    Write-Host "Pack manifest created: $manifestPath" -ForegroundColor Green
    Write-Host "  Files: $($manifest.TotalFiles)"
    Write-Host "  Size: $([math]::Round($manifest.TotalSizeBytes / 1KB, 2)) KB"
    Write-Host "  Hash: $($manifest.ManifestHash)"

    return $manifest
}

function Test-PackIntegrity {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory)]
        [string]$PackPath,

        [switch]$Strict
    )

    $manifestPath = Join-Path $PackPath $script:PackConfig.ManifestFileName

    if (-not (Test-Path $manifestPath)) {
        return @{
            Valid = $false
            Error = "Manifest not found: $manifestPath"
            Details = @()
        }
    }

    try {
        $manifest = Get-Content -Path $manifestPath -Raw | ConvertFrom-Json
    }
    catch {
        return @{
            Valid = $false
            Error = "Invalid manifest format: $($_.Exception.Message)"
            Details = @()
        }
    }

    $result = @{
        Valid = $true
        Error = $null
        Details = @()
        FilesChecked = 0
        FilesValid = 0
        FilesMissing = 0
        FilesModified = 0
        FilesExtra = 0
    }

    $rootPath = (Resolve-Path $PackPath).Path
    $manifestFiles = @{}

    foreach ($file in $manifest.Files) {
        $manifestFiles[$file.Path] = $file.Hash
    }

    foreach ($file in $manifest.Files) {
        $filePath = Join-Path $rootPath ($file.Path.Replace("/", "\"))
        $result.FilesChecked++

        if (-not (Test-Path $filePath)) {
            $result.FilesMissing++
            $result.Details += @{
                File = $file.Path
                Status = "MISSING"
                Expected = $file.Hash
                Actual = $null
            }
            $result.Valid = $false
            continue
        }

        $actualHash = Get-FileHashSHA256 -FilePath $filePath

        if ($actualHash -ne $file.Hash) {
            $result.FilesModified++
            $result.Details += @{
                File = $file.Path
                Status = "MODIFIED"
                Expected = $file.Hash
                Actual = $actualHash
            }
            $result.Valid = $false
        }
        else {
            $result.FilesValid++
            $result.Details += @{
                File = $file.Path
                Status = "OK"
                Expected = $file.Hash
                Actual = $actualHash
            }
        }
    }

    if ($Strict) {
        $currentManifest = Get-DirectoryManifest -Path $PackPath
        foreach ($file in $currentManifest.Files) {
            if (-not $manifestFiles.ContainsKey($file.Path)) {
                $result.FilesExtra++
                $result.Details += @{
                    File = $file.Path
                    Status = "EXTRA"
                    Expected = $null
                    Actual = $file.Hash
                }
                $result.Valid = $false
            }
        }
    }

    if (-not $result.Valid) {
        $result.Error = "Pack integrity check failed: $($result.FilesMissing) missing, $($result.FilesModified) modified, $($result.FilesExtra) extra"
    }

    return $result
}

function New-PackSigningKeyPair {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory)]
        [string]$OutputDirectory,

        [int]$KeySize = 2048
    )

    if ($KeySize -lt $script:PackConfig.MinKeySize) {
        throw "Key size must be at least $($script:PackConfig.MinKeySize) bits"
    }

    if (-not (Test-Path $OutputDirectory)) {
        New-Item -Path $OutputDirectory -ItemType Directory -Force | Out-Null
    }

    $rsa = [System.Security.Cryptography.RSA]::Create($KeySize)

    $privateKeyPath = Join-Path $OutputDirectory "pack-private.xml"
    $privateKeyXml = $rsa.ToXmlString($true)
    $privateKeyXml | Out-File -FilePath $privateKeyPath -Encoding UTF8

    $publicKeyPath = Join-Path $OutputDirectory "pack-public.xml"
    $publicKeyXml = $rsa.ToXmlString($false)
    $publicKeyXml | Out-File -FilePath $publicKeyPath -Encoding UTF8

    $publicKeyBase64 = [Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes($publicKeyXml))
    $publicKeyBase64Path = Join-Path $OutputDirectory "pack-public.b64"
    $publicKeyBase64 | Out-File -FilePath $publicKeyBase64Path -Encoding UTF8

    $rsa.Dispose()

    Write-Host "RSA key pair generated ($KeySize-bit)" -ForegroundColor Green
    Write-Host "  Private key: $privateKeyPath (KEEP SECRET!)" -ForegroundColor Red
    Write-Host "  Public key:  $publicKeyPath" -ForegroundColor Cyan
    Write-Host "  Base64 key:  $publicKeyBase64Path (for embedding)" -ForegroundColor Gray

    return @{
        PrivateKeyPath = $privateKeyPath
        PublicKeyPath = $publicKeyPath
        PublicKeyBase64 = $publicKeyBase64
        KeySize = $KeySize
    }
}

function Import-RSAPrivateKey {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory)]
        [string]$KeyPath
    )

    if (-not (Test-Path $KeyPath)) {
        throw "Private key file not found: $KeyPath"
    }

    $keyXml = Get-Content -Path $KeyPath -Raw
    $rsa = [System.Security.Cryptography.RSA]::Create()
    $rsa.FromXmlString($keyXml)

    return $rsa
}

function Import-RSAPublicKey {
    [CmdletBinding()]
    param(
        [string]$KeyPath = "",
        [string]$KeyBase64 = ""
    )

    $keyXml = ""

    if ($KeyPath -and (Test-Path $KeyPath)) {
        $keyXml = Get-Content -Path $KeyPath -Raw
    }
    elseif ($KeyBase64) {
        $keyXml = [System.Text.Encoding]::UTF8.GetString([Convert]::FromBase64String($KeyBase64))
    }
    elseif ($script:PackConfig.EmbeddedPublicKey) {
        $keyXml = [System.Text.Encoding]::UTF8.GetString([Convert]::FromBase64String($script:PackConfig.EmbeddedPublicKey))
    }
    else {
        throw "No public key available. Provide KeyPath, KeyBase64, or set EmbeddedPublicKey in config."
    }

    $rsa = [System.Security.Cryptography.RSA]::Create()
    $rsa.FromXmlString($keyXml)

    return $rsa
}

function Get-PackSignatureRSA {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory)]
        [string]$DataToSign,

        [Parameter(Mandatory)]
        [string]$PrivateKeyPath
    )

    $rsa = Import-RSAPrivateKey -KeyPath $PrivateKeyPath

    try {
        $dataBytes = [System.Text.Encoding]::UTF8.GetBytes($DataToSign)
        $signatureBytes = $rsa.SignData($dataBytes, [System.Security.Cryptography.HashAlgorithmName]::SHA256, [System.Security.Cryptography.RSASignaturePadding]::Pkcs1)
        $signature = [Convert]::ToBase64String($signatureBytes)

        return $signature
    }
    finally {
        $rsa.Dispose()
    }
}

function Test-PackSignatureRSA {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory)]
        [string]$DataToVerify,

        [Parameter(Mandatory)]
        [string]$Signature,

        [string]$PublicKeyPath = ""
    )

    $rsa = Import-RSAPublicKey -KeyPath $PublicKeyPath

    try {
        $dataBytes = [System.Text.Encoding]::UTF8.GetBytes($DataToVerify)
        $signatureBytes = [Convert]::FromBase64String($Signature)

        $isValid = $rsa.VerifyData($dataBytes, $signatureBytes, [System.Security.Cryptography.HashAlgorithmName]::SHA256, [System.Security.Cryptography.RSASignaturePadding]::Pkcs1)

        return $isValid
    }
    catch {
        return $false
    }
    finally {
        $rsa.Dispose()
    }
}

function New-PackSignature {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory)]
        [string]$PackPath,

        [Parameter(Mandatory)]
        [string]$PrivateKeyPath,

        [string]$SignerName = "RescuePC Build System"
    )

    $manifestPath = Join-Path $PackPath $script:PackConfig.ManifestFileName

    if (-not (Test-Path $manifestPath)) {
        throw "Manifest not found. Run New-PackManifest first."
    }

    if (-not (Test-Path $PrivateKeyPath)) {
        throw "Private key not found: $PrivateKeyPath"
    }

    $manifest = Get-Content -Path $manifestPath -Raw | ConvertFrom-Json
    $signedAt = (Get-Date).ToUniversalTime().ToString("o")

    $canonicalData = @{
        ManifestHash = $manifest.ManifestHash
        PackName = $manifest.PackName
        PackVersion = $manifest.PackVersion
        TotalFiles = $manifest.TotalFiles
        SignedAt = $signedAt
        SignerName = $SignerName
    } | ConvertTo-Json -Compress

    $signature = Get-PackSignatureRSA -DataToSign $canonicalData -PrivateKeyPath $PrivateKeyPath

    $signatureData = @{
        Signature = $signature
        CanonicalData = $canonicalData
        ManifestHash = $manifest.ManifestHash
        SignedAt = $signedAt
        SignedBy = $SignerName
        Algorithm = "RSA-SHA256"
        SchemaVersion = "2.0"
    }

    $signaturePath = Join-Path $PackPath $script:PackConfig.SignatureFileName
    $signatureData | ConvertTo-Json -Depth 5 | Out-File -FilePath $signaturePath -Encoding UTF8

    Write-Host "Pack signed with RSA-SHA256" -ForegroundColor Green
    Write-Host "  Signature: $($signature.Substring(0, 32))..."
    Write-Host "  Signed by: $SignerName"
    Write-Host "  Signed at: $signedAt"

    return $signatureData
}

function Test-PackSignature {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory)]
        [string]$PackPath,

        [string]$PublicKeyPath = "",

        [int]$MaxAgeDays = 0
    )

    $manifestPath = Join-Path $PackPath $script:PackConfig.ManifestFileName
    $signaturePath = Join-Path $PackPath $script:PackConfig.SignatureFileName

    if (-not (Test-Path $manifestPath)) {
        return @{ Valid = $false; Error = "Manifest not found"; Code = "NO_MANIFEST" }
    }

    if (-not (Test-Path $signaturePath)) {
        return @{ Valid = $false; Error = "Signature not found"; Code = "NO_SIGNATURE" }
    }

    try {
        $manifest = Get-Content -Path $manifestPath -Raw | ConvertFrom-Json
        $storedSig = Get-Content -Path $signaturePath -Raw | ConvertFrom-Json
    }
    catch {
        return @{ Valid = $false; Error = "Invalid manifest or signature format"; Code = "PARSE_ERROR" }
    }

    if ($storedSig.SchemaVersion -ne "2.0") {
        return @{ Valid = $false; Error = "Unsupported signature schema: $($storedSig.SchemaVersion)"; Code = "SCHEMA_MISMATCH" }
    }

    if ($storedSig.Algorithm -ne "RSA-SHA256") {
        return @{ Valid = $false; Error = "Unsupported algorithm: $($storedSig.Algorithm)"; Code = "ALGORITHM_MISMATCH" }
    }

    if ($MaxAgeDays -gt 0 -or $script:PackConfig.MaxSignatureAgeDays -gt 0) {
        $maxAge = if ($MaxAgeDays -gt 0) { $MaxAgeDays } else { $script:PackConfig.MaxSignatureAgeDays }
        $signedAt = [datetime]::Parse($storedSig.SignedAt)
        $age = (Get-Date) - $signedAt

        if ($age.TotalDays -gt $maxAge) {
            return @{
                Valid = $false
                Error = "Signature expired (age: $([math]::Round($age.TotalDays)) days, max: $maxAge days)"
                Code = "SIGNATURE_EXPIRED"
            }
        }
    }

    $integrityResult = Test-PackIntegrity -PackPath $PackPath
    if (-not $integrityResult.Valid) {
        return @{
            Valid = $false
            Error = "Integrity check failed: $($integrityResult.Error)"
            Code = "INTEGRITY_FAILED"
            Details = $integrityResult.Details
        }
    }

    if ($storedSig.ManifestHash -ne $manifest.ManifestHash) {
        return @{
            Valid = $false
            Error = "Manifest hash mismatch - manifest was modified after signing"
            Code = "HASH_MISMATCH"
        }
    }

    $isValidSig = Test-PackSignatureRSA -DataToVerify $storedSig.CanonicalData -Signature $storedSig.Signature -PublicKeyPath $PublicKeyPath

    if (-not $isValidSig) {
        return @{
            Valid = $false
            Error = "RSA signature verification failed - pack may have been tampered with"
            Code = "SIGNATURE_INVALID"
        }
    }

    return @{
        Valid = $true
        PackName = $manifest.PackName
        PackVersion = $manifest.PackVersion
        SignedAt = $storedSig.SignedAt
        SignedBy = $storedSig.SignedBy
        Algorithm = $storedSig.Algorithm
        FilesVerified = $integrityResult.FilesValid
        Code = "OK"
    }
}

function Invoke-SignedScript {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory)]
        [string]$ScriptPath,

        [Parameter(Mandatory)]
        [string]$PackPath,

        [string]$PublicKeyPath = "",

        [hashtable]$Arguments = @{},

        [switch]$AllowUnsigned
    )

    $startTime = Get-Date

    Write-Host ""
    Write-Host "================================================================" -ForegroundColor Cyan
    Write-Host " RESCUEPC SECURE SCRIPT EXECUTION" -ForegroundColor Cyan
    Write-Host "================================================================" -ForegroundColor Cyan
    Write-Host ""

    if ($AllowUnsigned) {
        Write-Host "[WARN] AllowUnsigned is set - SKIPPING SIGNATURE CHECK" -ForegroundColor Yellow
        Write-Host "[WARN] This should NEVER be used in production!" -ForegroundColor Yellow

        Write-Host "[1/2] Verifying file integrity..." -ForegroundColor Yellow
        $integrityResult = Test-PackIntegrity -PackPath $PackPath

        if (-not $integrityResult.Valid) {
            Write-Host "[BLOCKED] Integrity check failed!" -ForegroundColor Red
            Write-Host "  $($integrityResult.Error)" -ForegroundColor Red
            throw "EXECUTION BLOCKED: Pack integrity verification failed"
        }

        Write-Host "  [OK] $($integrityResult.FilesValid) files verified" -ForegroundColor Green
    }
    else {
        Write-Host "[1/2] Verifying RSA-SHA256 signature..." -ForegroundColor Yellow
        $signatureResult = Test-PackSignature -PackPath $PackPath -PublicKeyPath $PublicKeyPath

        if (-not $signatureResult.Valid) {
            Write-Host "[BLOCKED] Signature verification FAILED!" -ForegroundColor Red
            Write-Host "  Error: $($signatureResult.Error)" -ForegroundColor Red
            Write-Host "  Code:  $($signatureResult.Code)" -ForegroundColor Red
            Write-Host ""
            Write-Host "  This pack cannot be executed because:" -ForegroundColor Yellow

            switch ($signatureResult.Code) {
                "NO_MANIFEST" { Write-Host "  - No manifest file found (pack not properly built)" -ForegroundColor Yellow }
                "NO_SIGNATURE" { Write-Host "  - No signature file found (pack not signed)" -ForegroundColor Yellow }
                "SIGNATURE_INVALID" { Write-Host "  - Signature does not match (pack may be tampered)" -ForegroundColor Red }
                "INTEGRITY_FAILED" { Write-Host "  - Files have been modified since signing" -ForegroundColor Red }
                "SIGNATURE_EXPIRED" { Write-Host "  - Signature has expired (pack needs re-signing)" -ForegroundColor Yellow }
                default { Write-Host "  - Unknown verification error" -ForegroundColor Red }
            }

            throw "EXECUTION BLOCKED: Pack signature verification failed [$($signatureResult.Code)]"
        }

        Write-Host "  [OK] Signature valid" -ForegroundColor Green
        Write-Host "  [OK] Signed by: $($signatureResult.SignedBy)" -ForegroundColor Gray
        Write-Host "  [OK] Signed at: $($signatureResult.SignedAt)" -ForegroundColor Gray
        Write-Host "  [OK] Files verified: $($signatureResult.FilesVerified)" -ForegroundColor Gray
    }

    Write-Host "[2/2] Locating script in pack..." -ForegroundColor Yellow

    $fullScriptPath = Join-Path $PackPath $ScriptPath
    $normalizedPath = $ScriptPath.Replace("\", "/")

    if (-not (Test-Path $fullScriptPath)) {
        Write-Host "[BLOCKED] Script not found: $ScriptPath" -ForegroundColor Red
        throw "EXECUTION BLOCKED: Script not found in pack"
    }

    $manifestPath = Join-Path $PackPath $script:PackConfig.ManifestFileName
    if (Test-Path $manifestPath) {
        $manifest = Get-Content -Path $manifestPath -Raw | ConvertFrom-Json
        $scriptInManifest = $manifest.Files | Where-Object { $_.Path -eq $normalizedPath }

        if (-not $scriptInManifest) {
            Write-Host "[BLOCKED] Script not in manifest - possible injection attack!" -ForegroundColor Red
            throw "EXECUTION BLOCKED: Script not found in signed manifest"
        }

        $actualHash = Get-FileHashSHA256 -FilePath $fullScriptPath
        if ($actualHash -ne $scriptInManifest.Hash) {
            Write-Host "[BLOCKED] Script hash mismatch - file was modified!" -ForegroundColor Red
            throw "EXECUTION BLOCKED: Script hash does not match manifest"
        }
    }

    Write-Host "  [OK] Script verified: $ScriptPath" -ForegroundColor Green

    Write-Host ""
    Write-Host "Executing script..." -ForegroundColor Cyan
    Write-Host "----------------------------------------------------------------" -ForegroundColor Gray

    try {
        if ($Arguments.Count -gt 0) {
            & $fullScriptPath @Arguments
        }
        else {
            & $fullScriptPath
        }

        $duration = (Get-Date) - $startTime
        Write-Host "----------------------------------------------------------------" -ForegroundColor Gray
        Write-Host "[OK] Script completed in $([math]::Round($duration.TotalSeconds, 2))s" -ForegroundColor Green
    }
    catch {
        Write-Host "----------------------------------------------------------------" -ForegroundColor Gray
        Write-Host "[ERROR] Script execution failed: $($_.Exception.Message)" -ForegroundColor Red
        throw
    }
}

function Show-PackStatus {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory)]
        [string]$PackPath
    )

    Write-Host "`n========================================" -ForegroundColor Cyan
    Write-Host " Pack Status Report" -ForegroundColor Cyan
    Write-Host "========================================`n" -ForegroundColor Cyan

    $manifestPath = Join-Path $PackPath $script:PackConfig.ManifestFileName

    if (-not (Test-Path $manifestPath)) {
        Write-Host "[!] No manifest found - pack is unsigned" -ForegroundColor Yellow
        return
    }

    $manifest = Get-Content -Path $manifestPath -Raw | ConvertFrom-Json

    Write-Host "Pack Name:    $($manifest.PackName)" -ForegroundColor White
    Write-Host "Version:      $($manifest.PackVersion)" -ForegroundColor White
    Write-Host "Publisher:    $($manifest.Publisher)" -ForegroundColor White
    Write-Host "Files:        $($manifest.TotalFiles)" -ForegroundColor White
    Write-Host "Size:         $([math]::Round($manifest.TotalSizeBytes / 1KB, 2)) KB" -ForegroundColor White
    Write-Host "Generated:    $($manifest.GeneratedAt)" -ForegroundColor Gray
    Write-Host "Manifest Hash: $($manifest.ManifestHash.Substring(0, 16))..." -ForegroundColor Gray

    Write-Host "`nIntegrity Check:" -ForegroundColor Yellow
    $integrityResult = Test-PackIntegrity -PackPath $PackPath

    if ($integrityResult.Valid) {
        Write-Host "  [OK] All $($integrityResult.FilesValid) files verified" -ForegroundColor Green
    }
    else {
        Write-Host "  [FAIL] $($integrityResult.Error)" -ForegroundColor Red
        foreach ($detail in $integrityResult.Details | Where-Object { $_.Status -ne "OK" }) {
            Write-Host "    - $($detail.File): $($detail.Status)" -ForegroundColor Red
        }
    }

    $signaturePath = Join-Path $PackPath $script:PackConfig.SignatureFileName
    Write-Host "`nSignature Status:" -ForegroundColor Yellow

    if (Test-Path $signaturePath) {
        $sigData = Get-Content -Path $signaturePath -Raw | ConvertFrom-Json
        Write-Host "  Signed by:  $($sigData.SignedBy)" -ForegroundColor White
        Write-Host "  Signed at:  $($sigData.SignedAt)" -ForegroundColor White
        Write-Host "  Algorithm:  $($sigData.Algorithm)" -ForegroundColor Gray
    }
    else {
        Write-Host "  [!] No signature file found" -ForegroundColor Yellow
    }

    Write-Host ""
}

Export-ModuleMember -Function @(
    'Get-FileHashSHA256',
    'Get-DirectoryManifest',
    'New-PackManifest',
    'Test-PackIntegrity',
    'New-PackSigningKeyPair',
    'Import-RSAPrivateKey',
    'Import-RSAPublicKey',
    'Get-PackSignatureRSA',
    'Test-PackSignatureRSA',
    'New-PackSignature',
    'Test-PackSignature',
    'Invoke-SignedScript',
    'Show-PackStatus'
)