use super::check_profile;
use crate::authority::indexed::IndexedAuthorityMap;
use crate::cose::CoseSigned;
use crate::error::RejectReason;
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct PcaChallenge {
#[serde(with = "serde_bytes")]
pub next_challenge: Vec<u8>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct PicPcaPayload {
pub profile: String,
pub position: u64,
pub context_of_authority: IndexedAuthorityMap,
pub challenge: PcaChallenge,
}
impl PicPcaPayload {
pub fn new(position: u64, context: IndexedAuthorityMap, next_challenge: Vec<u8>) -> Self {
Self {
profile: crate::PROFILE_0_2.to_string(),
position,
context_of_authority: context,
challenge: PcaChallenge { next_challenge },
}
}
pub fn check_profile(&self) -> Result<(), RejectReason> {
check_profile("pic-pca+cose", &self.profile)
}
pub fn validate(&self) -> Result<(), RejectReason> {
self.check_profile()?;
if self.challenge.next_challenge.is_empty() {
return Err(RejectReason::NextChallengeInvalid);
}
self.context_of_authority.validate()
}
}
pub type PicPcaCose = CoseSigned<PicPcaPayload>;
#[cfg(test)]
mod tests {
use super::*;
use crate::authority::{AuthorityValue, Invariant, LogicalAuthority};
use std::collections::BTreeMap;
fn sample_map() -> IndexedAuthorityMap {
let mut contract = BTreeMap::new();
contract.insert("corporation".into(), AuthorityValue::One("ACME".into()));
let logical = LogicalAuthority::new(
None,
vec![Invariant::new("storage:save", "save", "storage", "*")],
contract,
);
IndexedAuthorityMap::from_logical(&logical).unwrap()
}
#[test]
fn pca_cbor_roundtrip() {
let pca = PicPcaPayload::new(0, sample_map(), b"challenge-0".to_vec());
let mut buf = Vec::new();
ciborium::into_writer(&pca, &mut buf).unwrap();
let decoded: PicPcaPayload = ciborium::from_reader(buf.as_slice()).unwrap();
assert_eq!(pca, decoded);
assert!(decoded.check_profile().is_ok());
}
}