Crypto - ShiroHero
Données
Quatre nombres sortis du générateur pseudo-aléatoire (PRNG) : ils sont visibles dans output.txt.
Une signature ECDSA sur secp256k1, la clé publique correspondante et un chiffré AES du flag.
- 4 fuites successives du PRNG xorshiro256 : 0x785a1cb672480875, 0x91c1748fec1dd008, 0x5c52ec3a5931f942, 0xac4a414750cd93d7
- la clé publique Q sur secp256k1
- le message, son hash, et la signature ECDSA (r,s) générée avec le même PRNG
- le chiffré AES (IV + cipher-text)
Fuites
- Le PRNG est un xoshiro256 : il possède un état interne de 256 bits
- xoshiro256 possède 256 bits d’état et chaque appel révèle 64 bits (s1).
- 4 sorties × 64 bits = 256 bits → il est possible de résoudre un système linéaire (GF(2)) pour obtenir l’état complet S0
Une fois S0 connu, on exécute une 5ᵉ itération du PRNG ; le résultat, après la fonction temper, donne exactement le nonce k utilisé dans la signature.
Avec k, h, r, s on obtient d.
Vérification : d · G = Q
Le programme dérive la clé AES par SHA-256(d). Il suffit de :
- key = SHA256(d)
- AES-CBC-decrypt(iv + ciphertext, key)
# Merci ChatGPT (soyons honnêtes)
from Crypto.Hash import SHA256
from Crypto.Cipher import AES
from Crypto.Util.number import inverse, bytes_to_long, long_to_bytes
from ecc import G, n
from prng import next_raw, temper
state = S0
raw5, state = next_raw(state)
k = temper(raw5) % n
d = ((s * k - h) * inverse(r, n)) % n
key = SHA256.new(long_to_bytes(d)).digest()
cipher = AES.new(key, AES.MODE_CBC, iv)
flag = cipher.decrypt(ctxt)[-len("L3AK{"):]
print(flag.decode())
L3AK{u_4r3_th3_sh1r0_h3r0!}