ot_certs/
cwt.rs

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
5use std::fs;
6use std::iter;
7use std::path::PathBuf;
8
9use crate::cbor;
10use crate::codegen::Codegen;
11use anyhow::{Context, Result, bail};
12use heck::{ToShoutySnakeCase, ToUpperCamelCase};
13use indexmap::IndexMap;
14use itertools::Itertools;
15use serde::{Deserialize, Serialize};
16
17#[derive(Clone, Debug, Deserialize, PartialEq, Eq, Serialize)]
18#[serde(deny_unknown_fields)]
19pub struct CwtTemplate {
20    /// Name of the certificate.
21    pub name: String,
22    /// Variable declarations.
23    pub variables: IndexMap<String, TemplateVariable>,
24    /// Constant declarations.
25    pub constants: IndexMap<String, TemplateConstant>,
26    /// Structure specification.
27    pub structure: TemplateStructure,
28}
29
30#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Eq, Serialize)]
31#[serde(rename_all = "kebab-case")]
32pub enum VariableSize {
33    /// The maximum size of the variable.
34    MaxSize(u64),
35    /// The exact size of the variable.
36    ExactSize(u64),
37}
38
39#[derive(Clone, Copy, Debug, PartialEq, Eq, Deserialize, Serialize)]
40#[serde(rename_all = "kebab-case")]
41pub enum VariableType {
42    /// Raw array of bytes.
43    ByteArray,
44    /// UTF-8 encoded string.
45    String,
46    /// Signed 64-bit integer.
47    Integer,
48}
49
50/// Used to parse the variables defined in the template.
51#[derive(Clone, Copy, Debug, PartialEq, Eq, Deserialize, Serialize)]
52#[serde(tag = "type", rename_all = "kebab-case")]
53pub enum TemplateVariable {
54    /// Byte-array variable.
55    ByteArray {
56        #[serde(flatten)]
57        size: VariableSize,
58    },
59    /// String variable.
60    String {
61        #[serde(flatten)]
62        size: VariableSize,
63    },
64    /// Integer variable.
65    Integer,
66}
67
68/// Used to parse the constants defined in the template.
69#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
70#[serde(tag = "type", rename_all = "kebab-case")]
71pub enum TemplateConstant {
72    /// Byte-array constant.
73    ByteArray {
74        /// Big-endian hex-encoded bytes
75        #[serde(with = "hex::serde")]
76        value: Vec<u8>,
77    },
78    /// String constant.
79    String { value: String },
80    /// Integer constant.
81    Integer { value: i64 },
82}
83
84/// Used to parse the structures defined in the template.
85#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
86#[serde(untagged)]
87pub enum TemplateStructure {
88    /// A byte-array which encodes a CBOR structure.
89    #[serde(rename_all = "kebab-case")]
90    CborByteArray {
91        cbor_byte_array: Box<TemplateStructure>,
92    },
93    /// An item is a variable or a constant.
94    Item(String),
95    /// A map that consists of key-value pairs.
96    Map(IndexMap<String, TemplateStructure>),
97    /// An array that consists of values.
98    Array(Vec<TemplateStructure>),
99}
100
101// CodegenVar is used to unify the representation of TemplateVariable and TemplateConstant.
102#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
103struct CodegenVar {
104    name: String,
105    size: VariableSize,
106    value: CodegenVarValue,
107}
108
109type CodegenVarTable = IndexMap<String, CodegenVar>;
110
111#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
112enum CodegenVarValue {
113    ByteArray(Option<Vec<u8>>),
114    String(Option<String>),
115    Integer(Option<i64>),
116}
117
118impl CodegenVar {
119    fn from_template_variable(name: &str, var: &TemplateVariable) -> Result<Self> {
120        let name = name.to_owned();
121        let (size, value) = match var {
122            TemplateVariable::ByteArray { size } => (*size, CodegenVarValue::ByteArray(None)),
123            TemplateVariable::String { size } => (*size, CodegenVarValue::String(None)),
124            TemplateVariable::Integer => (VariableSize::MaxSize(8), CodegenVarValue::Integer(None)),
125        };
126
127        Ok(Self { name, size, value })
128    }
129
130    fn from_template_constant(name: &str, var: &TemplateConstant) -> Result<Self> {
131        let name = name.to_owned();
132        let (value, size) = match var {
133            TemplateConstant::ByteArray { value } => (
134                CodegenVarValue::ByteArray(Some(value.clone())),
135                VariableSize::ExactSize(value.len().try_into().with_context(|| {
136                    format!("the size of {name} is too large for u64: {}", value.len())
137                })?),
138            ),
139            TemplateConstant::String { value } => (
140                CodegenVarValue::String(Some(value.clone())),
141                VariableSize::ExactSize(value.len().try_into().with_context(|| {
142                    format!("the size of {name} is too large for u64: {}", value.len())
143                })?),
144            ),
145            TemplateConstant::Integer { value } => {
146                let arg = if *value >= 0 { *value } else { -(*value + 1) } as u64;
147                (
148                    CodegenVarValue::Integer(Some(*value)),
149                    VariableSize::ExactSize(cbor::arg_size(arg).0),
150                )
151            }
152        };
153
154        Ok(Self { name, size, value })
155    }
156
157    fn is_constant(&self) -> bool {
158        match &self.value {
159            CodegenVarValue::ByteArray(v) => v.is_some(),
160            CodegenVarValue::String(v) => v.is_some(),
161            CodegenVarValue::Integer(v) => v.is_some(),
162        }
163    }
164
165    fn variable_type(&self) -> VariableType {
166        match &self.value {
167            CodegenVarValue::ByteArray(_) => VariableType::ByteArray,
168            CodegenVarValue::String(_) => VariableType::String,
169            CodegenVarValue::Integer(_) => VariableType::Integer,
170        }
171    }
172
173    // Generate the input fields.
174    fn declarations(&self, prefix: &str) -> String {
175        assert!(!self.is_constant());
176
177        match self.value {
178            CodegenVarValue::ByteArray(_) => {
179                indoc::formatdoc! { r#"
180                    {prefix}uint8_t *{name};
181                    {prefix}size_t {name}_size;
182                    "#,
183                    name = self.name
184                }
185            }
186            CodegenVarValue::String(_) => {
187                indoc::formatdoc! { r#"
188                    {prefix}char *{name};
189                    {prefix}size_t {name}_size;
190                    "#,
191                    name = self.name
192                }
193            }
194            CodegenVarValue::Integer(_) => indoc::formatdoc! { r#"
195                {prefix}int64_t {name};
196                "#,
197                name = self.name
198            },
199        }
200    }
201
202    // Generate the expression of the value.
203    fn value_expression(&self) -> String {
204        assert!(!self.is_constant());
205        format!("values->{}", self.name)
206    }
207
208    // Generate the expression of the value size.
209    fn size_expression(&self) -> String {
210        assert!(!self.is_constant());
211        if self.variable_type() != VariableType::Integer {
212            format!("values->{}_size", self.name)
213        } else {
214            format!("cbor_calc_int_size(values->{})", self.name)
215        }
216    }
217}
218
219// CodegenStructure are stored inside a vector, and therefore we use indices to refer to other CodegenStructure.
220// `Var` represents a leaf node, while others represent different types of internal nodes.
221#[derive(Clone, Debug, PartialEq, Eq)]
222enum CodegenStructure<'a> {
223    Var(&'a CodegenVar),
224    CborByteArray(usize),
225    Map(Vec<(usize, usize)>),
226    Array(Vec<usize>),
227}
228
229impl CodegenStructure<'_> {
230    fn from_template<'a>(
231        template: &CwtTemplate,
232        vars: &'a CodegenVarTable,
233    ) -> Result<Vec<CodegenStructure<'a>>> {
234        let mut nodes = Vec::<CodegenStructure>::new();
235        let mut id_mapping = IndexMap::<String, usize>::new();
236        Self::build_codegen_structure(&template.structure, vars, &mut nodes, &mut id_mapping)
237            .context("build_codegen_structure failed")?;
238        Ok(nodes)
239    }
240
241    // Assign the indices in postorder.
242    // For later size computations, we could just iterate the array without DFS,
243    // because child nodes are always processed before their parent node.
244    fn build_codegen_structure<'a>(
245        cur: &TemplateStructure,
246        vars: &'a CodegenVarTable,
247        nodes: &mut Vec<CodegenStructure<'a>>,
248        var_ids: &mut IndexMap<String, usize>,
249    ) -> Result<usize> {
250        let node = match cur {
251            TemplateStructure::Item(name) => {
252                let var = vars
253                    .get(name)
254                    .with_context(|| format!("cannot find item {name} in template"))?;
255                CodegenStructure::Var(var)
256            }
257            TemplateStructure::CborByteArray { cbor_byte_array } => {
258                let inner = Self::build_codegen_structure(cbor_byte_array, vars, nodes, var_ids)?;
259                CodegenStructure::CborByteArray(inner)
260            }
261            TemplateStructure::Map(items) => {
262                let mut pairs = Vec::new();
263                for (k, v) in items {
264                    let item = TemplateStructure::Item(k.clone());
265                    let first = Self::build_codegen_structure(&item, vars, nodes, var_ids)?;
266                    let second = Self::build_codegen_structure(v, vars, nodes, var_ids)?;
267                    pairs.push((first, second));
268                }
269                CodegenStructure::Map(pairs)
270            }
271            TemplateStructure::Array(items) => {
272                let mut values = Vec::new();
273                for item in items {
274                    let value = Self::build_codegen_structure(item, vars, nodes, var_ids)?;
275                    values.push(value);
276                }
277                CodegenStructure::Array(values)
278            }
279        };
280
281        if let CodegenStructure::Var(var) = node {
282            // Check if the node for this Var is created before.
283            if var_ids.contains_key(&var.name) {
284                // If so, just get its id and return.
285                // This means that if a variable or a constant is used multiple times,
286                // there will be exactly one node to represent it.
287                Ok(var_ids[&var.name])
288            } else {
289                // Otherwise, create a new node, store its id in `var_ids` and return.
290                let idx = nodes.len();
291                nodes.push(node);
292                var_ids.insert(var.name.clone(), idx);
293                Ok(idx)
294            }
295        } else {
296            // Always create a new node for it and return.
297            let idx = nodes.len();
298            nodes.push(node);
299            Ok(idx)
300        }
301    }
302}
303
304// A CBOR data item (called `item` in the source code) consists of the following parts:
305//
306//   * major type (3 bit): the type of this data item
307//
308//   * additional information (5 bit): the information of `argument`
309//
310//   * argument: different meaning for different type
311//
312//               could be omitted if less than 24,
313//               `additional information` will hold the actual value in this case
314//
315//   * content: the content of the data type
316//
317//
318// |--------------------------------------------------------------|
319// | major type | additional information | argument |   content   |
320// |--------------------------------------------------------------|
321//
322// |-------------------------------------|
323//                 1 byte
324//                                       |----------|
325//                                         arg_size
326//                                                  |-------------|
327//                                                   content_size
328// |--------------------------------------------------------------|
329//                            item_size
330//
331//
332// Since we have CodegenStructure::CborByteArray, and there might be variables in the inner nodes,
333// we need to compute the size of the whole inner structure before generating its CBOR.
334//
335// We define `content_size`, `arg_size` and `item_size` for each CodegenStructure,
336// and represent them in `SizeExpression`.
337//
338// When the size of a given child item is known before runtime, it will be accumulated into the `constant` field.
339// Otherwise, we could only know the exact size until runtime, and therefore we mark it in dependencies.
340//
341//
342// The formulations are:
343//
344//   `content_size` = constant + `item_size`` of each dependent CodegenStructure, or
345//                    constant + size expression of the CodegenVar
346//
347//   `item_size` = 1 + ArgSize::Constant + `content_size` or
348//                 1 + encoded size of `content_size` + `content_size`
349//
350#[derive(Clone, Debug, PartialEq, Eq)]
351struct SizeExpression<'a> {
352    index: usize,
353    arg_size: ArgSize,
354    constant: u64,
355    dependencies: SizeDependency<'a>,
356}
357
358#[derive(Clone, Debug, PartialEq, Eq)]
359enum ArgSize {
360    // Fixed size.
361    Constant(u64),
362    // Used for data types whose `argument` is its `content_size`.
363    LengthOfContentSize,
364}
365
366#[derive(Clone, Debug, PartialEq, Eq)]
367enum SizeDependency<'a> {
368    Var(&'a CodegenVar),
369    SubItemSize(Vec<usize>),
370}
371
372impl SizeExpression<'_> {
373    // Return a SizeExpression whose `content_size` depends on a variable.
374    fn from_codegenvar(index: usize, arg_size: ArgSize, var: &CodegenVar) -> SizeExpression<'_> {
375        SizeExpression {
376            index,
377            arg_size,
378            constant: 0,
379            dependencies: SizeDependency::Var(var),
380        }
381    }
382
383    // Return a SizeExpression with empty SubItemSize dependency.
384    fn empty_dependency(index: usize, arg_size: ArgSize, constant: u64) -> Self {
385        SizeExpression {
386            index,
387            arg_size,
388            constant,
389            dependencies: SizeDependency::SubItemSize(vec![]),
390        }
391    }
392
393    fn add_constant(&mut self, constant: u64) {
394        self.constant += constant;
395    }
396
397    fn add_dependency(&mut self, index: usize) {
398        if let SizeDependency::SubItemSize(deps) = &mut self.dependencies {
399            deps.push(index);
400        } else {
401            panic!(
402                "add_dependency should be only called on SizeExpression with SubItemSize dependency."
403            )
404        }
405    }
406
407    fn is_constant(&self) -> bool {
408        match &self.dependencies {
409            SizeDependency::Var(_) => false,
410            SizeDependency::SubItemSize(items) => items.is_empty(),
411        }
412    }
413
414    fn content_size(&self) -> u64 {
415        assert!(self.is_constant());
416        self.constant
417    }
418
419    fn item_size(&self) -> u64 {
420        assert!(self.is_constant());
421        match self.arg_size {
422            ArgSize::Constant(arg) => 1 + arg + self.constant,
423            ArgSize::LengthOfContentSize => 1 + cbor::arg_size(self.constant).0 + self.constant,
424        }
425    }
426
427    // Generate the expression to compute `content_size`.
428    fn content_size_expression(&self) -> String {
429        let mut terms = Vec::new();
430
431        if self.constant > 0 {
432            terms.push(self.constant.to_string());
433        }
434
435        match &self.dependencies {
436            SizeDependency::Var(var) => terms.push(var.size_expression()),
437            SizeDependency::SubItemSize(items) => {
438                terms.extend(items.iter().map(|idx| format!("item_size_{idx}")))
439            }
440        };
441
442        terms.join(" + ")
443    }
444
445    // Generate the expression to compute `item_size`.
446    // If not a constant, the result expression will always depend on its `content_size`.
447    fn item_size_expression(&self) -> String {
448        if self.is_constant() {
449            self.item_size().to_string()
450        } else {
451            match &self.arg_size {
452                ArgSize::Constant(arg) => {
453                    format!("{k} + content_size_{idx}", k = 1 + arg, idx = self.index)
454                }
455                ArgSize::LengthOfContentSize => format!(
456                    "1 + cbor_calc_arg_size(content_size_{idx}) + content_size_{idx}",
457                    idx = self.index
458                ),
459            }
460        }
461    }
462}
463
464// Derive the size expression of each CodegenStructure.
465//
466// `get_var_size` is used to query the exact size of CodegenVar.
467// If the return value is None, it means that the size can't be determined until runtime.
468//
469// The reason to make this function as a parameter is that we currently have two scenarios:
470//
471//   1. get the exact size to generate the size computation code
472//
473//      `get_var_size` only returns a value when the size is exact.
474//
475//   2. get the maximum size of this structure
476//
477//      `get_var_size` always returns the maximum size.
478fn derive_size_expressions<'a>(
479    nodes: &'a [CodegenStructure],
480    get_var_size: &impl Fn(&CodegenVar) -> Option<u64>,
481) -> Result<Vec<SizeExpression<'a>>> {
482    let mut exps = Vec::<SizeExpression>::new();
483
484    // The items in the slice are alreay sorted in postorder.
485    for (idx, node) in nodes.iter().enumerate() {
486        // A helper function to generate a size expression from CodegenVar.
487        let from_var = |arg_size, var| {
488            if let Some(size) = get_var_size(var) {
489                SizeExpression::empty_dependency(idx, arg_size, size)
490            } else {
491                SizeExpression::from_codegenvar(idx, arg_size, var)
492            }
493        };
494
495        // A helper function to generate a size expression from dependent items.
496        let from_deps = |arg_size, idxs: &mut dyn Iterator<Item = &usize>| {
497            let mut exp = SizeExpression::empty_dependency(idx, arg_size, 0);
498            for idx in idxs {
499                let dep = &exps[*idx];
500                if dep.is_constant() {
501                    exp.add_constant(dep.item_size());
502                } else {
503                    exp.add_dependency(*idx);
504                }
505            }
506            exp
507        };
508
509        let exp = match node {
510            CodegenStructure::Var(var) => match var.variable_type() {
511                VariableType::ByteArray => from_var(ArgSize::LengthOfContentSize, var),
512                VariableType::String => from_var(ArgSize::LengthOfContentSize, var),
513                VariableType::Integer => from_var(ArgSize::Constant(0), var),
514            },
515            CodegenStructure::CborByteArray(inner) => {
516                from_deps(ArgSize::LengthOfContentSize, &mut iter::once(inner))
517            }
518            CodegenStructure::Map(items) => {
519                let len = items.len().try_into().with_context(|| {
520                    format!("the size of the map is too large for u64: {}", items.len())
521                })?;
522                from_deps(
523                    ArgSize::Constant(cbor::arg_size(len).0),
524                    &mut items.iter().flat_map(|(a, b)| [a, b]),
525                )
526            }
527            CodegenStructure::Array(items) => {
528                let len = items.len().try_into().with_context(|| {
529                    format!(
530                        "the size of the array is too large for u64: {}",
531                        items.len()
532                    )
533                })?;
534                from_deps(ArgSize::Constant(cbor::arg_size(len).0), &mut items.iter())
535            }
536        };
537
538        exps.push(exp);
539    }
540
541    Ok(exps)
542}
543
544fn collect_codegenvar(template: &CwtTemplate) -> Result<CodegenVarTable> {
545    let mut vars = IndexMap::<String, CodegenVar>::new();
546
547    for (name, var) in &template.variables {
548        let ret = vars.insert(name.clone(), CodegenVar::from_template_variable(name, var)?);
549        if ret.is_some() {
550            bail!("A variable name {} already exists", name);
551        }
552    }
553
554    for (name, var) in &template.constants {
555        let ret = vars.insert(name.clone(), CodegenVar::from_template_constant(name, var)?);
556        if ret.is_some() {
557            bail!("A constant name {} already exists", name);
558        }
559    }
560
561    Ok(vars)
562}
563
564// Generate the data members of the input structure.
565fn generate_input_fields(vars: &CodegenVarTable, indent: &str) -> String {
566    vars.iter()
567        .filter(|(_, var)| !var.is_constant())
568        .map(|(_, var)| var.declarations(indent))
569        .collect()
570}
571
572// Generate the size computation code for each item whose size can't be determined until runtime.
573fn generate_size_computations(size_exps: &[SizeExpression], prefix: &str) -> Result<String> {
574    assert!(!size_exps.is_empty());
575
576    let mut code = String::new();
577    for size_exp in size_exps.iter().filter(|exp| !exp.is_constant()) {
578        code += &format!(
579            "{prefix}const size_t content_size_{} = {};\n",
580            size_exp.index,
581            size_exp.content_size_expression()
582        );
583        code += &format!(
584            "{prefix}const size_t item_size_{} = {};\n",
585            size_exp.index,
586            size_exp.item_size_expression()
587        );
588    }
589
590    Ok(code)
591}
592
593// Generate the code to check the sizes of input arguments.
594// The first return value is used to check all input sizes.
595// The second return value is used to check the final output size.
596fn generate_size_checks(
597    vars: &CodegenVarTable,
598    whole_struct_size: &SizeExpression,
599    prefix: &str,
600) -> Result<(String, String)> {
601    let mut input_checks = String::new();
602
603    // Check the size of each input variable.
604    for (_, var) in vars
605        .iter()
606        .filter(|(_, var)| !var.is_constant() && var.variable_type() != VariableType::Integer)
607    {
608        let size_exp = var.size_expression();
609        let cond = match var.size {
610            VariableSize::ExactSize(size) => format!("!= {size}"),
611            VariableSize::MaxSize(size) => format!("> {size}"),
612        };
613
614        input_checks +=
615            &format!("{prefix}if ({size_exp} {cond}) return kErrorCertInvalidArgument;\n");
616    }
617
618    let size_bound = if whole_struct_size.is_constant() {
619        whole_struct_size.item_size().to_string()
620    } else {
621        format!("item_size_{idx}", idx = whole_struct_size.index)
622    };
623
624    // Check the input buffer size.
625    input_checks +=
626        &format!("{prefix}if (*inout_size < {size_bound}) return kErrorCertInvalidSize;\n");
627
628    // Make sure that the output size is the same as calculated root `item_size`.
629    let output_checks =
630        format!("{prefix}if (*inout_size != {size_bound}) return kErrorCertInternal;\n");
631
632    Ok((input_checks, output_checks))
633}
634
635fn collect_preorder_indicies_helper(nodes: &[CodegenStructure], root: usize, res: &mut Vec<usize>) {
636    res.push(root);
637    match &nodes[root] {
638        CodegenStructure::Var(_) => {}
639        CodegenStructure::CborByteArray(inner) => {
640            collect_preorder_indicies_helper(nodes, *inner, res);
641        }
642        CodegenStructure::Map(items) => {
643            for (k, v) in items {
644                collect_preorder_indicies_helper(nodes, *k, res);
645                collect_preorder_indicies_helper(nodes, *v, res);
646            }
647        }
648        CodegenStructure::Array(items) => {
649            for item in items {
650                collect_preorder_indicies_helper(nodes, *item, res);
651            }
652        }
653    }
654}
655
656fn collect_preorder_indicies(nodes: &[CodegenStructure]) -> Vec<usize> {
657    let mut res = Vec::new();
658    collect_preorder_indicies_helper(nodes, nodes.len() - 1, &mut res);
659    res
660}
661
662// Each CodegenStructure maps to a CodeBlock.
663type CodeBlock = Vec<GeneratedCode>;
664
665#[derive(Clone, Debug, PartialEq, Eq)]
666enum GeneratedCode {
667    // A single CBOR-function call.
668    FunctionCall(String),
669    // If all the arguments are constants, derive its resulting bytes directly.
670    CborBytes(Vec<u8>),
671}
672
673struct GeneratedCborInstructions {
674    constant_definitions: String,
675    cbor_instructions: String,
676}
677
678fn generate_cbor_instructions(
679    nodes: &[CodegenStructure],
680    size_exps: &[SizeExpression],
681    prefix: &str,
682) -> Result<GeneratedCborInstructions> {
683    assert!(nodes.len() == size_exps.len());
684
685    let call_wrapper = |func_call: String| {
686        indoc::formatdoc! { r#"
687            {prefix}{func_call};
688            "#
689        }
690    };
691
692    let gen_inst = |inst: String| GeneratedCode::FunctionCall(call_wrapper(inst));
693
694    // For each CodegenStructure, generate its CodeBlock first.
695    let mut code_blocks = Vec::<CodeBlock>::new();
696    for (idx, (node, size_exp)) in nodes.iter().zip(size_exps.iter()).enumerate() {
697        let code_block = match node {
698            CodegenStructure::Var(var) => match &var.value {
699                CodegenVarValue::ByteArray(val) => {
700                    let header = if let VariableSize::ExactSize(size) = var.size {
701                        GeneratedCode::CborBytes(cbor::byte_array_header(size))
702                    } else {
703                        gen_inst(format!("cbor_write_bstr_header(&cbor, content_size_{idx})"))
704                    };
705
706                    let content = if let Some(constant) = val {
707                        GeneratedCode::CborBytes(constant.clone())
708                    } else {
709                        let val = var.value_expression();
710                        let size = var.size_expression();
711                        gen_inst(format!("cbor_write_raw_bytes(&cbor, {val}, {size})"))
712                    };
713
714                    vec![header, content]
715                }
716                CodegenVarValue::String(val) => {
717                    let header = if let VariableSize::ExactSize(size) = var.size {
718                        GeneratedCode::CborBytes(cbor::string_header(size))
719                    } else {
720                        gen_inst(format!("cbor_write_tstr_header(&cbor, content_size_{idx})"))
721                    };
722
723                    let content = if let Some(constant) = val {
724                        GeneratedCode::CborBytes(constant.as_bytes().to_vec())
725                    } else {
726                        let val = var.value_expression();
727                        let size = var.size_expression();
728                        gen_inst(format!(
729                            "cbor_write_raw_bytes(&cbor, (uint8_t *){val}, {size})"
730                        ))
731                    };
732
733                    vec![header, content]
734                }
735                CodegenVarValue::Integer(val) => match val {
736                    Some(constant) => {
737                        vec![GeneratedCode::CborBytes(cbor::int(*constant))]
738                    }
739                    None => {
740                        let val = var.value_expression();
741                        vec![gen_inst(format!("cbor_write_int(&cbor, {val})"))]
742                    }
743                },
744            },
745            CodegenStructure::CborByteArray(_) => {
746                if size_exp.is_constant() {
747                    let size = size_exp.content_size();
748                    vec![GeneratedCode::CborBytes(cbor::byte_array_header(size))]
749                } else {
750                    vec![gen_inst(format!(
751                        "cbor_write_bstr_header(&cbor, content_size_{idx})"
752                    ))]
753                }
754            }
755            CodegenStructure::Map(items) => {
756                let len = items.len().try_into().with_context(|| {
757                    format!("the size of the map is too large for u64: {}", items.len())
758                })?;
759                vec![GeneratedCode::CborBytes(cbor::map_header(len))]
760            }
761            CodegenStructure::Array(items) => {
762                let len = items.len().try_into().with_context(|| {
763                    format!(
764                        "the size of the array is too large for u64: {}",
765                        items.len()
766                    )
767                })?;
768                vec![GeneratedCode::CborBytes(cbor::array_header(len))]
769            }
770        };
771
772        code_blocks.push(code_block);
773    }
774
775    // Collect the code blocks, and then linearize them into a sequence of single operations.
776    let order = collect_preorder_indicies(nodes);
777    let insts = order.iter().flat_map(|&idx| code_blocks[idx].clone());
778
779    let mut constant_definitions = String::new();
780    let mut cbor_instructions = String::new();
781
782    // For neighboring CBOR, we collect them into a single array and write them at once.
783    let mut idx = 0usize;
784    for (is_constant, chunk) in &insts.chunk_by(|inst| matches!(inst, GeneratedCode::CborBytes(_)))
785    {
786        if is_constant {
787            let buf = chunk.flat_map(|inst| match inst {
788                GeneratedCode::CborBytes(inner) => inner,
789                _ => panic!("Shouldn't have any non GeneratedCode::CborBytes variant."),
790            });
791
792            let bytes = buf.map(|ch| format!("{}", ch));
793            let initializer: String =
794                itertools::Itertools::intersperse(bytes, ", ".to_owned()).collect();
795
796            constant_definitions += &indoc::formatdoc! { r#"
797                {prefix}const static uint8_t binary_{idx}[] = {{{initializer}}};
798                "#};
799            cbor_instructions += &call_wrapper(format!(
800                "cbor_write_raw_bytes(&cbor, binary_{idx}, sizeof(binary_{idx}))"
801            ));
802
803            idx += 1;
804        } else {
805            for block in chunk {
806                match block {
807                    GeneratedCode::FunctionCall(inner) => cbor_instructions += &inner,
808                    _ => panic!("Shouldn't have any non GeneratedCode::FunctionCall variant."),
809                }
810            }
811        }
812    }
813
814    Ok(GeneratedCborInstructions {
815        constant_definitions,
816        cbor_instructions,
817    })
818}
819
820fn generate_header(from_file: &str, template_name: &str, max_size: u64, decls: String) -> String {
821    let preproc_guard_include = template_name.to_shouty_snake_case();
822    let enum_name = template_name.to_upper_camel_case();
823
824    indoc::formatdoc! { r#"
825        // Copyright lowRISC contributors (OpenTitan project).
826        // Licensed under the Apache License, Version 2.0, see LICENSE for details.
827        // SPDX-License-Identifier: Apache-2.0
828
829        // This file was automatically generated using opentitantool from:
830        // {from_file}
831        #ifndef __{preproc_guard_include}__
832        #define __{preproc_guard_include}__
833
834        #include "sw/device/lib/base/status.h"
835
836        typedef struct {template_name}_values {{
837        {decls}}} {template_name}_values_t;
838
839        enum {{
840          k{enum_name}MaxVariableSizeBytes = {max_size},
841        }};
842
843        rom_error_t {template_name}_build({template_name}_values_t *values, uint8_t *output, size_t *inout_size);
844
845        #endif
846    "#}
847}
848
849fn generate_source(
850    from_file: &str,
851    template_name: &str,
852    constant_definitions: &str,
853    size_computations: &str,
854    input_size_checks: &str,
855    output_size_checks: &str,
856    cbor_instructions: &str,
857) -> String {
858    let source_template = indoc::formatdoc! { r#"
859        // Copyright lowRISC contributors (OpenTitan project).
860        // Licensed under the Apache License, Version 2.0, see LICENSE for details.
861        // SPDX-License-Identifier: Apache-2.0
862
863        // This file was automatically generated using opentitantool from:
864        // {from_file}
865
866        #include "{template_name}.h"
867        #include "sw/device/silicon_creator/lib/cert/cbor.h"
868
869        {constant_definitions}
870        rom_error_t {template_name}_build({template_name}_values_t *values, uint8_t *buffer, size_t *inout_size) {{
871        {size_computations}
872        {input_size_checks}
873          cbor_out_t cbor;
874          cbor_out_init(&cbor, buffer);
875
876        {cbor_instructions}
877          *inout_size = cbor_out_size(&cbor);
878        {output_size_checks}
879          return kErrorOk;
880        }}
881    "#};
882
883    source_template
884}
885
886impl CwtTemplate {
887    pub fn from_hjson_str(content: &str) -> Result<CwtTemplate> {
888        deser_hjson::from_str(content).context("CwtTemplate::from_hjson_str failed")
889    }
890}
891
892pub fn load_cwt_template(path: &PathBuf) -> Result<CwtTemplate> {
893    let template_content = fs::read_to_string(path)
894        .with_context(|| format!("could not load the template file {}", path.display()))?;
895
896    CwtTemplate::from_hjson_str(&template_content).with_context(|| {
897        format!(
898            "failed to parse CwtTemplate from the template file {}",
899            path.display()
900        )
901    })
902}
903
904pub fn generate_cert(from_file: &str, template: &CwtTemplate) -> Result<Codegen> {
905    let vars = collect_codegenvar(template).context("collect_codegenvar failed")?;
906
907    let structures = CodegenStructure::from_template(template, &vars)
908        .context("CodegenStructure::from_template failed")?;
909
910    let max_size = {
911        let exp = derive_size_expressions(&structures, &|var| match var.size {
912            VariableSize::ExactSize(size) => Some(size),
913            VariableSize::MaxSize(size) => Some(size),
914        })
915        .context("derive_size_expressions for maximum size failed")?;
916
917        exp.last()
918            .context("there isn't any item in the structure")?
919            .item_size()
920    };
921
922    let decls = generate_input_fields(&vars, "  ");
923
924    let source_h = generate_header(from_file, &template.name, max_size, decls);
925
926    let exact_sizes = derive_size_expressions(&structures, &|var| match var.size {
927        VariableSize::ExactSize(size) => Some(size),
928        _ => None,
929    })
930    .context("derive_size_expressions for exact size failed")?;
931
932    let size_computations = generate_size_computations(&exact_sizes, "  ")
933        .context("generate_size_computations failed")?;
934
935    let (input_size_checks, output_size_checks) = generate_size_checks(
936        &vars,
937        exact_sizes
938            .last()
939            .context("there isn't any item in the structure")?,
940        "  ",
941    )
942    .context("generate_size_checks failed")?;
943
944    let GeneratedCborInstructions {
945        constant_definitions,
946        cbor_instructions,
947    } = generate_cbor_instructions(&structures, &exact_sizes, "  ")
948        .context("generate_cbor_instructions failed")?;
949
950    let source_c = generate_source(
951        from_file,
952        &template.name,
953        &constant_definitions,
954        &size_computations,
955        &input_size_checks,
956        &output_size_checks,
957        &cbor_instructions,
958    );
959
960    // TODO: finish the unittest
961    let source_unittest = String::new();
962
963    Ok(Codegen {
964        source_h,
965        source_c,
966        source_unittest,
967    })
968}