SteganographyOnlineCodec.psm1
|
################################################################################ # # Steganography Online Codec Web API client for PowerShell. # Hide a password-encrypted message inside images using AES-256 / PBKDF2. # # Version : PowerShell SDK v1.0.0 # PowerShell : Windows PowerShell 5.1 / PowerShell 7+ # Author : Bartosz Wójcik (support@pelock.com) # Project : https://www.pelock.com/products/steganography-online-codec # Homepage : https://www.pelock.com # ################################################################################ Set-StrictMode -Version Latest if ($PSVersionTable.PSVersion.Major -lt 6) { [Net.ServicePointManager]::SecurityProtocol = [Net.ServicePointManager]::SecurityProtocol -bor [Net.SecurityProtocolType]::Tls12 } $script:StegoApiUrl = 'https://www.pelock.com/api/steganography-online-codec/v1' $script:StegoUserAgent = 'PELock Steganography Online Codec' $script:StegoTimeoutSec = 3600 class SteganographyErrors { static [int] $WEBAPI_CONNECTION = -1 static [int] $SUCCESS = 0 static [int] $UNKNOWN = 1 static [int] $MESSAGE_TOO_LONG = 2 static [int] $IMAGE_TOO_BIG = 3 static [int] $INVALID_INPUT = 4 static [int] $INVALID_IMAGE_FORMAT = 5 static [int] $IMAGE_MALFORMED = 6 static [int] $INVALID_PASSWORD = 7 static [int] $LIMIT_MESSAGE = 9 static [int] $LIMIT_PASSWORD = 10 static [int] $OUTPUT_FILE = 99 static [int] $INVALID_LICENSE = 100 } function ConvertTo-StegoFormBody { param([Parameter(Mandatory)] [hashtable]$Fields) $parts = foreach ($key in $Fields.Keys) { $name = [uri]::EscapeDataString([string]$key) $value = [uri]::EscapeDataString([string]$Fields[$key]) '{0}={1}' -f $name, $value } return ($parts -join '&') } function Invoke-StegoMultipartRequest { param( [Parameter(Mandatory)] [hashtable]$Fields, [string]$FileField, [string]$FilePath ) $headers = @{ 'User-Agent' = $script:StegoUserAgent } if ($PSVersionTable.PSVersion.Major -ge 6 -and $FilePath) { $form = @{} foreach ($key in $Fields.Keys) { $form[$key] = [string]$Fields[$key] } $form[$FileField] = Get-Item -LiteralPath $FilePath return Invoke-RestMethod -Uri $script:StegoApiUrl -Method Post -Form $form ` -Headers $headers -TimeoutSec $script:StegoTimeoutSec } $boundary = '---------------------------' + [guid]::NewGuid().ToString('N') $encoding = [System.Text.Encoding]::UTF8 $ms = New-Object System.IO.MemoryStream try { foreach ($key in $Fields.Keys) { $header = "--$boundary`r`nContent-Disposition: form-data; name=`"$key`"`r`n`r`n" $headerBytes = $encoding.GetBytes($header) $ms.Write($headerBytes, 0, $headerBytes.Length) $valueBytes = $encoding.GetBytes([string]$Fields[$key]) $ms.Write($valueBytes, 0, $valueBytes.Length) $crlf = $encoding.GetBytes("`r`n") $ms.Write($crlf, 0, $crlf.Length) } if ($FilePath) { $fileName = [System.IO.Path]::GetFileName($FilePath) $fileHeader = "--$boundary`r`nContent-Disposition: form-data; name=`"$FileField`"; filename=`"$fileName`"`r`nContent-Type: application/octet-stream`r`n`r`n" $fileHeaderBytes = $encoding.GetBytes($fileHeader) $ms.Write($fileHeaderBytes, 0, $fileHeaderBytes.Length) $fileBytes = [System.IO.File]::ReadAllBytes((Resolve-Path -LiteralPath $FilePath).Path) $ms.Write($fileBytes, 0, $fileBytes.Length) $crlf = $encoding.GetBytes("`r`n") $ms.Write($crlf, 0, $crlf.Length) } $end = $encoding.GetBytes("--$boundary--`r`n") $ms.Write($end, 0, $end.Length) $body = $ms.ToArray() } finally { $ms.Dispose() } return Invoke-RestMethod -Uri $script:StegoApiUrl -Method Post -Body $body ` -ContentType ("multipart/form-data; boundary={0}" -f $boundary) ` -Headers $headers -TimeoutSec $script:StegoTimeoutSec } class SteganographyOnlineCodec { static [string] $API_URL = 'https://www.pelock.com/api/steganography-online-codec/v1' hidden [string] $_api_key = '' SteganographyOnlineCodec() { $this.Initialize($null) } SteganographyOnlineCodec([string]$ApiKey) { $this.Initialize($ApiKey) } hidden [void] Initialize([string]$ApiKey) { $this._api_key = $ApiKey } [hashtable] Login() { return $this.PostRequest(@{ command = 'login' }) } [hashtable] Encode([string]$InputImagePath, [string]$MessageToHide, [string]$Password, [string]$OutputImagePath) { $result = $this.PostRequest(@{ command = 'encode' image = $InputImagePath message = $MessageToHide password = $Password }) if ([int]$result['error'] -eq [SteganographyErrors]::SUCCESS) { try { $binary = [Convert]::FromBase64String([string]$result['encodedImage']) [System.IO.File]::WriteAllBytes($OutputImagePath, $binary) $result.Remove('encodedImage') return $result } catch { return @{ error = [SteganographyErrors]::OUTPUT_FILE } } } return $result } [hashtable] Decode([string]$InputImagePath, [string]$Password) { return $this.PostRequest(@{ command = 'decode' image = $InputImagePath password = $Password }) } [hashtable] PostRequest([hashtable]$ParamsArray) { $defaultError = @{ error = [SteganographyErrors]::WEBAPI_CONNECTION } $params = @{} foreach ($key in $ParamsArray.Keys) { $params[$key] = $ParamsArray[$key] } if ($this._api_key) { $params['key'] = $this._api_key } $filePath = $null if ($params.ContainsKey('image')) { $filePath = [string]$params['image'] if (-not (Test-Path -LiteralPath $filePath -PathType Leaf)) { return @{ error = [SteganographyErrors]::INVALID_INPUT } } $params.Remove('image') } try { if ($filePath) { $result = Invoke-StegoMultipartRequest -Fields $params -FileField 'image' -FilePath $filePath } else { $body = ConvertTo-StegoFormBody -Fields $params $headers = @{ 'User-Agent' = $script:StegoUserAgent } $result = Invoke-RestMethod -Uri $script:StegoApiUrl -Method Post -Body $body ` -ContentType 'application/x-www-form-urlencoded' -Headers $headers ` -TimeoutSec $script:StegoTimeoutSec } } catch { return $defaultError } if ($null -eq $result) { return $defaultError } $table = @{} foreach ($property in $result.PSObject.Properties) { $table[$property.Name] = $property.Value } if (-not $table.ContainsKey('error')) { return $defaultError } return $table } [hashtable] post_request([hashtable]$params_array) { return $this.PostRequest($params_array) } [string] ConvertSize([double]$SizeBytes) { if ($SizeBytes -le 0) { return '0 bytes' } $sizeName = @('bytes', 'kB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB') $i = [int][Math]::Floor([Math]::Log($SizeBytes, 1024)) if ($i -ge $sizeName.Length) { $i = $sizeName.Length - 1 } $p = [Math]::Pow(1024, $i) $s = [Math]::Round($SizeBytes / $p, 2) return ('{0} {1}' -f $s, $sizeName[$i]) } } function New-SteganographyOnlineCodec { <# .SYNOPSIS Creates a Steganography Online Codec Web API client. .PARAMETER ApiKey Activation key from PELock. Empty keys run demo mode. .EXAMPLE $client = New-SteganographyOnlineCodec -ApiKey 'YOUR-WEB-API-KEY' $result = $client.Encode('input.jpg', 'Secret', 'Pa$$word', 'output.png') #> [CmdletBinding()] [OutputType([SteganographyOnlineCodec])] param( [string]$ApiKey ) if ([string]::IsNullOrWhiteSpace($ApiKey)) { return [SteganographyOnlineCodec]::new($null) } return [SteganographyOnlineCodec]::new($ApiKey) } Export-ModuleMember -Function @('New-SteganographyOnlineCodec') |