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    constant::TPM_HEADER_SIZE,
7    data::{TpmRc, TpmRcBase, TpmSt, TpmsAuthCommand, TpmsAuthResponse},
8    message::{TpmBodyBuild, TpmHeader},
9    TpmBuild, TpmError, TpmResult, TpmSized,
10};
11use core::{convert::TryFrom, mem::size_of};
12
13/// Builds a TPM command into a writer and returns the total bytes written.
14///
15/// # Errors
16///
17/// Returns `Err(TpmError)` on a build failure.
18pub fn tpm_build_command<C>(
19    command: &C,
20    tag: TpmSt,
21    sessions: &[TpmsAuthCommand],
22    writer: &mut crate::TpmWriter,
23) -> TpmResult<()>
24where
25    C: TpmHeader + TpmBodyBuild,
26{
27    if tag != TpmSt::NoSessions && tag != TpmSt::Sessions {
28        return Err(TpmError::MalformedData);
29    }
30
31    let handle_area_size = C::HANDLES * size_of::<u32>();
32    let param_area_size = command.len() - handle_area_size;
33    let auth_area_size = if tag == TpmSt::Sessions {
34        let sessions_len: usize = sessions.iter().map(TpmSized::len).sum();
35        size_of::<u32>() + sessions_len
36    } else {
37        0
38    };
39
40    let total_body_len = handle_area_size + auth_area_size + param_area_size;
41    let command_size =
42        u32::try_from(TPM_HEADER_SIZE + total_body_len).map_err(|_| TpmError::CapacityExceeded)?;
43
44    (tag as u16).build(writer)?;
45    command_size.build(writer)?;
46    (C::CC as u32).build(writer)?;
47
48    command.build_handles(writer)?;
49
50    if tag == TpmSt::Sessions {
51        let sessions_len = u32::try_from(auth_area_size - size_of::<u32>())
52            .map_err(|_| TpmError::CapacityExceeded)?;
53        sessions_len.build(writer)?;
54        for s in sessions {
55            s.build(writer)?;
56        }
57    }
58
59    command.build_parameters(writer)
60}
61
62/// Builds a TPM response.
63///
64/// # Errors
65///
66/// Returns `Err(TpmError)` on a build failure.
67pub fn tpm_build_response<R>(
68    response: &R,
69    sessions: &[TpmsAuthResponse],
70    rc: TpmRc,
71    writer: &mut crate::TpmWriter,
72) -> TpmResult<()>
73where
74    R: TpmHeader + TpmBodyBuild,
75{
76    if !matches!(rc, TpmRc::Fmt0(TpmRcBase::Success)) {
77        (TpmSt::NoSessions as u16).build(writer)?;
78        u32::try_from(TPM_HEADER_SIZE)?.build(writer)?;
79        rc.value().build(writer)?;
80        return Ok(());
81    }
82
83    let tag = if sessions.is_empty() {
84        TpmSt::NoSessions
85    } else {
86        TpmSt::Sessions
87    };
88
89    let handle_area_size = R::HANDLES * size_of::<u32>();
90    let param_area_size = response.len() - handle_area_size;
91    let sessions_len: usize = sessions.iter().map(TpmSized::len).sum();
92
93    let parameter_area_size_field_len = if tag == TpmSt::Sessions {
94        size_of::<u32>()
95    } else {
96        0
97    };
98
99    let total_body_len =
100        handle_area_size + parameter_area_size_field_len + param_area_size + sessions_len;
101
102    let response_size =
103        u32::try_from(TPM_HEADER_SIZE + total_body_len).map_err(|_| TpmError::CapacityExceeded)?;
104
105    (tag as u16).build(writer)?;
106    response_size.build(writer)?;
107    rc.value().build(writer)?;
108
109    response.build_handles(writer)?;
110
111    if tag == TpmSt::Sessions {
112        let params_len = u32::try_from(param_area_size).map_err(|_| TpmError::CapacityExceeded)?;
113        params_len.build(writer)?;
114    }
115
116    response.build_parameters(writer)?;
117
118    if tag == TpmSt::Sessions {
119        for s in sessions {
120            s.build(writer)?;
121        }
122    }
123    Ok(())
124}