zkgroup: Bind C_y5 in ProfileKeyCredential proof

This commit is contained in:
Jordan Rose
2026-04-07 15:08:40 -07:00
committed by GitHub
parent 1a347324e4
commit c235197dad
6 changed files with 94 additions and 34 deletions

View File

@@ -258,7 +258,7 @@ fn ServerPublicParams_CreateExpiringProfileKeyCredentialPresentationDeterministi
*randomness,
group_secret_params.into_inner(),
profile_key_credential.into_inner(),
),
) as &zkgroup::profiles::ExpiringProfileKeyCredentialPresentation,
)
}

View File

@@ -217,11 +217,12 @@ pub fn benchmark_integration_profile(c: &mut Criterion) {
// Create presentation
let randomness = zkgroup::TEST_ARRAY_32_5;
let presentation = server_public_params.create_expiring_profile_key_credential_presentation(
randomness,
group_secret_params,
profile_key_credential,
);
let presentation: zkgroup::profiles::ExpiringProfileKeyCredentialPresentation =
server_public_params.create_expiring_profile_key_credential_presentation(
randomness,
group_secret_params,
profile_key_credential,
);
c.bench_function("create_expiring_profile_key_credential_presentation", |b| {
b.iter(|| {
@@ -229,7 +230,7 @@ pub fn benchmark_integration_profile(c: &mut Criterion) {
randomness,
group_secret_params,
profile_key_credential,
)
) as zkgroup::profiles::ExpiringProfileKeyCredentialPresentation
})
});

View File

@@ -62,15 +62,19 @@ impl ProfileKeyCredentialPresentationV2 {
}
#[derive(Clone, Serialize, Deserialize, PartialDefault)]
pub struct ExpiringProfileKeyCredentialPresentation {
pub(crate) version: VersionByte<PRESENTATION_VERSION_3>,
pub struct ExpiringProfileKeyCredentialPresentation<const V: u8 = PRESENTATION_VERSION_3> {
pub(crate) version: VersionByte<V>,
pub(crate) proof: crypto::proofs::ExpiringProfileKeyCredentialPresentationProof,
pub(crate) uid_enc_ciphertext: crypto::uid_encryption::Ciphertext,
pub(crate) profile_key_enc_ciphertext: crypto::profile_key_encryption::Ciphertext,
pub(crate) credential_expiration_time: Timestamp,
}
impl ExpiringProfileKeyCredentialPresentation {
// The only thing that changes in V2 is the proof contents, so we can reuse the same structure.
pub type ExpiringProfileKeyCredentialPresentationV2 =
ExpiringProfileKeyCredentialPresentation<PRESENTATION_VERSION_4>;
impl<const V: u8> ExpiringProfileKeyCredentialPresentation<V> {
pub fn get_uuid_ciphertext(&self) -> api::groups::UuidCiphertext {
api::groups::UuidCiphertext {
reserved: Default::default(),
@@ -95,6 +99,7 @@ pub enum AnyProfileKeyCredentialPresentation {
V1(ProfileKeyCredentialPresentationV1),
V2(ProfileKeyCredentialPresentationV2),
V3(ExpiringProfileKeyCredentialPresentation),
V4(ExpiringProfileKeyCredentialPresentationV2),
}
impl AnyProfileKeyCredentialPresentation {
@@ -112,6 +117,10 @@ impl AnyProfileKeyCredentialPresentation {
crate::deserialize::<ExpiringProfileKeyCredentialPresentation>(presentation_bytes)
.map(AnyProfileKeyCredentialPresentation::V3)
}
PRESENTATION_VERSION_4 => {
crate::deserialize::<ExpiringProfileKeyCredentialPresentationV2>(presentation_bytes)
.map(AnyProfileKeyCredentialPresentation::V4)
}
_ => Err(ZkGroupDeserializationFailure::new::<Self>()),
}
}
@@ -127,6 +136,9 @@ impl AnyProfileKeyCredentialPresentation {
AnyProfileKeyCredentialPresentation::V3(presentation) => {
presentation.get_uuid_ciphertext()
}
AnyProfileKeyCredentialPresentation::V4(presentation) => {
presentation.get_uuid_ciphertext()
}
}
}
@@ -141,6 +153,9 @@ impl AnyProfileKeyCredentialPresentation {
AnyProfileKeyCredentialPresentation::V3(presentation) => {
presentation.get_profile_key_ciphertext()
}
AnyProfileKeyCredentialPresentation::V4(presentation) => {
presentation.get_profile_key_ciphertext()
}
}
}
@@ -175,6 +190,9 @@ impl Serialize for AnyProfileKeyCredentialPresentation {
AnyProfileKeyCredentialPresentation::V3(presentation) => {
presentation.serialize(serializer)
}
AnyProfileKeyCredentialPresentation::V4(presentation) => {
presentation.serialize(serializer)
}
}
}
}

View File

@@ -209,13 +209,19 @@ impl ServerSecretParams {
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(
pub fn verify_expiring_profile_key_credential_presentation<const V: u8>(
&self,
group_public_params: api::groups::GroupPublicParams,
presentation: &api::profiles::ExpiringProfileKeyCredentialPresentation,
presentation: &api::profiles::ExpiringProfileKeyCredentialPresentation<V>,
current_time: Timestamp,
) -> Result<(), ZkGroupVerificationFailure> {
let credentials_key_pair = self.expiring_profile_key_credentials_key_pair;
@@ -229,6 +235,7 @@ impl ServerSecretParams {
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 {
@@ -436,12 +443,12 @@ impl ServerPublicParams {
})
}
pub fn create_expiring_profile_key_credential_presentation(
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 {
) -> api::profiles::ExpiringProfileKeyCredentialPresentation<V> {
let mut sho = Sho::new(
b"Signal_ZKGroup_20220508_Random_ServerPublicParams_CreateExpiringProfileKeyCredentialPresentation",
&randomness,
@@ -465,6 +472,7 @@ impl ServerPublicParams {
profile_key_ciphertext.ciphertext,
expiring_profile_key_credential.aci_bytes,
expiring_profile_key_credential.profile_key_bytes,
V >= PRESENTATION_VERSION_4,
&mut sho,
);

View File

@@ -448,7 +448,7 @@ impl ProfileKeyCredentialPresentationProofV1 {
}
impl ExpiringProfileKeyCredentialPresentationProof {
pub fn get_poksho_statement() -> poksho::Statement {
pub fn get_poksho_statement(full_checking: bool) -> poksho::Statement {
let mut st = poksho::Statement::new();
st.add("Z", &[("z", "I")]);
st.add("C_x1", &[("t", "C_x0"), ("z0", "G_x0"), ("z", "G_x1")]);
@@ -467,6 +467,9 @@ impl ExpiringProfileKeyCredentialPresentationProof {
st.add("E_B1", &[("b1", "C_y3"), ("z2", "G_y3")]);
st.add("0", &[("z1", "I"), ("a1", "Z")]);
st.add("0", &[("z2", "I"), ("b1", "Z")]);
if full_checking {
st.add("C_y5", &[("z", "G_y5")]);
}
st
}
@@ -480,6 +483,7 @@ impl ExpiringProfileKeyCredentialPresentationProof {
profile_key_ciphertext: profile_key_encryption::Ciphertext,
aci_bytes: UidBytes,
profile_key_bytes: ProfileKeyBytes,
full_checking: bool,
sho: &mut Sho,
) -> Self {
let credentials_system = credentials::SystemParams::get_hardcoded();
@@ -557,7 +561,12 @@ impl ExpiringProfileKeyCredentialPresentationProof {
point_args.add("G_y3", credentials_system.G_y[3]);
point_args.add("0", RistrettoPoint::identity());
let poksho_proof = Self::get_poksho_statement()
if full_checking {
point_args.add("C_y5", C_y5);
point_args.add("G_y5", credentials_system.G_y[5]);
}
let poksho_proof = Self::get_poksho_statement(full_checking)
.prove(
&scalar_args,
&point_args,
@@ -587,6 +596,7 @@ impl ExpiringProfileKeyCredentialPresentationProof {
profile_key_ciphertext: profile_key_encryption::Ciphertext,
profile_key_enc_public_key: profile_key_encryption::PublicKey,
credential_expiration_time: Timestamp,
full_checking: bool,
) -> Result<(), ZkGroupVerificationFailure> {
let uid_enc_system = uid_encryption::SystemParams::get_hardcoded();
let profile_key_enc_system = profile_key_encryption::SystemParams::get_hardcoded();
@@ -604,8 +614,8 @@ impl ExpiringProfileKeyCredentialPresentationProof {
poksho_proof,
} = self;
let (C_x0, C_x1, C_y1, C_y2, C_y3, C_y4, C_V) =
(*C_x0, *C_x1, *C_y1, *C_y2, *C_y3, *C_y4, *C_V);
let (C_x0, C_x1, C_y1, C_y2, C_y3, C_y4, C_y5, C_V) =
(*C_x0, *C_x1, *C_y1, *C_y2, *C_y3, *C_y4, *C_y5, *C_V);
let credentials::KeyPair {
W,
@@ -661,7 +671,13 @@ impl ExpiringProfileKeyCredentialPresentationProof {
point_args.add("G_y3", credentials_system.G_y[3]);
point_args.add("0", RistrettoPoint::identity());
match Self::get_poksho_statement().verify_proof(poksho_proof, &point_args, &[]) {
if full_checking {
point_args.add("C_y5", C_y5);
point_args.add("G_y5", credentials_system.G_y[5]);
}
match Self::get_poksho_statement(full_checking).verify_proof(poksho_proof, &point_args, &[])
{
Err(_) => Err(ZkGroupVerificationFailure),
Ok(_) => Ok(()),
}

View File

@@ -52,6 +52,10 @@ const PROFILE_KEY_CREDENTIAL_PRESENTATION_V3_RESULT: &[u8] = &hex!(
"02fc58a4f2c9bd736238abfc28890c8b2363d084bee430692f05ee559bd37dea3378949e72b271fe0d815b6d908035106cd670b45892df40780c62c37fae106c41be38371fe042a4d4f697db112972d79204b3d48d1253d3231c22926e107f661d40897cb7fdb4777c1680a57008655db71efaac1f69cd9ddf8cda33b226662d7ba443416281508fcdbb026d63f83168470a83e12803a6d2ee2c907343f2f6b063fe6bf0f17a032fabe61e77e904dfe7d3042125728c1984c86a094a0e3991ba554c1ebf604c14a8b13c384c5c01909656c114b24f9d3615d3b14bde7ce9cf126aca3e073e804b2016f7c5affa158a3a68ed9024c6880ecb441a346e7e91aedd6240010000000000002e70f27fb3f4c58cb40dfe58ce1d122312969426abb0bbb820bfbc5ff61d400a419d5ddb7c30c546427273d4fca3096ee4dd2fd03ccbbd26304ffcfe54fef50db8538177ebc61117a222253b4d4189f795abbde3b3d8a0a72d97b7750e0394010a01b474c3e942ef1ee807e17421689c6ca793c4f30b09c989b8a9679aee130eb034f64a34dbcaf12616970d2c8d58ca715bf5c4d42475fa6a1b82ba31574e072506652253e86cd783e30e1c06d2e861ba864a5373759472b31c5b26a8e46d062b8b5da2ec0a3ba499648e80f307728b7815aa60d167a0a9d01c2d2cbfb0a60ddc9dfc5343564b5f021fd1adba6d2a389e7c331bfffeed2a5d1887634323840574e49255a62d9e00ffc21f56afbb12fb9660e185f979223ec714c01e403a3a0a3276d0ef78182f12c092f5237befe3f0afea7693370788f854ec697e44c9bd02765de9df4cfa5487f360e29e99343e91811baec331c4680985e608ca5d408e21725c6aa1b61d5a8b48d75f4aaa9a3cbe88d3e0f1a54319081f77c72c8f52547448c03ab4afbf6b8fb0e126c037a0ad4094600dd0e0634d76f88c21087f3cfb485a89bc1e3abc4c95041d1d170eccf02933ec5393d4be1dc573f83c33d3b9a7468069160000000000"
);
const PROFILE_KEY_CREDENTIAL_PRESENTATION_V4_RESULT: &[u8] = &hex!(
"03fc58a4f2c9bd736238abfc28890c8b2363d084bee430692f05ee559bd37dea3378949e72b271fe0d815b6d908035106cd670b45892df40780c62c37fae106c41be38371fe042a4d4f697db112972d79204b3d48d1253d3231c22926e107f661d40897cb7fdb4777c1680a57008655db71efaac1f69cd9ddf8cda33b226662d7ba443416281508fcdbb026d63f83168470a83e12803a6d2ee2c907343f2f6b063fe6bf0f17a032fabe61e77e904dfe7d3042125728c1984c86a094a0e3991ba554c1ebf604c14a8b13c384c5c01909656c114b24f9d3615d3b14bde7ce9cf126aca3e073e804b2016f7c5affa158a3a68ed9024c6880ecb441a346e7e91aedd6240010000000000006b4403e6adc3322acd34eea13fcc1ae971aab14386fa9b4d85ba0490dfca2f0d21de78e92719ad40a6a49d7549551a4bc6f7e4e4f0da81e9d6cc054da9529e053d1360b9d83e3ab9d3c8a9b7d3b07e2bab0979f3912c4bb41f621bf7685ee607fc147670196e009a9cd92b0fb525ca9b8e8fdf4c332732c02ccbcf57b5c19f001df37a5104d367f0f3f5cc0d9a2bd299e1f37872d7580b05596eadd5dec3e80366ab47357205c407bbcac49540a1fd69f36d308d0fdb72d0273f0c7a0d92220fe25daba5885a162bf238495463971c61b380084b7f79ad817d6f343e254722019da9ad32e72f2d864074023096cb8dd615b6fbbb47c6bef0926290a68403340e0a4dfb961e5b9002fc104e480823f12178cb91fafd51eb5dc9eb9fec4e6f7c0a130f31eb84cc70be9e5fd0bfb7d279590bdb49e7a4fe3b156a922fc73f78a50e765de9df4cfa5487f360e29e99343e91811baec331c4680985e608ca5d408e21725c6aa1b61d5a8b48d75f4aaa9a3cbe88d3e0f1a54319081f77c72c8f52547448c03ab4afbf6b8fb0e126c037a0ad4094600dd0e0634d76f88c21087f3cfb485a89bc1e3abc4c95041d1d170eccf02933ec5393d4be1dc573f83c33d3b9a7468069160000000000"
);
#[test]
fn test_auth_credential_presentation_v1_is_rejected() {
assert!(
@@ -160,8 +164,11 @@ fn test_integration_auth_zkc() {
auth_credential_bytes.copy_from_slice(&bincode::serialize(&auth_credential).unwrap());
}
#[test]
fn test_integration_expiring_profile() {
fn test_integration_expiring_profile<const V: u8>()
where
zkgroup::profiles::AnyProfileKeyCredentialPresentation:
From<zkgroup::profiles::ExpiringProfileKeyCredentialPresentation<V>>,
{
// SERVER
let server_secret_params = zkgroup::ServerSecretParams::generate(zkgroup::TEST_ARRAY_32);
let server_public_params = server_secret_params.get_public_params();
@@ -226,11 +233,12 @@ fn test_integration_expiring_profile() {
// Create presentation
let randomness = zkgroup::TEST_ARRAY_32_5;
let presentation = server_public_params.create_expiring_profile_key_credential_presentation(
randomness,
group_secret_params,
profile_key_credential,
);
let presentation: zkgroup::profiles::ExpiringProfileKeyCredentialPresentation<V> =
server_public_params.create_expiring_profile_key_credential_presentation(
randomness,
group_secret_params,
profile_key_credential,
);
assert_eq!(expiration, presentation.get_expiration_time());
let presentation_bytes = &bincode::serialize(&presentation).unwrap();
@@ -238,15 +246,14 @@ fn test_integration_expiring_profile() {
presentation.into();
let presentation_any_bytes = &bincode::serialize(&presentation_any).unwrap();
assert_hex_eq!(
PROFILE_KEY_CREDENTIAL_PRESENTATION_V3_RESULT[..],
presentation_bytes[..]
);
let expected_hex = match V {
zkgroup::PRESENTATION_VERSION_3 => PROFILE_KEY_CREDENTIAL_PRESENTATION_V3_RESULT,
zkgroup::PRESENTATION_VERSION_4 => PROFILE_KEY_CREDENTIAL_PRESENTATION_V4_RESULT,
_ => panic!("unexpected ExpiringProfileKeyCredentialPresentation version {V}"),
};
assert_hex_eq!(
PROFILE_KEY_CREDENTIAL_PRESENTATION_V3_RESULT[..],
presentation_any_bytes[..]
);
assert_hex_eq!(expected_hex, &presentation_bytes[..]);
assert_hex_eq!(expected_hex, &presentation_any_bytes[..]);
server_secret_params
.verify_profile_key_credential_presentation(
@@ -310,6 +317,16 @@ fn test_integration_expiring_profile() {
profile_key_credential_response_bytes.copy_from_slice(&bincode::serialize(&response).unwrap());
}
#[test]
fn test_integration_expiring_profile_v1() {
test_integration_expiring_profile::<{ zkgroup::PRESENTATION_VERSION_3 }>();
}
#[test]
fn test_integration_expiring_profile_v2() {
test_integration_expiring_profile::<{ zkgroup::PRESENTATION_VERSION_4 }>();
}
#[test]
fn test_server_sigs() {
let server_secret_params =