Add project files.

This commit is contained in:
micro
2026-06-20 09:31:59 -05:00
parent 9bb3f00d86
commit 4baa4ce8c0
346 changed files with 55751 additions and 0 deletions
@@ -0,0 +1,73 @@
use partial_default::PartialDefault;
use serde::{Serialize, Serializer};
use crate::api;
use crate::auth::AuthCredentialWithPniZkcPresentation;
use crate::common::constants::*;
use crate::common::errors::*;
use crate::common::simple_types::*;
#[derive(derive_more::From)]
pub enum AnyAuthCredentialPresentation {
V4(AuthCredentialWithPniZkcPresentation),
}
#[repr(u8)]
#[derive(Copy, Clone, Debug, PartialDefault, derive_more::TryFrom)]
#[try_from(repr)]
enum PresentationVersion {
// V1-V3 are no longer supported.
#[partial_default]
V4 = PRESENTATION_VERSION_4,
}
impl From<PresentationVersion> for u8 {
fn from(value: PresentationVersion) -> Self {
value as u8
}
}
impl AnyAuthCredentialPresentation {
pub fn new(presentation_bytes: &[u8]) -> Result<Self, ZkGroupDeserializationFailure> {
let first = *presentation_bytes
.first()
.ok_or(ZkGroupDeserializationFailure::new::<Self>())?;
let version = PresentationVersion::try_from(first)
.map_err(|_| ZkGroupDeserializationFailure::new::<Self>())?;
match version {
PresentationVersion::V4 => Ok(crate::deserialize::<
AuthCredentialWithPniZkcPresentation,
>(presentation_bytes)?
.into()),
}
}
pub fn get_aci_ciphertext(&self) -> api::groups::UuidCiphertext {
match self {
AnyAuthCredentialPresentation::V4(presentation) => presentation.aci_ciphertext(),
}
}
pub fn get_pni_ciphertext(&self) -> api::groups::UuidCiphertext {
match self {
AnyAuthCredentialPresentation::V4(presentation) => presentation.pni_ciphertext(),
}
}
pub fn get_redemption_time(&self) -> Timestamp {
match self {
AnyAuthCredentialPresentation::V4(presentation) => presentation.redemption_time(),
}
}
}
impl Serialize for AnyAuthCredentialPresentation {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
match self {
AnyAuthCredentialPresentation::V4(presentation) => presentation.serialize(serializer),
}
}
}
@@ -0,0 +1,121 @@
//
// Copyright 2022 Signal Messenger, LLC.
// SPDX-License-Identifier: AGPL-3.0-only
//
use libsignal_core::{Aci, Pni};
use partial_default::PartialDefault;
use serde::Serialize;
use crate::auth::AnyAuthCredentialPresentation;
use crate::groups::GroupSecretParams;
use crate::{
RandomnessBytes, ServerPublicParams, ZkGroupDeserializationFailure, ZkGroupVerificationFailure,
};
mod zkc;
pub use zkc::{
AuthCredentialWithPniZkc, AuthCredentialWithPniZkcPresentation,
AuthCredentialWithPniZkcResponse,
};
#[derive(Clone, PartialDefault, derive_more::From)]
pub enum AuthCredentialWithPni {
#[partial_default]
Zkc(AuthCredentialWithPniZkc),
}
#[derive(Clone, PartialDefault, derive_more::From)]
pub enum AuthCredentialWithPniResponse {
#[partial_default]
Zkc(AuthCredentialWithPniZkcResponse),
}
#[repr(u8)]
#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialDefault, derive_more::TryFrom)]
#[try_from(repr)]
pub enum AuthCredentialWithPniVersion {
#[partial_default]
Zkc = 3,
}
impl AuthCredentialWithPni {
pub fn new(bytes: &[u8]) -> Result<Self, ZkGroupDeserializationFailure> {
let first = bytes
.first()
.ok_or_else(ZkGroupDeserializationFailure::new::<Self>)?;
let version = AuthCredentialWithPniVersion::try_from(*first)
.map_err(|_| ZkGroupDeserializationFailure::new::<Self>())?;
match version {
AuthCredentialWithPniVersion::Zkc => {
crate::common::serialization::deserialize(bytes).map(Self::Zkc)
}
}
}
pub fn present(
&self,
public_params: &ServerPublicParams,
group_secret_params: &GroupSecretParams,
randomness: RandomnessBytes,
) -> AnyAuthCredentialPresentation {
match self {
Self::Zkc(credential) => AnyAuthCredentialPresentation::V4(credential.present(
public_params,
group_secret_params,
randomness,
)),
}
}
}
impl AuthCredentialWithPniResponse {
pub fn new(bytes: &[u8]) -> Result<Self, ZkGroupDeserializationFailure> {
let first = bytes
.first()
.ok_or_else(ZkGroupDeserializationFailure::new::<Self>)?;
let version = AuthCredentialWithPniVersion::try_from(*first)
.map_err(|_| ZkGroupDeserializationFailure::new::<Self>())?;
match version {
AuthCredentialWithPniVersion::Zkc => {
crate::common::serialization::deserialize(bytes).map(Self::Zkc)
}
}
}
pub fn receive(
self,
public_params: &ServerPublicParams,
aci: Aci,
pni: Pni,
redemption_time: crate::Timestamp,
) -> Result<AuthCredentialWithPni, ZkGroupVerificationFailure> {
match self {
Self::Zkc(credential) => credential
.receive(aci, pni, redemption_time, public_params)
.map(AuthCredentialWithPni::Zkc),
}
}
}
impl Serialize for AuthCredentialWithPni {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
match self {
Self::Zkc(z) => z.serialize(serializer),
}
}
}
impl Serialize for AuthCredentialWithPniResponse {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
match self {
Self::Zkc(z) => z.serialize(serializer),
}
}
}
@@ -0,0 +1,267 @@
//
// Copyright 2024 Signal Messenger, LLC.
// SPDX-License-Identifier: AGPL-3.0-only
//
use libsignal_core::{Aci, Pni};
use partial_default::PartialDefault;
use serde::{Deserialize, Serialize};
use zkcredential::credentials::{CredentialKeyPair, CredentialPublicKey};
use crate::api::auth::auth_credential_with_pni::AuthCredentialWithPniVersion;
use crate::common::constants::PRESENTATION_VERSION_4;
use crate::common::serialization::VersionByte;
use crate::common::simple_types::{RandomnessBytes, Timestamp};
use crate::crypto::uid_encryption;
use crate::crypto::uid_struct::UidStruct;
use crate::groups::{GroupPublicParams, GroupSecretParams, UuidCiphertext};
use crate::{ServerPublicParams, ServerSecretParams, ZkGroupVerificationFailure};
const CREDENTIAL_LABEL: &[u8] = b"20240222_Signal_AuthCredentialZkc";
/// Authentication credential implemented using [`zkcredential`].
///
/// The same credential as [`crate::api::auth::AuthCredentialWithPni`] but
/// implemented using the types and mechanism from the `zkcredential` crate.
#[derive(Clone, Serialize, Deserialize, PartialDefault)]
pub struct AuthCredentialWithPniZkc {
version: VersionByte<{ AuthCredentialWithPniVersion::Zkc as u8 }>,
credential: zkcredential::credentials::Credential,
aci: UidStruct,
pni: UidStruct,
redemption_time: Timestamp,
}
#[derive(Clone, Serialize, Deserialize, PartialDefault)]
pub struct AuthCredentialWithPniZkcResponse {
version: VersionByte<{ AuthCredentialWithPniVersion::Zkc as u8 }>,
proof: zkcredential::issuance::IssuanceProof,
}
#[derive(Clone, Serialize, Deserialize, PartialDefault)]
pub struct AuthCredentialWithPniZkcPresentation {
version: VersionByte<PRESENTATION_VERSION_4>,
proof: zkcredential::presentation::PresentationProof,
aci_ciphertext: uid_encryption::Ciphertext,
pni_ciphertext: uid_encryption::Ciphertext,
redemption_time: Timestamp,
}
impl AuthCredentialWithPniZkcResponse {
pub fn issue_credential(
aci: Aci,
pni: Pni,
redemption_time: Timestamp,
params: &ServerSecretParams,
randomness: RandomnessBytes,
) -> Self {
Self::issue_credential_for_key(
aci,
pni,
redemption_time,
&params.generic_credential_key_pair,
randomness,
)
}
pub fn receive(
self,
aci: Aci,
pni: Pni,
redemption_time: Timestamp,
public_params: &ServerPublicParams,
) -> Result<AuthCredentialWithPniZkc, ZkGroupVerificationFailure> {
self.receive_for_key(
aci,
pni,
redemption_time,
&public_params.generic_credential_public_key,
)
}
pub(crate) fn issue_credential_for_key(
aci: Aci,
pni: Pni,
redemption_time: Timestamp,
credential_key: &CredentialKeyPair,
randomness: RandomnessBytes,
) -> Self {
let proof = zkcredential::issuance::IssuanceProofBuilder::new(CREDENTIAL_LABEL)
.add_attribute(&UidStruct::from_service_id(aci.into()))
.add_attribute(&UidStruct::from_service_id(pni.into()))
.add_public_attribute(&redemption_time)
.issue(credential_key, randomness);
Self {
version: VersionByte,
proof,
}
}
pub(crate) fn receive_for_key(
self,
aci: Aci,
pni: Pni,
redemption_time: Timestamp,
public_key: &CredentialPublicKey,
) -> Result<AuthCredentialWithPniZkc, ZkGroupVerificationFailure> {
if !redemption_time.is_day_aligned() {
return Err(ZkGroupVerificationFailure);
}
let aci = UidStruct::from_service_id(aci.into());
let pni = UidStruct::from_service_id(pni.into());
let raw_credential = zkcredential::issuance::IssuanceProofBuilder::new(CREDENTIAL_LABEL)
.add_attribute(&aci)
.add_attribute(&pni)
.add_public_attribute(&redemption_time)
.verify(public_key, self.proof)?;
Ok(AuthCredentialWithPniZkc {
credential: raw_credential,
version: VersionByte,
aci,
pni,
redemption_time,
})
}
}
impl AuthCredentialWithPniZkc {
pub fn present(
&self,
public_params: &ServerPublicParams,
group_secret_params: &GroupSecretParams,
randomness: RandomnessBytes,
) -> AuthCredentialWithPniZkcPresentation {
self.present_for_key(
&public_params.generic_credential_public_key,
group_secret_params,
randomness,
)
}
pub(crate) fn present_for_key(
&self,
public_key: &CredentialPublicKey,
group_secret_params: &GroupSecretParams,
randomness: RandomnessBytes,
) -> AuthCredentialWithPniZkcPresentation {
let Self {
aci,
credential,
pni,
redemption_time,
version: _,
} = self;
let proof = zkcredential::presentation::PresentationProofBuilder::new(CREDENTIAL_LABEL)
.add_attribute(aci, &group_secret_params.uid_enc_key_pair)
.add_attribute(pni, &group_secret_params.uid_enc_key_pair)
.present(public_key, credential, randomness);
AuthCredentialWithPniZkcPresentation {
aci_ciphertext: group_secret_params.uid_enc_key_pair.encrypt(&self.aci),
pni_ciphertext: group_secret_params.uid_enc_key_pair.encrypt(&self.pni),
proof,
redemption_time: *redemption_time,
version: VersionByte,
}
}
}
impl AuthCredentialWithPniZkcPresentation {
pub fn verify(
&self,
params: &ServerSecretParams,
group_public_params: &GroupPublicParams,
redemption_time: Timestamp,
) -> Result<(), ZkGroupVerificationFailure> {
self.verify_for_key(
&params.generic_credential_key_pair,
group_public_params,
redemption_time,
)
}
pub(crate) fn verify_for_key(
&self,
credential_key: &CredentialKeyPair,
group_public_params: &GroupPublicParams,
redemption_time: Timestamp,
) -> Result<(), ZkGroupVerificationFailure> {
zkcredential::presentation::PresentationProofVerifier::new(CREDENTIAL_LABEL)
.add_attribute(
&self.aci_ciphertext,
&group_public_params.uid_enc_public_key,
)
.add_attribute(
&self.pni_ciphertext,
&group_public_params.uid_enc_public_key,
)
.add_public_attribute(&redemption_time)
.verify(credential_key, &self.proof)
.map_err(|_| ZkGroupVerificationFailure)
}
pub fn aci_ciphertext(&self) -> UuidCiphertext {
UuidCiphertext {
reserved: Default::default(),
ciphertext: self.aci_ciphertext,
}
}
pub fn pni_ciphertext(&self) -> UuidCiphertext {
UuidCiphertext {
reserved: Default::default(),
ciphertext: self.pni_ciphertext,
}
}
pub fn redemption_time(&self) -> Timestamp {
self.redemption_time
}
}
#[cfg(test)]
mod test {
use zkcredential::RANDOMNESS_LEN;
use super::*;
use crate::SECONDS_PER_DAY;
#[test]
fn issue_receive_present() {
const ACI: Aci = Aci::from_uuid_bytes([b'a'; 16]);
const PNI: Pni = Pni::from_uuid_bytes([b'p'; 16]);
const REDEMPTION_TIME: Timestamp = Timestamp::from_epoch_seconds(12345 * SECONDS_PER_DAY);
let credential_key = CredentialKeyPair::generate([1; RANDOMNESS_LEN]);
let public_key = credential_key.public_key();
let group_secret_params = GroupSecretParams::generate([2; RANDOMNESS_LEN]);
let response = AuthCredentialWithPniZkcResponse::issue_credential_for_key(
ACI,
PNI,
REDEMPTION_TIME,
&credential_key,
[3; RANDOMNESS_LEN],
);
let credential = response
.receive_for_key(ACI, PNI, REDEMPTION_TIME, public_key)
.expect("is valid");
let presentation =
credential.present_for_key(public_key, &group_secret_params, [4; RANDOMNESS_LEN]);
presentation
.verify_for_key(
&credential_key,
&group_secret_params.get_public_params(),
REDEMPTION_TIME,
)
.expect("can verify")
}
}
+383
View File
@@ -0,0 +1,383 @@
//
// Copyright 2020 Signal Messenger, LLC.
// SPDX-License-Identifier: AGPL-3.0-only
//
use aes_gcm_siv::aead::Aead;
use aes_gcm_siv::aead::generic_array::GenericArray;
use aes_gcm_siv::{Aes256GcmSiv, KeyInit};
use partial_default::PartialDefault;
use serde::{Deserialize, Serialize};
use crate::common::constants::*;
use crate::common::errors::*;
use crate::common::serialization::ReservedByte;
use crate::common::sho::*;
use crate::common::simple_types::*;
use crate::crypto::uid_encryption;
use crate::{api, crypto};
#[derive(Copy, Clone, Serialize, Deserialize, Default)]
pub struct GroupMasterKey {
pub(crate) bytes: [u8; GROUP_MASTER_KEY_LEN],
}
#[derive(Copy, Clone, Serialize, Deserialize, PartialDefault)]
pub struct GroupSecretParams {
reserved: ReservedByte,
master_key: GroupMasterKey,
group_id: GroupIdentifierBytes,
blob_key: AesKeyBytes,
pub(crate) uid_enc_key_pair: crypto::uid_encryption::KeyPair,
pub(crate) profile_key_enc_key_pair: crypto::profile_key_encryption::KeyPair,
}
impl AsRef<uid_encryption::KeyPair> for GroupSecretParams {
fn as_ref(&self) -> &uid_encryption::KeyPair {
&self.uid_enc_key_pair
}
}
#[derive(Copy, Clone, Serialize, Deserialize, PartialDefault)]
pub struct GroupPublicParams {
reserved: ReservedByte,
group_id: GroupIdentifierBytes,
pub(crate) uid_enc_public_key: crypto::uid_encryption::PublicKey,
pub(crate) profile_key_enc_public_key: crypto::profile_key_encryption::PublicKey,
}
impl GroupMasterKey {
pub fn new(bytes: [u8; GROUP_MASTER_KEY_LEN]) -> Self {
GroupMasterKey { bytes }
}
}
const ENCRYPTED_BLOB_PADDING_LENGTH_SIZE: usize = std::mem::size_of::<u32>();
impl GroupSecretParams {
pub fn generate(randomness: RandomnessBytes) -> Self {
let mut sho = Sho::new(
b"Signal_ZKGroup_20200424_Random_GroupSecretParams_Generate",
&randomness,
);
let master_key = GroupMasterKey::new(sho.squeeze_as_array());
GroupSecretParams::derive_from_master_key(master_key)
}
pub fn derive_from_master_key(master_key: GroupMasterKey) -> Self {
let mut sho = Sho::new(
b"Signal_ZKGroup_20200424_GroupMasterKey_GroupSecretParams_DeriveFromMasterKey",
&master_key.bytes,
);
let group_id: GroupIdentifierBytes = sho.squeeze_as_array();
let blob_key: AesKeyBytes = sho.squeeze_as_array();
let uid_enc_key_pair = crypto::uid_encryption::KeyPair::derive_from(sho.as_mut());
let profile_key_enc_key_pair =
crypto::profile_key_encryption::KeyPair::derive_from(sho.as_mut());
Self {
reserved: Default::default(),
master_key,
group_id,
blob_key,
uid_enc_key_pair,
profile_key_enc_key_pair,
}
}
pub fn get_master_key(&self) -> GroupMasterKey {
self.master_key
}
pub fn get_group_identifier(&self) -> GroupIdentifierBytes {
self.group_id
}
pub fn get_public_params(&self) -> GroupPublicParams {
GroupPublicParams {
reserved: Default::default(),
uid_enc_public_key: self.uid_enc_key_pair.public_key,
profile_key_enc_public_key: self.profile_key_enc_key_pair.public_key,
group_id: self.group_id,
}
}
pub fn encrypt_service_id(
&self,
service_id: libsignal_core::ServiceId,
) -> api::groups::UuidCiphertext {
let uid = crypto::uid_struct::UidStruct::from_service_id(service_id);
self.encrypt_uid_struct(uid)
}
pub fn encrypt_uid_struct(
&self,
uid: crypto::uid_struct::UidStruct,
) -> api::groups::UuidCiphertext {
let ciphertext = self.uid_enc_key_pair.encrypt(&uid);
api::groups::UuidCiphertext {
reserved: Default::default(),
ciphertext,
}
}
pub fn decrypt_service_id(
&self,
ciphertext: api::groups::UuidCiphertext,
) -> Result<libsignal_core::ServiceId, ZkGroupVerificationFailure> {
crypto::uid_encryption::UidEncryptionDomain::decrypt(
&self.uid_enc_key_pair,
&ciphertext.ciphertext,
)
}
pub fn encrypt_profile_key(
&self,
profile_key: api::profiles::ProfileKey,
user_id: libsignal_core::Aci,
) -> api::groups::ProfileKeyCiphertext {
self.encrypt_profile_key_bytes(profile_key.bytes, user_id)
}
pub fn encrypt_profile_key_bytes(
&self,
profile_key_bytes: ProfileKeyBytes,
user_id: libsignal_core::Aci,
) -> api::groups::ProfileKeyCiphertext {
let profile_key = crypto::profile_key_struct::ProfileKeyStruct::new(
profile_key_bytes,
uuid::Uuid::from(user_id).into_bytes(),
);
let ciphertext = self.profile_key_enc_key_pair.encrypt(&profile_key);
api::groups::ProfileKeyCiphertext {
reserved: Default::default(),
ciphertext,
}
}
pub fn decrypt_profile_key(
&self,
ciphertext: api::groups::ProfileKeyCiphertext,
user_id: libsignal_core::Aci,
) -> Result<api::profiles::ProfileKey, ZkGroupVerificationFailure> {
let profile_key_struct =
crypto::profile_key_encryption::ProfileKeyEncryptionDomain::decrypt(
&self.profile_key_enc_key_pair,
&ciphertext.ciphertext,
uuid::Uuid::from(user_id).into_bytes(),
)?;
Ok(api::profiles::ProfileKey {
bytes: profile_key_struct.bytes,
})
}
pub fn encrypt_blob(&self, randomness: RandomnessBytes, plaintext: &[u8]) -> Vec<u8> {
let mut sho = Sho::new(
b"Signal_ZKGroup_20200424_Random_GroupSecretParams_EncryptBlob",
&randomness,
);
let nonce_vec = sho.squeeze_as_array::<AESGCM_NONCE_LEN>();
let mut ciphertext_vec = self.encrypt_blob_aesgcmsiv(&self.blob_key, &nonce_vec, plaintext);
ciphertext_vec.extend(nonce_vec);
ciphertext_vec.extend([0u8]); // reserved byte
ciphertext_vec
}
pub fn encrypt_blob_with_padding(
&self,
randomness: RandomnessBytes,
plaintext: &[u8],
padding_len: u32,
) -> Vec<u8> {
let full_length =
ENCRYPTED_BLOB_PADDING_LENGTH_SIZE + plaintext.len() + padding_len as usize;
let mut padded_plaintext = Vec::with_capacity(full_length);
padded_plaintext.extend_from_slice(&padding_len.to_be_bytes());
padded_plaintext.extend_from_slice(plaintext);
padded_plaintext.resize(full_length, 0);
self.encrypt_blob(randomness, &padded_plaintext)
}
pub fn decrypt_blob(&self, ciphertext: &[u8]) -> Result<Vec<u8>, ZkGroupVerificationFailure> {
if ciphertext.len() < AESGCM_NONCE_LEN + 1 {
// AESGCM_NONCE_LEN = 12 bytes for IV
return Err(ZkGroupVerificationFailure);
}
let unreserved_len = ciphertext.len() - 1;
let (ciphertext, nonce) = ciphertext[..unreserved_len]
.split_last_chunk::<AESGCM_NONCE_LEN>()
.expect("checked length already");
self.decrypt_blob_aesgcmsiv(&self.blob_key, nonce, ciphertext)
}
pub fn decrypt_blob_with_padding(
&self,
ciphertext: &[u8],
) -> Result<Vec<u8>, ZkGroupVerificationFailure> {
let mut decrypted = self.decrypt_blob(ciphertext)?;
let (padding_len_bytes, plaintext_plus_padding) = decrypted
.split_first_chunk::<ENCRYPTED_BLOB_PADDING_LENGTH_SIZE>()
.ok_or(ZkGroupVerificationFailure)?;
let padding_len = u32::from_be_bytes(*padding_len_bytes);
if plaintext_plus_padding.len() < padding_len as usize {
return Err(ZkGroupVerificationFailure);
}
decrypted.truncate(decrypted.len() - padding_len as usize);
decrypted.drain(..ENCRYPTED_BLOB_PADDING_LENGTH_SIZE);
Ok(decrypted)
}
fn encrypt_blob_aesgcmsiv(&self, key: &[u8], nonce: &[u8], plaintext: &[u8]) -> Vec<u8> {
let key = GenericArray::from_slice(key);
let aead_cipher = Aes256GcmSiv::new(key);
let nonce = GenericArray::from_slice(nonce);
aead_cipher
.encrypt(nonce, plaintext)
.expect("aead encrypt failure")
}
fn decrypt_blob_aesgcmsiv(
&self,
key: &[u8],
nonce: &[u8],
ciphertext: &[u8],
) -> Result<Vec<u8>, ZkGroupVerificationFailure> {
if ciphertext.len() < AESGCM_TAG_LEN {
// AESGCM_TAG_LEN = 16 bytes for tag
return Err(ZkGroupVerificationFailure);
}
let key = GenericArray::from_slice(key);
let aead_cipher = Aes256GcmSiv::new(key);
let nonce = GenericArray::from_slice(nonce);
match aead_cipher.decrypt(nonce, ciphertext) {
Ok(plaintext_vec) => Ok(plaintext_vec),
Err(_) => Err(ZkGroupVerificationFailure),
}
}
}
impl GroupPublicParams {
pub fn get_group_identifier(&self) -> GroupIdentifierBytes {
self.group_id
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_aesgcmsiv_vec1() {
// https://tools.ietf.org/html/rfc8452#appendix-C
let group_secret_params = GroupSecretParams::generate([0u8; RANDOMNESS_LEN]);
let plaintext_vec = vec![
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
];
let key = [
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
];
let nonce = [
0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
];
let ciphertext = [
0x4a, 0x6a, 0x9d, 0xb4, 0xc8, 0xc6, 0x54, 0x92, 0x01, 0xb9, 0xed, 0xb5, 0x30, 0x06,
0xcb, 0xa8, 0x21, 0xec, 0x9c, 0xf8, 0x50, 0x94, 0x8a, 0x7c, 0x86, 0xc6, 0x8a, 0xc7,
0x53, 0x9d, 0x02, 0x7f, 0xe8, 0x19, 0xe6, 0x3a, 0xbc, 0xd0, 0x20, 0xb0, 0x06, 0xa9,
0x76, 0x39, 0x76, 0x32, 0xeb, 0x5d,
];
let calc_ciphertext =
group_secret_params.encrypt_blob_aesgcmsiv(&key, &nonce, &plaintext_vec);
assert!(calc_ciphertext[..ciphertext.len()] == ciphertext[..]);
let calc_plaintext = group_secret_params
.decrypt_blob_aesgcmsiv(&key, &nonce, &calc_ciphertext)
.unwrap();
assert!(calc_plaintext[..] == plaintext_vec[..]);
}
#[test]
fn test_aesgcmsiv_vec2() {
// https://tools.ietf.org/html/rfc8452#appendix-C
let group_secret_params = GroupSecretParams::generate([0u8; RANDOMNESS_LEN]);
let plaintext_vec = vec![
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x4d, 0xb9, 0x23, 0xdc, 0x79, 0x3e, 0xe6, 0x49, 0x7c, 0x76, 0xdc, 0xc0,
0x3a, 0x98, 0xe1, 0x08,
];
let key = [
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
];
let nonce = [
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
];
let ciphertext = [
0xf3, 0xf8, 0x0f, 0x2c, 0xf0, 0xcb, 0x2d, 0xd9, 0xc5, 0x98, 0x4f, 0xcd, 0xa9, 0x08,
0x45, 0x6c, 0xc5, 0x37, 0x70, 0x3b, 0x5b, 0xa7, 0x03, 0x24, 0xa6, 0x79, 0x3a, 0x7b,
0xf2, 0x18, 0xd3, 0xea, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
];
let calc_ciphertext =
group_secret_params.encrypt_blob_aesgcmsiv(&key, &nonce, &plaintext_vec);
assert!(calc_ciphertext[..ciphertext.len()] == ciphertext[..]);
let calc_plaintext = group_secret_params
.decrypt_blob_aesgcmsiv(&key, &nonce, &calc_ciphertext)
.unwrap();
assert!(calc_plaintext[..] == plaintext_vec[..]);
}
#[test]
fn test_encrypt_with_padding() {
let group_secret_params = GroupSecretParams::generate([0u8; RANDOMNESS_LEN]);
let plaintext = b"secret team";
{
let expected_ciphertext_hex = "3798afe9c65ffb35a63b2c048b16f19dd50ee9acc33cc925667a9abad4d4c6f86675fa8e32243e0831203700";
let calc_ciphertext =
group_secret_params.encrypt_blob_with_padding([0u8; RANDOMNESS_LEN], plaintext, 0);
assert_eq!(hex::encode(&calc_ciphertext), expected_ciphertext_hex);
let calc_plaintext = group_secret_params
.decrypt_blob_with_padding(&calc_ciphertext)
.unwrap();
assert_eq!(calc_plaintext[..], plaintext[..]);
}
{
let expected_ciphertext_hex = "880a70e071b33f81e1219842c8514f34901abb734c191292ac325455d898da000484080099c620f86675fa8e32243e0831203700";
let calc_ciphertext =
group_secret_params.encrypt_blob_with_padding([0u8; RANDOMNESS_LEN], plaintext, 8);
assert_eq!(hex::encode(&calc_ciphertext), expected_ciphertext_hex);
let calc_plaintext = group_secret_params
.decrypt_blob_with_padding(&calc_ciphertext)
.unwrap();
assert_eq!(calc_plaintext[..], plaintext[..]);
}
}
}
@@ -0,0 +1,16 @@
//
// Copyright 2020 Signal Messenger, LLC.
// SPDX-License-Identifier: AGPL-3.0-only
//
use partial_default::PartialDefault;
use serde::{Deserialize, Serialize};
use crate::common::serialization::ReservedByte;
use crate::crypto;
#[derive(Copy, Clone, Serialize, Deserialize, PartialEq, Eq, PartialDefault)]
pub struct ProfileKeyCiphertext {
pub(crate) reserved: ReservedByte,
pub(crate) ciphertext: crypto::profile_key_encryption::Ciphertext,
}
+597
View File
@@ -0,0 +1,597 @@
//
// Copyright 2020-2022 Signal Messenger, LLC.
// SPDX-License-Identifier: AGPL-3.0-only
//
use partial_default::PartialDefault;
use serde::{Deserialize, Serialize};
use crate::common::constants::*;
use crate::common::errors::*;
use crate::common::serialization::{ReservedByte, VersionByte};
use crate::common::sho::*;
use crate::common::simple_types::*;
use crate::{api, crypto};
#[derive(Clone, Serialize, Deserialize, PartialDefault)]
pub struct ServerSecretParams {
reserved: ReservedByte,
// Now unused
auth_credentials_key_pair: crypto::credentials::KeyPair<crypto::credentials::AuthCredential>,
// Now unused
pub(crate) profile_key_credentials_key_pair:
crypto::credentials::KeyPair<crypto::credentials::ProfileKeyCredential>,
sig_key_pair: crypto::signature::KeyPair,
receipt_credentials_key_pair:
crypto::credentials::KeyPair<crypto::credentials::ReceiptCredential>,
// Now unused
pni_credentials_key_pair: crypto::credentials::KeyPair<crypto::credentials::PniCredential>,
expiring_profile_key_credentials_key_pair:
crypto::credentials::KeyPair<crypto::credentials::ExpiringProfileKeyCredential>,
// Now unused
auth_credentials_with_pni_key_pair:
crypto::credentials::KeyPair<crypto::credentials::AuthCredentialWithPni>,
pub(crate) generic_credential_key_pair: zkcredential::credentials::CredentialKeyPair,
pub(crate) endorsement_key_pair: zkcredential::endorsements::ServerRootKeyPair,
}
impl AsRef<zkcredential::endorsements::ServerRootKeyPair> for ServerSecretParams {
fn as_ref(&self) -> &zkcredential::endorsements::ServerRootKeyPair {
&self.endorsement_key_pair
}
}
#[derive(Clone, Serialize, Deserialize, PartialDefault)]
pub struct ServerPublicParams {
reserved: ReservedByte,
// Now unused
auth_credentials_public_key: crypto::credentials::PublicKey,
// Now unused
pub(crate) profile_key_credentials_public_key: crypto::credentials::PublicKey,
sig_public_key: crypto::signature::PublicKey,
receipt_credentials_public_key: crypto::credentials::PublicKey,
// Now unused
pni_credentials_public_key: crypto::credentials::PublicKey,
expiring_profile_key_credentials_public_key: crypto::credentials::PublicKey,
// Now unused
auth_credentials_with_pni_public_key: crypto::credentials::PublicKey,
pub(crate) generic_credential_public_key: zkcredential::credentials::CredentialPublicKey,
pub(crate) endorsement_public_key: zkcredential::endorsements::ServerRootPublicKey,
}
impl AsRef<zkcredential::endorsements::ServerRootPublicKey> for ServerPublicParams {
fn as_ref(&self) -> &zkcredential::endorsements::ServerRootPublicKey {
&self.endorsement_public_key
}
}
impl ServerSecretParams {
pub fn generate(randomness: RandomnessBytes) -> Self {
let mut sho = Sho::new(
b"Signal_ZKGroup_20200424_Random_ServerSecretParams_Generate",
&randomness,
);
let auth_credentials_key_pair = crypto::credentials::KeyPair::generate(&mut sho);
let profile_key_credentials_key_pair = crypto::credentials::KeyPair::generate(&mut sho);
let sig_key_pair = crypto::signature::KeyPair::generate(&mut sho);
let receipt_credentials_key_pair = crypto::credentials::KeyPair::generate(&mut sho);
let pni_credentials_key_pair = crypto::credentials::KeyPair::generate(&mut sho);
let expiring_profile_key_credentials_key_pair =
crypto::credentials::KeyPair::generate(&mut sho);
let auth_credentials_with_pni_key_pair = crypto::credentials::KeyPair::generate(&mut sho);
let generic_credential_key_pair =
zkcredential::credentials::CredentialKeyPair::generate(randomness);
let endorsement_key_pair =
zkcredential::endorsements::ServerRootKeyPair::generate(randomness);
Self {
reserved: Default::default(),
auth_credentials_key_pair,
profile_key_credentials_key_pair,
sig_key_pair,
receipt_credentials_key_pair,
pni_credentials_key_pair,
expiring_profile_key_credentials_key_pair,
auth_credentials_with_pni_key_pair,
generic_credential_key_pair,
endorsement_key_pair,
}
}
pub fn get_endorsement_root_key_pair(&self) -> EndorsementServerRootKeyPair {
EndorsementServerRootKeyPair {
reserved: Default::default(),
key_pair: self.endorsement_key_pair.clone(),
}
}
pub fn get_public_params(&self) -> ServerPublicParams {
ServerPublicParams {
reserved: Default::default(),
auth_credentials_public_key: self.auth_credentials_key_pair.get_public_key(),
profile_key_credentials_public_key: self
.profile_key_credentials_key_pair
.get_public_key(),
sig_public_key: self.sig_key_pair.get_public_key(),
receipt_credentials_public_key: self.receipt_credentials_key_pair.get_public_key(),
pni_credentials_public_key: self.pni_credentials_key_pair.get_public_key(),
expiring_profile_key_credentials_public_key: self
.expiring_profile_key_credentials_key_pair
.get_public_key(),
auth_credentials_with_pni_public_key: self
.auth_credentials_with_pni_key_pair
.get_public_key(),
generic_credential_public_key: self.generic_credential_key_pair.public_key().clone(),
endorsement_public_key: self.endorsement_key_pair.public_key().clone(),
}
}
pub fn sign(&self, randomness: RandomnessBytes, message: &[u8]) -> NotarySignatureBytes {
let mut sho = Sho::new(
b"Signal_ZKGroup_20200424_Random_ServerSecretParams_Sign",
&randomness,
);
self.sig_key_pair.sign(message, &mut sho)
}
/// Checks that `current_time` is within the validity window defined by
/// `redemption_time`.
///
/// All times are relative to SystemTime::UNIX_EPOCH,
/// but we don't actually use SystemTime because it's too small on 32-bit Linux.
pub(crate) fn check_auth_credential_redemption_time(
redemption_time: Timestamp,
current_time: Timestamp,
) -> Result<(), ZkGroupVerificationFailure> {
let acceptable_start_time = redemption_time
.checked_sub_seconds(SECONDS_PER_DAY)
.ok_or(ZkGroupVerificationFailure)?;
let acceptable_end_time = redemption_time
.checked_add_seconds(2 * SECONDS_PER_DAY)
.ok_or(ZkGroupVerificationFailure)?;
if !(acceptable_start_time..=acceptable_end_time).contains(&current_time) {
return Err(ZkGroupVerificationFailure);
}
Ok(())
}
pub fn verify_auth_credential_presentation(
&self,
group_public_params: api::groups::GroupPublicParams,
presentation: &api::auth::AnyAuthCredentialPresentation,
current_time: Timestamp,
) -> Result<(), ZkGroupVerificationFailure> {
Self::check_auth_credential_redemption_time(
presentation.get_redemption_time(),
current_time,
)?;
match presentation {
api::auth::AnyAuthCredentialPresentation::V4(presentation) => {
presentation.verify(self, &group_public_params, presentation.redemption_time())
}
}
}
pub fn verify_profile_key_credential_presentation(
&self,
group_public_params: api::groups::GroupPublicParams,
presentation: &api::profiles::AnyProfileKeyCredentialPresentation,
current_time: Timestamp,
) -> Result<(), ZkGroupVerificationFailure> {
match presentation {
api::profiles::AnyProfileKeyCredentialPresentation::V1(_) => {
Err(ZkGroupVerificationFailure)
}
api::profiles::AnyProfileKeyCredentialPresentation::V2(_) => {
Err(ZkGroupVerificationFailure)
}
api::profiles::AnyProfileKeyCredentialPresentation::V3(presentation) => self
.verify_expiring_profile_key_credential_presentation(
group_public_params,
presentation,
current_time,
),
api::profiles::AnyProfileKeyCredentialPresentation::V4(presentation) => self
.verify_expiring_profile_key_credential_presentation(
group_public_params,
presentation,
current_time,
),
}
}
pub fn verify_expiring_profile_key_credential_presentation<const V: u8>(
&self,
group_public_params: api::groups::GroupPublicParams,
presentation: &api::profiles::ExpiringProfileKeyCredentialPresentation<V>,
current_time: Timestamp,
) -> Result<(), ZkGroupVerificationFailure> {
let credentials_key_pair = self.expiring_profile_key_credentials_key_pair;
let uid_enc_public_key = group_public_params.uid_enc_public_key;
let profile_key_enc_public_key = group_public_params.profile_key_enc_public_key;
presentation.proof.verify(
credentials_key_pair,
presentation.uid_enc_ciphertext,
uid_enc_public_key,
presentation.profile_key_enc_ciphertext,
profile_key_enc_public_key,
presentation.credential_expiration_time,
V >= PRESENTATION_VERSION_4,
)?;
if presentation.credential_expiration_time <= current_time {
return Err(ZkGroupVerificationFailure);
}
Ok(())
}
pub fn issue_expiring_profile_key_credential(
&self,
randomness: RandomnessBytes,
request: &api::profiles::ProfileKeyCredentialRequest,
aci: libsignal_core::Aci,
commitment: api::profiles::ProfileKeyCommitment,
credential_expiration_time: Timestamp,
) -> Result<api::profiles::ExpiringProfileKeyCredentialResponse, ZkGroupVerificationFailure>
{
let mut sho = Sho::new(
b"Signal_ZKGroup_20220508_Random_ServerSecretParams_IssueExpiringProfileKeyCredential",
&randomness,
);
request.proof.verify(
request.public_key,
request.ciphertext,
commitment.commitment,
)?;
let uid = crypto::uid_struct::UidStruct::from_service_id(aci.into());
let blinded_credential_with_secret_nonce = self
.expiring_profile_key_credentials_key_pair
.create_blinded_expiring_profile_key_credential(
uid,
request.public_key,
request.ciphertext,
credential_expiration_time,
&mut sho,
);
let proof = crypto::proofs::ExpiringProfileKeyCredentialIssuanceProof::new(
self.expiring_profile_key_credentials_key_pair,
request.public_key,
request.ciphertext,
blinded_credential_with_secret_nonce,
uid,
credential_expiration_time,
&mut sho,
);
Ok(api::profiles::ExpiringProfileKeyCredentialResponse {
reserved: Default::default(),
blinded_credential: blinded_credential_with_secret_nonce
.get_blinded_expiring_profile_key_credential(),
credential_expiration_time,
proof,
})
}
pub fn issue_receipt_credential(
&self,
randomness: RandomnessBytes,
request: &api::receipts::ReceiptCredentialRequest,
receipt_expiration_time: Timestamp,
receipt_level: ReceiptLevel,
) -> api::receipts::ReceiptCredentialResponse {
let mut sho = Sho::new(
b"Signal_ZKGroup_20210919_Random_ServerSecretParams_IssueReceiptCredential",
&randomness,
);
let blinded_credential_with_secret_nonce = self
.receipt_credentials_key_pair
.create_blinded_receipt_credential(
request.public_key,
request.ciphertext,
receipt_expiration_time,
receipt_level,
&mut sho,
);
let proof = crypto::proofs::ReceiptCredentialIssuanceProof::new(
self.receipt_credentials_key_pair,
request.public_key,
request.ciphertext,
blinded_credential_with_secret_nonce,
receipt_expiration_time,
receipt_level,
&mut sho,
);
api::receipts::ReceiptCredentialResponse {
reserved: Default::default(),
receipt_expiration_time,
receipt_level,
blinded_credential: blinded_credential_with_secret_nonce
.get_blinded_receipt_credential(),
proof,
}
}
pub fn verify_receipt_credential_presentation(
&self,
presentation: &api::receipts::ReceiptCredentialPresentation,
) -> Result<(), ZkGroupVerificationFailure> {
presentation.proof.verify(
self.receipt_credentials_key_pair,
presentation.get_receipt_struct(),
)
}
}
impl ServerPublicParams {
pub fn get_endorsement_public_key(&self) -> EndorsementPublicKey {
EndorsementPublicKey {
reserved: Default::default(),
public_key: self.endorsement_public_key.clone(),
}
}
pub fn verify_signature(
&self,
message: &[u8],
signature: NotarySignatureBytes,
) -> Result<(), ZkGroupVerificationFailure> {
self.sig_public_key.verify(message, signature)
}
pub fn create_profile_key_credential_request_context(
&self,
randomness: RandomnessBytes,
aci: libsignal_core::Aci,
profile_key: api::profiles::ProfileKey,
) -> api::profiles::ProfileKeyCredentialRequestContext {
let mut sho = Sho::new(
b"Signal_ZKGroup_20200424_Random_ServerPublicParams_CreateProfileKeyCredentialRequestContext",
&randomness,
);
let uid_bytes = uuid::Uuid::from(aci).into_bytes();
let profile_key_struct =
crypto::profile_key_struct::ProfileKeyStruct::new(profile_key.bytes, uid_bytes);
let commitment_with_secret_nonce =
crypto::profile_key_commitment::CommitmentWithSecretNonce::new(
profile_key_struct,
uid_bytes,
);
let key_pair = crypto::profile_key_credential_request::KeyPair::generate(&mut sho);
let ciphertext_with_secret_nonce = key_pair.encrypt(profile_key_struct, &mut sho);
let proof = crypto::proofs::ProfileKeyCredentialRequestProof::new(
key_pair,
ciphertext_with_secret_nonce,
commitment_with_secret_nonce,
&mut sho,
);
api::profiles::ProfileKeyCredentialRequestContext {
reserved: Default::default(),
aci_bytes: uid_bytes,
profile_key_bytes: profile_key_struct.bytes,
key_pair,
ciphertext_with_secret_nonce,
proof,
}
}
pub fn receive_expiring_profile_key_credential(
&self,
context: &api::profiles::ProfileKeyCredentialRequestContext,
response: &api::profiles::ExpiringProfileKeyCredentialResponse,
current_time: Timestamp,
) -> Result<api::profiles::ExpiringProfileKeyCredential, ZkGroupVerificationFailure> {
response.proof.verify(
self.expiring_profile_key_credentials_public_key,
context.key_pair.get_public_key(),
context.aci_bytes,
context.ciphertext_with_secret_nonce.get_ciphertext(),
response.blinded_credential,
response.credential_expiration_time,
)?;
if !response.credential_expiration_time.is_day_aligned() {
return Err(ZkGroupVerificationFailure);
}
let days_remaining = response
.credential_expiration_time
.saturating_seconds_since(current_time)
/ SECONDS_PER_DAY;
if days_remaining == 0 || days_remaining > 7 {
return Err(ZkGroupVerificationFailure);
}
let credential = context
.key_pair
.decrypt_blinded_expiring_profile_key_credential(response.blinded_credential);
Ok(api::profiles::ExpiringProfileKeyCredential {
reserved: Default::default(),
credential,
aci_bytes: context.aci_bytes,
profile_key_bytes: context.profile_key_bytes,
credential_expiration_time: response.credential_expiration_time,
})
}
pub fn create_expiring_profile_key_credential_presentation<const V: u8>(
&self,
randomness: RandomnessBytes,
group_secret_params: api::groups::GroupSecretParams,
expiring_profile_key_credential: api::profiles::ExpiringProfileKeyCredential,
) -> api::profiles::ExpiringProfileKeyCredentialPresentation<V> {
let mut sho = Sho::new(
b"Signal_ZKGroup_20220508_Random_ServerPublicParams_CreateExpiringProfileKeyCredentialPresentation",
&randomness,
);
let uid_enc_key_pair = group_secret_params.uid_enc_key_pair;
let profile_key_enc_key_pair = group_secret_params.profile_key_enc_key_pair;
let credentials_public_key = self.expiring_profile_key_credentials_public_key;
let uid = expiring_profile_key_credential.aci();
let uuid_ciphertext = group_secret_params.encrypt_service_id(uid.into());
let profile_key_ciphertext = group_secret_params
.encrypt_profile_key_bytes(expiring_profile_key_credential.profile_key_bytes, uid);
let proof = crypto::proofs::ExpiringProfileKeyCredentialPresentationProof::new(
uid_enc_key_pair,
profile_key_enc_key_pair,
credentials_public_key,
expiring_profile_key_credential.credential,
uuid_ciphertext.ciphertext,
profile_key_ciphertext.ciphertext,
expiring_profile_key_credential.aci_bytes,
expiring_profile_key_credential.profile_key_bytes,
V >= PRESENTATION_VERSION_4,
&mut sho,
);
api::profiles::ExpiringProfileKeyCredentialPresentation {
version: VersionByte,
proof,
uid_enc_ciphertext: uuid_ciphertext.ciphertext,
profile_key_enc_ciphertext: profile_key_ciphertext.ciphertext,
credential_expiration_time: expiring_profile_key_credential.credential_expiration_time,
}
}
pub fn create_receipt_credential_request_context(
&self,
randomness: RandomnessBytes,
receipt_serial_bytes: ReceiptSerialBytes,
) -> api::receipts::ReceiptCredentialRequestContext {
let mut sho = Sho::new(
b"Signal_ZKGroup_20210919_Random_ServerPublicParams_CreateReceiptCredentialRequestContext",
&randomness,
);
let key_pair = crypto::receipt_credential_request::KeyPair::generate(&mut sho);
let ciphertext_with_secret_nonce = key_pair.encrypt(receipt_serial_bytes, &mut sho);
api::receipts::ReceiptCredentialRequestContext {
reserved: Default::default(),
receipt_serial_bytes,
key_pair,
ciphertext_with_secret_nonce,
}
}
pub fn receive_receipt_credential(
&self,
context: &api::receipts::ReceiptCredentialRequestContext,
response: &api::receipts::ReceiptCredentialResponse,
) -> Result<api::receipts::ReceiptCredential, ZkGroupVerificationFailure> {
if !response.receipt_expiration_time.is_day_aligned() {
return Err(ZkGroupVerificationFailure);
}
let receipt_struct = crypto::receipt_struct::ReceiptStruct::new(
context.receipt_serial_bytes,
response.receipt_expiration_time,
response.receipt_level,
);
response.proof.verify(
self.receipt_credentials_public_key,
context.key_pair.get_public_key(),
context.ciphertext_with_secret_nonce.get_ciphertext(),
response.blinded_credential,
receipt_struct,
)?;
let credential = context
.key_pair
.decrypt_blinded_receipt_credential(response.blinded_credential);
Ok(api::receipts::ReceiptCredential {
reserved: Default::default(),
credential,
receipt_expiration_time: response.receipt_expiration_time,
receipt_level: response.receipt_level,
receipt_serial_bytes: context.receipt_serial_bytes,
})
}
pub fn create_receipt_credential_presentation(
&self,
randomness: RandomnessBytes,
receipt_credential: &api::receipts::ReceiptCredential,
) -> api::receipts::ReceiptCredentialPresentation {
let mut sho = Sho::new(
b"Signal_ZKGroup_20210919_Random_ServerPublicParams_CreateReceiptCredentialPresentation",
&randomness,
);
let proof = crypto::proofs::ReceiptCredentialPresentationProof::new(
self.receipt_credentials_public_key,
receipt_credential.credential,
&mut sho,
);
api::receipts::ReceiptCredentialPresentation {
reserved: Default::default(),
proof,
receipt_expiration_time: receipt_credential.receipt_expiration_time,
receipt_level: receipt_credential.receipt_level,
receipt_serial_bytes: receipt_credential.receipt_serial_bytes,
}
}
}
#[derive(Clone, Serialize, Deserialize, PartialDefault)]
pub struct EndorsementServerRootKeyPair {
reserved: ReservedByte,
pub(crate) key_pair: zkcredential::endorsements::ServerRootKeyPair,
}
impl AsRef<zkcredential::endorsements::ServerRootKeyPair> for EndorsementServerRootKeyPair {
fn as_ref(&self) -> &zkcredential::endorsements::ServerRootKeyPair {
&self.key_pair
}
}
impl EndorsementServerRootKeyPair {
pub fn public_key(&self) -> EndorsementPublicKey {
EndorsementPublicKey {
reserved: self.reserved,
public_key: self.key_pair.public_key().clone(),
}
}
}
#[derive(Clone, Serialize, Deserialize, PartialDefault)]
pub struct EndorsementPublicKey {
reserved: ReservedByte,
public_key: zkcredential::endorsements::ServerRootPublicKey,
}
impl AsRef<zkcredential::endorsements::ServerRootPublicKey> for EndorsementPublicKey {
fn as_ref(&self) -> &zkcredential::endorsements::ServerRootPublicKey {
&self.public_key
}
}
+16
View File
@@ -0,0 +1,16 @@
//
// Copyright 2020 Signal Messenger, LLC.
// SPDX-License-Identifier: AGPL-3.0-only
//
use partial_default::PartialDefault;
use serde::{Deserialize, Serialize};
use crate::common::serialization::ReservedByte;
use crate::crypto;
#[derive(Copy, Clone, Serialize, Deserialize, PartialEq, Eq, PartialDefault)]
pub struct UuidCiphertext {
pub(crate) reserved: ReservedByte,
pub(crate) ciphertext: crypto::uid_encryption::Ciphertext,
}