コード例 #1
0
def ldl_fft(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:     FFT
    """
    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 for j in range(deg)]
        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_fft(
                    L[i][j],
                    mul_fft(mul_fft(L[i][k], adj_fft(L[j][k])), D[k][k]))
            L[i][j] = div_fft(L[i][j], D[j][j])
            D[i][i] = sub_fft(
                D[i][i], mul_fft(mul_fft(L[i][j], adj_fft(L[i][j])), D[j][j]))
    return [L, D]
コード例 #2
0
ファイル: ffsampling.py プロジェクト: yad50968/falcon.py
def ldl_fft(G):
    """Compute the LDL decomposition of G.

	Args:
		G: a Gram matrix

	Format: FFT

	Corresponds to algorithm 14 (LDL) of Falcon's documentation.
	"""
    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 for j in range(deg)]
        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_fft(
                    L[i][j],
                    mul_fft(mul_fft(L[i][k], adj_fft(L[j][k])), D[k][k]))
            L[i][j] = div_fft(L[i][j], D[j][j])
            D[i][i] = sub_fft(
                D[i][i], mul_fft(mul_fft(L[i][j], adj_fft(L[i][j])), D[j][j]))
    return [L, D]
コード例 #3
0
ファイル: falcon.py プロジェクト: weicuivt/pqBitcoinTX
    def sample_preimage(self, point):
        """
        Sample a short vector s such that s[0] + s[1] * h = point.
        """
        [[a, b], [c, d]] = self.B0_fft

        # We compute a vector t_fft such that:
        #     (fft(point), fft(0)) * B0_fft = t_fft
        # Because fft(0) = 0 and the inverse of B has a very specific form,
        # we can do several optimizations.
        point_fft = fft(point)
        t0_fft = [(point_fft[i] * d[i]) / q for i in range(self.n)]
        t1_fft = [(-point_fft[i] * b[i]) / q for i in range(self.n)]
        t_fft = [t0_fft, t1_fft]

        # We now compute v such that:
        #     v = z * B0 for an integral vector z
        #     v is close to (point, 0)
        z_fft = ffsampling_fft(t_fft, self.T_fft, self.sigmin)
        v0_fft = add_fft(mul_fft(z_fft[0], a), mul_fft(z_fft[1], c))
        v1_fft = add_fft(mul_fft(z_fft[0], b), mul_fft(z_fft[1], d))
        v0 = [int(round(elt)) for elt in ifft(v0_fft)]
        v1 = [int(round(elt)) for elt in ifft(v1_fft)]

        # The difference s = (point, 0) - v is such that:
        #     s is short
        #     s[0] + s[1] * h = point
        s = [sub(point, v0), neg(v1)]
        return s
コード例 #4
0
def ldl_fft(G):
    """
    Compute the LDL decomposition of G. Only works with 2 * 2 matrices.

    Args:
        G: a Gram matrix

    Format: FFT

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

    zero = [0] * deg
    one = [1] * deg
    D00 = G[0][0][:]
    L10 = div_fft(G[1][0], G[0][0])
    D11 = sub_fft(G[1][1], mul_fft(mul_fft(L10, adj_fft(L10)), G[0][0]))
    L = [[one, zero], [L10, one]]
    D = [[D00, zero], [zero, D11]]

    return [L, D]
コード例 #5
0
def reduce(f, g, F, G):
    """
    Reduce (F, G) relatively to (f, g).

    This is done via Babai's reduction.
    (F, G) <-- (F, G) - k * (f, g), where k = round((F f* + G g*) / (f f* + g g*)).
    Corresponds to algorithm 7 (Reduce) of Falcon's documentation.
    """
    n = len(f)
    size = max(53, bitsize(min(f)), bitsize(max(f)), bitsize(min(g)),
               bitsize(max(g)))

    f_adjust = [elt >> (size - 53) for elt in f]
    g_adjust = [elt >> (size - 53) for elt in g]
    fa_fft = fft(f_adjust)
    ga_fft = fft(g_adjust)

    while (1):
        # Because we work in finite precision to reduce very large polynomials,
        # we may need to perform the reduction several times.
        Size = max(53, bitsize(min(F)), bitsize(max(F)), bitsize(min(G)),
                   bitsize(max(G)))
        if Size < size:
            break

        F_adjust = [elt >> (Size - 53) for elt in F]
        G_adjust = [elt >> (Size - 53) for elt in G]
        Fa_fft = fft(F_adjust)
        Ga_fft = fft(G_adjust)

        den_fft = add_fft(mul_fft(fa_fft, adj_fft(fa_fft)),
                          mul_fft(ga_fft, adj_fft(ga_fft)))
        num_fft = add_fft(mul_fft(Fa_fft, adj_fft(fa_fft)),
                          mul_fft(Ga_fft, adj_fft(ga_fft)))
        k_fft = div_fft(num_fft, den_fft)
        k = ifft(k_fft)
        k = [int(round(elt)) for elt in k]
        if all(elt == 0 for elt in k):
            break
        # The two next lines are the costliest operations in ntru_gen
        # (more than 75% of the total cost in dimension n = 1024).
        # There are at least two ways to make them faster:
        # - replace Karatsuba with Toom-Cook
        # - mutualized Karatsuba, see ia.cr/2020/268
        # For simplicity reasons, we didn't implement these optimisations here.
        fk = karamul(f, k)
        gk = karamul(g, k)
        for i in range(n):
            F[i] -= fk[i] << (Size - size)
            G[i] -= gk[i] << (Size - size)
    return F, G
コード例 #6
0
ファイル: ntrugen.py プロジェクト: tprest/ModFalcon
def reduce(f, g, F, G):
    """
    Reduce (F, G) relatively to (f, g).

    This is done via Babai's reduction.
    (F, G) <-- (F, G) - k * (f, g), where k = round((F f* + G g*) / (f f* + g g*)).
    Similar to algorithm Reduce of Falcon's documentation.

    Input:
    f, g, F, G      Four polynomials mod (x ** n + 1)

    Output:
    None            The inputs are reduced as detailed above.

    Format:         Coefficient
    """
    n = len(f)
    size = max(53, bitsize(min(f)), bitsize(max(f)), bitsize(min(g)), bitsize(max(g)))

    f_adjust = [elt >> (size - 53) for elt in f]
    g_adjust = [elt >> (size - 53) for elt in g]
    fa_fft = fft(f_adjust)
    ga_fft = fft(g_adjust)

    while(1):
        # Because we are working in finite precision to reduce very large polynomials,
        # we may need to perform the reduction several times.
        Size = max(53, bitsize(min(F)), bitsize(max(F)), bitsize(min(G)), bitsize(max(G)))
        if Size < size:
            break

        F_adjust = [elt >> (Size - 53) for elt in F]
        G_adjust = [elt >> (Size - 53) for elt in G]
        Fa_fft = fft(F_adjust)
        Ga_fft = fft(G_adjust)

        den_fft = add_fft(mul_fft(fa_fft, adj_fft(fa_fft)), mul_fft(ga_fft, adj_fft(ga_fft)))
        num_fft = add_fft(mul_fft(Fa_fft, adj_fft(fa_fft)), mul_fft(Ga_fft, adj_fft(ga_fft)))
        k_fft = div_fft(num_fft, den_fft)
        k = ifft(k_fft)
        k = [int(round(elt)) for elt in k]
        if all(elt == 0 for elt in k):
            break
        fk = karamul(f, k)
        gk = karamul(g, k)
        for i in range(n):
            F[i] -= fk[i] << (Size - size)
            G[i] -= gk[i] << (Size - size)
    return F, G
コード例 #7
0
 def sample_preimage_fft(self, point):
     """Sample preimage."""
     B = self.B0_fft
     c = point, [0] * self.n
     t_fft = self.get_coord_in_fft(c)
     z_fft = ffsampling_fft(t_fft, self.T_fft)
     v0_fft = add_fft(mul_fft(z_fft[0], B[0][0]),
                      mul_fft(z_fft[1], B[1][0]))
     v1_fft = add_fft(mul_fft(z_fft[0], B[0][1]),
                      mul_fft(z_fft[1], B[1][1]))
     v0 = [int(round(elt)) for elt in ifft(v0_fft)]
     v1 = [int(round(elt)) for elt in ifft(v1_fft)]
     v = v0, v1
     s = [sub(c[0], v[0]), sub(c[1], v[1])]
     return s
コード例 #8
0
def ffsampling_fft(t, T, sigmin, randombytes):
    """Compute the ffsampling of t, using T as auxilary information.

    Args:
        t: a vector
        T: a ldl decomposition tree

    Format: FFT

    Corresponds to algorithm 11 (ffSampling) of Falcon's documentation.
    """
    n = len(t[0]) * fft_ratio
    z = [0, 0]
    if (n > 1):
        l10, T0, T1 = T
        z[1] = merge_fft(
            ffsampling_fft(split_fft(t[1]), T1, sigmin, randombytes))
        t0b = add_fft(t[0], mul_fft(sub_fft(t[1], z[1]), l10))
        z[0] = merge_fft(
            ffsampling_fft(split_fft(t0b), T0, sigmin, randombytes))
        return z
    elif (n == 1):
        z[0] = [samplerz(t[0][0].real, T[0], sigmin, randombytes)]
        z[1] = [samplerz(t[1][0].real, T[0], sigmin, randombytes)]
        return z
コード例 #9
0
ファイル: ffsampling.py プロジェクト: tprest/ModFalcon
def ffnp_fft(t, T):
    """
    Compute the FFNP reduction of t, using T as auxilary information.

    Input:
    t           A vector
    T           The LDL decomposition tree of an (implicit) matrix G

    Output:
    z           An integer vector such that (t - z) * B is short

    Format:     FFT
    """
    m = len(t)
    n = len(t[0]) * fft_ratio
    z = [None] * m
    # General case
    if (n > 1):
        L = T[0]
        for i in range(m - 1, -1, -1):
            # t[i] is "corrected", taking into accounts the t[j], z[j] (j > i)
            tib = t[i][:]
            for j in range(m - 1, i, -1):
                tib = add_fft(tib, mul_fft(sub_fft(t[j], z[j]), L[j][i]))
            # Recursive call
            z[i] = merge_fft(ffnp_fft(split_fft(tib), T[i + 1]))
        return z
    # Bottom case: round each coefficient in parallel
    elif (n == 1):
        z[0] = [round(t[0][0].real)]
        z[1] = [round(t[1][0].real)]
        return z
コード例 #10
0
ファイル: falcon.py プロジェクト: dcmiddle/falcon.py
    def sample_preimage(self, point, seed=None):
        """
        Sample a short vector s such that s[0] + s[1] * h = point.
        """
        [[a, b], [c, d]] = self.B0_fft

        # We compute a vector t_fft such that:
        #     (fft(point), fft(0)) * B0_fft = t_fft
        # Because fft(0) = 0 and the inverse of B has a very specific form,
        # we can do several optimizations.
        point_fft = fft(point)
        t0_fft = [(point_fft[i] * d[i]) / q for i in range(self.n)]
        t1_fft = [(-point_fft[i] * b[i]) / q for i in range(self.n)]
        t_fft = [t0_fft, t1_fft]

        # We now compute v such that:
        #     v = z * B0 for an integral vector z
        #     v is close to (point, 0)
        if seed is None:
            # If no seed is defined, use urandom as the pseudo-random source.
            z_fft = ffsampling_fft(t_fft, self.T_fft, self.sigmin, urandom)
        else:
            # If a seed is defined, initialize a ChaCha20 PRG
            # that is used to generate pseudo-randomness.
            chacha_prng = ChaCha20(seed)
            z_fft = ffsampling_fft(t_fft, self.T_fft, self.sigmin,
                                   chacha_prng.randombytes)

        v0_fft = add_fft(mul_fft(z_fft[0], a), mul_fft(z_fft[1], c))
        v1_fft = add_fft(mul_fft(z_fft[0], b), mul_fft(z_fft[1], d))
        v0 = [int(round(elt)) for elt in ifft(v0_fft)]
        v1 = [int(round(elt)) for elt in ifft(v1_fft)]

        # The difference s = (point, 0) - v is such that:
        #     s is short
        #     s[0] + s[1] * h = point
        s = [sub(point, v0), neg(v1)]
        return s
コード例 #11
0
ファイル: module_falcon.py プロジェクト: tprest/ModFalcon
    def get_coord_in_fft(self, point):
        """
        Compute t such that t * B = point.
        Useful for the signing procedure.

        Input:
        self        The private key
        point       A row vector in the span generated by B

        Output:
        t           A vector such that t * B = point

        Format:
        Input       Coefficient
        Output      FFT
        """
        zero = [0] * self.d
        t = [fft(point)] + [zero[:] for _ in range(self.m)]
        t = [mul_fft(t[0], self.invB_fft[i][0]) for i in range(self.m + 1)]
        return t
コード例 #12
0
def gram_fft(B):
    """
    Compute the Gram matrix of B.

    Input:
    B           A matrix

    Output:
    G           The Gram matrix of B: G = B * (B*)

    Format:     FFT
    """
    rows = range(len(B))
    ncols = len(B[0])
    deg = len(B[0][0])
    G = [[[0 for coef in range(deg)] for j in rows] for i in rows]
    for i in rows:
        for j in rows:
            for k in range(ncols):
                G[i][j] = add_fft(G[i][j], mul_fft(B[i][k], adj_fft(B[j][k])))
    return G
コード例 #13
0
def ffnp_fft(t, T):
    """Compute the ffnp reduction of t, using T as auxilary information.

    Args:
        t: a vector
        T: a ldl decomposition tree

    Format: FFT
    """
    n = len(t[0]) * fft_ratio
    z = [0, 0]
    if (n > 1):
        l10, T0, T1 = T
        z[1] = merge_fft(ffnp_fft(split_fft(t[1]), T1))
        t0b = add_fft(t[0], mul_fft(sub_fft(t[1], z[1]), l10))
        z[0] = merge_fft(ffnp_fft(split_fft(t0b), T0))
        return z
    elif (n == 1):
        z[0] = [round(t[0][0].real)]
        z[1] = [round(t[1][0].real)]
        return z
コード例 #14
0
def vecmatmul_fft(t, B):
    """
    Compute the product t * B, where t is a vector and B is a square matrix.

    Input:
    t           A row vector
    B           A matrix

    Output:
    v           The row vector t * B

    Format:     FFT
    """
    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)]
    for j in range(ncols):
        for i in range(nrows):
            v[j] = add_fft(v[j], mul_fft(t[i], B[i][j]))
    return v
コード例 #15
0
ファイル: ffsampling.py プロジェクト: tprest/ModFalcon
def ffsampling_fft(t, T):
    """
    Compute the fast Fourier sampling of t, using T as auxilary information.

    Input:
    t           A vector
    T           The LDL decomposition tree of an (implicit) matrix G

    Output:
    z           An integer vector such that (t - z) * B is short

    Format:     FFT

    This algorithim is a randomized version of ffnp_fft,
    such that z * B is distributed as a spherical Gaussian
    centered around t * B.
    """
    m = len(t)
    n = len(t[0]) * fft_ratio
    z = [None] * m
    # General case
    if (n > 1):
        L = T[0]
        for i in range(m - 1, -1, -1):
            # t[i] is "corrected", taking into accounts the t[j], z[j] (j > i)
            tib = t[i][:]
            for j in range(m - 1, i, -1):
                tib = add_fft(tib, mul_fft(sub_fft(t[j], z[j]), L[j][i]))
            # Recursive call
            z[i] = merge_fft(ffsampling_fft(split_fft(tib), T[i + 1]))
        return z
    # Bottom case: round each coefficient in parallel
    elif (n == 1):
        z[0] = [sampler_z(T[0], t[0][0].real)]
        z[1] = [sampler_z(T[0], t[1][0].real)]
        return z