tpm2_protocol/message/
parse.rs1use super::{
6 TpmAuthCommands, TpmAuthResponses, TpmCommandBody, TpmHandles, TpmResponseBody,
7 PARSE_COMMAND_MAP, PARSE_RESPONSE_MAP, TPM_HEADER_SIZE,
8};
9use crate::{
10 data::{TpmCc, TpmRc, TpmSt, TpmsAuthCommand, TpmsAuthResponse},
11 TpmErrorKind, TpmParse, TpmResult,
12};
13use core::convert::TryFrom;
14
15pub type TpmParseResult<'a> = Result<(TpmRc, TpmResponseBody, TpmAuthResponses), (TpmRc, &'a [u8])>;
18
19pub fn tpm_parse_command(buf: &[u8]) -> TpmResult<(TpmHandles, TpmCommandBody, TpmAuthCommands)> {
27 if buf.len() < TPM_HEADER_SIZE {
28 return Err(TpmErrorKind::Boundary);
29 }
30 let command_len = buf.len();
31
32 let (tag_raw, buf) = u16::parse(buf)?;
33 let tag = TpmSt::try_from(tag_raw).map_err(|()| TpmErrorKind::InvalidDiscriminant {
34 type_name: "TpmSt",
35 value: u64::from(tag_raw),
36 })?;
37 let (size, buf) = u32::parse(buf)?;
38 let (cc_raw, mut buf) = u32::parse(buf)?;
39
40 if command_len != size as usize {
41 return Err(TpmErrorKind::Boundary);
42 }
43
44 let cc = TpmCc::try_from(cc_raw).map_err(|()| TpmErrorKind::InvalidDiscriminant {
45 type_name: "TpmCc",
46 value: u64::from(cc_raw),
47 })?;
48 let dispatch = PARSE_COMMAND_MAP
49 .binary_search_by_key(&cc, |d| d.0)
50 .map(|index| &PARSE_COMMAND_MAP[index])
51 .map_err(|_| TpmErrorKind::InvalidDiscriminant {
52 type_name: "TpmCc",
53 value: u64::from(cc_raw),
54 })?;
55
56 if tag == TpmSt::Sessions && !dispatch.2 {
57 return Err(TpmErrorKind::InvalidTag {
58 type_name: "TpmSt",
59 expected: TpmSt::NoSessions as u16,
60 got: tag_raw,
61 });
62 }
63 if tag == TpmSt::NoSessions && !dispatch.1 {
64 return Err(TpmErrorKind::InvalidTag {
65 type_name: "TpmSt",
66 expected: TpmSt::Sessions as u16,
67 got: tag_raw,
68 });
69 }
70
71 let mut handles = TpmHandles::new();
72 for _ in 0..dispatch.3 {
73 let (handle, rest) = u32::parse(buf)?;
74 handles
75 .try_push(handle)
76 .map_err(|_| TpmErrorKind::ValueTooLarge)?;
77 buf = rest;
78 }
79
80 let mut sessions = TpmAuthCommands::new();
81 let param_buf = if tag == TpmSt::Sessions {
82 let (auth_area_size, auth_buf) = u32::parse(buf)?;
83 let auth_area_size = auth_area_size as usize;
84 if auth_buf.len() < auth_area_size {
85 return Err(TpmErrorKind::Boundary);
86 }
87 let (mut auth_area, param_buf) = auth_buf.split_at(auth_area_size);
88 while !auth_area.is_empty() {
89 let (session, rest) = TpmsAuthCommand::parse(auth_area)?;
90 sessions
91 .try_push(session)
92 .map_err(|_| TpmErrorKind::ValueTooLarge)?;
93 auth_area = rest;
94 }
95 if !auth_area.is_empty() {
96 return Err(TpmErrorKind::TrailingData);
97 }
98 param_buf
99 } else {
100 buf
101 };
102
103 let (command_data, remainder) = (dispatch.4)(param_buf)?;
104
105 if !remainder.is_empty() {
106 return Err(TpmErrorKind::TrailingData);
107 }
108
109 Ok((handles, command_data, sessions))
110}
111
112pub fn tpm_parse_response(cc: TpmCc, buf: &[u8]) -> TpmResult<TpmParseResult<'_>> {
121 if buf.len() < TPM_HEADER_SIZE {
122 return Err(TpmErrorKind::Boundary);
123 }
124
125 let (tag_raw, remainder) = u16::parse(buf)?;
126 let (size, remainder) = u32::parse(remainder)?;
127 let (code, body_buf) = u32::parse(remainder)?;
128
129 if buf.len() != size as usize {
130 return Err(TpmErrorKind::Boundary);
131 }
132
133 let rc = TpmRc::try_from(code)?;
134 if rc.is_error() {
135 return Ok(Err((rc, body_buf)));
136 }
137
138 let tag = TpmSt::try_from(tag_raw).map_err(|()| TpmErrorKind::InvalidDiscriminant {
139 type_name: "TpmSt",
140 value: u64::from(tag_raw),
141 })?;
142
143 let dispatch = PARSE_RESPONSE_MAP
144 .binary_search_by_key(&cc, |d| d.0)
145 .map(|index| &PARSE_RESPONSE_MAP[index])
146 .map_err(|_| TpmErrorKind::InvalidDiscriminant {
147 type_name: "TpmCc",
148 value: u64::from(cc as u32),
149 })?;
150
151 let (body, mut session_area) = (dispatch.2)(body_buf)?;
152
153 let mut auth_responses = TpmAuthResponses::new();
154 if tag == TpmSt::Sessions {
155 while !session_area.is_empty() {
156 let (session, rest) = TpmsAuthResponse::parse(session_area)?;
157 auth_responses
158 .try_push(session)
159 .map_err(|_| TpmErrorKind::ValueTooLarge)?;
160 session_area = rest;
161 }
162 }
163
164 if !session_area.is_empty() {
165 return Err(TpmErrorKind::TrailingData);
166 }
167
168 Ok(Ok((rc, body, auth_responses)))
169}