File 3323-Implement-bytes-2-plug-in-callback.patch of Package erlang
From 21b6f2ca152111fc1bf9618574abc71827de25f6 Mon Sep 17 00:00:00 2001
From: Raimo Niskanen <raimo@erlang.org>
Date: Tue, 2 Dec 2025 11:10:32 +0100
Subject: [PATCH 3/7] Implement bytes/2 plug-in callback
---
lib/crypto/src/crypto.erl | 77 +++++++++++++++++++++++++++++++--------
lib/stdlib/src/rand.erl | 26 +++++++------
2 files changed, 76 insertions(+), 27 deletions(-)
diff --git a/lib/crypto/src/crypto.erl b/lib/crypto/src/crypto.erl
index dcfe7f5968..2e3532a98e 100644
--- a/lib/crypto/src/crypto.erl
+++ b/lib/crypto/src/crypto.erl
@@ -165,7 +165,9 @@ end
-export([rand_plugin_aes_next/1, rand_plugin_aes_jump/1]).
-export([rand_plugin_uniform/1]).
-export([rand_plugin_uniform/2]).
+-export([rand_plugin_bytes/2]).
-export([rand_cache_plugin_next/1]).
+-export([rand_cache_plugin_bytes/2]).
-export([rand_uniform/2]).
-export([public_encrypt/4, private_decrypt/4]).
-export([private_encrypt/4, public_decrypt/4]).
@@ -2185,9 +2187,13 @@ Create a generator for `m:rand`.
Create a state object (generator) for [random number generation](`m:rand`),
which when used by the `m:rand` functions produce
**cryptographically strong** random numbers (based on OpenSSL's
-`BN_rand_range` function). See also `rand:seed_s/1`, and for example
+`BN_rand_range` function). See `rand:seed_s/1`, and for example
`rand:uniform_s/2`.
+This generator also implements generating bytes efficiently
+(based on OpenSSL's `RAND_bytes` function).
+See `rand:bytes_s/2` and `strong_rand_bytes/1`.
+
#### _Example_
``` erlang
@@ -2270,7 +2276,7 @@ FloatValue = rand:uniform(). % again
rand_seed_alg(Alg, Seed) ->
rand:seed(rand_seed_alg_s(Alg, Seed)).
--define(CRYPTO_CACHE_BITS, 56).
+-define(CRYPTO_CACHE_BITS, 56). % Has to be divisible by 8
-define(CRYPTO_AES_BITS, 58).
-doc(#{title => <<"Plug-In Generators">>}).
@@ -2281,13 +2287,17 @@ Create a state object (generator) for [random number generation](`m:rand`),
which when used by the `m:rand` functions produce
**cryptographically strong** random number.
-See also `rand:seed_s/1` and for example `rand:uniform_s/2`.
+See `rand:seed_s/1` and for example `rand:uniform_s/2`.
If `Alg` is `crypto` this function is equivalent to `rand_seed_s/0`.
If `Alg` is `crypto_cache` the returned generator fetches random data
- with OpenSSL's `RAND_bytes` and caches it as 56 bit numbers
-which makes calculations fast on 64 bit machines.
+ with OpenSSL's `RAND_bytes` and caches it. Then 56 bit numbers
+are extracted which makes calculations in module `m:rand` fast
+on 64 bit machines.
+
+`Alg = crypto_cache` also implements extracting bytes efficiently.
+See `rand:bytes_s/2` and `strong_rand_bytes/1`.
#### _Example_
@@ -2302,7 +2312,7 @@ to raise the exception `error:low_entropy` in case
the random generator failed due to lack of secure "randomness".
The cache size can be changed from its default value using the
-[crypto app's ](crypto_app.md)configuration parameter `rand_cache_size`.
+[crypto app's ](crypto_app.md) configuration parameter `rand_cache_size`.
> #### Note {: .info }
>
@@ -2333,8 +2343,8 @@ Create a state object (generator) for [random number generation](`m:rand`),
which when used by the `m:rand` functions produce
**cryptographically unpredictable** random numbers
-See also `rand:seed_s/1`, and for example `rand:uniform_s/2`.
-Compare to `rand_seed_alg/1`.
+See `rand:seed_s/1`, and for example `rand:uniform_s/2`,
+and compare to `rand_seed_alg/1`.
To get a long period the Xoroshiro928 generator from the `m:rand` module is used
as a counter (with period 2^928 - 1) and the generator states are scrambled
@@ -2384,11 +2394,13 @@ mk_alg_handler(?MODULE = Alg) ->
bits => 64,
next => fun ?MODULE:rand_plugin_next/1,
uniform => fun ?MODULE:rand_plugin_uniform/1,
- uniform_n => fun ?MODULE:rand_plugin_uniform/2};
+ uniform_n => fun ?MODULE:rand_plugin_uniform/2,
+ bytes => fun ?MODULE:rand_plugin_bytes/2};
mk_alg_handler(crypto_cache = Alg) ->
#{ type => Alg,
bits => ?CRYPTO_CACHE_BITS,
- next => fun ?MODULE:rand_cache_plugin_next/1};
+ next => fun ?MODULE:rand_cache_plugin_next/1,
+ bytes => fun ?MODULE:rand_cache_plugin_bytes/2};
mk_alg_handler(crypto_aes = Alg) ->
#{ type => Alg,
bits => ?CRYPTO_AES_BITS,
@@ -2435,14 +2447,49 @@ rand_plugin_uniform(State) ->
rand_plugin_uniform(Max, State) ->
{strong_rand_range(Max) + 1, State}.
+-doc false.
+rand_plugin_bytes(N, State) ->
+ {strong_rand_bytes(N), State}.
+
-doc false.
-rand_cache_plugin_next({CacheBits, GenBytes, <<>>}) ->
- rand_cache_plugin_next(
- {CacheBits, GenBytes, strong_rand_bytes(GenBytes)});
rand_cache_plugin_next({CacheBits, GenBytes, Cache}) ->
- <<I:CacheBits, NewCache/binary>> = Cache,
- {I, {CacheBits, GenBytes, NewCache}}.
+ rand_cache_plugin_next(CacheBits, GenBytes, Cache).
+%%
+rand_cache_plugin_next(CacheBits, GenBytes, Cache) ->
+ case Cache of
+ <<I:CacheBits, NewCache/binary>> ->
+ {I, {CacheBits, GenBytes, NewCache}};
+ <<>> ->
+ NewCache = strong_rand_bytes(GenBytes),
+ rand_cache_plugin_next(CacheBits, GenBytes, NewCache);
+ <<_/binary>> ->
+ <<NewBytes:((CacheBits bsr 3) - byte_size(Cache))/binary,
+ NewCache/binary>> = strong_rand_bytes(GenBytes),
+ <<I:CacheBits>> = <<Cache/binary, NewBytes/binary>>,
+ {I, {CacheBits, GenBytes, NewCache}}
+ end.
+
+-doc false.
+rand_cache_plugin_bytes(
+ N, State = {AlgHandler, {CacheBits, GenBytes, Cache}})
+ when is_integer(N), 0 =< N ->
+ case Cache of
+ <<Bytes:N/binary, NewCache/binary>> ->
+ {Bytes, {AlgHandler, {CacheBits, GenBytes, NewCache}}};
+ <<>> ->
+ {strong_rand_bytes(N), State};
+ <<_/binary>> ->
+ if
+ N =< GenBytes ->
+ <<NewBytes:(N - byte_size(Cache))/binary,
+ NewCache/binary>> = strong_rand_bytes(GenBytes),
+ Bytes = <<Cache/binary, NewBytes/binary>>,
+ {Bytes, {AlgHandler, {CacheBits, GenBytes, NewCache}}};
+ true ->
+ {strong_rand_bytes(N), State}
+ end
+ end.
%% Encrypt 128 bit counter values and use the 58 lowest
--
2.51.0