tpm2_protocol/data/
tpm_rc.rs

1// SPDX-License-Identifier: MIT OR Apache-2.0
2// Copyright (c) 2025 Opinsys Oy
3// Copyright (c) 2024-2025 Jarkko Sakkinen
4
5#[allow(unused_imports)]
6use crate::{tpm_enum, TpmErrorKind, TpmParse};
7use core::{
8    convert::TryFrom,
9    fmt::{self, Debug, Display, Formatter},
10};
11
12pub const TPM_RC_VER1: u32 = 0x0100;
13pub const TPM_RC_FMT1: u32 = 0x0080;
14pub const TPM_RC_WARN: u32 = 0x0900;
15pub const TPM_RC_P_BIT: u32 = 1 << 6;
16pub const TPM_RC_N_SHIFT: u32 = 8;
17pub const TPM_RC_FMT1_ERROR_MASK: u32 = 0x003F;
18
19const MAX_HANDLE_INDEX: u8 = 7;
20const SESSION_INDEX_OFFSET: u8 = 8;
21
22#[derive(Debug, PartialEq, Eq, Copy, Clone)]
23pub enum TpmRcIndex {
24    Parameter(u8),
25    Handle(u8),
26    Session(u8),
27}
28
29impl Display for TpmRcIndex {
30    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
31        match self {
32            Self::Parameter(i) => write!(f, "parameter[{i}]"),
33            Self::Handle(i) => write!(f, "handle[{i}]"),
34            Self::Session(i) => write!(f, "session[{i}]"),
35        }
36    }
37}
38
39tpm_enum! {
40    #[derive(Debug, PartialEq, Eq, Copy, Clone)]
41    #[allow(clippy::upper_case_acronyms)]
42    pub enum TpmRcBase(u32) {
43        (Success, 0x0000, "TPM_RC_SUCCESS"),
44        (BadTag, 0x001E, "TPM_RC_BAD_TAG"),
45        (Initialize, TPM_RC_VER1, "TPM_RC_INITIALIZE"),
46        (Failure, TPM_RC_VER1 | 0x001, "TPM_RC_FAILURE"),
47        (AuthMissing, TPM_RC_VER1 | 0x025, "TPM_RC_AUTH_MISSING"),
48        (CommandSize, TPM_RC_VER1 | 0x042, "TPM_RC_COMMAND_SIZE"),
49        (Sensitive, TPM_RC_VER1 | 0x055, "TPM_RC_SENSITIVE"),
50        (Asymmetric, TPM_RC_FMT1 | 0x001, "TPM_RC_ASYMMETRIC"),
51        (Attributes, TPM_RC_FMT1 | 0x002, "TPM_RC_ATTRIBUTES"),
52        (Value, TPM_RC_FMT1 | 0x004, "TPM_RC_VALUE"),
53        (Handle, TPM_RC_FMT1 | 0x00B, "TPM_RC_HANDLE"),
54        (AuthFail, TPM_RC_FMT1 | 0x00E, "TPM_RC_AUTH_FAIL"),
55        (BadAuth, TPM_RC_FMT1 | 0x022, "TPM_RC_BAD_AUTH"),
56        (Curve, TPM_RC_FMT1 | 0x026, "TPM_RC_CURVE"),
57        (ContextGap, TPM_RC_WARN | 0x001, "TPM_RC_CONTEXT_GAP"),
58        (NvUnavailable, TPM_RC_WARN | 0x023, "TPM_RC_NV_UNAVAILABLE"),
59    }
60}
61
62/// Extracts the base response code from a raw `u32` value.
63fn get_base_code(value: u32) -> u32 {
64    if (value & TPM_RC_FMT1) != 0 {
65        TPM_RC_FMT1 | (value & TPM_RC_FMT1_ERROR_MASK)
66    } else {
67        value
68    }
69}
70
71#[must_use]
72#[derive(Debug, PartialEq, Eq, Copy, Clone)]
73pub struct TpmRc(u32);
74
75impl TpmRc {
76    /// Returns the base error code, stripping any handle, parameter, or session index.
77    ///
78    /// # Errors
79    ///
80    /// Returns a `TpmErrorKind::InvalidDiscriminant` if the response code does not correspond
81    /// to a known base error code.
82    pub fn base(self) -> Result<TpmRcBase, TpmErrorKind> {
83        let base_code = get_base_code(self.0);
84        TpmRcBase::try_from(base_code).map_err(|()| TpmErrorKind::InvalidDiscriminant {
85            type_name: "TpmRcBase",
86            value: u64::from(base_code),
87        })
88    }
89
90    #[must_use]
91    pub fn index(self) -> Option<TpmRcIndex> {
92        let value = self.0;
93        if (value & TPM_RC_FMT1) == 0 {
94            return None;
95        }
96        let is_parameter = (value & TPM_RC_P_BIT) != 0;
97        let n = ((value >> TPM_RC_N_SHIFT) & 0b1111) as u8;
98
99        match (is_parameter, n) {
100            (_, 0) => None,
101            (true, num) => Some(TpmRcIndex::Parameter(num)),
102            (false, num @ 1..=MAX_HANDLE_INDEX) => Some(TpmRcIndex::Handle(num)),
103            (false, num) => Some(TpmRcIndex::Session(num - SESSION_INDEX_OFFSET)),
104        }
105    }
106
107    #[must_use]
108    pub fn value(self) -> u32 {
109        self.0
110    }
111    #[must_use]
112    pub fn is_warning(self) -> bool {
113        (self.0 & TPM_RC_WARN) == TPM_RC_WARN
114    }
115    #[must_use]
116    pub fn is_error(self) -> bool {
117        !self.is_warning() && self.0 != 0
118    }
119}
120
121impl crate::TpmSized for TpmRc {
122    const SIZE: usize = core::mem::size_of::<u32>();
123    fn len(&self) -> usize {
124        Self::SIZE
125    }
126}
127
128impl crate::TpmBuild for TpmRc {
129    fn build(&self, writer: &mut crate::TpmWriter) -> crate::TpmResult<()> {
130        self.0.build(writer)
131    }
132}
133
134impl crate::TpmParse for TpmRc {
135    fn parse(buf: &[u8]) -> crate::TpmResult<(Self, &[u8])> {
136        let (val, remainder) = u32::parse(buf)?;
137        let rc = Self::try_from(val)?;
138        Ok((rc, remainder))
139    }
140}
141
142impl TryFrom<u32> for TpmRc {
143    type Error = TpmErrorKind;
144    fn try_from(value: u32) -> Result<Self, Self::Error> {
145        let base_code = get_base_code(value);
146        TpmRcBase::try_from(base_code).map_err(|()| TpmErrorKind::InvalidDiscriminant {
147            type_name: "TpmRcBase",
148            value: u64::from(base_code),
149        })?;
150        Ok(Self(value))
151    }
152}
153
154impl From<TpmRcBase> for TpmRc {
155    fn from(value: TpmRcBase) -> Self {
156        Self(value as u32)
157    }
158}
159
160impl Display for TpmRc {
161    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
162        if let Ok(base) = self.base() {
163            if let Some(index) = self.index() {
164                write!(f, "[{base}, {index}]")
165            } else {
166                write!(f, "{base}")
167            }
168        } else {
169            write!(f, "TPM_RC_UNKNOWN(0x{:08X})", self.0)
170        }
171    }
172}