1use alloc::boxed::Box;
2use alloc::collections::BTreeMap;
3use alloc::format;
4use alloc::string::String;
5use alloc::vec::Vec;
6
7use serde::{Deserialize, Serialize};
8use umbral_pre::{
9 decrypt_original, encrypt, serde_bytes, Capsule, EncryptionError, PublicKey, SecretKey,
10 Signature, Signer, VerifiedKeyFrag,
11};
12
13use crate::address::Address;
14use crate::hrac::HRAC;
15use crate::key_frag::{DecryptionError, EncryptedKeyFrag};
16use crate::versioning::{
17 messagepack_deserialize, messagepack_serialize, ProtocolObject, ProtocolObjectInner,
18};
19use crate::RevocationOrder;
20
21#[derive(PartialEq, Debug, Clone, Serialize, Deserialize)]
23pub struct TreasureMap {
24 pub threshold: u8,
26 pub hrac: HRAC,
28 pub destinations: BTreeMap<Address, EncryptedKeyFrag>,
30 pub policy_encrypting_key: PublicKey,
32 pub publisher_verifying_key: PublicKey,
34}
35
36impl TreasureMap {
37 pub fn new(
43 signer: &Signer,
44 hrac: &HRAC,
45 policy_encrypting_key: &PublicKey,
46 assigned_kfrags: impl IntoIterator<Item = (Address, (PublicKey, VerifiedKeyFrag))>,
47 threshold: u8,
48 ) -> Self {
49 assert!(threshold != 0, "threshold must be non-zero");
51
52 let mut destinations = BTreeMap::new();
54 for (ursula_address, (ursula_encrypting_key, verified_kfrag)) in assigned_kfrags.into_iter()
55 {
56 let encrypted_kfrag =
57 EncryptedKeyFrag::new(signer, &ursula_encrypting_key, hrac, verified_kfrag);
58 if destinations
59 .insert(ursula_address, encrypted_kfrag)
60 .is_some()
61 {
62 panic!(
65 "{}",
66 format!("Repeating address in assigned_kfrags: {:?}", ursula_address)
67 )
68 };
69 }
70
71 assert!(
73 destinations.len() >= threshold as usize,
74 "threshold cannot be larger than the total number of shares"
75 );
76
77 Self {
78 threshold,
79 hrac: *hrac,
80 destinations,
81 policy_encrypting_key: *policy_encrypting_key,
82 publisher_verifying_key: signer.verifying_key(),
83 }
84 }
85
86 pub fn encrypt(&self, signer: &Signer, recipient_key: &PublicKey) -> EncryptedTreasureMap {
88 EncryptedTreasureMap::new(signer, recipient_key, self)
89 }
90
91 pub fn make_revocation_orders(&self, signer: &Signer) -> Vec<RevocationOrder> {
93 self.destinations
94 .iter()
95 .map(|(address, ekfrag)| RevocationOrder::new(signer, address, ekfrag))
96 .collect()
97 }
98}
99
100impl<'a> ProtocolObjectInner<'a> for TreasureMap {
101 fn brand() -> [u8; 4] {
102 *b"TMap"
103 }
104
105 fn version() -> (u16, u16) {
106 (3, 0)
107 }
108
109 fn unversioned_to_bytes(&self) -> Box<[u8]> {
110 messagepack_serialize(&self)
111 }
112
113 fn unversioned_from_bytes(minor_version: u16, bytes: &[u8]) -> Option<Result<Self, String>> {
114 if minor_version == 0 {
115 Some(messagepack_deserialize(bytes))
116 } else {
117 None
118 }
119 }
120}
121
122impl<'a> ProtocolObject<'a> for TreasureMap {}
123
124#[derive(PartialEq, Debug, Clone, Serialize, Deserialize)]
125struct AuthorizedTreasureMap {
126 signature: Signature,
127 treasure_map: TreasureMap,
128}
129
130impl AuthorizedTreasureMap {
131 fn message_to_sign(recipient_key: &PublicKey, treasure_map: &TreasureMap) -> Vec<u8> {
132 let mut message = recipient_key.to_compressed_bytes().to_vec();
133 message.extend(treasure_map.to_bytes().iter());
134 message
135 }
136
137 fn new(signer: &Signer, recipient_key: &PublicKey, treasure_map: &TreasureMap) -> Self {
138 let message = Self::message_to_sign(recipient_key, treasure_map);
139 let signature = signer.sign(&message);
140
141 Self {
142 signature,
143 treasure_map: treasure_map.clone(),
144 }
145 }
146
147 fn verify(
148 self,
149 recipient_key: &PublicKey,
150 publisher_verifying_key: &PublicKey,
151 ) -> Option<TreasureMap> {
152 let message = Self::message_to_sign(recipient_key, &self.treasure_map);
153 if !self.signature.verify(publisher_verifying_key, &message) {
154 return None;
155 }
156 Some(self.treasure_map)
157 }
158}
159
160impl<'a> ProtocolObjectInner<'a> for AuthorizedTreasureMap {
161 fn brand() -> [u8; 4] {
162 *b"AMap"
163 }
164
165 fn version() -> (u16, u16) {
166 (3, 0)
167 }
168
169 fn unversioned_to_bytes(&self) -> Box<[u8]> {
170 messagepack_serialize(&self)
171 }
172
173 fn unversioned_from_bytes(minor_version: u16, bytes: &[u8]) -> Option<Result<Self, String>> {
174 if minor_version == 0 {
175 Some(messagepack_deserialize(bytes))
176 } else {
177 None
178 }
179 }
180}
181
182impl<'a> ProtocolObject<'a> for AuthorizedTreasureMap {}
183
184#[derive(PartialEq, Debug, Clone, Serialize, Deserialize)]
186pub struct EncryptedTreasureMap {
187 capsule: Capsule,
188 #[serde(with = "serde_bytes::as_base64")]
189 ciphertext: Box<[u8]>,
190}
191
192impl EncryptedTreasureMap {
193 fn new(signer: &Signer, recipient_key: &PublicKey, treasure_map: &TreasureMap) -> Self {
194 let authorized_tmap = AuthorizedTreasureMap::new(signer, recipient_key, treasure_map);
202 let (capsule, ciphertext) = match encrypt(recipient_key, &authorized_tmap.to_bytes()) {
203 Ok(result) => result,
204 Err(err) => match err {
205 EncryptionError::PlaintextTooLarge => panic!("encryption failed - out of memory?"),
208 },
209 };
210 Self {
211 capsule,
212 ciphertext,
213 }
214 }
215
216 pub fn decrypt(
218 &self,
219 sk: &SecretKey,
220 publisher_verifying_key: &PublicKey,
221 ) -> Result<TreasureMap, DecryptionError> {
222 let auth_tmap_bytes = decrypt_original(sk, &self.capsule, &self.ciphertext)
223 .map_err(DecryptionError::DecryptionFailed)?;
224 let auth_tmap = AuthorizedTreasureMap::from_bytes(&auth_tmap_bytes)
225 .map_err(DecryptionError::DeserializationFailed)?;
226 auth_tmap
227 .verify(&sk.public_key(), publisher_verifying_key)
228 .ok_or(DecryptionError::VerificationFailed)
229 }
230}
231
232impl<'a> ProtocolObjectInner<'a> for EncryptedTreasureMap {
233 fn brand() -> [u8; 4] {
234 *b"EMap"
235 }
236
237 fn version() -> (u16, u16) {
238 (3, 0)
239 }
240
241 fn unversioned_to_bytes(&self) -> Box<[u8]> {
242 messagepack_serialize(&self)
243 }
244
245 fn unversioned_from_bytes(minor_version: u16, bytes: &[u8]) -> Option<Result<Self, String>> {
246 if minor_version == 0 {
247 Some(messagepack_deserialize(bytes))
248 } else {
249 None
250 }
251 }
252}
253
254impl<'a> ProtocolObject<'a> for EncryptedTreasureMap {}