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] - [private] Write-Debug "[$scriptName] - [functions] - [private] - Processing folder" #region [functions] - [private] - [ConvertTo-ByteArray] Write-Debug "[$scriptName] - [functions] - [private] - [ConvertTo-ByteArray] - Importing" function ConvertTo-ByteArray { <# .SYNOPSIS Converts a string into a byte array. .DESCRIPTION This function attempts to convert an input string into a byte array. It first checks if the input is a Base64-encoded string and attempts to decode it. If the Base64 decoding fails, it assumes the input consists of space-separated decimal values and converts them to bytes. .EXAMPLE ConvertTo-ByteArray "SGVsbG8gd29ybGQ=" Converts the Base64-encoded string "SGVsbG8gd29ybGQ=" into a byte array. .EXAMPLE ConvertTo-ByteArray "72 101 108 108 111" Converts the space-separated decimal values into a byte array representing the string "Hello". .NOTES This function assumes that if the input is not valid Base64, it must be space-separated decimal values. #> [OutputType([byte[]])] [CmdletBinding()] param ( # The input string to be converted into a byte array. [Parameter(Mandatory = $true)] [string] $InputString ) # Check if it's a Base64 string first (GitHub API provides keys in Base64) try { return [Convert]::FromBase64String($InputString) } catch { # If not Base64, assume it's space-separated decimal values return $InputString -split '\s+' | ForEach-Object { [byte]$_ } } } Write-Debug "[$scriptName] - [functions] - [private] - [ConvertTo-ByteArray] - Done" #endregion [functions] - [private] - [ConvertTo-ByteArray] #region [functions] - [private] - [Initialize-Sodium] Write-Debug "[$scriptName] - [functions] - [private] - [Initialize-Sodium] - Importing" function Initialize-Sodium { <# .SYNOPSIS Initializes the Sodium cryptographic library. .DESCRIPTION Calls the sodium_init() function from the PSModule.Sodium namespace to initialize the Sodium cryptographic library. This function must be called before using any other Sodium cryptographic functions. .EXAMPLE Initialize-Sodium Initializes the Sodium cryptographic library for use. .NOTES Ensure that the PSModule.Sodium module is properly installed and loaded before calling this function. #> [CmdletBinding()] param () $null = [PSModule.Sodium]::sodium_init() } Write-Debug "[$scriptName] - [functions] - [private] - [Initialize-Sodium] - Done" #endregion [functions] - [private] - [Initialize-Sodium] Write-Debug "[$scriptName] - [functions] - [private] - Done" #endregion [functions] - [private] #region [functions] - [public] Write-Debug "[$scriptName] - [functions] - [public] - Processing folder" #region [functions] - [public] - [ConvertFrom-SodiumEncryptedString] Write-Debug "[$scriptName] - [functions] - [public] - [ConvertFrom-SodiumEncryptedString] - Importing" function ConvertFrom-SodiumEncryptedString { <# .SYNOPSIS Decrypts a base64-encoded, Sodium-encrypted string. .DESCRIPTION Converts a base64-encoded, Sodium-encrypted string into its original plaintext form. Uses the provided public and private keys to decrypt the sealed message. .EXAMPLE $params = @{ EncryptedSecret = $encryptedSecret PublicKey = $publicKey PrivateKey = $privateKey } ConvertFrom-SodiumEncryptedString @params Decrypts the given encrypted secret using the specified public and private keys and returns the original string. .LINK https://psmodule.io/Sodium/Functions/ConvertFrom-SodiumEncryptedString/ #> [OutputType([string])] [CmdletBinding()] param( # The base64-encoded encrypted secret string to decrypt. [Parameter(Mandatory)] [string] $Secret, # 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 { Initialize-Sodium } process { $ciphertext = [Convert]::FromBase64String($Secret) $publicKeyByteArray = ConvertTo-ByteArray $PublicKey $privateKeyByteArray = ConvertTo-ByteArray $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-SodiumEncryptedString] - Done" #endregion [functions] - [public] - [ConvertFrom-SodiumEncryptedString] #region [functions] - [public] - [ConvertTo-SodiumEncryptedString] Write-Debug "[$scriptName] - [functions] - [public] - [ConvertTo-SodiumEncryptedString] - Importing" function ConvertTo-SodiumEncryptedString { <# .SYNOPSIS Encrypts a secret using a sealed public key box. .DESCRIPTION This function encrypts a given secret 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-SodiumEncryptedString -Secret "mysecret" -PublicKey "BASE64_PUBLIC_KEY" Encrypts the secret "mysecret" using the provided base64-encoded public key and returns a base64-encoded sealed box. .LINK https://psmodule.io/Sodium/Functions/ConvertTo-SodiumEncryptedString/ #> [OutputType([string])] [CmdletBinding()] param( # The secret string to be encrypted. [Parameter(Mandatory)] [string] $Secret, # The base64-encoded public key used for encryption. [Parameter(Mandatory)] [string] $PublicKey ) begin { Initialize-Sodium } process { # Convert public key from Base64 or space-separated string $publicKeyByteArray = ConvertTo-ByteArray $PublicKey if ($publicKeyByteArray.Length -ne 32) { throw "Invalid public key. Expected 32 bytes but got $($publicKeyByteArray.Length)." } $secretBytes = [System.Text.Encoding]::UTF8.GetBytes($Secret) $overhead = [PSModule.Sodium]::crypto_box_sealbytes().ToUInt32() $cipherLength = $secretBytes.Length + $overhead $ciphertext = New-Object byte[] $cipherLength # Encrypt message $result = [PSModule.Sodium]::crypto_box_seal($ciphertext, $secretBytes, [uint64]$secretBytes.Length, $publicKeyByteArray) if ($result -ne 0) { throw 'Encryption failed.' } return [Convert]::ToBase64String($ciphertext) } } Write-Debug "[$scriptName] - [functions] - [public] - [ConvertTo-SodiumEncryptedString] - Done" #endregion [functions] - [public] - [ConvertTo-SodiumEncryptedString] #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. .EXAMPLE New-SodiumKeyPair Generates a new key pair and returns a custom object containing the base64-encoded public and private keys. .LINK https://psmodule.io/Sodium/Functions/New-SodiumKeyPair/ #> [Diagnostics.CodeAnalysis.SuppressMessageAttribute( 'PSUseShouldProcessForStateChangingFunctions', '', Scope = 'Function', Justification = 'Does not change state' )] [OutputType([pscustomobject])] [CmdletBinding()] param() begin { Initialize-Sodium } 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 # Generate key pair $null = [PSModule.Sodium]::crypto_box_keypair($publicKey, $privateKey) # Convert to Base64 for easy storage/transfer 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-SodiumEncryptedString' 'ConvertTo-SodiumEncryptedString' 'New-SodiumKeyPair' ) } Export-ModuleMember @exports #endregion Member exporter |