概述
signup是来自于HTB(hackthebox.com)的一个中级密码学挑战,完成该挑战所需要掌握的知识点在于数字签名算法(DSA)。
题目分析
相关的任务文件包括app.py源代码和output.txt文本文件。
app.py内容如下
from Crypto.Util.number import getPrime, getRandomRange, isPrime, inverse, long_to_bytes, bytes_to_long
from hashlib import sha512
from random import SystemRandom
from FLAG import flag
L = 2048
N = 256
def repeating_xor_key(message, key):
repeation = 1 + (len(message) // len(key))
key = key * repeation
key = key[:len(message)]
msg = bytes([c ^ k for c, k in zip(message, key)])
return msg
def domain_params_generation():
q = getPrime(N)
print(f"[+] q condition is satisfied : {N} bit")
print(q)
p = 0
while not (isPrime(p) and len(bin(p)[2:]) == L):
factor = getRandomRange(2**(L-N-1), 2**(L-N))
p = factor*q + 1
print(f"[+] p condition is satisfied : {L} bit")
print(p)
g = 1
while g == 1:
h = getRandomRange(2, p-2)
g = pow(h, factor, p)
print(f"[+] g condition is satisfied ")
print(g)
return(p, q, g)
def key_generation(p, q, g):
x = getRandomRange(1, q-1)
y = pow(g, x, p)
# print(f"[+] private key : {x}")
print(f"[+] public key : {y}")
return(x, y)
def sign(message, private_key, parameters):
p, q, g = parameters
k = getRandomRange(1, q-1)
r = 0
while r == 0:
r = pow(g, k, p) % q
s = 0
while s == 0:
Hm = int(sha512(message.encode('utf-8')).hexdigest(), 16)
s = (inverse(k, q) * (Hm + private_key*r)) % q
return (r, s)
def verify(message, signature, public_key, parameters):
p, q, g = parameters
r, s = signature
if 0 < r < q and 0 < s < q:
w = inverse(s, q)
Hm = int(sha512(message.encode('utf-8')).hexdigest(), 16)
u1 = (Hm * w) % q
u2 = (r * w) % q
v = ( (pow(g, u1, p) * pow(public_key, u2, p)) % p ) % q
if v == r:
print(f"[+] Valid signature")
return True
else:
print("[!] Invalid signature")
return False
parameters = domain_params_generation()
p, q, g = parameters
keys = key_generation(p, q, g)
x, y = keys
print()
print("=============================================================================================")
print("===================================== Let's signup ==========================================")
print("=============================================================================================")
print()
with open('./messages', 'r') as o:
messages = o.readlines()
for message in messages:
message = message.rstrip()
print("********************************************************************************")
print(f"message : {message}")
signature = sign(message, x, parameters)
r, s = signature
print(f"signature: {signature}")
# verify(message, signature, y, parameters)
o.close()
key = long_to_bytes(x)
ctf = repeating_xor_key(flag, key)
print()
print("[+] Cipher Text Flag (CTF) : ")
print(hex(bytes_to_long(ctf))[2:])
output.txt节选如下
[+] q condition is satisfied : 256 bit
82349764091980216703243528787846721157571379253101971061732427939554681522787
[+] p condition is satisfied : 2048 bit
22597614376179368541927291014351181151227386315855982530475290304066480549601872481125000412312232411854471891135859606462198144416258790805203892259557432117585853522644532244043882866950543772453962473369978828348563267281282352402870551728874298860312184560283971631533453430210347478794479982659822354364676170461394200151617927348650724770337556315374296708920869883704844016422867256502854967833552352244354161923370938186895496059235837548125257119775236437293842236838420824347387394024112360489719718482356279060719080630335848636921250967340335457343005624222097640480295331833551512059471129216303573167509
[+] g condition is satisfied
1522212929726315022276463307120954560926577974082882818811797965963142559037271005677110576098054722157527674127902562772533441779935566082403254092732974888203331702770403906546118095393659134079664766399608052006354212408035095060235607110586791971501473747663375215625574865706205289870455822949113508277054039439561937834562865074493156250996591912564352116714825726533396169890605271470775008065174813981767038809048518345994705536020788635479039463106099323099699225053825245500316037480436011003338091199884975066597695902083102085310246580054607257558459623691521498166469718547232285924180800892984387501802
[+] public key : 10301551349126329107088362265646928131049491442403482867496980836175873288506342576259707854365987702240527285572213439531723683002024146579359208402373918640506155640001430423630072829735081526301031527635977510509965942632354962864260004282149309957641462890526767244115715946314304320419688533957853639652278917522214541525010449214567907170562229712395613781822381944931215562795920884762599015541122869232934183665333423423899053585371134224417264749221110182731383922695086302701203378645475314119934887765168566160180674997559835947912322992312116118206408320274722250233256553213251576546136974127152356743543
=============================================================================================
===================================== Let's signup ==========================================
=============================================================================================
********************************************************************************
message : AUSSMKJVUNUPKBQ9X10U15409YQT79I6XNF7U0K5YI1568Q42L
signature: (56550035924113170505830328735387144247082155512866957659656089559388264283806, 81360207435150401587545818478565496253332501404776627654827674339875574853292)
********************************************************************************
message : TXNJSIVFXYVZL7SO393MU20XJAK9TN75M0VV29RYI1TMORLJB3
signature: (59454243776673042612495706274074683779593162173045377254234644102590461254174, 69901126149177379418089041430016833757460549835486806478242279543570064790515)
********************************************************************************
...
********************************************************************************
message : VRE2H23VMQ2ORK8WNRTLBEASQF3GY9VK05CEYQ74KGO44B8QJK
signature: (4221419519268981734259482267401150235000986484722820856700522542664903741971, 39042686015475285979469968400043348530602713981922607759813976003431964156934)
[+] Cipher Text Flag (CTF) :
caaa08c4332e5701a9105ab701cc830b9ddbe18f6612c999f82a344bdc597819fba00f81772e4001bc584cff06d287089d8085a3123bdca5e7706612d7641f66a2b6228a67336c60975719ef04e1b55edad4e6850126ee92bc25692bd15e274db3b214973f224001af5f46bb40d2930cd69bae
我们可以看到以上代码实现了数字签名算法(DSA),而输出文件中给出了p,q,g和y等参数,以及100个字串及其签名结果。 我们必须得到私钥x的值以获得Flag明文。
解题过程
在给出的100个字串中,我们发现有两个字串其签名结果的r值相同。
message : 2XKSQ0LYL0ASEKH5ROEB02AKNEKGRWEQCMBE6UUCJO1GK0MXP5
signature: (14238399380669249177320087107027881078478542661146360644630244485798709030798, 31010083525559078114799933393024424305929989859345011192619627339807386206438)
message : EISZZOL199OKHQB4JMJB7A3Z1W9S0P0ENI98V7RFS8K9RPYQ1V
signature: (14238399380669249177320087107027881078478542661146360644630244485798709030798, 33299491256343856509045723633358260063303030066061530037315886172050511280880)
那么根据以下链接中给出的公式,我们可以获得k和x。
rdist.root.org/2010/11/19/… rdist.root.org/2009/05/17/…
from hashlib import sha512
messageA = "2XKSQ0LYL0ASEKH5ROEB02AKNEKGRWEQCMBE6UUCJO1GK0MXP5"
hA = int(sha512(messageA.encode('utf-8')).hexdigest(), 16)
signatureA = (14238399380669249177320087107027881078478542661146360644630244485798709030798, 31010083525559078114799933393024424305929989859345011192619627339807386206438)
messageB = "EISZZOL199OKHQB4JMJB7A3Z1W9S0P0ENI98V7RFS8K9RPYQ1V"
hB = int(sha512(messageB.encode('utf-8')).hexdigest(), 16)
signatureB = (14238399380669249177320087107027881078478542661146360644630244485798709030798, 33299491256343856509045723633358260063303030066061530037315886172050511280880)
k = (hA - hB) * pow(signatureA[1] - signatureB[1], -1, q) % q
r = pow(g, k, p) % q
assert r == signatureA[0]
x = (((signatureA[1] * k) - hA) * pow(r, -1, q)) % q
然后使用x获得Flag明文
from Crypto.Util.number import long_to_bytes, bytes_to_long
ctf = bytes.fromhex("caaa08c4332e5701a9105ab701cc830b9ddbe18f6612c999f82a344bdc597819fba00f81772e4001bc584cff06d287089d8085a3123bdca5e7706612d7641f66a2b6228a67336c60975719ef04e1b55edad4e6850126ee92bc25692bd15e274db3b214973f224001af5f46bb40d2930cd69bae")
key = long_to_bytes(x)
flag = repeating_xor_key(ctf, key)
print("flag =", flag)