Packages

Cryptography library for Gleam targeting Erlang and JavaScript

Current section

Files

Jump to
kryptos src kryptos@hmac.erl
Raw

src/kryptos@hmac.erl

-module(kryptos@hmac).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/kryptos/hmac.gleam").
-export([supported_hash/1, new/2, update/2, final/1, verify/4]).
-export_type([hmac/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(
" Hash-based Message Authentication Code (HMAC).\n"
"\n"
" HMAC provides message authentication using a cryptographic hash function\n"
" combined with a secret key. Use it to verify both data integrity and\n"
" authenticity.\n"
"\n"
" ## Example\n"
"\n"
" ```gleam\n"
" import kryptos/hmac\n"
" import kryptos/hash\n"
"\n"
" let assert Ok(h) = hmac.new(hash.Sha256, <<\"secret key\":utf8>>)\n"
" let mac = h |> hmac.update(<<\"hello\":utf8>>) |> hmac.final()\n"
" ```\n"
).
-type hmac() :: any().
-file("src/kryptos/hmac.gleam", 28).
?DOC(
" Checks if a hash algorithm is supported for HMAC operations.\n"
"\n"
" ## Parameters\n"
" - `algorithm`: The hash algorithm to check\n"
"\n"
" ## Returns\n"
" `True` if the algorithm is supported, `False` otherwise.\n"
).
-spec supported_hash(kryptos@hash:hash_algorithm()) -> boolean().
supported_hash(Algorithm) ->
case Algorithm of
md5 ->
true;
sha1 ->
true;
sha256 ->
true;
sha384 ->
true;
sha512 ->
true;
sha512x224 ->
true;
sha512x256 ->
true;
_ ->
false
end.
-file("src/kryptos/hmac.gleam", 58).
?DOC(
" Creates a new HMAC for incremental authentication.\n"
"\n"
" Use this when you need to authenticate data in chunks, such as when streaming\n"
" or when the full input isn't available at once.\n"
"\n"
" ## Parameters\n"
" - `algorithm`: The hash algorithm to use for the HMAC\n"
" - `key`: The secret key for authentication\n"
"\n"
" ## Returns\n"
" - `Ok(Hmac)` - A new HMAC ready to accept input data\n"
" - `Error(Nil)` - If the hash algorithm is not supported\n"
).
-spec new(kryptos@hash:hash_algorithm(), bitstring()) -> {ok, hmac()} |
{error, nil}.
new(Algorithm, Key) ->
case supported_hash(Algorithm) of
true ->
kryptos_ffi:hmac_new(Algorithm, Key);
false ->
{error, nil}
end.
-file("src/kryptos/hmac.gleam", 81).
?DOC(
" Adds data to an in-progress HMAC computation.\n"
"\n"
" Can be called multiple times to incrementally authenticate data.\n"
"\n"
" ## Parameters\n"
" - `hmac`: The HMAC to update\n"
" - `data`: The data to add to the authentication\n"
"\n"
" ## Returns\n"
" The updated HMAC.\n"
).
-spec update(hmac(), bitstring()) -> hmac().
update(Hmac, Data) ->
crypto:mac_update(Hmac, Data).
-file("src/kryptos/hmac.gleam", 94).
?DOC(
" Finalizes the HMAC computation and returns the authentication code.\n"
"\n"
" After calling this function, the HMAC should not be reused.\n"
"\n"
" ## Parameters\n"
" - `hmac`: The HMAC to finalize\n"
"\n"
" ## Returns\n"
" A `BitArray` containing the computed message authentication code.\n"
).
-spec final(hmac()) -> bitstring().
final(Hmac) ->
crypto:mac_final(Hmac).
-file("src/kryptos/hmac.gleam", 111).
?DOC(
" Verifies that a MAC matches the expected value using constant-time comparison.\n"
"\n"
" This function computes the HMAC and compares it to the expected value in\n"
" constant time to prevent timing attacks.\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"
" - `expected`: The expected MAC value to compare against\n"
"\n"
" ## Returns\n"
" - `Ok(True)` - If the computed HMAC matches the expected value\n"
" - `Ok(False)` - If the computed HMAC does not match\n"
" - `Error(Nil)` - If the hash algorithm is not supported\n"
).
-spec verify(
kryptos@hash:hash_algorithm(),
bitstring(),
bitstring(),
bitstring()
) -> {ok, boolean()} | {error, nil}.
verify(Algorithm, Key, Data, Expected) ->
gleam@result:'try'(
new(Algorithm, Key),
fun(Hmac_state) ->
Actual = begin
_pipe = Hmac_state,
_pipe@1 = crypto:mac_update(_pipe, Data),
crypto:mac_final(_pipe@1)
end,
{ok, kryptos_ffi:constant_time_equal(Actual, Expected)}
end
).