# sage e = 3 cnt = 3 N=108684504406001730978107355065522913091470167674222436489722232508562878942265531378563853986279259519842855383477949581415300994618557266600557629814170912555530200441331549267805294484355321505430150563949802485514426432450612355535035910835277447960752175362457165729276266536456917482476441605301749223673 c1=11916677858912595626741303803048763073265062084232819699152039070779448603839943645221450568650559197753804295090892194306963679051905125 c2=11916677858912595626741303803048763073265066091036090039985967549263766666260772390533720397506805484501981105835442312723743004564263781 c3=95333422871300765013930430424390104586121138764086629711853351243347558333657355599432416841199469970564180649403376333107226365096683496 c = [c1,c2,c3] PR = PolynomialRing(Zmod(N), 'x', cnt) x = PR.gens() F = [] for i in range(cnt): F.append(pow(x[i],e)-c[i]) F.append(x[0]+x[1]-x[2]+10000) I = Ideal(F) G= I.groebner_basis() for b in G[:-1]: mi = ZZ(-b(0, 0, 0)) print(bytes.fromhex(hex(mi)[2:])) ''' b'flag{This_is_flag1}' b'flag{This_is_flag2}' '''
# sage def linearPaddingHastads1(cArray, nArray, aArray, bArray, e=3, eps=1/8): """ Performs Hastads attack on raw RSA with no padding. This is for RSA encryptions of the form: cArray[i] = pow(aArray[i]*msg + bArray[i],e,nArray[i]) Where they are all encryptions of the same message. cArray = Ciphertext Array nArray = Modulus Array aArray = Array of 'slopes' for the linear padding bArray = Array of 'y-intercepts' for the linear padding e = public exponent """ if(len(cArray) == len(nArray) == len(aArray) == len(bArray) == e): for i in range(e): cArray[i] = Integer(cArray[i]) nArray[i] = Integer(nArray[i]) aArray[i] = Integer(aArray[i]) bArray[i] = Integer(bArray[i]) TArray = [-1]*e for i in range(e): arrayToCRT = [0]*e arrayToCRT[i] = 1 TArray[i] = crt(arrayToCRT, nArray) P.<x> = PolynomialRing(Zmod(prod(nArray))) gArray = [-1]*e for i in range(e): gArray[i] = TArray[i]*(pow(aArray[i]*x + bArray[i], e) - cArray[i]) g = sum(gArray) g = g.monic() # Use Sage's inbuilt coppersmith method roots = g.small_roots(epsilon=eps) if(len(roots) == 0): print("No Solutions found") return -1 return roots[0]
else: print("CiphertextArray, ModulusArray, and the linear padding arrays need to be of the same length," + "and the same size as the public exponent")
def linearPaddingHastads2(cArray, nArray, aArray, bArray, e=3, eps=1/8): cnt = e PR = PolynomialRing(ZZ, 'x') x = PR.gen() Fs = [] for i in range(cnt): f = PR((A[i]*x + B[i])**e - Cs[i]) ff = f.change_ring(Zmod(Ns[i])) ff = ff.monic() f = ff.change_ring(ZZ) Fs.append(f)
F = crt(Fs, Ns) M = reduce(lambda x, y: x * y, Ns) FF = F.change_ring(Zmod(M)) m = FF.small_roots(epsilon=1/16) if m: return m[0] else: return None
在经历了模数相同的相关消息攻击,也看过了模数不同的Hastad攻击,但是我们的指数$e$始终是一致的,SMUPE问题是论文《Solving Systems of Modular Equations in One Variable: How Many RSA-Encrypted Messages Does Eve Need to Know? 》中提出的,不仅模数不同,且指数$e$也不同,具体如下:
Cs = [c1, c2] A = [a1, a2] B = [b1, b2] cnt = 2 PKs = [(2,n1), (3,n2)] PR = PolynomialRing(ZZ, 'x') x = PR.gen() Fs = [] for i in range(cnt): f = PR( ( A[i]*x + B[i] )**PKs[i][0] - Cs[i] ) ff = f.change_ring( Zmod(PKs[i][1]) ) ff = ff.monic() f = ff.change_ring(ZZ) Fs.append(f) F = crt( [ Fs[0]*x, Fs[1]], [ PKs[i][1] for i in range(cnt) ] )
M = reduce( lambda x, y: x * y, [ PKs[i][1] for i in range(cnt) ] ) FF = F.change_ring( Zmod(M) ) m = FF.small_roots(epsilon=1.0/16)[0] print(m)
PS:small_roots的参数需要根据实际情况进行调整
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
Let `N` be the characteristic of the base ring this polynomial is defined over: ``N = self.base_ring().characteristic()``. This method returns small roots of this polynomial modulo some factor `b` of `N` with the constraint that `b >= N^\beta`. Small in this context means that if `x` is a root of `f` modulo `b` then `|x| < X`. This `X` is either provided by the user or the maximum `X` is chosen such that this algorithm terminates in polynomial time. If `X` is chosen automatically it is `X = ceil(1/2 N^{\beta^2/\delta - \epsilon})`. The algorithm may also return some roots which are larger than `X`. 'This algorithm' in this context means Coppersmith's algorithm for finding small roots using the LLL algorithm. The implementation of this algorithm follows Alexander May's PhD thesis referenced below. INPUT: - ``X`` -- an absolute bound for the root (default: see above) - ``beta`` -- compute a root mod `b` where `b` is a factor of `N` and `b \ge N^\beta`. (Default: 1.0, so `b = N`.) - ``epsilon`` -- the parameter `\epsilon` described above. (Default: `\beta/8`) - ``**kwds`` -- passed through to method :meth:`Matrix_integer_dense.LLL()