Software APIs
random_order.c
1// Copyright lowRISC contributors (OpenTitan project).
2// Licensed under the Apache License, Version 2.0, see LICENSE for details.
3// SPDX-License-Identifier: Apache-2.0
4
6
9
10void random_order_init(random_order_t *ctx, size_t min_len) {
11 ctx->ctr = 0;
12
13 // If the sequence length is zero or one, there's not much randomization we
14 // can do, but we can at least return one value past the range.
15 if (launder32(min_len) < 2) {
16 ctx->state = 0;
17 ctx->step = 1;
18 ctx->max = min_len + 1;
19 return;
20 }
21 HARDENED_CHECK_LE(2, min_len);
22
23 // Sample the start index from the range [0, n-1].
24 ctx->state = random_order_random_word() % min_len;
25 // Sample the step size from the range [1, n-1].
26 ctx->step = (random_order_random_word() % (min_len - 1)) + 1;
27
28 if (min_len - (min_len % ctx->step) > UINT32_MAX - ctx->step) {
29 // Safe fallback for cases where the max calculation would overflow.
30 ctx->max = UINT32_MAX;
31 ctx->step = 1;
32 } else {
33 // The maximum value is the smallest multiple of `step` that is strictly
34 // greater than `min_len`.
35 ctx->max = min_len - (min_len % ctx->step) + ctx->step;
36 }
37 HARDENED_CHECK_LE(min_len, ctx->max);
38}
39
40size_t random_order_len(const random_order_t *ctx) { return ctx->max; }
41
42size_t random_order_advance(random_order_t *ctx) {
43 size_t out = ctx->state;
44
45 // Normally, the increment is the step size. However, if we came back around
46 // to the start index, increase it by one to change the offset of the step.
47 uint32_t inc = ctx->step;
48 ctx->ctr = launder32(ctx->ctr) + 1;
49 if (ctx->ctr % (ctx->max / ctx->step) == 0) {
50 inc++;
51 }
52
53 if (launder32(inc) >= ctx->max - ctx->state) {
54 // If the next step would be greater than the maximum value, then loop back
55 // around to the start.
56 ctx->state = ctx->state + inc - ctx->max;
57 } else {
58 ctx->state += inc;
59 }
60 HARDENED_CHECK_LE(ctx->state, ctx->max);
61
62 return out;
63}