zkgroup/crypto/
uid_struct.rs

1//
2// Copyright 2020 Signal Messenger, LLC.
3// SPDX-License-Identifier: AGPL-3.0-only
4//
5
6#![allow(non_snake_case)]
7
8use curve25519_dalek_signal::ristretto::RistrettoPoint;
9use libsignal_core::ServiceId;
10use partial_default::PartialDefault;
11use serde::{Deserialize, Serialize};
12use sha2::Sha256;
13
14use crate::common::sho::*;
15use crate::common::simple_types::*;
16
17#[derive(Copy, Clone, PartialEq, Eq, Serialize, Deserialize, PartialDefault)]
18pub struct UidStruct {
19    // Currently unused. It would be possible to convert this back to the correct kind of ServiceId
20    // using the same technique as decryption: comparing possible M1 points and seeing which one
21    // matches. But we don't have a need for that, and therefore it's better if that operation
22    // remains part of decryption, so that you're guaranteed to get a valid result or an error in
23    // one step.
24    //
25    // At the same time, we can't just remove the field: it's serialized as part of AuthCredential
26    // and AuthCredentialWithPni, which clients store locally.
27    #[serde(rename = "bytes")]
28    raw_uuid_bytes: UidBytes,
29    pub(crate) M1: RistrettoPoint,
30    pub(crate) M2: RistrettoPoint,
31}
32
33impl UidStruct {
34    pub fn from_service_id(service_id: ServiceId) -> Self {
35        let M1 = Self::calc_M1(service_id);
36        let raw_uuid_bytes = service_id.raw_uuid().into_bytes();
37        let M2 = RistrettoPoint::lizard_encode::<Sha256>(&raw_uuid_bytes);
38        UidStruct {
39            raw_uuid_bytes,
40            M1,
41            M2,
42        }
43    }
44
45    pub fn calc_M1(service_id: ServiceId) -> RistrettoPoint {
46        let mut sho = Sho::new(
47            b"Signal_ZKGroup_20200424_UID_CalcM1",
48            &service_id.service_id_binary(),
49        );
50        sho.get_point()
51    }
52}
53
54impl zkcredential::attributes::Attribute for UidStruct {
55    fn as_points(&self) -> [RistrettoPoint; 2] {
56        [self.M1, self.M2]
57    }
58}