示例#1
0
def recover_key_from_nonce(msg, sig, k, q, hash=sha1):
    """Recovers a DSA private key (`x`) from a message-signature-pair given a known nonce

    Example:
    ```python
    >>> from bop.crypto_constructor import dsa
    >>> my_dsa = dsa()
    >>> msg = b"Hello kind stranger!"
    >>> sig = my_dsa.sign(msg)
    >>> leaked_nonce = sig._k
    >>> my_dsa.x == recover_key_from_nonce(msg, sig, leaked_nonce, my_dsa.q, hash=my_dsa.hash)
    True

    ```

    Arguments:
        msg {bytes or int} -- The message which was signed
        sig {tuple or Signature} -- The signature of the given message in form `(r, s)`
        k {int} -- The leaked nonce
        q {int} -- The public parameter q
    """
    if type(msg) != bytes:
        msg = i2b(msg)

    h = b2i(hash(msg))
    r, s = sig

    return ((s * k - h) * invmod(r, q)) % q
示例#2
0
def _dsa_sign(msg, p, q, g, x, k, hash=hash):
    if type(msg) != bytes:
        msg = i2b(msg)

    h = b2i(hash(msg))

    r = pow(g, k, p) % q
    s = (invmod(k, q) * (h + r * x)) % q

    return r, s
示例#3
0
def rsa(p=None, q=None, e=0x10001):
    if p is None or q is None:
        p = 0xfe8557c536be50a41a682a848731377fec2e7889a02b68697ba8a93dfd7aca45
        q = 0xcc1215873a81091163885ebd390c0e3ae18c2e5c961cdaa5daa563cbd459685d
        p = 11657510804603879190708332092675312014343829286377852544041511516023862575778860458573625524357360917135073366324924898682703984245741417367112631371911313
        q = 10089976740518582496356045969057602987024596248854888881417032743865018594042312715942618960983484996648974071339742122325713010480659249911953583913570703

    n = p * q

    pq = (p - 1) * (q - 1)
    d = invmod(e, pq)

    key_size = bit_length_exp2(n)
    return SimpleRSAInterface(d, n, e, key_size=key_size)
示例#4
0
    def verify(self, msg, sig):
        r, s = sig
        if r <= 0 or r >= self.q or s <= 0 or s >= self.q:
            return False

        if type(msg) != bytes:
            msg = i2b(msg)

        h = b2i(self.hash(msg))
        w = invmod(s, self.q)
        u1 = (w * h) % self.q
        u2 = (w * r) % self.q

        v = (pow(self.g, u1, self.p) * pow(self.y, u2, self.p)) % self.q
        return v == r
示例#5
0
文件: rsa.py 项目: liona24/bop
def broadcast_e3(messages, public_keys):
    """Perform a RSA broadcast attack for `e=3`

    For this to work the same plain text message has to be encrypted at least
    `e=3` times. This method will solve for the plain text using the chinese
    remainder theorem.

    This attack is well known as [Håstad's broadcast attack](https://en.wikipedia.org/wiki/Coppersmith%27s_attack#H.C3.A5stad.27s_broadcast_attack)

    Example:
    ```python
    >>> plain = 1818
    >>> n0 = 179 * 181
    >>> n1 = 191 * 193
    >>> n2 = 197 * 199
    >>> e = 3
    >>> msg0 = pow(plain, e, n0)
    >>> msg1 = pow(plain, e, n1)
    >>> msg2 = pow(plain, e, n2)
    >>> broadcast_e3([msg0, msg1, msg2], [n0, n1, n2])
    1818

    ```

    Arguments:
        messages {list of int} -- The cipher texts captured
        public_keys {list of int} -- The public key parameters `N` for each message

    Returns:
        int -- The decrypted plain text
    """

    e = 3
    public_keys = public_keys[:e]
    messages = messages[:e]

    assert (len(set(public_keys)) == len(public_keys))
    assert (len(public_keys) == e)
    assert (len(messages) == e)

    it = zip(messages, public_keys)
    x, N = next(it)
    for (c, n) in it:
        x += ((invmod(N, n) * (c - x)) % n) * N
        N *= n

    return cubic_root(x)
示例#6
0
    def sign(self, msg):
        if type(msg) != bytes:
            msg = i2b(msg)

        h = b2i(self.hash(msg))
        s = 0
        while s == 0:
            r = 0
            while r == 0:
                if self.generate_nonce is not None:
                    k = self.generate_nonce()
                else:
                    k = secrets.randbelow(self.q - 2) + 2

                r = pow(self.g, k, self.p) % self.q
            s = (invmod(k, self.q) * (h + r * self.x)) % self.q

        return self.Signature(r, s, k)
示例#7
0
文件: rsa.py 项目: liona24/bop
def recover_unpadded(oracle, ciphertext, e, n, s=2):
    """Recover the plaintext given an oracle which decrypts recently unique messages.

    This is useful if to bypass the "recently unique" property of the oracle.

    Example:
    ```python3
    >>> from bop.oracles.message_recovery import MessageRecoveryOracle as Oracle
    >>> oracle = Oracle()
    >>> cipher = oracle.encrypt(1337)
    >>> oracle(cipher)
    1337
    >>> # We cannot recover the plaintext again:
    >>> assert(oracle(cipher) is None)
    >>> # Or can we?
    >>> recover_unpadded(oracle, cipher, oracle.rsa.e, oracle.rsa.n)
    1337

    ```

    Arguments:
        oracle {callable} -- The oracle to use. It should decrypt some ciphertext with some fixed key.
            You are only allowed to decrypt a message once.
        ciphertext {int} -- [description]
        e {int} -- The RSA public key parameter e
        n {int} -- The RSA public key parameter n

    Keyword Arguments:
        s {int} -- A *random* parameter to use for key recovery in the range [2, n] (default: {2})

    Returns:
        int -- The recovered plaintext
    """
    new_ciphertext = (pow(s, e, n) * ciphertext) % n
    tmp = oracle(new_ciphertext)
    return (invmod(s, n) * tmp) % n
示例#8
0
文件: rsa.py 项目: liona24/bop
def decrypt_pkcs_padding_leak(oracle, msg, e, n):
    """Perform an adaptive chosen ciphertext against a weak RSA implementation leaking PKCS1v5 padding information.

    This attack was discovered by Daniel Bleichenbacher and is well described in the original [paper](http://archiv.infsec.ethz.ch/education/fs08/secsem/bleichenbacher98.pdf).

    Side note:\
    This attack is generally faster if `msg` is already properly padded.
    Depending on the keysize the expected running time may be somewhere between a few seconds (key size <= 512 bits) or somewhere around
    one minute for a 1024 public modulus.

    I once tested using a 2048 bit key which ran about an hour..\
    I am pretty sure this implementation is far from optimal, though I am not qualified to judge where I went wrong.


    Example:
    ```python3
    >>> from bop.oracles.padding import PaddingRSAOracle as Oracle
    >>> import bop.utils as utils
    >>> # We will choose smaller params to keep the running time down a little bit
    >>> p, q = utils.gen_rsa_key_params(256)
    >>> o = Oracle(p, q)
    >>> e, n = o.public_key()
    >>> plain = b"Hey! This message is save!"
    >>> # Optional: Pad the message:
    >>> # plain = utils.pad_pkcs1v5(plain, n)
    >>> msg = o.encrypt(plain)
    >>> decrypt_pkcs_padding_leak(o, msg, e, n)
    b'Hey! This message is save!'

    ```


    Args:
        oracle (callable): An oracle which leaks whether the decrypted message has a valid PKCS1v5 padding or not
            (i. e. the first byte is equal to 0 and the second byte is equal to 2)
        msg (bytes or int): The message to decrypt
        e (int): The public exponent
        n (int): The public modulus

    Returns:
        bytes or int: The decrypted message. Depending on the input type the output type is matched.
    """

    was_bytes = False
    if type(msg) != int:
        msg = b2i(msg)
        was_bytes = True

    k = bit_length_exp2(n)
    assert k > 16

    B = 1 << (k - 16)

    s0 = 1
    c0 = msg

    first = True

    M = {(2 * B, 3 * B - 1)}
    if not oracle(msg):
        # Blinding
        # ensure we have a valid padding to work with
        while True:
            s0 = secrets.randbelow(n - 1) + 1
            c0 = (msg * pow(s0, e, n)) % n
            if oracle(c0):
                break

    while True:
        if first:
            # Step 2a
            s = n // (3 * B)
            while oracle((c0 * pow(s, e, n)) % n) is False:
                s += 1
            first = False
        elif len(M) > 1:
            # Step 2b
            s_ = s + 1
            while not oracle((c0 * pow(s_, e, n)) % n):
                s_ += 1
            s = s_
        else:
            # Step 2c
            a, b = next(iter(M))
            found = False
            r = 2 * (b * s - 2 * B) // n
            while not found:
                s_ = (2 * B + r * n) // b

                s_max = (3 * B + r * n) // a

                while s_ <= s_max:
                    if oracle((c0 * pow(s_, e, n)) % n):
                        found = True
                        break
                    s_ += 1

                r += 1

            s = s_

        # Step 3
        M_ = set()
        for a, b in M:
            r_low = (a * s - 3 * B + 1) // n
            r_high = (b * s - 2 * B) // n
            for r in range(r_low, r_high + 1):
                # note the + s - 1 in order to ensure rounding to the next integer
                low = max(a, (2 * B + r * n + s - 1) // s)
                high = min(b, (3 * B - 1 + r * n) // s)

                if low <= high and (low, high) not in M_:
                    M_.add((low, high))

        M = M_

        # Step 4
        if len(M) == 1:
            a, b = next(iter(M))
            if a == b:
                plain = (a * invmod(s0, n)) % n
                if was_bytes:
                    return i2b(plain)
                return plain
示例#9
0
def recover_key_from_duplicate_nonce(gen, public_parameters, hash=sha1):
    """Attempt to recover a DSA private key (`x`) from a stream of messages by searching for a duplicate usage of a nonce.

    Note that this is a pretty naive implementation running in O(n^2) where n is the number of messages (signatures) checked.

    Example:
    ```python
    >>> from bop.crypto_constructor import dsa
    >>> import secrets
    >>>
    >>> # Setup a weak implementation
    >>> my_dsa = dsa()
    >>> def weak_nonce_generator():
    ...     return secrets.choice(range(2, 11))
    ...
    >>> my_dsa.generate_nonce = weak_nonce_generator
    >>> def message_src():
    ...     while True:
    ...         msg = secrets.token_bytes(16)
    ...         yield msg, my_dsa.sign(msg)
    ...
    >>> x, leaked_nonce = recover_key_from_duplicate_nonce(message_src(), my_dsa.public_parameters, hash=my_dsa.hash)
    >>> my_dsa.x == x
    True

    ```

    Arguments:
        gen {generator} -- A generator yielding message-signature-pairs `(msg, signature)`.
            It will be consumed until a duplicate nonce is found or it is empty.
        public_parameters {tuple} -- The public parameters of the DSA algorithm used

    Keyword Arguments:
        hash {callable} -- The hash function to use (default: {sha1})

    Returns:
        (int, int) -- (x, leaked_nonce), i.e. the private key and the leaked nonce. Or (None, None) if no duplicate was found.
    """

    p, q, g, y = public_parameters

    first_msg, first_sig = next(gen)
    if type(first_msg) != bytes:
        first_msg = i2b(first_msg)
    bases = [(first_msg, first_sig)]

    # we do a simple exhaustive search for a duplicated nonce
    for msg1, sig1 in gen:
        if type(msg1) != bytes:
            msg1 = i2b(msg1)
        h1 = b2i(hash(msg1))
        r1, s1 = sig1

        for msg2, sig2 in bases:
            h2 = b2i(hash(msg2))
            r2, s2 = sig2
            # Assume k was equal
            k = ((h2 - h1) * invmod(s2 - s1, q)) % q

            # Check if our assumption holds
            x = recover_key_from_nonce(msg1, sig1, k, q, hash=hash)

            if _dsa_sign(msg1, p, q, g, x, k, hash=hash) == (r1, s1):
                return x, k

        bases.append((msg1, sig1))

    return None, None