use alloc::{vec, vec::Vec};
use coset::cbor::Value;
use crate::{Error, Result};
const COSE_SIGN1_TAG: u64 = 18;
pub fn derive_candidate_entry(transparent_statement: &[u8]) -> Result<Vec<u8>> {
let mut cursor = transparent_statement;
let value: Value = coset::cbor::de::from_reader(&mut cursor)
.map_err(|_| Error::Cose("failed to parse Transparent Statement CBOR"))?;
if !cursor.is_empty() {
return Err(Error::Cose("trailing bytes after Transparent Statement"));
}
let array_value = match value {
Value::Tag(tag, inner) => {
if tag != COSE_SIGN1_TAG {
return Err(Error::Cose(
"unsupported tag wrapper; only tag 18 is accepted",
));
}
*inner
}
Value::Array(_) => value,
_ => {
return Err(Error::Cose(
"Transparent Statement must be a COSE_Sign1 array or tag-18 wrapper",
));
}
};
let Value::Array(items) = array_value else {
return Err(Error::Cose(
"Transparent Statement must be a COSE_Sign1 array",
));
};
if items.len() != 4 {
return Err(Error::Cose("COSE_Sign1 must have exactly 4 elements"));
}
let Value::Bytes(protected) = &items[0] else {
return Err(Error::Cose("protected header must be a byte string"));
};
if !matches!(&items[1], Value::Map(_)) {
return Err(Error::Cose("unprotected header must be a map"));
}
let payload = match &items[2] {
Value::Bytes(bytes) => bytes.clone(),
Value::Null => {
return Err(Error::Cose(
"detached payload (null) is not permitted; payload must be present as a byte string",
));
}
_ => return Err(Error::Cose("payload must be a byte string")),
};
let Value::Bytes(signature) = &items[3] else {
return Err(Error::Cose("signature must be a byte string"));
};
let candidate = Value::Array(vec![
Value::Bytes(protected.clone()),
Value::Map(vec![]),
Value::Bytes(payload),
Value::Bytes(signature.clone()),
]);
let mut output = Vec::new();
coset::cbor::ser::into_writer(&candidate, &mut output)
.map_err(|_| Error::Cose("failed to serialize candidate entry"))?;
Ok(output)
}
#[must_use]
pub fn candidate_leaf_hash(candidate_entry: &[u8]) -> [u8; 32] {
crate::receipt::leaf_hash(candidate_entry)
}