Ejemplo n.º 1
0
def gs_norm(f, g, q):
    """Compute the squared Gram-Schmidt norm of the NTRU matrix generated by f, g.

    This matrix is [[g, - f], [G, - F]].
    This algorithm is equivalent to line 9 of algorithm 5 (NTRUGen).
    """
    sqnorm_fg = sqnorm([f, g])
    ffgg = add(mul(f, adj(f)), mul(g, adj(g)))
    Ft = div(adj(g), ffgg)
    Gt = div(adj(f), ffgg)
    sqnorm_FG = (q**2) * sqnorm([Ft, Gt])
    return max(sqnorm_fg, sqnorm_FG)
Ejemplo n.º 2
0
def ldl(G):
    """
    Compute the LDL decomposition of G.

    Input:
    G           A self-adjoint matrix (i.e. G is equal to its conjugate transpose)

    Output:
    L, D        The LDL decomposition of G, that is G = L * D * (L*), where:
                - L is lower triangular with a diagonal of 1's
                - D is diagonal

    Format:     Coefficient
    """
    deg = len(G[0][0])
    dim = len(G)
    L = [[[0 for k in range(deg)] for j in range(dim)] for i in range(dim)]
    D = [[[0 for k in range(deg)] for j in range(dim)] for i in range(dim)]
    for i in range(dim):
        L[i][i] = [1] + [0 for j in range(deg - 1)]
        D[i][i] = G[i][i]
        for j in range(i):
            L[i][j] = G[i][j]
            for k in range(j):
                L[i][j] = sub(L[i][j], mul(mul(L[i][k], adj(L[j][k])),
                                           D[k][k]))
            L[i][j] = div(L[i][j], D[j][j])
            D[i][i] = sub(D[i][i], mul(mul(L[i][j], adj(L[i][j])), D[j][j]))
    return [L, D]
def ldl(G):
    """
    Compute the LDL decomposition of G. Only works with 2 * 2 matrices.

    Args:
        G: a Gram matrix

    Format: coefficient

    Corresponds to algorithm 8 (LDL*) of Falcon's documentation,
    except it's in polynomial representation.
    """
    deg = len(G[0][0])
    dim = len(G)
    assert (dim == 2)
    assert (dim == len(G[0]))

    zero = [0] * deg
    one = [1] + [0] * (deg - 1)
    D00 = G[0][0][:]
    L10 = div(G[1][0], G[0][0])
    D11 = sub(G[1][1], mul(mul(L10, adj(L10)), G[0][0]))
    L = [[one, zero], [L10, one]]
    D = [[D00, zero], [zero, D11]]

    return [L, D]
Ejemplo n.º 4
0
def ldl(G):
    """Compute the LDL decomposition of G.

	Args:
		G: a Gram matrix

	Format: coefficient

	Corresponds to algorithm 14 (LDL) of Falcon's documentation,
	except it's in polynomial representation.
	"""
    deg = len(G[0][0])
    dim = len(G)
    L = [[[0 for k in range(deg)] for j in range(dim)] for i in range(dim)]
    D = [[[0 for k in range(deg)] for j in range(dim)] for i in range(dim)]
    for i in range(dim):
        L[i][i] = [1] + [0 for j in range(deg - 1)]
        D[i][i] = G[i][i]
        for j in range(i):
            L[i][j] = G[i][j]
            for k in range(j):
                L[i][j] = sub(L[i][j], mul(mul(L[i][k], adj(L[j][k])),
                                           D[k][k]))
            L[i][j] = div(L[i][j], D[j][j])
            D[i][i] = sub(D[i][i], mul(mul(L[i][j], adj(L[i][j])), D[j][j]))
    return [L, D]
Ejemplo n.º 5
0
def test_fft(n, iterations=10):
    """Test the FFT."""
    for i in range(iterations):
        f = [randint(-3, 4) for j in range(n)]
        g = [randint(-3, 4) for j in range(n)]
        h = mul(f, g)
        k = div(h, f)
        k = [int(round(elt)) for elt in k]
        if k != g:
            print("(f * g) / f =", k)
            print("g =", g)
            print("mismatch")
            return False
    return True
Ejemplo n.º 6
0
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