Sodium.psm1
[CmdletBinding()] param() $baseName = [System.IO.Path]::GetFileNameWithoutExtension($PSCommandPath) $script:PSModuleInfo = Test-ModuleManifest -Path "$PSScriptRoot\$baseName.psd1" $script:PSModuleInfo | Format-List | Out-String -Stream | ForEach-Object { Write-Debug $_ } $scriptName = $script:PSModuleInfo.Name Write-Debug "[$scriptName] - Importing module" #region [functions] - [public] Write-Debug "[$scriptName] - [functions] - [public] - Processing folder" #region [functions] - [public] - [ConvertFrom-SodiumSealedBox] Write-Debug "[$scriptName] - [functions] - [public] - [ConvertFrom-SodiumSealedBox] - Importing" function ConvertFrom-SodiumSealedBox { <# .SYNOPSIS Decrypts a base64-encoded, Sodium SealedBox-encrypted string. .DESCRIPTION Converts a base64-encoded, Sodium SealedBox-encrypted string into its original plaintext form. Uses the provided public and private keys to decrypt the sealed message. .EXAMPLE $params = @{ SealedBox = $encryptedMessage PublicKey = $publicKey PrivateKey = $privateKey } ConvertFrom-SodiumSealedBox @params Decrypts the given encrypted message using the specified public and private keys and returns the original string. .LINK https://psmodule.io/Sodium/Functions/ConvertFrom-SodiumSealedBox/ .LINK https://doc.libsodium.org/public-key_cryptography/sealed_boxes #> [OutputType([string])] [CmdletBinding()] param( # The base64-encoded encrypted secret string to decrypt. [Parameter(Mandatory)] [Alias('CipherText')] [string] $SealedBox, # The base64-encoded public key used for decryption. [Parameter(Mandatory)] [string] $PublicKey, # The base64-encoded private key used for decryption. [Parameter(Mandatory)] [string] $PrivateKey ) begin { $null = [PSModule.Sodium]::sodium_init() } process { $ciphertext = [Convert]::FromBase64String($SealedBox) $publicKeyByteArray = [Convert]::FromBase64String($PublicKey) $privateKeyByteArray = [Convert]::FromBase64String($PrivateKey) if ($publicKeyByteArray.Length -ne 32) { throw 'Invalid public key.' } if ($privateKeyByteArray.Length -ne 32) { throw 'Invalid private key.' } $overhead = [PSModule.Sodium]::crypto_box_sealbytes().ToUInt32() $decryptedBytes = New-Object byte[] ($ciphertext.Length - $overhead) # Attempt to decrypt $result = [PSModule.Sodium]::crypto_box_seal_open( $decryptedBytes, $ciphertext, [uint64]$ciphertext.Length, $publicKeyByteArray, $privateKeyByteArray ) if ($result -ne 0) { throw 'Decryption failed.' } return [System.Text.Encoding]::UTF8.GetString($decryptedBytes) } } Write-Debug "[$scriptName] - [functions] - [public] - [ConvertFrom-SodiumSealedBox] - Done" #endregion [functions] - [public] - [ConvertFrom-SodiumSealedBox] #region [functions] - [public] - [ConvertTo-SodiumSealedBox] Write-Debug "[$scriptName] - [functions] - [public] - [ConvertTo-SodiumSealedBox] - Importing" function ConvertTo-SodiumSealedBox { <# .SYNOPSIS Encrypts a message using a sealed public key box. .DESCRIPTION This function encrypts a given message using a public key with the SealedPublicKeyBox method from the Sodium library. The result is a base64-encoded sealed box that can only be decrypted by the corresponding private key. .EXAMPLE ConvertTo-SodiumSealedBox -Message "Hello world!" -PublicKey "BASE64_PUBLIC_KEY" Encrypts the message "Hello world!" using the provided base64-encoded public key and returns a base64-encoded sealed box. .LINK https://psmodule.io/Sodium/Functions/ConvertTo-SodiumSealedBox/ .LINK https://doc.libsodium.org/public-key_cryptography/sealed_boxes #> [OutputType([string])] [CmdletBinding()] param( # The message string to be encrypted. [Parameter(Mandatory)] [string] $Message, # The base64-encoded public key used for encryption. [Parameter(Mandatory)] [string] $PublicKey ) begin { $null = [PSModule.Sodium]::sodium_init() } process { # Convert public key from Base64 or space-separated string try { $publicKeyByteArray = [Convert]::FromBase64String($PublicKey) } catch { $PSCmdlet.ThrowTerminatingError($_) } if ($publicKeyByteArray.Length -ne 32) { throw "Invalid public key. Expected 32 bytes but got $($publicKeyByteArray.Length)." } $messageBytes = [System.Text.Encoding]::UTF8.GetBytes($Message) $overhead = [PSModule.Sodium]::crypto_box_sealbytes().ToUInt32() $cipherLength = $messageBytes.Length + $overhead $ciphertext = New-Object byte[] $cipherLength # Encrypt message $result = [PSModule.Sodium]::crypto_box_seal($ciphertext, $messageBytes, [uint64]$messageBytes.Length, $publicKeyByteArray) if ($result -ne 0) { throw 'Encryption failed.' } return [Convert]::ToBase64String($ciphertext) } } Write-Debug "[$scriptName] - [functions] - [public] - [ConvertTo-SodiumSealedBox] - Done" #endregion [functions] - [public] - [ConvertTo-SodiumSealedBox] #region [functions] - [public] - [New-SodiumKeyPair] Write-Debug "[$scriptName] - [functions] - [public] - [New-SodiumKeyPair] - Importing" function New-SodiumKeyPair { <# .SYNOPSIS Generates a new Sodium key pair. .DESCRIPTION This function creates a new cryptographic key pair using Sodium's PublicKeyBox. The keys are returned as a PowerShell custom object, with both the public and private keys encoded in base64 format. If a seed is provided, the key pair is deterministically generated using a SHA-256 derived seed. This ensures that the same input seed will always produce the same key pair. .EXAMPLE New-SodiumKeyPair Output: ```powershell PublicKey PrivateKey --------- ---------- Ac0wdsq6lqLGktckJrasPcTbVRuUCU+OKzVpMno+v0g= PVXI64v00+aT2b2O6Q4l+SfMBUY2R/Nogsl2mp/hXAs= ``` Generates a new key pair and returns a custom object containing the base64-encoded public and private keys. .EXAMPLE New-SodiumKeyPair -Seed "MySecureSeed" Output: ```powershell PublicKey PrivateKey --------- ---------- WQakMx2mIAQMwLqiZteHUTwmMP6mUdK2FL0WEybWgB8= ci5/7eZ0IbGXtqQMaNvxhJ2d9qwFxA8Kjx+vivSTXqU= ``` Generates a deterministic key pair using the given seed string. The same seed will produce the same key pair every time. .LINK https://psmodule.io/Sodium/Functions/New-SodiumKeyPair/ .LINK https://doc.libsodium.org/public-key_cryptography/public-key_signatures #> [Diagnostics.CodeAnalysis.SuppressMessageAttribute( 'PSUseShouldProcessForStateChangingFunctions', '', Scope = 'Function', Justification = 'Does not change state' )] [OutputType([pscustomobject])] [CmdletBinding(DefaultParameterSetName = 'NewKeyPair')] param( # A seed value to use for key generation. [Parameter(Mandatory, ParameterSetName = 'SeededKeyPair')] [string] $Seed ) begin { $null = [PSModule.Sodium]::sodium_init() } process { $pkSize = [PSModule.Sodium]::crypto_box_publickeybytes().ToUInt32() $skSize = [PSModule.Sodium]::crypto_box_secretkeybytes().ToUInt32() $publicKey = New-Object byte[] $pkSize $privateKey = New-Object byte[] $skSize switch ($PSCmdlet.ParameterSetName) { 'SeededKeyPair' { # Derive a 32-byte seed from the provided string seed (using SHA-256) $seedBytes = [System.Text.Encoding]::UTF8.GetBytes($Seed) $derivedSeed = [System.Security.Cryptography.SHA256]::Create().ComputeHash($seedBytes) $result = [PSModule.Sodium]::crypto_box_seed_keypair($publicKey, $privateKey, $derivedSeed) break } default { $result = [PSModule.Sodium]::crypto_box_keypair($publicKey, $privateKey) } } if ($result -ne 0) { throw 'Key pair generation failed.' } return [pscustomobject]@{ PublicKey = [Convert]::ToBase64String($publicKey) PrivateKey = [Convert]::ToBase64String($privateKey) } } } Write-Debug "[$scriptName] - [functions] - [public] - [New-SodiumKeyPair] - Done" #endregion [functions] - [public] - [New-SodiumKeyPair] Write-Debug "[$scriptName] - [functions] - [public] - Done" #endregion [functions] - [public] #region [main] Write-Debug "[$scriptName] - [main] - Importing" switch ($true) { $IsLinux { Import-Module "$PSScriptRoot/libs/linux-x64/PSModule.Sodium.dll" } $IsMacOS { if ("$(sysctl -n machdep.cpu.brand_string)" -Like 'Apple*') { Import-Module "$PSScriptRoot/libs/osx-arm64/PSModule.Sodium.dll" } else { Import-Module "$PSScriptRoot/libs/osx-x64/PSModule.Sodium.dll" } } $IsWindows { if ([System.Environment]::Is64BitProcess) { Import-Module "$PSScriptRoot/libs/win-x64/PSModule.Sodium.dll" } else { Import-Module "$PSScriptRoot/libs/win-x86/PSModule.Sodium.dll" } } default { throw 'Unsupported platform. Please refer to the documentation for more information.' } } Write-Debug "[$scriptName] - [main] - Done" #endregion [main] #region Member exporter $exports = @{ Alias = '*' Cmdlet = '' Function = @( 'ConvertFrom-SodiumSealedBox' 'ConvertTo-SodiumSealedBox' 'New-SodiumKeyPair' ) } Export-ModuleMember @exports #endregion Member exporter |