|
1 | 1 | # Prefix |
2 | 2 |
|
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 | +``` |
4 | 133 |
|
5 | 134 | Overview |
6 | 135 | -------- |
|
0 commit comments