Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cherry pick ot_certs changes #20915

Merged
merged 9 commits into from
Jan 23, 2024
25 changes: 22 additions & 3 deletions rules/certificates.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,14 @@ def _certificate_codegen_impl(ctx):
# Output files before formatting.
pre_c = ctx.actions.declare_file("{}.pre.c".format(basename))
pre_h = ctx.actions.declare_file("{}.pre.h".format(basename))
pre_ut = ctx.actions.declare_file("{}_unittest.pre.cc".format(basename))

# Final output files.
out_c = ctx.actions.declare_file("{}.c".format(basename))
out_h = ctx.actions.declare_file("{}.h".format(basename))
out_ut = ctx.actions.declare_file("{}_unittest.cc".format(basename))
ctx.actions.run(
outputs = [pre_c, pre_h],
outputs = [pre_c, pre_h, pre_ut],
inputs = [
ctx.file.template,
],
Expand All @@ -30,13 +32,14 @@ def _certificate_codegen_impl(ctx):
"--template={}".format(ctx.file.template.path),
"--output-c={}".format(pre_c.path),
"--output-h={}".format(pre_h.path),
"--output-unittest={}".format(pre_ut.path),
],
executable = tc.tools.opentitantool,
mnemonic = "GenCertTemplate",
)

# Format files
format_list = {out_c: pre_c, out_h: pre_h}
format_list = {out_c: pre_c, out_h: pre_h, out_ut: pre_ut}
for (out, pre) in format_list.items():
ctx.actions.run_shell(
outputs = [out],
Expand All @@ -46,10 +49,11 @@ def _certificate_codegen_impl(ctx):
)

return [
DefaultInfo(files = depset([out_c, out_h])),
DefaultInfo(files = depset([out_c, out_h, out_ut])),
OutputGroupInfo(
sources = depset([out_c]),
headers = depset([out_h]),
unittest = depset([out_ut]),
),
]

Expand Down Expand Up @@ -101,6 +105,12 @@ def certificate_template(name, template):
output_group = "headers",
)

native.filegroup(
name = "{}_unittest_srcs".format(name),
srcs = [":{}".format(name)],
output_group = "unittest",
)

native.cc_library(
name = "{}_library".format(name),
srcs = [":{}_srcs".format(name)],
Expand All @@ -109,3 +119,12 @@ def certificate_template(name, template):
":asn1",
],
)

native.cc_test(
name = "{}_unittest".format(name),
srcs = [":{}_unittest_srcs".format(name)],
deps = [
":{}_library".format(name),
"@googletest//:gtest_main",
],
)
102 changes: 81 additions & 21 deletions sw/device/silicon_creator/lib/cert/asn1.c
Original file line number Diff line number Diff line change
Expand Up @@ -49,13 +49,11 @@ rom_error_t asn1_start_tag(asn1_state_t *state, asn1_tag_t *new_tag,
RETURN_IF_ERROR(asn1_push_byte(state, id));
new_tag->len_offset = state->offset;
// We do not yet known how many bytes we need to encode the length. For now
// reserve three bytes so we can use the 16-bit encoding. This is then fixed
// in asn1_finish_tag.
// reserve one byte which is the minimum. This is then fixed in
// asn1_finish_tag by moving the data if necessary.

// Three bytes: one to hold the number of octets to use
// and two to store the length.
RETURN_IF_ERROR(asn1_push_bytes(state, (uint8_t[]){0x82, 0, 0}, 3));
new_tag->len_size = 3;
RETURN_IF_ERROR(asn1_push_byte(state, 0));
new_tag->len_size = 1;
return kErrorOk;
}

Expand All @@ -64,41 +62,60 @@ rom_error_t asn1_finish_tag(asn1_tag_t *tag) {
if (tag->state == NULL) {
return kErrorAsn1Internal;
}
// Sanity check: asn1_start_tag should have output three bytes.
if (tag->len_size != 3) {
// Sanity check: asn1_start_tag should have output one byte.
if (tag->len_size != 1) {
return kErrorAsn1Internal;
}
// Compute actually used length.
size_t length = tag->state->offset - tag->len_offset - tag->len_size;
// Fixup the length bytes and compute the size of the minimal encoding.
// Compute the size of the minimal encoding.
size_t final_len_size;
if (length <= 0x7f) {
// We only need one byte to hold the length.
tag->state->buffer[tag->len_offset] = (uint8_t)length;
final_len_size = 1;
} else if (length <= 0xff) {
// We need two bytes to hold the length.
// We need two bytes to hold the length: we need to move the data before
// we can write the second byte.
final_len_size = 2;
} else if (length <= 0xffff) {
// We need three bytes to hold the length: we need to move the data before
// we can write the second and third bytes.
final_len_size = 3;
} else {
// Length too large.
return kErrorAsn1Internal;
}
// If the final length uses more bytes than we initially allocated, we
// need to shift all the tag data backwards.
if (tag->len_size != final_len_size) {
// Make sure that the data actually fits into the buffer.
size_t new_buffer_size =
tag->state->offset + final_len_size - tag->len_size;
if (new_buffer_size > tag->state->size) {
return kErrorAsn1BufferExhausted;
}
// Copy backwards.
for (size_t i = 0; i < length; i++) {
tag->state->buffer[tag->len_offset + final_len_size + length - 1 - i] =
tag->state->buffer[tag->len_offset + tag->len_size + length - 1 - i];
}
}
// Write the length in the buffer.
if (length <= 0x7f) {
tag->state->buffer[tag->len_offset] = (uint8_t)length;
} else if (length <= 0xff) {
tag->state->buffer[tag->len_offset + 0] = 0x81;
tag->state->buffer[tag->len_offset + 1] = (uint8_t)length;
final_len_size = 2;
} else if (length <= 0xffff) {
// We need three bytes to hold the length.
tag->state->buffer[tag->len_offset + 0] = 0x82;
tag->state->buffer[tag->len_offset + 1] = (uint8_t)(length >> 8);
tag->state->buffer[tag->len_offset + 2] = (uint8_t)(length & 0xff);
final_len_size = 3;
} else {
// Length too large.
return kErrorAsn1Internal;
}
// If the final length uses less bytes than we initially allocated, we
// need to shift all the tag data forward.
for (size_t i = 0; i < length; i++) {
tag->state->buffer[tag->len_offset + final_len_size + i] =
tag->state->buffer[tag->len_offset + tag->len_size + i];
}
// Fix up state offset.
tag->state->offset -= tag->len_size - final_len_size;
tag->state->offset += final_len_size - tag->len_size;
// Hardening: clear out the tag structure to prevent accidental reuse.
tag->state = NULL;
tag->len_offset = 0;
Expand Down Expand Up @@ -205,3 +222,46 @@ rom_error_t asn1_push_hexstring(asn1_state_t *state, uint8_t id,
}
return asn1_finish_tag(&tag);
}

rom_error_t asn1_start_bitstring(asn1_state_t *state,
asn1_bitstring_t *out_bitstring) {
out_bitstring->state = state;
out_bitstring->unused_bits_offset = state->offset;
out_bitstring->used_bits = 0;
out_bitstring->current_byte = 0;
// Push a single byte that will hold the unused bit count (it will be updated
// in asn1_finish_bitstring.
RETURN_IF_ERROR(asn1_push_byte(state, 0));
return kErrorOk;
}

rom_error_t asn1_bitstring_push_bit(asn1_bitstring_t *bitstring, bool bit) {
// Update the current byte: bits are added from MSB to LSB.
if (bit) {
bitstring->current_byte |= 1 << (7 - bitstring->used_bits);
}
// If this makes a full byte, push it and reset.
bitstring->used_bits++;
if (bitstring->used_bits == 8) {
RETURN_IF_ERROR(asn1_push_byte(bitstring->state, bitstring->current_byte));
bitstring->current_byte = 0;
bitstring->used_bits = 0;
}
return kErrorOk;
}

rom_error_t asn1_finish_bitstring(asn1_bitstring_t *bitstring) {
// If the last byte contains some bits, we need to push it and update
// the number of unused bits. If the string length was a multiple of 8
// (ie used_bits = 0) then there are 0 unused bits which is the value pushed
// in asn1_start_bitstring so we do not need to update it.
if (bitstring->used_bits != 0) {
RETURN_IF_ERROR(asn1_push_byte(bitstring->state, bitstring->current_byte));
// Update the "unused bits value"
bitstring->state->buffer[bitstring->unused_bits_offset] =
8 - bitstring->used_bits;
}
// Hardening: clear out the tag structure to prevent accidental reuse.
bitstring->state = NULL;
return kErrorOk;
}
40 changes: 40 additions & 0 deletions sw/device/silicon_creator/lib/cert/asn1.h
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,23 @@ typedef struct asn1_tag {
size_t len_size;
} asn1_tag_t;

/**
* Structure holding the information about an unfinished bistring.
*
* The fields in this structure should be considered
* private and not be read or written directly.
*/
typedef struct asn1_bitstring {
// Pointer to state.
asn1_state_t *state;
// Offset of the "unused bits" byte.
size_t unused_bits_offset;
// How many bits have been added to the current byte (between 0 and 7).
size_t used_bits;
// Current value of the last byte of the string.
size_t current_byte;
} asn1_bitstring_t;

// ASN1 tag classes.
typedef enum asn1_tag_class {
kAsn1TagClassUniversal = 0 << 6,
Expand Down Expand Up @@ -233,6 +250,29 @@ rom_error_t asn1_push_string(asn1_state_t *state, uint8_t tag, const char *str,
rom_error_t asn1_push_hexstring(asn1_state_t *state, uint8_t tag,
const uint8_t *bytes, size_t size);

/**
* Start an ASN1 bitstring.
*
* @param state Pointer to the state initialized by asn1_start.
* @param[out] out_bitstring Pointer to a user-allocated bitstring to be
* initialized.
*/
rom_error_t asn1_start_bitstring(asn1_state_t *state,
asn1_bitstring_t *out_bitstring);

/** Add a bit to a bitstring.
*
* @param bitstring Pointer to a bitstring initialized by asn1_start_bitstring.
* @param bit Bit to add at the end of the string.
*/
rom_error_t asn1_bitstring_push_bit(asn1_bitstring_t *bitstring, bool bit);

/** Finish an ASN1 bitstring.
*
* @param bitstring Pointer to a bitstring initialized by asn1_start_bitstring.
*/
rom_error_t asn1_finish_bitstring(asn1_bitstring_t *bitstring);

#ifdef __cplusplus
} // extern "C"
#endif // __cplusplus
Expand Down
Loading
Loading