-module(kryptos@crypto). -compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]). -define(FILEPATH, "src/kryptos/crypto.gleam"). -export([hash/2, hmac/3, hkdf/5, pbkdf2/5, random_bytes/1, random_uuid/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( " The crypto module provides cryptographic primitives.\n" "\n" " ## Example\n" "\n" " ```gleam\n" " import kryptos/crypto\n" "\n" " // Generate 32 random bytes (suitable for a 256-bit key)\n" " let key = crypto.random_bytes(32)\n" " ```\n" ). -file("src/kryptos/crypto.gleam", 33). ?DOC( " Computes the hash digest of input data in one call.\n" "\n" " ## Parameters\n" " - `algorithm`: The hash algorithm to use\n" " - `data`: The data to hash\n" "\n" " ## Returns\n" " - `Ok(BitArray)` - The computed hash digest\n" " - `Error(Nil)` - If the hash algorithm is not supported by the runtime\n" ). -spec hash(kryptos@hash:hash_algorithm(), bitstring()) -> {ok, bitstring()} | {error, nil}. hash(Algorithm, Data) -> gleam@result:'try'( kryptos_ffi:hash_new(Algorithm), fun(Hasher) -> _pipe = Hasher, _pipe@1 = crypto:hash_update(_pipe, Data), _pipe@2 = crypto:hash_final(_pipe@1), {ok, _pipe@2} end ). -file("src/kryptos/crypto.gleam", 51). ?DOC( " Computes the HMAC of input data in one call.\n" "\n" " ## Parameters\n" " - `algorithm`: The hash algorithm to use for the HMAC\n" " - `key`: The secret key for authentication\n" " - `data`: The data to authenticate\n" "\n" " ## Returns\n" " - `Ok(BitArray)` - The computed message authentication code\n" " - `Error(Nil)` - If the hash algorithm is not supported\n" ). -spec hmac(kryptos@hash:hash_algorithm(), bitstring(), bitstring()) -> {ok, bitstring()} | {error, nil}. hmac(Algorithm, Key, Data) -> gleam@result:'try'( kryptos@hmac:new(Algorithm, Key), fun(Hmac) -> _pipe = Hmac, _pipe@1 = crypto:mac_update(_pipe, Data), _pipe@2 = crypto:mac_final(_pipe@1), {ok, _pipe@2} end ). -file("src/kryptos/crypto.gleam", 79). ?DOC( " Derives key material using HKDF (RFC 5869).\n" "\n" " HKDF combines an extract-then-expand approach to derive cryptographically\n" " strong key material from input key material.\n" "\n" " ## Parameters\n" " - `algorithm`: The hash algorithm to use (must be HMAC-compatible)\n" " - `input`: Input key material (IKM) - the source keying material\n" " - `salt`: Optional salt value (None uses hash-length zeros per RFC 5869)\n" " - `info`: Context and application specific information\n" " - `length`: Desired output length in bytes (max: 255 * hash_length)\n" "\n" " ## Returns\n" " - `Ok(BitArray)` - The derived key material of the requested length\n" " - `Error(Nil)` - If the algorithm is not supported or length exceeds maximum\n" ). -spec hkdf( kryptos@hash:hash_algorithm(), bitstring(), gleam@option:option(bitstring()), bitstring(), integer() ) -> {ok, bitstring()} | {error, nil}. hkdf(Algorithm, Ikm, Salt, Info, Length) -> Hash_len = kryptos@hash:byte_size(Algorithm), Max_length = 255 * Hash_len, Salt_bytes = gleam@option:lazy_unwrap( Salt, fun() -> _pipe = gleam@list:repeat(<<0>>, Hash_len), gleam_stdlib:bit_array_concat(_pipe) end ), case {kryptos@hmac:supported_hash(Algorithm), Length > 0, Length =< Max_length} of {true, true, true} -> kryptos@internal@hkdf:do_derive( Algorithm, Ikm, Salt_bytes, Info, Length ); {_, _, _} -> {error, nil} end. -file("src/kryptos/crypto.gleam", 115). ?DOC( " Derives key material from a password using PBKDF2 (RFC 8018).\n" "\n" " PBKDF2 applies a pseudorandom function (HMAC) to derive keys from passwords.\n" " It is designed to be computationally expensive to resist brute-force attacks.\n" "\n" " ## Parameters\n" " - `algorithm`: The hash algorithm to use for HMAC (must be HMAC-compatible).\n" " SHA-256 or stronger is recommended; MD5 and SHA-1 are weak for password hashing.\n" " - `password`: The password to derive the key from\n" " - `salt`: A random salt value (should be unique per password)\n" " - `iterations`: Number of iterations (higher = slower but more secure)\n" " - `length`: Desired output length in bytes\n" "\n" " ## Returns\n" " - `Ok(BitArray)` - The derived key material of the requested length\n" " - `Error(Nil)` - If the algorithm is not supported, iterations <= 0, or length <= 0\n" ). -spec pbkdf2( kryptos@hash:hash_algorithm(), bitstring(), bitstring(), integer(), integer() ) -> {ok, bitstring()} | {error, nil}. pbkdf2(Algorithm, Password, Salt, Iterations, Length) -> case {kryptos@hmac:supported_hash(Algorithm), Iterations > 0, Length > 0} of {true, true, true} -> kryptos_ffi:pbkdf2_derive( Algorithm, Password, Salt, Iterations, Length ); {_, _, _} -> {error, nil} end. -file("src/kryptos/crypto.gleam", 140). ?DOC( " Generates cryptographically secure random bytes using the platform's\n" " cryptographically secure random number generator.\n" "\n" " ## Parameters\n" " - `length`: The number of random bytes to generate. If negative, returns\n" " an empty `BitArray`.\n" "\n" " ## Returns\n" " A `BitArray` containing the generated random bytes.\n" ). -spec random_bytes(integer()) -> bitstring(). random_bytes(Length) -> kryptos_ffi:random_bytes(Length). -file("src/kryptos/crypto.gleam", 147). ?DOC( " Generates a cryptographically secure random UUID v4.\n" "\n" " ## Returns\n" " A `String` containing a UUID v4.\n" ). -spec random_uuid() -> binary(). random_uuid() -> {A@1, B@1, C_raw@1, D_raw@1, E@1} = case kryptos_ffi:random_bytes(16) of <> -> {A, B, C_raw, D_raw, E}; _assert_fail -> erlang:error(#{gleam_error => let_assert, message => <<"Pattern match failed, no pattern matched the value."/utf8>>, file => <>, module => <<"kryptos/crypto"/utf8>>, function => <<"random_uuid"/utf8>>, line => 148, value => _assert_fail, start => 4773, 'end' => 4843, pattern_start => 4784, pattern_end => 4824}) end, C = erlang:'bor'(erlang:'band'(C_raw@1, 16#0FFF), 16#4000), D = erlang:'bor'(erlang:'band'(D_raw@1, 16#3FFF), 16#8000), Uuid = <<<<<<<<<<<<<<<<(gleam@string:pad_start( gleam@int:to_base16(A@1), 8, <<"0"/utf8>> ))/binary, "-"/utf8>>/binary, (gleam@string:pad_start( gleam@int:to_base16(B@1), 4, <<"0"/utf8>> ))/binary>>/binary, "-"/utf8>>/binary, (gleam@string:pad_start( gleam@int:to_base16(C), 4, <<"0"/utf8>> ))/binary>>/binary, "-"/utf8>>/binary, (gleam@string:pad_start(gleam@int:to_base16(D), 4, <<"0"/utf8>>))/binary>>/binary, "-"/utf8>>/binary, (gleam@string:pad_start(gleam@int:to_base16(E@1), 12, <<"0"/utf8>>))/binary>>, string:lowercase(Uuid).