1#![allow(non_snake_case)]
7
8use curve25519_dalek_signal::constants::RISTRETTO_BASEPOINT_POINT;
9use curve25519_dalek_signal::ristretto::RistrettoPoint;
10use curve25519_dalek_signal::scalar::Scalar;
11use hex_literal::hex;
12use lazy_static::lazy_static;
13use partial_default::PartialDefault;
14use serde::{Deserialize, Serialize};
15
16use crate::common::array_utils::{ArrayLike, OneBased};
17use crate::common::sho::*;
18use crate::common::simple_types::*;
19use crate::crypto::receipt_struct::ReceiptStruct;
20use crate::crypto::timestamp_struct::TimestampStruct;
21use crate::crypto::{
22 profile_key_credential_request, receipt_credential_request, receipt_struct, uid_struct,
23};
24use crate::{
25 NUM_AUTH_CRED_ATTRIBUTES, NUM_PROFILE_KEY_CRED_ATTRIBUTES, NUM_RECEIPT_CRED_ATTRIBUTES,
26};
27
28lazy_static! {
29 static ref SYSTEM_PARAMS: SystemParams =
30 crate::deserialize::<SystemParams>(SystemParams::SYSTEM_HARDCODED).unwrap();
31}
32
33const NUM_SUPPORTED_ATTRS: usize = 6;
34#[derive(Copy, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
35pub struct SystemParams {
36 pub(crate) G_w: RistrettoPoint,
37 pub(crate) G_wprime: RistrettoPoint,
38 pub(crate) G_x0: RistrettoPoint,
39 pub(crate) G_x1: RistrettoPoint,
40 pub(crate) G_y: OneBased<[RistrettoPoint; NUM_SUPPORTED_ATTRS]>,
41 pub(crate) G_m1: RistrettoPoint,
42 pub(crate) G_m2: RistrettoPoint,
43 pub(crate) G_m3: RistrettoPoint,
44 pub(crate) G_m4: RistrettoPoint,
45 pub(crate) G_m5: RistrettoPoint,
46 pub(crate) G_V: RistrettoPoint,
47 pub(crate) G_z: RistrettoPoint,
48}
49
50pub trait AttrScalars {
57 type Storage: ArrayLike<Scalar> + Copy + Eq + Serialize + for<'a> Deserialize<'a>;
59
60 const NUM_ATTRS: usize = Self::Storage::LEN;
64}
65
66impl AttrScalars for AuthCredential {
67 type Storage = [Scalar; 4];
69 const NUM_ATTRS: usize = NUM_AUTH_CRED_ATTRIBUTES;
70}
71impl AttrScalars for AuthCredentialWithPni {
72 type Storage = [Scalar; 5];
73}
74impl AttrScalars for ProfileKeyCredential {
75 type Storage = [Scalar; 4];
77 const NUM_ATTRS: usize = NUM_PROFILE_KEY_CRED_ATTRIBUTES;
78}
79impl AttrScalars for ExpiringProfileKeyCredential {
80 type Storage = [Scalar; 5];
81}
82impl AttrScalars for ReceiptCredential {
83 type Storage = [Scalar; 4];
85 const NUM_ATTRS: usize = NUM_RECEIPT_CRED_ATTRIBUTES;
86}
87impl AttrScalars for PniCredential {
88 type Storage = [Scalar; 6];
89}
90
91#[derive(Serialize, Deserialize, PartialDefault)]
92#[partial_default(bound = "S::Storage: Default")]
93pub struct KeyPair<S: AttrScalars> {
94 pub(crate) w: Scalar,
96 pub(crate) wprime: Scalar,
97 pub(crate) W: RistrettoPoint,
98 pub(crate) x0: Scalar,
99 pub(crate) x1: Scalar,
100 pub(crate) y: OneBased<S::Storage>,
101
102 pub(crate) C_W: RistrettoPoint,
104 pub(crate) I: RistrettoPoint,
105}
106
107impl<S: AttrScalars> Clone for KeyPair<S> {
108 fn clone(&self) -> Self {
109 *self
111 }
112}
113
114impl<S: AttrScalars> Copy for KeyPair<S> {}
115
116impl<S: AttrScalars> PartialEq for KeyPair<S> {
117 fn eq(&self, other: &Self) -> bool {
118 self.w == other.w
119 && self.wprime == other.wprime
120 && self.W == other.W
121 && self.x0 == other.x0
122 && self.x1 == other.x1
123 && self.y == other.y
124 && self.C_W == other.C_W
125 && self.I == other.I
126 }
127}
128impl<S: AttrScalars> Eq for KeyPair<S> {}
129
130#[derive(Copy, Clone, PartialEq, Eq, Serialize, Deserialize, PartialDefault)]
131pub struct PublicKey {
132 pub(crate) C_W: RistrettoPoint,
133 pub(crate) I: RistrettoPoint,
134}
135
136#[derive(Copy, Clone, PartialEq, Eq, Serialize, Deserialize, PartialDefault)]
137pub struct AuthCredential {
138 pub(crate) t: Scalar,
139 pub(crate) U: RistrettoPoint,
140 pub(crate) V: RistrettoPoint,
141}
142
143#[derive(Copy, Clone, PartialEq, Eq, Serialize, Deserialize, PartialDefault)]
144pub struct AuthCredentialWithPni {
145 pub(crate) t: Scalar,
146 pub(crate) U: RistrettoPoint,
147 pub(crate) V: RistrettoPoint,
148}
149
150#[derive(Copy, Clone, PartialEq, Eq, Serialize, Deserialize)]
152pub struct ProfileKeyCredential {
153 pub(crate) t: Scalar,
154 pub(crate) U: RistrettoPoint,
155 pub(crate) V: RistrettoPoint,
156}
157
158#[derive(Copy, Clone, PartialEq, Eq, Serialize, Deserialize, PartialDefault)]
159pub struct ExpiringProfileKeyCredential {
160 pub(crate) t: Scalar,
161 pub(crate) U: RistrettoPoint,
162 pub(crate) V: RistrettoPoint,
163}
164
165#[derive(Copy, Clone, PartialEq, Eq, Serialize, Deserialize)]
166pub struct BlindedExpiringProfileKeyCredentialWithSecretNonce {
167 pub(crate) rprime: Scalar,
168 pub(crate) t: Scalar,
169 pub(crate) U: RistrettoPoint,
170 pub(crate) S1: RistrettoPoint,
171 pub(crate) S2: RistrettoPoint,
172}
173
174#[derive(Copy, Clone, PartialEq, Eq, Serialize, Deserialize, PartialDefault)]
175pub struct BlindedExpiringProfileKeyCredential {
176 pub(crate) t: Scalar,
177 pub(crate) U: RistrettoPoint,
178 pub(crate) S1: RistrettoPoint,
179 pub(crate) S2: RistrettoPoint,
180}
181
182#[derive(Copy, Clone, PartialEq, Eq, Serialize, Deserialize)]
184pub struct PniCredential {
185 pub(crate) t: Scalar,
186 pub(crate) U: RistrettoPoint,
187 pub(crate) V: RistrettoPoint,
188}
189
190#[derive(Copy, Clone, PartialEq, Eq, Serialize, Deserialize, PartialDefault)]
191pub struct ReceiptCredential {
192 pub(crate) t: Scalar,
193 pub(crate) U: RistrettoPoint,
194 pub(crate) V: RistrettoPoint,
195}
196
197#[derive(Copy, Clone, PartialEq, Eq, Serialize, Deserialize)]
198pub struct BlindedReceiptCredentialWithSecretNonce {
199 pub(crate) rprime: Scalar,
200 pub(crate) t: Scalar,
201 pub(crate) U: RistrettoPoint,
202 pub(crate) S1: RistrettoPoint,
203 pub(crate) S2: RistrettoPoint,
204}
205
206#[derive(Copy, Clone, PartialEq, Eq, Serialize, Deserialize, PartialDefault)]
207pub struct BlindedReceiptCredential {
208 pub(crate) t: Scalar,
209 pub(crate) U: RistrettoPoint,
210 pub(crate) S1: RistrettoPoint,
211 pub(crate) S2: RistrettoPoint,
212}
213
214pub(crate) fn convert_to_points_aci_pni_timestamp(
215 aci: uid_struct::UidStruct,
216 pni: uid_struct::UidStruct,
217 redemption_time: Timestamp,
218) -> Vec<RistrettoPoint> {
219 let system = SystemParams::get_hardcoded();
220 let redemption_time_scalar = TimestampStruct::calc_m_from(redemption_time);
221 vec![
222 aci.M1,
223 aci.M2,
224 pni.M1,
225 pni.M2,
226 redemption_time_scalar * system.G_m5,
227 ]
228}
229
230pub(crate) fn convert_to_points_receipt_struct(
231 receipt: receipt_struct::ReceiptStruct,
232) -> Vec<RistrettoPoint> {
233 let system = SystemParams::get_hardcoded();
234 let m1 = receipt.calc_m1();
235 let receipt_serial_scalar = encode_receipt_serial_bytes(receipt.receipt_serial_bytes);
236 vec![m1 * system.G_m1, receipt_serial_scalar * system.G_m2]
237}
238
239pub(crate) fn convert_to_point_M2_receipt_serial_bytes(
240 receipt_serial_bytes: ReceiptSerialBytes,
241) -> RistrettoPoint {
242 let system = SystemParams::get_hardcoded();
243 let receipt_serial_scalar = encode_receipt_serial_bytes(receipt_serial_bytes);
244 receipt_serial_scalar * system.G_m2
245}
246
247impl SystemParams {
248 #[cfg(test)]
249 fn generate() -> Self {
250 let mut sho = Sho::new(
251 b"Signal_ZKGroup_20200424_Constant_Credentials_SystemParams_Generate",
252 b"",
253 );
254 let G_w = sho.get_point();
255 let G_wprime = sho.get_point();
256
257 let G_x0 = sho.get_point();
258 let G_x1 = sho.get_point();
259
260 let G_y1 = sho.get_point();
261 let G_y2 = sho.get_point();
262 let G_y3 = sho.get_point();
263 let G_y4 = sho.get_point();
264
265 let G_m1 = sho.get_point();
266 let G_m2 = sho.get_point();
267 let G_m3 = sho.get_point();
268 let G_m4 = sho.get_point();
269
270 let G_V = sho.get_point();
271 let G_z = sho.get_point();
272
273 let G_y5 = sho.get_point();
276 let G_y6 = sho.get_point();
277
278 let G_m5 = sho.get_point();
279
280 SystemParams {
281 G_w,
282 G_wprime,
283 G_x0,
284 G_x1,
285 G_y: OneBased([G_y1, G_y2, G_y3, G_y4, G_y5, G_y6]),
286 G_m1,
287 G_m2,
288 G_m3,
289 G_m4,
290 G_m5,
291 G_V,
292 G_z,
293 }
294 }
295
296 pub fn get_hardcoded() -> SystemParams {
297 *SYSTEM_PARAMS
298 }
299
300 const SYSTEM_HARDCODED: &'static [u8] = &hex!("9ae7c8e5ed779b114ae7708aa2f794670adda324987b659913122c35505b105e6ca31025d2d76be7fd34944f98f7fa0e37babb2c8b98bbbdbd3dd1bf130cca2c8a9a3bdfaaa2b6b322d46b93eca7b0d51c86a3c839e11466358258a6c10c577fc2bffd34cd99164c9a6cd29fab55d91ff9269322ec3458603cc96a0d47f704058288f62ee0acedb8aa23242121d98965a9bb2991250c11758095ece0fd2b33285286fe1fcb056103b6081744b975f550d08521568dd3d8618f25c140375a0f4024c3aa23bdfffb27fbd982208d3ecd1fd3bcb7ac0c3a14b109804fc748d7fa456cffb4934f980b6e09a248a60f44a6150ae6c13d7e3c06261d7e4eed37f39f60b04dd9d607fd357012274d3c63dbb38e7378599c9e97dfbb28842694891d5f0ddc729919b798b4131503408cc57a9c532f4427632c88f54cea53861a5bc44c61cc6037dc31c2e8d4474fb519587a448693182ad9d6d86b535957858f547b9340127da75f8074caee944ac36c0ac662d38c9b3ccce03a093fcd9644047398b86b6e83372ff14fb8bb0dea65531252ac70d58a4a0810d682a0e709c9227b30ef6c8e17c5915d527221bb00da8175cd6489aa8aa492a500f9abee5690b9dfca8855dc0bd02a7f277add240f639ac16801e81574afb4683edff63b9a01e93dbd867a04b616c706c80c756c11a3016bbfb60977f4648b5f2395a4b428b7211940813e3afde2b87aa9c2c37bf716e2578f95656df12c2fb6f5d0631f6f71e2c3193f6d");
301}
302
303impl<S: AttrScalars> KeyPair<S> {
304 pub fn generate(sho: &mut Sho) -> Self {
305 assert!(S::NUM_ATTRS >= 1, "at least one attribute required");
306 assert!(
307 S::NUM_ATTRS <= NUM_SUPPORTED_ATTRS,
308 "more than {} attributes not supported",
309 NUM_SUPPORTED_ATTRS
310 );
311 assert!(
312 S::NUM_ATTRS <= S::Storage::LEN,
313 "more attributes than storage",
314 );
315
316 let system = SystemParams::get_hardcoded();
317 let w = sho.get_scalar();
318 let W = w * system.G_w;
319 let wprime = sho.get_scalar();
320 let x0 = sho.get_scalar();
321 let x1 = sho.get_scalar();
322
323 let y = OneBased::<S::Storage>::create(|| sho.get_scalar());
324
325 let C_W = (w * system.G_w) + (wprime * system.G_wprime);
326 let mut I = system.G_V - (x0 * system.G_x0) - (x1 * system.G_x1);
327
328 for (yn, G_yn) in y.iter().zip(system.G_y.iter()).take(S::NUM_ATTRS) {
329 I -= yn * G_yn;
330 }
331
332 KeyPair {
333 w,
334 wprime,
335 W,
336 x0,
337 x1,
338 y,
339 C_W,
340 I,
341 }
342 }
343
344 pub fn get_public_key(&self) -> PublicKey {
345 PublicKey {
346 C_W: self.C_W,
347 I: self.I,
348 }
349 }
350
351 fn credential_core(
352 &self,
353 M: &[RistrettoPoint],
354 sho: &mut Sho,
355 ) -> (Scalar, RistrettoPoint, RistrettoPoint) {
356 assert!(
357 M.len() <= S::NUM_ATTRS,
358 "more than {} attributes not supported",
359 S::NUM_ATTRS
360 );
361 let t = sho.get_scalar();
362 let U = sho.get_point();
363
364 let mut V = self.W + (self.x0 + self.x1 * t) * U;
365 for (yn, Mn) in self.y.iter().zip(M) {
366 V += yn * Mn;
367 }
368 (t, U, V)
369 }
370}
371
372impl KeyPair<AuthCredentialWithPni> {
373 pub fn create_auth_credential_with_pni(
374 &self,
375 aci: uid_struct::UidStruct,
376 pni: uid_struct::UidStruct,
377 redemption_time: Timestamp,
378 sho: &mut Sho,
379 ) -> AuthCredentialWithPni {
380 let M = convert_to_points_aci_pni_timestamp(aci, pni, redemption_time);
381 let (t, U, V) = self.credential_core(&M, sho);
382 AuthCredentialWithPni { t, U, V }
383 }
384}
385
386impl KeyPair<ExpiringProfileKeyCredential> {
387 pub fn create_blinded_expiring_profile_key_credential(
388 &self,
389 uid: uid_struct::UidStruct,
390 public_key: profile_key_credential_request::PublicKey,
391 ciphertext: profile_key_credential_request::Ciphertext,
392 credential_expiration_time: Timestamp,
393 sho: &mut Sho,
394 ) -> BlindedExpiringProfileKeyCredentialWithSecretNonce {
395 let M = [uid.M1, uid.M2];
396
397 let (t, U, Vprime) = self.credential_core(&M, sho);
398
399 let params = SystemParams::get_hardcoded();
400 let m5 = TimestampStruct::calc_m_from(credential_expiration_time);
401 let M5 = m5 * params.G_m5;
402 let Vprime_with_expiration = Vprime + (self.y[5] * M5);
403
404 let rprime = sho.get_scalar();
405 let R1 = rprime * RISTRETTO_BASEPOINT_POINT;
406 let R2 = rprime * public_key.Y + Vprime_with_expiration;
407 let S1 = R1 + (self.y[3] * ciphertext.D1) + (self.y[4] * ciphertext.E1);
408 let S2 = R2 + (self.y[3] * ciphertext.D2) + (self.y[4] * ciphertext.E2);
409 BlindedExpiringProfileKeyCredentialWithSecretNonce {
410 rprime,
411 t,
412 U,
413 S1,
414 S2,
415 }
416 }
417}
418
419impl KeyPair<ReceiptCredential> {
420 pub fn create_blinded_receipt_credential(
421 &self,
422 public_key: receipt_credential_request::PublicKey,
423 ciphertext: receipt_credential_request::Ciphertext,
424 receipt_expiration_time: Timestamp,
425 receipt_level: ReceiptLevel,
426 sho: &mut Sho,
427 ) -> BlindedReceiptCredentialWithSecretNonce {
428 let params = SystemParams::get_hardcoded();
429 let m1 = ReceiptStruct::calc_m1_from(receipt_expiration_time, receipt_level);
430 let M = [m1 * params.G_m1];
431
432 let (t, U, Vprime) = self.credential_core(&M, sho);
433 let rprime = sho.get_scalar();
434 let R1 = rprime * RISTRETTO_BASEPOINT_POINT;
435 let R2 = rprime * public_key.Y + Vprime;
436 let S1 = self.y[2] * ciphertext.D1 + R1;
437 let S2 = self.y[2] * ciphertext.D2 + R2;
438 BlindedReceiptCredentialWithSecretNonce {
439 rprime,
440 t,
441 U,
442 S1,
443 S2,
444 }
445 }
446}
447
448impl BlindedExpiringProfileKeyCredentialWithSecretNonce {
449 pub fn get_blinded_expiring_profile_key_credential(
450 &self,
451 ) -> BlindedExpiringProfileKeyCredential {
452 BlindedExpiringProfileKeyCredential {
453 t: self.t,
454 U: self.U,
455 S1: self.S1,
456 S2: self.S2,
457 }
458 }
459}
460
461impl BlindedReceiptCredentialWithSecretNonce {
462 pub fn get_blinded_receipt_credential(&self) -> BlindedReceiptCredential {
463 BlindedReceiptCredential {
464 t: self.t,
465 U: self.U,
466 S1: self.S1,
467 S2: self.S2,
468 }
469 }
470}
471
472#[cfg(test)]
473mod tests {
474 use super::*;
475 use crate::common::constants::*;
476 use crate::crypto::proofs;
477
478 #[test]
479 fn test_system() {
480 let params = SystemParams::generate();
481 println!("PARAMS = {:#x?}", bincode::serialize(¶ms));
482 assert!(SystemParams::generate() == SystemParams::get_hardcoded());
483 }
484
485 #[test]
486 fn test_mac() {
487 let mut sho = Sho::new(b"Test_Credentials", b"");
492 let keypair = KeyPair::<AuthCredentialWithPni>::generate(&mut sho);
493
494 let uid_bytes = TEST_ARRAY_16;
495 let redemption_time = Timestamp::from_epoch_seconds(37 * SECONDS_PER_DAY);
496 let aci = libsignal_core::Aci::from_uuid_bytes(uid_bytes);
497 let aci_struct = uid_struct::UidStruct::from_service_id(aci.into());
498 let pni = libsignal_core::Aci::from_uuid_bytes(uid_bytes);
499 let pni_struct = uid_struct::UidStruct::from_service_id(pni.into());
500 let credential = keypair.create_auth_credential_with_pni(
501 aci_struct,
502 pni_struct,
503 redemption_time,
504 &mut sho,
505 );
506 let proof = proofs::AuthCredentialWithPniIssuanceProof::new(
507 keypair,
508 credential,
509 aci_struct,
510 pni_struct,
511 redemption_time,
512 &mut sho,
513 );
514
515 let public_key = keypair.get_public_key();
516 proof
517 .verify(
518 public_key,
519 credential,
520 aci_struct,
521 pni_struct,
522 redemption_time,
523 )
524 .unwrap();
525
526 let keypair_bytes = bincode::serialize(&keypair).unwrap();
527 let keypair2 = bincode::deserialize(&keypair_bytes).unwrap();
528 assert!(keypair == keypair2);
529
530 let public_key_bytes = bincode::serialize(&public_key).unwrap();
531 let public_key2 = bincode::deserialize(&public_key_bytes).unwrap();
532 assert!(public_key == public_key2);
533
534 let mac_bytes = bincode::serialize(&credential).unwrap();
535
536 println!("mac_bytes = {:#x?}", mac_bytes);
537 assert!(
538 mac_bytes
539 == vec![
540 0xf0, 0xf7, 0x41, 0xdd, 0x71, 0xb3, 0x3a, 0x18, 0x67, 0x26, 0x43, 0x15, 0xb0,
541 0x4, 0x8e, 0xef, 0x31, 0xf0, 0x85, 0x77, 0x43, 0x68, 0x54, 0xf5, 0x91, 0x39,
542 0xaa, 0xf, 0xb4, 0xca, 0x7c, 0x4, 0xfe, 0x4b, 0x1c, 0xcd, 0x3c, 0xef, 0x74,
543 0x8, 0xca, 0x30, 0x7f, 0xcb, 0xfd, 0xda, 0xe9, 0xdb, 0xfd, 0xed, 0xbb, 0xec,
544 0xce, 0x23, 0x2, 0x2, 0xa3, 0xec, 0xaa, 0x96, 0xea, 0xd, 0xac, 0x57, 0x62,
545 0x8f, 0x6e, 0x51, 0x84, 0x7f, 0xb1, 0x62, 0x39, 0x6, 0xbc, 0x4d, 0x14, 0xed,
546 0x96, 0xe9, 0x6, 0x27, 0xbd, 0x54, 0xc0, 0x31, 0xdc, 0x92, 0x9e, 0x40, 0x1,
547 0x33, 0xfc, 0xc5, 0x56, 0x7a,
548 ]
549 );
550 }
551}