cli/command/
unseal.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    device::{self, Auth, Device, DeviceError},
9    uri::Uri,
10};
11use argh::FromArgs;
12use std::{cell::RefCell, rc::Rc, str::FromStr};
13use tpm2_protocol::{data::TpmCc, data::TpmSe, message::TpmUnsealCommand};
14
15/// Retrieves data from a sealed data object.
16#[derive(FromArgs, Debug)]
17#[argh(
18    subcommand,
19    name = "unseal",
20    note = "Retrieves data from a sealed data object."
21)]
22pub struct Unseal {
23    /// input: 'tpm://<handle>' or 'key://<grip>'
24    #[argh(positional)]
25    pub input: String,
26
27    /// auth for the sealed object: 'password://<hex>' or 'session://<handle>'
28    /// Uses TPM2SH_AUTH environment variable if not set.
29    #[argh(option, arg_name = "auth", short = 'a')]
30    pub auth: Option<String>,
31
32    /// hmac auth: 'password://<hex>' or 'session://<handle>'
33    /// Uses TPM2SH_HMAC_AUTH environment variable if not set.
34    #[argh(option, arg_name = "auth", short = 'm', long = "hmac-auth")]
35    pub hmac_auth: Option<String>,
36}
37
38impl SubCommand for Unseal {
39    /// `unseal` requires authorization for the sealed object itself.
40    ///
41    /// 1.  The sealed object's context is loaded into the TPM if it is not
42    ///     already active. This step does not require authorization.
43    /// 2.  The data is retrieved using `TPM2_Unseal`. This command must be
44    ///     authorized by the sealed object's own authorization policy. The
45    ///     session provided via `--auth` is used for this step.
46    fn run(
47        &self,
48        device: Option<Rc<RefCell<Device>>>,
49        context: &mut ContextCache,
50        _plain: bool,
51    ) -> Result<(), CommandError> {
52        let auth = match (self.auth.as_ref(), self.hmac_auth.as_ref()) {
53            (Some(_), Some(_)) => {
54                return Err(CommandError::InvalidInput(
55                    "Cannot use --auth and --hmac-auth at the same time".to_string(),
56                ));
57            }
58            (Some(auth_str), None) => get_auth(
59                Some(auth_str),
60                "TPM2SH_AUTH",
61                &context.session_map,
62                &[TpmSe::Policy],
63            )?,
64            (None, Some(hmac_auth_str)) => get_auth(
65                Some(hmac_auth_str),
66                "TPM2SH_HMAC_AUTH",
67                &context.session_map,
68                &[TpmSe::Hmac],
69            )?,
70            (None, None) => {
71                let auth = get_auth(None, "TPM2SH_AUTH", &context.session_map, &[TpmSe::Policy])?;
72                if matches!(&auth, Auth::Password(p) if p.is_empty()) {
73                    get_auth(
74                        None,
75                        "TPM2SH_HMAC_AUTH",
76                        &context.session_map,
77                        &[TpmSe::Hmac],
78                    )?
79                } else {
80                    auth
81                }
82            }
83        };
84        device::with_device(device, |device| {
85            let input_uri = Uri::from_str(&self.input)?;
86
87            if matches!(input_uri, Uri::Path(_) | Uri::Password(_)) {
88                return Err(CommandError::InvalidInput("{input_uri}".to_string()));
89            }
90
91            let item_handle = context.load_context(device, &input_uri)?;
92
93            let unseal_cmd = TpmUnsealCommand {
94                item_handle: item_handle.0.into(),
95            };
96            let unseal_handles = [item_handle.0];
97            let auths = &[auth];
98
99            let (unseal_resp, _) = context.execute(device, &unseal_cmd, &unseal_handles, auths)?;
100
101            let out_data = unseal_resp
102                .Unseal()
103                .map_err(|_| DeviceError::ResponseMismatch(TpmCc::Unseal))?
104                .out_data;
105
106            context.write_data(None, &out_data)?;
107
108            Ok(())
109        })
110    }
111}