Skip to main content

deaddrop_core/receipt/
mod.rs

1use 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    /// Optional peer this receipt attests a forward toward. Absent on older receipts.
12    #[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    /// Compact signed statement: issuer possessed `object_id` and forwarded toward `to`.
39    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}