Packages

Cryptographically secure random key generation for Gleam

Current section

Files

Jump to
apiculture src apiculture@key.erl
Raw

src/apiculture@key.erl

-module(apiculture@key).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/apiculture/key.gleam").
-export([new/0, with_random_bytes/2, with_random_chars/2, with_alphabet/2, with_encoding/2, with_prefix/2, with_separator/2, with_checksum/2, with_checksum_bytes/2, without_checksum/1, generate/1, from_bytes/2, from_uuid_with/2, from_uuid/1, from_ulid_with/2, from_ulid/1, value/1, bytes/1]).
-export_type([key_config/0, key/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.
-type key_config() :: {key_config,
gleam@option:option(integer()),
gleam@option:option(integer()),
gleam@option:option(apiculture@alphabet:alphabet()),
gleam@option:option(apiculture@encoding:encoding()),
gleam@option:option(binary()),
gleam@option:option(binary()),
gleam@option:option(apiculture@checksum:checksum()),
gleam@option:option(integer())}.
-type key() :: {key, binary(), bitstring()}.
-file("src/apiculture/key.gleam", 36).
-spec new() -> key_config().
new() ->
{key_config, none, none, none, none, none, none, none, none}.
-file("src/apiculture/key.gleam", 49).
-spec with_random_bytes(key_config(), integer()) -> key_config().
with_random_bytes(Config, Count) ->
{key_config,
{some, Count},
erlang:element(3, Config),
erlang:element(4, Config),
erlang:element(5, Config),
erlang:element(6, Config),
erlang:element(7, Config),
erlang:element(8, Config),
erlang:element(9, Config)}.
-file("src/apiculture/key.gleam", 53).
-spec with_random_chars(key_config(), integer()) -> key_config().
with_random_chars(Config, Count) ->
{key_config,
erlang:element(2, Config),
{some, Count},
erlang:element(4, Config),
erlang:element(5, Config),
erlang:element(6, Config),
erlang:element(7, Config),
erlang:element(8, Config),
erlang:element(9, Config)}.
-file("src/apiculture/key.gleam", 57).
-spec with_alphabet(key_config(), apiculture@alphabet:alphabet()) -> key_config().
with_alphabet(Config, Alphabet) ->
{key_config,
erlang:element(2, Config),
erlang:element(3, Config),
{some, Alphabet},
erlang:element(5, Config),
erlang:element(6, Config),
erlang:element(7, Config),
erlang:element(8, Config),
erlang:element(9, Config)}.
-file("src/apiculture/key.gleam", 61).
-spec with_encoding(key_config(), apiculture@encoding:encoding()) -> key_config().
with_encoding(Config, Encoding) ->
{key_config,
erlang:element(2, Config),
erlang:element(3, Config),
erlang:element(4, Config),
{some, Encoding},
erlang:element(6, Config),
erlang:element(7, Config),
erlang:element(8, Config),
erlang:element(9, Config)}.
-file("src/apiculture/key.gleam", 65).
-spec with_prefix(key_config(), binary()) -> key_config().
with_prefix(Config, Prefix) ->
{key_config,
erlang:element(2, Config),
erlang:element(3, Config),
erlang:element(4, Config),
erlang:element(5, Config),
{some, Prefix},
erlang:element(7, Config),
erlang:element(8, Config),
erlang:element(9, Config)}.
-file("src/apiculture/key.gleam", 69).
-spec with_separator(key_config(), binary()) -> key_config().
with_separator(Config, Separator) ->
{key_config,
erlang:element(2, Config),
erlang:element(3, Config),
erlang:element(4, Config),
erlang:element(5, Config),
erlang:element(6, Config),
{some, Separator},
erlang:element(8, Config),
erlang:element(9, Config)}.
-file("src/apiculture/key.gleam", 73).
-spec with_checksum(key_config(), apiculture@checksum:checksum()) -> key_config().
with_checksum(Config, Checksum) ->
{key_config,
erlang:element(2, Config),
erlang:element(3, Config),
erlang:element(4, Config),
erlang:element(5, Config),
erlang:element(6, Config),
erlang:element(7, Config),
{some, Checksum},
erlang:element(9, Config)}.
-file("src/apiculture/key.gleam", 77).
-spec with_checksum_bytes(key_config(), integer()) -> key_config().
with_checksum_bytes(Config, Count) ->
{key_config,
erlang:element(2, Config),
erlang:element(3, Config),
erlang:element(4, Config),
erlang:element(5, Config),
erlang:element(6, Config),
erlang:element(7, Config),
erlang:element(8, Config),
{some, Count}}.
-file("src/apiculture/key.gleam", 81).
-spec without_checksum(key_config()) -> key_config().
without_checksum(Config) ->
{key_config,
erlang:element(2, Config),
erlang:element(3, Config),
erlang:element(4, Config),
erlang:element(5, Config),
erlang:element(6, Config),
erlang:element(7, Config),
none,
none}.
-file("src/apiculture/key.gleam", 414).
-spec hex_digit(integer()) -> binary().
hex_digit(Nibble) ->
case Nibble of
0 ->
<<"0"/utf8>>;
1 ->
<<"1"/utf8>>;
2 ->
<<"2"/utf8>>;
3 ->
<<"3"/utf8>>;
4 ->
<<"4"/utf8>>;
5 ->
<<"5"/utf8>>;
6 ->
<<"6"/utf8>>;
7 ->
<<"7"/utf8>>;
8 ->
<<"8"/utf8>>;
9 ->
<<"9"/utf8>>;
10 ->
<<"a"/utf8>>;
11 ->
<<"b"/utf8>>;
12 ->
<<"c"/utf8>>;
13 ->
<<"d"/utf8>>;
14 ->
<<"e"/utf8>>;
15 ->
<<"f"/utf8>>;
_ ->
<<""/utf8>>
end.
-file("src/apiculture/key.gleam", 408).
-spec int_to_hex(integer()) -> binary().
int_to_hex(Byte) ->
Nibble_high = Byte div 16,
Nibble_low = Byte rem 16,
<<(hex_digit(Nibble_high))/binary, (hex_digit(Nibble_low))/binary>>.
-file("src/apiculture/key.gleam", 397).
-spec do_bytes_to_hex(bitstring(), list(binary())) -> list(binary()).
do_bytes_to_hex(Bytes, Acc) ->
case Bytes of
<<>> ->
Acc;
<<Byte, Rest/bitstring>> ->
Hex = int_to_hex(Byte),
do_bytes_to_hex(Rest, [Hex | Acc]);
_ ->
Acc
end.
-file("src/apiculture/key.gleam", 390).
-spec bytes_to_hex(bitstring()) -> binary().
bytes_to_hex(Bytes) ->
_pipe = Bytes,
_pipe@1 = do_bytes_to_hex(_pipe, []),
_pipe@2 = lists:reverse(_pipe@1),
erlang:list_to_binary(_pipe@2).
-file("src/apiculture/key.gleam", 170).
-spec encode_bytes_with_config(bitstring(), key_config()) -> binary().
encode_bytes_with_config(Bytes, Config) ->
case erlang:element(5, Config) of
{some, Enc} ->
apiculture@encoding:encode(Enc, Bytes);
none ->
bytes_to_hex(Bytes)
end.
-file("src/apiculture/key.gleam", 122).
-spec generate_from_bytes(key_config()) -> {ok, key()} |
{error, apiculture@error:error()}.
generate_from_bytes(Config) ->
case erlang:element(2, Config) of
{some, Byte_count} ->
case apiculture@random:random_bytes(Byte_count) of
{ok, Bytes} ->
Encoded = encode_bytes_with_config(Bytes, Config),
{ok, {key, Encoded, Bytes}};
{error, E} ->
{error, E}
end;
none ->
{error, invalid_byte_count}
end.
-file("src/apiculture/key.gleam", 224).
-spec encode_checksum(bitstring(), key_config()) -> binary().
encode_checksum(Crc_bytes, Config) ->
case erlang:element(5, Config) of
{some, Enc} ->
apiculture@encoding:encode(Enc, Crc_bytes);
none ->
bytes_to_hex(Crc_bytes)
end.
-file("src/apiculture/key.gleam", 205).
-spec drop_first_bytes(bitstring(), integer()) -> bitstring().
drop_first_bytes(Bytes, N) ->
case N of
0 ->
Bytes;
_ ->
case Bytes of
<<_, Rest/binary>> ->
drop_first_bytes(Rest, N - 1);
_ ->
<<>>
end
end.
-file("src/apiculture/key.gleam", 198).
-spec truncate_checksum(bitstring(), gleam@option:option(integer())) -> bitstring().
truncate_checksum(Bytes, Byte_count) ->
case Byte_count of
{some, Count} ->
drop_first_bytes(Bytes, erlang:byte_size(Bytes) - Count);
none ->
Bytes
end.
-file("src/apiculture/key.gleam", 177).
-spec format_key(binary(), bitstring(), key_config()) -> binary().
format_key(Content, Bytes, Config) ->
Prefixed = case erlang:element(6, Config) of
{some, Prefix} ->
Sep = begin
_pipe = erlang:element(7, Config),
gleam@option:unwrap(_pipe, <<""/utf8>>)
end,
<<<<Prefix/binary, Sep/binary>>/binary, Content/binary>>;
none ->
Content
end,
case erlang:element(8, Config) of
{some, Chk} ->
Checksum = (erlang:element(2, Chk))(Bytes),
Checksum_truncated = truncate_checksum(
Checksum,
erlang:element(9, Config)
),
Checksum_encoded = encode_checksum(Checksum_truncated, Config),
<<Prefixed/binary, Checksum_encoded/binary>>;
none ->
Prefixed
end.
-file("src/apiculture/key.gleam", 154).
-spec generate_from_encoding(key_config()) -> {ok, key()} |
{error, apiculture@error:error()}.
generate_from_encoding(Config) ->
case {erlang:element(2, Config), erlang:element(5, Config)} of
{{some, Byte_count}, {some, Encoding}} ->
case apiculture@random:random_bytes(Byte_count) of
{ok, Bytes} ->
Encoded = apiculture@encoding:encode(Encoding, Bytes),
Formatted = format_key(Encoded, Bytes, Config),
{ok, {key, Formatted, Bytes}};
{error, E} ->
{error, E}
end;
{_, _} ->
{error, invalid_byte_count}
end.
-file("src/apiculture/key.gleam", 382).
-spec get_nth(integer(), list(binary())) -> binary().
get_nth(N, Items) ->
case {N, Items} of
{0, [First | _]} ->
First;
{_, [_ | Rest]} ->
get_nth(N - 1, Rest);
{_, []} ->
<<""/utf8>>
end.
-file("src/apiculture/key.gleam", 370).
-spec chars_from_indices(list(integer()), apiculture@alphabet:alphabet()) -> binary().
chars_from_indices(Indices, Alphabet) ->
Char_list = apiculture@alphabet:characters(Alphabet),
_pipe = Indices,
_pipe@1 = gleam@list:map(
_pipe,
fun(Index) ->
case (Index >= 0) andalso (Index < erlang:length(Char_list)) of
true ->
get_nth(Index, Char_list);
false ->
<<""/utf8>>
end
end
),
erlang:list_to_binary(_pipe@1).
-file("src/apiculture/key.gleam", 137).
-spec generate_from_alphabet(key_config()) -> {ok, key()} |
{error, apiculture@error:error()}.
generate_from_alphabet(Config) ->
case {erlang:element(3, Config), erlang:element(4, Config)} of
{{some, Char_count}, {some, Alphabet}} ->
case apiculture@random:random_chars(Alphabet, Char_count) of
{ok, Indices} ->
Chars = chars_from_indices(Indices, Alphabet),
Bytes = gleam_stdlib:identity(Chars),
Formatted = format_key(Chars, Bytes, Config),
{ok, {key, Formatted, Bytes}};
{error, E} ->
{error, E}
end;
{_, _} ->
{error, invalid_byte_count}
end.
-file("src/apiculture/key.gleam", 85).
-spec generate(key_config()) -> {ok, key()} | {error, apiculture@error:error()}.
generate(Config) ->
case {erlang:element(2, Config),
erlang:element(3, Config),
erlang:element(4, Config),
erlang:element(5, Config)} of
{none, {some, _}, {some, _}, none} ->
generate_from_alphabet(Config);
{{some, _}, none, none, {some, _}} ->
generate_from_encoding(Config);
{{some, _}, none, none, none} ->
generate_from_bytes(Config);
{_, _, _, _} ->
{error, invalid_byte_count}
end.
-file("src/apiculture/key.gleam", 94).
-spec from_bytes(key_config(), bitstring()) -> {ok, key()} |
{error, apiculture@error:error()}.
from_bytes(Config, Bytes) ->
Encoded = encode_bytes_with_config(Bytes, Config),
Formatted = format_key(Encoded, Bytes, Config),
{ok, {key, Formatted, Bytes}}.
-file("src/apiculture/key.gleam", 217).
-spec default_import_config() -> key_config().
default_import_config() ->
_pipe = new(),
_pipe@1 = with_encoding(_pipe, apiculture@encoding:base62()),
_pipe@2 = with_checksum(_pipe@1, apiculture@checksum:crc32_checksum()),
with_checksum_bytes(_pipe@2, 4).
-file("src/apiculture/key.gleam", 231).
-spec parse_uuid_bytes(binary()) -> {ok, bitstring()} |
{error, apiculture@error:error()}.
parse_uuid_bytes(Input) ->
Normalized = begin
_pipe = Input,
_pipe@1 = string:lowercase(_pipe),
_pipe@2 = gleam@string:replace(
_pipe@1,
<<"urn:uuid:"/utf8>>,
<<""/utf8>>
),
gleam@string:replace(_pipe@2, <<"-"/utf8>>, <<""/utf8>>)
end,
case string:length(Normalized) =:= 32 of
false ->
{error, malformed_input};
true ->
case apiculture@encoding:decode(
apiculture@encoding:hex_lower(),
Normalized
) of
{ok, Bytes} ->
case erlang:byte_size(Bytes) =:= 16 of
true ->
{ok, Bytes};
false ->
{error, malformed_input}
end;
_ ->
{error, malformed_input}
end
end.
-file("src/apiculture/key.gleam", 104).
-spec from_uuid_with(key_config(), binary()) -> {ok, key()} |
{error, apiculture@error:error()}.
from_uuid_with(Config, Input) ->
case parse_uuid_bytes(Input) of
{ok, Bytes} ->
from_bytes(Config, Bytes);
{error, E} ->
{error, E}
end.
-file("src/apiculture/key.gleam", 100).
-spec from_uuid(binary()) -> {ok, key()} | {error, apiculture@error:error()}.
from_uuid(Input) ->
from_uuid_with(default_import_config(), Input).
-file("src/apiculture/key.gleam", 356).
-spec low_bits_mask(integer()) -> integer().
low_bits_mask(Bit_count) ->
case Bit_count of
0 ->
0;
_ ->
erlang:'bsl'(1, Bit_count) - 1
end.
-file("src/apiculture/key.gleam", 318).
-spec ulid_char_value(binary()) -> {ok, integer()} |
{error, apiculture@error:error()}.
ulid_char_value(Char) ->
case Char of
<<"0"/utf8>> ->
{ok, 0};
<<"O"/utf8>> ->
{ok, 0};
<<"1"/utf8>> ->
{ok, 1};
<<"I"/utf8>> ->
{ok, 1};
<<"L"/utf8>> ->
{ok, 1};
<<"2"/utf8>> ->
{ok, 2};
<<"3"/utf8>> ->
{ok, 3};
<<"4"/utf8>> ->
{ok, 4};
<<"5"/utf8>> ->
{ok, 5};
<<"6"/utf8>> ->
{ok, 6};
<<"7"/utf8>> ->
{ok, 7};
<<"8"/utf8>> ->
{ok, 8};
<<"9"/utf8>> ->
{ok, 9};
<<"A"/utf8>> ->
{ok, 10};
<<"B"/utf8>> ->
{ok, 11};
<<"C"/utf8>> ->
{ok, 12};
<<"D"/utf8>> ->
{ok, 13};
<<"E"/utf8>> ->
{ok, 14};
<<"F"/utf8>> ->
{ok, 15};
<<"G"/utf8>> ->
{ok, 16};
<<"H"/utf8>> ->
{ok, 17};
<<"J"/utf8>> ->
{ok, 18};
<<"K"/utf8>> ->
{ok, 19};
<<"M"/utf8>> ->
{ok, 20};
<<"N"/utf8>> ->
{ok, 21};
<<"P"/utf8>> ->
{ok, 22};
<<"Q"/utf8>> ->
{ok, 23};
<<"R"/utf8>> ->
{ok, 24};
<<"S"/utf8>> ->
{ok, 25};
<<"T"/utf8>> ->
{ok, 26};
<<"V"/utf8>> ->
{ok, 27};
<<"W"/utf8>> ->
{ok, 28};
<<"X"/utf8>> ->
{ok, 29};
<<"Y"/utf8>> ->
{ok, 30};
<<"Z"/utf8>> ->
{ok, 31};
_ ->
{error, malformed_input}
end.
-file("src/apiculture/key.gleam", 363).
-spec ints_to_bytes(list(integer())) -> bitstring().
ints_to_bytes(Ints) ->
case Ints of
[] ->
<<>>;
[First | Rest] ->
gleam@bit_array:append(<<First>>, ints_to_bytes(Rest))
end.
-file("src/apiculture/key.gleam", 277).
-spec decode_ulid_chars(list(binary()), integer(), integer(), list(integer())) -> {ok,
bitstring()} |
{error, apiculture@error:error()}.
decode_ulid_chars(Chars, Acc, Bit_count, Out) ->
case Chars of
[] ->
case ((erlang:length(Out) =:= 16) andalso (Bit_count =:= 0)) andalso (Acc
=:= 0) of
true ->
{ok, ints_to_bytes(lists:reverse(Out))};
false ->
{error, malformed_input}
end;
[Char | Rest] ->
case ulid_char_value(Char) of
{ok, Value} ->
Next_acc = (Acc * 32) + Value,
Next_bit_count = Bit_count + 5,
case Next_bit_count >= 8 of
true ->
Remaining_bits = Next_bit_count - 8,
Byte = begin
_pipe = Next_acc,
_pipe@1 = erlang:'bsr'(_pipe, Remaining_bits),
erlang:'band'(_pipe@1, 255)
end,
Remaining = erlang:'band'(
Next_acc,
low_bits_mask(Remaining_bits)
),
decode_ulid_chars(
Rest,
Remaining,
Remaining_bits,
[Byte | Out]
);
false ->
decode_ulid_chars(
Rest,
Next_acc,
Next_bit_count,
Out
)
end;
{error, E} ->
{error, E}
end
end.
-file("src/apiculture/key.gleam", 252).
-spec parse_ulid_bytes(binary()) -> {ok, bitstring()} |
{error, apiculture@error:error()}.
parse_ulid_bytes(Input) ->
Chars = begin
_pipe = Input,
_pipe@1 = string:uppercase(_pipe),
gleam@string:to_graphemes(_pipe@1)
end,
case erlang:length(Chars) =:= 26 of
false ->
{error, malformed_input};
true ->
case Chars of
[First | Rest] ->
case ulid_char_value(First) of
{ok, Value} ->
case Value =< 7 of
true ->
decode_ulid_chars(Rest, Value, 3, []);
false ->
{error, malformed_input}
end;
_ ->
{error, malformed_input}
end;
[] ->
{error, malformed_input}
end
end.
-file("src/apiculture/key.gleam", 115).
-spec from_ulid_with(key_config(), binary()) -> {ok, key()} |
{error, apiculture@error:error()}.
from_ulid_with(Config, Input) ->
case parse_ulid_bytes(Input) of
{ok, Bytes} ->
from_bytes(Config, Bytes);
{error, E} ->
{error, E}
end.
-file("src/apiculture/key.gleam", 111).
-spec from_ulid(binary()) -> {ok, key()} | {error, apiculture@error:error()}.
from_ulid(Input) ->
from_ulid_with(default_import_config(), Input).
-file("src/apiculture/key.gleam", 437).
?DOC(" Returns the string value of the key.\n").
-spec value(key()) -> binary().
value(Key) ->
erlang:element(2, Key).
-file("src/apiculture/key.gleam", 442).
?DOC(" Returns the raw bytes that were used to generate the key.\n").
-spec bytes(key()) -> bitstring().
bytes(Key) ->
erlang:element(3, Key).