bling_yu

Predicting Fibonacci LFSRs over Integer Residue Rings Using Multi-Segment Truncated Sequences

Dec 22nd, 2025 (edited)
125
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 9.29 KB | Source Code | 0 0
  1. '''
  2. This code is used to recover the truncated ZUC's driving sequence by multi-segment truncated sequences.
  3.  
  4. Suppose that the modulus, the coefficients and the initial state are unknown, given the first N truncated digits which consist of the middle segment(s) of the sequence, we need to recover the modulus, the coefficients and the initial state.
  5.  
  6. '''
  7. import time
  8.  
  9. # Search annihilating polynomials over Z/(m) of the sequence
  10. def Search_linear_relations(truncated_a, v, r, t):
  11.     l = len(v)
  12.     L = Matrix(ZZ, r + t, r + t)
  13.     L[0 : t, 0 : t] = identity_matrix(t) * 2 ** (v[l - 1] - v[1])
  14.     L[t : r + t, t : r + t] = identity_matrix(r) * 2 ** (v[l - 2] - v[1])
  15.     for i in range(r):
  16.         for j in range(t):
  17.             for w in range((l - 1) // 2):
  18.                 L[i + t, j] += 2 ** (v[2 * w + 1] - v[1]) * truncated_a[2 * w + 1, i + j]
  19.     reduced_L = L.BKZ(algorithm = 'NTL', fp = 'fp', block_size = 20)
  20.     C = reduced_L[0 : r, t : r + t] / 2 ** (v[l - 2] - v[1])
  21.    
  22.     total_eta_m = 0
  23.     for j in range(r):
  24.         s_m = 0
  25.         if C[j] != 0:
  26.             for i in range(t):
  27.                 if (C[j] * a[i : i + r]) % m == 0:
  28.                     s_m += 1
  29.             if s_m == t:
  30.                 total_eta_m += 1
  31.     print("Number of annihilating polynomials = ", total_eta_m)    
  32.     return C
  33.  
  34. # Determine whether c is the n-th power of some k-bit prime
  35. def is_nth_power(c, n, k):
  36.     if ZZ(c).is_perfect_power():
  37.         base, exp = ZZ(c).perfect_power()
  38.         if base.is_prime() and base.nbits() == k and exp == n:
  39.             return base
  40.         else:
  41.             return False
  42.     else:
  43.         return False
  44.  
  45. # Recover the modulus
  46. def recover_the_modulus(C, n, k):
  47.     R = PolynomialRing(ZZ, 'x')
  48.     (x,) = R._first_ngens(1)
  49.     r = C.ncols()
  50.     f = []
  51.     fi = C[0, 0] * x**0
  52.     for i in range(1, r):
  53.         fi += C[0, i] * x**i
  54.     f.append(fi)
  55.     fi = C[1, 0] * x**0
  56.     for i in range(1, r):
  57.         fi += C[1, i] * x**i
  58.     f.append(fi)
  59.     m_power = f[0].resultant(f[1])
  60.  
  61.     flag = 2
  62.     recovered_m = is_nth_power(m_power, n, k)
  63.  
  64.     while recovered_m == False and flag < r:
  65.         fi = C[flag, 0] * x**0
  66.         for i in range(1, r):
  67.             fi += C[flag, i] * x**i
  68.         f.append(fi)
  69.         for i in range(flag):
  70.             res = f[flag].resultant(f[i])
  71.             m_power = gcd(m_power, res)
  72.         flag += 1
  73.         recovered_m = is_nth_power(m_power, n, k)
  74.  
  75.     if recovered_m == False:
  76.         Flag = 0
  77.         print("Fail to recover m!")
  78.         #print("The wrong m == ", recovered_m, "\n")
  79.     else:
  80.         Flag = 1
  81.         print("Success to recover m by ", flag, " polynomials!")
  82.         print("m = ", recovered_m, "\n")
  83.    
  84.     return recovered_m
  85.  
  86. # Recover the coefficients
  87. def recover_the_coefficients(C, m, n):
  88.     R = PolynomialRing(Integers(m), 'x')
  89.     (x,) = R._first_ngens(1)
  90.     r = C.ncols()
  91.    
  92.     for i in range(r):
  93.         mark = r - 1
  94.         while C[i, mark] == 0:
  95.             mark -= 1
  96.         for j in range(r):
  97.             C[i, j] = (C[i, j] * C[i, mark]**(-1)) % m
  98.    
  99.     f = []
  100.     fi = ZZ(C[0, 0]) * x**0
  101.     for i in range(1, r):
  102.         fi += ZZ(C[0, i]) * x**i
  103.     f.append(fi)
  104.     fi = ZZ(C[1, 0]) * x**0
  105.     for i in range(1, r):
  106.         fi += ZZ(C[1, i]) * x**i
  107.     f.append(fi)
  108.     g = f[0].gcd(f[1])
  109.  
  110.     flag = 2
  111.     while g.degree() > n and flag < r:
  112.         fi = ZZ(C[flag, 0]) * x**0
  113.         for i in range(1, r):
  114.             fi += ZZ(C[flag, i]) * x**i
  115.         f.append(fi)
  116.         for i in range(flag):
  117.             g = g.gcd(f[flag])
  118.         flag += 1
  119.  
  120.     if g.degree() != n:
  121.         Flag = 0
  122.         print("Fail to recover f!")
  123.         #print("The wrong f = ", g, "\n")
  124.     else:
  125.         Flag = 1
  126.         print("Success to recover f by ", flag, " polynomials!")
  127.         print("f = ", g, "\n")
  128.    
  129.     return g
  130.  
  131. def recover_the_initial_state(m, n, f, truncated_a, k, v, d):
  132.     l = len(k)
  133.     dim = d * (l + 1) // 2 + 1
  134.     recovered_a = vector(ZZ, n)
  135.     Q = Matrix(ZZ, n, n)
  136.     Q_power = identity_matrix(n)
  137.     L = Matrix(ZZ, dim, dim)
  138.    
  139.     Q[0, n - 1] = (-f.list()[0]) % m
  140.     for i in range(1, n):
  141.         Q[i, i - 1] = 1
  142.         Q[i, n - 1] = (-f.list()[i]) % m
  143.    
  144.     for i in range(1, n):
  145.         Q_power = (Q_power * Q) % m
  146.     for i in range(n, d):
  147.         Q_power = (Q_power * Q) % m
  148.         b = 0
  149.         L[1 : n + 1, i + 1] = Q_power[0 : n, 0]
  150.         for j in range(int((l - 1) / 2)):
  151.             L[j * n + d + 1 : j * n + d + n + 1, i + 1] = Q_power[0 : n, 0] * 2**v[2 * (j + 1)]
  152.             b += 2 ** v[2 * j + 1] * truncated_a[2 * j + 1, i]
  153.             for w in range(n):
  154.                 b -= Q_power[w, 0] * 2 ** v[2 * j + 1] * truncated_a[2 * j + 1, w]
  155.             b = b % m
  156.         L[0, i + 1] = b + 2**(k[0] - 1)
  157.        
  158.     for i in range(n + 1):
  159.         L[0, i] = 2**(k[0] - 1)
  160.     for i in range(d + 1, dim):
  161.         L[0, i] = 2**(k[0] - 1)
  162.     L[1 : n + 1, 1 : n + 1] = identity_matrix(n)
  163.     L[n + 1: d + 1, n + 1: d + 1] = identity_matrix(d - n) * m
  164.     for i in range(int((l - 1) / 2)):
  165.         L[i * n + d + 1: i * n + d + n + 1, i * n + d + 1: i * n + d + n + 1] = identity_matrix(n) * 2**(k[0] - k[2 * (i + 1)])
  166.         L[i * (d - n) + d + (l - 1) // 2 * n + 1: i * (d - n) + d + (l - 1) // 2 * n + d - n + 1, n + 1 : d + 1] = identity_matrix(d - n) * (-2**v[2 * (i + 1)])
  167.         L[i * (d - n) + d + (l - 1) // 2 * n + 1: i * (d - n) + d + (l - 1) // 2 * n + d - n + 1, i * (d - n) + d + (l - 1) // 2 * n + 1: i * (d - n) + d + (l - 1) // 2 * n + d - n + 1] = identity_matrix(d - n) * 2**(k[0] - k[2 * (i + 1)])
  168.  
  169.     reduced_L = L.BKZ(algorithm = 'NTL', fp = 'fp', block_size = 20)
  170.    
  171.     flag = 0
  172.     while reduced_L[flag, 0].abs() != 2**(k[0] - 1):
  173.         flag += 1
  174.     if reduced_L[flag, 0] == -2**(k[0] - 1):
  175.         for i in range(0, n):
  176.             recovered_a[i] = reduced_L[flag, i + 1] + 2**(k[0] - 1)
  177.             for j in range(int((l - 1) / 2)):
  178.                 recovered_a[i] += truncated_a[2 * j + 1, i] * 2**v[2 * j + 1]
  179.                 recovered_a[i] += (reduced_L[flag, j * n + d + 1 + i] + 2**(k[0] - 1)) // 2**(k[0] - k[2 * (j + 1)]) * 2**v[2 * (j + 1)]
  180.             recovered_a[i] = recovered_a[i] % m
  181.     if reduced_L[flag, 0] == 2**(k[0] - 1):
  182.         for i in range(0, n):
  183.             recovered_a[i] = -reduced_L[flag, i + 1] + 2**(k[0] - 1)
  184.             for j in range(int((l - 1) / 2)):
  185.                 recovered_a[i] += truncated_a[2 * j + 1, i] * 2**v[2 * j + 1]
  186.                 recovered_a[i] += (-reduced_L[flag, j * n + d + 1 + i] + 2**(k[0] - 1)) // 2**(k[0] - k[2 * (j + 1)]) * 2**v[2 * (j + 1)]
  187.             recovered_a[i] = recovered_a[i] % m
  188.    
  189.     return recovered_a
  190.  
  191.  
  192. n = 16
  193. m = 2147483647
  194. # Exp.1
  195. r = 70
  196. # Exp.2
  197. #r = 105
  198. # Exp.3
  199. #r = 104
  200. # Exp.4
  201. #r = 90
  202. t = r
  203. N = r + t - 1
  204. a = vector(ZZ, N)
  205.  
  206. a[0] = 5113033
  207. a[1] = 101513454
  208. a[2] = 342517303
  209. a[3] = 602278014
  210. a[4] = 545182365
  211. a[5] = 2109832480
  212. a[6] = 83899935
  213. a[7] = 1353806595
  214. a[8] = 56274808
  215. a[9] = 220365776
  216. a[10] = 1900047307
  217. a[11] = 1287703311
  218. a[12] = 754976298
  219. a[13] = 344592060
  220. a[14] = 2072123851
  221. a[15] = 1906585150
  222. for i in range(n, N):
  223.     a[i] = (2^15 * a[i - 1] + 2^17 * a[i - 3] + 2^21 * a[i - 6] + 2^20 * a[i - 12] + 257 * a[i - 16]) % m
  224.    
  225. # Exp.1 and Exp.2    
  226. l = 3
  227. # Exp.3 and Exp.4    
  228. #l = 5
  229. # Exp.1
  230. k = vector([9, 17, 5])   # l = 3, r = 70, d = 30
  231. # Exp.2
  232. #k = vector([12, 14, 5])   # l = 3, r = 105, d = 36
  233. # Exp.3
  234. #k = vector([6, 3, 3, 14, 5])   # l = 5, r = 104, d = 30
  235. # Exp.4
  236. #k = vector([6, 2, 3, 15, 5])   # l = 5, r = 90, d = 31
  237.  
  238. v = vector(ZZ, l)
  239. for i in range(l):
  240.     for j in range(i):
  241.         v[i] += k[j]
  242.        
  243. truncated_a = Matrix(ZZ, l, N)
  244. for i in range(l):
  245.     for j in range(N):
  246.         truncated_a[i, j] = (a[j] >> v[i]) % 2 ** k[i]
  247.  
  248. print("Number of segments l = ", l)
  249. print("Bit-length of each segment k = ", k, "\n")
  250. print("******************************************************")
  251. print("Step 1: Search annihilating polynomials over Z/(m) of the sequence")
  252. Tstart = time.perf_counter()
  253. C = Search_linear_relations(truncated_a, v, r, t)
  254. Tend = time.perf_counter()
  255. elapsed = Tend - Tstart
  256. print("Wall Time: ",elapsed ," s\n")
  257.  
  258. print("******************************************************")
  259. print("Step 2: Recover the modulus")
  260. Tstart = time.perf_counter()
  261. recovered_m = recover_the_modulus(C, n, 31)
  262. Tend = time.perf_counter()
  263. elapsed = Tend - Tstart
  264. print("Wall Time: ",elapsed ," s\n")
  265.  
  266. print("******************************************************")
  267. print("Step 3: Recover the coefficients")
  268. Tstart = time.perf_counter()
  269. recovered_f = recover_the_coefficients(C, recovered_m, n)
  270. Tend = time.perf_counter()
  271. elapsed = Tend - Tstart
  272. print("Wall Time: ",elapsed ," s\n")
  273. '''
  274. R = PolynomialRing(Integers(m), 'x')
  275. (x,) = R._first_ngens(1)
  276. f = x**16 - (2**15 * x**15 + 2**17 * x**13 + 2**21 * x**10 + 2**20 * x**4 + 2**8 + 1)
  277. '''
  278.  
  279. # Exp.1
  280. d = 30
  281. # Exp.2
  282. #d = 36
  283. # Exp.3
  284. #d = 30
  285. # Exp.4
  286. #d = 31
  287.  
  288. print("******************************************************")
  289. print("Step 4: Recover the initial state")
  290. Tstart = time.perf_counter()
  291. recovered_a = recover_the_initial_state(recovered_m, n, recovered_f, truncated_a, k, v, d)
  292. print("a = ", recovered_a)
  293. Tend = time.perf_counter()
  294. elapsed = Tend - Tstart
  295. print("Wall Time: ",elapsed ," s\n")
Add Comment
Please, Sign In to add comment