lhe module
Python homomorphic encryption library supporting up to three multiplications and unlimited additions.
- class lhe.lhe.CTG1(g1r: G1, g1m_pr: G1)[source]
Bases:
tupleCiphertext in strictly G1 x G1 only.
- g1r: mclbn256.mclbn256.G1
Alias for field number 0
- g1m_pr: mclbn256.mclbn256.G1
Alias for field number 1
- class lhe.lhe.CTG2(g2r: G2, g2m_pr: G2)[source]
Bases:
tupleCiphertext in strictly G2 x G2 only.
- g2r: mclbn256.mclbn256.G2
Alias for field number 0
- g2m_pr: mclbn256.mclbn256.G2
Alias for field number 1
- class lhe.lhe.CTGT(z_r1_r2: GT, z_m2_s2_r2__r1: GT, z_m1_s1_r1__r2: GT, z_m1_s1_r1__m2_s2_r2: GT)[source]
Bases:
tupleLevel-2 ciphertext in $ extsf{GT}^{4}$.
- z_r1_r2: mclbn256.mclbn256.GT
Alias for field number 0
- z_m2_s2_r2__r1: mclbn256.mclbn256.GT
Alias for field number 1
- z_m1_s1_r1__r2: mclbn256.mclbn256.GT
Alias for field number 2
- z_m1_s1_r1__m2_s2_r2: mclbn256.mclbn256.GT
Alias for field number 3
- class lhe.lhe.CT1(ctg1: CTG1, ctg2: CTG2)[source]
Bases:
tupleAll-purpose (dual) level-1 ciphertext making use of both G1 and G2.
- ctg1: lhe.lhe.CTG1
Alias for field number 0
- ctg2: lhe.lhe.CTG2
Alias for field number 1
- class lhe.lhe.CT2(ctgt: CTGT)[source]
Bases:
tupleLevel-2 ciphertext (wrapper around GT^4).
- ctgt: lhe.lhe.CTGT
Alias for field number 0
- class lhe.lhe.KPG1(sk: Fr, pk: G1)[source]
Bases:
tupleKeypair for G1-based encryption.
- sk: mclbn256.mclbn256.Fr
Alias for field number 0
- pk: mclbn256.mclbn256.G1
Alias for field number 1
- class lhe.lhe.KPG2(sk: Fr, pk: G2)[source]
Bases:
tupleKeypair for G2-based encryption.
- sk: mclbn256.mclbn256.Fr
Alias for field number 0
- pk: mclbn256.mclbn256.G2
Alias for field number 1
- class lhe.lhe.SK(s1: Fr, s2: Fr)[source]
Bases:
tupleDual secret key for decryption
- s1: mclbn256.mclbn256.Fr
Alias for field number 0
- s2: mclbn256.mclbn256.Fr
Alias for field number 1
- class lhe.lhe.PK(p1: G1, p2: G2)[source]
Bases:
tupleDual public key for encryption (either ‘dumb’ group-agnostic, or optimal)
- p1: mclbn256.mclbn256.G1
Alias for field number 0
- p2: mclbn256.mclbn256.G2
Alias for field number 1
- lhe.lhe.keygen_G1() Tuple[mclbn256.mclbn256.Fr, mclbn256.mclbn256.G1][source]
Generate a G1 keypair.
- lhe.lhe.keygen_G2() lhe.lhe.KPG2[source]
Generate a G2 keypair.
- lhe.lhe.keygen() Tuple[lhe.lhe.SK, lhe.lhe.PK][source]
Generate a dual keypair.
- lhe.lhe.encrypt_G1(p: mclbn256.mclbn256.G1, m: int) lhe.lhe.CTG1[source]
Encrypt a plaintext to be a G1 ciphertext.
- lhe.lhe.encrypt_G2(p: mclbn256.mclbn256.G2, m: int) lhe.lhe.CTG2[source]
Encrypt a plaintext to be a G2 ciphertext.
- lhe.lhe.encrypt_GT(p1: mclbn256.mclbn256.G1, p2: mclbn256.mclbn256.G2, m: int) lhe.lhe.CTGT[source]
Encrypt a plaintext to be a GT ciphertext.
Each such ciphertext is made of the components z1 ** (-t + r + s) z1 ** (r * s2) z1 ** (s * s1) z1 ** (m + (t * s1 * s2)) from which (given the secrets, s1 and s2) you can compute z1 ** s and z1 ** r, then subtract from the first to get z1 ** -t which can then yield z1 ** (-t * s1 * s2), the inverse of everything in z1 ** (m + (t * s1 * s2)) except z1 ** m. m is finally extracted by a d.log. computation.
- lhe.lhe.encrypt_lvl_1(pk: lhe.lhe.PK, m: int) lhe.lhe.CT1[source]
Encrypt a plaintext to be a dual (‘dumb’) ciphertext.
- lhe.lhe.encrypt_lvl_2(pk: lhe.lhe.PK, m: int) lhe.lhe.CT2[source]
Encrypt a level-2 ciphertext.
- lhe.lhe.add_G1(ct1: lhe.lhe.CTG1, ct2: lhe.lhe.CTG1) lhe.lhe.CTG1[source]
Homomorphically add two G1 ciphertexts in level 1.
- lhe.lhe.add_G2(ct1: lhe.lhe.CTG2, ct2: lhe.lhe.CTG2) lhe.lhe.CTG2[source]
Homomorphically add two G2 ciphertexts in level 1.
- lhe.lhe.add_GT(ct1: lhe.lhe.CTGT, ct2: lhe.lhe.CTGT) lhe.lhe.CTGT[source]
Homomorphically add two GT ciphertexts in level 2.
- lhe.lhe.multiply_G1_G2(ct1: lhe.lhe.CTG1, ct2: lhe.lhe.CTG2) lhe.lhe.CTGT[source]
Homomorphically multiply two complementary level-1 ciphertexts and return a level-2 ciphertext of their product.
- lhe.lhe.decrypt_G1(s1: mclbn256.mclbn256.Fr, ct: lhe.lhe.CTG1) Optional[int][source]
Decrypt a G1 ciphertext to a plaintext.
>>> sk, pk = keygen_G1() >>> ct = encrypt_G1(pk, 737) >>> print(decrypt_G1(sk, ct)) 737
- lhe.lhe.decrypt_G2(s2: mclbn256.mclbn256.Fr, ct: lhe.lhe.CTG2) Optional[int][source]
Decrypt a G2 ciphertext to a plaintext.
>>> sk, pk = keygen_G2() >>> ct = encrypt_G2(pk, 747) >>> int(decrypt_G2(sk, ct)) 747
- lhe.lhe.decrypt_GT(s1: mclbn256.mclbn256.Fr, s2: mclbn256.mclbn256.Fr, ct: lhe.lhe.CTGT)[source]
Decrypt a level-2 ciphertext.
>>> sk1, pk1 = keygen_G1() >>> sk2, pk2 = keygen_G2()
>>> ct11 = encrypt_G1(pk1, 1) >>> ct12 = encrypt_G1(pk1, 2) >>> ct21 = encrypt_G2(pk2, 200) >>> ct22 = encrypt_G2(pk2, 22)
>>> ct1 = ct11 + ct12 >>> ct2 = ct21 + ct22
>>> ct3 = ct1 * ct2
>>> pt = decrypt_GT(sk1, sk2, ct3) >>> int(pt) 666
>>> sk, pk = keygen()
>>> ct_1 = encrypt_lvl_1(pk, 1) >>> ct_2 = encrypt_lvl_1(pk, 2) >>> ct_200 = encrypt_lvl_1(pk, 200) >>> ct_22 = encrypt_lvl_1(pk, 22)
>>> ct_3 = ct_1 + ct_2 >>> ct_222 = ct_200 + ct_22
>>> ct_666 = ct_3 * ct_222
>>> pt = decrypt(sk, ct_666) >>> int(pt) 666
The goal is to unmask the last ciphertext component and get z1 ** (m1 * m2).
Note that that component, z1 ** ((m1 + (s1 * r1)) * (m2 + (s2 * r2))), expands to equal
= z1 ** (m2 * r1 * s1) * z1 ** (m1 * r2 * s2) * z1 ** (r1 * r2 * s1 * s2) * z1 ** (m1 * m2) for whose terms we already have the ingredients to construct.
The z1 ** (r1 * r2 * s1 * s2) specifically cancels the last negative term in the ct.z_m2_s2_r2__r1 by ct.z_m1_s1_r1__r2 product.
We have z1 to the power of, (m2 + r1 s1)(m1 + r2 s2) = (m1 m2) + (m1 r1 s1) + (m2 r2 s2) + (r1 r2 s1 s2).
And z1 to the power of, (r1 r2)(s1 s2) + r1 (m1 + r2 s2)(-s1) + r2 (m2 + r1 s1)(-s2) = -(m1 r1 s1) + -(m2 r2 s2) + -(r1 r2 s2 s1).
Thus, we may decrypt by add these exponents (by multiplying powers) to get m1*m2 which can be extracted by a discrete log.
- lhe.lhe.decrypt(sk: lhe.lhe.SK, ct: Union[lhe.lhe.CT1, lhe.lhe.CT2, lhe.lhe.CTG1, lhe.lhe.CTG2, lhe.lhe.CTGT]) mclbn256.mclbn256.Fr[source]
Type-generic decryption helper
>>> sk, pk = keygen()
>>> pt_m = Fr() % (2 ** 12) >>> m = int(pt_m) >>> 0 <= m < 2 ** 12 True
>>> decrypt(sk, encrypt_G1(pk.p1, m)) == pt_m True
>>> decrypt(sk, encrypt_G2(pk.p2, m)) == pt_m True
>>> decrypt(sk, encrypt_GT(pk.p1, pk.p2, m)) == pt_m True
>>> decrypt(sk, encrypt_lvl_1(pk, m)) == pt_m True
>>> decrypt(sk, encrypt_lvl_2(pk, m)) == pt_m True
- lhe.lhe.dlog(base: Union[mclbn256.mclbn256.Fr, mclbn256.mclbn256.G1, mclbn256.mclbn256.G2, mclbn256.mclbn256.GT], power: mclbn256.mclbn256.GT, unsigned=False) Optional[mclbn256.mclbn256.Fr][source]
Discrete logarithm on any group, either Fr, G1, G2, or GT.
Can work with up to 20-bits before giving up. The example below tests 16-bit exponents of each type (for efficiency).
This helper may be replaced with Pollard’s Kangaroo method for a big boost (~2x) in performance. That optimization is unimplemented. Alternatively, we may use a lookup table.
>>> x = Fr() >>> a = Fr() % (2 ** 16) >>> dlog(x, x ** a) == a True
>>> x = G1().randomize() >>> a = Fr() % (2 ** 16) >>> y = x * a >>> dlog(x, y) == a True
>>> x = G2().randomize() >>> a = Fr() % (2 ** 16) >>> y = x * a >>> dlog(x, y) == a True
>>> x = G1().randomize() @ G2().randomize() >>> a = Fr() % (2 ** 16) >>> y = x ** a >>> dlog(x, y) == a True
- lhe.lhe.encrypt(pk: lhe.lhe.PK, m: int) lhe.lhe.CT1
Encrypt a plaintext to be a dual (‘dumb’) ciphertext.