-module(kryptos@block). -compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]). -define(FILEPATH, "src/kryptos/block.gleam"). -export([key_size/1, block_size/1, aes_128/1, aes_192/1, aes_256/1, ecb/1, cbc/2, ctr/2, encrypt/2, decrypt/2, cipher_name/1, cipher_key/1, cipher_iv/1]). -export_type([block_cipher/0, cipher_context/0]). -if(?OTP_RELEASE >= 27). -define(MODULEDOC(Str), -moduledoc(Str)). -define(DOC(Str), -doc(Str)). -else. -define(MODULEDOC(Str), -compile([])). -define(DOC(Str), -compile([])). -endif. ?MODULEDOC( " Block cipher implementations and modes of operation.\n" "\n" " This module provides AES block ciphers and modes of operation (ECB, CBC, CTR).\n" "\n" " **IMPORTANT SECURITY WARNING:**\n" " ECB, CBC, and CTR modes do NOT provide authentication. An attacker can modify\n" " ciphertext without detection. For most applications, you should use\n" " authenticated encryption modes like AES-GCM or ChaCha20-Poly1305\n" " from the `kryptos/aead` module instead.\n" "\n" " Use these modes only when:\n" " - Interoperating with legacy systems that require them\n" " - Implementing higher-level protocols that provide their own authentication\n" " - You fully understand the security implications\n" "\n" " ## Modes Overview\n" "\n" " - **ECB (Electronic Codebook):** Encrypts each block independently.\n" " INSECURE for most uses - reveals patterns in data. Only use for\n" " single-block encryption or specific legacy requirements.\n" " - **CBC (Cipher Block Chaining):** Each block XORed with previous ciphertext.\n" " Requires random IV per encryption. Uses PKCS7 padding automatically.\n" " - **CTR (Counter):** Converts block cipher to stream cipher.\n" " Nonce reuse is catastrophic - NEVER reuse a nonce with the same key.\n" "\n" " ## Example\n" "\n" " ```gleam\n" " import kryptos/block\n" " import kryptos/crypto\n" "\n" " // CBC encryption with random IV\n" " let assert Ok(cipher) = block.aes_256(crypto.random_bytes(32))\n" " let assert Ok(ctx) = block.cbc(cipher, iv: crypto.random_bytes(16))\n" " let assert Ok(ciphertext) = block.encrypt(ctx, <<\"secret\":utf8>>)\n" " let assert Ok(decrypted) = block.decrypt(ctx, ciphertext)\n" " // decrypted == <<\"secret\":utf8>>\n" " ```\n" ). -type block_cipher() :: {aes, integer(), bitstring()}. -type cipher_context() :: {ecb, block_cipher()} | {cbc, block_cipher(), bitstring()} | {ctr, block_cipher(), bitstring()}. -file("src/kryptos/block.gleam", 76). ?DOC( " Returns the key size in bytes for a block cipher.\n" "\n" " ## Parameters\n" " - `cipher`: The block cipher to get the key size for\n" "\n" " ## Returns\n" " The key size in bytes (16, 24, or 32 for AES).\n" ). -spec key_size(block_cipher()) -> integer(). key_size(Cipher) -> case Cipher of {aes, Key_size, _} -> Key_size end. -file("src/kryptos/block.gleam", 89). ?DOC( " Returns the block size in bytes for a block cipher.\n" "\n" " ## Parameters\n" " - `cipher`: The block cipher to get the block size for\n" "\n" " ## Returns\n" " The block size in bytes (16 for AES).\n" ). -spec block_size(block_cipher()) -> integer(). block_size(Cipher) -> case Cipher of {aes, _, _} -> 16 end. -file("src/kryptos/block.gleam", 103). ?DOC( " Creates a new AES-128 block cipher with the given key.\n" "\n" " ## Parameters\n" " - `key`: A 16-byte key for AES-128\n" "\n" " ## Returns\n" " - `Ok(BlockCipher)` if the key is exactly 16 bytes\n" " - `Error(Nil)` if the key size is incorrect\n" ). -spec aes_128(bitstring()) -> {ok, block_cipher()} | {error, nil}. aes_128(Key) -> case erlang:byte_size(Key) =:= 16 of true -> {ok, {aes, 128, Key}}; false -> {error, nil} end. -file("src/kryptos/block.gleam", 118). ?DOC( " Creates a new AES-192 block cipher with the given key.\n" "\n" " ## Parameters\n" " - `key`: A 24-byte key for AES-192\n" "\n" " ## Returns\n" " - `Ok(BlockCipher)` if the key is exactly 24 bytes\n" " - `Error(Nil)` if the key size is incorrect\n" ). -spec aes_192(bitstring()) -> {ok, block_cipher()} | {error, nil}. aes_192(Key) -> case erlang:byte_size(Key) =:= 24 of true -> {ok, {aes, 192, Key}}; false -> {error, nil} end. -file("src/kryptos/block.gleam", 133). ?DOC( " Creates a new AES-256 block cipher with the given key.\n" "\n" " ## Parameters\n" " - `key`: A 32-byte key for AES-256\n" "\n" " ## Returns\n" " - `Ok(BlockCipher)` if the key is exactly 32 bytes\n" " - `Error(Nil)` if the key size is incorrect\n" ). -spec aes_256(bitstring()) -> {ok, block_cipher()} | {error, nil}. aes_256(Key) -> case erlang:byte_size(Key) =:= 32 of true -> {ok, {aes, 256, Key}}; false -> {error, nil} end. -file("src/kryptos/block.gleam", 151). ?DOC( " Creates an ECB mode context for the given cipher.\n" "\n" " **SECURITY WARNING:** ECB mode is insecure for most use cases.\n" " Identical plaintext blocks produce identical ciphertext blocks,\n" " revealing patterns in the data.\n" "\n" " ## Parameters\n" " - `cipher`: The block cipher to use\n" "\n" " ## Returns\n" " An ECB cipher context.\n" ). -spec ecb(block_cipher()) -> cipher_context(). ecb(Cipher) -> {ecb, Cipher}. -file("src/kryptos/block.gleam", 164). ?DOC( " Creates a CBC mode context with the given cipher and IV.\n" "\n" " ## Parameters\n" " - `cipher`: The block cipher to use\n" " - `iv`: A 16-byte initialization vector (must be random and unique per encryption)\n" "\n" " ## Returns\n" " - `Ok(CipherContext)` if the IV is exactly 16 bytes\n" " - `Error(Nil)` if the IV size is incorrect\n" ). -spec cbc(block_cipher(), bitstring()) -> {ok, cipher_context()} | {error, nil}. cbc(Cipher, Iv) -> case erlang:byte_size(Iv) =:= 16 of true -> {ok, {cbc, Cipher, Iv}}; false -> {error, nil} end. -file("src/kryptos/block.gleam", 183). ?DOC( " Creates a CTR mode context with the given cipher and nonce.\n" "\n" " **SECURITY WARNING:** Nonce reuse is catastrophic in CTR mode.\n" " NEVER reuse a nonce with the same key.\n" "\n" " ## Parameters\n" " - `cipher`: The block cipher to use\n" " - `nonce`: A 16-byte nonce (must be unique per encryption with the same key)\n" "\n" " ## Returns\n" " - `Ok(CipherContext)` if the nonce is exactly 16 bytes\n" " - `Error(Nil)` if the nonce size is incorrect\n" ). -spec ctr(block_cipher(), bitstring()) -> {ok, cipher_context()} | {error, nil}. ctr(Cipher, Nonce) -> case erlang:byte_size(Nonce) =:= 16 of true -> {ok, {ctr, Cipher, Nonce}}; false -> {error, nil} end. -file("src/kryptos/block.gleam", 214). -spec validate_iv(cipher_context()) -> boolean(). validate_iv(Ctx) -> case Ctx of {ecb, _} -> true; {cbc, _, Iv} -> erlang:byte_size(Iv) =:= 16; {ctr, _, Nonce} -> erlang:byte_size(Nonce) =:= 16 end. -file("src/kryptos/block.gleam", 207). ?DOC( " Encrypts plaintext using the cipher mode.\n" "\n" " ## Parameters\n" " - `ctx`: The cipher context (includes IV/nonce for CBC/CTR)\n" " - `plaintext`: The data to encrypt\n" "\n" " ## Returns\n" " - `Ok(ciphertext)` on success\n" " - `Error(Nil)` if IV/nonce size is incorrect\n" "\n" " ## Notes\n" " - ECB: No IV required\n" " - CBC: Automatically applies PKCS7 padding; ciphertext may be larger than plaintext\n" " - CTR: No padding needed; ciphertext is same size as plaintext\n" ). -spec encrypt(cipher_context(), bitstring()) -> {ok, bitstring()} | {error, nil}. encrypt(Ctx, Plaintext) -> case validate_iv(Ctx) of true -> kryptos_ffi:block_cipher_encrypt(Ctx, Plaintext); false -> {error, nil} end. -file("src/kryptos/block.gleam", 240). ?DOC( " Decrypts ciphertext using the cipher mode.\n" "\n" " ## Parameters\n" " - `ctx`: The cipher context (includes IV/nonce for CBC/CTR)\n" " - `ciphertext`: The encrypted data\n" "\n" " ## Returns\n" " - `Ok(plaintext)` on success\n" " - `Error(Nil)` if IV/nonce size is incorrect, ciphertext size is invalid, or padding is invalid\n" "\n" " ## Notes\n" " - ECB: No IV required\n" " - CBC: Automatically removes PKCS7 padding; returns error if padding is invalid\n" " - CTR: No padding; ciphertext size equals plaintext size\n" ). -spec decrypt(cipher_context(), bitstring()) -> {ok, bitstring()} | {error, nil}. decrypt(Ctx, Ciphertext) -> case validate_iv(Ctx) of true -> kryptos_ffi:block_cipher_decrypt(Ctx, Ciphertext); false -> {error, nil} end. -file("src/kryptos/block.gleam", 255). ?DOC(false). -spec cipher_name(cipher_context()) -> binary(). cipher_name(Ctx) -> case Ctx of {ecb, Cipher} -> case Cipher of {aes, Key_size, _} -> <<<<"aes-"/utf8, (erlang:integer_to_binary(Key_size))/binary>>/binary, "-ecb"/utf8>> end; {cbc, Cipher@1, _} -> case Cipher@1 of {aes, Key_size@1, _} -> <<<<"aes-"/utf8, (erlang:integer_to_binary(Key_size@1))/binary>>/binary, "-cbc"/utf8>> end; {ctr, Cipher@2, _} -> case Cipher@2 of {aes, Key_size@2, _} -> <<<<"aes-"/utf8, (erlang:integer_to_binary(Key_size@2))/binary>>/binary, "-ctr"/utf8>> end end. -file("src/kryptos/block.gleam", 273). ?DOC(false). -spec cipher_key(cipher_context()) -> bitstring(). cipher_key(Ctx) -> case Ctx of {ecb, Cipher} -> case Cipher of {aes, _, Key} -> Key end; {cbc, Cipher, _} -> case Cipher of {aes, _, Key} -> Key end; {ctr, Cipher, _} -> case Cipher of {aes, _, Key} -> Key end end. -file("src/kryptos/block.gleam", 283). ?DOC(false). -spec cipher_iv(cipher_context()) -> bitstring(). cipher_iv(Ctx) -> case Ctx of {ecb, _} -> <<>>; {cbc, _, Iv} -> Iv; {ctr, _, Nonce} -> Nonce end.