ot_certs/template/
subst.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
5//! This module defines substitution data that can be used to replace the
6//! variables in a template by actual values.
7
8use anyhow::{Context, Result, bail, ensure};
9use hex::{FromHex, ToHex};
10use indexmap::IndexMap;
11use num_bigint_dig::{BigUint, ToBigUint};
12use num_traits::Num;
13use serde::{Deserialize, Serialize};
14
15use crate::template::{
16    BasicConstraints, Certificate, CertificateExtension, Conversion, DiceTcbInfoExtension,
17    DiceTcbInfoFlags, EcPublicKey, EcPublicKeyInfo, EcdsaSignature, FirmwareId, KeyUsage,
18    Signature, SizeRange, SubjectPublicKeyInfo, Template, Value, Variable, VariableType,
19};
20
21/// Substitution value: this is the raw value loaded from a hjson/json file
22/// before any parsing.
23#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
24#[serde(untagged)]
25pub enum SubstValue {
26    ByteArray(Vec<u8>),
27    Uint32(u32),
28    String(String),
29    Boolean(bool),
30}
31
32/// Substitution data for a certificate: it maps certain variables to concrete
33/// values.
34#[derive(Clone, Debug, Default, Deserialize, Serialize)]
35pub struct SubstData {
36    #[serde(flatten)]
37    pub values: IndexMap<String, SubstValue>,
38}
39
40impl SubstData {
41    pub fn new() -> SubstData {
42        SubstData {
43            values: IndexMap::new(),
44        }
45    }
46
47    pub fn to_json(&self) -> Result<String> {
48        Ok(serde_json::to_string(&self)?)
49    }
50
51    pub fn from_json(content: &str) -> Result<SubstData> {
52        Ok(serde_json::from_str(content)?)
53    }
54}
55
56/// Trait for variable substition: implement this trait to support substition
57/// in data structure.
58pub trait Subst: Sized {
59    /// Substitute the indicated variables by their values and leave the others
60    /// untouched.
61    fn subst(&self, data: &SubstData) -> Result<Self>;
62}
63
64impl SubstValue {
65    // Parse the content of the data according to a specified
66    // type. If the type specified size is zero then any size is accepted, otherwise
67    // the size constraint will be enforced.
68    pub fn parse(&self, var_type: &VariableType) -> Result<SubstValue> {
69        match *var_type {
70            VariableType::ByteArray { .. } => self.parse_as_byte_array(var_type.size()),
71            VariableType::Integer { .. } => self.parse_as_integer(var_type.size()),
72            VariableType::String { .. } => self.parse_as_string(var_type.size()),
73            VariableType::Boolean => self.parse_as_boolean(),
74        }
75    }
76
77    fn parse_as_byte_array(&self, size: usize) -> Result<SubstValue> {
78        match self {
79            SubstValue::ByteArray(bytes) => {
80                ensure!(
81                    size == 0 || bytes.len() == size,
82                    "expected a byte array of size {size} but got {} bytes",
83                    bytes.len()
84                );
85                Ok(self.clone())
86            }
87            SubstValue::String(s) => {
88                // To be consistent with the template, interpret this
89                // as a hexstring.
90                let bytes = Vec::<u8>::from_hex(s)
91                    .with_context(|| format!("cannot parse {s} as an hexstring"))?;
92                ensure!(
93                    size == 0 || bytes.len() == size,
94                    "expected a byte array of size {size} but got {} bytes",
95                    bytes.len()
96                );
97                Ok(SubstValue::ByteArray(bytes))
98            }
99            _ => bail!("cannot parse value {self:?} as a byte-array"),
100        }
101    }
102
103    fn parse_as_integer(&self, size: usize) -> Result<SubstValue> {
104        match self {
105            SubstValue::ByteArray(bytes) => {
106                // Integer are represented as byte arrays.
107                ensure!(
108                    size == 0 || bytes.len() <= size,
109                    "expected an integer that fits on {size} bytes but it uses {} bytes",
110                    bytes.len()
111                );
112                Ok(self.clone())
113            }
114            SubstValue::String(s) => {
115                // Unless the string starts with '0x', expect a decimal string.
116                let (radix, s) = s
117                    .strip_prefix("0x")
118                    .map_or_else(|| (10, s.as_str()), |s| (16, s));
119                let val = BigUint::from_str_radix(s, radix)
120                    .with_context(|| format!("cannot parse {s} as an integer"))?;
121                let bytes = val.to_bytes_be();
122                ensure!(
123                    size == 0 || bytes.len() <= size,
124                    "expected an integer that fits on {size} bytes but it uses {} bytes",
125                    bytes.len()
126                );
127                Ok(SubstValue::ByteArray(bytes))
128            }
129            SubstValue::Uint32(x) => {
130                let bigint = x.to_biguint().expect("cannot convert a i32 to BigInt");
131                let bytes = bigint.to_bytes_be();
132                ensure!(
133                    size == 0 || bytes.len() <= size,
134                    "expected an integer that fits on {size} bytes but it uses {} bytes",
135                    bytes.len()
136                );
137                Ok(SubstValue::ByteArray(bytes))
138            }
139            _ => bail!("cannot parse value {self:?} as an integer"),
140        }
141    }
142
143    fn parse_as_string(&self, size: usize) -> Result<SubstValue> {
144        match self {
145            SubstValue::String(s) => {
146                ensure!(
147                    size == 0 || s.len() <= size,
148                    "expected a string of at most {size} bytes but it uses {} bytes",
149                    s.len()
150                );
151                Ok(self.clone())
152            }
153            _ => bail!("cannot parse value {self:?} as a string"),
154        }
155    }
156
157    fn parse_as_boolean(&self) -> Result<SubstValue> {
158        Ok(match self {
159            SubstValue::Boolean(_) => self.clone(),
160            SubstValue::String(s) => match s.as_str() {
161                "true" => SubstValue::Boolean(true),
162                "false" => SubstValue::Boolean(false),
163                _ => bail!("cannot parse string '{s}' as a boolean, used either 'true' or 'false'"),
164            },
165            _ => bail!("cannot parse value {self:?} as a boolean"),
166        })
167    }
168}
169
170impl Subst for Template {
171    // Substitute data into the template. Variables that are not
172    // specified in the data are left untouched. Variables that appear
173    /// in the data will be removed from the template's list of variables.
174    // The substitution will take into account the type specified in the template
175    // variables. Consider an example where the substitution data
176    // specifies:
177    //   "x": String("3256")
178    // If the template specifies:
179    //   x: { type: "integer" }
180    // Then "3256" will be parsed as integer 3256. On the other hand,
181    // if the template specifies:
182    //   x: { type: "byte-array" }
183    // Then "3256" will be parsed as an hexstring and represent [0x32, 0x56].
184    // This function will return an error if a substitution does not make sense
185    /// (wrong type or impossible conversion).
186    fn subst(&self, data: &SubstData) -> Result<Template> {
187        // The first step is to match all variables in the substitution
188        // data with variables in the template to parse them if necessary.
189        let mut variables = self.variables.clone();
190        let mut new_data = SubstData::new();
191        for (var_name, val) in data.values.iter() {
192            let Some(var_type) = variables.shift_remove(var_name) else {
193                // Variable does not appear in the template: ignore it.
194                continue;
195            };
196            new_data.values.insert(var_name.clone(), val.parse(&var_type).with_context(
197                || format!("cannot parse content of substitution variable {var_name} according to the type {var_type:?} specified in the template ")
198            )?);
199        }
200        Ok(Template {
201            name: self.name.clone(),
202            variables,
203            certificate: self.certificate.subst(&new_data)?,
204        })
205    }
206}
207
208/// Trait to implement conversion from the raw (h)json data to structured data. This is used to implement variable
209/// substitution in `Subst`.
210pub trait ConvertValue<T>
211where
212    Self: Sized,
213{
214    /// Convert from fraw data to structured data (i.e. deserialize). Optionally provide
215    /// an indication of how the data should first be parsed and how it should be converted
216    /// to the type `T`.
217    fn convert(&self, convert: &Option<Conversion>) -> Result<T>;
218    /// Convert from structured data to raw data (i.e. serialize). The returned value shall
219    /// satisfy that if then converted using `convert(None, None)` then it should return the same
220    /// value.
221    fn unconvert(val: &T) -> Result<Self>;
222}
223
224impl ConvertValue<Vec<u8>> for SubstValue {
225    fn convert(&self, convert: &Option<Conversion>) -> Result<Vec<u8>> {
226        // Calling `parse` will ensure that that the returned value is a byte
227        // array, this avoids duplicating code.
228        let val = self.parse(&VariableType::ByteArray {
229            size: SizeRange::ExactSize(0),
230            tweak_msb: None,
231        })?;
232        // The only supported conversion to byte array is from a byte array.
233        let SubstValue::ByteArray(bytes) = val else {
234            bail!("cannot substitute a byte-array field with value {:?}", self);
235        };
236        ensure!(
237            convert.is_none(),
238            "substitution of a byte-array field with a byte-array value cannot specify a conversion"
239        );
240        Ok(bytes.clone())
241    }
242
243    fn unconvert(val: &Vec<u8>) -> Result<SubstValue> {
244        Ok(SubstValue::ByteArray(val.clone()))
245    }
246}
247
248impl ConvertValue<BigUint> for SubstValue {
249    fn convert(&self, convert: &Option<Conversion>) -> Result<BigUint> {
250        // Calling `parse` will ensure that that the returned value is a byte array.
251        let val = self.parse(&VariableType::Integer {
252            size: SizeRange::ExactSize(0),
253        })?;
254        match val {
255            SubstValue::ByteArray(bytes) => {
256                // No conversion means big-endian.
257                match convert {
258                    None | Some(Conversion::BigEndian) => Ok(BigUint::from_bytes_be(&bytes)),
259                    _ => bail!(
260                        "substitution of an integer field with a byte-array cannot specify conversion {:?}",
261                        convert
262                    ),
263                }
264            }
265            _ => bail!("cannot substitute an integer field with value {:?}", self),
266        }
267    }
268
269    fn unconvert(val: &BigUint) -> Result<SubstValue> {
270        // Big-endian byte array.
271        Ok(SubstValue::ByteArray(val.to_bytes_be()))
272    }
273}
274
275impl ConvertValue<String> for SubstValue {
276    fn convert(&self, convert: &Option<Conversion>) -> Result<String> {
277        match self {
278            SubstValue::String(x) => {
279                // No conversion supported.
280                ensure!(
281                    convert.is_none(),
282                    "substitution of a string field with a string value cannot specify a conversion"
283                );
284                Ok(x.clone())
285            }
286            SubstValue::ByteArray(bytes) => match convert {
287                Some(Conversion::LowercaseHex) => Ok(bytes.encode_hex::<String>()),
288                _ => bail!(
289                    "substitution of a string field with a byte-array cannot specify conversion {:?}",
290                    convert
291                ),
292            },
293            _ => bail!("cannot substitute a string field with value {:?}", self),
294        }
295    }
296
297    fn unconvert(val: &String) -> Result<SubstValue> {
298        // Big-endian byte array.
299        Ok(SubstValue::String(val.clone()))
300    }
301}
302
303impl ConvertValue<bool> for SubstValue {
304    fn convert(&self, convert: &Option<Conversion>) -> Result<bool> {
305        let SubstValue::Boolean(b) = self else {
306            bail!("cannot substitute a boolean field with value {:?}", self)
307        };
308        // No conversion supported.
309        ensure!(
310            convert.is_none(),
311            "substitution of a boolean field with a boolean value cannot specify a conversion"
312        );
313        Ok(*b)
314    }
315
316    fn unconvert(val: &bool) -> Result<SubstValue> {
317        // Big-endian byte array.
318        Ok(SubstValue::Boolean(*val))
319    }
320}
321
322impl<T> Subst for Value<T>
323where
324    Value<T>: Clone,
325    SubstValue: ConvertValue<T>,
326{
327    fn subst(&self, data: &SubstData) -> Result<Value<T>> {
328        match self {
329            Value::Literal(_) => Ok(self.clone()),
330            Value::Variable(Variable { name, convert }) => match data.values.get(name) {
331                None => Ok(self.clone()),
332                Some(val) => Ok(Value::Literal(val.convert(convert)?)),
333            },
334        }
335    }
336}
337
338impl Subst for Certificate {
339    fn subst(&self, data: &SubstData) -> Result<Certificate> {
340        Ok(Certificate {
341            serial_number: self
342                .serial_number
343                .subst(data)
344                .context("cannot substitute serial number")?,
345            not_before: self
346                .not_before
347                .subst(data)
348                .context("cannot substitute not_before")?,
349            not_after: self
350                .not_after
351                .subst(data)
352                .context("cannot substitute not_after")?,
353            issuer: self
354                .issuer
355                .subst(data)
356                .context("cannot substitute issuer")?,
357            subject: self
358                .subject
359                .subst(data)
360                .context("cannot substitute subject")?,
361            subject_public_key_info: self
362                .subject_public_key_info
363                .subst(data)
364                .context("cannot substitute subject public key info")?,
365            authority_key_identifier: self
366                .authority_key_identifier
367                .subst(data)
368                .context("cannot substitute authority key id")?,
369            subject_key_identifier: self
370                .subject_key_identifier
371                .subst(data)
372                .context("cannot substitute subject key id")?,
373            basic_constraints: self
374                .basic_constraints
375                .subst(data)
376                .context("cannot substitute basic constraints")?,
377            key_usage: self
378                .key_usage
379                .subst(data)
380                .context("cannot substitute key usage")?,
381            private_extensions: self
382                .private_extensions
383                .iter()
384                .map(|ext| ext.subst(data))
385                .collect::<Result<Vec<_>>>()
386                .context("cannot substitute in extensions")?,
387            signature: self
388                .signature
389                .subst(data)
390                .context("cannot substitute signature")?,
391
392            subject_alt_name: self
393                .subject_alt_name
394                .subst(data)
395                .context("cannot substitute subject alt name")?,
396        })
397    }
398}
399
400impl Subst for BasicConstraints {
401    fn subst(&self, data: &SubstData) -> Result<BasicConstraints> {
402        Ok(BasicConstraints {
403            ca: self.ca.subst(data)?,
404        })
405    }
406}
407
408impl Subst for CertificateExtension {
409    fn subst(&self, data: &SubstData) -> Result<CertificateExtension> {
410        match self {
411            CertificateExtension::DiceTcbInfo(dice) => Ok(CertificateExtension::DiceTcbInfo(
412                dice.subst(data)
413                    .context("cannot substitute in DICE extension")?,
414            )),
415        }
416    }
417}
418
419impl Subst for DiceTcbInfoExtension {
420    fn subst(&self, data: &SubstData) -> Result<DiceTcbInfoExtension> {
421        Ok(DiceTcbInfoExtension {
422            model: self
423                .model
424                .subst(data)
425                .context("cannot substitute DICE model")?,
426            vendor: self
427                .vendor
428                .subst(data)
429                .context("cannot substitute DICE vendor")?,
430            version: self
431                .version
432                .subst(data)
433                .context("cannot substitute DICE version")?,
434            svn: self.svn.subst(data).context("cannot substitute DICE svn")?,
435            layer: self
436                .layer
437                .subst(data)
438                .context("cannot substitute DICE layer")?,
439            fw_ids: self
440                .fw_ids
441                .subst(data)
442                .context("cannot substitute DICE firmware ids")?,
443            flags: self
444                .flags
445                .subst(data)
446                .context("cannot substitute DICE flags")?,
447        })
448    }
449}
450
451impl Subst for FirmwareId {
452    fn subst(&self, data: &SubstData) -> Result<FirmwareId> {
453        Ok(FirmwareId {
454            hash_algorithm: self.hash_algorithm,
455            digest: self.digest.subst(data)?,
456        })
457    }
458}
459
460impl Subst for DiceTcbInfoFlags {
461    fn subst(&self, data: &SubstData) -> Result<DiceTcbInfoFlags> {
462        Ok(DiceTcbInfoFlags {
463            not_configured: self
464                .not_configured
465                .subst(data)
466                .context("cannot substitute not_configured flag")?,
467            not_secure: self
468                .not_secure
469                .subst(data)
470                .context("cannot substitute not_configured flag")?,
471            recovery: self
472                .recovery
473                .subst(data)
474                .context("cannot substitute not_configured flag")?,
475            debug: self
476                .debug
477                .subst(data)
478                .context("cannot substitute not_configured flag")?,
479        })
480    }
481}
482
483impl Subst for KeyUsage {
484    fn subst(&self, data: &SubstData) -> Result<KeyUsage> {
485        Ok(KeyUsage {
486            digital_signature: self
487                .digital_signature
488                .subst(data)
489                .context("cannot substitute digital signature key usage")?,
490            key_agreement: self
491                .key_agreement
492                .subst(data)
493                .context("cannot substitute key agreement")?,
494            cert_sign: self
495                .cert_sign
496                .subst(data)
497                .context("cannot substitute cert sign")?,
498        })
499    }
500}
501
502impl Subst for SubjectPublicKeyInfo {
503    fn subst(&self, data: &SubstData) -> Result<SubjectPublicKeyInfo> {
504        match self {
505            SubjectPublicKeyInfo::EcPublicKey(ec) => {
506                Ok(SubjectPublicKeyInfo::EcPublicKey(ec.subst(data)?))
507            }
508        }
509    }
510}
511
512impl Subst for EcPublicKeyInfo {
513    fn subst(&self, data: &SubstData) -> Result<EcPublicKeyInfo> {
514        Ok(EcPublicKeyInfo {
515            curve: self.curve.clone(),
516            public_key: self.public_key.subst(data)?,
517        })
518    }
519}
520
521impl Subst for EcPublicKey {
522    fn subst(&self, data: &SubstData) -> Result<EcPublicKey> {
523        Ok(EcPublicKey {
524            x: self.x.subst(data)?,
525            y: self.y.subst(data)?,
526        })
527    }
528}
529
530impl Subst for Signature {
531    fn subst(&self, data: &SubstData) -> Result<Signature> {
532        match self {
533            Signature::EcdsaWithSha256 { value } => Ok(Signature::EcdsaWithSha256 {
534                value: value.subst(data)?,
535            }),
536        }
537    }
538}
539
540impl Subst for EcdsaSignature {
541    fn subst(&self, data: &SubstData) -> Result<EcdsaSignature> {
542        Ok(EcdsaSignature {
543            r: self.r.subst(data)?,
544            s: self.s.subst(data)?,
545        })
546    }
547}
548
549impl<T> Subst for Option<T>
550where
551    T: Subst,
552{
553    fn subst(&self, data: &SubstData) -> Result<Option<T>> {
554        self.as_ref().map(|x| x.subst(data)).transpose()
555    }
556}
557
558impl<T> Subst for Vec<T>
559where
560    T: Subst,
561{
562    fn subst(&self, data: &SubstData) -> Result<Vec<T>> {
563        self.iter()
564            .map(|x| x.subst(data))
565            .collect::<Result<Vec<_>>>()
566    }
567}
568
569impl<K, V> Subst for IndexMap<K, V>
570where
571    K: Clone + Eq + std::hash::Hash,
572    V: Subst,
573{
574    fn subst(&self, data: &SubstData) -> Result<IndexMap<K, V>> {
575        self.iter()
576            .map(|(k, v)| Ok((k.clone(), v.subst(data)?)))
577            .collect::<Result<IndexMap<K, V>>>()
578    }
579}
580
581#[cfg(test)]
582mod tests {
583    use super::*;
584
585    /// Test parsing of byte arrays.
586    #[test]
587    fn parse_byte_array() {
588        let byte_array = SubstValue::ByteArray(vec![0xde, 0xad, 0xbe, 0xef]);
589        // Size 0 means any size.
590        assert_eq!(
591            byte_array
592                .parse(&VariableType::ByteArray {
593                    size: SizeRange::ExactSize(0),
594                    tweak_msb: None
595                })
596                .unwrap(),
597            byte_array
598        );
599        assert_eq!(
600            byte_array
601                .parse(&VariableType::ByteArray {
602                    size: SizeRange::ExactSize(4),
603                    tweak_msb: None
604                })
605                .unwrap(),
606            byte_array
607        );
608        assert!(
609            byte_array
610                .parse(&VariableType::ByteArray {
611                    size: SizeRange::ExactSize(3),
612                    tweak_msb: None
613                })
614                .is_err()
615        );
616        // Size must match exactly.
617        assert!(
618            byte_array
619                .parse(&VariableType::ByteArray {
620                    size: SizeRange::ExactSize(5),
621                    tweak_msb: None
622                })
623                .is_err()
624        );
625
626        // Strings are interpreted as hexstrings.
627        let byte_array_str = SubstValue::String("deadbeef".into());
628        // Size 0 means any size.
629        assert_eq!(
630            byte_array_str
631                .parse(&VariableType::ByteArray {
632                    size: SizeRange::ExactSize(0),
633                    tweak_msb: None
634                })
635                .unwrap(),
636            byte_array
637        );
638        assert_eq!(
639            byte_array_str
640                .parse(&VariableType::ByteArray {
641                    size: SizeRange::ExactSize(4),
642                    tweak_msb: None
643                })
644                .unwrap(),
645            byte_array
646        );
647        assert!(
648            byte_array_str
649                .parse(&VariableType::ByteArray {
650                    size: SizeRange::ExactSize(3),
651                    tweak_msb: None
652                })
653                .is_err()
654        );
655        // Size must match exactly.
656        assert!(
657            byte_array_str
658                .parse(&VariableType::ByteArray {
659                    size: SizeRange::ExactSize(5),
660                    tweak_msb: None
661                })
662                .is_err()
663        );
664    }
665
666    /// Test parsing of integers.
667    #[test]
668    fn parse_integers() {
669        // Big-endian integer.
670        let byte_array = SubstValue::ByteArray(vec![0x3f, 0x2e, 0x1d, 0x0c]);
671        // Strings: hexdecimal and decimal.
672        let byte_array_str_hex = SubstValue::String("0x3f2e1d0c".to_string());
673        let byte_array_str_dec = SubstValue::String("1059986700".to_string());
674        // Fixed-size integer.
675        let byte_array_int = SubstValue::Uint32(0x3f2e1d0c);
676
677        for val in [
678            &byte_array,
679            &byte_array_int,
680            &byte_array_str_hex,
681            &byte_array_str_dec,
682        ] {
683            // Size 0 means any size.
684            assert_eq!(
685                val.parse(&VariableType::Integer {
686                    size: SizeRange::ExactSize(0),
687                })
688                .unwrap(),
689                byte_array
690            );
691            assert_eq!(
692                val.parse(&VariableType::Integer {
693                    size: SizeRange::ExactSize(4),
694                })
695                .unwrap(),
696                byte_array
697            );
698            // Size does not need not match exactly.
699            assert_eq!(
700                val.parse(&VariableType::Integer {
701                    size: SizeRange::ExactSize(5),
702                })
703                .unwrap(),
704                byte_array
705            );
706            // Too small size in an error.
707            assert!(
708                val.parse(&VariableType::Integer {
709                    size: SizeRange::ExactSize(3),
710                })
711                .is_err()
712            );
713        }
714    }
715
716    /// Test parsing of strings.
717    #[test]
718    fn parse_strings() {
719        // Big-endian integer.
720        let s = SubstValue::String("OpenTitan".into());
721        // Size 0 means any size.
722        assert_eq!(
723            s.parse(&VariableType::String {
724                size: SizeRange::ExactSize(0),
725            })
726            .unwrap(),
727            s
728        );
729        assert_eq!(
730            s.parse(&VariableType::String {
731                size: SizeRange::ExactSize(9),
732            })
733            .unwrap(),
734            s
735        );
736        // A shorting string than specified is acceptable.
737        assert_eq!(
738            s.parse(&VariableType::String {
739                size: SizeRange::ExactSize(10),
740            })
741            .unwrap(),
742            s
743        );
744        assert!(
745            s.parse(&VariableType::String {
746                size: SizeRange::ExactSize(8),
747            })
748            .is_err()
749        );
750    }
751
752    /// Test parsing of booleans.
753    #[test]
754    fn parse_booleans() {
755        for b in [false, true] {
756            let b_val = SubstValue::Boolean(b);
757            assert_eq!(b_val.parse(&VariableType::Boolean).unwrap(), b_val);
758            let b_str = SubstValue::String(format!("{b}"));
759            assert_eq!(b_str.parse(&VariableType::Boolean).unwrap(), b_val);
760        }
761    }
762
763    /// Test conversion to byte arrays.
764    #[test]
765    fn convert_to_byte_array() {
766        // Parsing is already tested so we only need to test conversion *after* parsing.
767
768        // The only valid conversion from a byte array to a byte array is None.
769        let byte_array = vec![0xde, 0xad, 0xbe, 0xef, 0x13, 0x24, 0x35];
770        let array_val = SubstValue::ByteArray(byte_array.clone());
771        let conv_none: Result<Vec<u8>> = array_val.convert(&None);
772        let conv_lowercase: Result<Vec<u8>> = array_val.convert(&Some(Conversion::LowercaseHex));
773        let conv_bigendian: Result<Vec<u8>> = array_val.convert(&Some(Conversion::BigEndian));
774
775        assert_eq!(conv_none.unwrap(), byte_array);
776        assert!(conv_lowercase.is_err());
777        assert!(conv_bigendian.is_err());
778        // There are no valid conversions from any other type to a byte array.
779    }
780
781    /// Test conversion to strings.
782    #[test]
783    fn convert_to_string() {
784        // The only valid conversion from a string to a string is None.
785        let s = "OpenTitan".to_string();
786        let s_val = SubstValue::String(s.clone());
787        let conv_none: Result<String> = s_val.convert(&None);
788        let conv_lowercase: Result<String> = s_val.convert(&Some(Conversion::LowercaseHex));
789        let conv_bigendian: Result<String> = s_val.convert(&Some(Conversion::BigEndian));
790
791        assert_eq!(conv_none.unwrap(), s);
792        assert!(conv_lowercase.is_err());
793        assert!(conv_bigendian.is_err());
794
795        // It is possible to convert a byte array to string (which gives the corresponding hexstring).
796        // Explicitly check that the hexstring produces two characters per byte with '0' padding.
797        let byte_array = vec![0x0e, 0xad, 0xbe, 0xef, 0x13, 0x24, 0x35];
798        let array_hexstr = "0eadbeef132435".to_string();
799        let array_val = SubstValue::ByteArray(byte_array);
800        let conv_none: Result<String> = array_val.convert(&None);
801        let conv_lowercase: Result<String> = array_val.convert(&Some(Conversion::LowercaseHex));
802        let conv_bigendian: Result<String> = array_val.convert(&Some(Conversion::BigEndian));
803
804        assert!(conv_none.is_err());
805        assert_eq!(conv_lowercase.unwrap(), array_hexstr);
806        assert!(conv_bigendian.is_err());
807    }
808
809    /// Test conversion to integers.
810    #[test]
811    fn convert_to_integer() {
812        // Parsing is already tested so we only need to test conversion *after* parsing.
813
814        // The only valid conversion to an integer is from a byte array using either None or big-endian.
815        let byte_array = vec![0xde, 0xad, 0xbe, 0xef, 0x13, 0x24, 0x35];
816        let array_val = SubstValue::ByteArray(byte_array.clone());
817        let array_int = BigUint::from_bytes_be(&byte_array);
818        let conv_none: Result<BigUint> = array_val.convert(&None);
819        let conv_lowercase: Result<BigUint> = array_val.convert(&Some(Conversion::LowercaseHex));
820        let conv_bigendian: Result<BigUint> = array_val.convert(&Some(Conversion::BigEndian));
821
822        assert_eq!(conv_none.unwrap(), array_int);
823        assert!(conv_lowercase.is_err());
824        assert_eq!(conv_bigendian.unwrap(), array_int);
825    }
826
827    /// Test conversion to booleans.
828    #[test]
829    fn convert_to_boolean() {
830        // The only valid conversion from a boolean to a boolean is None.
831        let b = false;
832        let b_val = SubstValue::Boolean(b);
833        let conv_none: Result<bool> = b_val.convert(&None);
834        let conv_lowercase: Result<bool> = b_val.convert(&Some(Conversion::LowercaseHex));
835        let conv_bigendian: Result<bool> = b_val.convert(&Some(Conversion::BigEndian));
836
837        assert_eq!(conv_none.unwrap(), b);
838        assert!(conv_lowercase.is_err());
839        assert!(conv_bigendian.is_err());
840    }
841}