deaddrop_core/receipt/
mod.rs1use crate::crypto::{CryptoProvider, DefaultProvider, PrivateIdentity};
2use crate::{ObjectId, PeerId, ReceiptKind, Result};
3use serde::{Deserialize, Serialize};
4
5#[derive(Debug, Clone, Serialize, Deserialize)]
6pub struct Receipt {
7 pub kind: ReceiptKind,
8 pub object_id: ObjectId,
9 pub issuer: PeerId,
10 pub created_at: u64,
11 #[serde(default, skip_serializing_if = "Option::is_none")]
13 pub witness_peer: Option<PeerId>,
14 #[serde(with = "serde_bytes")]
15 pub signature: [u8; 64],
16}
17
18impl Receipt {
19 pub fn issue(
20 kind: ReceiptKind,
21 object_id: ObjectId,
22 issuer: &PrivateIdentity,
23 now: u64,
24 ) -> Self {
25 let mut msg = preimage(kind, &object_id, issuer.peer_id, now, None);
26 let signature = DefaultProvider.sign(&issuer.signing_key(), &msg);
27 msg.clear();
28 Self {
29 kind,
30 object_id,
31 issuer: issuer.peer_id,
32 created_at: now,
33 witness_peer: None,
34 signature,
35 }
36 }
37
38 pub fn issue_forwarded(
40 object_id: ObjectId,
41 issuer: &PrivateIdentity,
42 to: PeerId,
43 now: u64,
44 ) -> Self {
45 let mut msg = preimage(
46 ReceiptKind::Forwarded,
47 &object_id,
48 issuer.peer_id,
49 now,
50 Some(to),
51 );
52 let signature = DefaultProvider.sign(&issuer.signing_key(), &msg);
53 msg.clear();
54 Self {
55 kind: ReceiptKind::Forwarded,
56 object_id,
57 issuer: issuer.peer_id,
58 created_at: now,
59 witness_peer: Some(to),
60 signature,
61 }
62 }
63
64 pub fn verify(&self, issuer_pk: &[u8; 32]) -> Result<()> {
65 let msg = preimage(
66 self.kind,
67 &self.object_id,
68 self.issuer,
69 self.created_at,
70 self.witness_peer,
71 );
72 DefaultProvider.verify(issuer_pk, &msg, &self.signature)
73 }
74}
75
76fn preimage(
77 kind: ReceiptKind,
78 object_id: &ObjectId,
79 issuer: PeerId,
80 ts: u64,
81 witness: Option<PeerId>,
82) -> Vec<u8> {
83 let mut m = Vec::from(&b"ddp-receipt-v2"[..]);
84 m.push(kind as u8);
85 m.extend_from_slice(object_id.as_bytes());
86 m.extend_from_slice(issuer.as_bytes());
87 m.extend_from_slice(&ts.to_be_bytes());
88 if let Some(p) = witness {
89 m.extend_from_slice(b"ddp-witness-v2");
90 m.extend_from_slice(p.as_bytes());
91 }
92 m
93}
94
95#[cfg(test)]
96mod tests {
97 use super::*;
98 use crate::crypto::PrivateIdentity;
99
100 #[test]
101 fn receipt_sign_verify() {
102 let n = PrivateIdentity::generate();
103 let oid = ObjectId::blake3([9u8; 32]);
104 let r = Receipt::issue(ReceiptKind::Delivered, oid, &n, 42);
105 r.verify(&n.public.ed25519_pk).unwrap();
106 let f = Receipt::issue_forwarded(oid, &n, n.peer_id, 43);
107 f.verify(&n.public.ed25519_pk).unwrap();
108 assert!(f.witness_peer.is_some());
109 }
110}