use super::nitro_doc::wrap_as_eat;
use super::{AttestationError, AttestationProvider};
pub use crate::AttestationParams;
use crate::EatClaimsSet;
use aws_nitro_enclaves_nsm_api::api::Digest;
use aws_nitro_enclaves_nsm_api::api::{Request, Response};
use aws_nitro_enclaves_nsm_api::driver::{nsm_exit, nsm_init, nsm_process_request};
use std::path::Path;
#[derive(Debug, Clone, PartialEq)]
pub struct NsmDescription {
pub version_major: u16,
pub version_minor: u16,
pub version_patch: u16,
pub module_id: String,
pub max_pcrs: u16,
pub locked_pcrs: std::collections::BTreeSet<u16>,
pub digest: Digest,
}
#[derive(Debug)]
pub struct NsmSession {
fd: i32,
}
impl AttestationProvider for NsmSession {
fn name(&self) -> &'static str {
"aws-nitro"
}
fn is_available() -> bool {
Path::new("/dev/nsm").exists()
}
fn generate_document(
&self,
params: &AttestationParams,
) -> Result<EatClaimsSet, AttestationError> {
wrap_as_eat(&self.create_attestation(params)?)
}
}
impl NsmSession {
pub fn open() -> Result<Self, AttestationError> {
let fd = nsm_init();
if fd < 0 {
return Err(AttestationError::DeviceOpenFailed(
"Unable to open /dev/nsm. Ensure this process is running inside an AWS Nitro Enclave with the NSM device enabled."
.to_string(),
));
}
Ok(Self { fd })
}
pub fn from_raw_fd(fd: i32) -> Result<Self, AttestationError> {
if fd < 0 {
return Err(AttestationError::DeviceOpenFailed(
"Invalid file descriptor provided".to_string(),
));
}
Ok(Self { fd })
}
pub fn raw_fd(&self) -> i32 {
self.fd
}
pub fn create_attestation(
&self,
params: &AttestationParams,
) -> Result<Vec<u8>, AttestationError> {
let request = Request::Attestation {
user_data: params.user_data.as_ref().map(|d| d.clone().into()),
nonce: params.nonce.as_ref().map(|n| n.clone().into()),
public_key: params.public_key.as_ref().map(|pk| pk.clone().into()),
};
match nsm_process_request(self.fd, request) {
Response::Attestation { document } => Ok(document),
Response::Error(err) => Err(AttestationError::Driver(format!("{err:?}"))),
other => Err(AttestationError::UnexpectedResponse(format!("{other:?}"))),
}
}
pub fn create_attestation_for_cert(
&self,
cert_der: &[u8],
) -> Result<Vec<u8>, AttestationError> {
let params = AttestationParams::new().with_user_data_hash(cert_der);
self.create_attestation(¶ms)
}
pub fn describe_nsm(&self) -> Result<NsmDescription, AttestationError> {
match nsm_process_request(self.fd, Request::DescribeNSM) {
Response::DescribeNSM {
version_major,
version_minor,
version_patch,
module_id,
max_pcrs,
locked_pcrs,
digest,
} => Ok(NsmDescription {
version_major,
version_minor,
version_patch,
module_id,
max_pcrs,
locked_pcrs,
digest,
}),
Response::Error(err) => Err(AttestationError::Driver(format!("{err:?}"))),
other => Err(AttestationError::UnexpectedResponse(format!("{other:?}"))),
}
}
pub fn get_random(&self) -> Result<Vec<u8>, AttestationError> {
match nsm_process_request(self.fd, Request::GetRandom) {
Response::GetRandom { random } => Ok(random),
Response::Error(err) => Err(AttestationError::Driver(format!("{err:?}"))),
other => Err(AttestationError::UnexpectedResponse(format!("{other:?}"))),
}
}
pub fn describe_pcr(&self, index: u16) -> Result<(bool, Vec<u8>), AttestationError> {
match nsm_process_request(self.fd, Request::DescribePCR { index }) {
Response::DescribePCR { lock, data } => Ok((lock, data)),
Response::Error(err) => Err(AttestationError::Driver(format!("{err:?}"))),
other => Err(AttestationError::UnexpectedResponse(format!("{other:?}"))),
}
}
pub fn extend_pcr(&self, index: u16, data: Vec<u8>) -> Result<Vec<u8>, AttestationError> {
match nsm_process_request(self.fd, Request::ExtendPCR { index, data }) {
Response::ExtendPCR { data } => Ok(data),
Response::Error(err) => Err(AttestationError::Driver(format!("{err:?}"))),
other => Err(AttestationError::UnexpectedResponse(format!("{other:?}"))),
}
}
pub fn lock_pcr(&self, index: u16) -> Result<(), AttestationError> {
match nsm_process_request(self.fd, Request::LockPCR { index }) {
Response::LockPCR => Ok(()),
Response::Error(err) => Err(AttestationError::Driver(format!("{err:?}"))),
other => Err(AttestationError::UnexpectedResponse(format!("{other:?}"))),
}
}
}
impl Drop for NsmSession {
fn drop(&mut self) {
if self.fd >= 0 {
nsm_exit(self.fd);
self.fd = -1;
}
}
}