ttk_server/attestation/
nitro.rs1use super::nitro_doc::wrap_as_eat;
7use super::{AttestationError, AttestationProvider};
8pub use crate::AttestationParams;
9use crate::EatClaimsSet;
10use aws_nitro_enclaves_nsm_api::api::Digest;
11use aws_nitro_enclaves_nsm_api::api::{Request, Response};
12use aws_nitro_enclaves_nsm_api::driver::{nsm_exit, nsm_init, nsm_process_request};
13use std::path::Path;
14
15#[derive(Debug, Clone, PartialEq)]
17pub struct NsmDescription {
18 pub version_major: u16,
20 pub version_minor: u16,
22 pub version_patch: u16,
24 pub module_id: String,
26 pub max_pcrs: u16,
28 pub locked_pcrs: std::collections::BTreeSet<u16>,
30 pub digest: Digest,
32}
33
34#[derive(Debug)]
39pub struct NsmSession {
40 fd: i32,
41}
42
43impl AttestationProvider for NsmSession {
45 fn name(&self) -> &'static str {
47 "aws-nitro"
48 }
49
50 fn is_available() -> bool {
52 Path::new("/dev/nsm").exists()
53 }
54
55 fn generate_document(
57 &self,
58 params: &AttestationParams,
59 ) -> Result<EatClaimsSet, AttestationError> {
60 wrap_as_eat(&self.create_attestation(params)?)
61 }
62}
63
64impl NsmSession {
66 pub fn open() -> Result<Self, AttestationError> {
71 let fd = nsm_init();
72 if fd < 0 {
73 return Err(AttestationError::DeviceOpenFailed(
74 "Unable to open /dev/nsm. Ensure this process is running inside an AWS Nitro Enclave with the NSM device enabled."
75 .to_string(),
76 ));
77 }
78 Ok(Self { fd })
79 }
80
81 pub fn from_raw_fd(fd: i32) -> Result<Self, AttestationError> {
83 if fd < 0 {
84 return Err(AttestationError::DeviceOpenFailed(
85 "Invalid file descriptor provided".to_string(),
86 ));
87 }
88 Ok(Self { fd })
89 }
90
91 pub fn raw_fd(&self) -> i32 {
93 self.fd
94 }
95
96 pub fn create_attestation(
100 &self,
101 params: &AttestationParams,
102 ) -> Result<Vec<u8>, AttestationError> {
103 let request = Request::Attestation {
104 user_data: params.user_data.as_ref().map(|d| d.clone().into()),
105 nonce: params.nonce.as_ref().map(|n| n.clone().into()),
106 public_key: params.public_key.as_ref().map(|pk| pk.clone().into()),
107 };
108
109 match nsm_process_request(self.fd, request) {
110 Response::Attestation { document } => Ok(document),
111 Response::Error(err) => Err(AttestationError::Driver(format!("{err:?}"))),
112 other => Err(AttestationError::UnexpectedResponse(format!("{other:?}"))),
113 }
114 }
115
116 pub fn create_attestation_for_cert(
120 &self,
121 cert_der: &[u8],
122 ) -> Result<Vec<u8>, AttestationError> {
123 let params = AttestationParams::new().with_user_data_hash(cert_der);
124 self.create_attestation(¶ms)
125 }
126
127 pub fn describe_nsm(&self) -> Result<NsmDescription, AttestationError> {
129 match nsm_process_request(self.fd, Request::DescribeNSM) {
130 Response::DescribeNSM {
131 version_major,
132 version_minor,
133 version_patch,
134 module_id,
135 max_pcrs,
136 locked_pcrs,
137 digest,
138 } => Ok(NsmDescription {
139 version_major,
140 version_minor,
141 version_patch,
142 module_id,
143 max_pcrs,
144 locked_pcrs,
145 digest,
146 }),
147 Response::Error(err) => Err(AttestationError::Driver(format!("{err:?}"))),
148 other => Err(AttestationError::UnexpectedResponse(format!("{other:?}"))),
149 }
150 }
151
152 pub fn get_random(&self) -> Result<Vec<u8>, AttestationError> {
154 match nsm_process_request(self.fd, Request::GetRandom) {
155 Response::GetRandom { random } => Ok(random),
156 Response::Error(err) => Err(AttestationError::Driver(format!("{err:?}"))),
157 other => Err(AttestationError::UnexpectedResponse(format!("{other:?}"))),
158 }
159 }
160
161 pub fn describe_pcr(&self, index: u16) -> Result<(bool, Vec<u8>), AttestationError> {
164 match nsm_process_request(self.fd, Request::DescribePCR { index }) {
165 Response::DescribePCR { lock, data } => Ok((lock, data)),
166 Response::Error(err) => Err(AttestationError::Driver(format!("{err:?}"))),
167 other => Err(AttestationError::UnexpectedResponse(format!("{other:?}"))),
168 }
169 }
170
171 pub fn extend_pcr(&self, index: u16, data: Vec<u8>) -> Result<Vec<u8>, AttestationError> {
173 match nsm_process_request(self.fd, Request::ExtendPCR { index, data }) {
174 Response::ExtendPCR { data } => Ok(data),
175 Response::Error(err) => Err(AttestationError::Driver(format!("{err:?}"))),
176 other => Err(AttestationError::UnexpectedResponse(format!("{other:?}"))),
177 }
178 }
179
180 pub fn lock_pcr(&self, index: u16) -> Result<(), AttestationError> {
182 match nsm_process_request(self.fd, Request::LockPCR { index }) {
183 Response::LockPCR => Ok(()),
184 Response::Error(err) => Err(AttestationError::Driver(format!("{err:?}"))),
185 other => Err(AttestationError::UnexpectedResponse(format!("{other:?}"))),
186 }
187 }
188}
189
190impl Drop for NsmSession {
192 fn drop(&mut self) {
194 if self.fd >= 0 {
195 nsm_exit(self.fd);
196 self.fd = -1;
197 }
198 }
199}