|
| 1 | +from secrets import randbelow |
| 2 | +from typing import List, Tuple |
| 3 | + |
| 4 | +from mife.common import discrete_log_bound, invertible_matrix |
| 5 | +from mife.data.pairing import PairingBase |
| 6 | +from mife.data.pyecc_bn128_wrapper import Bn128Pairing |
| 7 | +from mife.data.matrix import Matrix |
| 8 | +from mife.data.group import GroupElem |
| 9 | +from mife.data.zmod_r import ZmodR, _ZmodRElem |
| 10 | + |
| 11 | + |
| 12 | +# References: |
| 13 | +# https://eprint.iacr.org/2018/206.pdf |
| 14 | + |
| 15 | +class _FeDDH_MSK: |
| 16 | + def __init__(self, s: List[_ZmodRElem], t: List[_ZmodRElem]): |
| 17 | + self.s = s |
| 18 | + self.t = t |
| 19 | + |
| 20 | +class _FeDDH_MK: |
| 21 | + def __init__(self, n: int, F: PairingBase, G: ZmodR, gs: List[GroupElem], gt: List[GroupElem], msk: _FeDDH_MSK = None): |
| 22 | + self.n = n |
| 23 | + self.F = F |
| 24 | + self.G = G |
| 25 | + self.gs = gs |
| 26 | + self.gt = gt |
| 27 | + self.msk = msk |
| 28 | + |
| 29 | + def has_private_key(self) -> bool: |
| 30 | + return self.msk is not None |
| 31 | + |
| 32 | + def get_public_key(self): |
| 33 | + return _FeDDH_MK(self.n, self.F, self.G, self.gs, self.gt) |
| 34 | + |
| 35 | + |
| 36 | +class _FeDDH_SK: |
| 37 | + def __init__(self, g2f: GroupElem, f: List[List[int]]): |
| 38 | + self.g2f = g2f |
| 39 | + self.f = f |
| 40 | + |
| 41 | +class _FeDDH_C: |
| 42 | + def __init__(self, g1_gamma: GroupElem, c: List[List[GroupElem]]): |
| 43 | + self.g1_gamma = g1_gamma |
| 44 | + self.c = c |
| 45 | + |
| 46 | +class FeDDH: |
| 47 | + |
| 48 | + @staticmethod |
| 49 | + def generate(n: int, F: PairingBase = None) -> _FeDDH_MK: |
| 50 | + """ |
| 51 | + Generate a FeDDH master key |
| 52 | +
|
| 53 | + :param n: Dimension of the encrypt vector |
| 54 | + :param F: Group to use for the scheme. If set to None, bn128 will be used |
| 55 | + :return: FeDDH master key |
| 56 | + """ |
| 57 | + if F is None: |
| 58 | + F = Bn128Pairing() |
| 59 | + |
| 60 | + G = ZmodR(F.order()) |
| 61 | + |
| 62 | + s = [G(randbelow(F.order())) for i in range(n)] |
| 63 | + t = [G(randbelow(F.order())) for i in range(n)] |
| 64 | + |
| 65 | + g1 = F.generator1() |
| 66 | + g2 = F.generator2() |
| 67 | + |
| 68 | + gs = [int(s[i]) * g1 for i in range(n)] |
| 69 | + gt = [int(t[i]) * g2 for i in range(n)] |
| 70 | + |
| 71 | + msk = _FeDDH_MSK(s,t) |
| 72 | + |
| 73 | + return _FeDDH_MK(n, F, G, gs, gt, msk=msk) |
| 74 | + |
| 75 | + @staticmethod |
| 76 | + def encrypt(x: List[int], y: List[int], key: _FeDDH_MK) -> _FeDDH_C: |
| 77 | + """ |
| 78 | + Encrypt FeDDH message vector |
| 79 | +
|
| 80 | + :param x: First Message vector |
| 81 | + :param y: Second Message vector |
| 82 | + :param key: FeDDH master key |
| 83 | + :return: FeDDH cipher text |
| 84 | + """ |
| 85 | + if len(x) != key.n or len(y) != key.n: |
| 86 | + raise Exception(f"Encrypt vector must be of length {key.n}") |
| 87 | + |
| 88 | + gamma = randbelow(key.G.order()) |
| 89 | + W = invertible_matrix(key.G, 2) |
| 90 | + W_iT = W.inverse().T |
| 91 | + |
| 92 | + c = [[] for i in range(key.n)] |
| 93 | + |
| 94 | + g1 = key.F.generator1() |
| 95 | + g2 = key.F.generator2() |
| 96 | + |
| 97 | + for i in range(key.n): |
| 98 | + a = Matrix.flatten((W_iT * Matrix([x[i], gamma * key.msk.s[i]]).T).M) |
| 99 | + b = Matrix.flatten((W * Matrix([y[i], -key.msk.t[i]]).T).M) |
| 100 | + c[i] = [int(a[0]) * g1, int(a[1]) * g1, int(b[0]) * g2, int(b[1]) * g2] |
| 101 | + |
| 102 | + return _FeDDH_C(gamma * g1, c) |
| 103 | + |
| 104 | + @staticmethod |
| 105 | + def decrypt(c: _FeDDH_C, pub: _FeDDH_MK, sk: _FeDDH_SK, bound: Tuple[int, int]) -> int: |
| 106 | + """ |
| 107 | + Decrypt FeDDH cipher text within a bound |
| 108 | +
|
| 109 | + :param c: FeDDH cipher text |
| 110 | + :param pub: FeDDH public key |
| 111 | + :param sk: FeDDH decryption key |
| 112 | + :param bound: Bound for discrete logarithm search, the decrypted text should be within the bound |
| 113 | + :return: Decrypted message |
| 114 | + """ |
| 115 | + out = pub.F.pairing(c.g1_gamma, sk.g2f) |
| 116 | + |
| 117 | + for i in range(pub.n): |
| 118 | + for j in range(pub.n): |
| 119 | + t = pub.F.pairing(c.c[i][0], c.c[j][2]) + pub.F.pairing(c.c[i][1], c.c[j][3]) |
| 120 | + out += sk.f[i][j] * t |
| 121 | + |
| 122 | + g1 = pub.F.generator1() |
| 123 | + g2 = pub.F.generator2() |
| 124 | + |
| 125 | + return discrete_log_bound(out, pub.F.pairing(g1, g2), bound) |
| 126 | + |
| 127 | + |
| 128 | + @staticmethod |
| 129 | + def keygen(f: List[List[int]], key: _FeDDH_MK) -> _FeDDH_SK: |
| 130 | + """ |
| 131 | + Generate FeDDH decryption key |
| 132 | +
|
| 133 | + :param f: Function vector f[i][j] is the coefficient for x_iy_j |
| 134 | + :param key: FeDDH master key |
| 135 | + :return: FeDDH decryption key |
| 136 | + """ |
| 137 | + if len(f) != key.n: |
| 138 | + raise Exception(f"Function vector must be of shape {key.n} x {key.n}") |
| 139 | + if not key.has_private_key(): |
| 140 | + raise Exception("Private key not found in master key") |
| 141 | + |
| 142 | + eval = 0 |
| 143 | + |
| 144 | + for i in range(key.n): |
| 145 | + if len(f[i]) != key.n: |
| 146 | + raise Exception(f"Function vector must be of shape {key.n} x {key.n}") |
| 147 | + for j in range(key.n): |
| 148 | + eval += f[i][j] * key.msk.s[i] * key.msk.t[j] |
| 149 | + |
| 150 | + g2f = int(eval) * key.F.generator2() |
| 151 | + |
| 152 | + return _FeDDH_SK(g2f, f) |
0 commit comments