cli/
cli.rs

1// SPDX-License-Identifier: GPL-3-0-or-later
2// Copyright (c) 2025 Opinsys Oy
3// Copyright (c) 2024-2025 Jarkko Sakkinen
4
5#![allow(clippy::doc_markdown)]
6
7use crate::{
8    auth::Auth,
9    command::{
10        Algorithm, Cache, Certificate, CommandError, Convert, Create, CreatePrimary, Delete, Evict,
11        Load, Memory, PcrEvent, Policy, ResetLock, ReturnCode, Unseal,
12    },
13    job::Job,
14};
15use clap::{builder::styling::Styles, Parser, Subcommand};
16use std::path::PathBuf;
17use strum::{Display, EnumString};
18use tpm2_protocol::data::TpmRh;
19
20const STYLES: Styles = Styles::styled()
21    .header(clap::builder::styling::Style::new().bold())
22    .usage(clap::builder::styling::Style::new().bold())
23    .literal(clap::builder::styling::Style::new())
24    .placeholder(clap::builder::styling::Style::new());
25
26/// A subcommand of the main CLI application.
27pub trait SubCommand {
28    /// Runs a command.
29    ///
30    /// # Errors
31    ///
32    /// Returns an error if the execution fails.
33    fn run(&self, job: &mut Job) -> Result<(), CommandError>;
34
35    /// Returns `true` if the command can be run without a TPM device.
36    #[must_use]
37    fn is_local(&self) -> bool {
38        false
39    }
40}
41
42#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Display, EnumString)]
43#[strum(serialize_all = "kebab-case")]
44pub enum LogFormat {
45    #[default]
46    Plain,
47    Pretty,
48}
49
50/// TPM 2.0 shell
51#[derive(Parser, Debug)]
52#[command(version, about, styles = STYLES)]
53pub struct TopLevel {
54    /// Device path
55    #[arg(short = 'd', long, default_value = "/dev/tpmrm0")]
56    pub device: PathBuf,
57
58    /// Log format: 'plain' or 'pretty'
59    #[arg(long, default_value_t = LogFormat::default(), value_parser = clap::value_parser!(LogFormat))]
60    pub log_format: LogFormat,
61
62    /// Authentication: 'password:<hex>', 'policy:<hex>' or 'vtpm:<handle>'
63    #[arg(short = 'A', long = "auth", global = true, value_delimiter = ',')]
64    pub auth: Vec<Auth>,
65
66    #[command(subcommand)]
67    pub command: Command,
68}
69
70#[derive(Subcommand, Debug)]
71pub enum Command {
72    Algorithm(Algorithm),
73    Cache(Cache),
74    Certificate(Certificate),
75    Convert(Convert),
76    Create(Create),
77    CreatePrimary(CreatePrimary),
78    Delete(Delete),
79    Evict(Evict),
80    Load(Load),
81    Memory(Memory),
82    PcrEvent(PcrEvent),
83    Policy(Policy),
84    ReturnCode(ReturnCode),
85    ResetLock(ResetLock),
86    Unseal(Unseal),
87}
88
89impl Command {
90    fn as_subcommand(&self) -> &dyn SubCommand {
91        match self {
92            Self::Algorithm(cmd) => cmd,
93            Self::Cache(cmd) => cmd,
94            Self::Certificate(cmd) => cmd,
95            Self::Convert(cmd) => cmd,
96            Self::Create(cmd) => cmd,
97            Self::CreatePrimary(cmd) => cmd,
98            Self::Delete(cmd) => cmd,
99            Self::Evict(cmd) => cmd,
100            Self::Load(cmd) => cmd,
101            Self::Memory(cmd) => cmd,
102            Self::PcrEvent(cmd) => cmd,
103            Self::Policy(cmd) => cmd,
104            Self::ReturnCode(cmd) => cmd,
105            Self::ResetLock(cmd) => cmd,
106            Self::Unseal(cmd) => cmd,
107        }
108    }
109}
110
111impl SubCommand for Command {
112    fn run(&self, job: &mut Job) -> Result<(), CommandError> {
113        self.as_subcommand().run(job)
114    }
115
116    fn is_local(&self) -> bool {
117        self.as_subcommand().is_local()
118    }
119}
120
121#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Display, EnumString)]
122#[strum(serialize_all = "kebab-case")]
123pub enum Hierarchy {
124    #[default]
125    Owner,
126    Platform,
127    Endorsement,
128}
129
130impl From<Hierarchy> for TpmRh {
131    fn from(h: Hierarchy) -> Self {
132        match h {
133            Hierarchy::Owner => TpmRh::Owner,
134            Hierarchy::Platform => TpmRh::Platform,
135            Hierarchy::Endorsement => TpmRh::Endorsement,
136        }
137    }
138}