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 */
20static 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
32void 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 */
59static const uint32_t kCheckShares[kHmacDigestNumWords + 3] = {
60 0xc038253c, 0xfa1ebc13, 0x608b15e1, 0x883053ed, 0x3d28e980, 0x16009f6e,
61 0xa7944bde, 0x3c096b6f, 0xe2828469, 0x2d507673, 0xefee6c10,
62};
63
64rom_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}