cli/
handle.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 std::{convert::TryFrom, str::FromStr};
6use thiserror::Error;
7use tpm2_protocol::data::TpmHt;
8
9#[derive(Debug, Error)]
10pub enum HandleError {
11    #[error("invalid handle")]
12    InvalidHandle,
13}
14
15/// Handle types.
16#[derive(Debug, Clone, Copy, PartialEq, Eq)]
17pub enum HandleClass {
18    Tpm,
19    Vtpm,
20}
21
22/// TPM and vTPM handles.
23#[derive(Debug, Clone, Copy, PartialEq, Eq)]
24pub struct Handle(pub (HandleClass, u32));
25
26impl Handle {
27    /// Returns class of the handle.
28    #[must_use]
29    pub fn class(&self) -> HandleClass {
30        self.0 .0
31    }
32
33    /// Returns value of the handle.
34    #[must_use]
35    pub fn value(&self) -> u32 {
36        self.0 .1
37    }
38}
39
40impl std::fmt::Display for Handle {
41    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
42        let value = self.value();
43        match self.class() {
44            HandleClass::Tpm => write!(f, "tpm:{value:08x}"),
45            HandleClass::Vtpm => write!(f, "vtpm:{value:08x}"),
46        }
47    }
48}
49
50impl FromStr for Handle {
51    type Err = HandleError;
52
53    fn from_str(s: &str) -> Result<Self, Self::Err> {
54        let (scheme_str, value_str) = s.split_once(':').ok_or(HandleError::InvalidHandle)?;
55
56        let class = match scheme_str {
57            "tpm" => HandleClass::Tpm,
58            "vtpm" => HandleClass::Vtpm,
59            _ => return Err(HandleError::InvalidHandle),
60        };
61
62        let value = u32::from_str_radix(value_str, 16).map_err(|_| HandleError::InvalidHandle)?;
63
64        Ok(Handle((class, value)))
65    }
66}
67
68impl TryFrom<Handle> for TpmHt {
69    type Error = HandleError;
70
71    fn try_from(handle: Handle) -> Result<Self, Self::Error> {
72        let raw_handle = handle.value();
73        let ht_byte = (raw_handle >> 24) as u8;
74        TpmHt::try_from(ht_byte).map_err(|()| HandleError::InvalidHandle)
75    }
76}
77
78#[derive(Debug, Error, PartialEq, Eq)]
79pub enum HandlePatternError {
80    #[error("handle pattern is not a valid hex string")]
81    InvalidHexString,
82    #[error("handle pattern has less than eight characters")]
83    TooFewDigits,
84    #[error("handle pattern has more than one '*'")]
85    TooManyAsterisks,
86    #[error("handle pattern has more than eight characters")]
87    TooManyDigits,
88}
89
90/// Pattern matcher for a 32-bit unsigned value represented
91/// as a string of exactly eight hex digits.
92///
93/// Wildcards:
94///
95/// 1. `?` matches any digit and can be used in place of any digit.
96/// 2. `*` matches zero or more digits. Only one `*` is supported per pattern.
97#[derive(Debug, Clone, Copy, PartialEq, Eq)]
98pub struct HandlePattern(u32, u32);
99
100impl HandlePattern {
101    /// Compiles a query string into a `HandlePattern`.
102    ///
103    /// # Errors
104    ///
105    /// Returns `HandlePatternError` if the query string is invalid.
106    pub fn new(query: &str) -> Result<Self, HandlePatternError> {
107        if query == "*" {
108            return Ok(Self(0, 0));
109        }
110
111        let mut mask: u32 = 0;
112        let mut value: u32 = 0;
113
114        let (prefix, suffix) = if let Some((p, s)) = query.split_once('*') {
115            if s.contains('*') {
116                return Err(HandlePatternError::TooManyAsterisks);
117            }
118            if p.len() + s.len() > 8 {
119                return Err(HandlePatternError::TooManyDigits);
120            }
121            (p, s)
122        } else {
123            if query.len() < 8 {
124                return Err(HandlePatternError::TooFewDigits);
125            }
126            if query.len() > 8 {
127                return Err(HandlePatternError::TooManyDigits);
128            }
129            (query, "")
130        };
131
132        for (i, c) in prefix.chars().enumerate() {
133            let shift = (7 - i) * 4;
134            match c.to_digit(16) {
135                Some(v) => {
136                    mask |= 0xF << shift;
137                    value |= v << shift;
138                }
139                None if c == '?' => {}
140                None => return Err(HandlePatternError::InvalidHexString),
141            }
142        }
143
144        for (i, c) in suffix.chars().rev().enumerate() {
145            let shift = i * 4;
146            match c.to_digit(16) {
147                Some(v) => {
148                    mask |= 0xF << shift;
149                    value |= v << shift;
150                }
151                None if c == '?' => {}
152                None => return Err(HandlePatternError::InvalidHexString),
153            }
154        }
155
156        Ok(Self(mask, value))
157    }
158
159    /// Checks if a given handle matches the compiled pattern.
160    #[must_use]
161    pub fn matches(&self, handle: u32) -> bool {
162        if self.0 == 0 && self.1 == 0 {
163            return true;
164        }
165        (handle & self.0) == self.1
166    }
167}