diff --git a/Cargo.toml b/Cargo.toml index 96af3fb..6a0e84a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cesride" -version = "0.5.0" +version = "0.6.0" edition = "2021" description = "Cryptographic primitives for use with Composable Event Streaming Representation (CESR)" license = "Apache-2.0" diff --git a/Makefile b/Makefile index b2d4f94..6bcb3d2 100644 --- a/Makefile +++ b/Makefile @@ -12,6 +12,7 @@ clippy: cargo clippy --all-targets -- -D warnings preflight: + cargo generate-lockfile cargo fmt --check cargo outdated -R --exit-code 1 cargo audit diff --git a/src/core/counter/mod.rs b/src/core/counter/mod.rs index 6722954..4d95b34 100644 --- a/src/core/counter/mod.rs +++ b/src/core/counter/mod.rs @@ -303,8 +303,8 @@ impl Counter { Ok(()) } - pub fn full_size(&self) -> Result { - Ok(tables::sizage(&self.code())?.fs) + pub fn full_size(&self) -> Result { + Ok(tables::sizage(&self.code())?.fs as usize) } } @@ -537,7 +537,7 @@ mod test { #[case("-AAB", counter::Codex::ControllerIdxSigs, 4)] #[case("-0VAAAQA", counter::Codex::BigAttachedMaterialQuadlets, 8)] #[case("--AAAAAA", counter::Codex::KERIProtocolStack, 8)] - fn qb_size(#[case] qsc: &str, #[case] code: &str, #[case] full_size: u32) { + fn qb_size(#[case] qsc: &str, #[case] code: &str, #[case] full_size: usize) { let counter = Counter::new(None, None, None, None, Some(qsc), None).unwrap(); assert_eq!(counter.code(), code); // Just a self-check of the input data assert_eq!(counter.full_size().unwrap(), full_size); diff --git a/src/core/creder.rs b/src/core/creder.rs new file mode 100644 index 0000000..33b7962 --- /dev/null +++ b/src/core/creder.rs @@ -0,0 +1,296 @@ +use crate::{ + common::{Identage, Ids, Serialage, Version, CURRENT_VERSION}, + core::matter::tables as matter, + core::sadder::Sadder, + core::saider::Saider, + data::{dat, Value}, + error::{err, Error, Result}, +}; + +#[derive(Clone, Debug, PartialEq)] +pub struct Creder { + code: String, + raw: Vec, + ked: Value, + ident: String, + kind: String, + size: u32, + version: Version, + saider: Saider, +} + +fn validate_ident(ident: &str) -> Result<()> { + if ident != Identage::ACDC { + return err!(Error::Value("creder must be an ACDC".to_string())); + } + + Ok(()) +} + +impl Creder { + pub fn new( + code: Option<&str>, + raw: Option<&[u8]>, + kind: Option<&str>, + ked: Option<&Value>, + sad: Option<&Self>, + ) -> Result { + let code = code.unwrap_or(matter::Codex::Blake3_256); + let creder = Sadder::new(Some(code), raw, kind, ked, sad)?; + validate_ident(&creder.ident())?; + + Ok(creder) + } + + pub fn new_with_ked(ked: &Value, code: Option<&str>, kind: Option<&str>) -> Result { + Self::new(code, None, kind, Some(ked), None) + } + + pub fn new_with_raw(raw: &[u8]) -> Result { + Self::new(None, Some(raw), None, None, None) + } + + pub fn crd(&self) -> Value { + self.ked() + } + + pub fn issuer(&self) -> Result { + self.ked()[Ids::i].to_string() + } + + pub fn schema(&self) -> Result { + self.ked()[Ids::s].to_string() + } + + pub fn subject(&self) -> Value { + self.ked()[Ids::a].clone() + } + + pub fn status(&self) -> Result> { + let map = self.ked().to_map()?; + + if map.contains_key("ri") { + Ok(Some(map["ri"].to_string()?)) + } else { + Ok(None) + } + } + + pub fn chains(&self) -> Result { + let map = self.ked().to_map()?; + + if map.contains_key("e") { + Ok(map["e"].clone()) + } else { + Ok(dat!({})) + } + } +} + +impl Default for Creder { + fn default() -> Self { + Creder { + code: matter::Codex::Blake3_256.to_string(), + raw: vec![], + ked: dat!({}), + ident: Identage::ACDC.to_string(), + kind: Serialage::JSON.to_string(), + size: 0, + version: CURRENT_VERSION.clone(), + saider: Saider::default(), + } + } +} + +impl Sadder for Creder { + fn code(&self) -> String { + self.code.clone() + } + + fn raw(&self) -> Vec { + self.raw.clone() + } + + fn ked(&self) -> Value { + self.ked.clone() + } + + fn ident(&self) -> String { + self.ident.clone() + } + + fn kind(&self) -> String { + self.kind.clone() + } + + fn size(&self) -> u32 { + self.size + } + + fn version(&self) -> Version { + self.version.clone() + } + + fn saider(&self) -> Saider { + self.saider.clone() + } + + fn set_code(&mut self, code: &str) { + self.code = code.to_string(); + } + + fn set_raw(&mut self, raw: &[u8]) { + self.raw = raw.to_vec(); + } + + fn set_ked(&mut self, ked: &Value) { + self.ked = ked.clone(); + } + + fn set_ident(&mut self, ident: &str) { + self.ident = ident.to_string(); + } + + fn set_kind(&mut self, kind: &str) { + self.kind = kind.to_string(); + } + + fn set_size(&mut self, size: u32) { + self.size = size; + } + + fn set_version(&mut self, version: &Version) { + self.version = version.clone(); + } + + fn set_saider(&mut self, saider: &Saider) { + self.saider = saider.clone(); + } +} + +#[cfg(test)] +mod test { + use crate::{ + common::{versify, Identage, Serialage, CURRENT_VERSION}, + Saider, + }; + + use super::{Creder, Sadder}; + + #[test] + fn sanity() { + let keri_value = dat!({ + "v": "KERI10JSON0001e7_", + "t": "icp", + "d": "EA_1ZGv4tEhJW2AYH0wLh2lLlllmH3dwpH3RGs2GtgXr", + "i": "EA_1ZGv4tEhJW2AYH0wLh2lLlllmH3dwpH3RGs2GtgXr", + "s": "0", + "kt": "2", + "k": [ + "DID6gcblxh8yiILkx_tratCNdDiYHWInyrZOF0dHgn-s", + "DJgKrw-dQFtDUZ6ahEzc-HJqe5NOXF_F4xMXy41bvApe", + "DMEijEab-eqt7AEhLyrMcHH8I36HPYOn1rjnvwycPURK" + ], + "nt": "2", + "n": [ + "EP7pWgkdErxn23QcvAH5ovQQrjZgtgc2qv-X79JKctUV", + "ENJus7HZN9Dsm7jHkn8vTC5Wk2VRhjtGQ9NaOa57OydR", + "EMLWypoar99qGWhnlaX_07W8bbqchTILXH96SGbSV42I" + ], + "bt": "0", + "b": [], + "c": [], + "a": [] + }); + + let acdc_value = dat!({ + "v": "ACDC10JSON00022b_", + "d": "ENIcZJXSgLgz5whOszoME4DPe7B93Qltk6n6C6E9YxF2", + "i": "ENayINhHQnx6525EpcTmkvo6ZixiJyiskwkVNbMPohYa", + "ri": "EINZnO3Z30Q7y2oV1sDCQphieRH244-XJFRAbzuFbU7n", + "s": "EE5uDJTq5cc6AEdqbyMpvARUjsK_chNdInf3xyRoCBcT", + "a": { + "d": "EOsCUbK6Ve7qb-h15ljNyvVhLz2rq6iaCcA86AAoeZyX", + "dt": "2023-04-30T00:34:11.853572+00:00" + }, + "e": { + "d": "ECuynR9pRY6A6dWRlc2DTSF7AWY2a-w-6qhx7vd-pWT-", + "acceptedBlock": { + "d": "EOvQJIx58cCC-xB5LIWeApUH80Jxo8WxGNsLb-1HKLcy", + "n": "EE_Wrv2OHqIOptEni3mE3Ckc4C6jO1RvgtxdpDZBiuB0", + "s": "EDiWb-53cI8FBPOpF69LrLCSElNjG-BAChHp2-OsLmbC" + } + } + }); + + let keri_json = keri_value.to_json().unwrap(); + let keri_message = keri_json.as_bytes(); + let acdc_json = acdc_value.to_json().unwrap(); + let acdc_message = acdc_json.as_bytes(); + + assert!(Creder::new_with_raw(keri_message).is_err()); + let result = Creder::new(None, Some(acdc_message), None, None, None); + assert!(result.is_ok()); + let creder = result.unwrap(); + assert!(Creder::new_with_ked(&creder.crd(), None, None).is_ok()); + assert_eq!( + creder.chains().unwrap().to_json().unwrap(), + dat!({ + "d": "ECuynR9pRY6A6dWRlc2DTSF7AWY2a-w-6qhx7vd-pWT-", + "acceptedBlock": { + "d": "EOvQJIx58cCC-xB5LIWeApUH80Jxo8WxGNsLb-1HKLcy", + "n": "EE_Wrv2OHqIOptEni3mE3Ckc4C6jO1RvgtxdpDZBiuB0", + "s": "EDiWb-53cI8FBPOpF69LrLCSElNjG-BAChHp2-OsLmbC" + } + }) + .to_json() + .unwrap() + ); + assert_eq!(creder.issuer().unwrap(), "ENayINhHQnx6525EpcTmkvo6ZixiJyiskwkVNbMPohYa"); + assert_eq!( + creder.subject().to_json().unwrap(), + dat!({ + "d": "EOsCUbK6Ve7qb-h15ljNyvVhLz2rq6iaCcA86AAoeZyX", + "dt": "2023-04-30T00:34:11.853572+00:00" + }) + .to_json() + .unwrap() + ); + assert_eq!(creder.schema().unwrap(), "EE5uDJTq5cc6AEdqbyMpvARUjsK_chNdInf3xyRoCBcT"); + assert_eq!( + creder.status().unwrap().unwrap(), + "EINZnO3Z30Q7y2oV1sDCQphieRH244-XJFRAbzuFbU7n" + ); + + let mut acdc_value = dat!({ + "v": "ACDC10JSON000000_", + "d": "", + "i": "ENayINhHQnx6525EpcTmkvo6ZixiJyiskwkVNbMPohYa", + "s": "EE5uDJTq5cc6AEdqbyMpvARUjsK_chNdInf3xyRoCBcT", + "a": { + "d": "EOsCUbK6Ve7qb-h15ljNyvVhLz2rq6iaCcA86AAoeZyX", + "dt": "2023-04-30T00:34:11.853572+00:00" + }, + }); + + let acdc_json = acdc_value.to_json().unwrap(); + let acdc_message = acdc_json.as_bytes(); + + let vs = + versify(Some(Identage::ACDC), None, None, Some(acdc_message.len() as u32)).unwrap(); + acdc_value["v"] = dat!(&vs); + let (_, acdc_value) = Saider::saidify(&acdc_value, None, None, None, None).unwrap(); + + let acdc_json = acdc_value.to_json().unwrap(); + let acdc_message = acdc_json.as_bytes(); + + let creder = Creder::new_with_raw(acdc_message).unwrap(); + + assert_eq!(creder.status().unwrap(), None); + assert_eq!(creder.chains().unwrap(), dat!({})); + assert_eq!(creder.raw(), acdc_message); + assert_eq!(creder.kind(), Serialage::JSON); + assert_eq!(creder.size(), acdc_message.len() as u32); + assert_eq!(creder.version(), *CURRENT_VERSION); + } +} diff --git a/src/core/indexer/mod.rs b/src/core/indexer/mod.rs index a205717..b59e5a7 100644 --- a/src/core/indexer/mod.rs +++ b/src/core/indexer/mod.rs @@ -630,13 +630,13 @@ pub trait Indexer: Default { Ok(()) } - fn full_size(&self) -> Result { + fn full_size(&self) -> Result { let sizage = tables::sizage(&self.code())?; if sizage.fs != u32::MAX { - Ok(sizage.fs) + Ok(sizage.fs as usize) } else { let cs = sizage.hs + sizage.ss; - Ok(self.index() * 4 + cs) + Ok((self.index() * 4 + cs) as usize) } } } @@ -1026,7 +1026,7 @@ mod test { #[case(indexer::Codex::Ed448, 156)] #[case(indexer::Codex::Ed25519_Big, 92)] #[case(indexer::Codex::Ed448_Big, 160)] - fn raw_size(#[case] code: &str, #[case] full_size: u32) { + fn raw_size(#[case] code: &str, #[case] full_size: usize) { let raw = (0..full_size as usize - code.len()).map(|_| "A").collect::(); let qb64 = [code, &raw].join(""); let indexer = TestIndexer::new(None, None, None, None, None, Some(&qb64), None).unwrap(); diff --git a/src/core/matter/mod.rs b/src/core/matter/mod.rs index 7a2e3b1..8c9e8aa 100644 --- a/src/core/matter/mod.rs +++ b/src/core/matter/mod.rs @@ -504,12 +504,12 @@ pub trait Matter: Default { Ok(()) } - fn full_size(&self) -> Result { + fn full_size(&self) -> Result { let sizage = tables::sizage(&self.code())?; if sizage.fs != u32::MAX { - Ok(sizage.fs) + Ok(sizage.fs as usize) } else { - Ok(sizage.hs + sizage.ss + self.size() * 4) + Ok((sizage.hs + sizage.ss + self.size() * 4) as usize) } } } @@ -794,7 +794,7 @@ mod test { #[rstest] #[case("NAAAAAAAAAAA", 12)] #[case("4AAC-A-1-B-3", 12)] - fn full_size(#[case] qb64: &str, #[case] size: u32) { + fn full_size(#[case] qb64: &str, #[case] size: usize) { let matter = TestMatter::new(None, None, None, Some(qb64), None).unwrap(); assert_eq!(matter.full_size().unwrap(), size); } diff --git a/src/core/mod.rs b/src/core/mod.rs index baac479..a5a89ce 100644 --- a/src/core/mod.rs +++ b/src/core/mod.rs @@ -2,6 +2,7 @@ pub mod bexter; pub mod cigar; pub mod common; pub mod counter; +pub mod creder; pub mod dater; pub mod diger; pub mod indexer; diff --git a/src/core/serder.rs b/src/core/serder.rs index 6f7aa7d..502a5e4 100644 --- a/src/core/serder.rs +++ b/src/core/serder.rs @@ -52,6 +52,10 @@ impl Serder { Self::new(code, None, kind, Some(ked), None) } + pub fn new_with_raw(raw: &[u8]) -> Result { + Self::new(None, Some(raw), None, None, None) + } + pub fn verfers(&self) -> Result> { let mut result: Vec = Vec::new(); let map = self.ked.to_map()?; @@ -289,6 +293,24 @@ pub(crate) mod test { error::{err, Error, Result}, }; + #[test] + fn convenience() { + let _vs = "KERI10JSON000000_"; + let e1 = dat!({ + "v": _vs, + "d": "", + "i": "ABCDEFG", + "s": "0001", + "t": "rot" + }); + let (_, e1) = Saider::saidify(&e1, None, None, None, None).unwrap(); + + let serder = Serder::new_with_ked(&e1, None, None).unwrap(); + let serder2 = Serder::new_with_raw(&serder.raw()).unwrap(); + + assert_eq!(serder.pre().unwrap(), serder2.pre().unwrap()); + } + #[test] fn python_interop() { assert!(Serder::new(None, None, None, None, None).is_err()); diff --git a/src/lib.rs b/src/lib.rs index b478ebd..0fd4041 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -13,6 +13,7 @@ pub use crate::{ cigar::Cigar, common, counter::{tables as counter, Counter}, // This seems like it shoudl be an abstract class + creder::Creder, dater::Dater, diger::Diger, indexer::{tables as indexer, Indexer},