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