Skip to content

Commit f88b8f6

Browse files
Add Prefix example (LCG cracker) to README.md
1 parent 0c894ae commit f88b8f6

1 file changed

Lines changed: 130 additions & 1 deletion

File tree

README.md

Lines changed: 130 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,135 @@
11
# Prefix
22

3-
C17 implementation of the Prefix programming language.
3+
Prefix is a programming language focused on explicit, readable code.
4+
5+
```prefix
6+
! LCG cracker.
7+
! Recovers the modulus m, multiplier a, and constant c of a linear
8+
! congruential generator from a short run of consecutive outputs, then
9+
! predicts the next value.
10+
11+
! ---------- modular inverse via the extended Euclidean algorithm ----------
12+
FUNC INT modinv(INT a, INT m){
13+
a = MOD(a, m)
14+
IF(LT(a, 0d0)){ a = +(a, m) }
15+
INT r0 = m
16+
INT r1 = a
17+
INT t0 = 0d0
18+
INT t1 = 0d1
19+
WHILE(NEQ(r1, 0d0)){
20+
INT q = /(r0, r1)
21+
INT nr = -(r0, *(q, r1))
22+
INT nt = -(t0, *(q, t1))
23+
r0 = r1
24+
r1 = nr
25+
t0 = t1
26+
t1 = nt
27+
}
28+
IF(NEQ(r0, 0d1)){
29+
THROW("modinv: arguments not coprime")
30+
}
31+
INT inv = MOD(t0, m)
32+
IF(LT(inv, 0d0)){ inv = +(inv, m) }
33+
RETURN(inv)
34+
}
35+
36+
! ---------- the hidden LCG (the secret being cracked) ----------
37+
! x_{n+1} = (a * x_n + c) mod m
38+
! The modulus is kept below 2^31 so that products of two consecutive
39+
! differences (each < m) never overflow Prefix's signed 64-bit INT.
40+
INT m = 0x186A1
41+
INT a = 0d16807
42+
INT c = 0d12345
43+
INT seed = 0d48271
44+
45+
FUNC INT step(INT s){
46+
RETURN(MOD(+( *(a, s), c), m))
47+
}
48+
49+
! ---------- collect observed outputs (all the attacker sees) ----------
50+
MAP obs
51+
INT s = seed
52+
FOR(i, 0d8){
53+
obs<i> = s
54+
s = step(s)
55+
}
56+
DEL("seed")
57+
DEL("s")
58+
59+
! ---------- recover the modulus m ----------
60+
! t_i = x_{i+1} - x_i follows t_{i+1} = a * t_i (mod m), so each
61+
! u_k = t_{k+1} * t_{k-1} - t_k^2 is a multiple of m. GCD of several
62+
! |u_k| yields m.
63+
MAP d
64+
FOR(i, 0d7){
65+
d<i> = -(obs<+(i, 0d1)>, obs<i>)
66+
}
67+
68+
INT mrec = 0d0
69+
INT k = 0d2
70+
WHILE(LTE(k, 0d6)){
71+
INT lo = -(k, 0d1)
72+
INT hi = +(k, 0d1)
73+
INT tk = d<k>
74+
mrec = GCD(mrec, ABS(-( *(d<hi>, d<lo>), *(tk, tk) )))
75+
k = +(k, 0d1)
76+
}
77+
DEL("d")
78+
DEL("k")
79+
80+
! ---------- recover a and c ----------
81+
! a = (x2 - x1) * (x1 - x0)^{-1} (mod m)
82+
! c = x1 - a * x0 (mod m)
83+
INT x0 = obs<0d1>
84+
INT x1 = obs<0d2>
85+
INT x2 = obs<0d3>
86+
INT arec = MOD(*( -(x2, x1), modinv(-(x1, x0), mrec) ), mrec)
87+
INT crec = MOD(-(x1, *(arec, x0)), mrec)
88+
IF(LT(arec, 0d0)){ arec = +(arec, mrec) }
89+
IF(LT(crec, 0d0)){ crec = +(crec, mrec) }
90+
DEL("r0")
91+
DEL("r1")
92+
DEL("t0")
93+
DEL("t1")
94+
DEL("q")
95+
DEL("nr")
96+
DEL("nt")
97+
DEL("inv")
98+
DEL("x2")
99+
DEL("x1")
100+
DEL("modinv")
101+
102+
! ---------- verify by regenerating the whole sequence ----------
103+
INT ok = 0d1
104+
INT v = x0
105+
FOR(i, 0d8){
106+
IF(NEQ(v, obs<i>)){
107+
ok = 0d0
108+
}
109+
v = MOD(+( *(arec, v), crec), mrec)
110+
}
111+
DEL("x0")
112+
DEL("v")
113+
114+
! ---------- report ----------
115+
PRINT("hidden : m=", m, " a=", a, " c=", c)
116+
PRINT("cracked: m=", mrec, " a=", arec, " c=", crec)
117+
PRINT("sequence reproduced: ", ok)
118+
119+
! ---------- predict the next output ----------
120+
INT next_pred = MOD(+( *(arec, obs<0d8>), crec), mrec)
121+
INT next_true = step(obs<0d8>)
122+
DEL("obs")
123+
DEL("step")
124+
PRINT("next output predicted: ", next_pred)
125+
PRINT("next output actual : ", next_true)
126+
127+
ASSERT(EQ(ok, 0d1))
128+
ASSERT(EQ(mrec, m))
129+
ASSERT(EQ(arec, a))
130+
ASSERT(EQ(crec, c))
131+
ASSERT(EQ(next_pred, next_true))
132+
```
4133

5134
Overview
6135
--------

0 commit comments

Comments
 (0)