証明書計算は2つの部分に分ける事が出来る.
前半
g = 1
certificate = 0
for i in SALT + user + passwd:
certificate = (certificate * certificate + ord(i) * g) % PRIME
g = g * G % PRIME
後半
return pow(certificate,2**len(SALT + user + passwd),PRIME)
前半を pre_make_certificate,後半を post_make_certificate と定義する.つまり
def pre_make_certificate(user,passwd)
g = 1
certificate = 0
for i in SALT + user + passwd:
certificate = (certificate * certificate + ord(i) * g) % PRIME
g = g * G % PRIME
def post_make_certificate(user,passwd,certificate):
return pow(certificate,2**len(SALT + user + passwd),PRIME)
def make_certificate(user,passwd):
pre_certificate = pre_make_certificate(user,passwd):
return post_make_certificate(user, passwd, pre_certificate)
pre_make_certificate("","") の結果がわかれば,任意のpre_make_certficate(user,passwd) が計算出来る事がわかる. 具体的には
def pre_make_certficate2(user,passwd):
g = pow(G,len(SALT),PRIME)
certificate = pre_make_certificate("","")
for i in user + passwd:
certificate = (certificate * certificate + ord(i) * g) % PRIME
g = g * G % PRIME
return certificate
と計算出来る
-
Tonelli-Shanks algorithm a^2 = x (mod p) を高速に求める事が出来るアルゴリズム これを知っていれば貼り付けるだけだが…
-
平方剰余の相互法則 "平方数 mod" でググると出てくる a^2 = x (mod p)の,解a が存在するかを判定する方法 x^((p-1)/2) == 1 なら解 a は存在, -1 なら存在しない
-
式変形を頑張る a^2 = x (mod p) なる a が存在する場合のみ考える. x^((p-1)/2) == 1 より a^2 * 1 = x * x^((p-1)/2) (mod p) a^2 = x^((p+1) / 2) (mod p) 今回,p % 4 == 3 なので, (p+1) / 4 は整数. x^((p+1) / 4) の 2乗 は x ^ ((p+1) / 2) なので,これは解の1つとなる.
ところで,a^2 = x の解は高々2個である.一方の解を b とすると,もう一方は -b となる. (b^2 = x の時, (-b)^2 = x となるため) 3個以上存在しないことは,背理法で示せば良い
以上で, a^2 = x (mod p) が求められた. しかし,今回求めたいのは a^(2^length) == certificate である. 上記のアルゴリズムを length 回実行すれば解は求められるが,解の個数は 1, 2, 4, 8, 16, ... と増加して,計算出来ない可能性が… と感じるが,実は何回反復しても,回は高々2個になる.
というか拡張gcdで (a^(2^length)) ^ y = certificate ^ y a^2 = certificate ^ y となるのでこっちでもいい
- p % 4 == 3 の時,aの平方剰余が 1 ならば,-a の平方剰余は必ず -1 となる 証明は省略する.
上の二つを組み合わせると, a^(2**k) = x (mod p) から a を高速に求める事が出来る len(SALT)は固定なので,これが計算出来れば pre_make_certificate("","") の候補が 2個 となる.
この2つの候補を,手元で他の(user,passwd,certificate) の組と一致するか確かめれば,pre_make_certificate("","")を特定することが出来る.
後は
def answer():
pre = pre_make_certficate2('flag','uasqlpgeqezahcqokjgelksxvqosmrhekbycrzpcmmmcmqbbve')
return pow(pre,len('flag') + len('uasqlpgeqezahcqokjgelksxvqosmrhekbycrzpcmmmcmqbbve') + SALT_length,PRIME)
を計算すればよい.