cli/command/
load.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 super::CommandError;
6use crate::{
7    cli::{get_auth, SubCommand},
8    context::ContextCache,
9    convert::from_input_to_bytes,
10    device::{self, Auth, Device, DeviceError},
11    key::AnyKey,
12    uri::Uri,
13};
14use argh::FromArgs;
15use std::{cell::RefCell, rc::Rc};
16use tpm2_protocol::{
17    data::{Tpm2bName, Tpm2bPrivate, Tpm2bPublic, TpmCc, TpmSe},
18    message::TpmLoadCommand,
19    TpmHandle, TpmParse,
20};
21
22/// Loads a key under a parent and caches its context.
23#[derive(FromArgs, Debug)]
24#[argh(subcommand, name = "load")]
25pub struct Load {
26    /// parent: 'tpm://<handle>', or 'key://<name grip>'
27    #[argh(positional)]
28    pub parent: Uri,
29
30    /// input: PKCS#1, PKCS#8, SEC1 or TPMKey file
31    #[argh(positional)]
32    pub input: Option<Uri>,
33
34    /// parent auth: 'password://<hex>' or 'session://<handle>'
35    /// Uses TPM2SH_PARENT_AUTH environment variable if not set.
36    #[argh(option, arg_name = "auth", short = 'p')]
37    pub parent_auth: Option<String>,
38
39    /// auth: 'password://<hex>' or 'session://<handle>'
40    /// Uses TPM2SH_AUTH environment variable if not set.
41    #[argh(option, arg_name = "auth", short = 'a')]
42    pub auth: Option<String>,
43
44    /// hmac auth: 'password://<hex>' or 'session://<handle>'
45    /// Uses TPM2SH_HMAC_AUTH environment variable if not set.
46    #[argh(option, arg_name = "auth", short = 'm', long = "hmac-auth")]
47    pub hmac_auth: Option<String>,
48}
49
50impl SubCommand for Load {
51    fn run(
52        &self,
53        device: Option<Rc<RefCell<Device>>>,
54        context: &mut ContextCache,
55        _plain: bool,
56    ) -> Result<(), CommandError> {
57        let parent_auth = get_auth(
58            self.parent_auth.as_ref(),
59            "TPM2SH_PARENT_AUTH",
60            &context.session_map,
61            &[TpmSe::Policy],
62        )?;
63        device::with_device(device, |device| -> Result<(), CommandError> {
64            let parent_handle = context.load_parent(device, &self.parent)?;
65            let input_bytes = from_input_to_bytes(self.input.as_ref())?;
66
67            let (object_handle, name) =
68                Self::run_input(context, device, parent_handle, &input_bytes, &[parent_auth])?;
69
70            context.new_context(device, object_handle, &name)?;
71            Ok(())
72        })
73    }
74}
75
76impl Load {
77    fn run_input(
78        context: &mut ContextCache,
79        device: &mut Device,
80        parent_handle: TpmHandle,
81        input_bytes: &[u8],
82        auths: &[Auth],
83    ) -> Result<(TpmHandle, Tpm2bName), CommandError> {
84        let tpm_key = match AnyKey::try_from(input_bytes)? {
85            AnyKey::Tpm(key) => key,
86            AnyKey::External(_) => {
87                let imported_key = context.import_key(device, parent_handle, input_bytes, auths)?;
88                Box::new(imported_key)
89            }
90        };
91
92        let (in_public, _) = Tpm2bPublic::parse(&tpm_key.pub_key)?;
93        let (in_private, _) = Tpm2bPrivate::parse(&tpm_key.priv_key)?;
94
95        let load_cmd = TpmLoadCommand {
96            parent_handle: parent_handle.0.into(),
97            in_private,
98            in_public,
99        };
100        let handles = [parent_handle.0];
101
102        let (resp, _) = context.execute(device, &load_cmd, &handles, auths)?;
103
104        let resp = resp
105            .Load()
106            .map_err(|_| DeviceError::ResponseMismatch(TpmCc::Load))?;
107
108        device.add_name_to_cache(resp.object_handle.0, resp.name);
109        context.track(resp.object_handle)?;
110        Ok((resp.object_handle, resp.name))
111    }
112}