Skip to main content

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.

  1. 4 fuites successives du PRNG xorshiro256 : 0x785a1cb672480875, 0x91c1748fec1dd008, 0x5c52ec3a5931f942, 0xac4a414750cd93d7
  2. la clé publique Q sur secp256k1
  3. le message, son hash, et la signature ECDSA (r,s) générée avec le même PRNG
  4. 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 :

  1. key = SHA256(d)
  2. 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!}