1use anyhow::{Context, Result};
9use heck::ToUpperCamelCase;
10use indexmap::IndexMap;
11use itertools::Itertools;
12use std::fmt::Write;
13
14use crate::asn1::codegen::{self, CodegenOutput, VariableCodegenInfo, VariableInfo};
15use crate::asn1::x509::X509;
16use crate::template::subst::{Subst, SubstValue};
17use crate::template::{
18 EcdsaSignature, Signature, SizeRange, Template, Value, Variable, VariableType,
19};
20use crate::x509;
21
22const TEST_CASE_COUNT: u32 = 100;
24
25pub struct Codegen {
26 pub source_h: String,
28 pub source_c: String,
30 pub source_unittest: String,
32}
33
34pub fn generate_cert(from_file: &str, tmpl: &Template) -> Result<Codegen> {
63 let mut source_c = String::new();
64 let mut source_h = String::new();
65 let mut source_unittest = String::new();
66
67 let license_and_warning = indoc::formatdoc! { r#"
68 // Copyright lowRISC contributors (OpenTitan project).
69 // Licensed under the Apache License, Version 2.0, see LICENSE for details.
70 // SPDX-License-Identifier: Apache-2.0
71
72 // This file was automatically generated using opentitantool from:
73 // {from_file}
74 "#};
75
76 source_c.push_str(&license_and_warning);
78 source_h.push_str(&license_and_warning);
79 let preproc_guard_include = tmpl.name.to_uppercase();
80 writeln!(source_h, "#ifndef __{}__", preproc_guard_include)?;
81 writeln!(source_h, "#define __{}__\n", preproc_guard_include)?;
82
83 source_c.push('\n');
85 writeln!(source_c, "#include \"{}.h\"", tmpl.name)?;
86 source_c.push_str("#include \"sw/device/silicon_creator/lib/cert/asn1.h\"\n\n");
87 source_c.push_str("#include \"sw/device/silicon_creator/lib/cert/template.h\"\n\n");
88
89 source_h.push_str("#include \"sw/device/lib/base/status.h\"\n\n");
90
91 let mut tbs_vars = IndexMap::<String, VariableType>::new();
93 let mut sig_vars = IndexMap::<String, VariableType>::new();
94 for (var_name, var) in tmpl.variables.clone() {
95 if var_appears_in_sig(&var_name, &tmpl.certificate.signature) {
96 sig_vars.insert(var_name, var);
97 } else {
98 tbs_vars.insert(var_name, var);
99 }
100 }
101
102 let tbs_value_struct_name = format!("{}_tbs_values", tmpl.name);
104 source_h.push_str(&generate_value_struct(&tbs_value_struct_name, &tbs_vars));
105 let tbs_value_struct_name = tbs_value_struct_name + "_t";
106
107 let generate_tbs_fn_name = format!("{}_build_tbs", tmpl.name);
109 let generate_tbs_fn_params =
110 format!("{tbs_value_struct_name} *values, uint8_t *out_buf, size_t *inout_size");
111 let (generate_tbs_fn_def, generate_tbs_fn_impl) = generate_builder(
112 CertificateComponent::Tbs,
113 &generate_tbs_fn_name,
114 &generate_tbs_fn_params,
115 &tbs_vars,
116 |builder| X509::push_tbs_certificate(builder, &tmpl.certificate),
117 )?;
118
119 let tbs_binary_val_name = "tbs";
121 sig_vars.insert(
122 tbs_binary_val_name.to_string(),
123 VariableType::ByteArray {
124 size: SizeRange::RangeSize(
125 generate_tbs_fn_impl.min_size,
126 generate_tbs_fn_impl.max_size,
127 ),
128 tweak_msb: None,
129 },
130 );
131 let tbs_binary_val = Value::Variable(Variable {
132 name: tbs_binary_val_name.to_string(),
133 convert: None,
134 });
135
136 let sig_value_struct_name = format!("{}_sig_values", tmpl.name);
138 source_h.push_str(&generate_value_struct(&sig_value_struct_name, &sig_vars));
139 let sig_value_struct_name = sig_value_struct_name + "_t";
140
141 let generate_cert_fn_name = format!("{}_build_cert", tmpl.name);
143 let generate_cert_fn_params =
144 format!("{sig_value_struct_name} *values, uint8_t *out_buf, size_t *inout_size");
145 let (generate_cert_fn_def, generate_cert_fn_impl) = generate_builder(
146 CertificateComponent::Certificate,
147 &generate_cert_fn_name,
148 &generate_cert_fn_params,
149 &sig_vars,
150 |builder| X509::push_certificate(builder, &tbs_binary_val, &tmpl.certificate.signature),
151 )?;
152
153 let tmpl_name = tmpl.name.to_upper_camel_case();
154
155 source_h.push_str("enum {\n");
157 for (var_name, var_type) in tbs_vars.iter().chain(sig_vars.iter()) {
158 let (codegen, _) = c_variable_info(var_name, "", var_type);
159 let const_name = var_name.to_upper_camel_case();
160 let (min_size, max_size) = if let VariableCodegenInfo::Pointer { .. } = codegen {
161 let (min_size, max_size) = var_type.array_size();
162 if var_type.has_constant_array_size() {
163 writeln!(
164 source_h,
165 "k{tmpl_name}Exact{const_name}SizeBytes = {max_size},"
166 )?;
167 }
168 (min_size, max_size)
169 } else {
170 (1, 100)
172 };
173 writeln!(
174 source_h,
175 "k{tmpl_name}Min{const_name}SizeBytes = {min_size},"
176 )?;
177 writeln!(
178 source_h,
179 "k{tmpl_name}Max{const_name}SizeBytes = {max_size},"
180 )?;
181 }
182 source_h.push_str("};\n");
183
184 for (var_name, _) in tbs_vars.iter().chain(sig_vars.iter()) {
186 let const_name = var_name.to_upper_camel_case();
187 writeln!(source_h, "#define k{tmpl_name}Field{const_name} {var_name}")?;
188 }
189
190 let max_cert_size_const_name = format!("k{}MaxCertSizeBytes", tmpl.name.to_upper_camel_case());
191 source_h.push_str("// Maximum possible size of a certificate\n");
192 source_h.push_str(&indoc::formatdoc! {"enum {{
193 {max_cert_size_const_name} = {},
194 }};", generate_cert_fn_impl.max_size});
195
196 source_h.push_str("\n\n");
198 source_h.push_str(&generate_tbs_fn_def);
199 source_h.push_str(&generate_cert_fn_def);
200 source_h.push('\n');
201
202 source_c.push_str(&generate_tbs_fn_impl.code);
204 source_c.push_str(&generate_cert_fn_impl.code);
205 source_c.push('\n');
206
207 writeln!(source_h, "\n#endif /* __{}__ */", preproc_guard_include)?;
208
209 source_unittest.push_str(&license_and_warning);
211 source_unittest.push('\n');
212 source_unittest.push_str("extern \"C\" {\n");
213 writeln!(source_unittest, "#include \"{}.h\"", tmpl.name)?;
214 source_unittest.push_str("}\n");
215 source_unittest.push_str("#include \"gtest/gtest.h\"\n\n");
216
217 for idx in 0..TEST_CASE_COUNT {
218 let test_case = generate_test_case(
219 &format!("Verify{idx}"),
220 &tbs_vars,
221 &sig_vars,
222 generate_tbs_fn_impl.min_size,
223 generate_tbs_fn_impl.max_size,
224 generate_cert_fn_impl.max_size,
225 tmpl,
226 )?;
227 source_unittest.push_str(&test_case);
228 }
229
230 Ok(Codegen {
231 source_h,
232 source_c,
233 source_unittest,
234 })
235}
236
237fn generate_test_case(
239 test_name: &str,
240 tbs_vars: &IndexMap<String, VariableType>,
241 sig_vars: &IndexMap<String, VariableType>,
242 min_tbs_size: usize,
243 max_tbs_size: usize,
244 max_cert_size: usize,
245 tmpl: &Template,
246) -> Result<String> {
247 let mut source_unittest = String::new();
248 let unittest_data = tmpl.random_test()?;
249 let expected_cert = x509::generate_certificate(&tmpl.subst(&unittest_data)?)?;
250
251 let tmpl_name = tmpl.name.to_upper_camel_case();
252 let generate_tbs_fn_name = format!("{}_build_tbs", tmpl.name);
253 let generate_cert_fn_name = format!("{}_build_cert", tmpl.name);
254 let tbs_value_struct_name = format!("{}_tbs_values_t", tmpl.name);
255 let sig_value_struct_name = format!("{}_sig_values_t", tmpl.name);
256
257 source_unittest.push_str(&format! { r#"
258 TEST({tmpl_name}, {test_name})
259 "#});
260
261 source_unittest.push_str(
262 "
263 {
264 ",
265 );
266
267 for (var_name, data) in unittest_data.values {
269 match data {
270 SubstValue::ByteArray(bytes) => {
271 writeln!(
272 source_unittest,
273 "static uint8_t g_{var_name}[] = {{ {} }};",
274 bytes
275 .iter()
276 .map(|x| format!("{:#02x}", x))
277 .collect::<Vec<_>>()
278 .join(", ")
279 )?;
280 }
281 SubstValue::String(s) => {
282 let s = s.chars().map(|c| format!("'{c}'")).join(", ");
283 writeln!(source_unittest, "static char g_{var_name}[] = {{{s}}};")?
284 }
285
286 SubstValue::Uint32(val) => writeln!(source_unittest, "uint32_t g_{var_name} = {val};")?,
287 SubstValue::Boolean(val) => writeln!(source_unittest, "bool g_{var_name} = {val};")?,
288 }
289 }
290 source_unittest.push('\n');
292 writeln!(source_unittest, "{tbs_value_struct_name} g_tbs_values = {{")?;
293 source_unittest.push_str(&generate_value_struct_assignment(tbs_vars)?);
294 source_unittest.push_str("};\n");
295 source_unittest.push('\n');
297 writeln!(source_unittest, "uint8_t g_tbs[{max_tbs_size}];")?;
298 source_unittest.push('\n');
300 writeln!(source_unittest, "{sig_value_struct_name} g_sig_values = {{")?;
301 source_unittest.push_str(&generate_value_struct_assignment(sig_vars)?);
302 source_unittest.push_str("};\n");
303 source_unittest.push('\n');
305 writeln!(source_unittest, "uint8_t g_cert_data[{max_cert_size}];\n")?;
306 writeln!(
308 source_unittest,
309 "const uint8_t kExpectedCert[{}] = {{ {} }};\n",
310 expected_cert.len(),
311 expected_cert
312 .iter()
313 .map(|x| format!("0x{:02x}", x))
314 .collect::<Vec<_>>()
315 .join(", ")
316 )?;
317 source_unittest.push('\n');
318
319 let no_tbs_size = if min_tbs_size == max_tbs_size {
321 "// "
322 } else {
323 ""
324 };
325
326 source_unittest.push_str(&format! { r#"
328 size_t tbs_size = sizeof(g_tbs);
329 EXPECT_EQ(kErrorOk, {generate_tbs_fn_name}(&g_tbs_values, g_tbs, &tbs_size));
330 EXPECT_GE(tbs_size, {min_tbs_size});
331 EXPECT_LE(tbs_size, {max_tbs_size});
332
333 {no_tbs_size} g_sig_values.tbs_size = tbs_size;
334
335 size_t cert_size = sizeof(g_cert_data);
336 EXPECT_EQ(kErrorOk, {generate_cert_fn_name}(&g_sig_values, g_cert_data, &cert_size));
337 EXPECT_EQ(cert_size, sizeof(kExpectedCert));
338 printf("Generated cert: \n");
339 for (size_t i=0; i<cert_size; i++) {{
340 printf("%02x, ", g_cert_data[i]);
341 }}
342 printf("\n");
343 EXPECT_EQ(0, memcmp(g_cert_data, kExpectedCert, cert_size));
344 "#,
345 });
346
347 source_unittest.push_str(
348 "
349 }
350 ",
351 );
352
353 Ok(source_unittest)
354}
355
356fn generate_value_struct(
358 value_struct_name: &str,
359 variables: &IndexMap<String, VariableType>,
360) -> String {
361 let mut source = String::new();
362 writeln!(source, "typedef struct {value_struct_name} {{").unwrap();
363 for (var_name, var_type) in variables {
364 let (_, struct_def) = c_variable_info(var_name, "", var_type);
365 source.push_str(&struct_def);
366 }
367 writeln!(source, "}} {value_struct_name}_t;\n").unwrap();
368 source
369}
370
371fn generate_value_struct_assignment(variables: &IndexMap<String, VariableType>) -> Result<String> {
374 let mut source = String::new();
375 for (var_name, var_type) in variables {
376 let (codegen, _) = c_variable_info(var_name, "", var_type);
377 match codegen {
379 VariableCodegenInfo::Pointer {
380 ptr_expr,
381 size_expr,
382 } => {
383 writeln!(source, ".{ptr_expr} = g_{var_name},")?;
384 if !var_type.has_constant_array_size() {
385 writeln!(source, ".{size_expr} = sizeof(g_{var_name}),")?;
386 }
387 }
388 VariableCodegenInfo::Uint32 { value_expr }
389 | VariableCodegenInfo::Boolean { value_expr } => {
390 writeln!(source, ".{value_expr} = g_{var_name},\n")?;
391 }
392 }
393 }
394 Ok(source)
395}
396
397fn var_appears_in_sig(var_name: &str, sig: &Signature) -> bool {
399 match sig {
400 Signature::EcdsaWithSha256 { value } => {
401 let Some(EcdsaSignature { r, s }) = value else {
402 return false;
403 };
404 r.refers_to(var_name) || s.refers_to(var_name)
405 }
406 }
407}
408
409#[derive(Debug, PartialEq)]
410enum CertificateComponent {
411 Certificate,
412 Tbs,
413}
414
415fn generate_builder(
420 component: CertificateComponent,
421 fn_name: &str,
422 fn_params_str: &str,
423 variables: &IndexMap<String, VariableType>,
424 build: impl FnOnce(&mut codegen::Codegen) -> Result<()>,
425) -> Result<(String, CodegenOutput)> {
426 let get_var_info = |var_name: &str| -> Result<VariableInfo> {
427 let var_type = variables
428 .get(var_name)
429 .with_context(|| format!("could not find variable '{var_name}'"))
430 .copied()?;
431 let (codegen, _) = c_variable_info(var_name, "values->", &var_type);
432 Ok(VariableInfo { var_type, codegen })
433 };
434 let generate_fn_def: String;
435 let mut generated_code: CodegenOutput;
436 if component == CertificateComponent::Tbs {
437 generate_fn_def = indoc::formatdoc! { r#"
438 /**
439 * Generates a TBS certificate.
440 *
441 * @param values Pointer to a structure giving the values to use to generate the TBS
442 * portion of the certificate.
443 * @param[out] out_buf Pointer to a user-allocated buffer that will contain the TBS portion of
444 * the certificate.
445 * @param[in,out] inout_size Pointer to an integer holding the size of
446 * the provided buffer; this value will be updated to reflect the actual size of
447 * the output.
448 * @return The result of the operation.
449 */
450 rom_error_t {fn_name}({fn_params_str});
451
452 "#
453 };
454 generated_code = codegen::Codegen::generate(
455 "out_buf",
456 "inout_size",
457 &get_var_info,
458 build,
459 )?;
460 } else {
461 generate_fn_def = indoc::formatdoc! { r#"
462 /**
463 * Generates an endorsed certificate from a TBS certificate and a signature.
464 *
465 * @param values Pointer to a structure giving the values to use to generate the
466 * certificate (TBS and signature).
467 * @param[out] out_buf Pointer to a user-allocated buffer that will contain the
468 * result.
469 * @param[in,out] inout_size Pointer to an integer holding the size of
470 * the provided buffer, this value will be updated to reflect the actual size of
471 * the output.
472 * @return The result of the operation.
473 */
474 rom_error_t {fn_name}({fn_params_str});
475
476 "#
477 };
478 generated_code = codegen::Codegen::generate(
479 "out_buf",
480 "inout_size",
481 &get_var_info,
482 build,
483 )?;
484 }
485
486 let mut generate_fn_impl = String::new();
487 writeln!(
488 generate_fn_impl,
489 "rom_error_t {fn_name}({fn_params_str}) {{"
490 )?;
491 generate_fn_impl.push_str(&generated_code.code);
492 generate_fn_impl.push_str(" return kErrorOk;\n");
493 generate_fn_impl.push_str("}\n\n");
494 generated_code.code = generate_fn_impl;
495
496 Ok((generate_fn_def, generated_code))
497}
498
499fn c_integer_for_length(size: usize) -> Option<&'static str> {
502 match size {
503 4 => Some("uint32_t"),
504 _ => None,
505 }
506}
507
508fn c_variable_info(
510 name: &str,
511 struct_expr: &str,
512 var_type: &VariableType,
513) -> (VariableCodegenInfo, String) {
514 match var_type {
515 VariableType::ByteArray { .. } => {
516 if var_type.has_constant_array_size() {
517 (
518 VariableCodegenInfo::Pointer {
519 ptr_expr: format!("{struct_expr}{name}"),
520 size_expr: format!("{}", var_type.size()),
521 },
522 indoc::formatdoc! {r#"
523 // Pointer to an array of bytes.
524 uint8_t *{name};
525 "#
526 },
527 )
528 } else {
529 (
530 VariableCodegenInfo::Pointer {
531 ptr_expr: format!("{struct_expr}{name}"),
532 size_expr: format!("{struct_expr}{name}_size"),
533 },
534 indoc::formatdoc! {r#"
535 // Pointer to an array of bytes.
536 uint8_t *{name};
537 // Size of this array in bytes.
538 size_t {name}_size;
539 "#
540 },
541 )
542 }
543 }
544 VariableType::Integer { .. } => match c_integer_for_length(var_type.size()) {
545 Some(c_type) => (
546 VariableCodegenInfo::Uint32 {
547 value_expr: format!("{struct_expr}{name}"),
548 },
549 format!(" {c_type} {name};\n"),
550 ),
551 None => {
552 if var_type.has_constant_array_size() {
553 (
554 VariableCodegenInfo::Pointer {
555 ptr_expr: format!("{struct_expr}{name}"),
556 size_expr: format!("{}", var_type.size()),
557 },
558 indoc::formatdoc! {r#"
559 // Pointer to an unsigned big-endian in integer.
560 uint8_t *{name};
561 "#
562 },
563 )
564 } else {
565 (
566 VariableCodegenInfo::Pointer {
567 ptr_expr: format!("{struct_expr}{name}"),
568 size_expr: format!("{struct_expr}{name}_size"),
569 },
570 indoc::formatdoc! {r#"
571 // Pointer to an unsigned big-endian in integer.
572 uint8_t *{name};
573 // Size of this array in bytes.
574 size_t {name}_size;
575 "#
576 },
577 )
578 }
579 }
580 },
581 VariableType::String { .. } => {
582 if var_type.has_constant_array_size() {
583 (
584 VariableCodegenInfo::Pointer {
585 ptr_expr: format!("{struct_expr}{name}"),
586 size_expr: format!("{}", var_type.size()),
587 },
588 indoc::formatdoc! {r#"
589 // Pointer to a (not necessarily zero-terminated) string.
590 char *{name};
591 "#
592 },
593 )
594 } else {
595 (
596 VariableCodegenInfo::Pointer {
597 ptr_expr: format!("{struct_expr}{name}"),
598 size_expr: format!("{struct_expr}{name}_len"),
599 },
600 indoc::formatdoc! {r#"
601 // Pointer to a (not necessarily zero-terminated) string.
602 char *{name};
603 // Length of this string.
604 size_t {name}_len;
605 "#
606 },
607 )
608 }
609 }
610 VariableType::Boolean => (
611 VariableCodegenInfo::Boolean {
612 value_expr: format!("{struct_expr}{name}"),
613 },
614 format!("bool {name};\n"),
615 ),
616 }
617}