Example #1
0
 def __init__(self,
              key: bytes = None,
              H: FunctionType = SHA256().hash,
              q: int = DiffieHellman.MODP_2048):
     """
     Parameters:
         key (bytes): Bytes-like object shared by both parties to authenticate each other.
         H    (func): Cryptographic hash function. Takes in bytes and returns the hash digest.
         q     (int): Modulus.
     """
     Primitive.__init__(self)
     self.key = key or Bytes(random_int_between(1, q))
     self.q = q
     self.A = random_int_between(1, q)
     self.a = random_int_between(1, q)
     self.H = H
Example #2
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)
Example #3
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
Example #4
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)
Example #5
0
 def __init__(self,
              G: WeierstrassCurve,
              hash_obj: object = SHA256(),
              d: int = None):
     """
     Parameters:
         G (WeierstrassCurve): Generator point for a curve.
         hash_obj    (object): Instantiated object with compatible hash interface.
         d              (int): (Optional) Private key.
     """
     Primitive.__init__(self)
     self.G = G
     self.q = self.G.curve.q
     self.d = Bytes.wrap(d).int() if d else random_int_between(1, self.q)
     self.Q = self.d * self.G
     self.hash_obj = hash_obj
Example #6
0
    def random(self, size: int = None) -> object:
        """
        Generate a random element.

        Parameters:
            size (int): The ring-specific 'size' of the element.
    
        Returns:
            MontgomeryPoint: Random element of the algebra.
        """
        while True:
            try:
                return self.clamp_to_curve(
                    random_int_between(1, size or self.p))
            except AssertionError:
                pass
Example #7
0
    def __init__(self,
                 g: int = 2,
                 p: int = DiffieHellman.MODP_2048,
                 key: int = None):
        """
        Parameters:
            g   (int): Generator.
            p   (int): Prime modulus.
            key (int): Key.
        """
        Primitive.__init__(self)

        self.key = key or random_int_between(1, p)
        self.g = g
        self.p = p
        self.pub = pow(self.g, self.key, self.p)
Example #8
0
 def __init__(self,
              g: int = 2,
              p: int = MODP_2048,
              q: int = None,
              key: int = None):
     """
     Parameters:
         key (int): Secret key.
         g   (int): Exponent base.
         p   (int): Modulus.
         q   (int): Order.
     """
     Primitive.__init__(self)
     self.key = key or random_int_between(2, q or p)
     self.g = g
     self.p = p
     self.q = q
Example #9
0
    def random(self, size: WeierstrassPoint = None) -> WeierstrassPoint:
        """
        Generate a random element.

        Parameters:
            size (int): The ring-specific 'size' of the element.
    
        Returns:
            WeierstrassPoint: Random element of the algebra.
        """
        while True:
            try:
                return self.recover_point_from_x(
                    random_int_between(1,
                                       int(size.x) if size else self.p))
            except AssertionError:
                pass
Example #10
0
    def __init__(self, hash_obj: object=SHA256(), p: int=None, q: int=None, g: int=None, x: int=None, L: int=2048, N: int=256):
        """
        Parameters:
            hash_obj (object): Instantiated object with compatible hash interface.
            p           (int): (Optional) Prime modulus.
            q           (int): (Optional) Prime modulus.
            g           (int): (Optional) Generator.
            x           (int): (Optional) Private key.
            L           (int): (Optional) Bit length of `p`.
            N           (int): (Optional) Bit length of `q`.
        """
        Primitive.__init__(self)
        # Parameter generation
        # https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.186-4.pdf
        if not q:
            q = find_prime(N)

            # Start somewhere in 2**(L-1); ensure it's even
            i = Bytes.random((L-1) // 8).int() // 2 * 2

            # Construct the base as an even multiple of `q`
            base = 2**(L-1) // (2*q) * 2
            while not is_prime((base + i) * q + 1):
                i += 2

            p = (base + i) * q + 1
            assert (p-1) % q == 0

            # Construct `g`
            while True:
                h = Bytes.random(N // 8).int() % (p-1)
                g = pow(h, (p-1) // q, p)

                if h > 1 and h < (p-1) and g > 1:
                    break

        self.p = p
        self.q = q
        self.g = g

        self.x = x or random_int_between(1, self.q)
        self.y = pow(self.g, self.x, self.p)
        self.hash_obj = hash_obj
Example #11
0
    def encrypt(self, plaintext: bytes, k: int = None) -> (int, int):
        """
        Encrypts `plaintext`.

        Parameters:
            plaintext (bytes): Message to encrypt.
            k           (int): (Optional) Ephemeral key.
        
        Returns:
            (int, int): Formatted as (ephemeral key, ciphertext).
        
        References:
            https://en.wikipedia.org/wiki/ElGamal_encryption
        """
        K_e = k or random_int_between(1, self.p)
        c_1 = pow(self.g, K_e, self.p)
        s = pow(self.pub, K_e, self.p)
        plaintext = Bytes.wrap(plaintext)
        return c_1, (s * plaintext.int()) % self.p
Example #12
0
    def __init__(self,
                 d: int = None,
                 pub: int = None,
                 base: int = None,
                 curve: MontgomeryCurve = Curve25519):
        """
        Parameters:
            d                 (int): Secret key that will be clamped to the curve.
            base              (int): Base multiplier used in generating the challenge.
            curve (MontgomeryCurve): The curve used.
        """
        Primitive.__init__(self)
        self.d = Bytes.wrap(d or random_int_between(1, curve.ring.order)).int()
        self.curve = curve
        self.key = curve.clamp_to_curve(self.d)
        self.base = base or curve.U

        self.pub = pub

        if not pub:
            self.recompute_public()