libpep/low_level/
primitives.rs

1//! PEP primitives for [rekey]ing, [reshuffle]ing, [rerandomize]ation of [ElGamal] ciphertexts, their
2//! transitive and reversible n-PEP extensions, and combined versions.
3
4use crate::internal::arithmetic::*;
5use crate::low_level::elgamal::*;
6
7/// Change the representation of a ciphertext without changing the contents.
8/// Used to make multiple unlinkable copies of the same ciphertext (when disclosing a single
9/// stored message multiple times).
10#[cfg(feature = "elgamal3")]
11pub fn rerandomize(encrypted: &ElGamal, r: &ScalarNonZero) -> ElGamal {
12    ElGamal {
13        gb: r * G + encrypted.gb,
14        gc: r * encrypted.gy + encrypted.gc,
15        gy: encrypted.gy,
16    }
17}
18/// Change the representation of a ciphertext without changing the contents.
19/// Used to make multiple unlinkable copies of the same ciphertext (when disclosing a single
20/// stored message multiple times).
21/// Requires the public key `gy` that was used to encrypt the message to be provided.
22#[cfg(not(feature = "elgamal3"))]
23pub fn rerandomize(encrypted: &ElGamal, gy: &GroupElement, r: &ScalarNonZero) -> ElGamal {
24    ElGamal {
25        gb: r * G + encrypted.gb,
26        gc: r * gy + encrypted.gc,
27    }
28}
29
30/// Change the contents of a ciphertext with factor `s`, i.e. message `M` becomes `s * M`.
31/// Can be used to blindly and pseudo-randomly pseudonymize identifiers.
32pub fn reshuffle(encrypted: &ElGamal, s: &ScalarNonZero) -> ElGamal {
33    ElGamal {
34        gb: s * encrypted.gb,
35        gc: s * encrypted.gc,
36        #[cfg(feature = "elgamal3")]
37        gy: encrypted.gy,
38    }
39}
40
41/// Make a message encrypted under one key decryptable under another key.
42/// If the original message was encrypted under key `Y`, the new message will be encrypted under key
43/// `k * Y` such that users with secret key `k * y` can decrypt it.
44pub fn rekey(encrypted: &ElGamal, k: &ScalarNonZero) -> ElGamal {
45    ElGamal {
46        gb: k.invert() * encrypted.gb, // TODO k.invert can be precomputed
47        gc: encrypted.gc,
48        #[cfg(feature = "elgamal3")]
49        gy: k * encrypted.gy,
50    }
51}
52
53/// Combination of  [`reshuffle`] and [`rekey`] (more efficient and secure than applying them
54/// separately).
55pub fn rsk(encrypted: &ElGamal, s: &ScalarNonZero, k: &ScalarNonZero) -> ElGamal {
56    ElGamal {
57        gb: (s * k.invert()) * encrypted.gb, // TODO s * k.invert can be precomputed
58        gc: s * encrypted.gc,
59        #[cfg(feature = "elgamal3")]
60        gy: k * encrypted.gy,
61    }
62}
63
64/// Combination of [`rerandomize`], [`reshuffle`] and [`rekey`] (more efficient and secure than
65/// applying them separately).
66#[cfg(feature = "elgamal3")]
67pub fn rrsk(m: &ElGamal, r: &ScalarNonZero, s: &ScalarNonZero, k: &ScalarNonZero) -> ElGamal {
68    let ski = s * k.invert();
69    ElGamal {
70        gb: ski * m.gb + ski * r * G,
71        gc: (s * r) * m.gy + s * m.gc,
72        gy: k * m.gy,
73    }
74}
75
76/// Combination of [`rerandomize`], [`reshuffle`] and [`rekey`] (more efficient and secure than
77/// applying them separately).
78#[cfg(not(feature = "elgamal3"))]
79pub fn rrsk(
80    m: &ElGamal,
81    gy: &GroupElement,
82    r: &ScalarNonZero,
83    s: &ScalarNonZero,
84    k: &ScalarNonZero,
85) -> ElGamal {
86    let ski = s * k.invert();
87    ElGamal {
88        gb: ski * m.gb + ski * r * G,
89        gc: (s * r) * gy + s * m.gc,
90    }
91}
92
93/// A transitive and reversible n-PEP extension of [`reshuffle`], reshuffling from one pseudonym to
94/// another.
95pub fn reshuffle2(m: &ElGamal, s_from: &ScalarNonZero, s_to: &ScalarNonZero) -> ElGamal {
96    let s = s_from.invert() * s_to;
97    reshuffle(m, &s)
98}
99/// A transitive and reversible n-PEP extension of [`rekey`], rekeying from one key to
100/// another.
101pub fn rekey2(m: &ElGamal, k_from: &ScalarNonZero, k_to: &ScalarNonZero) -> ElGamal {
102    let k = k_from.invert() * k_to;
103    rekey(m, &k)
104}
105
106/// A transitive and reversible n-PEP extension of [`rsk`].
107pub fn rsk2(
108    m: &ElGamal,
109    s_from: &ScalarNonZero,
110    s_to: &ScalarNonZero,
111    k_from: &ScalarNonZero,
112    k_to: &ScalarNonZero,
113) -> ElGamal {
114    let s = s_from.invert() * s_to;
115    let k = k_from.invert() * k_to;
116    rsk(m, &s, &k)
117}
118
119/// A transitive and reversible n-PEP extension of [`rrsk`].
120#[cfg(feature = "elgamal3")]
121pub fn rrsk2(
122    m: &ElGamal,
123    r: &ScalarNonZero,
124    s_from: &ScalarNonZero,
125    s_to: &ScalarNonZero,
126    k_from: &ScalarNonZero,
127    k_to: &ScalarNonZero,
128) -> ElGamal {
129    let s = s_from.invert() * s_to;
130    let k = k_from.invert() * k_to;
131    rrsk(m, r, &s, &k)
132}
133/// A transitive and reversible n-PEP extension of [`rrsk`].
134#[cfg(not(feature = "elgamal3"))]
135pub fn rrsk2(
136    m: &ElGamal,
137    gy: &GroupElement,
138    r: &ScalarNonZero,
139    s_from: &ScalarNonZero,
140    s_to: &ScalarNonZero,
141    k_from: &ScalarNonZero,
142    k_to: &ScalarNonZero,
143) -> ElGamal {
144    let s = s_from.invert() * s_to;
145    let k = k_from.invert() * k_to;
146    rrsk(m, gy, r, &s, &k)
147}