Software APIs
boot_svc_header.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/boot_svc/boot_svc_header.h"
6 
7 #include <stddef.h>
8 
9 #include "sw/device/silicon_creator/lib/drivers/hmac.h"
10 
11 /**
12  * Computes the digest of a boot services message.
13  *
14  * This function assumes that message payload starts immediately after the
15  * header and is exactly `length - sizeof(boot_svc_header_t)` bytes.
16  *
17  * @param header Header of a boot services message.
18  * @param[out] digest Output buffer for the digest.
19  */
20 static void boot_svc_header_digest_compute(const boot_svc_header_t *header,
21  hmac_digest_t *digest) {
22  enum {
23  kDigestRegionOffset = sizeof(header->digest),
24  };
25  static_assert(offsetof(boot_svc_header_t, digest) == 0,
26  "`digest` must be the first field of `boot_svc_header_t`.");
27 
28  hmac_sha256((const char *)header + kDigestRegionOffset,
29  header->length - kDigestRegionOffset, digest);
30 }
31 
32 void boot_svc_header_finalize(uint32_t type, uint32_t length,
33  boot_svc_header_t *header) {
34  header->identifier = kBootSvcIdentifier;
35  header->type = type;
36  header->length = length;
37  boot_svc_header_digest_compute(header, &header->digest);
38 }
39 
40 /**
41  * Shares for producing the `error` value in `boot_svc_header_check()`. First 8
42  * shares are used for checking the digest while the 9th and the 10th shares are
43  * used during the length check. First 10 shares are generated using the
44  * `sparse-fsm-encode` script while the last share is `kErrorOk ^
45  * kErrorBootSvcBadHeader ^ kBootSvcIdentifier ^ kCheckDigestShares[0] ^ ... ^
46  * kCheckDigestShares[7] ^ kCheckLengthShares[0] ^ kCheckLengthShares[1]` so
47  * that xor'ing all shares with the initial value of `error`, i.e.
48  * `kErrorBootSvcBadHeader`, and `kBootSvcIdentifier` produces `kErrorOk`.
49  *
50  * Encoding generated with
51  * $ ./util/design/sparse-fsm-encode.py -d 6 -m 10 -n 32 \
52  * -s 1606528195 --language=c
53 
54  * Minimum Hamming distance: 9
55  * Maximum Hamming distance: 22
56  * Minimum Hamming weight: 12
57  * Maximum Hamming weight: 18
58  */
59 static const uint32_t kCheckShares[kHmacDigestNumWords + 3] = {
60  0xc038253c, 0xfa1ebc13, 0x608b15e1, 0x883053ed, 0x3d28e980, 0x16009f6e,
61  0xa7944bde, 0x3c096b6f, 0xe2828469, 0x2d507673, 0xefee6c10,
62 };
63 
64 rom_error_t boot_svc_header_check(const boot_svc_header_t *header) {
65  rom_error_t error = kErrorBootSvcBadHeader;
66  hmac_digest_t digest;
67  boot_svc_header_digest_compute(header, &digest);
68 
69  size_t i = 0;
70  for (; launder32(i) < kHmacDigestNumWords; ++i) {
71  error ^= header->digest.digest[i] ^ digest.digest[i] ^ kCheckShares[i];
72  }
73 
74  if (launder32(header->length) >= CHIP_BOOT_SVC_MSG_HEADER_SIZE) {
75  HARDENED_CHECK_GE(header->length, CHIP_BOOT_SVC_MSG_HEADER_SIZE);
76  error ^= kCheckShares[i];
77  }
78 
79  ++i;
80  if (launder32(header->length) <= CHIP_BOOT_SVC_MSG_SIZE_MAX) {
81  HARDENED_CHECK_LE(header->length, CHIP_BOOT_SVC_MSG_SIZE_MAX);
82  error ^= kCheckShares[i];
83  }
84 
85  error ^= header->identifier ^ kCheckShares[++i];
86  if (launder32(error) == kErrorOk) {
87  HARDENED_CHECK_EQ(error, kErrorOk);
88  HARDENED_CHECK_EQ(i, ARRAYSIZE(kCheckShares) - 1);
89  return error;
90  }
91 
92  return kErrorBootSvcBadHeader;
93 }