Ejemplo n.º 1
0
    def sign(self, message: bytes, k: int = None) -> (int, int):
        """
        Signs a `message`.

        Parameters:
            message (bytes): Message to sign.
            k         (int): (Optional) Ephemeral key.
        
        Returns:
            (int, int): Signature formatted as (r, s).
        """
        r = 0
        s = 0

        while s == 0 or r == 0:
            k = k or random_int_between(1, self.q)
            inv_k = mod_inv(k, self.q)

            z = self.hash_obj.hash(message).int()
            z >>= max(self.hash_obj.digest_size * 8 - self.q.bit_length(), 0)

            r = int((k * self.G).x) % self.q
            s = (inv_k * (z + self.d * r)) % self.q

        return (r, s)
Ejemplo n.º 2
0
def prepare_fft(v1, v2):
    v1_len = len(v1)
    v2_len = len(v2)

    # Pad vectors for radix-2 FFT
    max_len = max(v1_len, v2_len)
    vec_len = 2**math.ceil(math.log(max_len * 2, 2))
    v1 = v1 + [0] * (vec_len - v1_len)
    v2 = v2 + [0] * (vec_len - v2_len)

    # Prepare NTT twiddle factors
    root, modulus = generate_ntt_params(v1, v2)
    twiddle_factors = []
    inv_twid_factors = []

    twiddle = 1
    inv_twid = 1
    inv_root = mod_inv(root, modulus)

    for _ in range(vec_len // 2):
        twiddle_factors.append(twiddle)
        twiddle = (twiddle * root) % modulus

        inv_twid_factors.append(inv_twid)
        inv_twid = (inv_twid * inv_root) % modulus

    return (v1, v2), root, modulus, twiddle_factors, inv_twid_factors
Ejemplo n.º 3
0
    def decrypt(self, sums: list) -> Bytes:
        """
        Decrypts `sums` back into plaintext.

        Parameters:
            sums (list): List of ciphertext sums.
        
        Returns:
            Bytes: Decrypted plaintext.
        """
        r_inv = mod_inv(self.r, self.q)
        inv_sums = [(byte_sum * r_inv) % self.q for byte_sum in sums]
        plaintext = Bytes(b'')

        for inv_sum in inv_sums:
            curr = inv_sum
            bin_string = ''

            for i in range(len(self.pub) - 1, -1, -1):
                if self.priv[i] <= curr:
                    curr -= self.priv[i]
                    bin_string += '1'
                else:
                    bin_string += '0'

            plaintext += int.to_bytes(int(bin_string[::-1], 2),
                                      len(self.pub) // 8, 'big')

        return plaintext
Ejemplo n.º 4
0
    def execute(self, public_key: int, max_factor_size: int = 2**16) -> int:
        """
        Executes the attack.

        Parameters:
            public_key      (int): Diffie-Hellman public key to crack.
            max_factor_size (int): Max factor size to prevent attempting to factor forever.
        
        Returns:
            int: Private key.
        """
        # Factor as much as we can
        factors = [
            r for r in factorint((self.p - 1) // self.order,
                                 use_rho=False,
                                 limit=max_factor_size) if r < max_factor_size
        ]
        log.debug(f'Found factors: {factors}')

        residues = []

        # Request residues from crafted public keys
        for factor in RUNTIME.report_progress(
                factors, desc='Sending malicious public keys', unit='factor'):
            h = 1
            while h == 1:
                h = pow(random_int_between(1, self.p), (self.p - 1) // factor,
                        self.p)

            residue = self.oracle.request(h, factor)
            residues.append((residue, factor))

        # Build partials using CRT
        n, r = crt(residues)

        # Oh, I guess we already found it...
        if r > self.order:
            return n

        g_prime = pow(self.g, r, self.p)
        y_prime = (public_key *
                   mod_inv(pow(self.g, n, self.p), self.p)) % self.p

        log.info(
            f'Recovered {"%.2f"%math.log(reduce(int.__mul__, factors, 1), 2)}/{"%.2f"%math.log(self.order, 2)} bits'
        )
        log.info(f'Found relation: x = {n} + m*{r}')
        log.debug(f"g' = {g_prime}")
        log.debug(f"y' = {y_prime}")
        log.info('Attempting to catch a kangaroo...')

        # Probabilistically solve DLP
        R = (ZZ / ZZ(self.p)).mul_group()
        m = pollards_kangaroo(R(g_prime),
                              R(y_prime),
                              a=0,
                              b=(self.order - 1) // r)
        return n + m * r
Ejemplo n.º 5
0
def fft_op(v1, v2, operation: FFTOp = FFTOp.CONVOLVE):
    (v1, v2), _root, modulus, twiddle_factors, inv_twid_factors = prepare_fft(
        v1, v2)
    v1_t = fft(v1, modulus, twiddle_factors)
    v2_t = fft(v2, modulus, twiddle_factors)

    v3_t = [operation(a, b, modulus) for a, b in zip(v1_t, v2_t)]
    v3 = fft(v3_t, modulus, inv_twid_factors)
    scaler = mod_inv(len(v1), modulus)
    return [val * scaler % modulus for val in v3]
Ejemplo n.º 6
0
    def get_challenge(self) -> (int, int):
        """
        Gets the challenge.

        Returns:
            (int, int): The challenge.
        """
        sA = self.a + self.A

        PE = self.H(self.key).int()
        eA = mod_inv(pow(PE, self.A, self.q), self.q)
        return pow(PE, sA, self.q), eA
Ejemplo n.º 7
0
    def assert_correct(self, c_b: int) -> bool:
        """
        Processes the final challenge and asserts its correctness.

        Parameters:
            c_b (int): Peer's final challenge.
        
        Returns:
            bool: Whether or not the challenge was correct.
        """
        c = pow(c_b, self.exp2, self.p)
        return c == ((self.P * mod_inv(self.P_b, self.p)) % self.p)
Ejemplo n.º 8
0
    def __init__(self, p: int=None, q: int=None):
        """
        Parameters:
            p (int): Large prime.
            q (int): Large prime.
        """
        self.p = p or find_prime(512)
        self.q = q or find_prime(512)
        self.n = self.p * self.q

        self.phi = (self.p - 1) * (self.q - 1)
        self.g = self.n + 1
        self.priv = mod_inv(self.phi, self.n)
Ejemplo n.º 9
0
    def decrypt(self, key_and_ciphertext: (int, int)) -> Bytes:
        """
        Decrypts `key_and_ciphertext`.

        Parameters:
            key_and_ciphertext ((int, int)): Ephemeral key and ciphertext.
        
        Returns:
            Bytes: Plaintext.
        """
        c_1, ciphertext = key_and_ciphertext
        s = pow(c_1, self.key, self.p)
        return Bytes((mod_inv(s, self.p) * ciphertext) % self.p)
Ejemplo n.º 10
0
    def test_import_openssh(self):
        for key, passphrase in [
                TEST_OPENSSH0, TEST_OPENSSH1, TEST_OPENSSH2, TEST_OPENSSH3
        ]:
            if passphrase:
                with self.assertRaises(ValueError):
                    RSA.import_key(key)

            rsa = RSA.import_key(key, passphrase=passphrase)
            self.assertEqual(rsa.p * rsa.q, rsa.n)
            self.assertEqual(rsa.alt_d,
                             mod_inv(rsa.e, (rsa.p - 1) * (rsa.q - 1)))
            self.assertTrue(is_prime(rsa.p))
            self.assertTrue(is_prime(rsa.q))
Ejemplo n.º 11
0
    def derive_x_from_k(self, message: bytes, k: int, sig: (int, int)) -> int:
        """
        Derives `x` from a known `k`.

        Parameters:
            message  (bytes): Message.
            k          (int): `k` used in `message`'s signature.
            sig ((int, int)): Signature of `message`.
        
        Returns:
            int: Derived `x`.
        """
        (r, s) = sig
        return ((s * k) - self.hash_obj.hash(message).int()) * mod_inv(r, self.q) % self.q
Ejemplo n.º 12
0
    def crack(states: list,
              multiplier: int = None,
              increment: int = None,
              modulus: int = None,
              sanity_check: bool = True):
        """
        Given a few full states (probably under ten) and any (or even none) of the parameters of an LCG, returns a replica LCG.

        Parameters:
            states       (list): List of full-state outputs (in order).
            multiplier    (int): (Optional) The LCG's multiplier.
            increment     (int): (Optional) The LCG's increment.
            modulus       (int): (Optional) The LCG's modulus.
            sanity_check (bool): Whether to tests the generated LCG against the provided states.
        
        Returns:
            LCG: Replica LCG that predicts all future outputs of the original.
        
        References:
            https://tailcall.net/blog/cracking-randomness-lcgs/
        """
        if not modulus:
            diffs = [
                state1 - state0 for state0, state1 in zip(states, states[1:])
            ]
            congruences = [
                t2 * t0 - t1 * t1
                for t0, t1, t2 in zip(diffs, diffs[1:], diffs[2:])
            ]
            modulus = abs(functools.reduce(gcd, congruences))

        if not multiplier:
            multiplier = (states[2] - states[1]) * mod_inv(
                states[1] - states[0], modulus) % modulus

        if not increment:
            increment = (states[1] - states[0] * multiplier) % modulus

        # Sanity test
        lcg = LCG(states[0], multiplier, increment, modulus)
        num_tests = min(3, len(states) - 1)

        if sanity_check and [lcg.generate() for _ in range(num_tests)
                             ] != states[1:1 + num_tests]:
            raise RuntimeError(
                "Generated LCG does not match 'states'. Are you sure this came from an untruncated LCG?"
            )

        return LCG(states[-1], multiplier, increment, modulus)
Ejemplo n.º 13
0
    def sign(self, message: bytes, k: int=None) -> (int, int):
        """
        Signs a `message`.

        Parameters:
            message (bytes): Message to sign.
            k         (int): (Optional) Ephemeral key.
        
        Returns:
            (int, int): Signature formatted as (r, s).
        """
        k = k or random_int_between(1, self.q)
        inv_k = mod_inv(k, self.q)
        r = pow(self.g, k, self.p) % self.q
        s = (inv_k * (self.hash_obj.hash(message).int() + self.x * r)) % self.q
        return (r, s)
Ejemplo n.º 14
0
    def get_final_challenge(self, challenge: (int, int)) -> int:
        """
        Uses its peer's P and Q values to generate the final challenge.

        Parameters:
            challenge ((int, int)): P and Q values of peer's challenge.
        
        Returns:
            int: The final challenge.
        """
        self.P_b, Q_b = challenge
        if self.validate:
            assert self.P != self.P_b
            assert self.Q != Q_b

        return pow(Q_b * mod_inv(self.Q, self.p), self.exp2, self.p)
Ejemplo n.º 15
0
    def verify(self, message: bytes, sig: (int, int)) -> bool:
        """
        Verifies a `message` against a `sig`.

        Parameters:
            message  (bytes): Message.
            sig ((int, int)): Signature of `message`.
        
        Returns:
            bool: Whether the signature is valid or not.
        """
        (r, s) = sig
        w = mod_inv(s, self.q)
        u_1 = (self.hash_obj.hash(message).int() * w) % self.q
        u_2 = (r * w) % self.q
        v = (pow(self.g, u_1, self.p) * pow(self.y, u_2, self.p) % self.p) % self.q
        return v == r
Ejemplo n.º 16
0
    def generate_backdoor(
            curve: WeierstrassCurve
    ) -> (WeierstrassPoint, WeierstrassPoint, int):
        """
        Generates backdoored parameters.

        Parameters:
            curve (WeierstrassCurve): Curve to use.
        
        Returns:
            (WeierstrassPoint, WeierstrassPoint, int): Result formatted as (P, backdoored Q, backdoor d)
        """
        P = curve.G
        d = random.randint(2, curve.q)
        e = mod_inv(d, curve.q)
        Q = e * P

        return P, Q, d
Ejemplo n.º 17
0
    def derive_k_from_sigs(self, msg_a: bytes, sig_a: (int, int), msg_b: bytes, sig_b: (int, int)) -> int:
        """
        Derives `k` from signatures that share an `r` value.

        Parameters:
            msg_a      (bytes): Message A.
            msg_b      (bytes): Message B.
            sig_a ((int, int)): Signature of `msg_a`.
            sig_b ((int, int)): Signature of `msg_b`.

        Returns:
            int: Derived `k`.
        """
        (r_a, s_a) = sig_a
        (r_b, s_b) = sig_b
        assert r_a == r_b

        s = (s_a - s_b) % self.q
        m = (self.hash_obj.hash(msg_a).int() - self.hash_obj.hash(msg_b).int()) % self.q
        return mod_inv(s, self.q) * m % self.q
Ejemplo n.º 18
0
    def verify(self, message: bytes, sig: (int, int)) -> bool:
        """
        Verifies a `message` against a `sig`.

        Parameters:
            message  (bytes): Message.
            sig ((int, int)): Signature of `message`.
        
        Returns:
            bool: Whether the signature is valid or not.
        """
        (r, s) = sig
        w = mod_inv(s, self.q)

        z = self.hash_obj.hash(message).int()
        z >>= max(self.hash_obj.digest_size * 8 - self.q.bit_length(), 0)

        u_1 = (z * w) % self.q
        u_2 = (r * w) % self.q
        v = u_1 * self.G + u_2 * self.Q
        return v.x == r
Ejemplo n.º 19
0
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
Ejemplo n.º 20
0
    def __init__(self,
                 a: int,
                 b: int,
                 alphabet: str = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ ,.'):
        """
        Parameters:
            a        (int): Multiplier.
            b        (int): Linear shift.
            alphabet (str): Alphabet (in order) to encrypt over. Input must also be in this alphabet.
        """
        self.a = a
        self.b = b
        self.alphabet = alphabet

        self.char_map = {}
        for i in range(len(alphabet)):
            self.char_map[alphabet[i]] = alphabet[(a * i + b) % len(alphabet)]

        inv_a = mod_inv(a, len(alphabet))
        self.inv_char_map = {}
        for i in range(len(alphabet)):
            self.inv_char_map[alphabet[i]] = alphabet[(inv_a * (i - b)) %
                                                      len(alphabet)]
Ejemplo n.º 21
0
    def encode(rsa_key: object, **kwargs):
        user = kwargs.get('user')
        if user and type(user) is str:
            user = user.encode('utf-8')

        public_key = RSAPublicKey('public_key', rsa_key.n, rsa_key.e)
        private_key = RSAPrivateKey('private_key',
                                    check_bytes=None,
                                    n=rsa_key.n,
                                    e=rsa_key.e,
                                    d=rsa_key.alt_d,
                                    q_mod_p=mod_inv(rsa_key.q, rsa_key.p),
                                    p=rsa_key.p,
                                    q=rsa_key.q,
                                    host=user or b'nohost@localhost')

        encoded = generate_openssh_private_key(public_key, private_key,
                                               kwargs.get('encode_pem'),
                                               kwargs.get('marker'),
                                               kwargs.get('encryption'),
                                               kwargs.get('iv'),
                                               kwargs.get('passphrase'))

        return encoded
Ejemplo n.º 22
0
class FFTOp(Enum):
    CONVOLVE = lambda a, b, mod: a * b
    DECONVOLVE = lambda a, b, mod: a * mod_inv(b, mod)
Ejemplo n.º 23
0
 def __invert__(self) -> object:
     return QuotientElement(mod_inv(self.val, self.ring.quotient),
                            self.ring)
Ejemplo n.º 24
0
    def __init__(self,
                 bits: int = None,
                 p: int = None,
                 q: int = None,
                 e: int = 65537,
                 n: int = None):
        """
        Parameters:
            bits (int): Number of bits for strength and capacity.
            p    (int): Secret prime modulus.
            q    (int): Secret prime modulus.
            e    (int): Public exponent.
            n    (int): Public modulus.
        """
        Primitive.__init__(self)

        self.e = e
        phi = 0

        if p and q:
            phi = lcm(p - 1, q - 1)
            self.n = p * q

            if gcd(self.e, phi) != 1:
                raise Exception("Invalid 'p' and 'q': GCD(e, phi) != 1")

            bits = p.bit_length() + q.bit_length()

        elif n:
            self.n = n

        else:
            next_p = p
            next_q = q

            # Take into account the bits needed to complete `bits` if `p` or `q` are already defined
            if p:
                q_bits = bits - p.bit_length()
            else:
                q_bits = bits // 2

            if q:
                p_bits = bits - q.bit_length()
            else:
                p_bits = bits // 2

            # Find the primes
            while gcd(self.e, phi) != 1 or next_p == next_q:
                if not p:
                    next_p = find_prime(p_bits)

                if not q:
                    next_q = find_prime(q_bits)

                phi = lcm(next_p - 1, next_q - 1)

            p = next_p
            q = next_q
            self.n = p * q

        self.p = p
        self.q = q
        self.phi = phi

        self.bits = bits

        if self.p and self.q:
            self.d = mod_inv(self.e, phi)
            self.alt_d = mod_inv(self.e, (self.p - 1) * (self.q - 1))

            self.dP = self.d % (self.p - 1)
            self.dQ = self.d % (self.q - 1)
            self.Qi = mod_inv(self.q, self.p)
        else:
            self.d = None
            self.alt_d = None

        self.pub = (self.e, self.n)
        self.priv = (self.d, self.n)
Ejemplo n.º 25
0
def ntt_inv(vec_t: list, root: int, modulus: int):
    vec = ntt(vec_t, mod_inv(root, modulus), modulus)
    scaler = mod_inv(len(vec_t), modulus)
    return [val * scaler % modulus for val in vec]