keri/event_parsing/
mod.rs

1use base64::URL_SAFE_NO_PAD;
2use serde::Deserialize;
3use std::convert::TryFrom;
4
5use crate::event::receipt::Receipt;
6use crate::event::sections::seal::{EventSeal, SourceSeal};
7use crate::event::EventMessage;
8use crate::event_message::key_event_message::KeyEvent;
9use crate::event_message::signed_event_message::{
10    Message, SignedEventMessage, SignedNontransferableReceipt, SignedTransferableReceipt,
11};
12use crate::event_parsing::payload_size::PayloadType;
13use crate::prefix::{
14    AttachedSignaturePrefix, BasicPrefix, IdentifierPrefix, Prefix, SelfSigningPrefix,
15};
16
17#[cfg(feature = "query")]
18use crate::query::{
19    query::QueryEvent,
20    reply::{ReplyEvent, SignedReply},
21};
22use crate::{error::Error, event::event_data::EventData};
23
24pub mod attachment;
25pub mod message;
26pub mod payload_size;
27pub mod prefix;
28
29#[derive(Debug, Clone, Deserialize, PartialEq)]
30pub enum Attachment {
31    // Count codes
32    SealSourceCouplets(Vec<SourceSeal>),
33    AttachedSignatures(Vec<AttachedSignaturePrefix>),
34    ReceiptCouplets(Vec<(BasicPrefix, SelfSigningPrefix)>),
35    // Group codes
36    SealSignaturesGroups(Vec<(EventSeal, Vec<AttachedSignaturePrefix>)>),
37    // List of signatures made using keys from last establishment event od identifier of prefix
38    LastEstSignaturesGroups(Vec<(IdentifierPrefix, Vec<AttachedSignaturePrefix>)>),
39    // Frame codes
40    Frame(Vec<Attachment>),
41}
42
43impl Attachment {
44    pub fn to_cesr(&self) -> String {
45        let (payload_type, att_len, serialized_attachment) = match self {
46            Attachment::SealSourceCouplets(sources) => {
47                let serialzied_sources = sources.iter().fold("".into(), |acc, s| {
48                    [acc, Self::pack_sn(s.sn), s.digest.to_str()].join("")
49                });
50
51                (PayloadType::MG, sources.len(), serialzied_sources)
52            }
53            Attachment::SealSignaturesGroups(seals_signatures) => {
54                let serialized_seals =
55                    seals_signatures
56                        .iter()
57                        .fold("".into(), |acc, (seal, sigs)| {
58                            [
59                                acc,
60                                seal.prefix.to_str(),
61                                Self::pack_sn(seal.sn),
62                                seal.event_digest.to_str(),
63                                Attachment::AttachedSignatures(sigs.to_vec()).to_cesr(),
64                            ]
65                            .join("")
66                        });
67                (PayloadType::MF, seals_signatures.len(), serialized_seals)
68            }
69            Attachment::AttachedSignatures(sigs) => {
70                let serialized_sigs = sigs
71                    .iter()
72                    .fold("".into(), |acc, sig| [acc, sig.to_str()].join(""));
73                (PayloadType::MA, sigs.len(), serialized_sigs)
74            }
75            Attachment::ReceiptCouplets(couplets) => {
76                let packed_couplets = couplets.iter().fold("".into(), |acc, (bp, sp)| {
77                    [acc, bp.to_str(), sp.to_str()].join("")
78                });
79
80                (PayloadType::MC, couplets.len(), packed_couplets)
81            }
82            Attachment::LastEstSignaturesGroups(signers) => {
83                let packed_signers = signers.iter().fold("".to_string(), |acc, (signer, sigs)| {
84                    [
85                        acc,
86                        signer.to_str(),
87                        Attachment::AttachedSignatures(sigs.clone()).to_cesr(),
88                    ]
89                    .concat()
90                });
91                (PayloadType::MH, signers.len(), packed_signers)
92            }
93            Attachment::Frame(att) => {
94                let packed_attachments = att
95                    .iter()
96                    .fold("".to_string(), |acc, att| [acc, att.to_cesr()].concat());
97                (
98                    PayloadType::MV,
99                    packed_attachments.len(),
100                    packed_attachments,
101                )
102            }
103        };
104        [
105            payload_type.adjust_with_num(att_len as u16),
106            serialized_attachment,
107        ]
108        .join("")
109    }
110
111    fn pack_sn(sn: u64) -> String {
112        let payload_type = PayloadType::OA;
113        let sn_raw: Vec<u8> = sn.to_be_bytes().into();
114        // Calculate how many zeros are missing to achieve expected base64 string
115        // length. Master code size is expected padding size.
116        let missing_zeros =
117            payload_type.size() / 4 * 3 - payload_type.master_code_size(false) - sn_raw.len();
118        let sn_vec: Vec<u8> = std::iter::repeat(0)
119            .take(missing_zeros)
120            .chain(sn_raw)
121            .collect();
122        [
123            payload_type.to_string(),
124            base64::encode_config(sn_vec, URL_SAFE_NO_PAD),
125        ]
126        .join("")
127    }
128}
129
130#[derive(Clone, Debug, PartialEq)]
131pub struct SignedEventData {
132    pub deserialized_event: EventType,
133    pub attachments: Vec<Attachment>,
134}
135
136#[derive(Clone, Debug, PartialEq)]
137pub enum EventType {
138    KeyEvent(Box<EventMessage<KeyEvent>>),
139    Receipt(EventMessage<Receipt>),
140    #[cfg(feature = "query")]
141    Qry(EventMessage<QueryEvent>),
142    #[cfg(feature = "query")]
143    Rpy(EventMessage<ReplyEvent>),
144}
145
146impl EventType {
147    pub fn serialize(&self) -> Result<Vec<u8>, Error> {
148        match self {
149            EventType::KeyEvent(event) => event.serialize(),
150            EventType::Receipt(rcp) => rcp.serialize(),
151            #[cfg(feature = "query")]
152            EventType::Qry(qry) => qry.serialize(),
153            #[cfg(feature = "query")]
154            EventType::Rpy(rpy) => rpy.serialize(),
155        }
156    }
157}
158
159impl SignedEventData {
160    pub fn to_cesr(&self) -> Result<Vec<u8>, Error> {
161        let attachments = self
162            .attachments
163            .iter()
164            .fold(String::default(), |acc, att| [acc, att.to_cesr()].concat())
165            .as_bytes()
166            .to_vec();
167        Ok([self.deserialized_event.serialize()?, attachments].concat())
168    }
169}
170
171impl From<&SignedEventMessage> for SignedEventData {
172    fn from(ev: &SignedEventMessage) -> Self {
173        let attachments = match ev.delegator_seal.clone() {
174            Some(delegator_seal) => [
175                Attachment::SealSourceCouplets(vec![delegator_seal]),
176                Attachment::AttachedSignatures(ev.signatures.clone()),
177            ]
178            .into(),
179            None => [Attachment::AttachedSignatures(ev.signatures.clone())].into(),
180        };
181
182        SignedEventData {
183            deserialized_event: EventType::KeyEvent(Box::new(ev.event_message.clone())),
184            attachments,
185        }
186    }
187}
188
189impl From<SignedNontransferableReceipt> for SignedEventData {
190    fn from(rcp: SignedNontransferableReceipt) -> SignedEventData {
191        let attachments = [Attachment::ReceiptCouplets(rcp.couplets)].into();
192        SignedEventData {
193            deserialized_event: EventType::Receipt(rcp.body),
194            attachments,
195        }
196    }
197}
198
199impl From<SignedTransferableReceipt> for SignedEventData {
200    fn from(rcp: SignedTransferableReceipt) -> SignedEventData {
201        let attachments = [Attachment::SealSignaturesGroups(vec![(
202            rcp.validator_seal,
203            rcp.signatures,
204        )])]
205        .into();
206        SignedEventData {
207            deserialized_event: EventType::Receipt(rcp.body),
208            attachments,
209        }
210    }
211}
212
213#[cfg(feature = "query")]
214impl From<SignedReply> for SignedEventData {
215    fn from(ev: SignedReply) -> Self {
216        use crate::event_message::signature::Signature;
217        let attachments = vec![match ev.signature.clone() {
218            Signature::Transferable(seal, sig) => {
219                Attachment::SealSignaturesGroups(vec![(seal, sig)])
220            }
221            Signature::NonTransferable(pref, sig) => Attachment::ReceiptCouplets(vec![(pref, sig)]),
222        }];
223
224        SignedEventData {
225            deserialized_event: EventType::Rpy(ev.reply),
226            attachments,
227        }
228    }
229}
230
231impl TryFrom<SignedEventData> for Message {
232    type Error = Error;
233
234    fn try_from(value: SignedEventData) -> Result<Self, Self::Error> {
235        match value.deserialized_event {
236            EventType::KeyEvent(ev) => signed_key_event(*ev, value.attachments),
237            EventType::Receipt(rct) => signed_receipt(rct, value.attachments),
238            #[cfg(feature = "query")]
239            EventType::Qry(qry) => signed_query(qry, value.attachments),
240            #[cfg(feature = "query")]
241            EventType::Rpy(rpy) => signed_reply(rpy, value.attachments),
242        }
243    }
244}
245
246#[cfg(feature = "query")]
247fn signed_reply(
248    rpy: EventMessage<ReplyEvent>,
249    mut attachments: Vec<Attachment>,
250) -> Result<Message, Error> {
251    match attachments
252        .pop()
253        .ok_or_else(|| Error::SemanticError("Missing attachment".into()))?
254    {
255        Attachment::ReceiptCouplets(couplets) => {
256            let signer = couplets[0].0.clone();
257            let signature = couplets[0].1.clone();
258            Ok(Message::KeyStateNotice(SignedReply::new_nontrans(
259                rpy, signer, signature,
260            )))
261        }
262        Attachment::SealSignaturesGroups(data) => {
263            let (seal, sigs) = data
264                // TODO what if more than one?
265                .last()
266                .ok_or_else(|| Error::SemanticError("More than one seal".into()))?
267                .to_owned();
268            Ok(Message::KeyStateNotice(SignedReply::new_trans(
269                rpy, seal, sigs,
270            )))
271        }
272        Attachment::Frame(atts) => signed_reply(rpy, atts),
273        _ => {
274            // Improper payload type
275            Err(Error::SemanticError("Improper payload type".into()))
276        }
277    }
278}
279
280#[cfg(feature = "query")]
281fn signed_query(
282    qry: EventMessage<QueryEvent>,
283    mut attachments: Vec<Attachment>,
284) -> Result<Message, Error> {
285    use crate::query::query::SignedQuery;
286
287    match attachments
288        .pop()
289        .ok_or_else(|| Error::SemanticError("Missing attachment".into()))?
290    {
291        Attachment::LastEstSignaturesGroups(groups) => {
292            let (signer, signatures) = groups[0].clone();
293            Ok(Message::Query(SignedQuery {
294                envelope: qry,
295                signer,
296                signatures,
297            }))
298        }
299        Attachment::Frame(atts) => signed_query(qry, atts),
300        _ => {
301            // Improper payload type
302            Err(Error::SemanticError(
303                "Improper attachments for query message".into(),
304            ))
305        }
306    }
307}
308
309fn signed_key_event(
310    event_message: EventMessage<KeyEvent>,
311    mut attachments: Vec<Attachment>,
312) -> Result<Message, Error> {
313    match event_message.event.get_event_data() {
314        EventData::Dip(_) | EventData::Drt(_) => {
315            let (att1, att2) = (
316                attachments
317                    .pop()
318                    .ok_or_else(|| Error::SemanticError("Missing attachment".into()))?,
319                attachments
320                    .pop()
321                    .ok_or_else(|| Error::SemanticError("Missing attachment".into()))?,
322            );
323
324            let (seals, sigs) = match (att1, att2) {
325                (Attachment::SealSourceCouplets(seals), Attachment::AttachedSignatures(sigs)) => {
326                    Ok((seals, sigs))
327                }
328                (Attachment::AttachedSignatures(sigs), Attachment::SealSourceCouplets(seals)) => {
329                    Ok((seals, sigs))
330                }
331                _ => {
332                    // Improper attachment type
333                    Err(Error::SemanticError("Improper attachment type".into()))
334                }
335            }?;
336            let delegator_seal = match seals.len() {
337                0 => Err(Error::SemanticError("Missing delegator seal".into())),
338                1 => Ok(seals.first().cloned()),
339                _ => Err(Error::SemanticError("Too many seals".into())),
340            };
341
342            Ok(Message::Event(Box::new(SignedEventMessage::new(
343                &event_message,
344                sigs,
345                delegator_seal?,
346            ))))
347        }
348        _ => {
349            let sigs = attachments
350                .first()
351                .cloned()
352                .ok_or_else(|| Error::SemanticError("Missing attachment".into()))?;
353            if let Attachment::AttachedSignatures(sigs) = sigs {
354                Ok(Message::Event(Box::new(SignedEventMessage::new(
355                    &event_message,
356                    sigs.to_vec(),
357                    None,
358                ))))
359            } else {
360                // Improper attachment type
361                Err(Error::SemanticError("Improper attachment type".into()))
362            }
363        }
364    }
365}
366
367fn signed_receipt(
368    event_message: EventMessage<Receipt>,
369    mut attachments: Vec<Attachment>,
370) -> Result<Message, Error> {
371    let att = attachments
372        .pop()
373        .ok_or_else(|| Error::SemanticError("Missing attachment".into()))?;
374    match att {
375        // Should be nontransferable receipt
376        Attachment::ReceiptCouplets(couplets) => {
377            Ok(Message::NontransferableRct(SignedNontransferableReceipt {
378                body: event_message,
379                couplets,
380            }))
381        }
382        Attachment::SealSignaturesGroups(data) => {
383            // Should be transferable receipt
384            let (seal, sigs) = data
385                // TODO what if more than one?
386                .last()
387                .ok_or_else(|| Error::SemanticError("More than one seal".into()))?
388                .to_owned();
389            Ok(Message::TransferableRct(Box::new(
390                SignedTransferableReceipt::new(event_message, seal, sigs),
391            )))
392        }
393        Attachment::Frame(atts) => signed_receipt(event_message, atts),
394        _ => {
395            // Improper payload type
396            Err(Error::SemanticError("Improper payload type".into()))
397        }
398    }
399}
400
401#[test]
402fn test_stream1() {
403    use crate::event_parsing;
404    // taken from KERIPY: tests/core/test_kevery.py#62
405    let stream = br#"{"v":"KERI10JSON000120_","t":"icp","d":"EG4EuTsxPiRM7soX10XXzNsS1KqXKUp8xsQ-kW_tWHoI","i":"DSuhyBcPZEZLK-fcw5tzHn2N46wRCG_ZOoeKtWTOunRA","s":"0","kt":"1","k":["DSuhyBcPZEZLK-fcw5tzHn2N46wRCG_ZOoeKtWTOunRA"],"n":"EPYuj8mq_PYYsoBKkzX1kxSPGYBWaIya3slgCOyOtlqU","bt":"0","b":[],"c":[],"a":[]}-AABAA0aSisI4ZZTH_6JCqsvAsEpuf_Jq6bDbvPWj_eCDnAGbSARqYHipNs-9W7MHnwnMfIXwLpcoJkKGrQ-SiaklhAw"#;
406
407    let parsed = event_parsing::message::signed_message(stream).unwrap().1;
408    let msg = Message::try_from(parsed).unwrap();
409    assert!(matches!(msg, Message::Event(_)));
410
411    match msg {
412        Message::Event(signed_event) => {
413            assert_eq!(
414                signed_event.event_message.serialize().unwrap().len(),
415                signed_event.event_message.serialization_info.size
416            );
417
418            let serialized_again = signed_event.serialize();
419            assert!(serialized_again.is_ok());
420            let stringified = String::from_utf8(serialized_again.unwrap()).unwrap();
421            assert_eq!(stream, stringified.as_bytes())
422        }
423        _ => panic!(),
424    }
425}
426
427#[test]
428fn test_stream2() {
429    use crate::event_parsing;
430    // taken from KERIPY: tests/core/test_eventing.py::test_multisig_digprefix#2256
431    let stream = br#"{"v":"KERI10JSON00017e_","t":"icp","d":"ELYk-z-SuTIeDncLr6GhwVUKnv3n3F1bF18qkXNd2bpk","i":"ELYk-z-SuTIeDncLr6GhwVUKnv3n3F1bF18qkXNd2bpk","s":"0","kt":"2","k":["DSuhyBcPZEZLK-fcw5tzHn2N46wRCG_ZOoeKtWTOunRA","DVcuJOOJF1IE8svqEtrSuyQjGTd2HhfAkt9y2QkUtFJI","DT1iAhBWCkvChxNWsby2J0pJyxBIxbAtbLA0Ljx-Grh8"],"n":"E9izzBkXX76sqt0N-tfLzJeRqj0W56p4pDQ_ZqNCDpyw","bt":"0","b":[],"c":[],"a":[]}-AADAA39j08U7pcU66OPKsaPExhBuHsL5rO1Pjq5zMgt_X6jRbezevis6YBUg074ZNKAGdUwHLqvPX_kse4buuuSUpAQABphobpuQEZ6EhKLhBuwgJmIQu80ZUV1GhBL0Ht47Hsl1rJiMwE2yW7-yi8k3idw2ahlpgdd9ka9QOP9yQmMWGAQACM7yfK1b86p1H62gonh1C7MECDCFBkoH0NZRjHKAEHebvd2_LLz6cpCaqKWDhbM2Rq01f9pgyDTFNLJMxkC-fAQ"#;
432
433    let parsed = event_parsing::message::signed_message(stream).unwrap().1;
434    let msg = Message::try_from(parsed);
435    assert!(msg.is_ok());
436    assert!(matches!(msg, Ok(Message::Event(_))));
437
438    match msg.unwrap() {
439        Message::Event(signed_event) => {
440            assert_eq!(
441                signed_event.event_message.serialize().unwrap().len(),
442                signed_event.event_message.serialization_info.size
443            );
444
445            let serialized_again = signed_event.serialize();
446            assert!(serialized_again.is_ok());
447            let stringified = String::from_utf8(serialized_again.unwrap()).unwrap();
448            assert_eq!(stream, stringified.as_bytes())
449        }
450        _ => panic!(),
451    }
452}
453
454#[test]
455fn test_deserialize_signed_receipt() {
456    use crate::event_parsing::message::signed_message;
457    // Taken from keripy/tests/core/test_eventing.py::test_direct_mode
458    let trans_receipt_event = br#"{"v":"KERI10JSON000091_","t":"rct","d":"EsZuhYAPBDnexP3SOl9YsGvWBrYkjYcRjomUYmCcLAYY","i":"EsZuhYAPBDnexP3SOl9YsGvWBrYkjYcRjomUYmCcLAYY","s":"0"}-FABE7pB5IKuaYh3aIWKxtexyYFhpSjDNTEGSQuxeJbWiylg0AAAAAAAAAAAAAAAAAAAAAAAE7pB5IKuaYh3aIWKxtexyYFhpSjDNTEGSQuxeJbWiylg-AABAAlIts3z2kNyis9l0Pfu54HhVN_yZHEV7NWIVoSTzl5IABelbY8xi7VRyW42ZJvBaaFTGtiqwMOywloVNpG_ZHAQ"#;
459    let parsed_trans_receipt = signed_message(trans_receipt_event).unwrap().1;
460    let msg = Message::try_from(parsed_trans_receipt);
461    assert!(matches!(msg, Ok(Message::TransferableRct(_))));
462    assert!(msg.is_ok());
463
464    // Taken from keripy/core/test_witness.py::test_nonindexed_witness_receipts
465    let nontrans_rcp = br#"{"v":"KERI10JSON000091_","t":"rct","d":"E77aKmmdHtYKuJeBOYWRHbi8C6dYqzG-ESfdvlUAptlo","i":"EHz9RXAr9JiJn-3wkBvsUo1Qq3hvMQPaITxzcfJND8NM","s":"2"}-CABB389hKezugU2LFKiFVbitoHAxXqJh6HQ8Rn9tH7fxd680Bpx_cu_UoMtD0ES-bS9Luh-b2A_AYmM3PmVNfgFrFXls4IE39-_D14dS46NEMqCf0vQmqDcQmhY-UOpgoyFS2Bw"#;
466    let parsed_nontrans_receipt = signed_message(nontrans_rcp).unwrap().1;
467    let msg = Message::try_from(parsed_nontrans_receipt);
468    assert!(msg.is_ok());
469    assert!(matches!(msg, Ok(Message::NontransferableRct(_))));
470
471    // Nontrans receipt with alternative attachment with -B payload type. Not implemented yet.
472    // takien from keripy/tests/core/test_witness.py::test_indexed_witness_reply
473    // let wintess_receipts = r#"{"v":"KERI10JSON000091_","t":"rct","d":"EHz9RXAr9JiJn-3wkBvsUo1Qq3hvMQPaITxzcfJND8NM","i":"EHz9RXAr9JiJn-3wkBvsUo1Qq3hvMQPaITxzcfJND8NM","s":"0"}-BADAAdgQkf11JTyF2WVA1Vji1ZhXD8di4AJsfro-sN_jURM1SUioeOleik7w8lkDldKtg0-Nr1X32V9Q8tk8RvBGxDgABZmkRun-qNliRA8WR2fIUnVeB8eFLF7aLFtn2hb31iW7wYSYafR0kT3fV_r1wNNdjm9dkBw-_2xsxThTGfO5UAwACRGJiRPFe4ClvpqZL3LHcEAeT396WVrYV10EaTdt0trINT8rPbz96deSFT32z3myNPVwLlNcq4FzIaQCooM2HDQ"#;
474    // let msg = signed_message(witness_receipts.as_bytes());
475    // assert!(msg.is_ok());
476}