user15534550
user15534550

Reputation: 43

Generate a random 16-byte string in the 32-character hexadecimal format

I am new to Elixir / Erlang. I have been using Ruby for a while and I am wanting to convert this piece of code to Elixir / Erlang.

SecureRandom::random_bytes(16).each_byte.map { |b| sprintf("%02X",b) }.join

Upvotes: 4

Views: 2198

Answers (3)

7stud
7stud

Reputation: 48649

First, your ruby code can be simplified to:

SecureRandom.hex(16).upcase

Next, in erlang a "string" can be somewhat of a nebulous concept. In erlang, it is typically more efficient to construct a list which contains chunks of your string, then pass around the list, then when you need to output the string, erlang will automatically join the chunks for you:

-module(a).
-compile(export_all).

go() ->
    MyIolist = [ io_lib:format("~2.16.0B", [X]) || <<X>> <= crypto:strong_rand_bytes(16) ],

    %% Print a ruler  -------
    Ruler = "1234567890",
    lists:foreach(fun(_X) -> io:format("~s", [Ruler]) end,
                       lists:seq(1, 4) ),    
    io:format("~n"),
    %%-----------------------

    %% Print the list containing the chunks of the string:
    io:format("~s~n", [MyIolist]).

The format sequence:

    "~2.16.0B"

is read as:

     2 => field width, 
    16 => base (base 10 is the default) 
     0 => padding character (so that the integers 0-15 are represented by two characters) 
    ~B => format code that allows you to specify the base of an integer and uses uppercase for letters
           (the particulars go between ~ and B)

In the shell:

23> c(a).
a.erl:2: Warning: export_all flag enabled - all functions will be exported
{ok,a}

24> a:go().
1234567890123456789012345678901234567890
21755436149C16E0A9902F7B7E9F6929
ok

25> a:go().
1234567890123456789012345678901234567890
86D3850FA2590B810C1CAB0B965EE415
ok

26> a:go().
1234567890123456789012345678901234567890
A9C31CAFCC74C8311A5389E3BA1CD19F
ok

27> a:go().
1234567890123456789012345678901234567890
FB82878326F7B6FDB87069AF031345FF
ok

28> a:go().
1234567890123456789012345678901234567890
57515F06DFA699710E543D9885CB90ED
ok

29> a:go().
1234567890123456789012345678901234567890
F84B308723DB660A8A4371E3A80C23DC
ok

I ran that a few times so you can see that the strings are all the same length. 16 bytes * 2 hex_chars/byte = 32 chars.

As Venkatakumar Srinivasan demonstrated, for your purposes you can just treat all 16 bytes(128 bits) as a single integer:

-module(a).
-compile(export_all).

go() ->
    <<X:128>> = crypto:strong_rand_bytes(16),
    Result = io_lib:format("~32.16.0B", [X]),

    %% Print a ruler:
    Ruler = "1234567890",
    lists:foreach(fun(_X) -> io:format("~s", [Ruler]) end,
                       lists:seq(1, 4) ),    
    io:format("~n"),
    %%-----------------------

    %% Print the list containing the chunks of the string:
    io:format("~s~n", Result).

In the shell:

42> c(a).  
a.erl:2: Warning: export_all flag enabled - all functions will be exported
{ok,a}

43> a:go().
1234567890123456789012345678901234567890
551589BE1CB7B0C048AEF67DD9343F7F
ok

44> a:go().
1234567890123456789012345678901234567890
AC3DC11F714D7F8FE8ABBED566E9AB5B
ok

45> a:go().
1234567890123456789012345678901234567890
176530F08AFEC3B5452A818E4A95C743
ok

46> a:go().
1234567890123456789012345678901234567890
8F2E651B3D7D53AF88147244BB8F6153
ok

There's no reason to call flatten(). Although, io_lib:format() doesn't concern itself with creating complete strings, when you tell erlang to output the list returned by io_lib:format() using the format sequence ~s, erlang will join everything together into one string.

Upvotes: 2

Vasiliy Ermolovich
Vasiliy Ermolovich

Reputation: 24627

To have the same result, in Elixir you can use the Erlang module :crypto.strong_rand_bytes(16) to generate the random number, and convert it using Base.encode16

Take a look at https://hexdocs.pm/elixir/Base.html to understand better the Base module . Example:

:crypto.strong_rand_bytes(16) |> Base.encode16 # => "4B14868924ACEE98C9C9C404A1F87B08"

Upvotes: 6

%% Generate random bytes
<<X:128/big-unsigned-integer>> = crypto:strong_rand_bytes(16).

%% conver to hex
lists:flatten(io_lib:format("~32.16.0b", [X])).

Upvotes: 4

Related Questions