xref: /cloud-hypervisor/tpm/src/lib.rs (revision 6f8bd27cf7629733582d930519e98d19e90afb16)
1 // Copyright © 2022, Microsoft Corporation
2 //
3 // SPDX-License-Identifier: Apache-2.0
4 //
5 
6 #[macro_use]
7 extern crate log;
8 
9 pub mod emulator;
10 pub mod socket;
11 
12 use anyhow::anyhow;
13 use byteorder::{BigEndian, ReadBytesExt};
14 use std::convert::TryInto;
15 use thiserror::Error;
16 
17 pub const TPM_CRB_BUFFER_MAX: usize = 3968; // 0x1_000 - 0x80
18 pub const TPM_SUCCESS: u32 = 0x0;
19 
20 /*
21  * Structures required to process Request and Responses of Control commands
22  * used by control channel over UNIX socket interface
23  *
24  * All messages contain big-endian data.
25  *
26  * Reference: https://github.com/stefanberger/swtpm/blob/master/man/man3/swtpm_ioctls.pod
27  */
28 #[derive(Debug, Clone, Copy)]
29 pub enum Commands {
30     CmdGetCapability = 1,
31     CmdInit,
32     CmdShutdown,
33     CmdGetTpmEstablished,
34     CmdSetLocality,
35     CmdHashStart,
36     CmdHashData,
37     CmdHashEnd,
38     CmdCancelTpmCmd,
39     CmdStoreVolatile,
40     CmdResetTpmEstablished,
41     CmdGetStateBlob,
42     CmdSetStateBlob,
43     CmdStop,
44     CmdGetConfig,
45     CmdSetDatafd,
46     CmdSetBufferSize,
47 }
48 
49 #[derive(Error, Debug)]
50 pub enum Error {
51     #[error("Failed converting buf to PTM : {0}")]
52     ConvertToPtm(#[source] anyhow::Error),
53 }
54 type Result<T> = anyhow::Result<T, Error>;
55 
56 #[derive(PartialEq, Eq, Copy, Clone, Debug)]
57 pub enum MemberType {
58     Request,
59     Response,
60     Error,
61     Cap,
62 }
63 
64 pub trait Ptm {
65     // Get Member Type
66     fn get_member_type(&self) -> MemberType;
67 
68     // Set Member Type
69     fn set_member_type(&mut self, mem: MemberType);
70 
71     // Convert PTM Request to bytes to be sent to tpm
72     fn ptm_to_request(&self) -> Vec<u8>;
73 
74     // Update PTM from tpm's reponse
75     fn update_ptm_with_response(&mut self, buf: &[u8]) -> Result<()>;
76 
77     // Update tpm result
78     fn set_result_code(&mut self, res: u32);
79 
80     fn get_result_code(&self) -> u32;
81 }
82 
83 /*
84  * Every response for a tpm Control Command execution must hold tpm return
85  * code (PtmResult) as its first element.
86  * Based on the type of input Control Command additional data could be
87  * appended to the response.
88  */
89 pub type PtmResult = u32;
90 
91 impl Ptm for PtmResult {
92     fn ptm_to_request(&self) -> Vec<u8> {
93         let buf: Vec<u8> = Vec::<u8>::new();
94         buf
95     }
96 
97     fn get_member_type(&self) -> MemberType {
98         MemberType::Response
99     }
100 
101     fn update_ptm_with_response(&mut self, buf: &[u8]) -> Result<()> {
102         if buf.len() < 4 {
103             return Err(Error::ConvertToPtm(anyhow!(
104                 "PtmRes buffer is of insufficient length. Buffer length should be atleast 4"
105             )));
106         }
107 
108         *self = u32::from_be_bytes(buf[0..4].try_into().unwrap());
109         Ok(())
110     }
111 
112     fn set_member_type(&mut self, _mem: MemberType) {}
113 
114     fn set_result_code(&mut self, res: u32) {
115         *self = res;
116     }
117 
118     fn get_result_code(&self) -> u32 {
119         *self
120     }
121 }
122 
123 /* GET_CAPABILITY Response */
124 pub type PtmCap = u64;
125 impl Ptm for PtmCap {
126     fn ptm_to_request(&self) -> Vec<u8> {
127         // tpm's GetCapability call doesn't need any supporting message
128         // return an empty Buffer
129         let buf: Vec<u8> = Vec::<u8>::new();
130         buf
131     }
132 
133     fn get_member_type(&self) -> MemberType {
134         MemberType::Cap
135     }
136 
137     fn update_ptm_with_response(&mut self, mut buf: &[u8]) -> Result<()> {
138         let buf_len = buf.len();
139         if buf_len != 8 {
140             return Err(Error::ConvertToPtm(anyhow!(
141                 "Response for GetCapability cmd is of incorrect length: {:?}. Response buffer should be 8 bytes long",
142             buf_len)));
143         }
144         *self = buf.read_u64::<BigEndian>().unwrap();
145         Ok(())
146     }
147 
148     fn set_member_type(&mut self, _mem: MemberType) {}
149 
150     fn set_result_code(&mut self, _res: u32) {}
151 
152     fn get_result_code(&self) -> u32 {
153         ((*self) >> 32) as u32
154     }
155 }
156 
157 /* GET_TPMESTABLISHED Reponse */
158 #[derive(Debug)]
159 pub struct PtmEstResp {
160     pub bit: u8,
161 }
162 
163 #[derive(Debug)]
164 pub struct PtmEst {
165     member: MemberType,
166     pub resp: PtmEstResp,
167     pub result_code: PtmResult,
168 }
169 
170 impl PtmEst {
171     pub fn new() -> Self {
172         Self {
173             member: MemberType::Response,
174             result_code: 0,
175             resp: PtmEstResp { bit: 0 },
176         }
177     }
178 }
179 
180 impl Default for PtmEst {
181     fn default() -> Self {
182         Self::new()
183     }
184 }
185 
186 impl Ptm for PtmEst {
187     fn ptm_to_request(&self) -> Vec<u8> {
188         // tpm's GetTpmEstablished call doesn't need any supporting message
189         // return an empty Buffer
190         let buf: Vec<u8> = Vec::<u8>::new();
191         buf
192     }
193 
194     fn get_member_type(&self) -> MemberType {
195         self.member
196     }
197 
198     fn update_ptm_with_response(&mut self, buf: &[u8]) -> Result<()> {
199         if buf.len() < 5 {
200             return Err(Error::ConvertToPtm(anyhow!(
201                 "Response for GetTpmEstablished cmd is of incorrect length. Response buffer should be 5 bytes long"
202             )));
203         }
204         let mut res = &buf[0..4];
205         self.set_result_code(res.read_u32::<BigEndian>().unwrap());
206         let bit = &buf[4];
207         self.resp.bit = *bit;
208         Ok(())
209     }
210 
211     fn set_member_type(&mut self, _mem: MemberType) {}
212 
213     fn set_result_code(&mut self, res: u32) {
214         self.result_code = res
215     }
216 
217     fn get_result_code(&self) -> u32 {
218         self.result_code
219     }
220 }
221 
222 /* INIT Response */
223 
224 #[derive(Debug)]
225 pub struct PtmInit {
226     pub member: MemberType,
227     /* request */
228     pub init_flags: u32,
229     /* response */
230     pub result_code: PtmResult,
231 }
232 
233 impl Default for PtmInit {
234     fn default() -> Self {
235         Self::new()
236     }
237 }
238 
239 impl PtmInit {
240     pub fn new() -> Self {
241         Self {
242             member: MemberType::Request,
243             init_flags: 0,
244             result_code: 0,
245         }
246     }
247 }
248 
249 impl Ptm for PtmInit {
250     fn ptm_to_request(&self) -> Vec<u8> {
251         let mut buf: Vec<u8> = Vec::<u8>::new();
252         buf.extend_from_slice(&self.init_flags.to_be_bytes());
253         buf
254     }
255 
256     fn get_member_type(&self) -> MemberType {
257         self.member
258     }
259 
260     fn update_ptm_with_response(&mut self, buf: &[u8]) -> Result<()> {
261         if buf.len() != 4 {
262             return Err(Error::ConvertToPtm(anyhow!(
263                 "Response for Init cmd is of incorrect length. Response buffer should be 4 bytes long"
264             )));
265         }
266         self.set_member_type(MemberType::Response);
267         let mut res = &buf[0..4];
268         self.set_result_code(res.read_u32::<BigEndian>().unwrap());
269         Ok(())
270     }
271 
272     fn set_member_type(&mut self, mem: MemberType) {
273         self.member = mem
274     }
275 
276     fn set_result_code(&mut self, res: u32) {
277         self.result_code = res
278     }
279 
280     fn get_result_code(&self) -> u32 {
281         self.result_code
282     }
283 }
284 
285 /*
286  * PTM_SET_BUFFERSIZE: Set the buffer size to be used by the tpm.
287  * A 0 on input queries for the current buffer size. Any other
288  * number will try to set the buffer size. The returned number is
289  * the buffer size that will be used, which can be larger than the
290  * requested one, if it was below the minimum, or smaller than the
291  * requested one, if it was above the maximum.
292  *
293  * SET_BUFFERSIZE Response
294  */
295 #[derive(Debug)]
296 pub struct PtmSBSReq {
297     buffersize: u32,
298 }
299 
300 #[derive(Debug)]
301 pub struct PtmSBSResp {
302     bufsize: u32,
303     minsize: u32,
304     maxsize: u32,
305 }
306 
307 #[derive(Debug)]
308 pub struct PtmSetBufferSize {
309     pub mem: MemberType,
310     /* request */
311     pub req: PtmSBSReq,
312     /* response */
313     pub resp: PtmSBSResp,
314     pub result_code: PtmResult,
315 }
316 
317 impl PtmSetBufferSize {
318     pub fn new(req_buffsize: u32) -> Self {
319         Self {
320             mem: MemberType::Request,
321             req: PtmSBSReq {
322                 buffersize: req_buffsize,
323             },
324             resp: PtmSBSResp {
325                 bufsize: 0,
326                 minsize: 0,
327                 maxsize: 0,
328             },
329             result_code: 0,
330         }
331     }
332     pub fn get_bufsize(&self) -> u32 {
333         self.resp.bufsize
334     }
335 }
336 
337 impl Ptm for PtmSetBufferSize {
338     fn ptm_to_request(&self) -> Vec<u8> {
339         let mut buf: Vec<u8> = Vec::<u8>::new();
340         buf.extend_from_slice(&self.req.buffersize.to_be_bytes());
341         buf
342     }
343 
344     fn get_member_type(&self) -> MemberType {
345         self.mem
346     }
347 
348     fn update_ptm_with_response(&mut self, buf: &[u8]) -> Result<()> {
349         if buf.len() != 16 {
350             return Err(Error::ConvertToPtm(anyhow!(
351                 "Response for CmdSetBufferSize cmd is of incorrect length. Response buffer should be 16 bytes long"
352             )));
353         }
354         self.set_member_type(MemberType::Response);
355         let mut res = &buf[0..4];
356         self.set_result_code(res.read_u32::<BigEndian>().unwrap());
357 
358         let mut bufsize = &buf[4..8];
359         self.resp.bufsize = bufsize.read_u32::<BigEndian>().unwrap();
360 
361         let mut minsize = &buf[8..12];
362         self.resp.minsize = minsize.read_u32::<BigEndian>().unwrap();
363 
364         let mut maxsize = &buf[12..16];
365         self.resp.maxsize = maxsize.read_u32::<BigEndian>().unwrap();
366 
367         Ok(())
368     }
369 
370     fn set_member_type(&mut self, mem: MemberType) {
371         self.mem = mem
372     }
373 
374     fn set_result_code(&mut self, res: u32) {
375         self.result_code = res
376     }
377 
378     fn get_result_code(&self) -> u32 {
379         self.result_code
380     }
381 }
382 
383 #[cfg(test)]
384 mod tests {
385     use super::*;
386     #[test]
387     fn test_ptmresult() -> Result<()> {
388         let buf: &[u8] = &[0, 0, 0, 1];
389         let mut result_code: PtmResult = 0;
390         result_code.update_ptm_with_response(buf)?;
391         assert_eq!(result_code.get_result_code(), 0x1);
392         Ok(())
393     }
394     #[test]
395     fn test_ptmcap() -> Result<()> {
396         let mut cap: PtmCap = 0x0;
397         let buf: &[u8] = &[0, 0, 0, 0xE, 0, 0, 0xFF, 0xFF];
398         cap.update_ptm_with_response(buf)?;
399         assert_eq!(cap.get_result_code(), 0xE);
400         Ok(())
401     }
402     #[test]
403     fn test_ptmest() -> Result<()> {
404         let mut est: PtmEst = PtmEst::new();
405         let buf: &[u8] = &[0, 0, 0xE, 0, 0xC, 0, 1, 1];
406         est.update_ptm_with_response(buf)?;
407         assert_eq!(est.get_result_code(), 0xE00);
408         assert_eq!(est.resp.bit, 0xC);
409         Ok(())
410     }
411     #[test]
412     /*PtmInit Testing */
413     fn test_ptminit() -> Result<()> {
414         let mut init: PtmInit = PtmInit::new();
415         init.init_flags = 0x1;
416         let buf = init.ptm_to_request();
417         assert_eq!(buf, [0x0, 0x0, 0x0, 0x1]);
418         let response_buf: &[u8] = &[0, 0, 0xE, 0];
419         init.update_ptm_with_response(response_buf)?;
420         assert_eq!(init.get_result_code(), 0xE00);
421         Ok(())
422     }
423     #[test]
424     /* PtmSetBufferSize Testing */
425     fn test_ptmsetbuffersize() -> Result<()> {
426         let mut psbs: PtmSetBufferSize = PtmSetBufferSize::new(1024);
427         // Member type should be Request after initialization
428         assert_eq!(psbs.get_member_type(), MemberType::Request);
429         let buf: &[u8] = &[
430             0, 0x12, 0x34, 0x56, 0, 0, 0, 0xA, 0, 0, 0, 0xB, 0, 0, 0, 0xC,
431         ];
432         psbs.update_ptm_with_response(buf)?;
433         assert_eq!(psbs.get_member_type(), MemberType::Response);
434         assert_eq!(psbs.get_result_code(), 0x123456);
435         assert_eq!(psbs.resp.bufsize, 0xA);
436         assert_eq!(psbs.resp.minsize, 0xB);
437         assert_eq!(psbs.resp.maxsize, 0xC);
438         Ok(())
439     }
440 }
441