cli/command/
seal.rs

1// SPDX-License-Identifier: GPL-3-0-or-later
2// Copyright (c) 2025 Opinsys Oy
3
4use super::CommandError;
5use crate::{
6    cli::{get_auth, SubCommand},
7    context::ContextCache,
8    convert::{from_input_to_bytes, from_str_to_alg, from_tpm_key_to_output},
9    device::{with_device, Device},
10    key::{Alg, TpmKey, TpmKeyTemplate, OID_SEALED_DATA},
11    uri::Uri,
12};
13use argh::FromArgs;
14use std::{cell::RefCell, rc::Rc};
15use tpm2_protocol::data::{Tpm2bSensitiveData, TpmSe};
16
17/// Creates a sealed data object.
18#[derive(FromArgs, Debug, Clone)]
19#[argh(subcommand, name = "seal", note = "Creates a sealed data object.")]
20pub struct Seal {
21    /// parent: 'tpm://<handle>', or 'key://<name grip>'
22    #[argh(positional)]
23    pub parent: Uri,
24
25    /// name algorithm
26    #[argh(positional, from_str_fn(from_str_to_alg))]
27    pub algorithm: Alg,
28
29    /// policy digest
30    #[argh(option)]
31    pub policy: Option<String>,
32
33    /// output: TPMKey file
34    #[argh(option, short = 'o')]
35    pub output: Option<Uri>,
36
37    /// input: data file
38    #[argh(option, short = 'i')]
39    pub input: Option<Uri>,
40
41    /// parent auth: 'password://<hex>' or 'session://<handle>'
42    /// Uses TPM2SH_PARENT_AUTH environment variable if not set.
43    #[argh(option, arg_name = "auth", short = 'p')]
44    pub parent_auth: Option<String>,
45
46    /// auth for the sealed object: 'password://<hex>' or 'session://<handle>'
47    /// Uses TPM2SH_AUTH environment variable if not set.
48    #[argh(option, arg_name = "auth", short = 'a')]
49    pub auth: Option<String>,
50
51    /// hmac auth: 'password://<hex>' or 'session://<handle>'
52    /// Uses TPM2SH_HMAC_AUTH environment variable if not set.
53    #[argh(option, arg_name = "auth", short = 'm', long = "hmac-auth")]
54    pub hmac_auth: Option<String>,
55}
56
57impl SubCommand for Seal {
58    fn run(
59        &self,
60        device: Option<Rc<RefCell<Device>>>,
61        context: &mut ContextCache,
62        _plain: bool,
63    ) -> Result<(), CommandError> {
64        let parent_auth = get_auth(
65            self.parent_auth.as_ref(),
66            "TPM2SH_PARENT_AUTH",
67            &context.session_map,
68            &[TpmSe::Policy],
69        )?;
70        let auth = get_auth(self.auth.as_ref(), "TPM2SH_AUTH", &context.session_map, &[])?;
71        with_device(device, |device| {
72            let parent_handle = context.load_parent(device, &self.parent)?;
73
74            let input_bytes = from_input_to_bytes(self.input.as_ref())?;
75
76            if input_bytes.is_empty() {
77                return Err(CommandError::InvalidInput(
78                    "Cannot seal empty data; please provide data via stdin or an input file."
79                        .to_string(),
80                ));
81            }
82
83            let data_to_seal = Tpm2bSensitiveData::try_from(input_bytes.as_slice())?;
84
85            let template = TpmKeyTemplate {
86                alg_desc: &self.algorithm,
87                policy: self.policy.as_ref(),
88                sensitive_data: data_to_seal,
89                key_type_oid: OID_SEALED_DATA,
90            };
91
92            let tpm_key = TpmKey::new(
93                device,
94                context,
95                &[parent_auth],
96                &auth,
97                parent_handle,
98                &template,
99            )?;
100
101            from_tpm_key_to_output(context, &tpm_key, self.output.as_ref())
102        })
103    }
104}