zkgroup/common/
simple_types.rs

1//
2// Copyright 2020 Signal Messenger, LLC.
3// SPDX-License-Identifier: AGPL-3.0-only
4//
5
6use curve25519_dalek_signal::scalar::Scalar;
7use partial_default::PartialDefault;
8use serde::{Deserialize, Serialize};
9use zkcredential::attributes::PublicAttribute;
10
11use crate::common::constants::*;
12
13pub type AesKeyBytes = [u8; AES_KEY_LEN];
14pub type GroupMasterKeyBytes = [u8; GROUP_MASTER_KEY_LEN];
15pub type UidBytes = [u8; UUID_LEN];
16pub type ProfileKeyBytes = [u8; PROFILE_KEY_LEN];
17pub type RandomnessBytes = [u8; RANDOMNESS_LEN];
18pub type SignatureBytes = [u8; SIGNATURE_LEN];
19pub type NotarySignatureBytes = [u8; SIGNATURE_LEN];
20pub type GroupIdentifierBytes = [u8; GROUP_IDENTIFIER_LEN];
21pub type ProfileKeyVersionBytes = [u8; PROFILE_KEY_VERSION_LEN];
22pub type ProfileKeyVersionEncodedBytes = [u8; PROFILE_KEY_VERSION_ENCODED_LEN];
23
24// A random UUID that the receipt issuing server will blind authorize to redeem a given receipt
25// level within a certain time frame.
26pub type ReceiptSerialBytes = [u8; RECEIPT_SERIAL_LEN];
27
28/// Timestamp measured in seconds past the epoch.
29///
30/// Clients should only accept round multiples of 86400 to avoid fingerprinting by the server.
31/// For expirations, the timestamp should be within a couple of days into the future;
32/// for redemption times, it should be within a day of the current date.
33#[derive(
34    Copy, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize, Deserialize, PartialDefault,
35)]
36#[serde(transparent)]
37#[repr(transparent)]
38pub struct Timestamp(u64);
39
40impl Timestamp {
41    #[inline]
42    pub const fn from_epoch_seconds(seconds: u64) -> Self {
43        Self(seconds)
44    }
45
46    #[inline]
47    pub const fn epoch_seconds(&self) -> u64 {
48        self.0
49    }
50
51    #[inline]
52    pub const fn add_seconds(&self, seconds: u64) -> Self {
53        Self(self.0 + seconds)
54    }
55
56    #[inline]
57    pub const fn sub_seconds(&self, seconds: u64) -> Self {
58        Self(self.0 - seconds)
59    }
60
61    #[inline]
62    pub fn checked_add_seconds(&self, seconds: u64) -> Option<Self> {
63        self.0.checked_add(seconds).map(Self)
64    }
65
66    #[inline]
67    pub fn checked_sub_seconds(&self, seconds: u64) -> Option<Self> {
68        self.0.checked_sub(seconds).map(Self)
69    }
70
71    #[inline]
72    pub const fn is_day_aligned(&self) -> bool {
73        self.0 % SECONDS_PER_DAY == 0
74    }
75
76    #[inline]
77    pub fn to_be_bytes(self) -> [u8; 8] {
78        self.0.to_be_bytes()
79    }
80
81    /// Number of seconds that `self` is after `before`.
82    ///
83    /// Returns `0` if `self` is equal to or earlier than `before`.
84    pub(crate) fn saturating_seconds_since(&self, before: Timestamp) -> u64 {
85        self.0.saturating_sub(before.0)
86    }
87}
88
89impl From<Timestamp> for std::time::SystemTime {
90    fn from(Timestamp(seconds): Timestamp) -> Self {
91        std::time::UNIX_EPOCH + std::time::Duration::from_secs(seconds)
92    }
93}
94
95impl rand::distributions::Distribution<Timestamp> for rand::distributions::Standard {
96    fn sample<R: rand::prelude::Rng + ?Sized>(&self, rng: &mut R) -> Timestamp {
97        Timestamp(Self::sample(self, rng))
98    }
99}
100
101impl PublicAttribute for Timestamp {
102    fn hash_into(&self, sho: &mut dyn poksho::ShoApi) {
103        self.0.hash_into(sho)
104    }
105}
106
107// Used to tell the server handling receipt redemptions what to redeem the receipt for. Clients
108// should validate this matches their expectations.
109pub type ReceiptLevel = u64;
110
111pub fn encode_redemption_time(redemption_time: u32) -> Scalar {
112    let mut scalar_bytes: [u8; 32] = Default::default();
113    scalar_bytes[0..4].copy_from_slice(&redemption_time.to_be_bytes());
114    Scalar::from_bytes_mod_order(scalar_bytes)
115}
116
117pub fn encode_receipt_serial_bytes(receipt_serial_bytes: ReceiptSerialBytes) -> Scalar {
118    let mut scalar_bytes: [u8; 32] = Default::default();
119    scalar_bytes[0..16].copy_from_slice(&receipt_serial_bytes[..]);
120    Scalar::from_bytes_mod_order(scalar_bytes)
121}
122
123#[cfg(test)]
124mod tests {
125    use super::*;
126
127    #[test]
128    fn test_encode_scalar() {
129        let s_bytes = [0xFF; 32];
130        match bincode::deserialize::<Scalar>(&s_bytes) {
131            Err(_) => (),
132            Ok(_) => unreachable!(),
133        }
134    }
135}