CRYPTOHACK-Modular Arithmetic部分wp
CRYPTOHACK学习记录
二次剩余(Quadratic Residues)
定义:
令整数a,p满足gcd(a,p)=1,若存在整数x使得
x2 ≡ a (mod p)
则称a为模p的二次剩余,否则称a为模p的二次非剩余,称x为a的平方根
题目为:

了解定义后可以编写代码解决此题:
p=29
a=[14,6,11]
for i in a:
for k in range (1,p):#i不会到p
if (k*k)%p==i:
print(f"x={k}")
print(f"a={i}")
为什么x只要遍历1到p-1?
经过验证发现x2%p的值呈现周期性变化,周期为p,证明如下:
设m属于(1,p-1)假如m2%p=m2,则(p+m)2%p=(((p+m)%p)*((p+m)%p) )%p=m2
勒让德符号(Legendre Symbol)
题目为:

Euler判别法
对奇素数p和满足gcd(a,p)=1的整数a,

证明:已知存在x使得 x2 ≡ a ( mod p),所以p|(x2-a)
由费马小定理,有 ap-1 ≡ 1 ( mod p),故

即要么a(p-1)/2 ≡ 1(mod p),要么a(p-1)/2 ≡ -1(mod p)。另外由于p是奇素数,所以p-1为偶数,有:

其中P(x)是某个整系数多项式,进而:

由费马小定理可知p|(xp-x),所以p|(a(p-1)/2-1)
Legendre 符号
对奇素数p和整数a,定义Legendre 符号如下:

根据题目提示 p ≡ 3 (mod 4)[确保(p+1)/4是整数],
首先当a是模p条件下的二次剩余时,有:a(p-1)/2 ≡ 1( mod p),
且ap ≡ a (mod p),相乘可得a(p+1)/2 ≡ a (mod p),即x=±a(p+1)/4,
由此可得解题脚本为:
from gmpy2 import *
p = ***
ints = ***
b=[]
for i in ints:
L=pow(i,(p-1)//2,p)
if L==1:
b.append(i)
print(len(b))
root=pow(b[0],(p+1)//4,p)
print(root)
"""for j in b:
for k in range(1,p):
if (k*k)%p==j:
print(k)"""#这种遍历方法运行时间太长根本无法运行
Modular Square Root
上一题求了 p≡1mod4情况下的模平方根,这题讨论了更普遍的情况,可以使用,tonelli_shanks算法,脚本如下
def legendre_symbol(a, p):
return pow(a, (p - 1) // 2, p)
def tonelli_shanks(a, p):
if legendre_symbol(a, p) != 1:
return None
q = p - 1
s = 0
while q % 2 == 0:
q //= 2
s += 1
if s == 1:
return pow(a, (p + 1) // 4, p)
for z in range(2, p):
if p - 1 == legendre_symbol(z, p):
break
c = pow(z, q, p)
r = pow(a, (q + 1) // 2, p)
t = pow(a, q, p)
m = s
while t != 1:
i = 1
while pow(t, 2**i, p) != 1:
i += 1
b = pow(c, 2**(m - i - 1), p)
r = (r * b) % p
t = (t * b * b) % p
c = (b * b) % p
m = i
return r
p =***
a =***
root = tonelli_shanks(a, p)
print(root)
也可以直接用python中sympy中的sqrt_mod()函数
from sympy import *
p = 30531851861994333252675935111487950694414332763909083514133769861350960895076504687261369815735742549428789138300843082086550059082835141454526618160634109969195486322015775943030060449557090064811940139431735209185996454739163555910726493597222646855506445602953689527405362207926990442391705014604777038685880527537489845359101552442292804398472642356609304810680731556542002301547846635101455995732584071355903010856718680732337369128498655255277003643669031694516851390505923416710601212618443109844041514942401969629158975457079026906304328749039997262960301209158175920051890620947063936347307238412281568760161
a = 8479994658316772151941616510097127087554541274812435112009425778595495359700244470400642403747058566807127814165396640215844192327900454116257979487432016769329970767046735091249898678088061634796559556704959846424131820416048436501387617211770124292793308079214153179977624440438616958575058361193975686620046439877308339989295604537867493683872778843921771307305602776398786978353866231661453376056771972069776398999013769588936194859344941268223184197231368887060609212875507518936172060702209557124430477137421847130682601666968691651447236917018634902407704797328509461854842432015009878011354022108661461024768
print(sqrt_mod(a, p))
中国剩余定理(Chinese Remainder Theorem)
题目为:

介绍:
中国剩余定理 (Chinese Remainder Theorem, CRT) 可求解如下形式的一元线性同余方程组(其中
两两互质):

方程组在模n意义下的唯一解为:
(n=n1·n2···nk,mi = n/ni,ci = mimi-1 ≡ 1 (mod ni))
证明:把ai乘在第i行等式两边,再将其相加可得上图第i个方程组成立

为什么要模n?
将x表示成kb+r的形式发现模n后代入方程组也成立
解题脚本如下:
from gmpy2 import *
def CRT(k,a,r):#k为方程的数量,a为余数列表,r为模数列表
n=1#模数的积
ans=0#储存最终结果
for i in range(0,k):
n=n*r[i]
for j in range(0,k):
m=n//r[j]
m_ni=invert(m,r[j])
ans=(ans+m*m_ni*a[j])%n
return ans
k=3
a=[2,3,5]
r=[5,11,17]
print(CRT(k,a,r))
使用sagemath中已封装好的函数则更简便:(只需传入两个参数,第一个是余数,第二个是模数)
a=[2,3,5];
n=[5,11,17];
crt(a,n)
Adrien's Signs
题目如下:
from random import randint
a = 288260533169915
p = 1007621497415251
FLAG = b'crypto{????????????????????}'
def encrypt_flag(flag):
ciphertext = []
plaintext = ''.join([bin(i)[2:].zfill(8) for i in flag])#bin()用于将数据转换为二进制,前两位为0b,[2:]代表删去0b,zfill(width) 是字符串方法,用于将字符串填充到指定长度 width。如果字符串的长度小于 width,则在字符串的左侧填充零(0),直到字符串的长度等于 width。
for b in plaintext:
e = randint(1, p)#从1到p中随机选取的数
n = pow(a, e, p)
if b == '1':
ciphertext.append(n)
else:
n = -n % p
ciphertext.append(n)
return ciphertext
print(encrypt_flag(FLAG))
经判断a为模p的二次剩余,即存在x,使得x2 ≡ a ( mod p),因为x2e ≡ ae ≡ n % p,所以n也应该是模p的二次剩余,但如果b≠1,n = -n % p,则n不是模p的二次剩余,由此可以逆推出二进制数据。
解题脚本如下:
a = 288260533169915
p = 1007621497415251
n=[67594220461269, 501237540280788, 718316769824518, 296304224247167***]
LS=pow(a,(p-1)//2,p)
print(LS)
b=""
flag=""
for i in n:
LS_n=pow(i,(p-1)//2,p)
if LS_n==1:
b+="1"
else:
b+="0"
for j in range(0,len(b),8):#(起始值,结束值,步长【表示每次增加数】)
b_split=b[j:j+8]
flag+=chr(int(b_split,2))#其中2表示二进制
print(flag)
python学习:
①append 是 列表(list) 的方法,用于在列表的末尾添加一个元素。
语法:
list.append(element)
注意事项:
append只能一次添加一个元素。- 如果添加的是一个列表,整个列表会被当作一个元素添加到原列表中。
my_list = [1, 2, 3]
my_list.append([4, 5])
print(my_list) # 输出: [1, 2, 3, [4, 5]]
②join 是 字符串(str) 的方法,用于将一个可迭代对象(如列表、元组等)中的元素连接成一个字符串。
语法:
str.join(iterable)
注意事项:
join只能用于字符串类型的可迭代对象。如果列表中包含非字符串元素,需要先将其转换为字符串。
my_list = [1, 2, 3]
result = '-'.join(map(str, my_list)) # 先将元素转换为字符串
print(result) # 输出: '1-2-3'
Modular Binomials
这题为模二项式题,有两种解法,一种是直接网站分解N,另一种是进行变换

在网上找到这篇写的过程很详细
from math import gcd
"""
c1 = (2p + 3q) ^ e1 % N
c2 = (5p + 7q) ^ e2 % N
--------------------------------------------------------------------------------------
N = pq => Binomial: mid elements % N == 0
=> c1 = (2p) ^ e1 + (3q) ^ e1 % N#先用二项式定理拆开,同时含有p,q项的在模N的条件下被约掉
c2 = (5p) ^ e2 + (7q) ^ e2 % N
--------------------------------------------------------------------------------------
raise c1 to e2 and c2 to e1: to create similar exponent
c1 ^ e2 = (2p) ^ (e1e2) + (3q) ^ (e1e2) % N (1)
c2 ^ e1 = (5p) ^ (e1e2) + (7q) ^ (e1e2) % N (2)
-------------------------------------------------------
Let (1) * 5 ^ (e1e2) - (2) * 2 ^ (e1e2) to get rid of p:
=> t = 5 ^ (e1e2) * c1 ^ e2 - 2 ^ (e1e1) * c2 ^ e1 = ( 15 ^ (e1e2) - 14 ^ (e1e2)) * q
--------------------------------------------------------------------------------------
=> t % q == 0 and we haved N % q == 0:
=> q = gcd(t, N)
p = N / q
"""
N = 14905562257842714057932724129575002825405393502650869767115942606408600343380327866258982402447992564988466588305174271674657844352454543958847568190372446723549627752274442789184236490768272313187410077124234699854724907039770193680822495470532218905083459730998003622926152590597710213127952141056029516116785229504645179830037937222022291571738973603920664929150436463632305664687903244972880062028301085749434688159905768052041207513149370212313943117665914802379158613359049957688563885391972151218676545972118494969247440489763431359679770422939441710783575668679693678435669541781490217731619224470152467768073
e1 = 12886657667389660800780796462970504910193928992888518978200029826975978624718627799215564700096007849924866627154987365059524315097631111242449314835868137
e2 = 12110586673991788415780355139635579057920926864887110308343229256046868242179445444897790171351302575188607117081580121488253540215781625598048021161675697
c1 = 14010729418703228234352465883041270611113735889838753433295478495763409056136734155612156934673988344882629541204985909650433819205298939877837314145082403528055884752079219150739849992921393509593620449489882380176216648401057401569934043087087362272303101549800941212057354903559653373299153430753882035233354304783275982332995766778499425529570008008029401325668301144188970480975565215953953985078281395545902102245755862663621187438677596628109967066418993851632543137353041712721919291521767262678140115188735994447949166616101182806820741928292882642234238450207472914232596747755261325098225968268926580993051
c2 = 14386997138637978860748278986945098648507142864584111124202580365103793165811666987664851210230009375267398957979494066880296418013345006977654742303441030008490816239306394492168516278328851513359596253775965916326353050138738183351643338294802012193721879700283088378587949921991198231956871429805847767716137817313612304833733918657887480468724409753522369325138502059408241232155633806496752350562284794715321835226991147547651155287812485862794935695241612676255374480132722940682140395725089329445356434489384831036205387293760789976615210310436732813848937666608611803196199865435145094486231635966885932646519
x1 = pow(5, e1 * e2, N) * pow(c1, e2, N)
x2 = pow(2, e1 * e2, N) * pow(c2, e1, N)
# t = x1 - x2
q = gcd(x1 - x2, N)
p = N // q
print(p)
print(q)

浙公网安备 33010602011771号