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 TpmDiscriminant, 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)> {
37 if buf.len() < TPM_HEADER_SIZE as usize {
38 return Err(TpmProtocolError::UnexpectedEof);
39 }
40 let buf_len = buf.len();
41
42 let (tag_raw, buf) = u16::unmarshal(buf)?;
43 let tag = TpmSt::try_from(tag_raw).map_err(|()| {
44 TpmProtocolError::InvalidDiscriminant(
45 "TpmSt",
46 TpmDiscriminant::Unsigned(u64::from(tag_raw)),
47 )
48 })?;
49 let (size, buf) = u32::unmarshal(buf)?;
50 let (cc_raw, body_buf) = u32::unmarshal(buf)?;
51
52 if buf_len < size as usize {
53 return Err(TpmProtocolError::UnexpectedEof);
54 } else if buf_len > size as usize {
55 return Err(TpmProtocolError::TrailingData);
56 }
57
58 let cc = TpmCc::try_from(cc_raw).map_err(|()| {
59 TpmProtocolError::InvalidDiscriminant("TpmCc", TpmDiscriminant::Unsigned(u64::from(cc_raw)))
60 })?;
61 let dispatch = TPM_DISPATCH_TABLE
62 .binary_search_by_key(&cc, |d| d.cc)
63 .map(|index| &TPM_DISPATCH_TABLE[index])
64 .map_err(|_| {
65 TpmProtocolError::InvalidDiscriminant(
66 "TpmCc",
67 TpmDiscriminant::Unsigned(u64::from(cc_raw)),
68 )
69 })?;
70
71 if tag != TpmSt::NoSessions && tag != TpmSt::Sessions {
72 return Err(TpmProtocolError::MalformedValue);
73 }
74
75 let handle_area_size = dispatch.handles * size_of::<u32>();
76 if body_buf.len() < handle_area_size {
77 return Err(TpmProtocolError::UnexpectedEof);
78 }
79 let (handle_area, after_handles) = body_buf.split_at(handle_area_size);
80
81 let mut sessions = TpmAuthCommands::new();
82 let param_area = if tag == TpmSt::Sessions {
83 let (auth_area_size, buf_after_auth_size) = u32::unmarshal(after_handles)?;
84 let auth_area_size = auth_area_size as usize;
85 if buf_after_auth_size.len() < auth_area_size {
86 return Err(TpmProtocolError::UnexpectedEof);
87 }
88 let (mut auth_area, param_area) = buf_after_auth_size.split_at(auth_area_size);
89 while !auth_area.is_empty() {
90 let (session, rest) = TpmsAuthCommand::unmarshal(auth_area)?;
91 sessions.push(session)?;
92 auth_area = rest;
93 }
94 if !auth_area.is_empty() {
95 return Err(TpmProtocolError::TrailingData);
96 }
97 param_area
98 } else {
99 after_handles
100 };
101
102 let (command_data, param_remainder) = (dispatch.command_unmarshaler)(handle_area, param_area)?;
103
104 if !param_remainder.is_empty() {
105 return Err(TpmProtocolError::TrailingData);
106 }
107
108 let mut handles = TpmHandles::new();
109 let mut temp_handle_cursor = handle_area;
110 while !temp_handle_cursor.is_empty() {
111 let (handle, rest) = u32::unmarshal(temp_handle_cursor)?;
112 handles.push(handle.into())?;
113 temp_handle_cursor = rest;
114 }
115
116 Ok((handles, command_data, sessions))
117}
118
119pub fn tpm_unmarshal_response(cc: TpmCc, buf: &[u8]) -> TpmResult<TpmResponseResult> {
127 if buf.len() < TPM_HEADER_SIZE as usize {
128 return Err(TpmProtocolError::UnexpectedEof);
129 }
130
131 let (tag_raw, remainder) = u16::unmarshal(buf)?;
132 let (size, remainder) = u32::unmarshal(remainder)?;
133 let (code, body_buf) = u32::unmarshal(remainder)?;
134
135 if buf.len() < size as usize {
136 return Err(TpmProtocolError::UnexpectedEof);
137 } else if buf.len() > size as usize {
138 return Err(TpmProtocolError::TrailingData);
139 }
140
141 let rc = TpmRc::try_from(code)?;
142 if !matches!(rc, TpmRc::Fmt0(TpmRcBase::Success)) {
143 return Ok(Err(rc));
144 }
145
146 let tag = TpmSt::try_from(tag_raw).map_err(|()| {
147 TpmProtocolError::InvalidDiscriminant(
148 "TpmSt",
149 TpmDiscriminant::Unsigned(u64::from(tag_raw)),
150 )
151 })?;
152
153 let dispatch = TPM_DISPATCH_TABLE
154 .binary_search_by_key(&cc, |d| d.cc)
155 .map(|index| &TPM_DISPATCH_TABLE[index])
156 .map_err(|_| {
157 TpmProtocolError::InvalidDiscriminant(
158 "TpmCc",
159 TpmDiscriminant::Unsigned(u64::from(cc as u32)),
160 )
161 })?;
162
163 let (body, mut session_area) = (dispatch.response_unmarshaler)(tag, body_buf)?;
164
165 let mut auth_responses = TpmAuthResponses::new();
166 if tag == TpmSt::Sessions {
167 while !session_area.is_empty() {
168 let (session, rest) = TpmsAuthResponse::unmarshal(session_area)?;
169 auth_responses.push(session)?;
170 session_area = rest;
171 }
172 }
173
174 if !session_area.is_empty() {
175 return Err(TpmProtocolError::TrailingData);
176 }
177
178 Ok(Ok((body, auth_responses)))
179}