Packages

Ethereum library for Gleam - JSON-RPC client, transaction signing, ABI encoding, and wallet management on the BEAM

Current section

Files

Jump to
gleeth src gleeth@crypto@random.erl
Raw

src/gleeth@crypto@random.erl

-module(gleeth@crypto@random).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/gleeth/crypto/random.gleam").
-export([generate_secure_bytes/1, generate_private_key_with_retries/1, generate_private_key/0, test_randomness_quality/2, error_to_string/1, is_retryable_error/1, test_random_availability/0]).
-export_type([random_error/0, randomness_test_result/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 random_error() :: {system_random_not_available, binary()} |
{insufficient_entropy, binary()} |
{invalid_length, binary()} |
{cryptographic_error, binary()} |
{generation_failed, binary()}.
-type randomness_test_result() :: {randomness_test_result,
integer(),
integer(),
boolean(),
boolean(),
boolean(),
float()}.
-file("src/gleeth/crypto/random.gleam", 51).
?DOC(
" Generate cryptographically secure random bytes of specified length\n"
" Uses system's cryptographically secure random number generator\n"
"\n"
" ## Implementation Details\n"
"\n"
" - **Erlang/BEAM**: Uses `:crypto.strong_rand_bytes/1` from OTP crypto module\n"
" - **JavaScript**: Uses Web Crypto API `crypto.getRandomValues()`\n"
"\n"
" Both implementations provide cryptographically secure pseudorandom number\n"
" generation suitable for cryptographic key material.\n"
"\n"
" ## Parameters\n"
"\n"
" * `length` - Number of random bytes to generate (must be positive)\n"
"\n"
" ## Returns\n"
"\n"
" * `Ok(BitArray)` - Generated random bytes\n"
" * `Error(RandomError)` - If generation fails\n"
"\n"
" ## Example\n"
"\n"
" ```gleam\n"
" // Generate 32 random bytes for a private key\n"
" case generate_secure_bytes(32) {\n"
" Ok(random_bytes) -> // Use the random bytes\n"
" Error(err) -> // Handle error\n"
" }\n"
" ```\n"
"\n"
" For Erlang: directly calls crypto:strong_rand_bytes/1 and wraps in Ok\n"
" For JavaScript: calls FFI that returns Result type\n"
).
-spec generate_secure_bytes_ffi(integer()) -> {ok, bitstring()} |
{error, binary()}.
generate_secure_bytes_ffi(Length) ->
gleeth_ffi:generate_secure_bytes(Length).
-file("src/gleeth/crypto/random.gleam", 59).
-spec generate_secure_bytes(integer()) -> {ok, bitstring()} |
{error, random_error()}.
generate_secure_bytes(Length) ->
case Length =< 0 of
true ->
{error,
{invalid_length,
<<"Length must be positive, got: "/utf8,
(gleam@string:inspect(Length))/binary>>}};
false ->
case generate_secure_bytes_ffi(Length) of
{ok, Bytes} ->
case erlang:byte_size(Bytes) of
Size when Size =:= Length ->
{ok, Bytes};
Actual_size ->
{error,
{generation_failed,
<<<<<<"Expected "/utf8,
(gleam@string:inspect(Length))/binary>>/binary,
" bytes, got "/utf8>>/binary,
(gleam@string:inspect(Actual_size))/binary>>}}
end;
{error, Msg} ->
{error, {system_random_not_available, Msg}}
end
end.
-file("src/gleeth/crypto/random.gleam", 130).
?DOC(" Internal recursive function to attempt private key generation\n").
-spec generate_private_key_attempt(integer(), integer()) -> {ok,
gleeth@crypto@secp256k1:private_key()} |
{error, random_error()}.
generate_private_key_attempt(Max_retries, Current_attempt) ->
case Current_attempt >= Max_retries of
true ->
{error,
{generation_failed,
<<<<"Failed to generate valid private key after "/utf8,
(gleam@string:inspect(Max_retries))/binary>>/binary,
" attempts"/utf8>>}};
false ->
gleam@result:'try'(
generate_secure_bytes(32),
fun(Random_bytes) ->
case gleeth@crypto@secp256k1:private_key_from_bytes(
Random_bytes
) of
{ok, Private_key} ->
case gleeth@crypto@secp256k1:is_valid_private_key(
Private_key
) of
true ->
{ok, Private_key};
false ->
generate_private_key_attempt(
Max_retries,
Current_attempt + 1
)
end;
{error, _} ->
generate_private_key_attempt(
Max_retries,
Current_attempt + 1
)
end
end
)
end.
-file("src/gleeth/crypto/random.gleam", 123).
?DOC(
" Generate a private key with specified retry attempts\n"
" This is useful for testing or when you want control over retry behavior\n"
).
-spec generate_private_key_with_retries(integer()) -> {ok,
gleeth@crypto@secp256k1:private_key()} |
{error, random_error()}.
generate_private_key_with_retries(Max_retries) ->
generate_private_key_attempt(Max_retries, 0).
-file("src/gleeth/crypto/random.gleam", 117).
?DOC(
" Generate a cryptographically secure secp256k1 private key\n"
"\n"
" This function generates a random 32-byte private key that is guaranteed to be:\n"
" - Cryptographically secure (using system CSPRNG)\n"
" - Within the valid secp256k1 curve order\n"
" - Non-zero (not the invalid all-zeros key)\n"
"\n"
" The function will retry generation if the random bytes don't form a valid\n"
" private key (extremely rare but theoretically possible).\n"
"\n"
" ## Returns\n"
"\n"
" * `Ok(PrivateKey)` - A valid secp256k1 private key\n"
" * `Error(RandomError)` - If generation fails after maximum retries\n"
"\n"
" ## Example\n"
"\n"
" ```gleam\n"
" case generate_private_key() {\n"
" Ok(private_key) -> {\n"
" // Use the private key to create a wallet\n"
" wallet.from_private_key_bytes(secp256k1.private_key_to_bytes(private_key))\n"
" }\n"
" Error(err) -> // Handle generation error\n"
" }\n"
" ```\n"
).
-spec generate_private_key() -> {ok, gleeth@crypto@secp256k1:private_key()} |
{error, random_error()}.
generate_private_key() ->
generate_private_key_with_retries(10).
-file("src/gleeth/crypto/random.gleam", 209).
?DOC(" Generate multiple random samples for testing\n").
-spec generate_multiple_samples(integer(), integer(), list(bitstring())) -> {ok,
list(bitstring())} |
{error, random_error()}.
generate_multiple_samples(Remaining, Byte_length, Acc) ->
case Remaining of
0 ->
{ok, Acc};
_ ->
gleam@result:'try'(
generate_secure_bytes(Byte_length),
fun(Sample) ->
generate_multiple_samples(
Remaining - 1,
Byte_length,
[Sample | Acc]
)
end
)
end.
-file("src/gleeth/crypto/random.gleam", 265).
?DOC(" Create a byte array of all zeros\n").
-spec create_all_zeros(integer()) -> bitstring().
create_all_zeros(Length) ->
case Length of
0 ->
<<>>;
_ ->
gleam_stdlib:bit_array_concat([<<0>>, create_all_zeros(Length - 1)])
end.
-file("src/gleeth/crypto/random.gleam", 273).
?DOC(" Create a byte array of all ones (0xFF bytes)\n").
-spec create_all_ones(integer()) -> bitstring().
create_all_ones(Length) ->
case Length of
0 ->
<<>>;
_ ->
gleam_stdlib:bit_array_concat(
[<<255>>, create_all_ones(Length - 1)]
)
end.
-file("src/gleeth/crypto/random.gleam", 311).
?DOC(" Count 1-bits in a single byte\n").
-spec count_bits_in_byte(integer(), integer()) -> integer().
count_bits_in_byte(Byte, Acc) ->
case Byte of
0 ->
Acc;
_ ->
New_acc = case Byte rem 2 of
1 ->
Acc + 1;
_ ->
Acc
end,
count_bits_in_byte(Byte div 2, New_acc)
end.
-file("src/gleeth/crypto/random.gleam", 292).
?DOC(" Count 1-bits in a single BitArray\n").
-spec count_one_bits_in_sample(bitstring(), integer()) -> integer().
count_one_bits_in_sample(Sample, Acc) ->
case erlang:byte_size(Sample) of
0 ->
Acc;
_ ->
case gleam_stdlib:bit_array_slice(Sample, 0, 1) of
{ok, <<Byte>>} ->
Bits = count_bits_in_byte(Byte, 0),
case gleam_stdlib:bit_array_slice(
Sample,
1,
erlang:byte_size(Sample) - 1
) of
{ok, Rest} ->
count_one_bits_in_sample(Rest, Acc + Bits);
{error, _} ->
Acc + Bits
end;
_ ->
Acc
end
end.
-file("src/gleeth/crypto/random.gleam", 281).
?DOC(" Count total number of 1-bits in a list of BitArrays\n").
-spec count_total_one_bits(list(bitstring()), integer()) -> integer().
count_total_one_bits(Samples, Acc) ->
case Samples of
[] ->
Acc;
[Sample | Rest] ->
One_bits = count_one_bits_in_sample(Sample, 0),
count_total_one_bits(Rest, Acc + One_bits)
end.
-file("src/gleeth/crypto/random.gleam", 329).
-spec list_length_acc(list(any()), integer()) -> integer().
list_length_acc(List, Acc) ->
case List of
[] ->
Acc;
[_ | Rest] ->
list_length_acc(Rest, Acc + 1)
end.
-file("src/gleeth/crypto/random.gleam", 325).
?DOC(" Get length of a list\n").
-spec list_length(list(any())) -> integer().
list_length(List) ->
list_length_acc(List, 0).
-file("src/gleeth/crypto/random.gleam", 337).
?DOC(" Check if list contains element\n").
-spec list_contains(list(HVN), HVN) -> boolean().
list_contains(List, Element) ->
case List of
[] ->
false;
[Head | Rest] ->
case Head =:= Element of
true ->
true;
false ->
list_contains(Rest, Element)
end
end.
-file("src/gleeth/crypto/random.gleam", 353).
-spec list_unique_acc(list(HVS), list(HVS)) -> list(HVS).
list_unique_acc(List, Acc) ->
case List of
[] ->
Acc;
[Head | Rest] ->
case list_contains(Acc, Head) of
true ->
list_unique_acc(Rest, Acc);
false ->
list_unique_acc(Rest, [Head | Acc])
end
end.
-file("src/gleeth/crypto/random.gleam", 349).
?DOC(" Remove duplicates from list (simple O(n²) implementation)\n").
-spec list_unique(list(HVP)) -> list(HVP).
list_unique(List) ->
list_unique_acc(List, []).
-file("src/gleeth/crypto/random.gleam", 365).
?DOC(" Convert Int to Float (helper function)\n").
-spec int_to_float(integer()) -> float().
int_to_float(Value) ->
case Value of
0 ->
+0.0;
1 ->
1.0;
2 ->
2.0;
3 ->
3.0;
4 ->
4.0;
5 ->
5.0;
6 ->
6.0;
7 ->
7.0;
8 ->
8.0;
9 ->
9.0;
_ ->
case Value > 0 of
true ->
int_to_float(Value div 2) * 2.0;
false ->
int_to_float(- Value) * -1.0
end
end.
-file("src/gleeth/crypto/random.gleam", 224).
?DOC(" Analyze randomness samples for basic statistical properties\n").
-spec analyze_randomness_samples(list(bitstring())) -> randomness_test_result().
analyze_randomness_samples(Samples) ->
Sample_count = list_length(Samples),
Byte_length = case Samples of
[First | _] ->
erlang:byte_size(First);
[] ->
0
end,
Unique_samples = list_unique(Samples),
All_different = list_length(Unique_samples) =:= Sample_count,
All_zeros = create_all_zeros(Byte_length),
All_ones = create_all_ones(Byte_length),
No_all_zeros = not list_contains(Samples, All_zeros),
No_all_ones = not list_contains(Samples, All_ones),
Total_bits = (Sample_count * Byte_length) * 8,
Total_one_bits = count_total_one_bits(Samples, 0),
Average_bit_density = case Total_bits of
0 ->
+0.0;
_ ->
case int_to_float(Total_bits) of
+0.0 -> +0.0;
-0.0 -> -0.0;
Gleam@denominator -> int_to_float(Total_one_bits) / Gleam@denominator
end
end,
{randomness_test_result,
Sample_count,
Byte_length,
All_different,
No_all_zeros,
No_all_ones,
Average_bit_density}.
-file("src/gleeth/crypto/random.gleam", 176).
?DOC(
" Test the quality of system randomness by generating multiple samples\n"
" and checking for basic statistical properties. This is useful for\n"
" development and debugging.\n"
"\n"
" Note: This is not a comprehensive randomness test - for production\n"
" systems, use proper statistical test suites like NIST SP 800-22.\n"
).
-spec test_randomness_quality(integer(), integer()) -> {ok,
randomness_test_result()} |
{error, random_error()}.
test_randomness_quality(Sample_count, Byte_length) ->
case (Sample_count =< 0) orelse (Byte_length =< 0) of
true ->
{error,
{invalid_length,
<<"Sample count and byte length must be positive"/utf8>>}};
false ->
gleam@result:'try'(
generate_multiple_samples(Sample_count, Byte_length, []),
fun(Samples) -> {ok, analyze_randomness_samples(Samples)} end
)
end.
-file("src/gleeth/crypto/random.gleam", 395).
?DOC(" Convert RandomError to string for display\n").
-spec error_to_string(random_error()) -> binary().
error_to_string(Error) ->
case Error of
{system_random_not_available, Msg} ->
<<"System random not available: "/utf8, Msg/binary>>;
{insufficient_entropy, Msg@1} ->
<<"Insufficient entropy: "/utf8, Msg@1/binary>>;
{invalid_length, Msg@2} ->
<<"Invalid length: "/utf8, Msg@2/binary>>;
{cryptographic_error, Msg@3} ->
<<"Cryptographic error: "/utf8, Msg@3/binary>>;
{generation_failed, Msg@4} ->
<<"Generation failed: "/utf8, Msg@4/binary>>
end.
-file("src/gleeth/crypto/random.gleam", 406).
?DOC(" Check if an error indicates a temporary failure that might succeed on retry\n").
-spec is_retryable_error(random_error()) -> boolean().
is_retryable_error(Error) ->
case Error of
{system_random_not_available, _} ->
false;
{insufficient_entropy, _} ->
true;
{invalid_length, _} ->
false;
{cryptographic_error, _} ->
true;
{generation_failed, _} ->
false
end.
-file("src/gleeth/crypto/random.gleam", 422).
?DOC(" Check if the random system is available and working\n").
-spec test_random_availability() -> {ok, nil} | {error, random_error()}.
test_random_availability() ->
gleam@result:'try'(generate_secure_bytes(1), fun(_) -> {ok, nil} end).