def encap(cls, pk, seed=None): """IND-CCA encapsulation sans compression or extra hash :param cls: Kyber class, inherit and change constants to change defaults :param pk: public key :param seed: seed used for random sampling if provided .. note :: Resembles Algorithm 4 of the Kyber paper. """ n = cls.n if seed is not None: set_random_seed(seed) m = random_vector(GF(2), n) m.set_immutable() set_random_seed(hash(m)) # NOTE: this is obviously not faithful K_ = random_vector(GF(2), n) K_.set_immutable() r = ZZ.random_element(0, 2**n-1) c = cls.enc(pk, m, r) K = hash((K_, c)) # NOTE: this obviously isn't a cryptographic hash return c, K
def gen_instance(n, q, h, alpha=None, m=None, seed=None, s=None): """ Generate FHE-style LWE instance :param n: dimension :param q: modulus :param alpha: noise rate (default: 8/q) :param h: hamming weight of the secret (default: 2/3n) :param m: number of samples (default: n) """ if seed is not None: set_random_seed(seed) q = next_prime(ceil(q) - 1, proof=False) if alpha is None: stddev = 3.2 else: stddev = alpha * q / sqrt(2 * pi) #RR = parent(alpha*q) #stddev = alpha*q/RR(sqrt(2*pi)) if m is None: m = n K = GF(q, proof=False) while 1: A = random_matrix(K, m, n) if A.rank() == n: break if s is not None: c = A * s else: if h is None: s = random_vector(ZZ, n, x=-1, y=1) else: S = [-1, 1] s = [S[randint(0, 1)] for i in range(h)] s += [0 for _ in range(n - h)] shuffle(s) s = vector(ZZ, s) c = A * s D = DiscreteGaussian(stddev) for i in range(m): c[i] += D() u = random_vector(K, m) print '(A, c) is n-dim LWE samples (with secret s) / (A, u) is uniform samples' return A, c, u, s
def decap(cls, sk, pk, c): """IND-CCA decapsulation :param cls: Kyber class, inherit and change constants to change defaults :param sk: secret key :param pk: public key :param c: ciphertext .. note :: Resembles Algorithm 5 of the Kyber paper. """ n = cls.n m = cls.dec(sk, c) m.set_immutable() set_random_seed(hash(m)) # NOTE: this is obviously not faithful K_ = random_vector(GF(2), n) K_.set_immutable() r = ZZ.random_element(0, 2**n-1) c_ = cls.enc(pk, m, r) if c == c_: return hash((K_, c)) # NOTE: this obviously isn't a cryptographic hash else: return hash(c) # NOTE ignoring z
def time_search_loop(p): y = random_vector(F, n) g = random_matrix(F, p, n).rows() scalars = [ [ Fstar[randint(0,q-2)] for i in range(p) ] for s in range(100) ] before = process_time() for m in scalars: e = y - sum(m[i]*g[i] for i in range(p)) return (process_time() - before)/100.
def time_search_loop(p): y = random_vector(F, n) g = random_matrix(F, p, n).rows() scalars = [[Fstar[randint(0, q - 2)] for i in range(p)] for s in range(100)] before = time.clock() for m in scalars: e = y - sum(m[i] * g[i] for i in range(p)) errs = e.hamming_weight() return (time.clock() - before) / 100.
def time_search_loop(p): y = random_vector(F, n) g = random_matrix(F, p, n).rows() scalars = [ [ Fstar[randint(0,q-2)] for i in range(p) ] for s in range(100) ] before = time.clock() for m in scalars: e = y - sum(m[i]*g[i] for i in range(p)) errs = e.hamming_weight() return (time.clock() - before)/100.
def test_skipper_cpa_enc(skipper=Skipper4, kyber=Kyber, t=128, l=None, exhaustive=False): if not exhaustive: for i in range(t): pk, sk = kyber.key_gen(seed=i) m0 = random_vector(GF(2), kyber.n) c = skipper.enc(kyber, pk, m0, seed=i, l=l) m1 = kyber.dec(sk, c) assert(m0 == m1) else: # exhaustive test for i in range(16): pk, sk = kyber.key_gen(seed=i) for m0 in FreeModule(GF(2), kyber.n): c = skipper.enc(kyber, pk, m0, seed=i, l=l) m1 = kyber.dec(sk, c) assert(m0 == m1)
def test_kyber_cpa(cls=Kyber, t=16): """ Test correctness of IND-CPA encryption/decryption. TESTS:: sage: test_kyber_cpa(Kyber) sage: test_kyber_cpa(MiniKyber) .. note :: An ``AssertionError`` if decrypted plaintext does not match original. """ for i in range(t): pk, sk = cls.key_gen(seed=i) m0 = random_vector(GF(2), cls.n) c = cls.enc(pk, m0, seed=i) m1 = cls.dec(sk, c) assert (m0 == m1)
def gen_fhe_instance(n, q, alpha=None, h=None, m=None, seed=None): """ Generate FHE-style LWE instance :param n: dimension :param q: modulus :param alpha: noise rate (default: 8/q) :param h: hamming weight of the secret (default: 2/3n) :param m: number of samples (default: n) """ if seed is not None: set_random_seed(seed) q = next_prime(ceil(q)-1, proof=False) if alpha is None: alpha = ZZ(8)/q n, alpha, q = preprocess_params(n, alpha, q) stddev = stddevf(alpha*q) if m is None: m = n K = GF(q, proof=False) A = random_matrix(K, m, n) if h is None: s = random_vector(ZZ, n, x=-1, y=1) else: S = [-1, 1] s = [S[randint(0, 1)] for i in range(h)] s += [0 for _ in range(n-h)] shuffle(s) s = vector(ZZ, s) c = A*s D = DiscreteGaussian(stddev) for i in range(m): c[i] += D() return A, c
def gen_fhe_instance(n, q, alpha=None, h=None, m=None, seed=None): """ Generate FHE-style LWE instance :param n: dimension :param q: modulus :param alpha: noise rate (default: 8/q) :param h: hamming weight of the secret (default: 2/3n) :param m: number of samples (default: n) """ if seed is not None: set_random_seed(seed) q = next_prime(ceil(q) - 1, proof=False) if alpha is None: alpha = ZZ(8) / q n, alpha, q = preprocess_params(n, alpha, q) stddev = stddevf(alpha * q) if m is None: m = n K = GF(q, proof=False) A = random_matrix(K, m, n) if h is None: s = random_vector(ZZ, n, x=-1, y=1) else: S = [-1, 1] s = [S[randint(0, 1)] for i in range(h)] s += [0 for _ in range(n - h)] shuffle(s) s = vector(ZZ, s) c = A * s D = DiscreteGaussian(stddev) for i in range(m): c[i] += D() return A, c
def get_totally_nonperp_vector(vectors, strategy='random'): """ Construct a vector 'w' such that w * v != 0 for all v in vectors. """ vectors = list(vectors) # We want to allow generators. if not vectors: return None n = len(vectors[0]) for k in (k for k in itertools.count() for _ in range((k + 1) * n)): if strategy == 'random': v = random_vector(n, x=-k, y=k + 2) elif strategy == 'moment': v = vector(ZZ, [1] + [k**i for i in range(1, n)]) else: raise TypeError('unknown strategy') if 0 in (v * w for w in vectors): continue return v
def keygen(self): return random_vector(F, self.k)
def random_vector_in_span(M, n = None): m = M.nrows() if n is None: n = M.ncols() L = random_vector(M.base_ring() ,n); return [ sum( [ L[j]*M[i,j] for j in range(n) ] ) for i in range(m) ]