Software APIs
template.h
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 #ifndef OPENTITAN_SW_DEVICE_SILICON_CREATOR_LIB_CERT_TEMPLATE_H_
6 #define OPENTITAN_SW_DEVICE_SILICON_CREATOR_LIB_CERT_TEMPLATE_H_
7 
10 #include "sw/device/silicon_creator/lib/error.h"
11 
12 #ifdef __cplusplus
13 extern "C" {
14 #endif
15 
16 /**
17  * Structure holding the state of the template engine.
18  *
19  * The fields in this structure should be considered
20  * private and not be read or written directly.
21  *
22  * The following diagram shows the pointers location during encoding.
23  * Takes Cdi0 TBS as as example:
24  *
25  * Template Const Bytes:
26  * `kTemplateConstBytes[]` `const_end` `+ sizeof(kTemplateConstBytes)`
27  * | Bytes Copied to Output | Unconsumed Template Const Bytes |
28  *
29  * `const_end` pointer will start from offset zero, and increase to
30  * `sizeof(kTemplateConstBytes)` at the end of encoding.
31  *
32  * Output Buffer:
33  * `out_begin` `out_end` `+ kCdi0MaxTbsSizeBytes`
34  * | Used Bytes | Remaining Unused Space |
35  *
36  * `out_begin` pointer will not change.
37  * `out_end` pointer starts from `out_begin` to the actual output size.
38  * The actual size is between [kCdi0MinTbsSizeBytes, kCdi0MaxTbsSizeBytes].
39  */
40 typedef struct template_state {
41  // Points to the remaining pre-computed const array from codegen.
42  const uint8_t *const_end;
43  // Points to the original start of the output buffer.
44  const uint8_t *out_begin;
45  // Points to the end of bytes already outputted.
46  uint8_t *out_end;
48 
49 /**
50  * Pointer to a specific output location.
51  */
52 typedef void *template_pos_t;
53 
54 /**
55  * Initialize the template engine.
56  *
57  * @param state Pointer to the template engine state.
58  * @param out_buf Pointer to a user-provided output buffer.
59  * @param const_bytes Pointer to a pre-generated template bytes.
60  */
61 static inline void template_init(template_state_t *state, uint8_t *out_buf,
62  const uint8_t *const_bytes) {
63  state->out_begin = state->out_end = out_buf;
64  state->const_end = const_bytes;
65 }
66 
67 /**
68  * Finalize template engine and set the actual output size.
69  *
70  * @param state Pointer to the template engine state.
71  * @return The generated size in bytes.
72  */
73 static inline uint16_t template_finalize(template_state_t *state) {
74  return (uint16_t)(state->out_end - state->out_begin);
75 }
76 
77 /**
78  * Set the `value` to the `bit_offset` bit of previous `byte_offset` byte.
79  *
80  * This function assume the corresponding bit is zero before calling, which
81  * is ensured by the template code generator.
82  *
83  * @param state Pointer to the template engine state.
84  * @param byte_offset Number of bytes before the last output bytes.
85  * @param bit_offset Index of the bit to be set.
86  * @param value Set the bit to one when true.
87  */
88 static inline void template_set_bit(template_state_t *state, int byte_offset,
89  int bit_offset, bool value) {
90  *(state->out_end - byte_offset) |= ((uint8_t) !!value) << bit_offset;
91 }
92 
93 /**
94  * Output a ASN1 DER boolean.
95  *
96  * @param state Pointer to the template engine state.
97  * @param val Value to be output.
98  */
99 static inline void template_push_asn1_bool(template_state_t *state, bool val) {
100  *state->out_end = val ? 0xff : 0x00;
101  state->out_end += 1;
102 }
103 
104 /**
105  * Private implementation of `template_push_hex`.
106  *
107  * @param out Pointer to the output buffer.
108  * @param inp Pointer to a byte array.
109  * @param size Number of the bytes in the array.
110  * @return the new end of the output buffer.
111  */
112 uint8_t *template_push_hex_impl(uint8_t *out, const uint8_t *inp, size_t size);
113 
114 /**
115  * Output the buffer as a hex-encoded string.
116  *
117  * @param state Pointer to the template engine state.
118  * @param buf Pointer to a byte array.
119  * @param size Number of the bytes in the array.
120  */
121 static inline void template_push_hex(template_state_t *state,
122  const uint8_t *buf, size_t size) {
123  state->out_end = template_push_hex_impl(state->out_end, buf, size);
124 }
125 
126 /**
127  * Output the buffer as raw bytes.
128  *
129  * @param state Pointer to the template engine state.
130  * @param buf Pointer to a byte array.
131  * @param size Number of the bytes in the array.
132  */
133 static inline void template_push_bytes(template_state_t *state,
134  const uint8_t *buf, size_t size) {
135  memcpy(state->out_end, buf, size);
136  state->out_end += size;
137 }
138 
139 /**
140  * Output `size` bytes from the pre-generated template.
141  *
142  * @param state Pointer to the template engine state.
143  * @param size Number of the bytes to output.
144  */
145 static inline void template_push_const(template_state_t *state, size_t size) {
146  memcpy(state->out_end, state->const_end, size);
147  state->out_end += size;
148  state->const_end += size;
149 }
150 
151 /**
152  * Private implementation of `template_asn1_integer`.
153  *
154  * @param out Pointer to the output buffer.
155  * @param tag Identifier octet of the tag.
156  * @param tweak_msb Set the MSB before encoding when true.
157  * @param bytes_be Pointer to a byte array holding an integer in big-endian
158  * format.
159  * @param size Size of the `bytes_be` array in bytes.
160  * @return the new end of the output buffer.
161  */
162 uint8_t *template_asn1_integer_impl(uint8_t *out, uint8_t tag, bool tweak_msb,
163  const uint8_t *bytes_be, size_t size);
164 
165 /**
166  * Output a tagged integer.
167  *
168  * This function allows the caller to set the tag to a non-standard value which
169  * can be useful for IMPLICIT integers. Use ASN1_TAG_INTEGER for standard
170  * integers.
171  *
172  * @param state Pointer to the template engine state.
173  * @param tag Identifier octet of the tag.
174  * @param tweak_msb Set the MSB before encoding when true.
175  * @param bytes_be Pointer to a byte array holding an integer in big-endian
176  * format.
177  * @param size Size of the `bytes_be` array in bytes.
178  */
179 static inline void template_asn1_integer(template_state_t *state, uint8_t tag,
180  bool tweak_msb,
181  const uint8_t *bytes_be, size_t size) {
182  state->out_end = template_asn1_integer_impl(state->out_end, tag, tweak_msb,
183  bytes_be, size);
184 }
185 
186 /**
187  * U32 version of the `template_asn1_integer`.
188  *
189  * @param state Pointer to the template engine state.
190  * @param tag Identifier octet of the tag.
191  * @param tweak_msb Set the MSB before encoding when true.
192  * @param value Integer value.
193  */
194 static inline void template_asn1_uint32(template_state_t *state, uint8_t tag,
195  bool tweak_msb, uint32_t value) {
196  uint32_t _value = __builtin_bswap32(value);
197  state->out_end = template_asn1_integer_impl(state->out_end, tag, tweak_msb,
198  (uint8_t *)&_value, 4);
199 }
200 
201 /**
202  * Memorize a location to be patched with the actual output size.
203  *
204  * The function will saved the address (last output byte + offset) for
205  * patching later, and it should be paired with a `template_patch_size_*`.
206  *
207  * @param state Pointer to the template engine state.
208  * @param offset Number of bytes after the last output byte.
209  * @return the memorized location for calling `template_patch_size_be`.
210  */
211 static inline template_pos_t template_save_pos(template_state_t *state,
212  ptrdiff_t offset) {
213  return (template_pos_t *)(state->out_end + offset);
214 }
215 
216 /**
217  * Private implementation of `template_patch_size_be`.
218  */
219 void template_patch_size_be_impl(template_pos_t memo, uint8_t *out_end);
220 
221 /**
222  * Add the actual output size after the memo to the patch location.
223  *
224  * The function will perform addition as BE u16 (i.e. mod 65536).
225  *
226  * @param state Pointer to the template engine state.
227  * @param memo The memorized location from `template_save_pos`.
228  */
229 static inline void template_patch_size_be(template_state_t *state,
230  template_pos_t memo) {
231  template_patch_size_be_impl(memo, state->out_end);
232 }
233 
234 /**
235  * Helpers for writing static assertions on variable array sizes.
236  */
237 #define ASSERT_VAR_SIZE_EQ(actual, expected) \
238  static_assert(actual == expected, "Invalid variable size.");
239 
240 #define ASSERT_VAR_SIZE_GE(actual, expected) \
241  static_assert(actual >= expected, "Invalid variable size.");
242 
243 #define ASSERT_VAR_SIZE_LE(actual, expected) \
244  static_assert(actual <= expected, "Invalid variable size.");
245 
246 /**
247  * Check if the given variable is fixed-length.
248  *
249  * @param template The name defined in the hjson template in upper camel case.
250  * @param field The variable name in upper camel case.
251  */
252 #define TEMPLATE_ASSERT_FIXED_LENGTH(template, field) \
253  ASSERT_VAR_SIZE_EQ(k##template##Min##field##SizeBytes, \
254  k##template##Max##field##SizeBytes);
255 
256 /**
257  * Check if the given size is within the range defined in the template.
258  *
259  * @param template The name defined in the hjson template in upper camel case.
260  * @param field The variable name in upper camel case.
261  * @param size The size of the variable value in bytes.
262  */
263 #define TEMPLATE_CHECK_SIZE(template, field, size) \
264  ASSERT_VAR_SIZE_GE((size), k##template##Min##field##SizeBytes); \
265  ASSERT_VAR_SIZE_LE((size), k##template##Max##field##SizeBytes);
266 
267 /**
268  * Set a template variable value and check its size.
269  *
270  * @param params Pointer to the generated *_values_t variable.
271  * @param template The name defined in the hjson template in upper camel case.
272  * @param field The variable name in upper camel case.
273  * @param value The value to set. It should be typed as a fixed size value.
274  */
275 #define TEMPLATE_SET(params, template, field, value) \
276  do { \
277  TEMPLATE_CHECK_SIZE(template, field, sizeof(value)); \
278  (params).k##template##Field##field = (void *)(value); \
279  } while (0)
280 
281 /**
282  * Set a template variable with the value array truncated to `size`.
283  *
284  * @param params Pointer to the generated *_values_t variable.
285  * @param template The name defined in the hjson template in upper camel case.
286  * @param field The variable name in upper camel case.
287  * @param value The value to set. It should be typed as a fixed size value.
288  * @param size The size after truncated.
289  */
290 #define TEMPLATE_SET_TRUNCATED(params, template, field, value, size) \
291  do { \
292  ASSERT_VAR_SIZE_GE(sizeof(value), size); \
293  TEMPLATE_CHECK_SIZE(template, field, size); \
294  (params).k##template##Field##field = (void *)(value); \
295  } while (0)
296 
297 #ifdef __cplusplus
298 } // extern "C"
299 #endif // __cplusplus
300 
301 #endif // OPENTITAN_SW_DEVICE_SILICON_CREATOR_LIB_CERT_TEMPLATE_H_