Software APIs
ecdsa_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/ecdsa_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', 's')
15 
16 // Declare the OTBN app.
17 OTBN_DECLARE_APP_SYMBOLS(p256_ecdsa); // The OTBN ECDSA/P-256 app.
18 static const otbn_app_t kOtbnAppEcdsa = OTBN_APP_T_INIT(p256_ecdsa);
19 
20 // Declare offsets for input and output buffers.
21 OTBN_DECLARE_SYMBOL_ADDR(p256_ecdsa, mode); // ECDSA mode (sign or verify).
22 OTBN_DECLARE_SYMBOL_ADDR(p256_ecdsa, msg); // Message digest.
23 OTBN_DECLARE_SYMBOL_ADDR(p256_ecdsa, r); // The signature scalar R.
24 OTBN_DECLARE_SYMBOL_ADDR(p256_ecdsa, s); // The signature scalar S.
25 OTBN_DECLARE_SYMBOL_ADDR(p256_ecdsa, x); // The public key x-coordinate.
26 OTBN_DECLARE_SYMBOL_ADDR(p256_ecdsa, y); // The public key y-coordinate.
27 OTBN_DECLARE_SYMBOL_ADDR(p256_ecdsa,
28  d0); // The private key scalar d (share 0).
29 OTBN_DECLARE_SYMBOL_ADDR(p256_ecdsa,
30  d1); // The private key scalar d (share 1).
31 OTBN_DECLARE_SYMBOL_ADDR(p256_ecdsa, x_r); // Verification result.
32 OTBN_DECLARE_SYMBOL_ADDR(p256_ecdsa, ok); // Status code.
33 
34 static const otbn_addr_t kOtbnVarEcdsaMode = OTBN_ADDR_T_INIT(p256_ecdsa, mode);
35 static const otbn_addr_t kOtbnVarEcdsaMsg = OTBN_ADDR_T_INIT(p256_ecdsa, msg);
36 static const otbn_addr_t kOtbnVarEcdsaR = OTBN_ADDR_T_INIT(p256_ecdsa, r);
37 static const otbn_addr_t kOtbnVarEcdsaS = OTBN_ADDR_T_INIT(p256_ecdsa, s);
38 static const otbn_addr_t kOtbnVarEcdsaX = OTBN_ADDR_T_INIT(p256_ecdsa, x);
39 static const otbn_addr_t kOtbnVarEcdsaY = OTBN_ADDR_T_INIT(p256_ecdsa, y);
40 static const otbn_addr_t kOtbnVarEcdsaD0 = OTBN_ADDR_T_INIT(p256_ecdsa, d0);
41 static const otbn_addr_t kOtbnVarEcdsaD1 = OTBN_ADDR_T_INIT(p256_ecdsa, d1);
42 static const otbn_addr_t kOtbnVarEcdsaXr = OTBN_ADDR_T_INIT(p256_ecdsa, x_r);
43 static const otbn_addr_t kOtbnVarEcdsaOk = OTBN_ADDR_T_INIT(p256_ecdsa, ok);
44 
45 // Declare mode constants.
46 OTBN_DECLARE_SYMBOL_ADDR(p256_ecdsa, MODE_KEYGEN);
47 OTBN_DECLARE_SYMBOL_ADDR(p256_ecdsa, MODE_SIGN);
48 OTBN_DECLARE_SYMBOL_ADDR(p256_ecdsa, MODE_VERIFY);
49 OTBN_DECLARE_SYMBOL_ADDR(p256_ecdsa, MODE_SIDELOAD_KEYGEN);
50 OTBN_DECLARE_SYMBOL_ADDR(p256_ecdsa, MODE_SIDELOAD_SIGN);
51 static const uint32_t kOtbnEcdsaModeKeygen =
52  OTBN_ADDR_T_INIT(p256_ecdsa, MODE_KEYGEN);
53 static const uint32_t kOtbnEcdsaModeSign =
54  OTBN_ADDR_T_INIT(p256_ecdsa, MODE_SIGN);
55 static const uint32_t kOtbnEcdsaModeVerify =
56  OTBN_ADDR_T_INIT(p256_ecdsa, MODE_VERIFY);
57 static const uint32_t kOtbnEcdsaModeSideloadKeygen =
58  OTBN_ADDR_T_INIT(p256_ecdsa, MODE_SIDELOAD_KEYGEN);
59 static const uint32_t kOtbnEcdsaModeSideloadSign =
60  OTBN_ADDR_T_INIT(p256_ecdsa, MODE_SIDELOAD_SIGN);
61 
62 enum {
63  /*
64  * Mode is represented by a single word.
65  */
66  kOtbnEcdsaModeWords = 1,
67 };
68 
69 status_t ecdsa_p256_keygen_start(void) {
70  // Load the ECDSA/P-256 app. Fails if OTBN is non-idle.
71  HARDENED_TRY(otbn_load_app(kOtbnAppEcdsa));
72 
73  // Set mode so start() will jump into keygen.
74  uint32_t mode = kOtbnEcdsaModeKeygen;
75  HARDENED_TRY(otbn_dmem_write(kOtbnEcdsaModeWords, &mode, kOtbnVarEcdsaMode));
76 
77  // Start the OTBN routine.
78  return otbn_execute();
79 }
80 
81 status_t ecdsa_p256_sideload_keygen_start(void) {
82  // Load the ECDSA/P-256 app. Fails if OTBN is non-idle.
83  HARDENED_TRY(otbn_load_app(kOtbnAppEcdsa));
84 
85  // Set mode so start() will jump into sideload-keygen.
86  uint32_t mode = kOtbnEcdsaModeSideloadKeygen;
87  HARDENED_TRY(otbn_dmem_write(kOtbnEcdsaModeWords, &mode, kOtbnVarEcdsaMode));
88 
89  // Start the OTBN routine.
90  return otbn_execute();
91 }
92 
93 status_t ecdsa_p256_keygen_finalize(p256_masked_scalar_t *private_key,
94  p256_point_t *public_key) {
95  // Spin here waiting for OTBN to complete.
96  HARDENED_TRY(otbn_busy_wait_for_done());
97 
98  // Read the masked private key from OTBN dmem.
99  HARDENED_TRY(otbn_dmem_read(kP256MaskedScalarShareWords, kOtbnVarEcdsaD0,
100  private_key->share0));
101  HARDENED_TRY(otbn_dmem_read(kP256MaskedScalarShareWords, kOtbnVarEcdsaD1,
102  private_key->share1));
103 
104  // Read the public key from OTBN dmem.
105  HARDENED_TRY(otbn_dmem_read(kP256CoordWords, kOtbnVarEcdsaX, public_key->x));
106  HARDENED_TRY(otbn_dmem_read(kP256CoordWords, kOtbnVarEcdsaY, public_key->y));
107 
108  // Wipe DMEM.
109  HARDENED_TRY(otbn_dmem_sec_wipe());
110 
111  return OTCRYPTO_OK;
112 }
113 
114 status_t ecdsa_p256_sideload_keygen_finalize(p256_point_t *public_key) {
115  // Spin here waiting for OTBN to complete.
116  HARDENED_TRY(otbn_busy_wait_for_done());
117 
118  // Read the public key from OTBN dmem.
119  HARDENED_TRY(otbn_dmem_read(kP256CoordWords, kOtbnVarEcdsaX, public_key->x));
120  HARDENED_TRY(otbn_dmem_read(kP256CoordWords, kOtbnVarEcdsaY, public_key->y));
121 
122  // Wipe DMEM.
123  HARDENED_TRY(otbn_dmem_sec_wipe());
124 
125  return OTCRYPTO_OK;
126 }
127 
128 /**
129  * Set the message digest for signature generation or verification.
130  *
131  * OTBN requires the digest in little-endian form, so this routine flips the
132  * bytes.
133  *
134  * @param digest Digest to set (big-endian).
135  * @return OK or error.
136  */
137 static status_t set_message_digest(const uint32_t digest[kP256ScalarWords]) {
138  // Set the message digest. We swap all the bytes so that OTBN can interpret
139  // the digest as a little-endian integer, which is a more natural fit for the
140  // architecture than the big-endian form requested by the specification (FIPS
141  // 186-5, section B.2.1).
142  uint32_t digest_little_endian[kP256ScalarWords];
143  size_t i = 0;
144  for (; launder32(i) < kP256ScalarWords; i++) {
145  digest_little_endian[i] =
146  __builtin_bswap32(digest[kP256ScalarWords - 1 - i]);
147  }
148  HARDENED_CHECK_EQ(i, kP256ScalarWords);
149  return otbn_dmem_write(kP256ScalarWords, digest_little_endian,
150  kOtbnVarEcdsaMsg);
151 }
152 
153 status_t ecdsa_p256_sign_start(const uint32_t digest[kP256ScalarWords],
154  const p256_masked_scalar_t *private_key) {
155  // Load the ECDSA/P-256 app. Fails if OTBN is non-idle.
156  HARDENED_TRY(otbn_load_app(kOtbnAppEcdsa));
157 
158  // Set mode so start() will jump into signing.
159  uint32_t mode = kOtbnEcdsaModeSign;
160  HARDENED_TRY(otbn_dmem_write(kOtbnEcdsaModeWords, &mode, kOtbnVarEcdsaMode));
161 
162  // Set the message digest.
163  HARDENED_TRY(set_message_digest(digest));
164 
165  // Set the private key shares.
166  HARDENED_TRY(
167  p256_masked_scalar_write(private_key, kOtbnVarEcdsaD0, kOtbnVarEcdsaD1));
168 
169  // Start the OTBN routine.
170  return otbn_execute();
171 }
172 
173 status_t ecdsa_p256_sideload_sign_start(
174  const uint32_t digest[kP256ScalarWords]) {
175  // Load the ECDSA/P-256 app. Fails if OTBN is non-idle.
176  HARDENED_TRY(otbn_load_app(kOtbnAppEcdsa));
177 
178  // Set mode so start() will jump into sideloaded signing.
179  uint32_t mode = kOtbnEcdsaModeSideloadSign;
180  HARDENED_TRY(otbn_dmem_write(kOtbnEcdsaModeWords, &mode, kOtbnVarEcdsaMode));
181 
182  // Set the message digest.
183  HARDENED_TRY(set_message_digest(digest));
184 
185  // Start the OTBN routine.
186  return otbn_execute();
187 }
188 
189 status_t ecdsa_p256_sign_finalize(ecdsa_p256_signature_t *result) {
190  // Spin here waiting for OTBN to complete.
191  HARDENED_TRY(otbn_busy_wait_for_done());
192 
193  // Read signature R out of OTBN dmem.
194  HARDENED_TRY(otbn_dmem_read(kP256ScalarWords, kOtbnVarEcdsaR, result->r));
195 
196  // Read signature S out of OTBN dmem.
197  HARDENED_TRY(otbn_dmem_read(kP256ScalarWords, kOtbnVarEcdsaS, result->s));
198 
199  // Wipe DMEM.
200  HARDENED_TRY(otbn_dmem_sec_wipe());
201 
202  return OTCRYPTO_OK;
203 }
204 
205 status_t ecdsa_p256_verify_start(const ecdsa_p256_signature_t *signature,
206  const uint32_t digest[kP256ScalarWords],
207  const p256_point_t *public_key) {
208  // Load the ECDSA/P-256 app and set up data pointers
209  HARDENED_TRY(otbn_load_app(kOtbnAppEcdsa));
210 
211  // Set mode so start() will jump into verifying.
212  uint32_t mode = kOtbnEcdsaModeVerify;
213  HARDENED_TRY(otbn_dmem_write(kOtbnEcdsaModeWords, &mode, kOtbnVarEcdsaMode));
214 
215  // Set the message digest.
216  HARDENED_TRY(set_message_digest(digest));
217 
218  // Set the signature R.
219  HARDENED_TRY(otbn_dmem_write(kP256ScalarWords, signature->r, kOtbnVarEcdsaR));
220 
221  // Set the signature S.
222  HARDENED_TRY(otbn_dmem_write(kP256ScalarWords, signature->s, kOtbnVarEcdsaS));
223 
224  // Set the public key x coordinate.
225  HARDENED_TRY(otbn_dmem_write(kP256CoordWords, public_key->x, kOtbnVarEcdsaX));
226 
227  // Set the public key y coordinate.
228  HARDENED_TRY(otbn_dmem_write(kP256CoordWords, public_key->y, kOtbnVarEcdsaY));
229 
230  // Start the OTBN routine.
231  return otbn_execute();
232 }
233 
234 status_t ecdsa_p256_verify_finalize(const ecdsa_p256_signature_t *signature,
235  hardened_bool_t *result) {
236  // Spin here waiting for OTBN to complete.
237  HARDENED_TRY(otbn_busy_wait_for_done());
238 
239  // Read the status code out of DMEM (false if basic checks on the validity of
240  // the signature and public key failed).
241  uint32_t ok;
242  HARDENED_TRY(otbn_dmem_read(1, kOtbnVarEcdsaOk, &ok));
243  if (launder32(ok) != kHardenedBoolTrue) {
244  return OTCRYPTO_BAD_ARGS;
245  }
247 
248  // Read x_r (recovered R) out of OTBN dmem.
249  uint32_t x_r[kP256ScalarWords];
250  HARDENED_TRY(otbn_dmem_read(kP256ScalarWords, kOtbnVarEcdsaXr, x_r));
251 
252  *result = hardened_memeq(x_r, signature->r, kP256ScalarWords);
253 
254  // Wipe DMEM.
255  HARDENED_TRY(otbn_dmem_sec_wipe());
256 
257  return OTCRYPTO_OK;
258 }