use crate::api::guest::CSV_RTMR_REG_SIZE;
use crate::error::*;
use crate::{
certs::{csv::Certificate, Usage, Verifiable},
crypto::{sig::ecdsa, PublicKey, Signature},
util::*,
};
use openssl::{
hash::{Hasher, MessageDigest},
pkey, sign,
};
use static_assertions::const_assert;
use serde::{Deserialize, Serialize};
use serde_big_array::BigArray;
use std::io::Write;
use bitfield::bitfield;
pub const ATTESTATION_EXT_MAGIC: [u8; 16] = *b"ATTESTATION_EXT\0";
#[repr(C)]
#[derive(PartialEq, Debug)]
pub struct ReportReq {
pub data: [u8; 64],
pub mnonce: [u8; 16],
pub hash: [u8; 32],
}
impl Default for ReportReq {
fn default() -> Self {
Self {
data: [0; 64],
mnonce: [0; 16],
hash: [0; 32],
}
}
}
impl ReportReq {
pub fn new(data: Option<[u8; 64]>, mnonce: [u8; 16]) -> Result<Self, Error> {
let mut request = Self::default();
if let Some(data) = data {
request.data = data;
}
request.mnonce = mnonce;
request.calculate_hash()?;
Ok(request)
}
fn calculate_hash(&mut self) -> Result<(), Error> {
let mut hasher = Hasher::new(MessageDigest::sm3())?;
hasher.update(self.data.as_ref())?;
hasher.update(self.mnonce.as_ref())?;
let hash = &hasher.finish()?;
self.hash.copy_from_slice(hash.as_ref());
Ok(())
}
}
#[repr(C)]
#[derive(PartialEq, Debug)]
pub struct ReportReqExt {
pub data: [u8; 64],
pub mnonce: [u8; 16],
pub hash: [u8; 32],
pub magic: [u8; 16],
pub flags: u32,
}
impl Default for ReportReqExt {
fn default() -> Self {
Self {
data: [0u8; 64],
mnonce: [0u8; 16],
hash: [0u8; 32],
magic: ATTESTATION_EXT_MAGIC,
flags: 0,
}
}
}
impl ReportReqExt {
pub fn new(data: Option<[u8; 64]>, mnonce: [u8; 16], flags: u32) -> Result<Self, Error> {
let mut request = Self::default();
if let Some(data) = data {
request.data = data;
}
request.mnonce = mnonce;
request.calculate_hash()?;
request.flags = flags;
Ok(request)
}
fn calculate_hash(&mut self) -> Result<(), Error> {
let mut hasher = Hasher::new(MessageDigest::sm3())?;
hasher.update(self.data.as_ref())?;
hasher.update(self.mnonce.as_ref())?;
let hash = &hasher.finish()?;
self.hash.copy_from_slice(hash.as_ref());
Ok(())
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct AttestationReportWrapper {
magic: [u8; 16],
flags: u32,
#[serde(with = "BigArray")]
data: [u8; 4096],
}
impl AttestationReportWrapper {
pub fn new(magic: [u8; 16], flags: u32, report: &mut [u8]) -> Self {
let mut bytes = [0u8; 4096];
let copy_len = std::cmp::min(report.len(), 4096);
bytes[..copy_len].copy_from_slice(&report[..copy_len]);
Self {
magic,
flags,
data: bytes,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub enum AttestationReport {
V1(AttestationReportV1),
V2(AttestationReportV2),
}
impl TryFrom<&AttestationReportWrapper> for AttestationReport {
type Error = std::io::Error;
fn try_from(report_wrapper: &AttestationReportWrapper) -> Result<Self, Self::Error> {
match (report_wrapper.magic, report_wrapper.flags) {
(magic, _) if magic == *b"\0".repeat(16) => {
let report_v1: AttestationReportV1 = TryFrom::try_from(&report_wrapper.data[..])?;
Ok(AttestationReport::V1(report_v1))
}
(ATTESTATION_EXT_MAGIC, 0) => {
let report_v1: AttestationReportV1 = TryFrom::try_from(&report_wrapper.data[..])?;
Ok(AttestationReport::V1(report_v1))
}
(ATTESTATION_EXT_MAGIC, 1) => {
let report_v2: AttestationReportV2 = TryFrom::try_from(&report_wrapper.data[..])?;
Ok(AttestationReport::V2(report_v2))
}
_ => Err(std::io::Error::new(
std::io::ErrorKind::Other,
"Invalid AttestationReport".to_string(),
)),
}
}
}
impl AttestationReport {
pub fn version(&self) -> u32 {
match self {
Self::V1(_) => 1,
Self::V2(_) => 2,
}
}
pub fn tee_info(&self) -> TeeInfo<'_> {
match self {
Self::V1(report) => TeeInfo::V1(&report.tee_info),
Self::V2(report) => TeeInfo::V2(&report.tee_info),
}
}
pub fn signer(&self) -> &TeeInfoSigner {
match self {
Self::V1(report) => &report.signer,
Self::V2(report) => &report.signer,
}
}
}
#[repr(C)]
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct AttestationReportV1 {
pub tee_info: TeeInfoV1,
pub signer: TeeInfoSigner,
#[serde(with = "BigArray")]
reserved:
[u8; 4096 - (std::mem::size_of::<TeeInfoV1>() + std::mem::size_of::<TeeInfoSigner>())],
}
const_assert!(std::mem::size_of::<AttestationReportV1>() == 4096);
impl Default for AttestationReportV1 {
fn default() -> Self {
Self {
tee_info: Default::default(),
signer: Default::default(),
reserved: [0u8; 4096
- (std::mem::size_of::<TeeInfoV1>() + std::mem::size_of::<TeeInfoSigner>())],
}
}
}
impl TryFrom<&[u8]> for AttestationReportV1 {
type Error = std::io::Error;
fn try_from(bytes: &[u8]) -> Result<Self, Self::Error> {
bincode::deserialize(bytes).map_err(|e| {
std::io::Error::new(
std::io::ErrorKind::InvalidData,
format!("Failed to deserialize AttestationReportV1: {}", e),
)
})
}
}
#[repr(C)]
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct AttestationReportV2 {
pub tee_info: TeeInfoV2,
pub signer: TeeInfoSigner,
#[serde(with = "BigArray")]
reserved:
[u8; 4096 - (std::mem::size_of::<TeeInfoV2>() + std::mem::size_of::<TeeInfoSigner>())],
}
const_assert!(std::mem::size_of::<AttestationReportV2>() == 4096);
impl Default for AttestationReportV2 {
fn default() -> Self {
Self {
tee_info: Default::default(),
signer: Default::default(),
reserved: [0u8; 4096
- (std::mem::size_of::<TeeInfoV2>() + std::mem::size_of::<TeeInfoSigner>())],
}
}
}
impl TryFrom<&[u8]> for AttestationReportV2 {
type Error = std::io::Error;
fn try_from(bytes: &[u8]) -> Result<Self, Self::Error> {
bincode::deserialize(bytes).map_err(|e| {
std::io::Error::new(
std::io::ErrorKind::InvalidData,
format!("Failed to deserialize AttestationReportV2: {}", e),
)
})
}
}
pub enum TeeInfo<'a> {
V1(&'a TeeInfoV1),
V2(&'a TeeInfoV2),
}
impl TeeInfo<'_> {
pub fn raw(&self, data: &[u8]) -> Vec<u8> {
let mut data_bytes = data.to_vec();
let anonce_bytes = self.anonce().to_le_bytes();
for (index, item) in data_bytes.iter_mut().enumerate() {
*item ^= anonce_bytes[index % 4];
}
data_bytes
}
pub fn user_pubkey_digest(&self) -> Vec<u8> {
match *self {
Self::V1(tee_info) => self.raw(&tee_info.user_pubkey_digest[..]),
Self::V2(tee_info) => tee_info.user_pubkey_digest.to_vec(),
}
}
pub fn vm_id(&self) -> Vec<u8> {
match *self {
Self::V1(tee_info) => self.raw(&tee_info.vm_id[..]),
Self::V2(tee_info) => tee_info.vm_id.to_vec(),
}
}
pub fn vm_version(&self) -> Vec<u8> {
match *self {
Self::V1(tee_info) => self.raw(&tee_info.vm_version[..]),
Self::V2(tee_info) => tee_info.vm_version.to_vec(),
}
}
pub fn report_data(&self) -> Vec<u8> {
match *self {
Self::V1(tee_info) => self.raw(&tee_info.report_data[..]),
Self::V2(tee_info) => tee_info.report_data.to_vec(),
}
}
pub fn mnonce(&self) -> Vec<u8> {
match *self {
Self::V1(tee_info) => self.raw(&tee_info.mnonce[..]),
Self::V2(tee_info) => tee_info.mnonce.to_vec(),
}
}
pub fn measure(&self) -> Vec<u8> {
match *self {
Self::V1(tee_info) => self.raw(&tee_info.measure[..]),
Self::V2(tee_info) => tee_info.measure.to_vec(),
}
}
pub fn policy(&self) -> GuestPolicy {
match *self {
Self::V1(tee_info) => tee_info.policy.xor(&self.anonce()),
Self::V2(tee_info) => tee_info.policy,
}
}
pub fn sig_usage(&self) -> u32 {
match *self {
Self::V1(tee_info) => tee_info.sig_usage ^ self.anonce(),
Self::V2(tee_info) => tee_info.sig_usage,
}
}
pub fn sig_algo(&self) -> u32 {
match *self {
Self::V1(tee_info) => tee_info.sig_algo ^ self.anonce(),
Self::V2(tee_info) => tee_info.sig_algo,
}
}
pub fn anonce(&self) -> u32 {
match *self {
Self::V1(tee_info) => tee_info.anonce,
Self::V2(_) => 0,
}
}
pub fn build(&self) -> u32 {
match *self {
Self::V1(_) => 0,
Self::V2(tee_info) => tee_info.build,
}
}
pub fn rtmr_version(&self) -> u16 {
match *self {
Self::V1(_) => 0,
Self::V2(tee_info) => tee_info.rtmr_version,
}
}
pub fn rtmr0(&self) -> &[u8] {
match *self {
Self::V1(_) => &[0u8; CSV_RTMR_REG_SIZE],
Self::V2(tee_info) => &tee_info.rtmr0,
}
}
pub fn rtmr1(&self) -> &[u8] {
match *self {
Self::V1(_) => &[0u8; CSV_RTMR_REG_SIZE],
Self::V2(tee_info) => &tee_info.rtmr1,
}
}
pub fn rtmr2(&self) -> &[u8] {
match *self {
Self::V1(_) => &[0u8; CSV_RTMR_REG_SIZE],
Self::V2(tee_info) => &tee_info.rtmr2,
}
}
pub fn rtmr3(&self) -> &[u8] {
match *self {
Self::V1(_) => &[0u8; CSV_RTMR_REG_SIZE],
Self::V2(tee_info) => &tee_info.rtmr3,
}
}
pub fn rtmr4(&self) -> &[u8] {
match *self {
Self::V1(_) => &[0u8; CSV_RTMR_REG_SIZE],
Self::V2(tee_info) => &tee_info.rtmr4,
}
}
}
impl<'a> TryFrom<&'a TeeInfo<'a>> for Signature {
type Error = std::io::Error;
#[inline]
fn try_from(value: &'a TeeInfo<'a>) -> Result<Self, std::io::Error> {
match *value {
TeeInfo::V1(v1) => {
let sig = Vec::try_from(&v1.sig)?;
Ok(Self {
sig,
id: None,
usage: Usage::PEK,
algo: None,
})
}
TeeInfo::V2(v2) => {
let sig = Vec::try_from(&v2.sig)?;
Ok(Self {
sig,
id: None,
usage: Usage::PEK,
algo: None,
})
}
}
}
}
impl<'a> Verifiable for (&'a Certificate, &'a TeeInfo<'a>) {
type Output = ();
fn verify(self) -> Result<(), std::io::Error> {
let (cert, tee_info) = self;
let key: PublicKey = cert.try_into()?;
let sig: Signature = tee_info.try_into()?;
match *tee_info {
TeeInfo::V1(v1) => key.verify(
v1,
&self.0.body.data.user_id[..self.0.body.data.uid_size as usize],
&sig,
),
TeeInfo::V2(v2) => key.verify(
v2,
&self.0.body.data.user_id[..self.0.body.data.uid_size as usize],
&sig,
),
}
}
}
#[repr(C)]
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct TeeInfoV1 {
pub user_pubkey_digest: [u8; 32],
pub vm_id: [u8; 16],
pub vm_version: [u8; 16],
#[serde(with = "BigArray")]
pub report_data: [u8; 64],
pub mnonce: [u8; 16],
pub measure: [u8; 32],
pub policy: GuestPolicy,
pub sig_usage: u32,
pub sig_algo: u32,
pub anonce: u32,
pub sig: ecdsa::Signature,
}
const_assert!(std::mem::size_of::<TeeInfoV1>() == 0x150);
impl Default for TeeInfoV1 {
fn default() -> Self {
Self {
user_pubkey_digest: Default::default(),
vm_id: Default::default(),
vm_version: Default::default(),
report_data: [0u8; 64],
mnonce: Default::default(),
measure: Default::default(),
policy: Default::default(),
sig_usage: Default::default(),
sig_algo: Default::default(),
anonce: Default::default(),
sig: Default::default(),
}
}
}
impl codicon::Encoder<crate::Body> for TeeInfoV1 {
type Error = std::io::Error;
fn encode(&self, mut writer: impl Write, _: crate::Body) -> Result<(), std::io::Error> {
writer.save(&self.user_pubkey_digest)?;
writer.save(&self.vm_id)?;
writer.save(&self.vm_version)?;
writer.save(&self.report_data)?;
writer.save(&self.mnonce)?;
writer.save(&self.measure)?;
writer.save(&self.policy)?;
Ok(())
}
}
#[repr(C)]
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct TeeInfoV2 {
pub user_pubkey_digest: [u8; 32],
pub vm_id: [u8; 16],
pub vm_version: [u8; 16],
#[serde(with = "BigArray")]
pub report_data: [u8; 64],
pub mnonce: [u8; 16],
pub measure: [u8; 32],
pub policy: GuestPolicy,
pub sig_usage: u32,
pub sig_algo: u32,
pub build: u32,
pub rtmr_version: u16,
pub reserved0: [u8; 14],
pub rtmr0: [u8; CSV_RTMR_REG_SIZE],
pub rtmr1: [u8; CSV_RTMR_REG_SIZE],
pub rtmr2: [u8; CSV_RTMR_REG_SIZE],
pub rtmr3: [u8; CSV_RTMR_REG_SIZE],
pub rtmr4: [u8; CSV_RTMR_REG_SIZE],
#[serde(with = "BigArray")]
pub reserved1: [u8; 656],
pub sig: ecdsa::Signature,
}
const_assert!(std::mem::size_of::<TeeInfoV2>() == 0x490);
impl Default for TeeInfoV2 {
fn default() -> Self {
Self {
user_pubkey_digest: Default::default(),
vm_id: Default::default(),
vm_version: Default::default(),
report_data: [0u8; 64],
mnonce: Default::default(),
measure: Default::default(),
policy: Default::default(),
sig_usage: Default::default(),
sig_algo: Default::default(),
build: Default::default(),
rtmr_version: Default::default(),
reserved0: Default::default(),
rtmr0: [0u8; CSV_RTMR_REG_SIZE],
rtmr1: [0u8; CSV_RTMR_REG_SIZE],
rtmr2: [0u8; CSV_RTMR_REG_SIZE],
rtmr3: [0u8; CSV_RTMR_REG_SIZE],
rtmr4: [0u8; CSV_RTMR_REG_SIZE],
reserved1: [0u8; 656],
sig: Default::default(),
}
}
}
impl codicon::Encoder<crate::Body> for TeeInfoV2 {
type Error = std::io::Error;
fn encode(&self, mut writer: impl Write, _: crate::Body) -> Result<(), std::io::Error> {
writer.save(&self.user_pubkey_digest)?;
writer.save(&self.vm_id)?;
writer.save(&self.vm_version)?;
writer.save(&self.report_data)?;
writer.save(&self.mnonce)?;
writer.save(&self.measure)?;
writer.save(&self.policy)?;
writer.save(&self.sig_usage)?;
writer.save(&self.sig_algo)?;
writer.save(&self.build)?;
writer.save(&self.rtmr_version)?;
writer.save(&self.reserved0)?;
writer.save(&self.rtmr0)?;
writer.save(&self.rtmr1)?;
writer.save(&self.rtmr2)?;
writer.save(&self.rtmr3)?;
writer.save(&self.rtmr4)?;
writer.save(&self.reserved1)?;
Ok(())
}
}
bitfield! {
#[repr(C)]
#[derive(Copy, Clone, PartialEq, Eq, Serialize, Deserialize, Default)]
pub struct GuestPolicy(u32);
impl Debug;
pub nodbg, _: 0, 0;
pub noks, _: 1, 1;
pub es, _: 2, 2;
pub nosend, _: 3, 3;
pub domain, _: 4, 4;
pub csv, _: 5, 5;
pub csv3, _: 6, 6;
pub asid_reuse, _: 7, 7;
pub hsk_version, _: 11, 8;
pub cek_version, _: 15, 12;
pub api_major, _: 23, 16;
pub api_minor, _: 31, 24;
}
impl GuestPolicy {
#[allow(dead_code)]
pub fn xor(&self, anonce: &u32) -> Self {
Self(self.0 ^ anonce)
}
}
#[repr(C)]
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct TeeInfoSigner {
#[serde(with = "BigArray")]
pub pek_cert: [u8; 2084],
#[serde(with = "BigArray")]
pub sn: [u8; 64],
pub reserved: [u8; 32],
pub mac: [u8; 32],
}
fn xor_with_anonce(data: &mut [u8], anonce: &u32) -> Result<(), Error> {
let mut anonce_array = [0u8; 4];
anonce_array[..].copy_from_slice(&anonce.to_le_bytes());
for (index, item) in data.iter_mut().enumerate() {
*item ^= anonce_array[index % 4];
}
Ok(())
}
impl TeeInfoSigner {
pub fn verify(
&mut self,
input_mnonce: &[u8],
mnonce: &[u8],
anonce: &u32,
) -> Result<(), Error> {
let mut real_mnonce = Vec::from(mnonce);
xor_with_anonce(&mut real_mnonce, anonce)?;
if real_mnonce != input_mnonce {
return Err(Error::BadSignature);
}
let key = pkey::PKey::hmac(&real_mnonce)?;
let mut sig = sign::Signer::new(MessageDigest::sm3(), &key)?;
sig.update(&self.pek_cert)?;
sig.update(&self.sn)?;
sig.update(&self.reserved)?;
if sig.sign_to_vec()? != self.mac {
return Err(Error::BadSignature);
}
self.restore(anonce)?;
Ok(())
}
fn restore(&mut self, anonce: &u32) -> Result<(), Error> {
xor_with_anonce(&mut self.pek_cert, anonce)?;
xor_with_anonce(&mut self.sn, anonce)?;
self.reserved.fill(0);
Ok(())
}
}
impl Default for TeeInfoSigner {
fn default() -> Self {
Self {
pek_cert: [0u8; 2084],
sn: [0u8; 64],
reserved: Default::default(),
mac: Default::default(),
}
}
}
#[cfg(test)]
mod test {
mod report_req {
use crate::api::guest::types::ReportReq;
#[test]
pub fn test_new() {
let data: [u8; 64] = [
103, 198, 105, 115, 81, 255, 74, 236, 41, 205, 186, 171, 242, 251, 227, 70, 124,
194, 84, 248, 27, 232, 231, 141, 118, 90, 46, 99, 51, 159, 201, 154, 102, 50, 13,
183, 49, 88, 163, 90, 37, 93, 5, 23, 88, 233, 94, 212, 171, 178, 205, 198, 155,
180, 84, 17, 14, 130, 116, 65, 33, 61, 220, 135,
];
let mnonce: [u8; 16] = [
112, 233, 62, 161, 65, 225, 252, 103, 62, 1, 126, 151, 234, 220, 107, 150,
];
let hash: [u8; 32] = [
19, 76, 8, 98, 33, 246, 247, 155, 28, 21, 245, 185, 118, 74, 162, 128, 82, 15, 160,
233, 212, 130, 106, 177, 89, 6, 119, 243, 130, 21, 3, 153,
];
let expected: ReportReq = ReportReq { data, mnonce, hash };
let actual: ReportReq = ReportReq::new(Some(data), mnonce).unwrap();
assert_eq!(expected, actual);
}
#[test]
#[should_panic]
pub fn test_new_error() {
let data: [u8; 64] = [
103, 198, 105, 115, 81, 255, 74, 236, 41, 205, 186, 171, 242, 251, 227, 70, 124,
194, 84, 248, 27, 232, 231, 141, 118, 90, 46, 99, 51, 159, 201, 154, 102, 50, 13,
183, 49, 88, 163, 90, 37, 93, 5, 23, 88, 233, 94, 212, 171, 178, 205, 198, 155,
180, 84, 17, 14, 130, 116, 65, 33, 61, 220, 135,
];
let mnonce: [u8; 16] = [
112, 233, 62, 161, 65, 225, 252, 103, 62, 1, 126, 151, 234, 220, 107, 150,
];
let wrong_mnonce: [u8; 16] = [
0, 233, 62, 161, 65, 225, 252, 103, 62, 1, 126, 151, 234, 220, 107, 150,
];
let hash: [u8; 32] = [
19, 76, 8, 98, 33, 246, 247, 155, 28, 21, 245, 185, 118, 74, 162, 128, 82, 15, 160,
233, 212, 130, 106, 177, 89, 6, 119, 243, 130, 21, 3, 153,
];
let expected: ReportReq = ReportReq { data, mnonce, hash };
let actual: ReportReq = ReportReq::new(Some(data), wrong_mnonce).unwrap();
assert_eq!(expected, actual);
}
}
}