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:

Modes Overview

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 mode
  • cbc() for CBC mode
  • ctr() 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 bytes
  • Error(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 bytes
  • Error(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 bytes
  • Error(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 use
  • iv: A 16-byte initialization vector (must be random and unique per encryption)

Returns

  • Ok(CipherContext) if the IV is exactly 16 bytes
  • Error(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 use
  • nonce: A 16-byte nonce (must be unique per encryption with the same key)

Returns

  • Ok(CipherContext) if the nonce is exactly 16 bytes
  • Error(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 success
  • Error(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 success
  • Error(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 bytes
  • Error(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 bytes
  • Error(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)
Search Document