Skip to content

Commit

Permalink
[ot_certs] Add support for boolean variables
Browse files Browse the repository at this point in the history
This commit adds support for boolean variables in the template,
ASN1 DER and codegen. This is relatively extensive change because
the flags that were previously just booleans need to be migrated
to use Value<bool>.
This commit also contains the unittests for the parsers and der
generator. The ASN1 codegen uses the `asn1_bitstring_t` facilities
introduced in the previous commit.

Signed-off-by: Amaury Pouly <[email protected]>
  • Loading branch information
pamaury committed Jan 19, 2024
1 parent fd7d5ce commit 595c769
Show file tree
Hide file tree
Showing 11 changed files with 242 additions and 105 deletions.
20 changes: 16 additions & 4 deletions sw/device/silicon_creator/lib/cert/generic.hjson
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,18 @@
type: "integer",
size: 32,
},
not_configured: {
type: "boolean",
}
not_secure: {
type: "boolean",
}
recovery: {
type: "boolean",
}
debug: {
type: "boolean",
}
},

certificate: {
Expand Down Expand Up @@ -99,10 +111,10 @@
{ hash_algorithm: "sha256", digest: { var: "hash_2" } },
],
flags: {
not_configured: false,
not_secure: false,
recovery: false,
debug: false,
not_configured: { var: "not_configured" },
not_secure: { var: "not_secure" },
recovery: { var: "recovery" },
debug: { var: "debug" },
}
signature: {
algorithm: "ecdsa-with-sha256",
Expand Down
6 changes: 5 additions & 1 deletion sw/device/silicon_creator/lib/cert/uds.hjson
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,10 @@
type: "byte-array",
size: 20,
},
// Debug?
debug_flag: {
type: "boolean",
}
},

certificate: {
Expand Down Expand Up @@ -78,7 +82,7 @@
not_configured: false,
not_secure: false,
recovery: false,
debug: false,
debug: { var: "debug_flag" },
}
signature: {
algorithm: "ecdsa-with-sha256",
Expand Down
21 changes: 2 additions & 19 deletions sw/host/ot_certs/src/asn1/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -129,23 +129,6 @@ pub trait Builder {
&mut self,
name_hint: Option<String>,
tag: &Tag,
bits: &[bool],
) -> Result<()> {
// See X.690 spec section 8.6 for encoding details.
// Note: the encoding of an empty bitstring must be the number of unused bits to 0 and have no content.
let nr_bytes = (bits.len() + 7) / 8;
let mut bytes = vec![0u8; nr_bytes];
for (i, bit) in bits.iter().enumerate() {
if *bit {
bytes[i / 8] |= 1u8 << (7 - (i % 8));
}
}

self.push_as_bit_string(
concat_suffix(&name_hint, "bitstring"),
tag,
bytes.len() * 8 - bits.len(),
|builder| builder.push_byte_array(name_hint.clone(), &Value::Literal(bytes.clone())),
)
}
bits: &[Value<bool>],
) -> Result<()>;
}
93 changes: 81 additions & 12 deletions sw/host/ot_certs/src/asn1/codegen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ impl ConstantPool {
}

/// Information about how to refer to a variable in the code.
#[derive(Debug, Clone)]
pub enum VariableCodegenInfo {
/// Variable can be referred to as a pointer.
Pointer {
Expand All @@ -61,9 +62,15 @@ pub enum VariableCodegenInfo {
// Expression generating the value.
value_expr: String,
},
/// Variable is a boolean,
Boolean {
// Expression generating the value.
value_expr: String,
},
}

/// Information about a variable.
#[derive(Debug, Clone)]
pub struct VariableInfo {
/// Type of the variable.
pub var_type: VariableType,
Expand Down Expand Up @@ -218,6 +225,40 @@ impl Codegen<'_> {
"RETURN_IF_ERROR(asn1_push_oid_raw(&state, {expr}, {expr_size}));\n"
))
}

/// Push a bit in a bitstring.
fn push_bit(&mut self, bitstring_tagname: &str, val: &Value<bool>) -> Result<()> {
match val {
Value::Literal(x) => {
self.push_str_with_indent(&format!(
"RETURN_IF_ERROR(asn1_bitstring_push_bit({bitstring_tagname}, {x}));\n"
));
}
Value::Variable(Variable { name, convert }) => {
let VariableInfo {
codegen,
var_type: source_type,
} = (self.variable_info)(name)?;
match source_type {
VariableType::Boolean => {
ensure!(
convert.is_none(),
"cannot use a convertion from boolean to boolean"
);
let VariableCodegenInfo::Boolean { value_expr } = codegen else {
bail!("internal error: boolean not represented by a VariableCodegenInfo::Boolean");
};
self.push_str_with_indent(&format!("RETURN_IF_ERROR(asn1_bitstring_push_bit({bitstring_tagname}, {value_expr}));\n"));
}
_ => bail!(
"conversion from to {:?} to boolean is not supported",
source_type
),
}
}
}
Ok(())
}
}

impl Tag {
Expand Down Expand Up @@ -318,9 +359,8 @@ impl Builder for Codegen<'_> {
size
}
_ => bail!(
"using a variable of type {:?} for an integer field is not supported",
source_type
),
"using a variable of type {source_type:?} for an integer field is not supported"
),
};
self.max_out_size += Self::tag_and_content_size(size);
// For variables, an integer can either be represented by a pointer to a big-endian
Expand All @@ -340,6 +380,7 @@ impl Builder for Codegen<'_> {
// Make sure the type is correct and get the size.
self.push_str_with_indent(&format!("RETURN_IF_ERROR(asn1_push_integer(&state, {}, false, {ptr_expr}, {size_expr}));\n", tag.codestring()))
}
_ => bail!("internal error: integer represented by a {source_type:?}"),
}
}
}
Expand Down Expand Up @@ -385,8 +426,7 @@ impl Builder for Codegen<'_> {
))
}
_ => bail!(
"using a variable of type {:?} for a padded integer field is not supported",
source_type
"using a variable of type {source_type:?} for a padded integer field is not supported"
),
}
}
Expand Down Expand Up @@ -426,8 +466,7 @@ impl Builder for Codegen<'_> {
))
}
_ => bail!(
"using a variable of type {:?} for a byte-array field is not supported",
source_type
"using a variable of type {source_type:?} for a byte-array field is not supported",
),
}
}
Expand Down Expand Up @@ -487,19 +526,49 @@ impl Builder for Codegen<'_> {
self.push_str_with_indent(
&format!("RETURN_IF_ERROR(asn1_push_hexstring(&state, {str_type}, {ptr_expr}, {size_expr}));\n"))
}
_ => bail!("conversion {:?} from byte array to string is not supported", convert),
_ => bail!("conversion {convert:?} from byte array to string is not supported"),
}
}
_ => bail!(
"conversion from to {:?} to string is not supported",
source_type
),
_ => bail!("conversion from to {source_type:?} to string is not supported",),
}
}
}
Ok(())
}

fn push_bitstring<'a>(
&mut self,
name_hint: Option<String>,
tag: &Tag,
bits: &[Value<bool>],
) -> Result<()> {
self.push_tag(name_hint.clone(), tag, |builder| {
let tag_name = format!(
"bit{}_{}",
builder.tag_idx,
name_hint.map(|x| x.to_snake_case()).unwrap_or("".into())
);
builder.tag_idx += 1;
builder.push_str_with_indent(&format!("asn1_bitstring_t {tag_name};\n"));
builder.push_str_with_indent(&format!(
"RETURN_IF_ERROR(asn1_start_bitstring(&state, &{tag_name}));\n"
));
builder.push_str_with_indent("{\n");
builder.indent_lvl += 1;
for bit in bits {
builder.push_bit(&format!("&{tag_name}"), bit)?;
}
// One byte for the unused bits and then one byte per 8 bits.
builder.max_out_size += 1 + (bits.len() + 7) / 8;
builder.indent_lvl -= 1;
builder.push_str_with_indent("}\n");
builder.push_str_with_indent(&format!(
"RETURN_IF_ERROR(asn1_finish_bitstring(&{tag_name}));\n"
));
Ok(())
})
}

fn push_oid(&mut self, oid: &Oid) -> Result<()> {
// Create constant.
let bytes = oid.to_der()?;
Expand Down
32 changes: 29 additions & 3 deletions sw/host/ot_certs/src/asn1/der.rs
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,29 @@ impl Builder for Der {
})
}

fn push_bitstring(
&mut self,
name_hint: Option<String>,
tag: &Tag,
bits: &[Value<bool>],
) -> Result<()> {
let bits = bits
.iter()
.map(Self::get_value_or_error)
.collect::<Result<Vec<_>>>()?;
// See X.690 spec section 8.6 for encoding details.
// Note: the encoding of an empty bitstring must be the number of unused bits to 0 and have no content.
let nr_bytes = (bits.len() + 7) / 8;
let mut bytes = vec![0u8; nr_bytes];
for (i, bit) in bits.iter().enumerate() {
bytes[i / 8] |= (**bit as u8) << (7 - (i % 8));
}

self.push_as_bit_string(None, tag, bytes.len() * 8 - bits.len(), |builder| {
builder.push_byte_array(name_hint.clone(), &Value::Literal(bytes.clone()))
})
}

/// Push tagged content into the ASN1 output. The closure can use any available function of the builder
/// and produces the content of the tagged data.
fn push_tag(
Expand Down Expand Up @@ -314,14 +337,17 @@ mod tests {
fn test_asn1_der_bitstring() -> Result<()> {
let der = Der::generate(|builder| {
builder.push_bitstring(None, &Tag::BitString, &[])?;
builder.push_bitstring(None, &Tag::BitString, &[true])?;
builder.push_bitstring(None, &Tag::OctetString, &[false])?;
builder.push_bitstring(None, &Tag::BitString, &[Value::Literal(true)])?;
builder.push_bitstring(None, &Tag::OctetString, &[Value::Literal(false)])?;
builder.push_bitstring(
None,
&Tag::BitString,
&[
true, false, true, true, false, false, false, false, true, false, false, true,
],
]
.into_iter()
.map(Value::Literal)
.collect::<Vec<_>>(),
)
})?;
const RESULT: &[u8] = &[
Expand Down
8 changes: 4 additions & 4 deletions sw/host/ot_certs/src/asn1/dice_tcb.rs
Original file line number Diff line number Diff line change
Expand Up @@ -151,10 +151,10 @@ impl DiceTcbInfo<'_> {
value: 7,
},
&[
flags.not_configured,
flags.not_secure,
flags.recovery,
flags.debug,
flags.not_configured.clone(),
flags.not_secure.clone(),
flags.recovery.clone(),
flags.debug.clone(),
],
)?;
}
Expand Down
14 changes: 9 additions & 5 deletions sw/host/ot_certs/src/asn1/x509.rs
Original file line number Diff line number Diff line change
Expand Up @@ -244,11 +244,15 @@ impl X509 {
Some("key_usage".into()),
&Tag::BitString,
&[
/* digitalSignature */ false, /* nonRepudiation */ false,
/* keyEncipherment */ false, /* dataEncipherment */ false,
/* keyAgreement */ false, /* keyCertSign */ true,
/* cRLSign */ false, /* encipherOnly */ false,
/* decipherOnly */ false,
/* digitalSignature */ Value::Literal(false),
/* nonRepudiation */ Value::Literal(false),
/* keyEncipherment */ Value::Literal(false),
/* dataEncipherment */ Value::Literal(false),
/* keyAgreement */ Value::Literal(false),
/* keyCertSign */ Value::Literal(true),
/* cRLSign */ Value::Literal(false),
/* encipherOnly */ Value::Literal(false),
/* decipherOnly */ Value::Literal(false),
],
)
})
Expand Down
20 changes: 14 additions & 6 deletions sw/host/ot_certs/src/codegen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
//! This module is capable of generating C code for generating a binary X.509
//! certificate according to a [`Template`](crate::template::Template).
use anyhow::{Context, Result};
use anyhow::{bail, Context, Result};
use heck::ToUpperCamelCase;
use std::collections::HashMap;
use std::fmt::Write;
Expand Down Expand Up @@ -144,11 +144,13 @@ pub fn generate_cert(from_file: &str, tmpl: &Template) -> Result<Codegen> {
// Only consider variables whose size can vary, ie pointers.
let (codegen, _) = c_variable_info(var_name, "", var_type);
if let VariableCodegenInfo::Pointer { .. } = codegen {
writeln!(
source_h,
"// - {var_name} is of size at most {} bytes.",
var_type.size()
)?;
let size = match var_type {
VariableType::ByteArray { size }
| VariableType::Integer { size }
| VariableType::String { size } => *size,
VariableType::Boolean => bail!("internal error: boolean represented by a pointer"),
};
writeln!(source_h, "// - {var_name} is of size at most {size} bytes.")?;
}
}
source_h.push_str(&indoc::formatdoc! {"enum {{
Expand Down Expand Up @@ -316,5 +318,11 @@ fn c_variable_info(
"#
},
),
VariableType::Boolean => (
VariableCodegenInfo::Boolean {
value_expr: format!("{struct_expr}{name}"),
},
format!("{INDENT}bool {name};\n"),
),
}
}
Loading

0 comments on commit 595c769

Please sign in to comment.