zkgroup/api/auth/
auth_credential_presentation.rs

1//
2// Copyright 2020-2022 Signal Messenger, LLC.
3// SPDX-License-Identifier: AGPL-3.0-only
4//
5use partial_default::PartialDefault;
6use serde::{Deserialize, Serialize, Serializer};
7
8use crate::auth::AuthCredentialWithPniZkcPresentation;
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(Serialize, Deserialize, PartialDefault)]
16pub struct AuthCredentialWithPniPresentation {
17    pub(crate) version: VersionByte<PRESENTATION_VERSION_3>,
18    pub(crate) proof: crypto::proofs::AuthCredentialWithPniPresentationProof,
19    pub(crate) aci_ciphertext: crypto::uid_encryption::Ciphertext,
20    pub(crate) pni_ciphertext: crypto::uid_encryption::Ciphertext,
21    pub(crate) redemption_time: Timestamp,
22}
23
24impl AuthCredentialWithPniPresentation {
25    pub fn get_aci_ciphertext(&self) -> api::groups::UuidCiphertext {
26        api::groups::UuidCiphertext {
27            reserved: Default::default(),
28            ciphertext: self.aci_ciphertext,
29        }
30    }
31
32    pub fn get_pni_ciphertext(&self) -> api::groups::UuidCiphertext {
33        api::groups::UuidCiphertext {
34            reserved: Default::default(),
35            ciphertext: self.pni_ciphertext,
36        }
37    }
38
39    pub fn get_redemption_time(&self) -> Timestamp {
40        self.redemption_time
41    }
42}
43
44#[allow(clippy::large_enum_variant)]
45pub enum AnyAuthCredentialPresentation {
46    V3(AuthCredentialWithPniPresentation),
47    V4(AuthCredentialWithPniZkcPresentation),
48}
49
50#[repr(u8)]
51#[derive(
52    Copy, Clone, Debug, PartialDefault, num_enum::IntoPrimitive, num_enum::TryFromPrimitive,
53)]
54enum PresentationVersion {
55    // V1 and V2 are no longer supported.
56    #[partial_default]
57    V3 = PRESENTATION_VERSION_3,
58    V4 = PRESENTATION_VERSION_4,
59}
60
61impl AnyAuthCredentialPresentation {
62    pub fn new(presentation_bytes: &[u8]) -> Result<Self, ZkGroupDeserializationFailure> {
63        let first = *presentation_bytes
64            .first()
65            .ok_or(ZkGroupDeserializationFailure::new::<Self>())?;
66        let version = PresentationVersion::try_from(first)
67            .map_err(|_| ZkGroupDeserializationFailure::new::<Self>())?;
68        match version {
69            PresentationVersion::V3 => Ok(crate::deserialize::<AuthCredentialWithPniPresentation>(
70                presentation_bytes,
71            )?
72            .into()),
73            PresentationVersion::V4 => Ok(crate::deserialize::<
74                AuthCredentialWithPniZkcPresentation,
75            >(presentation_bytes)?
76            .into()),
77        }
78    }
79
80    pub fn get_uuid_ciphertext(&self) -> api::groups::UuidCiphertext {
81        match self {
82            AnyAuthCredentialPresentation::V3(presentation) => presentation.get_aci_ciphertext(),
83            AnyAuthCredentialPresentation::V4(presentation) => presentation.aci_ciphertext(),
84        }
85    }
86
87    pub fn get_pni_ciphertext(&self) -> Option<api::groups::UuidCiphertext> {
88        // Even though the current implementation of this function could return
89        // a non-optional value, we might want to support PNI-less credentials
90        // in the future. Keep the optionality in the signature to make it
91        // easier to transition when that happens.
92        Some(match self {
93            AnyAuthCredentialPresentation::V3(presentation) => presentation.get_pni_ciphertext(),
94            AnyAuthCredentialPresentation::V4(presentation) => presentation.pni_ciphertext(),
95        })
96    }
97
98    pub fn get_redemption_time(&self) -> Timestamp {
99        match self {
100            AnyAuthCredentialPresentation::V3(presentation) => presentation.get_redemption_time(),
101            AnyAuthCredentialPresentation::V4(presentation) => presentation.redemption_time(),
102        }
103    }
104}
105
106impl Serialize for AnyAuthCredentialPresentation {
107    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
108    where
109        S: Serializer,
110    {
111        match self {
112            AnyAuthCredentialPresentation::V3(presentation) => presentation.serialize(serializer),
113            AnyAuthCredentialPresentation::V4(presentation) => presentation.serialize(serializer),
114        }
115    }
116}
117
118impl From<AuthCredentialWithPniPresentation> for AnyAuthCredentialPresentation {
119    fn from(presentation: AuthCredentialWithPniPresentation) -> Self {
120        Self::V3(presentation)
121    }
122}
123impl From<AuthCredentialWithPniZkcPresentation> for AnyAuthCredentialPresentation {
124    fn from(presentation: AuthCredentialWithPniZkcPresentation) -> Self {
125        Self::V4(presentation)
126    }
127}