Software APIs
asn1.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/cert/asn1.h"
6 
7 #include <limits.h>
8 
9 /**
10  * Return if the given asn1 state has an active error.
11  *
12  * @param state Pointer to a user-allocated `asn1_state_t` state.
13  */
14 #define RETURN_IF_ASN1_ERROR(state) \
15  do { \
16  if ((state)->error != kErrorOk) { \
17  return; \
18  } \
19  } while (false);
20 
21 /**
22  * Set the active error in asn1 state to `error_code` and return.
23  *
24  * @param state Pointer to a user-allocated `asn1_state_t` state.
25  */
26 #define RAISE_ASN1_ERROR(state, error_code) \
27  do { \
28  (state)->error = (error_code); \
29  return; \
30  } while (false);
31 
32 void asn1_clear_error(asn1_state_t *state) { state->error = kErrorOk; }
33 
34 rom_error_t asn1_start(asn1_state_t *new_state, uint8_t *buffer, size_t size) {
35  // Make sure that the buffer is not too large to prevent overflows.
36  if (new_state == NULL || buffer == NULL || size > PTRDIFF_MAX) {
37  return kErrorAsn1StartInvalidArgument;
38  }
39  new_state->buffer = buffer;
40  new_state->size = size;
41  new_state->offset = 0;
42  asn1_clear_error(new_state);
43  return kErrorOk;
44 }
45 
46 rom_error_t asn1_finish(asn1_state_t *state, size_t *out_size) {
47  rom_error_t result = state->error;
48  *out_size = state->offset;
49  state->buffer = NULL;
50  state->size = 0;
51  state->offset = 0;
52  asn1_clear_error(state);
53  return result;
54 }
55 
56 void asn1_push_byte(asn1_state_t *state, uint8_t byte) {
57  asn1_push_bytes(state, &byte, 1);
58 }
59 
60 void asn1_push_bytes(asn1_state_t *state, const uint8_t *bytes, size_t size) {
61  RETURN_IF_ASN1_ERROR(state);
62 
63  // Make sure that the addition will not overflow
64  if (size > PTRDIFF_MAX || state->offset > PTRDIFF_MAX) {
65  RAISE_ASN1_ERROR(state, kErrorAsn1PushBytesInvalidArgument);
66  }
67  if (state->offset + size > state->size) {
68  RAISE_ASN1_ERROR(state, kErrorAsn1BufferExhausted);
69  }
70  memcpy(state->buffer + state->offset, bytes, size);
71  state->offset += size;
72 }
73 
74 void asn1_start_tag(asn1_state_t *state, asn1_tag_t *new_tag, uint8_t id) {
75  new_tag->state = NULL;
76  RETURN_IF_ASN1_ERROR(state);
77 
78  new_tag->state = state;
79  asn1_push_byte(state, id);
80  RETURN_IF_ASN1_ERROR(state);
81  new_tag->len_offset = state->offset;
82  // We do not yet known how many bytes we need to encode the length. For now
83  // reserve one byte which is the minimum. This is then fixed in
84  // asn1_finish_tag by moving the data if necessary.
85 
86  asn1_push_byte(state, 0);
87  RETURN_IF_ASN1_ERROR(state);
88  new_tag->len_size = 1;
89 }
90 
91 void asn1_finish_tag(asn1_tag_t *tag) {
92  if (tag->state == NULL)
93  return;
94  RETURN_IF_ASN1_ERROR(tag->state);
95  // Sanity check: asn1_start_tag should have output one byte.
96  if (tag->len_size != 1) {
97  RAISE_ASN1_ERROR(tag->state, kErrorAsn1Internal);
98  }
99  // Compute actually used length.
100  size_t length = tag->state->offset - tag->len_offset - tag->len_size;
101  // Compute the size of the minimal encoding.
102  size_t final_len_size;
103  if (length <= 0x7f) {
104  // We only need one byte to hold the length.
105  final_len_size = 1;
106  } else if (length <= 0xff) {
107  // We need two bytes to hold the length: we need to move the data before
108  // we can write the second byte.
109  final_len_size = 2;
110  } else if (length <= 0xffff) {
111  // We need three bytes to hold the length: we need to move the data before
112  // we can write the second and third bytes.
113  final_len_size = 3;
114  } else {
115  // Length too large.
116  RAISE_ASN1_ERROR(tag->state, kErrorAsn1Internal);
117  }
118  // If the final length uses more bytes than we initially allocated, we
119  // need to shift all the tag data backwards.
120  if (tag->len_size != final_len_size) {
121  // Make sure that the data actually fits into the buffer.
122  size_t new_buffer_size =
123  tag->state->offset + final_len_size - tag->len_size;
124  if (new_buffer_size > tag->state->size) {
125  RAISE_ASN1_ERROR(tag->state, kErrorAsn1BufferExhausted);
126  }
127  // Copy backwards.
128  for (size_t i = 0; i < length; i++) {
129  tag->state->buffer[tag->len_offset + final_len_size + length - 1 - i] =
130  tag->state->buffer[tag->len_offset + tag->len_size + length - 1 - i];
131  }
132  }
133  // Write the length in the buffer.
134  if (length <= 0x7f) {
135  tag->state->buffer[tag->len_offset] = (uint8_t)length;
136  } else if (length <= 0xff) {
137  tag->state->buffer[tag->len_offset + 0] = 0x81;
138  tag->state->buffer[tag->len_offset + 1] = (uint8_t)length;
139  } else if (length <= 0xffff) {
140  tag->state->buffer[tag->len_offset + 0] = 0x82;
141  tag->state->buffer[tag->len_offset + 1] = (uint8_t)(length >> 8);
142  tag->state->buffer[tag->len_offset + 2] = (uint8_t)(length & 0xff);
143  } else {
144  // Length too large.
145  RAISE_ASN1_ERROR(tag->state, kErrorAsn1Internal);
146  }
147  // Fix up state offset.
148  tag->state->offset += final_len_size - tag->len_size;
149  // Hardening: clear out the tag structure to prevent accidental reuse.
150  tag->state = NULL;
151  tag->len_offset = 0;
152  tag->len_size = 0;
153 }
154 
155 void asn1_push_bool(asn1_state_t *state, uint8_t tag, bool value) {
156  asn1_tag_t tag_st;
157  asn1_start_tag(state, &tag_st, tag);
158  asn1_push_byte(state, value ? 0xff : 0);
159  asn1_finish_tag(&tag_st);
160 }
161 
162 void asn1_push_int32(asn1_state_t *state, uint8_t tag, int32_t value) {
163  uint32_t u_value = (uint32_t)value;
164  uint8_t bigint[4] = {
165  u_value >> 24,
166  (u_value >> 16) & 0xff,
167  (u_value >> 8) & 0xff,
168  u_value & 0xff,
169  };
170  asn1_push_integer(state, tag, true, bigint, sizeof(bigint));
171 }
172 
173 void asn1_push_uint32(asn1_state_t *state, uint8_t tag, uint32_t value) {
174  uint8_t bigint[4] = {
175  value >> 24,
176  (value >> 16) & 0xff,
177  (value >> 8) & 0xff,
178  value & 0xff,
179  };
180  asn1_push_integer(state, tag, false, bigint, sizeof(bigint));
181 }
182 
183 void asn1_push_integer(asn1_state_t *state, uint8_t tag, bool is_signed,
184  const uint8_t *bytes_be, size_t size) {
185  RETURN_IF_ASN1_ERROR(state);
186  if (size == 0 || (bytes_be == NULL && size > 0)) {
187  RAISE_ASN1_ERROR(state, kErrorAsn1PushIntegerInvalidArgument);
188  }
189  asn1_tag_t tag_st;
190  asn1_start_tag(state, &tag_st, tag);
191  // Compute smallest possible encoding: ASN1 forbids that the first 9 bits (ie
192  // first octet) and MSB of the second octet are either all ones or all zeroes.
193 
194  // First get rid of unnecessary zeroes.
195  while (size >= 2 && bytes_be[0] == 0 && (bytes_be[1] >> 7) == 0) {
196  bytes_be++;
197  size -= 1;
198  }
199 
200  if (is_signed) {
201  // Integers in ASN.1 are always signed and represented in two's complement.
202  // So for unsigned numbers that has MSB set, add a 0x00 padding.
203  while (size >= 2 && bytes_be[0] == 0xff && (bytes_be[1] >> 7) == 1) {
204  bytes_be++;
205  size -= 1;
206  }
207  } else {
208  // For unsigned numbers, add a 0x00 padding if the first octet has MSB set.
209  if ((bytes_be[0] >> 7) == 1) {
210  asn1_push_byte(state, 0);
211  }
212  }
213  asn1_push_bytes(state, bytes_be, size);
214  asn1_finish_tag(&tag_st);
215 }
216 
217 void asn1_push_integer_pad(asn1_state_t *state, bool is_signed,
218  const uint8_t *bytes_be, size_t size,
219  size_t padded_size) {
220  RETURN_IF_ASN1_ERROR(state);
221  if (size == 0 || size > padded_size) {
222  RAISE_ASN1_ERROR(state, kErrorAsn1PushIntegerPadInvalidArgument);
223  }
224  // Determine the padding byte.
225  uint8_t padding = 0;
226  if (is_signed && (bytes_be[0] >> 7) == 1) {
227  padding = 0xff;
228  }
229  // Output padding.
230  while (padded_size-- > size) {
231  asn1_push_byte(state, padding);
232  }
233  asn1_push_bytes(state, bytes_be, size);
234 }
235 
236 void asn1_push_oid_raw(asn1_state_t *state, const uint8_t *bytes, size_t size) {
237  asn1_tag_t tag;
238  asn1_start_tag(state, &tag, kAsn1TagNumberOid);
239  asn1_push_bytes(state, bytes, size);
240  asn1_finish_tag(&tag);
241 }
242 
243 void asn1_push_string(asn1_state_t *state, uint8_t id, const char *str,
244  size_t max_len) {
245  asn1_tag_t tag;
246  asn1_start_tag(state, &tag, id);
247  while (max_len > 0 && str[0] != 0) {
248  asn1_push_byte(state, (uint8_t)str[0]);
249  str++;
250  max_len--;
251  }
252  asn1_finish_tag(&tag);
253 }
254 
255 static const char kLowercaseHexChars[16] = {'0', '1', '2', '3', '4', '5',
256  '6', '7', '8', '9', 'a', 'b',
257  'c', 'd', 'e', 'f'};
258 
259 void asn1_push_hexstring(asn1_state_t *state, uint8_t id, const uint8_t *bytes,
260  size_t size) {
261  asn1_tag_t tag;
262  asn1_start_tag(state, &tag, id);
263  while (size > 0) {
264  asn1_push_byte(state, (uint8_t)kLowercaseHexChars[bytes[0] >> 4]);
265  asn1_push_byte(state, (uint8_t)kLowercaseHexChars[bytes[0] & 0xf]);
266  bytes++;
267  size--;
268  }
269  asn1_finish_tag(&tag);
270 }
271 
272 void asn1_start_bitstring(asn1_state_t *state,
273  asn1_bitstring_t *out_bitstring) {
274  out_bitstring->state = NULL;
275  RETURN_IF_ASN1_ERROR(state);
276  out_bitstring->state = state;
277  out_bitstring->unused_bits_offset = state->offset;
278  out_bitstring->used_bits = 0;
279  out_bitstring->current_byte = 0;
280  // Push a single byte that will hold the unused bit count (it will be updated
281  // in asn1_finish_bitstring.
282  asn1_push_byte(state, 0);
283 }
284 
285 void asn1_bitstring_push_bit(asn1_bitstring_t *bitstring, bool bit) {
286  if (bitstring->state == NULL)
287  return;
288  RETURN_IF_ASN1_ERROR(bitstring->state);
289  // Update the current byte: bits are added from MSB to LSB.
290  if (bit) {
291  bitstring->current_byte |= 1 << (7 - bitstring->used_bits);
292  }
293  // If this makes a full byte, push it and reset.
294  bitstring->used_bits++;
295  if (bitstring->used_bits == 8) {
296  asn1_push_byte(bitstring->state, bitstring->current_byte);
297  bitstring->current_byte = 0;
298  bitstring->used_bits = 0;
299  }
300 }
301 
302 void asn1_finish_bitstring(asn1_bitstring_t *bitstring) {
303  if (bitstring->state == NULL)
304  return;
305  RETURN_IF_ASN1_ERROR(bitstring->state);
306  if (bitstring->used_bits >= 8) {
307  asn1_state_t *state = bitstring->state;
308  bitstring->state = NULL;
309  RAISE_ASN1_ERROR(state, kErrorAsn1FinishBitstringInvalidArgument);
310  }
311  // If the last byte contains some bits, we need to push it and update
312  // the number of unused bits. If the string length was a multiple of 8
313  // (ie used_bits = 0) then there are 0 unused bits which is the value pushed
314  // in asn1_start_bitstring so we do not need to update it.
315  if (bitstring->used_bits != 0) {
316  asn1_push_byte(bitstring->state, bitstring->current_byte);
317  // Update the "unused bits value"
318  bitstring->state->buffer[bitstring->unused_bits_offset] =
319  8 - (uint8_t)bitstring->used_bits;
320  }
321  // Hardening: clear out the tag structure to prevent accidental reuse.
322  bitstring->state = NULL;
323 }