use ic_platform::certification::{
Certificate, Delegation, HashTree, empty, fork, labeled, leaf, pruned,
};
use serde::{
Deserialize, Deserializer,
de::{self, DeserializeOwned, IgnoredAny, SeqAccess, Visitor},
};
use std::fmt;
#[cfg(test)]
mod tests;
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct DecodeLimits {
pub bytes: usize,
}
#[derive(Debug, thiserror::Error)]
#[error("IC CBOR decoding failed")]
pub struct ParseError {
source: serde_cbor::Error,
}
#[derive(Debug, thiserror::Error)]
pub enum DecodeError {
#[error("IC CBOR input has {actual} bytes, exceeding {limit}")]
ByteLimit {
actual: usize,
limit: usize,
},
#[error("bytes follow the IC CBOR value")]
TrailingData,
#[error("{0}")]
Cbor(#[source] ParseError),
}
pub fn decode_certificate(bytes: &[u8], limits: DecodeLimits) -> Result<Certificate, DecodeError> {
let wire: CertificateWire = decode(bytes, limits)?;
Ok(Certificate {
tree: wire.tree.0,
signature: wire.signature.0,
delegation: wire.delegation.map(|wire| Delegation {
subnet_id: wire.subnet_id.0,
certificate: wire.certificate.0,
}),
})
}
pub fn decode_hash_tree(bytes: &[u8], limits: DecodeLimits) -> Result<HashTree, DecodeError> {
decode::<OwnedTree>(bytes, limits).map(|tree| tree.0)
}
fn decode<T: DeserializeOwned>(bytes: &[u8], limits: DecodeLimits) -> Result<T, DecodeError> {
if bytes.len() > limits.bytes {
return Err(DecodeError::ByteLimit {
actual: bytes.len(),
limit: limits.bytes,
});
}
let mut decoder = serde_cbor::Deserializer::from_slice(bytes);
let value =
T::deserialize(&mut decoder).map_err(|source| DecodeError::Cbor(ParseError { source }))?;
decoder.end().map_err(|_| DecodeError::TrailingData)?;
Ok(value)
}
#[derive(Deserialize)]
#[serde(deny_unknown_fields)]
struct CertificateWire {
tree: OwnedTree,
signature: ByteString,
#[serde(default)]
delegation: Option<DelegationWire>,
}
#[derive(Deserialize)]
#[serde(deny_unknown_fields)]
struct DelegationWire {
subnet_id: ByteString,
certificate: ByteString,
}
struct ByteString(Vec<u8>);
struct ByteVisitor;
impl<'de> Deserialize<'de> for ByteString {
fn deserialize<D: Deserializer<'de>>(d: D) -> Result<Self, D::Error> {
d.deserialize_any(ByteVisitor)
}
}
impl Visitor<'_> for ByteVisitor {
type Value = ByteString;
fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("CBOR byte string")
}
fn visit_bytes<E: de::Error>(self, bytes: &[u8]) -> Result<Self::Value, E> {
Ok(ByteString(bytes.to_vec()))
}
fn visit_byte_buf<E: de::Error>(self, bytes: Vec<u8>) -> Result<Self::Value, E> {
Ok(ByteString(bytes))
}
}
struct OwnedTree(HashTree);
struct TreeVisitor;
impl<'de> Deserialize<'de> for OwnedTree {
fn deserialize<D: Deserializer<'de>>(d: D) -> Result<Self, D::Error> {
d.deserialize_any(TreeVisitor)
}
}
impl<'de> Visitor<'de> for TreeVisitor {
type Value = OwnedTree;
fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("IC hash-tree array")
}
fn visit_seq<A: SeqAccess<'de>>(self, mut seq: A) -> Result<Self::Value, A::Error> {
let tag: u64 = required(&mut seq)?;
let tree = match tag {
0 => empty(),
1 => fork(
required::<_, OwnedTree>(&mut seq)?.0,
required::<_, OwnedTree>(&mut seq)?.0,
),
2 => labeled(
required::<_, ByteString>(&mut seq)?.0,
required::<_, OwnedTree>(&mut seq)?.0,
),
3 => leaf(required::<_, ByteString>(&mut seq)?.0),
4 => pruned(
<[u8; 32]>::try_from(required::<_, ByteString>(&mut seq)?.0)
.map_err(|_| de::Error::custom("pruned IC digest must have 32 bytes"))?,
),
_ => return Err(de::Error::custom("unknown IC hash-tree tag")),
};
if seq.next_element::<IgnoredAny>()?.is_some() {
return Err(de::Error::custom("extra IC hash-tree child"));
}
Ok(OwnedTree(tree))
}
}
fn required<'de, A: SeqAccess<'de>, T: Deserialize<'de>>(seq: &mut A) -> Result<T, A::Error> {
seq.next_element()?
.ok_or_else(|| de::Error::custom("missing IC hash-tree element"))
}