kryptos/block
Block cipher implementations and modes of operation.
This module provides AES block ciphers and modes of operation (ECB, CBC, CTR).
IMPORTANT SECURITY WARNING:
ECB, CBC, and CTR modes do NOT provide authentication. An attacker can modify
ciphertext without detection. For most applications, you should use
authenticated encryption modes like AES-GCM or ChaCha20-Poly1305
from the kryptos/aead module instead.
Use these modes only when:
- Interoperating with legacy systems that require them
- Implementing higher-level protocols that provide their own authentication
- You fully understand the security implications
Modes Overview
- ECB (Electronic Codebook): Encrypts each block independently. INSECURE for most uses - reveals patterns in data. Only use for single-block encryption or specific legacy requirements.
- CBC (Cipher Block Chaining): Each block XORed with previous ciphertext. Requires random IV per encryption. Uses PKCS7 padding automatically.
- CTR (Counter): Converts block cipher to stream cipher. Nonce reuse is catastrophic - NEVER reuse a nonce with the same key.
Example
import kryptos/block
import kryptos/crypto
// CBC encryption with random IV
let assert Ok(cipher) = block.aes_256(crypto.random_bytes(32))
let assert Ok(ctx) = block.cbc(cipher, iv: crypto.random_bytes(16))
let assert Ok(ciphertext) = block.encrypt(ctx, <<"secret":utf8>>)
let assert Ok(decrypted) = block.decrypt(ctx, ciphertext)
// decrypted == <<"secret":utf8>>
Types
A block cipher with its associated key material.
pub type BlockCipher {
Aes(key_size: Int, key: BitArray)
}
Constructors
-
Aes(key_size: Int, key: BitArray)AES block cipher with the specified key size and key.
Context for block cipher modes of operation.
Note: While the variants are public for pattern matching, direct construction is not recommended. Use the provided constructor functions which validate parameters:
ecb()for ECB modecbc()for CBC modectr()for CTR mode
pub type CipherContext {
Ecb(cipher: BlockCipher)
Cbc(cipher: BlockCipher, iv: BitArray)
Ctr(cipher: BlockCipher, nonce: BitArray)
}
Constructors
-
Ecb(cipher: BlockCipher)Electronic Codebook mode.
-
Cbc(cipher: BlockCipher, iv: BitArray)Cipher Block Chaining mode with PKCS7 padding.
-
Ctr(cipher: BlockCipher, nonce: BitArray)Counter mode (streaming cipher).
Values
pub fn aes_128(key: BitArray) -> Result(BlockCipher, Nil)
Creates a new AES-128 block cipher with the given key.
Parameters
key: A 16-byte key for AES-128
Returns
Ok(BlockCipher)if the key is exactly 16 bytesError(Nil)if the key size is incorrect
pub fn aes_192(key: BitArray) -> Result(BlockCipher, Nil)
Creates a new AES-192 block cipher with the given key.
Parameters
key: A 24-byte key for AES-192
Returns
Ok(BlockCipher)if the key is exactly 24 bytesError(Nil)if the key size is incorrect
pub fn aes_256(key: BitArray) -> Result(BlockCipher, Nil)
Creates a new AES-256 block cipher with the given key.
Parameters
key: A 32-byte key for AES-256
Returns
Ok(BlockCipher)if the key is exactly 32 bytesError(Nil)if the key size is incorrect
pub fn block_size(cipher: BlockCipher) -> Int
Returns the block size in bytes for a block cipher.
Parameters
cipher: The block cipher to get the block size for
Returns
The block size in bytes (16 for AES).
pub fn cbc(
cipher: BlockCipher,
iv iv: BitArray,
) -> Result(CipherContext, Nil)
Creates a CBC mode context with the given cipher and IV.
Parameters
cipher: The block cipher to useiv: A 16-byte initialization vector (must be random and unique per encryption)
Returns
Ok(CipherContext)if the IV is exactly 16 bytesError(Nil)if the IV size is incorrect
pub fn ctr(
cipher: BlockCipher,
nonce nonce: BitArray,
) -> Result(CipherContext, Nil)
Creates a CTR mode context with the given cipher and nonce.
SECURITY WARNING: Nonce reuse is catastrophic in CTR mode. NEVER reuse a nonce with the same key.
Parameters
cipher: The block cipher to usenonce: A 16-byte nonce (must be unique per encryption with the same key)
Returns
Ok(CipherContext)if the nonce is exactly 16 bytesError(Nil)if the nonce size is incorrect
pub fn decrypt(
ctx: CipherContext,
ciphertext: BitArray,
) -> Result(BitArray, Nil)
Decrypts ciphertext using the cipher mode.
Parameters
ctx: The cipher context (includes IV/nonce for CBC/CTR)ciphertext: The encrypted data
Returns
Ok(plaintext)on successError(Nil)if IV/nonce size is incorrect, ciphertext size is invalid, or padding is invalid
Notes
- ECB: No IV required
- CBC: Automatically removes PKCS7 padding; returns error if padding is invalid
- CTR: No padding; ciphertext size equals plaintext size
pub fn ecb(cipher: BlockCipher) -> CipherContext
Creates an ECB mode context for the given cipher.
SECURITY WARNING: ECB mode is insecure for most use cases. Identical plaintext blocks produce identical ciphertext blocks, revealing patterns in the data.
Parameters
cipher: The block cipher to use
Returns
An ECB cipher context.
pub fn encrypt(
ctx: CipherContext,
plaintext: BitArray,
) -> Result(BitArray, Nil)
Encrypts plaintext using the cipher mode.
Parameters
ctx: The cipher context (includes IV/nonce for CBC/CTR)plaintext: The data to encrypt
Returns
Ok(ciphertext)on successError(Nil)if IV/nonce size is incorrect
Notes
- ECB: No IV required
- CBC: Automatically applies PKCS7 padding; ciphertext may be larger than plaintext
- CTR: No padding needed; ciphertext is same size as plaintext
pub fn key_size(cipher: BlockCipher) -> Int
Returns the key size in bytes for a block cipher.
Parameters
cipher: The block cipher to get the key size for
Returns
The key size in bytes (16, 24, or 32 for AES).
pub fn unwrap(
cipher: BlockCipher,
ciphertext: BitArray,
) -> Result(BitArray, Nil)
Unwraps key material using AES Key Wrap (RFC 3394).
Parameters
cipher: The AES block cipher used as the key-encryption key (KEK)ciphertext: The wrapped key material (must be a multiple of 8 bytes, minimum 24 bytes)
Returns
Ok(plaintext)where plaintext is ciphertext.size - 8 bytesError(Nil)if ciphertext size is invalid or integrity check fails
Example
import kryptos/block
let assert Ok(kek) = block.aes_256(kek_bytes)
let assert Ok(unwrapped) = block.unwrap(kek, wrapped_key)
pub fn wrap(
cipher: BlockCipher,
plaintext: BitArray,
) -> Result(BitArray, Nil)
Wraps key material using AES Key Wrap (RFC 3394).
Key wrapping is used to protect cryptographic keys when they need to be transported or stored. Unlike general encryption, key wrapping:
- Does not require an IV (uses a default IV internally)
- Provides integrity protection
- Output is always 8 bytes larger than input
Parameters
cipher: The AES block cipher used as the key-encryption key (KEK)plaintext: The key material to wrap (must be a multiple of 8 bytes, minimum 16 bytes)
Returns
Ok(ciphertext)where ciphertext is plaintext.size + 8 bytesError(Nil)if plaintext size is invalid
Example
import kryptos/block
import kryptos/crypto
let assert Ok(kek) = block.aes_256(crypto.random_bytes(32))
let key_to_wrap = crypto.random_bytes(32)
let assert Ok(wrapped) = block.wrap(kek, key_to_wrap)