Software APIs
mod_exp_ibex.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 
5 #include "sw/device/silicon_creator/lib/sigverify/mod_exp_ibex.h"
6 
7 #include <stddef.h>
8 
11 
12 /**
13  * Subtracts the modulus of `key` from `a` in-place, i.e. `a -= n`.
14  *
15  * Since `a` can be smaller than the modulus, this function also returns the
16  * borrow.
17  *
18  * @param key An RSA public key.
19  * @param[in,out] a Buffer that holds `a`, little-endian.
20  * @return Borrow.
21  */
23 static uint32_t subtract_modulus(const sigverify_rsa_key_t *key,
25  uint32_t borrow = 0;
26  for (size_t i = 0; i < ARRAYSIZE(a->data); ++i) {
27  uint32_t temp = a->data[i] - borrow;
28  // We borrow if either the above or the below subtraction wraps around.
29  // Note: borrow can only be 0 or 1.
30  borrow = (a->data[i] < borrow) + (temp < key->n.data[i]);
31  a->data[i] = temp - key->n.data[i];
32  }
33  return borrow;
34 }
35 
36 /**
37  * Checks if `a` is greater than or equal to the modulus of `key`.
38  *
39  * @param key An RSA public key.
40  * @param a Buffer that holds `a`, little-endian.
41  * @return Comparison result.
42  */
44 static bool greater_equal_modulus(const sigverify_rsa_key_t *key,
45  const sigverify_rsa_buffer_t *a) {
46  // Note: Loop terminates when `i` wraps around.
47  for (size_t i = ARRAYSIZE(a->data) - 1; i < ARRAYSIZE(a->data); --i) {
48  if (a->data[i] != key->n.data[i]) {
49  return a->data[i] > key->n.data[i];
50  }
51  }
52  return true;
53 }
54 
55 /**
56  * Shifts `a` left one bit in-place, i.e. `a <<= 1`.
57  *
58  * Since the result may not fit in `a`, this function also returns the most
59  * significant bit of the result.
60  *
61  * @param[in,out] a Buffer that holds `a`, little-endian.
62  * @return Most significant bit of the result.
63  */
65 static uint32_t shift_left(sigverify_rsa_buffer_t *a) {
66  const uint32_t msb = a->data[ARRAYSIZE(a->data) - 1] >> 31;
67  for (size_t i = ARRAYSIZE(a->data) - 1; i > 0; --i) {
68  a->data[i] = (a->data[i] << 1) | (a->data[i - 1] >> 31);
69  }
70  a->data[0] <<= 1;
71  return msb;
72 }
73 
74 /**
75  * Computes the Montgomery reduction of the product of two integers.
76  *
77  * Given an RSA public key, x, and y this function computes x*y*R^-1 mod n,
78  * where
79  * - x and y are integers with `kSigVerifyRsaNumWords` base 2^32 digits,
80  * - n is the modulus of the key, and
81  * - R is 2^`kSigVerifyRsaNumBits`, e.g. 2^3072 for RSA-3072.
82  *
83  * See Handbook of Applied Cryptography, Ch. 14, Alg. 14.36.
84  *
85  * @param key An RSA public key.
86  * @param x Buffer that holds `x`, little-endian.
87  * @param y Buffer that holds `y`, little-endian.
88  * @param[out] result Buffer to write the result to, little-endian.
89  */
90 static void mont_mul(const sigverify_rsa_key_t *key,
91  const sigverify_rsa_buffer_t *x,
92  const sigverify_rsa_buffer_t *y,
93  sigverify_rsa_buffer_t *result) {
94  memset(result->data, 0, sizeof(result->data));
95 
96  for (size_t i = 0; i < ARRAYSIZE(x->data); ++i) {
97  // The loop below reads one word ahead of writes to avoid a separate loop
98  // for the division by `b` in step 2.2 of the algorithm. Thus, `acc0` and
99  // `acc1` are initialized here before the loop. `acc0` holds the sum of
100  // first two addends in step 2.2 while `acc1` holds the entire sum. Carries
101  // of these operations are stored separately in the upper words of `acc0`
102  // and `acc1`. `acc0` and `acc1` can safely store these intermediate values,
103  // i.e. without wrapping, because UINT32_MAX^2 + 2*UINT32_MAX is
104  // 0xffff_ffff_ffff_ffff.
105 
106  // Holds the sum of the first two addends in step 2.2.
107  uint64_t acc0 = (uint64_t)x->data[i] * y->data[0] + result->data[0];
108  const uint32_t u_i = (uint32_t)acc0 * key->n0_inv[0];
109  // Holds the sum of the all three addends in step 2.2.
110  uint64_t acc1 = (uint64_t)u_i * key->n.data[0] + (uint32_t)acc0;
111 
112  // Process the i^th digit of `x`, i.e. `x[i]`.
113  for (size_t j = 1; j < ARRAYSIZE(result->data); ++j) {
114  acc0 = (uint64_t)x->data[i] * y->data[j] + result->data[j] + (acc0 >> 32);
115  acc1 = (uint64_t)u_i * key->n.data[j] + (uint32_t)acc0 + (acc1 >> 32);
116  result->data[j - 1] = (uint32_t)acc1;
117  }
118  acc0 = (acc0 >> 32) + (acc1 >> 32);
119  result->data[ARRAYSIZE(result->data) - 1] = (uint32_t)acc0;
120 
121  // The intermediate result of this algorithm before the check below is
122  // bounded by R + n (Eq. (4) in Montgomery Arithmetic from a Software
123  // Perspective, Bos. J. W, Montgomery, P. L.) where n is the modulus of
124  // `key` and n < R. Therefore, if there is a carry, then `result` is not the
125  // least non-negative residue of x*y*R^-1 mod n. Since `acc0 >> 32` here is
126  // at most 1, we can subtract the modulus from `result` without taking it
127  // into account and fit `result` into `kSigVerifyRsaNumWords`. Since this is
128  // not a direct comparison with the modulus, the final result is not
129  // guaranteed to be the least non-negative residue of x*y*R^-1 mod n.
130  if (acc0 >> 32) {
131  OT_DISCARD(subtract_modulus(key, result));
132  }
133  }
134 }
135 
136 /**
137  * Calculates R^2 mod n, where R = 2^kSigVerifyRsaNumBits.
138  *
139  * @param key An RSA public key.
140  * @param[out] result Buffer to write the result to, little-endian.
141  */
142 static void calc_r_square(const sigverify_rsa_key_t *key,
143  sigverify_rsa_buffer_t *result) {
145  memset(buf.data, 0, sizeof(result->data));
146  // This subtraction sets buf = -n mod R = R - n, which is equivalent to R
147  // modulo n and ensures that `buf` fits in `kSigVerifyRsaNumWords` going
148  // into the loop.
149  OT_DISCARD(subtract_modulus(key, &buf));
150 
151  // Compute (2^96 * R) mod n.
152  // Each run of the loop doubles buf and reduces modulo n.
153  for (size_t i = 0; i < 96; ++i) {
154  uint32_t msb = shift_left(&buf);
155  // Reduce until buf < n. Doing this at every iteration minimizes the
156  // total number of subtractions that we need to perform.
157  while (msb > 0 || greater_equal_modulus(key, &buf)) {
158  msb -= subtract_modulus(key, &buf);
159  }
160  }
161 
162  // Perform 5 montgomery squares to get RR = ((2^96)^32 * R) mod n
163  mont_mul(key, &buf, &buf, result);
164  for (size_t i = 0; i < 2; ++i) {
165  mont_mul(key, result, result, &buf);
166  mont_mul(key, &buf, &buf, result);
167  }
168 }
169 
170 rom_error_t sigverify_mod_exp_ibex(const sigverify_rsa_key_t *key,
171  const sigverify_rsa_buffer_t *sig,
172  sigverify_rsa_buffer_t *result) {
173  // Reject the signature if it is too large (n <= sig): RFC 8017, section
174  // 5.2.2, step 1.
175  if (greater_equal_modulus(key, sig)) {
176  return kErrorSigverifyLargeRsaSignature;
177  }
178 
180 
181  // result = R^2 mod n
182  calc_r_square(key, result);
183  // buf = sig * R mod n
184  mont_mul(key, sig, result, &buf);
185  for (size_t i = 0; i < 8; ++i) {
186  // result = sig^{2*4^i} * R mod n (sig's exponent: 2, 8, 32, ..., 32768)
187  mont_mul(key, &buf, &buf, result);
188  // buf = sig^{4^{i+1}} * R mod n (sig's exponent: 4, 16, 64, ..., 65536)
189  mont_mul(key, result, result, &buf);
190  }
191  // result = sig^65537 mod n
192  mont_mul(key, &buf, sig, result);
193 
194  // We need this check because the result of `mont_mul` is not guaranteed to be
195  // the least non-negative residue. We need to subtract the modulus n from
196  // `result` at most once because R/2 < n < R.
197  if (greater_equal_modulus(key, result)) {
198  OT_DISCARD(subtract_modulus(key, result));
199  }
200 
201  return kErrorOk;
202 }