def main():
    # NOTE BEFORE STARTING: Do not be fooled! Signatures are created using
    # private keys, notwithstanding the (perhaps intentionally misleading?)
    # wording in this and the previous challenge that might imply otherwise.

    lines = loader("44.txt", lambda l: l.rstrip("\n").split(": "))

    msgs = []

    for i in range(0, len(lines), 4):
        block = lines[i:i + 4]

        # After the rstrip() and split() above, a block looks like:
        #
        #   [["msg", "Listen for me, you better listen for me now. "],
        #    ["s", "1267396447369736888040262262183731677867615804316"],
        #    ["r", "1105520928110492191417703162650245113664610474875"],
        #    ["m", "a4db3de27e2db3e5ef085ced2bced91b82e0df19"]]
        #
        # We have a message, the components of a DSA signature, and the SHA1
        # hash of the message. Everything here's a `str` at the moment, so
        # we'll transform the values.
        #
        # There's an error in "m" for one of the blocks in the challenge data,
        # but we can just calculate the hash ourselves.
        block[0][1] = block[0][1].encode()
        block[1][1] = int(block[1][1])
        block[2][1] = int(block[2][1])
        block[3][1] = from_bytes(SHA1(block[0][1]).digest())

        msgs.append({data[0]: data[1] for data in block})

    print(f"Loaded {len(msgs)} DSA-signed messages.")
    print("Recovering private key from the first repeated nonce we detect.")

    for msg1, msg2 in combinations(msgs, 2):
        if msg1["r"] != msg2["r"]:
            continue

        m1, m2 = msg1["m"], msg2["m"]
        s1, s2 = msg1["s"], msg2["s"]

        k = (((m1 - m2) % Q) * invmod(s1 - s2, Q)) % Q

        privkey = recover_dsa_privkey(k, msg1["r"], s1, m1)
        digest = SHA1(to_hexstring(to_bytes(privkey))).hexdigest()
        assert digest == "ca8f6f7c66fa362d40760d135b763eb8527d3d52"

        print()
        print("Recovered key:", privkey)

        break
    else:
        print("Failed to recover key!")
def main():
    messages = [b"", b"The quick brown fox jumps over the lazy dog", b"BEES"]

    print("Testing the SHA1 class.")
    print()

    for message in messages:
        print("Message: ", message)
        print("Expected:", sha1(message).hexdigest())
        print("Actual:  ", SHA1(message).hexdigest())
        print()
    print()

    print("Generating a secret-prefix MAC.")
    message = b"The quick brown fox jumps over the lazy cog"
    key = b"An arbitrary key"
    pmac = make_sha1_pmac(message, key)
    print()

    print("Message:      ", message)
    print("Key:          ", key)
    print("MAC:          ", pmac)
    print("Authenticates:", check_sha1_pmac(message, key, pmac))
    print()

    print("MAC still authenticates after modifying message?")
    message2 = bytes(reversed(message))
    print()

    print("Message:      ", message2)
    print("Key:          ", key)
    print("MAC:          ", pmac)
    print("Authenticates:", check_sha1_pmac(message2, key, pmac))
def main():
    print("First, we'll test our DSA implementation.")
    _test_dsa()
    print()

    print("Now, let's bruteforce a DSA private key from a 16-bit subkey.")
    print()

    ptext = (
        b"For those that envy a MC it can be hazardous to your health\n"
        b"So be friendly, a matter of life and death, just like a etch-a-sketch\n"
    )
    sig = [
        548099063082341131477253921760299949438196259240,
        857042759984254168557880549501802188789837994940,
    ]

    print(f"Plaintext:")
    print_indent(*ptext.split(b"\n"), width=70, as_hex=False)

    print(f"Bruteforced private key:")
    privkey = bruteforce_dsa_privkey(ptext, sig)
    print_indent(privkey, as_hex=False)

    # This part is a little convoluted, but the SHA-1 hashes mentioned
    # in the challenge must've been mentioned ~for a reason~!!
    known_sha1s = [
        "d2d0714f014a9784047eaeccf956520045c45265",
        "0954edd5e0afe5542a4adf012611a91912a3ec16",
    ]
    sha1s = [
        SHA1(ptext).hexdigest(),
        SHA1(to_hexstring(to_bytes(privkey))).hexdigest()
    ]
    print(
        "Calculated hashes match for plaintext and private key:",
        all(a == b for a, b in zip(known_sha1s, sha1s)),
    )
def main():
    orig_msg = (
        b"comment1=cooking%20MCs;userdata=foo;"
        b"comment2=%20like%20a%20pound%20of%20bacon"
    )
    key = random.choice(WORDS)
    pmac = make_sha1_pmac(orig_msg, key)

    print("First, let's verify that a message authenticates.")
    print()
    print("Message:      ", orig_msg)
    print("Key:          ", "<secret>")
    print("MAC:          ", pmac)
    print("Authenticates:", check_sha1_pmac(orig_msg, key, pmac))
    print()
    print()
    print("Now for the attack.")

    extra_data = b";admin=true"

    crafted_pmac = SHA1()  # Our result object.
    crafted_msg, h, key_length = b"", pmac.h.copy(), None

    # Since we don't know the key's bit length, we have to guess at it.
    for klen in range(1, 256):
        # Set "initial" state from original hash.
        crafted_pmac.h = h

        # The bit length of the key affects all of these.
        glue_padding = sha1_padding(orig_msg, klen)
        crafted_msg = orig_msg + glue_padding + extra_data
        padded = extra_data + sha1_padding(crafted_msg, klen)

        # Calculate hash of this particular crafted message.
        chunks = (padded[i : i + 64] for i in range(0, len(padded), 64))
        crafted_pmac._process(chunks)

        if check_sha1_pmac(crafted_msg, key, crafted_pmac):
            key_length = klen
            break
    else:
        print("Unfortunately, we failed.")
        return

    print()
    print("Message:      ", crafted_msg)
    print("Key:          ", "<still secret>")
    print("Key length:   ", key_length)  # We guessed this.
    print("MAC:          ", crafted_pmac)
    print("Authenticates:", check_sha1_pmac(crafted_msg, key, crafted_pmac))
def bruteforce_dsa_privkey(bs, sig, max_k=2**16, p=P, q=Q, g=G):
    r, s = sig
    z = from_bytes(SHA1(bs).digest())

    for k in range(1, max_k):
        k_inv = invmod(k, q)
        if k_inv is None:
            continue

        x = (((((s * k) % q) - z) % q) * invmod(r, q)) % q

        sk, rk = (k_inv * (z + x * r)) % q, modexp(g, k, p) % q
        if s == sk and r == rk:
            return x
Exemple #6
0
 def _aes_key(self, s=None):
     """Generates 128-bit key for AES from DH session key."""
     s = self.dh_shared if s is None else s
     return SHA1(s).digest()[:16]
def check_sha1_pmac(msg, key, pmac):
    return SHA1(key + msg) == pmac
def make_sha1_pmac(msg, key):
    return SHA1(key + msg)