cli/
convert.rs

1// SPDX-License-Identifier: GPL-3-0-or-later
2// Copyright (c) 2025 Opinsys Oy
3// Copyright (c) 2024-2025 Jarkko Sakkinen
4
5use crate::{
6    command::CommandError,
7    context::ContextCache,
8    key::{Alg, KeyError, TpmKey},
9    uri::Uri,
10};
11use std::io::{self, Read};
12use tpm2_protocol::{
13    constant::TPM_MAX_COMMAND_SIZE, data::TpmRc, TpmBuild, TpmErrorKind, TpmHandle, TpmWriter,
14};
15
16/// Parses a 16 character hex string with an optional `0x` prefix into
17/// `TpmHandle`.
18///
19/// # Errors
20///
21/// Returns `String` with `ParseIntError` converted to string.
22pub fn from_str_to_handle(input: &str) -> Result<TpmHandle, String> {
23    let input = match input.strip_prefix("0x") {
24        Some(input) => input,
25        None => input,
26    };
27    u32::from_str_radix(input, 16)
28        .map(TpmHandle)
29        .map_err(|e| e.to_string())
30}
31
32/// A helper to build a `TpmBuild` type into a `Vec<u8>`.
33///
34/// # Errors
35///
36/// Returns a `TpmErrorKind` if the object cannot be serialized into the buffer.
37pub fn from_tpm_object_to_vec<T: TpmBuild>(obj: &T) -> Result<Vec<u8>, TpmErrorKind> {
38    let mut buf = vec![0u8; TPM_MAX_COMMAND_SIZE];
39    let len = {
40        let mut writer = TpmWriter::new(&mut buf);
41        obj.build(&mut writer)?;
42        writer.len()
43    };
44    buf.truncate(len);
45    Ok(buf)
46}
47
48/// Reads data from a file path or from stdin if the path is not provided.
49///
50/// # Errors
51///
52/// Returns a `std::io::Error` on failure.
53pub fn from_input_to_bytes(input: Option<&Uri>) -> io::Result<Vec<u8>> {
54    let mut input_bytes = Vec::new();
55    match input {
56        Some(Uri::Path(path)) => {
57            input_bytes = std::fs::read(path)?;
58        }
59        None => {
60            io::stdin().read_to_end(&mut input_bytes)?;
61        }
62        Some(uri) => {
63            return Err(io::Error::new(
64                io::ErrorKind::InvalidInput,
65                format!("input must be a file path, but got '{uri}'"),
66            ));
67        }
68    }
69    Ok(input_bytes)
70}
71
72/// Handles the output logic for a command that produces a `TpmKey`.
73///
74/// This function will either save it to a file or print it to stdout as PEM,
75/// based on the provided output string.
76///
77/// # Errors
78///
79/// Returns `CommandError` on failure.
80pub fn from_tpm_key_to_output(
81    context: &mut ContextCache,
82    tpm_key: &TpmKey,
83    output: Option<&Uri>,
84) -> Result<(), CommandError> {
85    if let Some(output_uri) = output {
86        if !matches!(output_uri, Uri::Path(_)) {
87            return Err(CommandError::InvalidOutput(format!(
88                "output must be a file path, but got '{output_uri}'"
89            )));
90        }
91        context.write_key_data(Some(output_uri), tpm_key)?;
92    } else {
93        context.write_key_data(None, tpm_key)?;
94    }
95    Ok(())
96}
97
98/// Parses a string into a `TpmRc`.
99///
100/// # Errors
101///
102/// Returns a `String` error if parsing fails.
103pub fn from_str_to_tpm_rc(s: &str) -> Result<TpmRc, String> {
104    let s_no_prefix = s.strip_prefix("0x").unwrap_or(s);
105    let raw_rc = u32::from_str_radix(s_no_prefix, 16)
106        .map_err(|e| format!("Failed to parse hex u32: {e}"))?;
107    TpmRc::try_from(raw_rc).map_err(|e| format!("Invalid TPM RC value '{s}': {e}"))
108}
109
110/// Parses a string into an `Alg` for a sealed object.
111///
112/// # Errors
113///
114/// Returns a `String` error if parsing fails.
115pub fn from_str_to_alg(s: &str) -> Result<Alg, String> {
116    Alg::new_keyedhash(s).map_err(|e: KeyError| e.to_string())
117}