Skip to main content

zkgroup/api/profiles/
profile_key_credential_presentation.rs

1//
2// Copyright 2020-2022 Signal Messenger, LLC.
3// SPDX-License-Identifier: AGPL-3.0-only
4//
5
6use partial_default::PartialDefault;
7use serde::{Deserialize, Serialize, Serializer};
8
9use crate::common::constants::*;
10use crate::common::errors::*;
11use crate::common::serialization::VersionByte;
12use crate::common::simple_types::*;
13use crate::{api, crypto};
14
15#[derive(Clone, Serialize, Deserialize, PartialDefault)]
16pub struct ProfileKeyCredentialPresentationV1 {
17    pub(crate) version: u8, // Not ReservedByte or VersionByte to allow deserializing a V2 presentation as V1.
18    pub(crate) proof: crypto::proofs::ProfileKeyCredentialPresentationProofV1,
19    pub(crate) uid_enc_ciphertext: crypto::uid_encryption::Ciphertext,
20    pub(crate) profile_key_enc_ciphertext: crypto::profile_key_encryption::Ciphertext,
21}
22
23impl ProfileKeyCredentialPresentationV1 {
24    pub fn get_uuid_ciphertext(&self) -> api::groups::UuidCiphertext {
25        api::groups::UuidCiphertext {
26            reserved: Default::default(),
27            ciphertext: self.uid_enc_ciphertext,
28        }
29    }
30
31    pub fn get_profile_key_ciphertext(&self) -> api::groups::ProfileKeyCiphertext {
32        api::groups::ProfileKeyCiphertext {
33            reserved: Default::default(),
34            ciphertext: self.profile_key_enc_ciphertext,
35        }
36    }
37}
38
39/// Like [`ProfileKeyCredentialPresentationV1`], but with an optimized proof.
40#[derive(Clone, Serialize, Deserialize, PartialDefault)]
41pub struct ProfileKeyCredentialPresentationV2 {
42    pub(crate) version: VersionByte<PRESENTATION_VERSION_2>,
43    pub(crate) proof: crypto::proofs::ProfileKeyCredentialPresentationProofV2,
44    pub(crate) uid_enc_ciphertext: crypto::uid_encryption::Ciphertext,
45    pub(crate) profile_key_enc_ciphertext: crypto::profile_key_encryption::Ciphertext,
46}
47
48impl ProfileKeyCredentialPresentationV2 {
49    pub fn get_uuid_ciphertext(&self) -> api::groups::UuidCiphertext {
50        api::groups::UuidCiphertext {
51            reserved: Default::default(),
52            ciphertext: self.uid_enc_ciphertext,
53        }
54    }
55
56    pub fn get_profile_key_ciphertext(&self) -> api::groups::ProfileKeyCiphertext {
57        api::groups::ProfileKeyCiphertext {
58            reserved: Default::default(),
59            ciphertext: self.profile_key_enc_ciphertext,
60        }
61    }
62}
63
64#[derive(Clone, Serialize, Deserialize, PartialDefault)]
65pub struct ExpiringProfileKeyCredentialPresentation<const V: u8 = PRESENTATION_VERSION_3> {
66    pub(crate) version: VersionByte<V>,
67    pub(crate) proof: crypto::proofs::ExpiringProfileKeyCredentialPresentationProof,
68    pub(crate) uid_enc_ciphertext: crypto::uid_encryption::Ciphertext,
69    pub(crate) profile_key_enc_ciphertext: crypto::profile_key_encryption::Ciphertext,
70    pub(crate) credential_expiration_time: Timestamp,
71}
72
73// The only thing that changes in V2 is the proof contents, so we can reuse the same structure.
74pub type ExpiringProfileKeyCredentialPresentationV2 =
75    ExpiringProfileKeyCredentialPresentation<PRESENTATION_VERSION_4>;
76
77impl<const V: u8> ExpiringProfileKeyCredentialPresentation<V> {
78    pub fn get_uuid_ciphertext(&self) -> api::groups::UuidCiphertext {
79        api::groups::UuidCiphertext {
80            reserved: Default::default(),
81            ciphertext: self.uid_enc_ciphertext,
82        }
83    }
84
85    pub fn get_profile_key_ciphertext(&self) -> api::groups::ProfileKeyCiphertext {
86        api::groups::ProfileKeyCiphertext {
87            reserved: Default::default(),
88            ciphertext: self.profile_key_enc_ciphertext,
89        }
90    }
91
92    pub fn get_expiration_time(&self) -> Timestamp {
93        self.credential_expiration_time
94    }
95}
96
97#[derive(derive_more::From)]
98pub enum AnyProfileKeyCredentialPresentation {
99    V1(ProfileKeyCredentialPresentationV1),
100    V2(ProfileKeyCredentialPresentationV2),
101    V3(ExpiringProfileKeyCredentialPresentation),
102    V4(ExpiringProfileKeyCredentialPresentationV2),
103}
104
105impl AnyProfileKeyCredentialPresentation {
106    pub fn new(presentation_bytes: &[u8]) -> Result<Self, ZkGroupDeserializationFailure> {
107        match presentation_bytes[0] {
108            PRESENTATION_VERSION_1 => {
109                crate::deserialize::<ProfileKeyCredentialPresentationV1>(presentation_bytes)
110                    .map(AnyProfileKeyCredentialPresentation::V1)
111            }
112            PRESENTATION_VERSION_2 => {
113                crate::deserialize::<ProfileKeyCredentialPresentationV2>(presentation_bytes)
114                    .map(AnyProfileKeyCredentialPresentation::V2)
115            }
116            PRESENTATION_VERSION_3 => {
117                crate::deserialize::<ExpiringProfileKeyCredentialPresentation>(presentation_bytes)
118                    .map(AnyProfileKeyCredentialPresentation::V3)
119            }
120            PRESENTATION_VERSION_4 => {
121                crate::deserialize::<ExpiringProfileKeyCredentialPresentationV2>(presentation_bytes)
122                    .map(AnyProfileKeyCredentialPresentation::V4)
123            }
124            _ => Err(ZkGroupDeserializationFailure::new::<Self>()),
125        }
126    }
127
128    pub fn get_uuid_ciphertext(&self) -> api::groups::UuidCiphertext {
129        match self {
130            AnyProfileKeyCredentialPresentation::V1(presentation) => {
131                presentation.get_uuid_ciphertext()
132            }
133            AnyProfileKeyCredentialPresentation::V2(presentation) => {
134                presentation.get_uuid_ciphertext()
135            }
136            AnyProfileKeyCredentialPresentation::V3(presentation) => {
137                presentation.get_uuid_ciphertext()
138            }
139            AnyProfileKeyCredentialPresentation::V4(presentation) => {
140                presentation.get_uuid_ciphertext()
141            }
142        }
143    }
144
145    pub fn get_profile_key_ciphertext(&self) -> api::groups::ProfileKeyCiphertext {
146        match self {
147            AnyProfileKeyCredentialPresentation::V1(presentation) => {
148                presentation.get_profile_key_ciphertext()
149            }
150            AnyProfileKeyCredentialPresentation::V2(presentation) => {
151                presentation.get_profile_key_ciphertext()
152            }
153            AnyProfileKeyCredentialPresentation::V3(presentation) => {
154                presentation.get_profile_key_ciphertext()
155            }
156            AnyProfileKeyCredentialPresentation::V4(presentation) => {
157                presentation.get_profile_key_ciphertext()
158            }
159        }
160    }
161
162    pub fn to_structurally_valid_v1_presentation_bytes(&self) -> Vec<u8> {
163        let v1 = ProfileKeyCredentialPresentationV1 {
164            version: PRESENTATION_VERSION_1,
165            proof: crypto::proofs::ProfileKeyCredentialPresentationProofV1::from_invalid_proof(
166                // Hardcoded length of a valid v1 proof.
167                vec![0; 0x0140],
168            ),
169            uid_enc_ciphertext: self.get_uuid_ciphertext().ciphertext,
170            profile_key_enc_ciphertext: self.get_profile_key_ciphertext().ciphertext,
171        };
172        let result = crate::serialize(&v1);
173        debug_assert_eq!(result.len(), PROFILE_KEY_CREDENTIAL_PRESENTATION_V1_LEN);
174        result
175    }
176}
177
178impl Serialize for AnyProfileKeyCredentialPresentation {
179    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
180    where
181        S: Serializer,
182    {
183        match self {
184            AnyProfileKeyCredentialPresentation::V1(presentation) => {
185                presentation.serialize(serializer)
186            }
187            AnyProfileKeyCredentialPresentation::V2(presentation) => {
188                presentation.serialize(serializer)
189            }
190            AnyProfileKeyCredentialPresentation::V3(presentation) => {
191                presentation.serialize(serializer)
192            }
193            AnyProfileKeyCredentialPresentation::V4(presentation) => {
194                presentation.serialize(serializer)
195            }
196        }
197    }
198}