1use crate::tools::server_id::ServerId;
22use crate::tools::time::{MILLIS_IN_DAY, MILLIS_IN_MONTH, TimeMillis, TimeMillisBytes};
23use crate::tools::time_provider::time_provider::TimeProvider;
24use crate::tools::types::{Hash, Id, PQCommitmentBytes, Pow, Salt, Signature, SignatureKey, VerificationKey, VerificationKeyBytes};
25use crate::tools::{config, hashing, signing, tools};
26use crate::{anyhow_assert_eq, anyhow_assert_ge};
27use serde::{Deserialize, Serialize};
28use std::fmt;
29
30#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
44pub struct PeerPow {
45 #[serde(rename = "sp")]
46 pub sponsor_id: Id,
47 #[serde(rename = "t")]
48 pub timestamp: TimeMillis,
49 #[serde(rename = "ch")]
50 pub content_hash: Hash, #[serde(rename = "s")]
52 pub salt: Salt,
53 #[serde(rename = "z")]
54 pub pow: Pow,
55}
56
57impl PeerPow {
58 pub fn new(sponsor_id: Id, verification_key: &VerificationKeyBytes, pq_commitment_bytes: &PQCommitmentBytes, timestamp: TimeMillis, content_hash: Hash, salt: Salt) -> anyhow::Result<PeerPow> {
59 let (pow, _) = Self::pow(&sponsor_id, verification_key, pq_commitment_bytes, ×tamp.encode_be(), &content_hash, &salt)?;
60 Ok(Self {
61 sponsor_id,
62 timestamp,
63 content_hash,
64 salt,
65 pow,
66 })
67 }
68
69 pub fn zero() -> Self {
70 PeerPow {
71 sponsor_id: Id::zero(),
72 timestamp: TimeMillis::zero(),
73 content_hash: Hash::zero(),
74 salt: Salt::zero(),
75 pow: Pow(0),
76 }
77 }
78
79 pub fn random() -> Self {
80 Self {
81 sponsor_id: Id::random(),
82 timestamp: TimeMillis::random(),
83 content_hash: Hash::random(),
84 salt: Salt::random(),
85 pow: Pow(tools::random_u8()),
86 }
87 }
88
89 pub fn verify(&self, verification_key: &VerificationKeyBytes, pq_commitment_bytes: &PQCommitmentBytes) -> anyhow::Result<()> {
90 let (pow, _) = Self::pow(&self.sponsor_id, verification_key, pq_commitment_bytes, &self.timestamp.encode_be(), &self.content_hash, &self.salt)?;
91 match pow == self.pow {
92 true => Ok(()),
93 false => Err(anyhow::anyhow!("pow does not match inputs: {} != {}", self.pow, pow)),
94 }
95 }
96
97 pub fn pow_decayed_day(&self, current_time_millis: TimeMillis) -> f64 {
98 let pow_halflife_millis = MILLIS_IN_DAY;
99 let elapsed_millis = current_time_millis - self.timestamp;
100 (self.pow.0 as f64) / 2.0f64.powf(elapsed_millis.0 as f64 / pow_halflife_millis.0 as f64)
101 }
102
103 pub fn pow_decayed_month(&self, current_time_millis: TimeMillis) -> f64 {
104 let pow_halflife_millis = MILLIS_IN_MONTH;
105 let elapsed_millis = current_time_millis - self.timestamp;
106 (self.pow.0 as f64) / 2.0f64.powf(elapsed_millis.0 as f64 / pow_halflife_millis.0 as f64)
107 }
108
109 pub fn pow(sponsor_id: &Id, verification_key: &VerificationKeyBytes, pq_commitment_bytes: &PQCommitmentBytes, timestamp: &TimeMillisBytes, hash: &Hash, salt: &Salt) -> anyhow::Result<(Pow, Hash)> {
110 ServerId::pow_measure(sponsor_id, verification_key, pq_commitment_bytes, timestamp, hash, salt)
111 }
112
113 pub fn own_pow(&self, verification_key: &VerificationKeyBytes, pq_commitment_bytes: &PQCommitmentBytes) -> anyhow::Result<(Pow, Hash)> {
114 Self::pow(&self.sponsor_id, verification_key, pq_commitment_bytes, &self.timestamp.encode_be(), &self.content_hash, &self.salt)
115 }
116}
117
118#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
131pub struct ClientPow {
132 pub peer_verification_key: VerificationKeyBytes,
133 pub peer_pq_commitment_bytes: PQCommitmentBytes,
134 pub peer_pow: PeerPow,
135}
136
137impl ClientPow {
138 pub fn measure(peer_verification_key: &VerificationKeyBytes, peer_pq_commitment_bytes: &PQCommitmentBytes, sponsor_id: &Id, timestamp: TimeMillis, content_hash: &Hash, salt: &Salt) -> anyhow::Result<Self> {
139 let (pow, _) = ServerId::pow_measure(sponsor_id, peer_verification_key, peer_pq_commitment_bytes, ×tamp.encode_be(), content_hash, salt)?;
140 Ok(Self {
141 peer_verification_key: *peer_verification_key,
142 peer_pq_commitment_bytes: *peer_pq_commitment_bytes,
143 peer_pow: PeerPow {
144 sponsor_id: *sponsor_id,
145 timestamp,
146 content_hash: *content_hash,
147 salt: *salt,
148 pow,
149 },
150 })
151 }
152
153 pub fn verify(&self) -> anyhow::Result<()> {
154 self.peer_pow.verify(&self.peer_verification_key, &self.peer_pq_commitment_bytes)
155 }
156}
157
158impl fmt::Display for PeerPow {
159 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
160 write!(f, "timestamp={} hash={} salt={} pow={}", self.timestamp, hex::encode(self.content_hash), hex::encode(self.salt), self.pow,)
161 }
162}
163
164#[derive(Serialize, Deserialize, PartialEq, Clone)]
178pub struct Peer {
179 pub id: Id,
180
181 #[serde(rename = "vkb")]
182 pub verification_key_bytes: VerificationKeyBytes,
183 #[serde(rename = "pqcb")]
184 pub pq_commitment_bytes: PQCommitmentBytes,
185
186 #[serde(rename = "pi")]
187 pub pow_initial: PeerPow, #[serde(rename = "pcd")]
189 pub pow_current_day: PeerPow, #[serde(rename = "pcm")]
191 pub pow_current_month: PeerPow, #[serde(rename = "ver")]
194 pub version: String, #[serde(rename = "addr")]
196 pub address: String, #[serde(rename = "ts")]
199 pub timestamp: TimeMillis, #[serde(rename = "sig")]
202 pub signature: Signature, }
204
205impl fmt::Debug for Peer {
206 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
207 fmt::Display::fmt(self, f)
208 }
209}
210
211impl fmt::Display for Peer {
212 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
213 write!(
214 f,
215 "[ id={} version={} address={} pows=[ {} {} {} ] timestamp={} ]",
216 self.id, self.version, self.address, self.pow_initial.pow, self.pow_current_day.pow, self.pow_current_month.pow, self.timestamp
217 )
218
219 }
232}
233
234impl Peer {
235 pub fn zero() -> Peer {
236 Peer {
237 id: Id::zero(),
238 verification_key_bytes: VerificationKeyBytes::zero(),
239 pq_commitment_bytes: PQCommitmentBytes::zero(),
240
241 pow_initial: PeerPow::zero(),
242 pow_current_day: PeerPow::zero(),
243 pow_current_month: PeerPow::zero(),
244
245 version: env!("CARGO_PKG_VERSION").to_string(),
246 address: String::new(),
247
248 timestamp: TimeMillis::zero(),
249
250 signature: Signature::zero(),
251 }
252 }
253
254 pub fn signature_hash_generate(&self) -> anyhow::Result<Hash> {
255 Ok(hashing::hash_multiple(&[
256 self.id.as_ref(),
257 self.verification_key_bytes.as_ref(),
258 self.pq_commitment_bytes.as_ref(),
259 self.pow_initial.own_pow(&self.verification_key_bytes, &self.pq_commitment_bytes)?.1.as_ref(),
260 self.pow_current_day.own_pow(&self.verification_key_bytes, &self.pq_commitment_bytes)?.1.as_ref(),
261 self.pow_current_month.own_pow(&self.verification_key_bytes, &self.pq_commitment_bytes)?.1.as_ref(),
262 self.address.as_bytes(),
263 self.timestamp.encode_be().as_ref(),
264 ]))
265 }
266
267 pub fn sign(&mut self, time_provider: &dyn TimeProvider, signature_key: &SignatureKey) -> anyhow::Result<()> {
268 self.timestamp = time_provider.current_time_millis();
269
270 let signature_hash = self.signature_hash_generate()?;
271 self.signature = signing::sign(signature_key, signature_hash.as_ref());
272 Ok(())
273 }
274
275 pub fn verify(&self) -> anyhow::Result<()> {
276 self.verify_signature()?;
277 self.verify_server_id()?;
278 self.verify_pows()?;
279 Ok(())
280 }
281
282 fn verify_signature(&self) -> anyhow::Result<()> {
283 let signature_hash = self.signature_hash_generate()?;
284 signing::verify(&VerificationKey::from_bytes(&self.verification_key_bytes)?, &self.signature, signature_hash.as_ref())?;
285
286 Ok(())
287 }
288
289 fn verify_server_id(&self) -> anyhow::Result<()> {
290 let (pow, pow_hash) = ServerId::pow_measure(
291 &self.pow_initial.sponsor_id,
292 &self.verification_key_bytes,
293 &self.pq_commitment_bytes,
294 &self.pow_initial.timestamp.encode_be(),
295 &self.pow_initial.content_hash,
296 &self.pow_initial.salt,
297 )?;
298 anyhow_assert_ge!(&pow, &config::SERVER_KEY_POW_MIN, "insufficient server_id pow");
299
300 let id = ServerId::server_pow_hash_to_id(pow_hash)?;
301
302 anyhow_assert_eq!(&self.id, &id, "served_id mismatch");
303
304 Ok(())
305 }
306
307 fn verify_pows(&self) -> anyhow::Result<()> {
308 self.pow_initial.verify(&self.verification_key_bytes, &self.pq_commitment_bytes)?;
309 self.pow_current_day.verify(&self.verification_key_bytes, &self.pq_commitment_bytes)?;
310 self.pow_current_month.verify(&self.verification_key_bytes, &self.pq_commitment_bytes)?;
311 Ok(())
312 }
313}
314
315#[cfg(test)]
316mod tests {
317 use crate::protocol::peer::{Peer, PeerPow};
318 use crate::tools::server_id::ServerId;
319 use crate::tools::time::TimeMillis;
320 use crate::tools::time_provider::time_provider::RealTimeProvider;
321 use crate::tools::parallel_pow_generator::StubParallelPowGenerator;
322 use crate::tools::tools;
323 use crate::tools::types::{Pow, VerificationKeyBytes};
324
325 async fn get_random_ingredients() -> anyhow::Result<(ServerId, Peer)> {
326 let time_provider = RealTimeProvider::default();
327 let pow_generator = StubParallelPowGenerator::new();
328 let server_id = ServerId::new(&time_provider, Pow(0), true, &pow_generator).await?;
329
330 let mut peer = server_id.to_peer(&time_provider)?;
331 peer.pow_current_day = PeerPow::random();
332 peer.pow_current_month = PeerPow::random();
333 peer.address = tools::random_base64(16);
334
335 peer.sign(&time_provider, &server_id.keys.signature_key)?;
337
338 Ok((server_id, peer))
339 }
340
341 #[tokio::test]
342 async fn signing_test() -> anyhow::Result<()> {
343 let (_, peer) = get_random_ingredients().await?;
344
345 peer.verify_signature()?;
347 Ok(())
348 }
349
350 #[tokio::test]
351 async fn signing_fail_test() -> anyhow::Result<()> {
352 {
353 let (_, mut peer) = get_random_ingredients().await?;
354 peer.verification_key_bytes = VerificationKeyBytes::zero();
355 peer.verify_signature().unwrap_err();
356 }
357
358 {
359 let (_, mut peer) = get_random_ingredients().await?;
360 peer.pow_initial = PeerPow::random();
361 peer.verify_signature().unwrap_err();
362 }
363
364 {
365 let (_, mut peer) = get_random_ingredients().await?;
366 peer.pow_current_day = PeerPow::random();
367 peer.verify_signature().unwrap_err();
368 }
369
370 {
371 let (_, mut peer) = get_random_ingredients().await?;
372 peer.pow_current_month = PeerPow::random();
373 peer.verify_signature().unwrap_err();
374 }
375
376 {
377 let (_, mut peer) = get_random_ingredients().await?;
378 peer.address = tools::random_base64(16);
379 peer.verify_signature().unwrap_err();
380 }
381
382 {
383 let (_, mut peer) = get_random_ingredients().await?;
384 peer.timestamp = TimeMillis::random();
385 peer.verify_signature().unwrap_err();
386 }
387
388 Ok(())
389 }
390}