Software APIs
p256.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/lib/crypto/impl/ecc/p256.h"
6 
9 #include "sw/device/lib/crypto/drivers/otbn.h"
10 
12 
13 // Module ID for status codes.
14 #define MODULE_ID MAKE_MODULE_ID('p', '2', 'r')
15 
16 // Declare the OTBN app.
17 OTBN_DECLARE_APP_SYMBOLS(run_p256); // The OTBN P-256 app.
18 static const otbn_app_t kOtbnAppP256 = OTBN_APP_T_INIT(run_p256);
19 
20 // Declare offsets for input and output buffers.
21 OTBN_DECLARE_SYMBOL_ADDR(run_p256, mode); // Mode of operation.
22 OTBN_DECLARE_SYMBOL_ADDR(run_p256, msg); // ECDSA message digest.
23 OTBN_DECLARE_SYMBOL_ADDR(run_p256, r); // ECDSA signature scalar R.
24 OTBN_DECLARE_SYMBOL_ADDR(run_p256, s); // ECDSA signature scalar S.
25 OTBN_DECLARE_SYMBOL_ADDR(run_p256, x); // Public key x-coordinate.
26 OTBN_DECLARE_SYMBOL_ADDR(run_p256, y); // Public key y-coordinate.
27 OTBN_DECLARE_SYMBOL_ADDR(run_p256, d0); // Private key scalar d (share 0).
28 OTBN_DECLARE_SYMBOL_ADDR(run_p256, d1); // Private key scalar d (share 1).
29 OTBN_DECLARE_SYMBOL_ADDR(run_p256, x_r); // ECDSA verification result.
30 OTBN_DECLARE_SYMBOL_ADDR(run_p256, ok); // Status code.
31 
32 static const otbn_addr_t kOtbnVarMode = OTBN_ADDR_T_INIT(run_p256, mode);
33 static const otbn_addr_t kOtbnVarMsg = OTBN_ADDR_T_INIT(run_p256, msg);
34 static const otbn_addr_t kOtbnVarR = OTBN_ADDR_T_INIT(run_p256, r);
35 static const otbn_addr_t kOtbnVarS = OTBN_ADDR_T_INIT(run_p256, s);
36 static const otbn_addr_t kOtbnVarX = OTBN_ADDR_T_INIT(run_p256, x);
37 static const otbn_addr_t kOtbnVarY = OTBN_ADDR_T_INIT(run_p256, y);
38 static const otbn_addr_t kOtbnVarD0 = OTBN_ADDR_T_INIT(run_p256, d0);
39 static const otbn_addr_t kOtbnVarD1 = OTBN_ADDR_T_INIT(run_p256, d1);
40 static const otbn_addr_t kOtbnVarXr = OTBN_ADDR_T_INIT(run_p256, x_r);
41 static const otbn_addr_t kOtbnVarOk = OTBN_ADDR_T_INIT(run_p256, ok);
42 
43 // Declare mode constants.
44 OTBN_DECLARE_SYMBOL_ADDR(run_p256, MODE_KEYGEN);
45 OTBN_DECLARE_SYMBOL_ADDR(run_p256, MODE_SIGN);
46 OTBN_DECLARE_SYMBOL_ADDR(run_p256, MODE_VERIFY);
47 OTBN_DECLARE_SYMBOL_ADDR(run_p256, MODE_ECDH);
48 OTBN_DECLARE_SYMBOL_ADDR(run_p256, MODE_SIDELOAD_KEYGEN);
49 OTBN_DECLARE_SYMBOL_ADDR(run_p256, MODE_SIDELOAD_SIGN);
50 OTBN_DECLARE_SYMBOL_ADDR(run_p256, MODE_SIDELOAD_ECDH);
51 static const uint32_t kOtbnP256ModeKeygen =
52  OTBN_ADDR_T_INIT(run_p256, MODE_KEYGEN);
53 static const uint32_t kOtbnP256ModeSign = OTBN_ADDR_T_INIT(run_p256, MODE_SIGN);
54 static const uint32_t kOtbnP256ModeVerify =
55  OTBN_ADDR_T_INIT(run_p256, MODE_VERIFY);
56 static const uint32_t kOtbnP256ModeEcdh = OTBN_ADDR_T_INIT(run_p256, MODE_ECDH);
57 static const uint32_t kOtbnP256ModeSideloadKeygen =
58  OTBN_ADDR_T_INIT(run_p256, MODE_SIDELOAD_KEYGEN);
59 static const uint32_t kOtbnP256ModeSideloadSign =
60  OTBN_ADDR_T_INIT(run_p256, MODE_SIDELOAD_SIGN);
61 static const uint32_t kOtbnP256ModeSideloadEcdh =
62  OTBN_ADDR_T_INIT(run_p256, MODE_SIDELOAD_ECDH);
63 
64 enum {
65  /*
66  * Mode is represented by a single word.
67  */
68  kOtbnP256ModeWords = 1,
69  /**
70  * Number of extra padding words needed for masked scalar shares.
71  *
72  * Where W is the word size and S is the share size, the padding needed is:
73  * (W - (S % W)) % W
74  *
75  * The extra outer "% W" ensures that the padding is 0 if (S % W) is 0.
76  */
77  kMaskedScalarPaddingWords =
78  (kOtbnWideWordNumWords -
79  (kP256MaskedScalarShareWords % kOtbnWideWordNumWords)) %
80  kOtbnWideWordNumWords,
81 };
82 
83 static status_t p256_masked_scalar_write(const p256_masked_scalar_t *src,
84  const otbn_addr_t share0_addr,
85  const otbn_addr_t share1_addr) {
86  HARDENED_TRY(
87  otbn_dmem_write(kP256MaskedScalarShareWords, src->share0, share0_addr));
88  HARDENED_TRY(
89  otbn_dmem_write(kP256MaskedScalarShareWords, src->share1, share1_addr));
90 
91  // Write trailing 0s so that OTBN's 256-bit read of the second share does not
92  // cause an error.
93  HARDENED_TRY(otbn_dmem_set(kMaskedScalarPaddingWords, 0,
94  share0_addr + kP256MaskedScalarShareBytes));
95  HARDENED_TRY(otbn_dmem_set(kMaskedScalarPaddingWords, 0,
96  share1_addr + kP256MaskedScalarShareBytes));
97 
98  return OTCRYPTO_OK;
99 }
100 
101 status_t p256_keygen_start(void) {
102  // Load the P-256 app. Fails if OTBN is non-idle.
103  HARDENED_TRY(otbn_load_app(kOtbnAppP256));
104 
105  // Set mode so start() will jump into keygen.
106  uint32_t mode = kOtbnP256ModeKeygen;
107  HARDENED_TRY(otbn_dmem_write(kOtbnP256ModeWords, &mode, kOtbnVarMode));
108 
109  // Start the OTBN routine.
110  return otbn_execute();
111 }
112 
113 status_t p256_sideload_keygen_start(void) {
114  // Load the P-256 app. Fails if OTBN is non-idle.
115  HARDENED_TRY(otbn_load_app(kOtbnAppP256));
116 
117  // Set mode so start() will jump into sideload-keygen.
118  uint32_t mode = kOtbnP256ModeSideloadKeygen;
119  HARDENED_TRY(otbn_dmem_write(kOtbnP256ModeWords, &mode, kOtbnVarMode));
120 
121  // Start the OTBN routine.
122  return otbn_execute();
123 }
124 
125 status_t p256_keygen_finalize(p256_masked_scalar_t *private_key,
126  p256_point_t *public_key) {
127  // Spin here waiting for OTBN to complete.
128  HARDENED_TRY(otbn_busy_wait_for_done());
129 
130  // Read the masked private key from OTBN dmem.
131  HARDENED_TRY(otbn_dmem_read(kP256MaskedScalarShareWords, kOtbnVarD0,
132  private_key->share0));
133  HARDENED_TRY(otbn_dmem_read(kP256MaskedScalarShareWords, kOtbnVarD1,
134  private_key->share1));
135 
136  // Read the public key from OTBN dmem.
137  HARDENED_TRY(otbn_dmem_read(kP256CoordWords, kOtbnVarX, public_key->x));
138  HARDENED_TRY(otbn_dmem_read(kP256CoordWords, kOtbnVarY, public_key->y));
139 
140  // Wipe DMEM.
141  HARDENED_TRY(otbn_dmem_sec_wipe());
142 
143  return OTCRYPTO_OK;
144 }
145 
146 status_t p256_sideload_keygen_finalize(p256_point_t *public_key) {
147  // Spin here waiting for OTBN to complete.
148  HARDENED_TRY(otbn_busy_wait_for_done());
149 
150  // Read the public key from OTBN dmem.
151  HARDENED_TRY(otbn_dmem_read(kP256CoordWords, kOtbnVarX, public_key->x));
152  HARDENED_TRY(otbn_dmem_read(kP256CoordWords, kOtbnVarY, public_key->y));
153 
154  // Wipe DMEM.
155  HARDENED_TRY(otbn_dmem_sec_wipe());
156 
157  return OTCRYPTO_OK;
158 }
159 
160 /**
161  * Set the message digest for signature generation or verification.
162  *
163  * OTBN requires the digest in little-endian form, so this routine flips the
164  * bytes.
165  *
166  * @param digest Digest to set (big-endian).
167  * @return OK or error.
168  */
169 static status_t set_message_digest(const uint32_t digest[kP256ScalarWords]) {
170  // Set the message digest. We swap all the bytes so that OTBN can interpret
171  // the digest as a little-endian integer, which is a more natural fit for the
172  // architecture than the big-endian form requested by the specification (FIPS
173  // 186-5, section B.2.1).
174  uint32_t digest_little_endian[kP256ScalarWords];
175  size_t i = 0;
176  for (; launder32(i) < kP256ScalarWords; i++) {
177  digest_little_endian[i] =
178  __builtin_bswap32(digest[kP256ScalarWords - 1 - i]);
179  }
180  HARDENED_CHECK_EQ(i, kP256ScalarWords);
181  return otbn_dmem_write(kP256ScalarWords, digest_little_endian, kOtbnVarMsg);
182 }
183 
184 status_t p256_ecdsa_sign_start(const uint32_t digest[kP256ScalarWords],
185  const p256_masked_scalar_t *private_key) {
186  // Load the P-256 app. Fails if OTBN is non-idle.
187  HARDENED_TRY(otbn_load_app(kOtbnAppP256));
188 
189  // Set mode so start() will jump into signing.
190  uint32_t mode = kOtbnP256ModeSign;
191  HARDENED_TRY(otbn_dmem_write(kOtbnP256ModeWords, &mode, kOtbnVarMode));
192 
193  // Set the message digest.
194  HARDENED_TRY(set_message_digest(digest));
195 
196  // Set the private key shares.
197  HARDENED_TRY(p256_masked_scalar_write(private_key, kOtbnVarD0, kOtbnVarD1));
198 
199  // Start the OTBN routine.
200  return otbn_execute();
201 }
202 
203 status_t p256_ecdsa_sideload_sign_start(
204  const uint32_t digest[kP256ScalarWords]) {
205  // Load the P-256 app. Fails if OTBN is non-idle.
206  HARDENED_TRY(otbn_load_app(kOtbnAppP256));
207 
208  // Set mode so start() will jump into sideloaded signing.
209  uint32_t mode = kOtbnP256ModeSideloadSign;
210  HARDENED_TRY(otbn_dmem_write(kOtbnP256ModeWords, &mode, kOtbnVarMode));
211 
212  // Set the message digest.
213  HARDENED_TRY(set_message_digest(digest));
214 
215  // Start the OTBN routine.
216  return otbn_execute();
217 }
218 
219 status_t p256_ecdsa_sign_finalize(p256_ecdsa_signature_t *result) {
220  // Spin here waiting for OTBN to complete.
221  HARDENED_TRY(otbn_busy_wait_for_done());
222 
223  // Read signature R out of OTBN dmem.
224  HARDENED_TRY(otbn_dmem_read(kP256ScalarWords, kOtbnVarR, result->r));
225 
226  // Read signature S out of OTBN dmem.
227  HARDENED_TRY(otbn_dmem_read(kP256ScalarWords, kOtbnVarS, result->s));
228 
229  // Wipe DMEM.
230  HARDENED_TRY(otbn_dmem_sec_wipe());
231 
232  return OTCRYPTO_OK;
233 }
234 
235 status_t p256_ecdsa_verify_start(const p256_ecdsa_signature_t *signature,
236  const uint32_t digest[kP256ScalarWords],
237  const p256_point_t *public_key) {
238  // Load the P-256 app and set up data pointers
239  HARDENED_TRY(otbn_load_app(kOtbnAppP256));
240 
241  // Set mode so start() will jump into verifying.
242  uint32_t mode = kOtbnP256ModeVerify;
243  HARDENED_TRY(otbn_dmem_write(kOtbnP256ModeWords, &mode, kOtbnVarMode));
244 
245  // Set the message digest.
246  HARDENED_TRY(set_message_digest(digest));
247 
248  // Set the signature R.
249  HARDENED_TRY(otbn_dmem_write(kP256ScalarWords, signature->r, kOtbnVarR));
250 
251  // Set the signature S.
252  HARDENED_TRY(otbn_dmem_write(kP256ScalarWords, signature->s, kOtbnVarS));
253 
254  // Set the public key x coordinate.
255  HARDENED_TRY(otbn_dmem_write(kP256CoordWords, public_key->x, kOtbnVarX));
256 
257  // Set the public key y coordinate.
258  HARDENED_TRY(otbn_dmem_write(kP256CoordWords, public_key->y, kOtbnVarY));
259 
260  // Start the OTBN routine.
261  return otbn_execute();
262 }
263 
264 status_t p256_ecdsa_verify_finalize(const p256_ecdsa_signature_t *signature,
265  hardened_bool_t *result) {
266  // Spin here waiting for OTBN to complete.
267  HARDENED_TRY(otbn_busy_wait_for_done());
268 
269  // Read the status code out of DMEM (false if basic checks on the validity of
270  // the signature and public key failed).
271  uint32_t ok;
272  HARDENED_TRY(otbn_dmem_read(1, kOtbnVarOk, &ok));
273  if (launder32(ok) != kHardenedBoolTrue) {
274  return OTCRYPTO_BAD_ARGS;
275  }
277 
278  // Read x_r (recovered R) out of OTBN dmem.
279  uint32_t x_r[kP256ScalarWords];
280  HARDENED_TRY(otbn_dmem_read(kP256ScalarWords, kOtbnVarXr, x_r));
281 
282  *result = hardened_memeq(x_r, signature->r, kP256ScalarWords);
283 
284  // Wipe DMEM.
285  HARDENED_TRY(otbn_dmem_sec_wipe());
286 
287  return OTCRYPTO_OK;
288 }
289 
290 status_t p256_ecdh_start(const p256_masked_scalar_t *private_key,
291  const p256_point_t *public_key) {
292  // Load the P-256 app. Fails if OTBN is non-idle.
293  HARDENED_TRY(otbn_load_app(kOtbnAppP256));
294 
295  // Set mode so start() will jump into shared-key generation.
296  uint32_t mode = kOtbnP256ModeEcdh;
297  HARDENED_TRY(otbn_dmem_write(kOtbnP256ModeWords, &mode, kOtbnVarMode));
298 
299  // Set the private key shares.
300  HARDENED_TRY(p256_masked_scalar_write(private_key, kOtbnVarD0, kOtbnVarD1));
301 
302  // Set the public key x coordinate.
303  HARDENED_TRY(otbn_dmem_write(kP256CoordWords, public_key->x, kOtbnVarX));
304 
305  // Set the public key y coordinate.
306  HARDENED_TRY(otbn_dmem_write(kP256CoordWords, public_key->y, kOtbnVarY));
307 
308  // Start the OTBN routine.
309  return otbn_execute();
310 }
311 
312 status_t p256_ecdh_finalize(p256_ecdh_shared_key_t *shared_key) {
313  // Spin here waiting for OTBN to complete.
314  HARDENED_TRY(otbn_busy_wait_for_done());
315 
316  // Read the code indicating if the public key is valid.
317  uint32_t ok;
318  HARDENED_TRY(otbn_dmem_read(1, kOtbnVarOk, &ok));
319  if (launder32(ok) != kHardenedBoolTrue) {
320  return OTCRYPTO_BAD_ARGS;
321  }
323 
324  // Read the shares of the key from OTBN dmem (at vars x and y).
325  HARDENED_TRY(otbn_dmem_read(kP256CoordWords, kOtbnVarX, shared_key->share0));
326  HARDENED_TRY(otbn_dmem_read(kP256CoordWords, kOtbnVarY, shared_key->share1));
327 
328  // Wipe DMEM.
329  HARDENED_TRY(otbn_dmem_sec_wipe());
330 
331  return OTCRYPTO_OK;
332 }
333 
334 status_t p256_sideload_ecdh_start(const p256_point_t *public_key) {
335  // Load the P-256 app. Fails if OTBN is non-idle.
336  HARDENED_TRY(otbn_load_app(kOtbnAppP256));
337 
338  // Set mode so start() will jump into shared-key generation.
339  uint32_t mode = kOtbnP256ModeSideloadEcdh;
340  HARDENED_TRY(otbn_dmem_write(kOtbnP256ModeWords, &mode, kOtbnVarMode));
341 
342  // Set the public key x coordinate.
343  HARDENED_TRY(otbn_dmem_write(kP256CoordWords, public_key->x, kOtbnVarX));
344 
345  // Set the public key y coordinate.
346  HARDENED_TRY(otbn_dmem_write(kP256CoordWords, public_key->y, kOtbnVarY));
347 
348  // Start the OTBN routine.
349  return otbn_execute();
350 }