Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagesmc
Path: blob/master/src/sage/crypto/lfsr.py
8817 views
1
r"""
2
Linear feedback shift register (LFSR) sequence commands
3
4
Stream ciphers have been used for a long time as a source of
5
pseudo-random number generators.
6
7
S. Golomb [G]_ gives a list of three statistical properties a
8
sequence of numbers `{\bf a}=\{a_n\}_{n=1}^\infty`,
9
`a_n\in \{0,1\}`, should display to be considered
10
"random". Define the autocorrelation of `{\bf a}` to be
11
12
.. math::
13
14
C(k)=C(k,{\bf a})=\lim_{N\rightarrow \infty} {1\over N}\sum_{n=1}^N (-1)^{a_n+a_{n+k}}.
15
16
17
In the case where `{\bf a}` is periodic with period
18
`P` then this reduces to
19
20
.. math::
21
22
C(k)={1\over P}\sum_{n=1}^P (-1)^{a_n+a_{n+k}}.
23
24
25
Assume `{\bf a}` is periodic with period `P`.
26
27
28
- balance: `|\sum_{n=1}^P(-1)^{a_n}|\leq 1`.
29
30
- low autocorrelation:
31
32
.. math::
33
34
C(k)= \left\{ \begin{array}{cc} 1,& k=0,\\ \epsilon, & k\not= 0. \end{array} \right.
35
36
37
(For sequences satisfying these first two properties, it is known
38
that `\epsilon=-1/P` must hold.)
39
40
- proportional runs property: In each period, half the runs have
41
length `1`, one-fourth have length `2`, etc.
42
Moreover, there are as many runs of `1`'s as there are of
43
`0`'s.
44
45
46
A general feedback shift register is a map
47
`f:{\bf F}_q^d\rightarrow {\bf F}_q^d` of the form
48
49
.. math::
50
51
\begin{array}{c} f(x_0,...,x_{n-1})=(x_1,x_2,...,x_n),\\ x_n=C(x_0,...,x_{n-1}), \end{array}
52
53
54
where `C:{\bf F}_q^d\rightarrow {\bf F}_q` is a given
55
function. When `C` is of the form
56
57
.. math::
58
59
C(x_0,...,x_{n-1})=a_0x_0+...+a_{n-1}x_{n-1},
60
61
62
for some given constants `a_i\in {\bf F}_q`, the map is
63
called a linear feedback shift register (LFSR).
64
65
Example of a LFSR Let
66
67
.. math::
68
69
f(x)=a_{{0}}+a_{{1}}x+...+a_{{n}}{x}^n+...,
70
71
72
73
.. math::
74
75
g(x)=b_{{0}}+b_{{1}}x+...+b_{{n}}{x}^n+...,
76
77
78
be given polynomials in `{\bf F}_2[x]` and let
79
80
.. math::
81
82
h(x)={f(x)\over g(x)}=c_0+c_1x+...+c_nx^n+... \ .
83
84
85
We can compute a recursion formula which allows us to rapidly
86
compute the coefficients of `h(x)` (take `f(x)=1`):
87
88
.. math::
89
90
c_{n}=\sum_{i=1}^n {{-b_i\over b_0}c_{n-i}}.
91
92
93
94
The coefficients of `h(x)` can, under certain conditions on
95
`f(x)` and `g(x)`, be considered "random" from
96
certain statistical points of view.
97
98
Example: For instance, if
99
100
.. math::
101
102
f(x)=1,\ \ \ \ g(x)=x^4+x+1,
103
104
then
105
106
.. math::
107
108
h(x)=1+x+x^2+x^3+x^5+x^7+x^8+...\ .
109
110
The coefficients of `h` are
111
112
.. math::
113
114
\begin{array}{c} 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 1, 0, 0, 0, 1, \\ 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 1, 0, 0, 0, 1, ...\ . \end{array}
115
116
117
The sequence of `0,1`'s is periodic with period
118
`P=2^4-1=15` and satisfies Golomb's three randomness
119
conditions. However, this sequence of period 15 can be "cracked"
120
(i.e., a procedure to reproduce `g(x)`) by knowing only 8
121
terms! This is the function of the Berlekamp-Massey algorithm [M]_,
122
implemented as ``berlekamp_massey.py``.
123
124
.. [G] Solomon Golomb, Shift register sequences, Aegean Park Press,
125
Laguna Hills, Ca, 1967
126
127
.. [M] James L. Massey, "Shift-Register Synthesis and BCH Decoding."
128
IEEE Trans. on Information Theory, vol. 15(1), pp. 122-127, Jan
129
1969.
130
131
AUTHORS:
132
133
- Timothy Brock
134
135
Created 11-24-2005 by wdj. Last updated 12-02-2005.
136
"""
137
138
###########################################################################
139
# Copyright (C) 2006 Timothy Brock
140
# and William Stein <[email protected]>
141
#
142
# Distributed under the terms of the GNU General Public License (GPL)
143
#
144
# http://www.gnu.org/licenses/
145
###########################################################################
146
147
import copy
148
149
from sage.structure.all import Sequence
150
from sage.rings.all import Integer, PolynomialRing
151
from sage.rings.finite_rings.constructor import is_FiniteField
152
153
154
def lfsr_sequence(key, fill, n):
155
r"""
156
This function creates an lfsr sequence.
157
158
INPUT:
159
160
161
- ``key`` - a list of finite field elements,
162
[c_0,c_1,...,c_k].
163
164
- ``fill`` - the list of the initial terms of the lfsr
165
sequence, [x_0,x_1,...,x_k].
166
167
- ``n`` - number of terms of the sequence that the
168
function returns.
169
170
171
OUTPUT: The lfsr sequence defined by
172
`x_{n+1} = c_kx_n+...+c_0x_{n-k}`, for
173
`n \leq k`.
174
175
EXAMPLES::
176
177
sage: F = GF(2); l = F(1); o = F(0)
178
sage: F = GF(2); S = LaurentSeriesRing(F,'x'); x = S.gen()
179
sage: fill = [l,l,o,l]; key = [1,o,o,l]; n = 20
180
sage: L = lfsr_sequence(key,fill,20); L
181
[1, 1, 0, 1, 0, 1, 1, 0, 0, 1, 0, 0, 0, 1, 1, 1, 1, 0, 1, 0]
182
sage: g = berlekamp_massey(L); g
183
x^4 + x^3 + 1
184
sage: (1)/(g.reverse()+O(x^20))
185
1 + x + x^2 + x^3 + x^5 + x^7 + x^8 + x^11 + x^15 + x^16 + x^17 + x^18 + O(x^20)
186
sage: (1+x^2)/(g.reverse()+O(x^20))
187
1 + x + x^4 + x^8 + x^9 + x^10 + x^11 + x^13 + x^15 + x^16 + x^19 + O(x^20)
188
sage: (1+x^2+x^3)/(g.reverse()+O(x^20))
189
1 + x + x^3 + x^5 + x^6 + x^9 + x^13 + x^14 + x^15 + x^16 + x^18 + O(x^20)
190
sage: fill = [l,l,o,l]; key = [l,o,o,o]; n = 20
191
sage: L = lfsr_sequence(key,fill,20); L
192
[1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1]
193
sage: g = berlekamp_massey(L); g
194
x^4 + 1
195
sage: (1+x)/(g.reverse()+O(x^20))
196
1 + x + x^4 + x^5 + x^8 + x^9 + x^12 + x^13 + x^16 + x^17 + O(x^20)
197
sage: (1+x+x^3)/(g.reverse()+O(x^20))
198
1 + x + x^3 + x^4 + x^5 + x^7 + x^8 + x^9 + x^11 + x^12 + x^13 + x^15 + x^16 + x^17 + x^19 + O(x^20)
199
200
AUTHORS:
201
202
- Timothy Brock (2005-11): with code modified from Python
203
Cookbook, http://aspn.activestate.com/ASPN/Python/Cookbook/
204
"""
205
if not isinstance(key, list):
206
raise TypeError, "key must be a list"
207
key = Sequence(key)
208
F = key.universe()
209
if not is_FiniteField(F):
210
raise TypeError, "universe of sequence must be a finite field"
211
212
s = fill
213
k = len(fill)
214
L = []
215
for i in range(n):
216
s0 = copy.copy(s)
217
L.append(s[0])
218
s = s[1:k]
219
s.append(sum([key[i]*s0[i] for i in range(k)]))
220
return L
221
222
def lfsr_autocorrelation(L, p, k):
223
"""
224
INPUT:
225
226
227
- ``L`` - is a periodic sequence of elements of ZZ or
228
GF(2). L must have length = p
229
230
- ``p`` - the period of L
231
232
- ``k`` - k is an integer (0 k p)
233
234
235
OUTPUT: autocorrelation sequence of L
236
237
EXAMPLES::
238
239
sage: F = GF(2)
240
sage: o = F(0)
241
sage: l = F(1)
242
sage: key = [l,o,o,l]; fill = [l,l,o,l]; n = 20
243
sage: s = lfsr_sequence(key,fill,n)
244
sage: lfsr_autocorrelation(s,15,7)
245
4/15
246
sage: lfsr_autocorrelation(s,int(15),7)
247
4/15
248
249
AUTHORS:
250
251
- Timothy Brock (2006-04-17)
252
"""
253
if not isinstance(L, list):
254
raise TypeError, "L (=%s) must be a list"%L
255
p = Integer(p)
256
_p = int(p)
257
k = int(k)
258
L0 = L[:_p] # slices makes a copy
259
L0 = L0 + L0[:k]
260
L1 = [int(L0[i])*int(L0[i + k])/p for i in range(_p)]
261
return sum(L1)
262
263
def lfsr_connection_polynomial(s):
264
"""
265
INPUT:
266
267
268
- ``s`` - a sequence of elements of a finite field (F)
269
of even length
270
271
272
OUTPUT:
273
274
275
- ``C(x)`` - the connection polynomial of the minimal
276
LFSR.
277
278
279
This implements the algorithm in section 3 of J. L. Massey's
280
article [M]_.
281
282
EXAMPLE::
283
284
sage: F = GF(2)
285
sage: F
286
Finite Field of size 2
287
sage: o = F(0); l = F(1)
288
sage: key = [l,o,o,l]; fill = [l,l,o,l]; n = 20
289
sage: s = lfsr_sequence(key,fill,n); s
290
[1, 1, 0, 1, 0, 1, 1, 0, 0, 1, 0, 0, 0, 1, 1, 1, 1, 0, 1, 0]
291
sage: lfsr_connection_polynomial(s)
292
x^4 + x + 1
293
sage: berlekamp_massey(s)
294
x^4 + x^3 + 1
295
296
Notice that ``berlekamp_massey`` returns the reverse
297
of the connection polynomial (and is potentially must faster than
298
this implementation).
299
300
AUTHORS:
301
302
- Timothy Brock (2006-04-17)
303
"""
304
# Initialization:
305
FF = s[0].base_ring()
306
R = PolynomialRing(FF, "x")
307
x = R.gen()
308
C = R(1); B = R(1); m = 1; b = FF(1); L = 0; N = 0
309
310
while N < len(s):
311
if L > 0:
312
r = min(L+1,C.degree()+1)
313
d = s[N] + sum([(C.list())[i]*s[N-i] for i in range(1,r)])
314
if L == 0:
315
d = s[N]
316
if d == 0:
317
m += 1
318
N += 1
319
if d > 0:
320
if 2*L > N:
321
C = C - d*b**(-1)*x**m*B
322
m += 1
323
N += 1
324
else:
325
T = C
326
C = C - d*b**(-1)*x**m*B
327
L = N + 1 - L
328
m = 1
329
b = d
330
B = T
331
N += 1
332
return C
333
334