def H1(n, v1, v2, msg): k = q // 4 # is kinda much smaller than q temp = add_zq(add_zq(v1, v2), hash_to_point(n, msg)) # that's just some random stuff return [ e % (k // n) for e in temp ] # we'll take each componenet modulo k//n in order to satisfy Manhattan
def verify_1(n, m, sig, uid, pk, MPK): print("sig: {}".format(sig)) # parsing sig e = sig[0] z = sig[1] z1, z2, z1a, z2a = z[0], z[1], z[2], z[3] # restore e vev1 = add_zq(add_zq(z1, mul_zq(z2, MPK)), mul_zq(uid, neg(e))) vec2 = add_zq(add_zq(z1a, mul_zq(z2a, MPK)), mul_zq(pk, neg(e))) e_check = H1(n, vev1, vec2, m) #print('e from sig: {}'.format(e)) #print('e resttored: {}'.format(e_check)) return e == e_check
def vecvecmul(u, v, integer=False, modulus=None): """ Compute the product u * (v^t), u and v are row vectors, and v^t denotes the transose of v. Input: u A row vector v A row vector integer This flag should be set (to True) iff the elements are in Z[x] / (x ** n + 1) modulus This flag should be set (to q) iff the elements are in Z_q[x] / (x ** n + 1) Output: rep The product u * (v^t) = sum(u[i] * v[i] for i in range(len(u))) Format: Coefficient """ m = len(u) deg = len(u[0]) assert (len(u) == len(v)) rep = [0 for k in range(deg)] if modulus is not None: for i in range(m): rep = add_zq(rep, mul_zq(u[i], v[i], modulus), modulus) return rep else: for i in range(m): rep = add(rep, mul(u[i], v[i])) if integer is True: rep = [int(round(elt)) for elt in rep] return rep
def vecmatmul(t, B, integer=False, modulus=None): """ Compute the product t * B, where t is a vector and B is a square matrix. Input: t A row vector B A matrix integer This flag should be set (to True) iff the elements are in Z[x] / (x ** n + 1) modulus This flag should be set (to q) iff the elements are in Z_q[x] / (x ** n + 1) Output: v The row vector t * B Format: Coefficient """ nrows = len(B) ncols = len(B[0]) deg = len(B[0][0]) assert (len(t) == nrows) v = [[0 for k in range(deg)] for j in range(ncols)] if modulus is not None: for j in range(ncols): for i in range(nrows): v[j] = add_zq(v[j], mul_zq(t[i], B[i][j], modulus), modulus) return v else: for j in range(ncols): for i in range(nrows): v[j] = add(v[j], mul(t[i], B[i][j])) if integer is True: v = [[int(round(elt)) for elt in poly] for poly in v] return v
def test_module_ntru_gen(d, m, iterations): q = q_12289 for _ in range(iterations): A, B, inv_B, sqr_gsnorm = module_ntru_gen(d, q, m) # Check that the determinant of B is q if (my_det(B) != [q] + [0] * (d - 1)): print("det(B) != q") return False # Check that B * A = 0 mod q C = [None] * (m + 1) for i in range(m + 1): elt = [0] * d for j in range(m + 1): elt = add_zq(elt, mul_zq(B[i][j], A[j], q), q) C[i] = elt if any(elt != [0] * d for elt in C): print("Error: A and B are not orthogonal") return False # If all the checks passed, return True return True
def verify(self, message, signature): """Verify a signature.""" r, s = signature """1. hashes a message to a point of Z[x] mod (Phi,q""" hashed = self.hash_to_point(message, r) """2. Computes s0 + s1*h.""" result = add_zq(s[0], mul_zq(s[1], self.h)) """3. Verifies that the s0 + s1*h = hashed.""" # print "h =", self.h # print "r =", r if any(result[i] != hashed[i] for i in range(self.n)): print("The signature does not correspond to the hash!") return False """4. Verifies that the norm is small""" norm_sign = sum(sum(elt**2 for elt in part) for part in s) # print "signature bound = ", self.signature_bound # print "norm of signature = ", norm_sign if norm_sign > self.signature_bound: print("The squared norm of the signature is too big:", norm_sign) return False """5. If the previous steps did not fail, accept.""" return True
def verify(self, message, signature): """ Verify a signature. Input: self The private key message The message to sign signature The signature (r, s) Output: True If (s * A == H(r||message)) and (s is short) False Otherwise """ A = self.A q = self.q d = self.d m = self.m r, t = signature s = decompress(t, self.d, self.rate) # The message is hashed to a point of Z_q[x] / (x ** d + 1) hashed = self.hash_to_point(message, r) # One compute result = s * A result = [0] * d for i in range(m + 1): result = add_zq(result, mul_zq(s[i], A[i], q), q) # Check that the hashed point == result if any(result[i] != hashed[i] for i in range(d)): print("The signature does not correspond to the hash!") return False # Check that the norm is small norm_sign = sum(sum(elt**2 for elt in part) for part in s) if norm_sign > self.signature_bound: print("The squared norm of the signature is too big:", norm_sign) return False # If the two checks passed, accept return True
def module_ntru_gen(d, q, m): """ Take as input system parameters, and output two "module-NTRU" matrices A and B such that: - B * A = 0 [mod q] - B has small polynomials - A is in Hermite normal form Also compute the inverse of B (over the field K = Q[x] / (x ** d + 1)). Input: d The degree of the underlying ring R = Z[x] / (x ** d + 1) q An integer m An integer Output: A A matrix in R ^ ((m + 1) x 1) B A matrix in R ^ ((m + 1) x (m + 1)) inv_B A matrix in K ^ ((m + 1) x (m + 1)) sq_gs_norm A real number, the square of the Gram-Schmidt norm of B Format: Coefficient """ if m == 1: magic_constant = [1.15] gs_slack = 1.17 elif m == 2: magic_constant = [1.07, 1.14] gs_slack = 1.17 elif m == 3: magic_constant = [1.21, 1.10, 1.06] gs_slack = 1.24 else: print("No parameters implemented yet for m = {m}".format(m=m)) return max_gs_norm = gs_slack * (q ** (1 / (m + 1))) while True: # We generate all rows of B except the last B = [[None for j in range(m + 1)] for i in range(m + 1)] for i in range(m): for j in range(m + 1): # Each coefficient B[i][j] is a polynomial sigma = magic_constant[i] * (q ** (1 / (m + 1))) # ==> ||bi~|| = gs_slack * q^(1/(m+1)) sig = sqrt(1 / (d * (m + 1 - i))) * sigma # sig = stdv. dev. des coef de bi B[i][j] = [int(round(gauss(0, sig))) for k in range(d)] # We check that the GS norm is not larger than tolerated Bp_fft = [[fft(poly) for poly in row] for row in B[:-1]] Gp = gram_fft(Bp_fft) [Lp_fft, Dp_fft] = ldl_fft(Gp) Dp = [[[0] * d for col in range(m)] for row in range(m)] for i in range(m): Dp[i][i] = ifft(Dp_fft[i][i]) prod_di = [1] + [0] * (d - 1) for i in range(m): prod_di = mul(prod_di, Dp[i][i]) last = div([q ** 2] + [0] * (d - 1), prod_di) norms = [Dp[i][i][0] for i in range(m)] + [last[0]] # If the GS norm is too large, restart if sqrt(max(norms)) > max_gs_norm: continue # Try to solve the module-NTRU equation f = submatrix(B, m, 0) f = [[neg(elt) for elt in row] for row in f] g = [B[j][0] for j in range(m)] fp = my_det(f) adjf = my_adjugate(f) gp = [0] * d for i in range(m): gp = add(gp, karamul(adjf[0][i], g[i])) try: # Compute f^(-1) mod q fp_q = [elt % q for elt in fp] inv_f = [[elt[:] for elt in row] for row in adjf] for i in range(m): for j in range(m): inv_f[i][j] = [elt % q for elt in inv_f[i][j]] inv_f[i][j] = div_zq(inv_f[i][j], fp_q, q) # Compute h = f^(-1) * g mod q and A = [1 | h] h = [None] * m for i in range(m): elt = [0] * d for j in range(m): elt = add_zq(elt, mul_zq(inv_f[i][j], g[j], q), q) h[i] = elt one = [1] + [0 for _ in range(1, d)] A = [one] + h Fp, Gp = ntru_solve(fp, gp, q) B[m][0] = Gp B[m][1] = [- coef for coef in Fp] for i in range(2, m + 1): B[m][i] = [0 for _ in range(d)] # Compute the inverse of B det_B = my_det(B) inv_B = my_adjugate(B) inv_B = [[div(poly, det_B) for poly in row] for row in inv_B] return A, B, inv_B, max(norms) # If any step failed, restart except (ZeroDivisionError, ValueError): continue