Fork of the official github repository of the framework Leaky-LWE-Estimator, a Sage Toolkit to attack and estimate the hardness of LWE with Side Information. https://github.com/lducas/leaky-LWE-Estimator
選択できるのは25トピックまでです。 トピックは、先頭が英数字で、英数字とダッシュ('-')を使用した35文字以内のものにしてください。

  1. from numpy.random import choice as np_random_choice
  2. from numpy.linalg import slogdet, qr, cholesky
  3. from numpy import array
  4. from math import pi, exp
  5. print_style = {'SUCCESS': '\x1b[1;37;42m',
  6. 'FAILURE': '\x1b[1;37;41m',
  7. 'REJECT': '\x1b[3;31m',
  8. 'ACCEPT': '\x1b[3;32m',
  9. 'VALUE': '\x1b[1;33m',
  10. 'DATA': '\x1b[3;34m',
  11. 'WARNING': '\x1b[6;30;43m',
  12. 'ACTION': '\x1b[1;37m',
  13. 'HEADER': '\x1b[4;37m',
  14. 'NORMAL': '\x1b[0m',
  15. }
  16. def logging(message, style='NORMAL', newline=True):
  17. if style is not None:
  18. print(print_style[style], end=' ')
  19. print(message, end=' ')
  20. if newline:
  21. print('\x1b[0m')
  22. else:
  23. print('\x1b[0m', end=' ')
  24. sys.stdout.flush()
  25. def hint_to_string(v, l=None, lit="u", max_coeffs=5):
  26. s = ""
  27. count = 0
  28. for i in range(v.ncols()):
  29. x = v[0][i]
  30. if x == 0:
  31. continue
  32. count += 1
  33. if count > max_coeffs:
  34. s += "+ ... "
  35. break
  36. if x == 1:
  37. if count == 1:
  38. s += "%s%d " % (lit, i)
  39. else:
  40. s += "+ %s%d " % (lit, i)
  41. elif x == -1:
  42. s += "- %s%d " % (lit, i)
  43. elif x > 0 and count == 1:
  44. s += "%s*%s%d " % (str(x), lit, i)
  45. elif x > 0:
  46. s += "+ %s*%s%d " % (str(x), lit, i)
  47. else:
  48. s += "- %s*%s%d " % (str(-x), lit, i)
  49. if l is not None:
  50. s += "= %s" % str(l)
  51. return s
  52. ROUNDING_FACTOR = 2**64
  53. # Vectors will consistently be represented as 1*n matrices
  54. def vec(x):
  55. # Is this a 2-dim object, with only one row
  56. try:
  57. x[0][0]
  58. try:
  59. x[1]
  60. except:
  61. return matrix(x)
  62. raise ValueError(
  63. " The object has more than one line: can't convert to vec.")
  64. except:
  65. # Then it should be a 1 dim object
  66. return matrix([x])
  67. def mat(x):
  68. return matrix(x)
  69. # Convert a 1*1 matrix into a scalar
  70. def scal(M):
  71. assert M.nrows() == 1 and M.ncols() == 1, "This doesn't seem to be a scalar."
  72. return M[0, 0]
  73. def average_variance(D):
  74. mu = 0.
  75. s = 0.
  76. for (v, p) in D.items():
  77. mu += v * p
  78. s += v * v * p
  79. s -= mu * mu
  80. return round_to_rational(mu), round_to_rational(s)
  81. def canonical_vec(dimension, index):
  82. """Returns the vector [0,0 ... 0] with
  83. a 1 in a specific index
  84. :dimension: integer
  85. :index: integer
  86. """
  87. v = [0 for _ in range(dimension)]
  88. v[index] = 1
  89. return vec(v)
  90. def is_canonical_direction(v):
  91. """ Test wether the vector has a cannonical direction, and returns
  92. its index and length if so, else None, None.
  93. """
  94. nz = [x != 0 for x in v]
  95. if sum(nz) != 1:
  96. return None
  97. i = nz.index(True)
  98. return i
  99. def round_matrix_to_rational(M):
  100. A = matrix(ZZ, (ROUNDING_FACTOR * matrix(M)).apply_map(round))
  101. return matrix(QQ, A / ROUNDING_FACTOR)
  102. def round_vector_to_rational(v):
  103. A = vec(ZZ, (ROUNDING_FACTOR * vec(v)).apply_map(round))
  104. return vec(QQ, A / ROUNDING_FACTOR)
  105. def round_to_rational(x):
  106. A = ZZ(round(x * ROUNDING_FACTOR))
  107. return QQ(A) / QQ(ROUNDING_FACTOR)
  108. def concatenate(L1, L2=None):
  109. """
  110. concatenate vecs
  111. """
  112. if L2 is None:
  113. return vec(sum([list(vec(x)[0]) for x in L1], []))
  114. return vec(list(vec(L1)[0]) + list(vec(L2)[0]))
  115. def draw_from_distribution(D):
  116. """draw an element from the distribution D
  117. :D: distribution in a dictionnary form
  118. """
  119. X = np_random_choice([key for key in D.keys()],
  120. 1, replace=True,
  121. p=[float(prob) for prob in D.values()])
  122. return X[0]
  123. def GH_sv_factor_squared(k):
  124. return ((pi * k)**(1. / k) * k / (2. * pi * e))
  125. def compute_delta(k):
  126. """Computes delta from the block size k. Interpolation from the following
  127. data table:
  128. Source : https://bitbucket.org/malb/lwe-estimator/
  129. src/9302d4204b4f4f8ceec521231c4ca62027596337/estima
  130. tor.py?at=master&fileviewer=file-view-default
  131. :k: integer
  132. estimator.py table:
  133. """
  134. small = {0: 1e20, 1: 1e20, 2: 1.021900, 3: 1.020807, 4: 1.019713, 5: 1.018620,
  135. 6: 1.018128, 7: 1.017636, 8: 1.017144, 9: 1.016652, 10: 1.016160,
  136. 11: 1.015898, 12: 1.015636, 13: 1.015374, 14: 1.015112, 15: 1.014850,
  137. 16: 1.014720, 17: 1.014590, 18: 1.014460, 19: 1.014330, 20: 1.014200,
  138. 21: 1.014044, 22: 1.013888, 23: 1.013732, 24: 1.013576, 25: 1.013420,
  139. 26: 1.013383, 27: 1.013347, 28: 1.013310, 29: 1.013253, 30: 1.013197,
  140. 31: 1.013140, 32: 1.013084, 33: 1.013027, 34: 1.012970, 35: 1.012914,
  141. 36: 1.012857, 37: 1.012801, 38: 1.012744, 39: 1.012687, 40: 1.012631,
  142. 41: 1.012574, 42: 1.012518, 43: 1.012461, 44: 1.012404, 45: 1.012348,
  143. 46: 1.012291, 47: 1.012235, 48: 1.012178, 49: 1.012121, 50: 1.012065}
  144. if k != round(k):
  145. x = k - floor(k)
  146. d1 = compute_delta(floor(x))
  147. d2 = compute_delta(floor(x) + 1)
  148. return x * d2 + (1 - x) * d1
  149. k = int(k)
  150. if k < 50:
  151. return small[k]
  152. else:
  153. delta = GH_sv_factor_squared(k)**(1. / (2. * k - 2.))
  154. return delta.n()
  155. def bkzgsa_gso_len(logvol, i, d, beta=None, delta=None):
  156. if delta is None:
  157. delta = compute_delta(beta)
  158. return RR(delta**(d - 1 - 2 * i) * exp(logvol / d))
  159. rk = [0.789527997160000, 0.780003183804613, 0.750872218594458, 0.706520454592593, 0.696345241018901, 0.660533841808400, 0.626274718790505, 0.581480717333169, 0.553171463433503, 0.520811087419712, 0.487994338534253, 0.459541470573431, 0.414638319529319, 0.392811729940846, 0.339090376264829, 0.306561491936042, 0.276041187709516, 0.236698863270441, 0.196186341673080, 0.161214212092249, 0.110895134828114, 0.0678261623920553, 0.0272807162335610, -
  160. 0.0234609979600137, -0.0320527224746912, -0.0940331032784437, -0.129109087817554, -0.176965384290173, -0.209405754915959, -0.265867993276493, -0.299031324494802, -0.349338597048432, -0.380428160303508, -0.427399405474537, -0.474944677694975, -0.530140672818150, -0.561625221138784, -0.612008793872032, -0.669011014635905, -0.713766731570930, -0.754041787011810, -0.808609696192079, -0.859933249032210, -0.884479963601658, -0.886666930030433]
  161. simBKZ_c = [None] + [rk[-i] - sum(rk[-i:]) / i for i in range(1, 46)]
  162. pruning_proba = .5
  163. simBKZ_c += [RR(log(GH_sv_factor_squared(d)) / 2. -
  164. log(pruning_proba) / d) / log(2.) for d in range(46, 1000)]
  165. def simBKZ(l, beta, tours=1, c=simBKZ_c):
  166. n = len(l)
  167. l2 = copy(vector(RR, l))
  168. for k in range(n - 1):
  169. d = min(beta, n - k)
  170. f = k + d
  171. logV = sum(l2[k:f])
  172. lma = logV / d + c[d]
  173. if lma >= l2[k]:
  174. continue
  175. diff = l2[k] - lma
  176. l2[k] -= diff
  177. for a in range(k + 1, f):
  178. l2[a] += diff / (f - k - 1)
  179. return l2
  180. chisquared_table = {i: None for i in range(1000)}
  181. for i in range(1000):
  182. chisquared_table[i] = RealDistribution('chisquared', i)
  183. def conditional_chi_squared(d1, d2, lt, l2):
  184. """
  185. Probability that a gaussian sample (var=1) of dim d1+d2 has length at most
  186. lt knowing that the d2 first cordinates have length at most l2
  187. """
  188. D1 = chisquared_table[d1].cum_distribution_function
  189. D2 = chisquared_table[d2].cum_distribution_function
  190. l2 = RR(l2)
  191. PE2 = D2(l2)
  192. steps = 5 * (d1 + d2)
  193. # Numerical computation of the integral
  194. proba = 0.
  195. for i in range(steps)[::-1]:
  196. l2_min = i * l2 / steps
  197. l2_mid = (i + .5) * l2 / steps
  198. l2_max = (i + 1) * l2 / steps
  199. PC2 = (D2(l2_max) - D2(l2_min)) / PE2
  200. PE1 = D1(lt - l2_mid)
  201. proba += PC2 * PE1
  202. return proba
  203. def compute_beta_delta(d, logvol, tours=1, interpolate=True, probabilistic=False):
  204. """
  205. Computes the beta value for given dimension and volumes
  206. It is assumed that the instance has been normalized and sphericized,
  207. i.e. that the covariance matrices of the secret is the identity
  208. :d: integer
  209. :vol: float
  210. """
  211. bbeta = None
  212. pprev_margin = None
  213. # Keep increasing beta to be sure to catch the second intersection
  214. if not probabilistic:
  215. for beta in range(2, d):
  216. lhs = RR(sqrt(beta))
  217. rhs = bkzgsa_gso_len(logvol, d - beta, d, beta=beta)
  218. if lhs < rhs and bbeta is None:
  219. margin = rhs / lhs
  220. prev_margin = pprev_margin
  221. bbeta = beta
  222. if lhs > rhs:
  223. bbeta = None
  224. pprev_margin = rhs / lhs
  225. if bbeta is None:
  226. return 9999, 0
  227. ddelta = compute_delta(bbeta) * margin**(1. / d)
  228. if prev_margin is not None and interpolate:
  229. beta_low = log(margin) / (log(margin) - log(prev_margin))
  230. else:
  231. beta_low = 0
  232. assert beta_low >= 0
  233. assert beta_low <= 1
  234. return bbeta - beta_low, ddelta
  235. else:
  236. remaining_proba = 1.
  237. average_beta = 0.
  238. delta = compute_delta(2)
  239. l = [log(bkzgsa_gso_len(logvol, i, d, delta=delta)) / log(2)
  240. for i in range(d)]
  241. for beta in range(2, d):
  242. for t in range(tours):
  243. l = simBKZ(l, beta, 1)
  244. proba = 1.
  245. delta = compute_delta(beta)
  246. i = d - beta
  247. proba *= chisquared_table[beta].cum_distribution_function(
  248. 2**(2 * l[i]))
  249. for j in range(2, int(d / beta + 1)):
  250. i = d - j * (beta - 1) - 1
  251. xt = 2**(2 * l[i])
  252. if j > 1:
  253. x2 = 2**(2 * l[i + (beta - 1)])
  254. d2 = d - i + (beta - 1)
  255. proba *= conditional_chi_squared(beta - 1, d2, xt, x2)
  256. average_beta += beta * remaining_proba * proba
  257. remaining_proba *= 1. - proba
  258. if remaining_proba < .001:
  259. average_beta += beta * remaining_proba
  260. break
  261. if remaining_proba > .01:
  262. raise ValueError("This instance may be unsolvable")
  263. ddelta = compute_delta(average_beta)
  264. return average_beta, ddelta
  265. def block4(A, B, C, D):
  266. return block_matrix([[A, B], [C, D]])
  267. def build_LWE_lattice(A, q):
  268. """Builds a n*m matrix of the form
  269. q*I, 0
  270. A^T, -I
  271. It corresponds to the LWE matrix
  272. :A: a m*n matrix
  273. :q: integer
  274. """
  275. (m, n) = A.nrows(), A.ncols()
  276. lambd = block4(q * identity_matrix(m),
  277. zero_matrix(ZZ, m, n),
  278. A.T,
  279. identity_matrix(n)
  280. )
  281. return lambd
  282. def kannan_embedding(A, target, factor=1):
  283. """Creates a kannan embedding, i.e. it appends a line and
  284. a column as follows.
  285. A, 0
  286. target, uSVP_embedding_coeff
  287. :A: a matrix
  288. :target: a vector
  289. """
  290. d = A.nrows()
  291. lambd = block4(A,
  292. zero_matrix(ZZ, d, 1),
  293. mat(target),
  294. mat([factor])
  295. )
  296. return lambd
  297. def recenter(elt, q):
  298. if elt > q / 2:
  299. return elt - q
  300. return elt
  301. def get_distribution_from_table(table, multiplicative_factor):
  302. eta = len(table)
  303. D_s = {}
  304. support = set()
  305. for i in range(eta):
  306. D_s[i] = table[i] / multiplicative_factor
  307. D_s[-i] = D_s[i]
  308. support.add(i)
  309. support.add(-i)
  310. _, var = average_variance(D_s)
  311. assert(abs(sum([D_s[i] for i in support]) - 1) < 1e-5)
  312. return D_s