Example #1
0
def common_modulus_attack(cpair, epair, n):
    """Common Modulus Attack

    Given 2 (or more) ciphertext of same plaintext with different e,
    we can decrypt the ciphertext using Extended Euclid Algorithm.
    """
    if len(cpair) < 2 or len(epair) < 2:
        logger.warn("cpair and epair must have 2 or more elements.")
        return None

    c1, c2 = cpair[0], cpair[1]
    _, s1, s2 = xgcd(epair[0], epair[1])
    if s1 < 0:
        s1 = -s1
        c1 = inverse(c1, n)
    elif s2 < 0:
        s2 = -s2
        c2 = inverse(c2, n)
    return (pow(c1, s1, n) * pow(c2, s2, n)) % n
Example #2
0
def add(P, Q):
    if Q == infty:
        return P
    if P == infty:
        return Q
    x_1, y_1 = P
    x_2, y_2 = Q
    if x_1 != x_2:
        m = (y_2 - y_1) * invert(x_2 - x_1, p) % p
        x_3 = (pow(m, 2, p) - x_1 - x_2) % p
        y_3 = (m * (x_1 - x_3) - y_1) % p
        return (x_3, y_3)
    elif y_1 != y_2:
        return infty
    elif y_1 != 0:
        m = (3 * pow(x_1, 2, p) - 3) * invert(2 * y_1, p) % p
        x_3 = (pow(m, 2, p) - 2 * x_1) % p
        y_3 = (m * (x_1 - x_3) - y_1) % p
        return (x_3, y_3)
    else:
        return infty
Example #3
0
def faulty(key, padding=None):
    """Faulty attack against crt-rsa, Boneh-DeMillo-Lipton
    sp = padding(m)**(d % p-1) % p
    sq' = padding(m)**(d % q-1) % q <--any error during computation
    s' = crt(sp, sq') % n <-- broken signature
    s = crt(sp, sq) % n <-- correct signature
    p = gcd(s'**e - padding(m), n)
    p = gcd(s - s', n)

    Args:
        key(RSAKey): with at least one broken signature (key.texts[no]['cipher']) and corresponding
                     plaintext (key.texts[no]['plain']), or valid and broken signature
        padding(None/function): function used before signing message

    Returns:
        NoneType/RSAKey: False on failure, recovered private key otherwise
    """
    log.debug("Check signature-message pairs")
    for pair in key.texts:
        if 'plain' in pair and 'cipher' in pair:
            signature = gmpy2.mpz(pair['cipher'])
            message = pair['plain']
            if padding:
                message = padding(message)
            p = gmpy2.gcd(gmpy2.pow(signature, key.e) - message, key.n)
            if p != 1 and p != key.n:
                log.info("Found p={}".format(p))
                new_key = RSAKey.construct(key.n,
                                           key.e,
                                           p=p,
                                           identifier=key.identifier +
                                           '-private')
                new_key.texts = key.texts[:]
                return new_key

    log.debug("Check for valid-invalid signatures")
    signatures = [tmp['cipher'] for tmp in key.texts if 'cipher' in tmp]
    for pair in itertools.combinations(signatures, 2):
        p = gmpy2.gcd(pair[0] - pair[1], key.n)
        if p != 1 and p != key.n:
            log.info("Found p={}".format(p))
            new_key = RSAKey.construct(key.n,
                                       key.e,
                                       p=p,
                                       identifier=key.identifier + '-private')
            new_key.texts = key.texts[:]
            return new_key
    return None
Example #4
0
def lsb_leak_attack(lsb_oracle, n, e, c):
    """RSA LSB Leak Attack

    Given a cryptosystem such that:
    - Using the "textbook" RSA (RSA without pading)
    - We can give any ciphertexts to decrypt and can get the least significant bit of decrypted plaintext.
    - We can try to decrypt ciphertexts without limit
    we can break the ciphertext with LSB Leak Attack(We should make name more cool)

    Usage:
        plain = padding_oracle(lsb_oracle, N, e, C)

    The function lsb_oracle must return LSB (1 or 0).
    """
    logger = getLogger(__name__)

    L = n.bit_length()
    t = L // 100
    left, right = 0, n
    c2 = c
    i = 0
    while right - left > 1:
        m = Fraction(left + right, 2)
        c2 = (c2 * pow(2, e, n)) % n
        oracle = lsb_oracle(c2)
        if oracle == 1:
            left = m
        elif oracle == 0:
            right = m
        else:
            raise ValueError("The function `lsb_oracle` must return 1 or 0")
        i += 1
        if i % t == 0:
            logger.info("LSB Leak Attack {}/{}".format(i, L))
        assert (i <= L)
    return int(ceil(left))
Example #5
0
def bleichenbacher_pkcs15(pkcs15_padding_oracle,
                          key,
                          ciphertext=None,
                          incremental_blinding=False,
                          **kwargs):
    """Given oracle that checks if ciphertext decrypts to some valid plaintext with PKCS1.5 padding
    we can decrypt whole ciphertext
    pkcs15_padding_oracle function must be implemented

    http://archiv.infsec.ethz.ch/education/fs08/secsem/bleichenbacher98.pdf
    https://www.dsi.unive.it/~focardi/RSA-padding-oracle/#eq5

    Note that this attack is very slow. Approximate number of main loop iterations == key's bit length

    Args:
        pkcs15_padding_oracle(callable)
        key(RSAKey): contains ciphertexts to decrypt
        incremental_blinding(bool): if ciphertext is not pkcs confirming we need to blind it.
                                    this may be done using random or incremental values

    Returns:
        dict: decrypted ciphertexts
        update key texts
    """
    def ceil(a, b):
        return a // b + (a % b > 0)

    def floor(a, b):
        return a // b

    def insert_interval(M, lb, ub):
        lo, hi = 0, len(M)
        while lo < hi:
            mid = (lo + hi) // 2
            if M[mid][0] < lb:
                lo = mid + 1
            else:
                hi = mid
        # insert it
        M.insert(lo, (lb, ub))

        # lb inside previous interval
        if lo > 0 and M[lo - 1][1] >= lb:
            lb = min(lb, M[lo - 1][0])
            M[lo] = (lb, M[lo][1])
            del M[lo - 1]
            lo -= 1

        # remove covered intervals
        i = lo + 1
        to_remove_first = i
        to_remove_last = lo
        while i < len(M) and M[i][0] <= ub:
            to_remove_last += 1
            i += 1
        if to_remove_last > lo:
            new_ub = max(ub, M[to_remove_last][1])
            M[lo] = (M[lo][0], new_ub)
            del M[to_remove_first:to_remove_last + 1]

    def update_intervals(M, B, s, n):
        # step 3
        M2 = []
        for a, b in M:
            r_min = ceil(a * s - 3 * B + 1, n)
            r_max = floor(b * s - 2 * B, n)
            for r in range(r_min, r_max + 1):
                lb = max(a, ceil(2 * B + r * n, s))
                ub = min(b, floor(3 * B - 1 + r * n, s))
                insert_interval(M2, lb, ub)
        del M[:]
        return M2

    def find_si(si_start, si_max=None):
        si_new = si_start
        while si_max is None or si_new < si_max:
            cipheri = (cipher_blinded * gmpy2.powmod(si_new, e, n)) % n
            if pkcs15_padding_oracle(cipheri, **kwargs):
                return si_new
            si_new += 1
        return None

    recovered = {}
    for text_no, cipher in _prepare_ciphertexts(key=key,
                                                ciphertext=ciphertext):
        log.info("Decrypting {}".format(cipher))

        n = key.n
        e = key.e
        B = gmpy2.pow(2, key.size - 16)

        # step 1
        log.debug('Blinding the ciphertext (to make it PKCS1.5 confirming)')
        i = 0
        si = 1
        cipher_blinded = cipher
        while not pkcs15_padding_oracle(cipher_blinded, **kwargs):
            # the paper says to draw it, but seems like incrementation sometimes run faster
            if incremental_blinding:
                si += 1
            else:
                si = random.randint(2, 1 << (key.size - 16))
            cipher_blinded = (cipher * gmpy2.powmod(si, e, n)) % n
        Mi = [(2 * B, 3 * B - 1)]
        s0 = si
        log.debug('Found s{}: {}'.format(i, hex(si)))
        i = 1

        plaintext = None
        while plaintext is None:
            log.debug('len(M{}): {}'.format(i - 1, len(Mi)))

            if i == 1:
                # step 2.a
                si = find_si(si_start=ceil(n, (3 * B)))
                log.debug('Found s{}: {}'.format(i, hex(si)))

            elif len(Mi) > 1:
                # step 2.b
                si = find_si(si_start=si + 1)

            elif len(Mi) == 1 and Mi[0][0] != Mi[0][1]:
                # step 2.c
                a, b = Mi[0]
                ri = ceil(2 * (b * si - 2 * B), n)
                si = None
                while si is None:
                    si_min = ceil(2 * B + ri * n, b)
                    si_max = ceil(3 * B + ri * n, a)
                    si = find_si(si_start=si_min, si_max=si_max)
                    ri += 1

            else:
                log.error(
                    "Hm, something strange happend. Len(M{}) = {}".format(
                        i, len(Mi)))
                return None

            # step 3
            Mi = update_intervals(Mi, B, si, n)

            # step 4
            if len(Mi) == 1 and Mi[0][0] == Mi[0][1]:
                plaintext = Mi[0][0]
                if s0 != 1:
                    plaintext = (plaintext * invmod(s0, n)) % n
                log.success("Interval narrowed to one value")
                log.success("plaintext = {}".format(hex(plaintext)))
            else:
                i += 1

        recovered[text_no] = plaintext
        key.texts[text_no]['plain'] = recovered[text_no]

    return recovered
Example #6
0
def is_on_curve(P):
    if P == infty:
        return True
    (x, y) = P
    return pow(y, 2, p) == (pow(x, 3, p) - 3 * x + b) % p
Example #7
0
from __future__ import division
import re
import imp
import math
import uuid
import gmpy2
import os.path
import traceback
import __builtin__
from glob import glob

from HALapi import get_main_dir, module_filter, HALcannotHandle

# Some version of gmpy2 doesn't have pow()
try:
    gmpy2.pow(2, 10)
except (AttributeError, TypeError):
    gmpy2.pow = lambda x, y: x**y

gmpy2.context().precision = 256

math_list = ['acos', 'asin', 'atan', 'atan2', 'ceil', 'cos', 'cosh', 'exp', 'floor', 'fmod', 'hypot', 'modf', 'pow', 'sin', 'sinh', 'sqrt', 'tan', 'tanh']
builtin_list = ['abs']

funcs = dict((name, getattr(gmpy2, name)) for name in math_list)
funcs.update(dict((name, getattr(__builtin__, name)) for name in builtin_list))
funcs.update(fact=math.factorial, mpfr=gmpy2.mpfr, arctan=gmpy2.atan, arccos=gmpy2.acos, arcsin=gmpy2.asin, log=gmpy2.log10,
             ln=gmpy2.log)
const = dict(e =gmpy2.mpfr('2.7182818284590452353602874713526624977572470936999595749669676277240766303535475945713821785251664274'),
             pi=gmpy2.mpfr('3.1415926535897932384626433832795028841971693993751058209749445923078164062862089986280348253421170679'))
filters = {