zlicenser_protocol/evidence/
bundle.rs1use serde::{Deserialize, Serialize};
4
5use crate::{
6 crypto::signature::{Signature, SigningKey, VerifyingKey},
7 wire,
8};
9
10#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
13pub struct EvidenceBundle {
14 pub payload: EvidenceBundlePayload,
15 #[serde(with = "crate::wire::bytes::sig_bytes")]
16 pub vendor_signature: [u8; 64],
17 #[serde(with = "crate::wire::bytes::sig_bytes")]
18 pub customer_signature: [u8; 64],
19}
20
21#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
22pub struct EvidenceBundlePayload {
23 pub protocol_version: u16,
24 pub bundle_id: [u8; 16],
25
26 pub license_request: Vec<u8>,
28 pub license_grant: Vec<u8>,
29 pub receipt: Vec<u8>,
30 pub binding_certificate: Vec<u8>,
31
32 pub terms_hash: [u8; 32],
35 pub consent: ConsentRecord,
36
37 pub payment_reference: String,
39 pub tsa_token: Vec<u8>,
40
41 pub vendor_public_key: [u8; 32],
42 pub customer_public_key: [u8; 32],
43}
44
45#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
47pub struct ConsentRecord {
48 pub checkboxes_ticked: Vec<String>,
49 pub consented_at: u64,
50 pub ip_address: String,
51}
52
53impl EvidenceBundle {
54 pub fn payload_bytes(payload: &EvidenceBundlePayload) -> crate::Result<Vec<u8>> {
56 wire::encode(payload)
57 }
58
59 pub fn sign_vendor(
61 payload: EvidenceBundlePayload,
62 vendor_key: &SigningKey,
63 ) -> crate::Result<PartialBundle> {
64 let payload_bytes = Self::payload_bytes(&payload)?;
65 let sig = vendor_key.sign(&payload_bytes);
66 Ok(PartialBundle {
67 payload,
68 vendor_signature: sig.to_bytes(),
69 })
70 }
71
72 pub fn verify(
74 &self,
75 vendor_vk: &VerifyingKey,
76 customer_vk: &VerifyingKey,
77 ) -> crate::Result<()> {
78 let payload_bytes = Self::payload_bytes(&self.payload)?;
79 vendor_vk.verify(
80 &payload_bytes,
81 &Signature::from_bytes(&self.vendor_signature),
82 )?;
83 customer_vk.verify(
84 &payload_bytes,
85 &Signature::from_bytes(&self.customer_signature),
86 )?;
87 Ok(())
88 }
89
90 pub fn verify_vendor(&self, vendor_vk: &VerifyingKey) -> crate::Result<()> {
92 let payload_bytes = Self::payload_bytes(&self.payload)?;
93 vendor_vk.verify(
94 &payload_bytes,
95 &Signature::from_bytes(&self.vendor_signature),
96 )
97 }
98
99 pub fn to_bytes(&self) -> crate::Result<Vec<u8>> {
100 wire::encode(self)
101 }
102
103 pub fn from_bytes(bytes: &[u8]) -> crate::Result<Self> {
104 wire::decode(bytes)
105 }
106}
107
108pub struct PartialBundle {
110 pub payload: EvidenceBundlePayload,
111 pub vendor_signature: [u8; 64],
112}
113
114impl PartialBundle {
115 pub fn add_customer_signature(
117 self,
118 customer_key: &SigningKey,
119 ) -> crate::Result<EvidenceBundle> {
120 let payload_bytes = EvidenceBundle::payload_bytes(&self.payload)?;
121 let sig = customer_key.sign(&payload_bytes);
122 Ok(EvidenceBundle {
123 payload: self.payload,
124 vendor_signature: self.vendor_signature,
125 customer_signature: sig.to_bytes(),
126 })
127 }
128}
129
130#[cfg(test)]
131mod tests {
132 use super::*;
133 use crate::{crypto::signature::SigningKey, error::Error, message::PROTOCOL_VERSION};
134
135 fn fixture_payload() -> EvidenceBundlePayload {
136 EvidenceBundlePayload {
137 protocol_version: PROTOCOL_VERSION,
138 bundle_id: [0x01; 16],
139 license_request: vec![0x01, 0x02],
140 license_grant: vec![0x03, 0x04],
141 receipt: vec![0x05, 0x06],
142 binding_certificate: vec![0x07, 0x08],
143 terms_hash: [0xab; 32],
144 consent: ConsentRecord {
145 checkboxes_ticked: vec!["terms_of_service".into(), "privacy_policy".into()],
146 consented_at: 1700000000,
147 ip_address: "198.51.100.42".into(),
148 },
149 payment_reference: "ch_test_1234".into(),
150 tsa_token: vec![0xde, 0xad, 0xbe, 0xef],
151 vendor_public_key: [0xee; 32],
152 customer_public_key: [0xcc; 32],
153 }
154 }
155
156 #[test]
157 fn roundtrip_serialization() {
158 let vendor_sk = SigningKey::generate();
159 let customer_sk = SigningKey::generate();
160
161 let partial = EvidenceBundle::sign_vendor(fixture_payload(), &vendor_sk).unwrap();
162 let bundle = partial.add_customer_signature(&customer_sk).unwrap();
163
164 let bytes = bundle.to_bytes().unwrap();
165 let decoded = EvidenceBundle::from_bytes(&bytes).unwrap();
166 assert_eq!(bundle, decoded);
167 }
168
169 #[test]
170 fn verify_signatures_pass() {
171 let vendor_sk = SigningKey::generate();
172 let customer_sk = SigningKey::generate();
173
174 let partial = EvidenceBundle::sign_vendor(fixture_payload(), &vendor_sk).unwrap();
175 let bundle = partial.add_customer_signature(&customer_sk).unwrap();
176
177 bundle
178 .verify(&vendor_sk.verifying_key(), &customer_sk.verifying_key())
179 .unwrap();
180 }
181
182 #[test]
183 fn tampered_payload_fails_verification() {
184 let vendor_sk = SigningKey::generate();
185 let customer_sk = SigningKey::generate();
186
187 let partial = EvidenceBundle::sign_vendor(fixture_payload(), &vendor_sk).unwrap();
188 let mut bundle = partial.add_customer_signature(&customer_sk).unwrap();
189
190 bundle.payload.payment_reference = "ch_tampered".into();
192
193 let result = bundle.verify(&vendor_sk.verifying_key(), &customer_sk.verifying_key());
194 assert!(result.is_err());
195 }
196
197 #[test]
198 fn wrong_key_fails_vendor_verification() {
199 let vendor_sk = SigningKey::generate();
200 let wrong_sk = SigningKey::generate();
201 let customer_sk = SigningKey::generate();
202
203 let partial = EvidenceBundle::sign_vendor(fixture_payload(), &vendor_sk).unwrap();
204 let bundle = partial.add_customer_signature(&customer_sk).unwrap();
205
206 let result = bundle.verify_vendor(&wrong_sk.verifying_key());
207 assert!(matches!(result, Err(Error::SignatureInvalid)));
208 }
209}