Skip to content

🩹 [Patch]: Secret crypto operations now fail fast on invalid input with lower per-call overhead#45

Open
Marius Storhaug (MariusStorhaug) wants to merge 2 commits into
mainfrom
fix/44-harden-sodium-interop
Open

🩹 [Patch]: Secret crypto operations now fail fast on invalid input with lower per-call overhead#45
Marius Storhaug (MariusStorhaug) wants to merge 2 commits into
mainfrom
fix/44-harden-sodium-interop

Conversation

@MariusStorhaug
Copy link
Copy Markdown
Member

@MariusStorhaug Marius Storhaug (MariusStorhaug) commented May 14, 2026

Secret encryption and decryption operations in Sodium now validate malformed inputs earlier, return clearer errors for invalid key and payload shapes, and avoid repeated initialization overhead during repeated command usage.

Changed: Repeated crypto calls now incur less overhead

Public crypto commands now initialize Sodium once per module session and reuse cached key and sealed-box size constants instead of re-querying those values for every invocation. This reduces repeated setup cost in CI and automation scenarios that process many secrets.

Fixed: Invalid key and sealed payload shapes now fail fast with clear errors

Get-SodiumPublicKey now rejects wrong-length private keys before key derivation, and ConvertFrom-SodiumSealedBox now rejects sealed payloads shorter than the required Sodium overhead. This prevents low-level failures and returns actionable validation messages earlier in the command flow.

$shortPrivateKey = [Convert]::ToBase64String([byte[]]::new(16))
Get-SodiumPublicKey -PrivateKey $shortPrivateKey
# Invalid private key. Expected 32 bytes but got 16.
$shortSealedBox = [Convert]::ToBase64String([byte[]]::new(16))
ConvertFrom-SodiumSealedBox -SealedBox $shortSealedBox -PrivateKey $privateKey
# Invalid sealed box. Expected at least 48 bytes but got 16.

Changed: Runtime loading behavior is now more deterministic across platforms

Runtime selection now resolves from process architecture across Windows, Linux, and macOS, and Windows support validation aligns with the active process architecture, improving predictable module startup behavior on mixed environments.

Technical Details

  • Added managed validation wrappers in PSModule/Sodium/Sodium.cs around native interop calls, including buffer size checks before unmanaged execution.
  • Applied constrained native DLL search paths for libsodium imports in the C# bridge.
  • Added Initialize-Sodium and module-scoped cached constants via src/functions/private/Initialize-Sodium.ps1 and src/variables/private/Initialized.ps1.
  • Updated public functions in src/functions/public/* to use centralized initialization, enforce explicit input-length checks, and clear sensitive byte arrays in finally blocks.
  • Updated startup runtime resolution in src/main.ps1 to use RuntimeInformation.ProcessArchitecture.
  • Updated src/functions/private/Assert-VisualCRedistributableInstalled.ps1 to validate architecture-specific runtime installation/version conditions.
  • Updated PSModule/build.ps1 to fail fast on per-runtime publish failures.
  • Added regression coverage in tests/Sodium.Tests.ps1 for short sealed-box payloads and wrong-length private keys.
  • Implementation plan progress: all tasks in issue Harden Sodium native interop and reduce per-call cryptographic overhead #44 are completed by this PR.

@MariusStorhaug Marius Storhaug (MariusStorhaug) changed the title Draft: Sodium hardening for issue #44 🩹 [Patch]: Secret crypto operations now fail fast on invalid input with lower per-call overhead May 14, 2026
@github-actions
Copy link
Copy Markdown

Super-linter summary

Language Validation result
CHECKOV Pass ✅
CSHARP Pass ✅
GITHUB_ACTIONS Pass ✅
GITLEAKS Pass ✅
GIT_MERGE_CONFLICT_MARKERS Pass ✅
MARKDOWN Pass ✅
NATURAL_LANGUAGE Pass ✅
POWERSHELL Pass ✅
PRE_COMMIT Pass ✅
SPELL_CODESPELL Fail ❌
TRIVY Pass ✅
YAML Pass ✅

Super-linter detected linting errors

For more information, see the GitHub Actions workflow run

Powered by Super-linter

SPELL_CODESPELL
/github/workspace/PSModule/Sodium/Sodium.cs:28: clen ==> clean, clan
/github/workspace/PSModule/Sodium/Sodium.cs:78: clen ==> clean, clan
/github/workspace/PSModule/Sodium/Sodium.cs:81: clen ==> clean, clan
/github/workspace/PSModule/Sodium/Sodium.cs:86: clen ==> clean, clan
/github/workspace/PSModule/Sodium/Sodium.cs:87: clen ==> clean, clan
/github/workspace/PSModule/Sodium/Sodium.cs:91: clen ==> clean, clan

@MariusStorhaug Marius Storhaug (MariusStorhaug) marked this pull request as ready for review May 15, 2026 20:20
Copilot AI review requested due to automatic review settings May 15, 2026 20:20
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR hardens the Sodium PowerShell module by adding a managed validation layer in the C# interop bridge, centralizing module initialization with cached constants, validating input shapes earlier in public commands, clearing sensitive buffers in finally blocks, and making runtime selection deterministic by process architecture.

Changes:

  • Added validated wrappers around all native libsodium P/Invoke calls and constrained DLL search paths in PSModule/Sodium/Sodium.cs.
  • Introduced Initialize-Sodium with module-scoped cached size constants and refactored public crypto commands to use them, plus added explicit length validation and finally-block buffer clearing.
  • Switched platform/runtime selection to RuntimeInformation.ProcessArchitecture and aligned the Visual C++ Redistributable check to the active architecture; made build.ps1 fail fast on publish errors.

Reviewed changes

Copilot reviewed 11 out of 11 changed files in this pull request and generated no comments.

Show a summary per file
File Description
PSModule/Sodium/Sodium.cs Wraps native imports in a Native inner class with buffer length validation and constrained DLL search paths.
PSModule/build.ps1 Sets $ErrorActionPreference = 'Stop', builds with -c Release, checks $LASTEXITCODE, and ensures Pop-Location in finally.
src/main.ps1 Resolves runtime identifier from ProcessArchitecture for Linux/macOS/Windows and centralizes module import.
src/functions/private/Assert-VisualCRedistributableInstalled.ps1 Adds Architecture parameter, validates Installed != 0, and uses -ErrorAction SilentlyContinue.
src/functions/private/Initialize-Sodium.ps1 New helper that initializes Sodium once and caches public/private/seal byte sizes.
src/variables/private/Initialized.ps1 Declares module-scoped Sodium state and size variables.
src/functions/public/New-SodiumKeyPair.ps1 Uses cached sizes, disposes SHA256, clears sensitive buffers in finally.
src/functions/public/Get-SodiumPublicKey.ps1 Removes duplicate [CmdletBinding()], validates private-key length, clears the key buffer.
src/functions/public/ConvertTo-SodiumSealedBox.ps1 Uses cached sizes, validates public key length, clears plaintext in finally.
src/functions/public/ConvertFrom-SodiumSealedBox.ps1 Validates short sealed boxes and key lengths with explicit messages; clears sensitive buffers in finally.
tests/Sodium.Tests.ps1 Adds regression tests for short sealed box and wrong-length private key.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@github-actions
Copy link
Copy Markdown

Super-linter summary

Language Validation result
CHECKOV Pass ✅
CSHARP Pass ✅
GITHUB_ACTIONS Pass ✅
GITLEAKS Pass ✅
GIT_MERGE_CONFLICT_MARKERS Pass ✅
MARKDOWN Pass ✅
NATURAL_LANGUAGE Pass ✅
POWERSHELL Pass ✅
PRE_COMMIT Pass ✅
SPELL_CODESPELL Pass ✅
TRIVY Pass ✅
YAML Pass ✅

All files and directories linted successfully

For more information, see the GitHub Actions workflow run

Powered by Super-linter

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Harden Sodium native interop and reduce per-call cryptographic overhead

2 participants