Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Sodium Bundle Laravel Package

geekcell/sodium-bundle

GeekCell Sodium Bundle integrates PHP's Sodium extension with Symfony, providing configurable services for authenticated encryption, decryption, password hashing, and secure random bytes. It simplifies using libsodium's modern cryptography in your applications.

View on GitHub
Deep Wiki
Context7

Getting Started

Because the bundle is archived and has no dependents, treat it as a small, auditable wrapper you vendor into a project rather than a long-lived framework dependency. As of v1.0.3 (2024‑05‑28) the baseline moved: PHP 8.2 is the minimum, and the Symfony constraint is ^6.0 | ^7.0. Note this is a Symfony bundle — in a Laravel app you will typically be borrowing its classes (or calling sodium directly), not registering the bundle. Confirm each step against vendor/geekcell/sodium-bundle, since docs are thin.

  1. Install and verify the runtime requirement first. The bundle depends on PHP's sodium extension, not paragonie/sodium_compat. Check php -m | grep sodium in every environment (CLI, FPM, workers) and confirm php -v reports 8.2+ — 1.0.3 will not install on 8.1. If any environment lacks the extension, fix that before proceeding — the bundle will not silently fall back.
  2. Add the bundle. In a Symfony app with Flex the recipe (if any) registers it automatically; otherwise add it manually to config/bundles.php:
    return [
        // ...
        GeekCell\SodiumBundle\GeekCellSodiumBundle::class => ['all' => true],
    ];
    
    In a Laravel app there is no bundles.php; pull in the classes you need and bind them in a service provider (see the service-wrapper pattern below).
  3. Find the actual services before writing code. Run:
    bin/console debug:container --show-private | grep -i sodium
    bin/console config:dump-reference geek_cell_sodium 2>/dev/null
    
    Look for the encryptor service ID and its constructor arguments. This tells you whether keys come from bundle config, environment variables, or are injected manually — the single most important thing to confirm given the sparse documentation. Note that 1.0.3 includes "fixed command / bundle to respect symfony 7.0 changes," so the service wiring and any console command signatures may differ from what you'd infer from 1.0.2 blog posts or older vendor code.
  4. Configure a key pair or secret key. For symmetric use you need a 32‑byte secret key; for asymmetric use a 32‑byte secret key plus a 32‑byte public key. Generate them with Sodium itself:
    php -r 'echo base64_encode(sodium_crypto_secretbox_keygen()), PHP_EOL;'
    php -r '$kp = sodium_crypto_box_keypair(); echo base64_encode(sodium_crypto_box_publickey($kp)), PHP_EOL, base64_encode(sodium_crypto_box_secretkey($kp)), PHP_EOL;'
    
    Store the base64 values in your secrets manager and expose them as environment variables. Never commit them.
  5. First use case: encrypt one field at rest. Pick a single low-risk target — a stored API token, a TOTP seed, or a PII column — inject the encryptor, and wrap the value on write / unwrap on read. Confirm round-tripping in a test before rolling it out to more columns.

If debug:container shows no service, the bundle may only ship classes (not autowired services); in that case instantiate them directly and register aliases in config/services.yaml.

Implementation Patterns

Wrap the bundle in your own service. Do not let framework or domain code depend on the bundle's API directly. Define an EncryptionInterface in your app with encrypt(string): string and decrypt(string): string, and implement it using the bundle's service. This buys you a one-file migration path if you replace the bundle (likely, given it is archived) and keeps your tests mockable. It also absorbs the cost of future Symfony/PHP constraint bumps, which is the only kind of change 1.0.3 delivered.

Encode ciphertext before persistence. Sodium produces raw binary. Any ciphertext that touches a database, cache, message queue, or HTTP header must be encoded. Use base64url without padding and prefix the encoding and key ID so you can rotate later:

v1.<keyId>.<base64url(nonce || ciphertext)>

Storing the nonce alongside the ciphertext is correct and expected — nonces are public. Do not invent your own nonce derivation.

Nonce handling is the one thing you cannot get wrong. For sodium_crypto_secretbox (and any nonce-based primitive the bundle exposes), generate a fresh random_bytes(SODIUM_CRYPTO_SECRETBOX_NONCEBYTES) for every single encryption. Reusing a nonce with the same key is a catastrophic failure that breaks confidentiality entirely. If the bundle's API does not generate the nonce internally, generate it in your wrapper and never accept a caller-supplied nonce.

Prefer sealed boxes for one-way data. If you only need to encrypt (e.g., a webhook writes encrypted data into a queue that another service reads), use sodium_crypto_box_seal / sodium_crypto_box_seal_open. There is no nonce to manage and only the public key is needed on the write side, which means the write service never holds a decryption key. This is the pattern to reach for by default in multi-service setups.

Doctrine integration for encrypted columns. Implement a custom Doctrine type or a postLoad/prePersist listener rather than encrypting in controllers. A custom Doctrine\DBAL\Types\Type mapping to TEXT/BLOB keeps the encryption concern in one place, works with migrations, and survives refactors. Note that encrypted columns cannot be searched or sorted server-side — plan for a blind index (HMAC of the plaintext with a separate key) if you need lookups.

Key rotation with key IDs. Even with a single key today, store a key identifier in the ciphertext envelope. Implement decryption as "read key ID → fetch that key → decrypt," and encryption as "always use the current key." This makes rotation a background re-encryption job rather than a big-bang migration.

Messenger and HTTP payloads. Use the bundle for signing or encrypting message bodies crossing trust boundaries, but keep the envelope small and versioned. Encrypt the payload, not the entire serialized Envelope, so routing metadata remains inspectable and debuggable.

Testing. Generate ephemeral keys in test setup (sodium_crypto_secretbox_keygen() in setUp) and inject them, rather than relying on environment variables in CI. Add one test that asserts a tampered ciphertext fails decryption — this catches accidental use of unauthenticated primitives and encoding bugs. If you fork the package, note 1.0.3 migrated the PHPUnit config and removed deprecated PHPStan configuration; older config snippets you find in the wild will emit warnings or not run.

Gotchas and Tips

  • Archived status is still the headline risk. 1.0.3 is a chores-only release: CI PHP bump, PHPUnit config migration, PHPStan config cleanup, PHP 8.2 minimum, and Symfony 7.0 compatibility fixes. No new features, no security fixes, no API changes. Treat the security posture as unchanged, pin the exact version in composer.json, and read the entire source before adoption — it is small enough to audit in an hour. If it does not meet your needs, sodium is directly callable from PHP; a 40-line service of your own may be a better long-term bet.
  • 1.0.3 is a compatibility gate, not an upgrade you can skip or take casually. If you are on Symfony 7 you must be on 1.0.3+ (that is what the "respect symfony 7.0 changes" commit fixes). If you are on PHP 8.1 or Symfony 5, 1.0.3 is unreachable — you are stranded on 1.0.2 with no forward path, which is exactly the argument for isolating the dependency behind your own interface.
  • Decryption failure semantics vary. Sodium's primitives return false on authentication failure rather than throwing. If the bundle passes that through, you will get false silently flowing into your domain. Wrap every decrypt in a check and throw a dedicated exception — silently returning null or an empty string from a decrypt path is a data-corruption bug waiting to happen.
  • Key length is exact. Secretbox keys are 32 bytes, box keypairs are 32 + 32 bytes, and sodium_crypto_box_seal uses a 32-byte public key. If your config loader decodes base64 you can end up with the wrong length; assert lengths at bootstrap so failures are loud and early rather than at first decrypt.
  • config:cache and secrets. Symfony's config cache bakes in values at compile time. If the bundle resolves keys from %env()% at container compile, key rotation requires a cache clear and deploy rather than a restart. Verify with bin/console debug:container after changing the env var — especially after the 1.0.3 wiring changes.
  • Do not use this for passwords or key derivation from user input. This is an encryption wrapper. Passwords belong in password_hash/password_verify. If the bundle exposes a generic "hash" method, ignore it — sodium_crypto_generichash is fast and unsuited to passwords.
  • Watch for AAD support. Modern Sodium APIs support additional authenticated data. If the bundle does not expose it, you cannot bind ciphertext to a context (e.g., a user ID), which weakens protection against ciphertext-swapping attacks. If this matters, add a versioned envelope of your own or call sodium directly.
  • Binary-safety in logs and queues. An unencoded ciphertext written to a Monolog line or a Messenger transport header can corrupt or truncate. Encode at the boundary; this is the most common production failure with any sodium wrapper.
  • Extension point: replace the service alias. Register your own implementation under the bundle's service ID in a compiler pass or services.yaml override if you need to add key rotation, envelope versioning, or logging around it. This is cleaner than subclassing bundle internals and keeps upgrade surface near zero — particularly valuable now that the only published changes are Symfony/PHP constraint churn.
  • Error paths leak information. Sodium is constant-time on the primitives, but your own code may not be. Return the same generic "decryption failed" error regardless of cause, and avoid logging plaintext or key material in exception messages.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
elmogy/larafast
xeviant/laravel-paystack
gorilladash/laravel-website-sdk
martialbe/flysystem-oss
chondal/3312client
jeroen-g/flickr
opscale-co/nova-genbi
itsjustvita/laravel-retention
tequilarapido/trackit
gregsphotography/site-builder-filament
cabaoctavio/laravel-query-log
gregsphotography/site-builder-components
io-digital/clickatell
snowsoft/laravel-admin
oxygen/auth
relicsoftware/barakah-payment-gateway
tobya/work-with-composer
equipc/laravel-email-validator
equipc/laravel-email-database-log
adedsec/nova-translated-labels