Software APIs
ecdsa_p256_verify.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/ecdsa_p256_verify.h"
6 
8 #include "sw/device/silicon_creator/lib/otbn_boot_services.h"
9 #include "sw/device/silicon_creator/lib/sigverify/ecdsa_p256_key.h"
10 
11 #include "otp_ctrl_regs.h"
12 
13 /*
14  * Shares for producing the `kSigverifyEcdsaSuccess` value in
15  * `sigverify_encoded_message_check)`. First 7 shares are generated using the
16  * `sparse-fsm-encode` script while the last share is `kSigverifySpxSuccess ^
17  * kSigverifyFlashExec ^ kShares[0] ^ ... ^ kShares[6]` ==
18  * `kSigverifyEcdsaSuccess`.
19  *
20  * It follows that:
21  *
22  * `kSigverifyFlashExec = kSigverifyEcdsaSuccess ^ kSigverifySpxSuccess`
23  *
24  * Where `kSigverifyFlashExec` is the value to write to the flash_ctrl to enable
25  * code execution.
26  *
27  * Encoding generated with
28  * $ ./util/design/sparse-fsm-encode.py -d 6 -m 7 -n 32 \
29  * -s 3118901143 --language=c
30  *
31  * Minimum Hamming distance: 9
32  * Maximum Hamming distance: 25
33  * Minimum Hamming weight: 14
34  * Maximum Hamming weight: 23
35  */
36 static const uint32_t kSigverifyShares[kEcdsaP256SignatureComponentWords] = {
37  0xaf28073b, 0x5eb7dcfb, 0x177240b5, 0xa8469df3,
38  0x2e92e9c0, 0x83ed133b, 0x0c9e99f0, 0xc04cd16d,
39 };
40 
41 /**
42  * Checks the encoded message and produces the value to write to the flash_ctrl.
43  *
44  * @param recovered_r Recovered r parameter, little-endian.
45  * @param signature Provided signature, little-endian.
46  * @param[out] flash_exec Value to write to the flash_ctrl EXEC register.
47  * @return Result of the operation.
48  */
50 static rom_error_t sigverify_encoded_message_check(
51  ecdsa_p256_signature_t *recovered_r,
52  const ecdsa_p256_signature_t *signature, uint32_t *flash_exec) {
53  // The algorithm below uses shares, i.e. trivial secret sharing, to check an
54  // encoded message and produce two values: `flash_exec` and `result`.
55  // `flash_exec` is the value to write to the flash_ctrl EXEC register to
56  // unlock flash execution and `result` is the return value. We produce
57  // `result` in addition to `flash_exec` to avoid having the unlock value in
58  // registers or memory just for checking the result of signature verification.
59  // The algorithm consists of two steps:
60  //
61  // 1. First, we xor each word of `recovered_r` with the corresponding expected
62  // value and share (`kSigverifyShares[i]`). At the end of this step,
63  // `recovered_r` becomes `kSigverifyShares` if it's correct and garbage
64  // otherwise.
65  //
66  // 2. Next, we produce `flash_exec` and `result`. `flash_exec` is produced by
67  // xor'ing all words of `recovered_r` with each other. If `recovered_r` is
68  // correct, `flash_exec` will be `kSigverifyEcdsaSuccess` due to the way
69  // `kSigverifyShares` is defined. To make sure that we don't produce this
70  // value otherwise, we compare each word of `recovered_r` with the
71  // corresponding expected value and set `flash_exec` to `UINT32_MAX` at each
72  // iteration if there is a mismatch. Finally, we produce the return value
73  // `result` from `flash_exec` by xor'ing parts of it together. Note that the
74  // hardware constant `kSigverifyEcdsaSuccess` is chosen such that this
75  // operation results in `kErrorOk`.
76 
77  // Step 1: Process `recovered_r` so that it becomes `kSigverifyShares` if it's
78  // correct, garbage otherwise.
79  static_assert(sizeof(recovered_r->r) == sizeof(signature->r),
80  "Signature sizes must match");
81  static_assert(ARRAYSIZE(signature->r) == kEcdsaP256SignatureComponentWords,
82  "Signature sizes must match");
83 
84  uint32_t *recovered_r_ptr = recovered_r->r;
85  size_t i = 0;
86  for (size_t j = 0; launder32(j) < kEcdsaP256SignatureComponentWords;
87  ++j, ++i) {
88  recovered_r_ptr[i] ^= signature->r[j] ^ kSigverifyShares[i];
89  }
90  HARDENED_CHECK_EQ(i, kEcdsaP256SignatureComponentWords);
91 
92  // Step 2: Reduce `recovered_r` to produce the value to write to flash_ctrl
93  // EXEC register (`flash_exec`) and the return value (`result`).
94  uint32_t flash_exec_ecdsa = 0;
95  uint32_t diff = 0;
96  for (i = 0; launder32(i) < kEcdsaP256SignatureComponentWords; ++i) {
97  // Following three statements set `diff` to `UINT32_MAX` if `recovered_r[i]`
98  // is incorrect, no change otherwise.
99  diff |= recovered_r_ptr[i] ^ kSigverifyShares[i];
100  diff |= ~diff + 1; // Set upper bits to 1 if not 0, no change o/w.
101  diff |= ~(diff >> 31) + 1; // Set to all 1s if MSB is set, no change o/w.
102 
103  flash_exec_ecdsa ^= recovered_r_ptr[i];
104  // Set `flash_exec_ecdsa` to `UINT32_MAX` if `recovered_r` is incorrect.
105  flash_exec_ecdsa |= diff;
106  }
107  HARDENED_CHECK_EQ(i, kEcdsaP256SignatureComponentWords);
108 
109  // Note: `kSigverifyEcdsaSuccess` is defined such that the following operation
110  // produces `kErrorOk`.
111  rom_error_t result = sigverify_ecdsa_p256_success_to_ok(flash_exec_ecdsa);
112  *flash_exec ^= flash_exec_ecdsa;
113  if (launder32(result) == kErrorOk) {
114  HARDENED_CHECK_EQ(result, kErrorOk);
115  return result;
116  }
117 
118  return kErrorSigverifyBadEcdsaSignature;
119 }
120 
121 rom_error_t sigverify_ecdsa_p256_verify(const ecdsa_p256_signature_t *signature,
122  const ecdsa_p256_public_key_t *key,
123  const hmac_digest_t *act_digest,
124  uint32_t *flash_exec) {
125  ecdsa_p256_signature_t recovered_r;
126  rom_error_t error =
127  otbn_boot_sigverify(key, signature, act_digest, (uint32_t *)&recovered_r);
128  if (launder32(error) != kErrorOk) {
129  *flash_exec ^= UINT32_MAX;
130  return error;
131  }
132  HARDENED_CHECK_EQ(error, kErrorOk);
133  return sigverify_encoded_message_check(&recovered_r, signature, flash_exec);
134 }
135 
136 // Extern declarations for the inline functions in the header.
137 extern uint32_t sigverify_ecdsa_p256_success_to_ok(uint32_t v);