Challenge

Nostalgic was hard!? It's only ChaCha20!

from Crypto.Cipher import ChaCha20
import os

FLAG = os.getenv("FLAG", "Alpaca{dummy}").encode()

key = os.urandom(32)
nonce = os.urandom(8)

def encrypt(plaintext):
    cipher = ChaCha20.new(key=key, nonce=nonce)
    return cipher.encrypt(plaintext)

msg = b"Daily AlpacaHack is a daily CTF challenge with a fun new puzzle every day."
print("encrypted msg:", encrypt(msg).hex())
print("encrypted flag:", encrypt(FLAG).hex())
encrypted msg: aa9054e15ee209d458784a70bab6a5d62523f26eef2458cb3d48ccc6122fba1d75733c17c70a908a64b45b0caaf8d0e3bd30bcc0900283af88d82d4020cdacf7eaa5685ccf443776b4d9
encrypted flag: af9d4dec44a333d44d2e0e62ade1f5c95a38e438eb6a0acf0b10c1b92513cf627828250ff43cbbae42db6244a3

Solution

ストリーム暗号ChaCha20で既知のメッセージとFLAGが暗号化されています。

ストリーム暗号は次のように平文と鍵のXORで計算されます。

ciphertext = plaintext XOR keystream

そのため同じノンス・鍵の2つの暗号文がある場合、それらをXORしてkeystreamを打ち消し合うことが可能です。

Final Script

from binascii import unhexlify as u

msg = b"Daily AlpacaHack is a daily CTF challenge with a fun new puzzle every day."
c1  = u("aa9054e15ee209d458784a70bab6a5d62523f26eef2458cb3d48ccc6122fba1d75733c17c70a908a64b45b0caaf8d0e3bd30bcc0900283af88d82d4020cdacf7eaa5685ccf443776b4d9")
c2  = u("af9d4dec44a333d44d2e0e62ade1f5c95a38e438eb6a0acf0b10c1b92513cf627828250ff43cbbae42db6244a3")

print(bytes(a ^ b ^ c for a, b, c in zip(c1, msg, c2)).decode())