tpm2_protocol/message/
parse.rs1use super::{
6 TpmAuthCommands, TpmAuthResponses, TpmCommandBody, TpmHandles, TpmResponseBody,
7 TPM_DISPATCH_TABLE,
8};
9use crate::{
10 constant::TPM_HEADER_SIZE,
11 data::{TpmCc, TpmRc, TpmRcBase, TpmSt, TpmsAuthCommand, TpmsAuthResponse},
12 TpmDiscriminant, TpmError, TpmParse, TpmResult,
13};
14use core::{convert::TryFrom, mem::size_of};
15
16#[doc(hidden)]
18pub struct TpmDispatch {
19 pub cc: TpmCc,
20 pub handles: usize,
21 #[allow(clippy::type_complexity)]
22 pub command_parser: for<'a> fn(&'a [u8], &'a [u8]) -> TpmResult<(TpmCommandBody, &'a [u8])>,
23 #[allow(clippy::type_complexity)]
24 pub response_parser: for<'a> fn(TpmSt, &'a [u8]) -> TpmResult<(TpmResponseBody, &'a [u8])>,
25}
26
27pub type TpmResponseResult = Result<(TpmResponseBody, TpmAuthResponses), TpmRc>;
29
30pub fn tpm_parse_command(buf: &[u8]) -> TpmResult<(TpmHandles, TpmCommandBody, TpmAuthCommands)> {
38 if buf.len() < TPM_HEADER_SIZE {
39 return Err(TpmError::DataTruncated);
40 }
41 let buf_len = buf.len();
42
43 let (tag_raw, buf) = u16::parse(buf)?;
44 let tag = TpmSt::try_from(tag_raw).map_err(|()| {
45 TpmError::UnknownDiscriminant("TpmSt", TpmDiscriminant::Unsigned(u64::from(tag_raw)))
46 })?;
47 let (size, buf) = u32::parse(buf)?;
48 let (cc_raw, body_buf) = u32::parse(buf)?;
49
50 if buf_len < size as usize {
51 return Err(TpmError::DataTruncated);
52 } else if buf_len > size as usize {
53 return Err(TpmError::TrailingData);
54 }
55
56 let cc = TpmCc::try_from(cc_raw).map_err(|()| {
57 TpmError::UnknownDiscriminant("TpmCc", TpmDiscriminant::Unsigned(u64::from(cc_raw)))
58 })?;
59 let dispatch = TPM_DISPATCH_TABLE
60 .binary_search_by_key(&cc, |d| d.cc)
61 .map(|index| &TPM_DISPATCH_TABLE[index])
62 .map_err(|_| {
63 TpmError::UnknownDiscriminant("TpmCc", TpmDiscriminant::Unsigned(u64::from(cc_raw)))
64 })?;
65
66 if tag != TpmSt::NoSessions && tag != TpmSt::Sessions {
67 return Err(TpmError::MalformedData);
68 }
69
70 let handle_area_size = dispatch.handles * size_of::<u32>();
71 if body_buf.len() < handle_area_size {
72 return Err(TpmError::DataTruncated);
73 }
74 let (handle_area, after_handles) = body_buf.split_at(handle_area_size);
75
76 let mut sessions = TpmAuthCommands::new();
77 let param_area = if tag == TpmSt::Sessions {
78 let (auth_area_size, buf_after_auth_size) = u32::parse(after_handles)?;
79 let auth_area_size = auth_area_size as usize;
80 if buf_after_auth_size.len() < auth_area_size {
81 return Err(TpmError::DataTruncated);
82 }
83 let (mut auth_area, param_area) = buf_after_auth_size.split_at(auth_area_size);
84 while !auth_area.is_empty() {
85 let (session, rest) = TpmsAuthCommand::parse(auth_area)?;
86 sessions.try_push(session)?;
87 auth_area = rest;
88 }
89 if !auth_area.is_empty() {
90 return Err(TpmError::TrailingData);
91 }
92 param_area
93 } else {
94 after_handles
95 };
96
97 let (command_data, param_remainder) = (dispatch.command_parser)(handle_area, param_area)?;
98
99 if !param_remainder.is_empty() {
100 return Err(TpmError::TrailingData);
101 }
102
103 let mut handles = TpmHandles::new();
104 let mut temp_handle_cursor = handle_area;
105 while !temp_handle_cursor.is_empty() {
106 let (handle, rest) = u32::parse(temp_handle_cursor)?;
107 handles.try_push(handle.into())?;
108 temp_handle_cursor = rest;
109 }
110
111 Ok((handles, command_data, sessions))
112}
113
114pub fn tpm_parse_response(cc: TpmCc, buf: &[u8]) -> TpmResult<TpmResponseResult> {
122 if buf.len() < TPM_HEADER_SIZE {
123 return Err(TpmError::DataTruncated);
124 }
125
126 let (tag_raw, remainder) = u16::parse(buf)?;
127 let (size, remainder) = u32::parse(remainder)?;
128 let (code, body_buf) = u32::parse(remainder)?;
129
130 if buf.len() < size as usize {
131 return Err(TpmError::DataTruncated);
132 } else if buf.len() > size as usize {
133 return Err(TpmError::TrailingData);
134 }
135
136 let rc = TpmRc::try_from(code)?;
137 if !matches!(rc, TpmRc::Fmt0(TpmRcBase::Success)) {
138 return Ok(Err(rc));
139 }
140
141 let tag = TpmSt::try_from(tag_raw).map_err(|()| {
142 TpmError::UnknownDiscriminant("TpmSt", TpmDiscriminant::Unsigned(u64::from(tag_raw)))
143 })?;
144
145 let dispatch = TPM_DISPATCH_TABLE
146 .binary_search_by_key(&cc, |d| d.cc)
147 .map(|index| &TPM_DISPATCH_TABLE[index])
148 .map_err(|_| {
149 TpmError::UnknownDiscriminant("TpmCc", TpmDiscriminant::Unsigned(u64::from(cc as u32)))
150 })?;
151
152 let (body, mut session_area) = (dispatch.response_parser)(tag, body_buf)?;
153
154 let mut auth_responses = TpmAuthResponses::new();
155 if tag == TpmSt::Sessions {
156 while !session_area.is_empty() {
157 let (session, rest) = TpmsAuthResponse::parse(session_area)?;
158 auth_responses.try_push(session)?;
159 session_area = rest;
160 }
161 }
162
163 if !session_area.is_empty() {
164 return Err(TpmError::TrailingData);
165 }
166
167 Ok(Ok((body, auth_responses)))
168}