tpm2_protocol/message/
build.rs

1// SPDX-License-Identifier: MIT OR Apache-2.0
2// Copyright (c) 2025 Opinsys Oy
3// Copyright (c) 2024-2025 Jarkko Sakkinen
4
5use crate::{
6    data::{TpmRc, TpmSt, TpmsAuthCommand, TpmsAuthResponse},
7    message::{TpmHeader, TpmHeaderCommand, TPM_HEADER_SIZE},
8    TpmBuild, TpmErrorKind, TpmResult, TpmSized,
9};
10use core::mem::size_of;
11
12/// Builds a TPM command into a writer and returns the total bytes written.
13///
14/// # Errors
15///
16/// * `TpmErrorKind::ParseCapacity` if the command has unknown state
17pub fn tpm_build_command<C>(
18    command: &C,
19    tag: TpmSt,
20    sessions: &[TpmsAuthCommand],
21    writer: &mut crate::TpmWriter,
22) -> TpmResult<()>
23where
24    C: TpmHeaderCommand,
25{
26    match tag {
27        TpmSt::NoSessions => {
28            if !C::NO_SESSIONS {
29                return Err(TpmErrorKind::InvalidTag {
30                    type_name: "TpmSt",
31                    expected: TpmSt::Sessions as u16,
32                    got: tag as u16,
33                });
34            }
35        }
36        TpmSt::Sessions => {
37            if !C::WITH_SESSIONS {
38                return Err(TpmErrorKind::InvalidTag {
39                    type_name: "TpmSt",
40                    expected: TpmSt::NoSessions as u16,
41                    got: tag as u16,
42                });
43            }
44        }
45        _ => {
46            return Err(TpmErrorKind::InvalidValue);
47        }
48    }
49
50    let handle_area_size = C::HANDLES * size_of::<u32>();
51    let param_area_size = command.len() - handle_area_size;
52    let auth_area_size = if tag == TpmSt::Sessions {
53        let sessions_len: usize = sessions.iter().map(TpmSized::len).sum();
54        size_of::<u32>() + sessions_len
55    } else {
56        0
57    };
58
59    let total_body_len = handle_area_size + auth_area_size + param_area_size;
60    let command_size =
61        u32::try_from(TPM_HEADER_SIZE + total_body_len).map_err(|_| TpmErrorKind::ParseCapacity)?;
62
63    (tag as u16).build(writer)?;
64    command_size.build(writer)?;
65    (C::COMMAND as u32).build(writer)?;
66
67    command.build_handles(writer)?;
68
69    if tag == TpmSt::Sessions {
70        let sessions_len_u32 = u32::try_from(auth_area_size - size_of::<u32>())
71            .map_err(|_| TpmErrorKind::ParseCapacity)?;
72        sessions_len_u32.build(writer)?;
73        for s in sessions {
74            s.build(writer)?;
75        }
76    }
77
78    command.build_parameters(writer)
79}
80
81/// Builds a TPM response.
82///
83/// # Errors
84///
85/// * `TpmErrorKind::ParseCapacity` if the response has unknown state
86pub fn tpm_build_response<R>(
87    response: &R,
88    sessions: &[TpmsAuthResponse],
89    rc: TpmRc,
90    writer: &mut crate::TpmWriter,
91) -> TpmResult<()>
92where
93    R: TpmHeader,
94{
95    let tag = if !rc.is_error() && R::WITH_SESSIONS && !sessions.is_empty() {
96        TpmSt::Sessions
97    } else {
98        TpmSt::NoSessions
99    };
100
101    if rc.is_error() {
102        (TpmSt::NoSessions as u16).build(writer)?;
103        u32::try_from(TPM_HEADER_SIZE)?.build(writer)?;
104        rc.value().build(writer)?;
105        return Ok(());
106    }
107
108    let body_len = response.len();
109    let sessions_len: usize = sessions.iter().map(TpmSized::len).sum();
110    let total_body_len = body_len + sessions_len;
111    let response_size =
112        u32::try_from(TPM_HEADER_SIZE + total_body_len).map_err(|_| TpmErrorKind::ParseCapacity)?;
113
114    (tag as u16).build(writer)?;
115    response_size.build(writer)?;
116    rc.value().build(writer)?;
117
118    response.build(writer)?;
119
120    if tag == TpmSt::Sessions {
121        for s in sessions {
122            s.build(writer)?;
123        }
124    }
125    Ok(())
126}