Private/XOR.psm1

#!/usr/bin/env pwsh
using namespace System
using namespace System.IO
using namespace System.Text
using namespace System.Security
using namespace System.Security.Cryptography
using namespace System.Runtime.InteropServices

using module ./Models.psm1
using module ./Utilities.psm1

# XOR
# .SYNOPSIS
# Custom Xor implementation in Powershell
# .EXAMPLE
# $x = [XOR]::new("hello world!")
# $e = $x.Encrypt(5) # i.e: 5 times
# [convert]::ToBase64String($e) > xenc.txt
#
# # On the same PC
# $n = [XoR]::new([convert]::FromBase64String($(Get-Content ./xenc.txt)))
# $d = $n.Decrypt(5)
# echo (Bytes_To_Object($d)) # should be hello world!
# .EXAMPLE
# # Use static methods
class XOR : CryptobaseUtils {
  [ValidateNotNullOrEmpty()][CipherObject]$Object;
  [ValidateNotNullOrEmpty()][SecureString]$Password;
  static hidden [byte[]] $Salt = [Encoding]::UTF7.GetBytes('\SBOv!^L?XuCFlJ%*[6(pUVp5GeR^|U=NH3FaK#XECOaM}ExV)3_bkd:eG;Z,tWZRMg;.A!,:-k6D!CP>74G+TW7?(\6;Li]lA**2P(a2XxL}<.*oJY7bOx+lD>%DVVa');
  XOR([Object]$object) {
    $this.Object = [CipherObject]::new($object)
    $this.Password = [Encoding]::UTF7.GetString([Rfc2898DeriveBytes]::new([CryptobaseUtils]::GetUniqueMachineId(), [XOR]::Salt, 1000, [HashAlgorithmName]::SHA1).GetBytes(256 / 8)) | xconvert ToSecurestring
  }
  [byte[]] Encrypt() {
    return $this.Encrypt(1)
  }
  [byte[]] Encrypt([int]$iterations) {
    if ($null -eq $this.Object.Bytes) { throw ([System.ArgumentNullException]::new('Object.Bytes')) }
    if ($null -eq $this.Password) { throw ([System.ArgumentNullException]::new('key')) }
    $this.Object.Psobject.properties.add([psscriptproperty]::new('Bytes', [scriptblock]::Create("[Convert]::FromBase64String('$([convert]::ToBase64String([byte[]][XOR]::Encrypt($this.Object.Bytes, $this.Password, $iterations)))')")))
    return $this.Object.Bytes
  }
  static [byte[]] Encrypt([byte[]]$Bytes, [String]$Passw0rd) {
    return [XOR]::Encrypt($bytes, ([Encoding]::UTF7.GetString([Rfc2898DeriveBytes]::new($Passw0rd, [XOR]::Salt, 1000, [HashAlgorithmName]::SHA1).GetBytes(256 / 8)) | xconvert ToSecurestring), 1)
  }
  static [byte[]] Encrypt([byte[]]$Bytes, [SecureString]$password) {
    return [XOR]::Encrypt($bytes, $password, 1)
  }
  static [byte[]] Encrypt([byte[]]$Bytes, [SecureString]$password, [int]$iterations) {
    $xorkey = $password | xconvert ToString, ToBytes;
    return [XOR]::Encrypt($bytes, $xorkey, $iterations)
  }
  static [byte[]] Encrypt([byte[]]$Bytes, [byte[]]$xorkey, [int]$iterations) {
    if ($null -eq $xorkey) {
      throw ([System.ArgumentNullException]::new('xorkey'))
    }
    $_bytes = $bytes;
    for ($i = 1; $i -lt $iterations + 1; $i++) {
      $_bytes = [XOR]::Get_ED($_bytes, $xorkey);
    }; if ($_bytes.Equals($bytes)) { $_bytes = $null }
    return $_bytes
  }
  [byte[]]Decrypt() {
    return $this.Decrypt(1)
  }
  [byte[]]Decrypt([int]$iterations) {
    if ($null -eq $this.Object.Bytes) { throw ([System.ArgumentNullException]::new('Object.Bytes')) }
    if ($null -eq $this.Password) { throw ([System.ArgumentNullException]::new('Password')) }
    $this.Object.Psobject.properties.add([psscriptproperty]::new('Bytes', [scriptblock]::Create("[Convert]::FromBase64String('$([convert]::ToBase64String([byte[]][XOR]::Decrypt($this.Object.Bytes, $this.Password, $iterations)))')")));
    return $this.Object.Bytes
  }
  #!Not Recommended!
  static [byte[]] Decrypt([byte[]]$Bytes, [String]$Passw0rd) {
    return [XOR]::Decrypt($bytes, ([Encoding]::UTF7.GetString([Rfc2898DeriveBytes]::new($Passw0rd, [XOR]::Salt, 1000, [HashAlgorithmName]::SHA1).GetBytes(256 / 8)) | xconvert ToSecurestring), 1);
  }
  static [byte[]] Decrypt([byte[]]$Bytes, [SecureString]$password) {
    return [XOR]::Decrypt($bytes, $password, 1);
  }
  static [byte[]] Decrypt([byte[]]$Bytes, [SecureString]$password, [int]$iterations) {
    $xorkey = $password | xconvert ToString, ToBytes
    return [XOR]::Decrypt($bytes, $xorkey, $iterations);
  }
  static [byte[]] Decrypt([byte[]]$Bytes, [byte[]]$xorkey, [int]$iterations) {
    if ($null -eq $xorkey) {
      throw ([System.ArgumentNullException]::new('xorkey'))
    }
    $_bytes = $bytes; for ($i = 1; $i -lt $iterations + 1; $i++) {
      $_bytes = [XOR]::Get_ED($_bytes, $xorkey)
    };
    return $_bytes;
  }
  static hidden [byte[]] Get_ED([Byte[]]$data, [Byte[]]$Key, [Byte[]]$IV, [bool]$Encrypt) {
    $result = [byte[]]::new(0); $ms = [MemoryStream]::new(); $cs = $null
    try {
      $tdes = [TripleDESCryptoServiceProvider]::new()
      if ($null -eq $Key) { throw ([System.ArgumentNullException]::new('Key')) }else { $tdes.Key = $Key }
      if ($null -eq $IV) { $p4 = [CryptobaseUtils]::GetUniqueMachineId(); $tdes.IV = [Rfc2898DeriveBytes]::new($p4, [TripleDES]::Salt, [int]([int[]][char[]]$p4 | Measure-Object -Sum).Sum, [HashAlgorithmName]::SHA1).GetBytes(8) }else { $tdes.IV = $IV }
      $CryptoTransform = [ICryptoTransform]$(if ($Encrypt) { $tdes.CreateEncryptor() }else { $tdes.CreateDecryptor() })
      $cs = [CryptoStream]::new($ms, $CryptoTransform, [CryptoStreamMode]::Write)
      [void]$cs.Write($data, 0, $data.Length)
      [void]$cs.FlushFinalBlock()
      $ms.Position = 0
      $result = [Byte[]]::new($ms.Length)
      [void]$ms.Read($result, 0, $ms.Length)
    }
    catch [CryptographicException] {
      if ($_.exception.message -notlike "*data is not a complete block*") { throw $_.exception }
    }
    finally {
      Invoke-Command -ScriptBlock { $tdes.Clear(); $cs.Close(); $ms.Dispose() } -ErrorAction SilentlyContinue
    }
    return $result
  }
}