A zero-knowledge proof (ZKP) is a technique that enables one party (the prover) to demonstrate to another party (the verifier) the truth of a certain statement without revealing any additional information besides the fact that the statement is true. The core idea behind zero-knowledge proofs is that while it’s straightforward to prove you have certain knowledge by disclosing it, the real challenge lies in proving you have that knowledge without actually revealing the knowledge itself or any details about it.
# Diffie-Hellman group (512 bits) # p = 2*q + 1 where p,q are both prime, and 2 modulo p generates a group of order q p = 0x1ed344181da88cae8dc37a08feae447ba3da7f788d271953299e5f093df7aaca987c9f653ed7e43bad576cc5d22290f61f32680736be4144642f8bea6f5bf55ef q = 0xf69a20c0ed4465746e1bd047f57223dd1ed3fbc46938ca994cf2f849efbd5654c3e4fb29f6bf21dd6abb662e911487b0f9934039b5f20a23217c5f537adfaaf7 g = 2
# w,y for the relation `g^w = y mod P` we want to prove knowledge of # w = random.randint(0,q) # y = pow(g,w,P) w = 0x5a0f15a6a725003c3f65238d5f8ae4641f6bf07ebf349705b7f1feda2c2b051475e33f6747f4c8dc13cd63b9dd9f0d0dd87e27307ef262ba68d21a238be00e83 y = 0x514c8f56336411e75d5fa8c5d30efccb825ada9f5bf3f6eb64b5045bacf6b8969690077c84bea95aab74c24131f900f83adf2bfe59b80c5a0d77e8a9601454e5
assert (y%p) >= 1 assertpow(y, q, p) == 1
classChallenge: def__init__(self): self.before_input = "Prove to me that you know an w such that g^w = y mod p. Send me a = g^r mod p for some random r in range(q)\n" self.state = "CHALLENGE"
defchallenge(self, msg): ifself.state == "CHALLENGE": # Prover sends a randomly sampled `A` value from Z_p* to verifier self.a = msg["a"] if (self.a%p) < 1orpow(self.a, q, p) != 1: self.exit = True return {"error": "Invalid value"}
# Verifier sends a random challenge sampled from range(0, 2^t) where 2^t <= q self.e = random.randint(0,2**511) self.state = "PROVE" return {"e": self.e, "message": "send me z = r + e*w mod q"} elifself.state == "PROVE": # Prover sends z = r + e*w mod q to the Verifier z = msg["z"]
self.exit = True
# Verifier checks g^z = A*h^e mod p ifpow(g,z,p) == (self.a*pow(y,self.e,p)) % p: return {"flag": FLAG, "message": "You convinced me you know an `w` such that g^w = y mod p!"} else: return {"error": "something went wrong :("}
import builtins; builtins.Challenge = Challenge # hack to enable challenge to be run locally, see https://cryptohack.org/faq/#listener listener.start_server(port=13425)
p = 0x1ed344181da88cae8dc37a08feae447ba3da7f788d271953299e5f093df7aaca987c9f653ed7e43bad576cc5d22290f61f32680736be4144642f8bea6f5bf55ef q = 0xf69a20c0ed4465746e1bd047f57223dd1ed3fbc46938ca994cf2f849efbd5654c3e4fb29f6bf21dd6abb662e911487b0f9934039b5f20a23217c5f537adfaaf7 g = 2
w = 0x5a0f15a6a725003c3f65238d5f8ae4641f6bf07ebf349705b7f1feda2c2b051475e33f6747f4c8dc13cd63b9dd9f0d0dd87e27307ef262ba68d21a238be00e83
r = remote("socket.cryptohack.org", 13425)
random_r = random.randint(0, q) a = pow(g, random_r, p)
# 发送 a,注意是 JSON 格式 r.sendafter(b"for some random r in range(q)\n",json.dumps({"a": a}).encode())
line = r.recvline() response = json.loads(line.decode()) e = response["e"]
z = (random_r + e * w) % q
r.sendline(json.dumps({"z": z}).encode())
print(r.recvline().decode()) # {"flag": "crypto{sigma_protocol_complete!}", "message": "You convinced me you know an `w` such that g^w = y mod p!"}
from pwn import * import json import random from Crypto.Util.number import long_to_bytes
p = 0x1ed344181da88cae8dc37a08feae447ba3da7f788d271953299e5f093df7aaca987c9f653ed7e43bad576cc5d22290f61f32680736be4144642f8bea6f5bf55ef q = 0xf69a20c0ed4465746e1bd047f57223dd1ed3fbc46938ca994cf2f849efbd5654c3e4fb29f6bf21dd6abb662e911487b0f9934039b5f20a23217c5f537adfaaf7 g = 2
r = remote("socket.cryptohack.org", 13426) # 第一轮 r.recvuntil(b"such that y = g^w mod p.\n") line = r.recvline() response1 = json.loads(line.decode()) a1 = response1["a"] print(response1["message"]) # send random e in range 0 <= e < 2^511
e = 1 r.sendline(json.dumps({"e": e}).encode())
line = r.recvline() response1 = json.loads(line.decode()) z1 = response1["z"] print(response1["message"]) # not convinced? I'll happily do it again!
# -------------------------------------------------------------------- # 第二轮 line = r.recvline() response2 = json.loads(line.decode()) a2 = response2["a2"] print(response2["message"]) # send random e in range 0 <= e < 2^511
e = 2 r.sendline(json.dumps({"e": e}).encode())
line = r.recvline() response2 = json.loads(line.decode()) z2 = response2["z2"] print(response1["message"]) # not convinced? I'll happily do it again!
# -------------------------------------------------------------------- w = long_to_bytes((z2-z1)%q) print(w) # b'crypto{specially_sound_sigmas}\xf7c\xb0H\xa1j\t\x9f\x9ab`%\xf7\xe3\x1552\x15\xda%\xf7\xf5yk\xd2\xa7\x1f\xbb3\x8f\xfd&'
from pwn import * import json import random from Crypto.Util.number import long_to_bytes
p = 0x1ed344181da88cae8dc37a08feae447ba3da7f788d271953299e5f093df7aaca987c9f653ed7e43bad576cc5d22290f61f32680736be4144642f8bea6f5bf55ef q = 0xf69a20c0ed4465746e1bd047f57223dd1ed3fbc46938ca994cf2f849efbd5654c3e4fb29f6bf21dd6abb662e911487b0f9934039b5f20a23217c5f537adfaaf7 g = 2
r = remote("socket.cryptohack.org", 13426) # 第一轮 r.recvuntil(b"such that y = g^w mod p.\n") line = r.recvline() response1 = json.loads(line.decode()) a1 = response1["a"] print(response1["message"]) # send random e in range 0 <= e < 2^511
line = r.recvline() response1 = json.loads(line.decode()) z1 = response1["z"] print(response1["message"]) # not convinced? I'll happily do it again!
# -------------------------------------------------------------------- # 第二轮 line = r.recvline() response2 = json.loads(line.decode()) a2 = response2["a2"] print(response2["message"]) # send random e in range 0 <= e < 2^511