Software APIs
spx_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/spx_verify.h"
6 
9 #include "sw/device/silicon_creator/lib/drivers/otp.h"
10 #include "sw/device/silicon_creator/lib/sigverify/sphincsplus/verify.h"
11 
12 #include "otp_ctrl_regs.h"
13 
14 // Declared as a weak symbol so that we can override in tests. See
15 // spx_verify_functest.c.
16 OT_WEAK
17 uint32_t sigverify_spx_verify_enabled(lifecycle_state_t lc_state) {
18  switch (launder32(lc_state)) {
19  case kLcStateTest:
20  HARDENED_CHECK_EQ(lc_state, kLcStateTest);
21  // Don't read from OTP during manufacturing. Disable SPX+ signature
22  // verification by default.
23  return kSigverifySpxDisabledOtp;
24  case kLcStateDev:
25  HARDENED_CHECK_EQ(lc_state, kLcStateDev);
26  return otp_read32(OTP_CTRL_PARAM_CREATOR_SW_CFG_SIGVERIFY_SPX_EN_OFFSET);
27  case kLcStateProd:
28  HARDENED_CHECK_EQ(lc_state, kLcStateProd);
29  return otp_read32(OTP_CTRL_PARAM_CREATOR_SW_CFG_SIGVERIFY_SPX_EN_OFFSET);
30  case kLcStateProdEnd:
31  HARDENED_CHECK_EQ(lc_state, kLcStateProdEnd);
32  return otp_read32(OTP_CTRL_PARAM_CREATOR_SW_CFG_SIGVERIFY_SPX_EN_OFFSET);
33  case kLcStateRma:
34  HARDENED_CHECK_EQ(lc_state, kLcStateRma);
35  return otp_read32(OTP_CTRL_PARAM_CREATOR_SW_CFG_SIGVERIFY_SPX_EN_OFFSET);
36  default:
37  HARDENED_TRAP();
39  }
40 }
41 
42 /**
43  * Shares for producing the `flash_exec_spx` value in `sigverify_spx_verify()`
44  * when SPHINCS+ signature verification is enabled. First 3 shares are generated
45  * using the `sparse-fsm-encode` script while the last share is
46  * `kSigverifySpxSuccess ^ kSpxShares[0] ^ ... ^ kSpxShares[2]` so that xor'ing
47  * all shares produces `kSigverifySpxSuccess`.
48  *
49  * Encoding generated with
50  * $ ./util/design/sparse-fsm-encode.py -d 5 -m 3 -n 32 \
51  * -s 327911352 --language=c
52  *
53  * Minimum Hamming distance: 15
54  * Maximum Hamming distance: 19
55  * Minimum Hamming weight: 10
56  * Maximum Hamming weight: 17
57  */
58 static const uint32_t kSpxVerifyShares[kSigverifySpxRootNumWords] = {
59  0xa5bda1e8,
60  0x229044e4,
61  0x94eadad8,
62  0x9eabb3c3,
63 };
64 
65 /**
66  * Domain-separation prefix for SPHINCS+ with no prehashing.
67  *
68  * The domain separation prefix is the following byte sequence:
69  * 0x00 || len(ctx) || ctx
70  *
71  * In our case, `ctx` is always the empty string, so the length is 0.
72  */
73 static const uint8_t kSpxVerifyPureDomainSep[] = {
74  0x00,
75  0x00,
76 };
77 
78 /**
79  * Domain-separation prefix for SPHINCS+ with SHA256 prehashing.
80  *
81  * The domain separation prefix is the following byte sequence:
82  * 0x01 || len(ctx) || ctx || OID(PH)
83  *
84  * In our case, `ctx` is always the empty string and PH (the pre-hashing
85  * function) is always SHA256.
86  */
87 static const uint8_t kSpxVerifyPrehashDomainSep[] = {
88  0x01, 0x00, 0x06, 0x09, 0x60, 0x86, 0x48,
89  0x01, 0x65, 0x03, 0x04, 0x02, 0x01};
90 
91 rom_error_t sigverify_spx_verify(
92  const sigverify_spx_signature_t *signature, const sigverify_spx_key_t *key,
93  const sigverify_spx_config_id_t config, lifecycle_state_t lc_state,
94  const void *msg_prefix_1, size_t msg_prefix_1_len, const void *msg_prefix_2,
95  size_t msg_prefix_2_len, const void *msg, size_t msg_len,
96  const hmac_digest_t *digest, uint32_t *flash_exec) {
97  uint32_t spx_en = launder32(sigverify_spx_verify_enabled(lc_state));
98  rom_error_t error = kErrorSigverifyBadSpxSignature;
99  if (launder32(spx_en) != kSigverifySpxDisabledOtp) {
100  sigverify_spx_root_t expected_root;
101  spx_public_key_root(key->data, expected_root.data);
102  sigverify_spx_root_t actual_root;
103  if (launder32(config) == kSigverifySpxConfigIdSha2128sPrehash) {
104  HARDENED_CHECK_EQ(config, kSigverifySpxConfigIdSha2128sPrehash);
105  HARDENED_RETURN_IF_ERROR(
106  spx_verify(signature->data, kSpxVerifyPrehashDomainSep,
107  sizeof(kSpxVerifyPrehashDomainSep),
108  /*msg_prefix_2=*/NULL, /*msg_prefix_2_len=*/0,
109  /*msg_prefix_3=*/NULL, /*msg_prefix_3_len=*/0,
110  (unsigned char *)digest->digest, sizeof(digest->digest),
111  key->data, actual_root.data));
112  } else if (launder32(config) == kSigverifySpxConfigIdSha2128s) {
113  HARDENED_CHECK_EQ(config, kSigverifySpxConfigIdSha2128s);
114  HARDENED_RETURN_IF_ERROR(
115  spx_verify(signature->data, kSpxVerifyPureDomainSep,
116  sizeof(kSpxVerifyPureDomainSep), msg_prefix_1,
117  msg_prefix_1_len, msg_prefix_2, msg_prefix_2_len, msg,
118  msg_len, key->data, actual_root.data));
119  } else {
120  // Unsupported SPHINCS+ configuration.
121  return kErrorSigverifyBadSpxConfig;
122  }
123 
124  size_t i = 0;
125  for (; launder32(i) < kSigverifySpxRootNumWords; ++i) {
126  actual_root.data[i] ^= expected_root.data[i] ^ kSpxVerifyShares[i];
127  }
128  HARDENED_CHECK_EQ(i, kSigverifySpxRootNumWords);
129  uint32_t flash_exec_spx = 0;
130  uint32_t diff = 0;
131  for (--i; launder32(i) < kSigverifySpxRootNumWords; --i) {
132  // Following three statements set `diff` to `UINT32_MAX` if
133  // `actual_root[i]` is incorrect, no change otherwise.
134  diff |= actual_root.data[i] ^ kSpxVerifyShares[i];
135  diff |= ~diff + 1; // Set upper bits to 1 if not 0, no change o/w.
136  diff |= ~(diff >> 31) + 1; // Set to all 1s if MSB is set, no change o/w.
137 
138  flash_exec_spx ^= actual_root.data[i];
139  // Set `flash_exec_spx` to `UINT32_MAX` if `actual_root` is incorrect.
140  flash_exec_spx |= diff;
141  }
142  HARDENED_CHECK_EQ(i, SIZE_MAX);
143  error = sigverify_spx_success_to_ok(flash_exec_spx);
144  *flash_exec ^= flash_exec_spx;
145  } else {
146  HARDENED_CHECK_EQ(spx_en, kSigverifySpxDisabledOtp);
147  *flash_exec ^= spx_en;
148  uint32_t otp_val = sigverify_spx_verify_enabled(lc_state);
149  // Note: `kSigverifySpxSuccess` is defined such that the following operation
150  // produces `kErrorOk`.
151  error = sigverify_spx_success_to_ok(otp_val);
152  }
153  if (error != kErrorOk) {
154  return kErrorSigverifyBadSpxSignature;
155  }
156  return error;
157 }
158 
159 // Extern declarations for the inline functions in the header.
160 extern uint32_t sigverify_spx_success_to_ok(uint32_t v);