def coerce(self, other: object) -> CurvePolynomialElement: """ Attempts to coerce other into an element of the algebra. Parameters: other (object): Object to coerce. Returns: CurvePolynomialElement: Coerced element. """ if type(other) is CurvePolynomialElement: return other if type(other) is tuple: x_poly = other[0] y_poly = other[1] or self.poly_ring.zero() else: x_poly = other y_poly = self.poly_ring.zero() coerced = [] for poly in [x_poly, y_poly]: if type(poly) is list: coerced.append(Polynomial(poly, self.poly_ring.ring)) elif issubclass(type(poly), Polynomial): coerced.append(poly) elif type(poly) is int: coerced.append(Polynomial([poly], self.poly_ring.ring)) else: raise CoercionException('Coercion failed') return CurvePolynomialElement(*coerced, ring=self)
def __init__(self, p: int, n: int = 1, reducing_poly: Polynomial = None): """ Parameters: p (int): Prime. n (int): Exponent. reducing_poly (Polynomial): Polynomial to reduce the `PolynomialRing`. """ from samson.math.algebra.rings.integer_ring import ZZ assert is_prime(p) self.p = p self.n = n self.internal_ring = ZZ / ZZ(p) if not reducing_poly: if n == 1: reducing_poly = Polynomial([0, 1], self.internal_ring) else: for c in itertools.product(range(p), repeat=n): poly = Polynomial((1, *c)[::-1], self.internal_ring) if poly.is_irreducible(): reducing_poly = poly break self.reducing_poly = reducing_poly poly_ring = self.reducing_poly.ring self.internal_field = poly_ring / poly_ring(reducing_poly)
def coerce(self, other: object) -> Polynomial: """ Attempts to coerce other into an element of the algebra. Parameters: other (object): Object to coerce. Returns: Polynomial: Coerced element. """ # Handle grounds type_o = type(other) if type_o is int or hasattr(other, 'ring') and other.ring == self.ring: other = [other] type_o = type(other) if type_o is list or type_o is dict: return Polynomial(other, coeff_ring=self.ring, ring=self, symbol=self.symbol) elif type_o is Polynomial and other.ring == self: return other elif type_o is Symbol and other.var.ring == self: return other.var raise CoercionException('Coercion failed')
def __init__(self, seed: int, polynomial: Polynomial): """ Parameters: seed (int): Initial value. polynomial (Polynomial): Either a `Polynomial` or an integer that represents the polynomal. """ self.state = seed if type(polynomial) is Polynomial: polynomial = poly_to_int(polynomial) self.polynomial = polynomial self.mask = 1 self.wrap_around_mask = 2**polynomial.bit_length() - 1 self.state &= self.wrap_around_mask poly_mask = polynomial while poly_mask: if poly_mask & self.mask: poly_mask ^= self.mask if not poly_mask: break self.mask <<= 1
def one(self) -> CurvePolynomialElement: """ Returns: CurvePolynomialElement: '1' element of the algebra. """ return CurvePolynomialElement( Polynomial([self.poly_ring.ring(1)], self.poly_ring.ring), None, self)
def invert_poly(f_poly: Polynomial, R_poly: Polynomial, p: int) -> Polynomial: """ Inverts a polynomial `f_poly` over `R_poly` in GF(p). Parameters: f_poly (Polynomial): Polynomial to be inverted. R_poly (Polynomial): Polynomial to be inverted _over_. p (int): Integer modulus. Returns: Polynomial: Inverted polynomial. """ power_of_two = is_power_of_two(p) if is_prime(p) or power_of_two: if power_of_two: Z_p = ZZ / ZZ(2) else: Z_p = ZZ / ZZ(p) f_poly_p = Polynomial([(idx, Z_p[coeff]) for idx, coeff in f_poly.coeffs], Z_p) R_poly_p = Polynomial([(idx, Z_p[coeff]) for idx, coeff in R_poly.coeffs], Z_p) inv_poly = mod_inv(f_poly_p, R_poly_p) inv_poly = Polynomial([(idx, ZZ[int(coeff)]) for idx, coeff in inv_poly.coeffs], ZZ) if power_of_two: for _ in range(int(math.log(p, 2))): inv_poly = (2 * inv_poly) - (f_poly * (inv_poly**2)) inv_poly = (inv_poly % R_poly).trunc(p) else: raise Exception( f"Polynomial not invertible in Z_{p}. NTRU: p and q must be prime or power of two." ) return inv_poly
def gcm_to_poly(ad, ciphertext, tag): l = (len(ad) << (3 + 64)) | (len(ciphertext) << 3) ct_ints = [ chunk.int() for chunk in ciphertext.pad_congruent_right(16).chunk(16)[::-1] ] ad_ints = [ chunk.int() for chunk in ad.pad_congruent_right(16).chunk(16)[::-1] ] return Polynomial( [int_to_elem(coeff) for coeff in [tag.int(), l, *ct_ints, *ad_ints]])
def rand_poly(length: int, len_non_zeroes: int, neg_ones_mod: int = 0) -> Polynomial: """ Generates a random polynomial. Parameters: length (int): Desired length of polynomial. len_non_zeroes (int): Number of non-zero values in polynomial. neg_ones_mod (int): Modifier that reduces the number of -1 coefficients. Returns: Polynomial: Random polynomial. """ poly_arr = [0] * ((length - len_non_zeroes * 2) + neg_ones_mod) poly_arr += [1] * len_non_zeroes poly_arr += [-1] * (len_non_zeroes - neg_ones_mod) shuffle(poly_arr) return Polynomial(poly_arr, ZZ)
def one(self) -> Polynomial: """ Returns: Polynomial: '1' element of the algebra. """ return Polynomial([self.ring.one()], coeff_ring=self.ring, ring=self, symbol=self.symbol)