use alloc::vec::Vec;
use serde::{Deserialize, Serialize};
pub const SESSION_CORROBORATION_LABEL: &str = "offline-protocol session corroboration v1";
pub const SESSION_CORROBORATION_NONCE_LEN: usize = 16;
pub const SESSION_CORROBORATION_TAG_LEN: usize = 16;
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct SessionCorroboration {
pub epoch: u64,
pub nonce: Vec<u8>,
pub tag: Vec<u8>,
}
impl SessionCorroboration {
pub fn parse(body: &str) -> Option<Self> {
let parsed: Self = serde_json::from_str(body).ok()?;
(parsed.nonce.len() == SESSION_CORROBORATION_NONCE_LEN
&& parsed.tag.len() == SESSION_CORROBORATION_TAG_LEN)
.then_some(parsed)
}
pub fn to_body(&self) -> Option<alloc::string::String> {
serde_json::to_string(self).ok()
}
}
#[cfg(test)]
mod tests {
use super::*;
use alloc::vec;
#[test]
fn a_body_round_trips() {
let value = SessionCorroboration {
epoch: 1,
nonce: vec![7; SESSION_CORROBORATION_NONCE_LEN],
tag: vec![9; SESSION_CORROBORATION_TAG_LEN],
};
assert_eq!(
SessionCorroboration::parse(&value.to_body().unwrap()),
Some(value)
);
}
#[test]
fn only_a_complete_body_parses() {
assert_eq!(SessionCorroboration::parse(""), None);
assert_eq!(SessionCorroboration::parse("{}"), None);
let short = SessionCorroboration {
epoch: 1,
nonce: vec![7; SESSION_CORROBORATION_NONCE_LEN],
tag: vec![9; SESSION_CORROBORATION_TAG_LEN - 1],
};
assert_eq!(SessionCorroboration::parse(&short.to_body().unwrap()), None);
}
#[test]
fn the_label_is_pinned() {
assert_eq!(
SESSION_CORROBORATION_LABEL,
"offline-protocol session corroboration v1"
);
}
}