Definition
Encryption is the process of transforming plaintext data into ciphertext using a mathematical algorithm (cipher) and a secret key, such that only parties possessing the corresponding decryption key can recover the original information. Encryption is the foundational mechanism for data confidentiality -- one of the three pillars of information security alongside integrity and availability. Modern encryption operates through two primary paradigms: symmetric encryption (where the same key encrypts and decrypts) and asymmetric encryption (where mathematically related but distinct public and private keys are used for encryption and decryption respectively).
In the Prismatic Platform, encryption operates at multiple layers: TLS secures all network communication, encryption at rest protects stored data, credential management safeguards API keys and tokens, and certificate transparency monitoring ensures the integrity of the platform's PKI infrastructure.
Overview
Encryption has evolved from ancient substitution ciphers to mathematically rigorous algorithms that underpin all modern digital communication. The field rests on a fundamental principle articulated by Auguste Kerckhoffs in 1883: a cryptographic system should be secure even if everything about the system, except the key, is public knowledge. This principle drives modern encryption design, where algorithm transparency and key secrecy work in concert.
Symmetric Encryption
Symmetric algorithms use a single shared key for both encryption and decryption. They are computationally efficient and suitable for bulk data encryption:
- AES (Advanced Encryption Standard): The dominant symmetric cipher, operating on 128-bit blocks with 128, 192, or 256-bit keys. AES-256-GCM (Galois/Counter Mode) provides both confidentiality and integrity (authenticated encryption).
- ChaCha20-Poly1305: A stream cipher alternative to AES, often faster on systems without hardware AES acceleration. Used extensively in TLS 1.3 and WireGuard.
Asymmetric Encryption
Asymmetric algorithms use key pairs -- a public key for encryption and a private key for decryption. They solve the key distribution problem but are computationally expensive:
- RSA: Based on the difficulty of factoring large integers. Key sizes of 2048 or 4096 bits are standard.
- Elliptic Curve Cryptography (ECC): Achieves equivalent security with smaller keys (256-bit ECC approximates 3072-bit RSA), offering better performance.
- X25519/Ed25519: Modern elliptic curve implementations used in TLS 1.3 key exchange and digital signatures.
Hybrid Encryption
In practice, systems combine both approaches: asymmetric encryption exchanges a symmetric session key, which then encrypts the actual data. This is exactly how TLS works -- the handshake uses asymmetric cryptography to establish a shared secret, then symmetric ciphers encrypt the session.
Key Derivation
When encryption keys must be derived from passwords or other low-entropy sources, key derivation functions (KDFs) stretch and strengthen the input:
- Argon2: Memory-hard KDF, winner of the Password Hashing Competition, resistant to GPU-based attacks.
- PBKDF2: Older but widely deployed, uses iterated HMAC-SHA256.
- scrypt: Memory-hard alternative predating Argon2.
Technical Details
Encryption in the BEAM Ecosystem
Erlang/OTP provides the :crypto module, which wraps OpenSSL's libcrypto library. Elixir builds on this foundation through several layers:
defmodule Prismatic.Security.Encryption do
@moduledoc """
Provides encryption primitives for the Prismatic Platform.
Wraps Erlang's :crypto module with Elixir-idiomatic interfaces
and enforces platform security policies.
"""
@type cipher :: :aes_256_gcm | :chacha20_poly1305
@type key :: binary()
@type plaintext :: binary()
@type ciphertext :: binary()
@type iv :: binary()
@type tag :: binary()
@type aad :: binary()
@aes_key_size 32
@iv_size 12
@tag_size 16
@spec encrypt(plaintext(), key(), cipher()) ::
{:ok, {ciphertext(), iv(), tag()}} | {:error, term()}
def encrypt(plaintext, key, cipher \\ :aes_256_gcm)
def encrypt(plaintext, key, :aes_256_gcm) when byte_size(key) == @aes_key_size do
iv = :crypto.strong_rand_bytes(@iv_size)
aad = build_aad()
case :crypto.crypto_one_time_aead(
:aes_256_gcm, key, iv, plaintext, aad, @tag_size, true
) do
{ciphertext, tag} ->
{:ok, {ciphertext, iv, tag}}
error ->
{:error, {:encryption_failed, error}}
end
end
def encrypt(_plaintext, key, :aes_256_gcm) do
{:error, {:invalid_key_size, byte_size(key), @aes_key_size}}
end
def encrypt(plaintext, key, :chacha20_poly1305) when byte_size(key) == @aes_key_size do
iv = :crypto.strong_rand_bytes(@iv_size)
aad = build_aad()
case :crypto.crypto_one_time_aead(
:chacha20_poly1305, key, iv, plaintext, aad, @tag_size, true
) do
{ciphertext, tag} ->
{:ok, {ciphertext, iv, tag}}
error ->
{:error, {:encryption_failed, error}}
end
end
@spec decrypt(ciphertext(), key(), iv(), tag(), cipher()) ::
{:ok, plaintext()} | {:error, term()}
def decrypt(ciphertext, key, iv, tag, cipher \\ :aes_256_gcm)
def decrypt(ciphertext, key, iv, tag, :aes_256_gcm) do
aad = build_aad()
case :crypto.crypto_one_time_aead(
:aes_256_gcm, key, iv, ciphertext, aad, tag, false
) do
plaintext when is_binary(plaintext) ->
{:ok, plaintext}
:error ->
{:error, :decryption_failed}
end
end
@spec generate_key(cipher()) :: key()
def generate_key(_cipher \\ :aes_256_gcm) do
:crypto.strong_rand_bytes(@aes_key_size)
end
@spec build_aad() :: aad()
defp build_aad do
"prismatic-platform-v1"
end
endKey Management
Encryption is only as strong as its key management. The Prismatic Platform implements a layered key management strategy:
defmodule Prismatic.Security.KeyVault do
@moduledoc """
Manages encryption keys with rotation, derivation, and
secure storage. Keys are never logged or exposed in
error messages.
"""
@type key_id :: String.t()
@type key_metadata :: %{
id: key_id(),
algorithm: atom(),
created_at: DateTime.t(),
rotated_at: DateTime.t() | nil,
status: :active | :rotated | :revoked,
version: non_neg_integer()
}
@spec derive_key(String.t(), binary(), keyword()) ::
{:ok, binary()} | {:error, term()}
def derive_key(password, salt, opts \\ []) do
iterations = Keyword.get(opts, :iterations, 100_000)
key_length = Keyword.get(opts, :key_length, 32)
hash = Keyword.get(opts, :hash, :sha256)
derived =
:crypto.pbkdf2_hmac(hash, password, salt, iterations, key_length)
{:ok, derived}
rescue
error -> {:error, {:key_derivation_failed, error}}
end
@spec rotate_key(key_id()) :: {:ok, key_metadata()} | {:error, term()}
def rotate_key(key_id) do
with {:ok, current} <- fetch_key_metadata(key_id),
{:ok, new_key} <- generate_new_version(current),
:ok <- mark_previous_as_rotated(current),
:ok <- store_key_metadata(new_key) do
{:ok, new_key}
end
end
@spec fetch_key_metadata(key_id()) :: {:ok, key_metadata()} | {:error, term()}
defp fetch_key_metadata(key_id) do
case :ets.lookup(:key_vault, key_id) do
[{^key_id, metadata}] -> {:ok, metadata}
[] -> {:error, {:key_not_found, key_id}}
end
end
@spec generate_new_version(key_metadata()) :: {:ok, key_metadata()} | {:error, term()}
defp generate_new_version(current) do
{:ok, %{current |
version: current.version + 1,
rotated_at: DateTime.utc_now(),
status: :active
}}
end
@spec mark_previous_as_rotated(key_metadata()) :: :ok
defp mark_previous_as_rotated(metadata) do
:ets.insert(:key_vault, {metadata.id, %{metadata | status: :rotated}})
:ok
end
@spec store_key_metadata(key_metadata()) :: :ok
defp store_key_metadata(metadata) do
:ets.insert(:key_vault, {metadata.id, metadata})
:ok
end
endTLS Configuration
The platform enforces strict TLS configuration to ensure transport-layer encryption meets current security standards:
defmodule Prismatic.Security.TLSConfig do
@moduledoc """
Provides secure TLS configuration for all platform
network connections. Enforces TLS 1.2+ with strong
cipher suites.
"""
@type tls_opts :: keyword()
@spec client_options(String.t()) :: tls_opts()
def client_options(hostname) do
[
verify: :verify_peer,
cacerts: :public_key.cacerts_get(),
server_name_indication: String.to_charlist(hostname),
versions: [:"tlsv1.3", :"tlsv1.2"],
ciphers: preferred_ciphers(),
depth: 3,
customize_hostname_check: [
match_fun: :public_key.pkix_verify_hostname_match_fun(:https)
]
]
end
@spec preferred_ciphers() :: [tuple()]
defp preferred_ciphers do
:ssl.cipher_suites(:default, :"tlsv1.3") ++
:ssl.filter_cipher_suites(
:ssl.cipher_suites(:default, :"tlsv1.2"),
[
key_exchange: &(&1 in [:ecdhe_ecdsa, :ecdhe_rsa]),
cipher: &(&1 in [:aes_256_gcm, :aes_128_gcm, :chacha20_poly1305])
]
)
end
endImplementation in Prismatic Platform
The Prismatic Platform implements encryption across four distinct layers, each addressing specific threat models:
Layer 1: Transport Encryption (TLS)
All network communication -- between the platform and external services, between the web interface and browsers, and between internal components when deployed across nodes -- uses TLS 1.2 or 1.3. The platform's Cowboy/Bandit web servers are configured with modern cipher suites, and certificate management is automated through ACME (Let's Encrypt) integration on Fly.io.
Layer 2: Encryption at Rest
Sensitive data stored in PostgreSQL uses column-level encryption through Cloak.Ecto, which provides transparent encryption/decryption at the Ecto schema layer. ETS tables containing sensitive runtime state use application-level encryption before storage. See Encryption at Rest for detailed implementation.
Layer 3: Credential Management
API keys, tokens, and secrets are managed through the credential management system, which encrypts credentials before storage and provides controlled access through a vault abstraction. Credentials are never logged, never included in error messages, and never exposed through the API without explicit authorization.
Layer 4: Certificate Transparency Monitoring
The certificate transparency monitoring subsystem within Prismatic Perimeter tracks certificate issuance for monitored domains, detecting unauthorized certificate creation that could enable man-in-the-middle attacks.
Encryption in the Quality System
The platform's enforcement policy includes encryption-specific rules:
- Hardcoded secrets are blocked by pre-commit hooks (Phase 3)
- Credential files (
.env,credentials.json) are in.gitignore - All HTTP connections in production code must use HTTPS (enforced by Credo checks)
- Weak cipher suites and deprecated TLS versions are flagged during code review
Comparison with Alternatives
Encryption Approaches
| Approach | Strength | Weakness | Prismatic Usage |
|---|---|---|---|
| AES-256-GCM | Industry standard, hardware acceleration | Key management complexity | Primary symmetric cipher |
| ChaCha20-Poly1305 | Fast on ARM/mobile, constant-time | Less hardware support | TLS fallback cipher |
| RSA-4096 | Well-understood, wide support | Large keys, slow | Legacy compatibility |
| X25519 + Ed25519 | Small keys, fast, modern | Newer, less audited | TLS 1.3 key exchange |
| Application-level | Granular control, defense in depth | Implementation complexity | Column-level via Cloak |
| Database-level | Transparent to application | Coarser granularity | PostgreSQL TDE |
Language-Level Comparison
| Language/Runtime | Encryption Support | Prismatic Advantage |
|---|---|---|
| Elixir/BEAM | :crypto (OpenSSL wrapper) | Direct C-level performance, OTP integration |
| Node.js | crypto module | Similar OpenSSL wrapper but single-threaded |
| Go | crypto/ stdlib | Strong stdlib but no BEAM fault isolation |
| Rust | ring, rustls | Memory-safe but more manual key lifecycle |
| Python | cryptography | Comprehensive but GIL-limited for concurrent ops |
The BEAM's advantage is not in raw encryption speed (all wrap the same OpenSSL primitives) but in the ability to manage thousands of concurrent encrypted connections through lightweight processes with automatic fault recovery.
Best Practices
Key Management
-
Never Hardcode Keys: Use environment variables or vault services. The Prismatic pre-commit hook detects hardcoded secrets and blocks the commit.
-
Rotate Keys Regularly: Implement automated key rotation with overlap periods where both old and new keys are valid, allowing in-flight data to be decrypted with the previous key.
-
Use Authenticated Encryption: Always prefer AEAD modes (AES-GCM, ChaCha20-Poly1305) that provide both confidentiality and integrity. Unauthenticated encryption is vulnerable to padding oracle attacks.
-
Separate Keys by Purpose: Use distinct keys for transport encryption, data-at-rest encryption, and credential encryption. Compromise of one key should not expose all data.
-
Secure Key Derivation: When deriving keys from passwords, use memory-hard KDFs (Argon2id) with appropriate parameters. Never use raw hashing (SHA-256) as a KDF.
Implementation
-
Use Established Libraries: Never implement custom cryptographic algorithms. Use
:crypto,Plug.Crypto, andCloakfor Elixir encryption needs. -
Generate IVs/Nonces Properly: Use
:crypto.strong_rand_bytes/1for initialization vectors. Never reuse an IV with the same key -- this catastrophically breaks AES-GCM security. -
Handle Errors Explicitly: Decryption failures should return
{:error, reason}, never raise exceptions that might leak key material or plaintext in stack traces. -
Log Encrypted Operations, Not Data: Log that encryption occurred, the algorithm used, and the key ID -- never log the key, plaintext, or full ciphertext.
Common Pitfalls
ECB Mode Usage
Electronic Codebook (ECB) mode encrypts each block independently, producing identical ciphertext for identical plaintext blocks. This leaks patterns in the data. Always use authenticated modes like GCM or CCM.
Nonce Reuse
Reusing a nonce (number used once) with the same key in AES-GCM reveals the XOR of the two plaintexts and allows authentication tag forgery. This is a catastrophic failure. Use random nonces from :crypto.strong_rand_bytes/1 and ensure nonce uniqueness through counters or random generation with sufficient entropy.
Insufficient Key Entropy
Deriving encryption keys from short passwords without proper KDF produces weak keys vulnerable to brute-force attacks. Always use Argon2id, bcrypt, or PBKDF2 with high iteration counts.
Timing Side Channels
Comparing MACs or authentication tags using standard == is vulnerable to timing attacks. Use constant-time comparison functions like Plug.Crypto.secure_compare/2 or :crypto.hash_equals/2.
Encrypting Without Authenticating
Encryption without authentication (MAC) allows attackers to modify ciphertext without detection. This enables chosen-ciphertext attacks. AEAD ciphers (AES-GCM, ChaCha20-Poly1305) solve this by combining encryption and authentication.
Ignoring Certificate Validation
Disabling TLS certificate verification (setting verify: :verify_none) to "fix" connection errors eliminates the protection TLS provides against man-in-the-middle attacks. Always verify certificates against a trusted CA bundle.
Use Cases
Secure OSINT Data Collection
When Prismatic's 120 OSINT tools collect data from external sources, all connections use TLS with certificate pinning for critical sources. Collected intelligence data is encrypted at rest in PostgreSQL, ensuring that database compromise does not expose raw intelligence.
Multi-Tenant Data Isolation
In scenarios where the platform processes data for multiple clients, per-tenant encryption keys ensure that data from one tenant cannot be decrypted by another, even by a database administrator with direct table access.
Credential Rotation in Agent Operations
The 530+ AIAD agents that interact with external services require API credentials. The credential vault encrypts these credentials and supports zero-downtime rotation, allowing agents to seamlessly transition to new credentials without service interruption.
Compliance-Driven Encryption
NIS2 and ZKB compliance requirements mandate encryption for personal data processing. The Prismatic Perimeter's compliance assessment module verifies that monitored organizations meet these encryption requirements, while the platform itself demonstrates compliance through its own encryption implementation.
Related Concepts
Encryption integrates with numerous security and platform concepts:
- Encryption at Rest -- Applying encryption to stored data, complementing transport encryption
- TLS -- Transport Layer Security protocol for encrypted network communication
- Authentication -- Verifying identity, often using cryptographic mechanisms
- Security Operations -- The operational framework for managing encryption infrastructure
- Certificate Transparency -- Monitoring certificate issuance to prevent PKI abuse
- Credential Management -- Secure storage and rotation of encrypted credentials
- Authorization -- Access control that determines who can decrypt what data
- Audit Trail -- Logging encryption operations for compliance and forensics
- Policy -- Governance rules that mandate encryption standards
- Attack Surface -- The exposure that encryption helps minimize
See Also
- Glossary Index -- Complete glossary of Prismatic Platform terminology
- Enforcement Policy -- How encryption standards are enforced across the platform
- No Mercy No Doubts -- The doctrine that mandates zero tolerance for encryption shortcuts
- Quality Gate -- Automated checks that verify encryption compliance
- Formal Verification -- Proving encryption implementations are correct
Connect & Contribute
Created by Tomas Korcak (korczis) | Open Source under GHL