use crate::{
ErrorCode, InternalError,
json::JsonValue,
memory::{CBinaryHolder, CStringHolder},
};
use std::{ffi::c_void, num::NonZeroUsize};
const ERR_INVALID_ARGUMENTS: ErrorCode = ErrorCode::InternalError(InternalError::InvalidArguments);
const ERR_INCOMPLETE_RESULT: ErrorCode = ErrorCode::InternalError(InternalError::IncompleteResult);
#[derive(Debug)]
#[non_exhaustive]
pub struct TctiOpaqueContextBlob(pub *mut c_void);
#[derive(Debug)]
#[non_exhaustive]
pub struct FapiPollHandle();
macro_rules! not_empty {
($value:ident) => {
(!$value.is_empty())
};
}
macro_rules! opt_check {
($value:ident) => {
$value.as_ref().is_none_or(|inner| not_empty!(inner))
};
}
#[derive(Clone, Copy, Debug)]
#[non_exhaustive]
pub enum ImportData<'a> {
#[non_exhaustive]
Pem(&'a str),
#[non_exhaustive]
Json(&'a JsonValue),
}
impl<'a> ImportData<'a> {
pub fn from_pem(pem_data: &'a str) -> Result<Self, ErrorCode> {
let pem_trimmed = pem_data.trim_ascii_start();
if pem_trimmed.starts_with("-----BEGIN PUBLIC KEY-----")
|| pem_trimmed.starts_with("-----BEGIN PRIVATE KEY-----")
|| pem_trimmed.starts_with("-----BEGIN RSA PRIVATE KEY-----")
|| pem_trimmed.starts_with("-----BEGIN EC PRIVATE KEY-----")
{
Ok(Self::Pem(pem_trimmed))
} else {
Err(ERR_INVALID_ARGUMENTS)
}
}
pub fn from_json(json_value: &'a JsonValue) -> Result<Self, ErrorCode> {
if json_value.is_object() && (!json_value.is_empty()) {
Ok(Self::Json(json_value))
} else {
Err(ErrorCode::InternalError(InternalError::InvalidArguments))
}
}
pub fn as_pem(&self) -> Option<&str> {
match self {
Self::Pem(pem_data) => Some(pem_data),
_ => None,
}
}
pub fn as_json(&self) -> Option<&JsonValue> {
match self {
Self::Json(json_value) => Some(json_value),
_ => None,
}
}
}
impl TryFrom<ImportData<'_>> for CStringHolder {
type Error = ErrorCode;
fn try_from(data: ImportData) -> Result<Self, Self::Error> {
match data {
ImportData::Json(json_value) => CStringHolder::try_from(json_value),
ImportData::Pem(pem_data) => CStringHolder::try_from(pem_data),
}
}
}
pub type RawSealInfo = (NonZeroUsize, CBinaryHolder);
#[derive(Clone, Copy, Debug)]
#[non_exhaustive]
pub enum SealedData<'a> {
#[non_exhaustive]
Data(&'a [u8]),
#[non_exhaustive]
Size(NonZeroUsize),
}
impl<'a> SealedData<'a> {
pub fn from_size(size: usize) -> Result<Self, ErrorCode> {
Ok(Self::Size(NonZeroUsize::new(size).ok_or(ERR_INVALID_ARGUMENTS)?))
}
pub fn from_data(data: &'a [u8]) -> Result<Self, ErrorCode> {
if !data.is_empty() { Ok(Self::Data(data)) } else { Err(ERR_INVALID_ARGUMENTS) }
}
pub(crate) fn into_raw_data(self) -> Result<RawSealInfo, ErrorCode> {
match self {
Self::Size(size) => Ok((size, CBinaryHolder::empty())),
Self::Data(data) => {
let cstr_data = CBinaryHolder::try_from(data)?;
let cstr_size = NonZeroUsize::new(cstr_data.len()).expect("Size must not be zero!");
Ok((cstr_size, cstr_data))
}
}
}
pub fn as_data(&self) -> Option<&[u8]> {
match self {
Self::Data(data) => Some(data),
_ => None,
}
}
pub fn as_size(&self) -> Option<NonZeroUsize> {
match self {
Self::Size(size) => Some(*size),
_ => None,
}
}
}
#[non_exhaustive]
pub struct SignResult {
pub sign_value: Vec<u8>,
pub public_key: Option<String>,
pub certificate: Option<String>,
}
impl SignResult {
pub fn from(sign_value: Vec<u8>, public_key: Option<String>, certificate: Option<String>) -> Result<Self, ErrorCode> {
if not_empty!(sign_value) && opt_check!(public_key) && opt_check!(certificate) {
Ok(Self { sign_value, public_key, certificate })
} else {
Err(ERR_INCOMPLETE_RESULT)
}
}
}
#[non_exhaustive]
pub struct QuoteResult {
pub quote_info: JsonValue,
pub signature: Vec<u8>,
pub prc_log: Option<JsonValue>,
pub certificate: Option<String>,
}
impl QuoteResult {
pub fn from(quote_info: JsonValue, signature: Vec<u8>, prc_log: Option<JsonValue>, certificate: Option<String>) -> Result<Self, ErrorCode> {
if not_empty!(quote_info) && not_empty!(signature) && opt_check!(prc_log) && opt_check!(certificate) {
Ok(Self { quote_info, signature, prc_log, certificate })
} else {
Err(ERR_INCOMPLETE_RESULT)
}
}
}
#[non_exhaustive]
pub struct TpmBlobs {
pub public_key: Option<Vec<u8>>,
pub private_key: Option<Vec<u8>>,
pub policy: Option<JsonValue>,
}
impl TpmBlobs {
pub fn from(public_key: Option<Vec<u8>>, private_key: Option<Vec<u8>>, policy: Option<JsonValue>) -> Result<Self, ErrorCode> {
if opt_check!(public_key) && opt_check!(private_key) && opt_check!(policy) {
Ok(Self { public_key, private_key, policy })
} else {
Err(ERR_INCOMPLETE_RESULT)
}
}
}
#[cfg(test)]
mod tests {
use super::{ImportData, SealedData};
use json::JsonValue;
use std::sync::LazyLock;
#[test]
fn test_import_pem() {
for &id in &["PUBLIC KEY", "PRIVATE KEY", "RSA PRIVATE KEY", "EC PRIVATE KEY"] {
let pem_data = format!("-----BEGIN {id}-----\nMAAA");
let import_data = ImportData::from_pem(&pem_data).unwrap();
assert_eq!(import_data.as_json(), None);
assert_eq!(import_data.as_pem().unwrap(), pem_data);
}
}
#[test]
fn test_import_json() {
static JSON_VALUE: LazyLock<JsonValue> = LazyLock::new(|| {
let mut json_value = JsonValue::new_object();
json_value.insert("foo", JsonValue::String("bar".to_owned())).unwrap();
json_value
});
let import_data = ImportData::from_json(&JSON_VALUE).unwrap();
assert_eq!(import_data.as_pem(), None);
assert_eq!(import_data.as_json().unwrap(), &*JSON_VALUE);
}
#[test]
fn test_import_invalid() {
assert!(ImportData::from_pem("thingamabob").is_err());
assert!(ImportData::from_json(&JsonValue::new_object()).is_err());
}
#[test]
fn test_sealed_size() {
let sealed_data = SealedData::from_size(42usize).unwrap();
assert_eq!(sealed_data.as_data(), None);
assert_eq!(sealed_data.as_size().unwrap().get(), 42usize);
}
#[test]
fn test_sealed_data() {
const SEALED_DATA: &[u8] = b"thingamabob";
let sealed_data = SealedData::from_data(SEALED_DATA).unwrap();
assert_eq!(sealed_data.as_size(), None);
assert_eq!(sealed_data.as_data().unwrap(), SEALED_DATA);
}
#[test]
fn test_sealed_invalid() {
assert!(SealedData::from_size(0usize).is_err());
assert!(SealedData::from_data(b"").is_err());
}
}