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, 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::ValueTooLarge` if the command has unknown state
17pub fn tpm_build_command<C>(
18    command: &C,
19    tag: TpmSt,
20    handles: Option<&[u32]>,
21    sessions: &[TpmsAuthCommand],
22    writer: &mut crate::TpmWriter,
23) -> TpmResult<()>
24where
25    C: TpmHeader,
26{
27    match tag {
28        TpmSt::NoSessions => {
29            if !C::NO_SESSIONS {
30                return Err(TpmErrorKind::InvalidTag {
31                    type_name: "TpmSt",
32                    expected: TpmSt::Sessions as u16,
33                    got: tag as u16,
34                });
35            }
36        }
37        TpmSt::Sessions => {
38            if !C::WITH_SESSIONS {
39                return Err(TpmErrorKind::InvalidTag {
40                    type_name: "TpmSt",
41                    expected: TpmSt::NoSessions as u16,
42                    got: tag as u16,
43                });
44            }
45        }
46        _ => {
47            return Err(TpmErrorKind::InvalidValue);
48        }
49    }
50
51    let handles = handles.unwrap_or(&[]);
52    if handles.len() != C::HANDLES {
53        return Err(TpmErrorKind::InternalError);
54    }
55
56    let handle_area_len = core::mem::size_of_val(handles);
57    let parameters_len = command.len();
58
59    let auth_area_len = if tag == TpmSt::Sessions {
60        let sessions_len: usize = sessions.iter().map(TpmSized::len).sum();
61        size_of::<u32>() + sessions_len
62    } else {
63        0
64    };
65
66    let total_body_len = handle_area_len + auth_area_len + parameters_len;
67    let command_size =
68        u32::try_from(TPM_HEADER_SIZE + total_body_len).map_err(|_| TpmErrorKind::ValueTooLarge)?;
69
70    (tag as u16).build(writer)?;
71    command_size.build(writer)?;
72    (C::COMMAND as u32).build(writer)?;
73
74    for handle in handles {
75        handle.build(writer)?;
76    }
77
78    if tag == TpmSt::Sessions {
79        let sessions_len_u32 = u32::try_from(auth_area_len - size_of::<u32>())
80            .map_err(|_| TpmErrorKind::ValueTooLarge)?;
81        sessions_len_u32.build(writer)?;
82        for s in sessions {
83            s.build(writer)?;
84        }
85    }
86
87    command.build(writer)
88}
89
90/// Builds a TPM response.
91///
92/// # Errors
93///
94/// * `TpmErrorKind::ValueTooLarge` if the response has unknown state
95pub fn tpm_build_response<R>(
96    response: &R,
97    sessions: &[TpmsAuthResponse],
98    rc: TpmRc,
99    writer: &mut crate::TpmWriter,
100) -> TpmResult<()>
101where
102    R: TpmHeader,
103{
104    let tag = if !rc.is_error() && R::WITH_SESSIONS && !sessions.is_empty() {
105        TpmSt::Sessions
106    } else {
107        TpmSt::NoSessions
108    };
109
110    if rc.is_error() {
111        (TpmSt::NoSessions as u16).build(writer)?;
112        u32::try_from(TPM_HEADER_SIZE)?.build(writer)?;
113        rc.value().build(writer)?;
114        return Ok(());
115    }
116
117    let body_len = response.len();
118    let sessions_len: usize = sessions.iter().map(TpmSized::len).sum();
119    let total_body_len = body_len + sessions_len;
120    let response_size =
121        u32::try_from(TPM_HEADER_SIZE + total_body_len).map_err(|_| TpmErrorKind::ValueTooLarge)?;
122
123    (tag as u16).build(writer)?;
124    response_size.build(writer)?;
125    rc.value().build(writer)?;
126
127    response.build(writer)?;
128
129    if tag == TpmSt::Sessions {
130        for s in sessions {
131            s.build(writer)?;
132        }
133    }
134    Ok(())
135}