blob: de821e89d6a44dc6c51ac5be5b8b9a7b717b1277 [file] [log] [blame]
Sylvain Munautf1d33442011-04-23 15:34:11 +02001/*
2 * a5.c
3 *
4 * Full reimplementation of A5/1,2 (split and threadsafe)
5 *
6 * The logic behind the algorithm is taken from "A pedagogical implementation
7 * of the GSM A5/1 and A5/2 "voice privacy" encryption algorithms." by
8 * Marc Briceno, Ian Goldberg, and David Wagner.
9 *
10 * Copyright (C) 2011 Sylvain Munaut <tnt@246tNt.com>
11 *
12 * All Rights Reserved
13 *
14 * This program is free software; you can redistribute it and/or modify
15 * it under the terms of the GNU General Public License as published by
16 * the Free Software Foundation; either version 2 of the License, or
17 * (at your option) any later version.
18 *
19 * This program is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 * GNU General Public License for more details.
23 *
24 * You should have received a copy of the GNU General Public License along
25 * with this program; if not, write to the Free Software Foundation, Inc.,
26 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
27 */
28
Sylvain Munaut2735ac42011-11-17 21:01:46 +010029/*! \addtogroup a5
30 * @{
31 */
32
33/*! \file gsm/a5.c
34 * \brief Osmocom GSM A5 ciphering algorithm implementation
35 */
36
Sylvain Munautc1e9be92012-12-06 08:23:02 +010037#include <errno.h>
Sylvain Munautf1d33442011-04-23 15:34:11 +020038#include <string.h>
39
40#include <osmocom/gsm/a5.h>
41
Sylvain Munaut2735ac42011-11-17 21:01:46 +010042/*! \brief Main method to generate a A5/x cipher stream
43 * \param[in] n Which A5/x method to use
44 * \param[in] key 8 byte array for the key (as received from the SIM)
45 * \param[in] fn Frame number
46 * \param[out] dl Pointer to array of ubits to return Downlink cipher stream
47 * \param[out] ul Pointer to array of ubits to return Uplink cipher stream
Sylvain Munautc1e9be92012-12-06 08:23:02 +010048 * \returns 0 for success, -ENOTSUP for invalid cipher selection.
Sylvain Munaut2735ac42011-11-17 21:01:46 +010049 *
50 * Currently A5/[0-2] are supported.
51 * Either (or both) of dl/ul can be NULL if not needed.
52 */
Sylvain Munautc1e9be92012-12-06 08:23:02 +010053int
Sylvain Munaut3e387cb2011-11-17 19:45:42 +010054osmo_a5(int n, const uint8_t *key, uint32_t fn, ubit_t *dl, ubit_t *ul)
Sylvain Munautf1d33442011-04-23 15:34:11 +020055{
56 switch (n)
57 {
58 case 0:
59 if (dl)
60 memset(dl, 0x00, 114);
61 if (ul)
62 memset(ul, 0x00, 114);
63 break;
64
65 case 1:
66 osmo_a5_1(key, fn, dl, ul);
67 break;
68
69 case 2:
70 osmo_a5_2(key, fn, dl, ul);
71 break;
72
73 default:
74 /* a5/[3..7] not supported here/yet */
Sylvain Munautc1e9be92012-12-06 08:23:02 +010075 return -ENOTSUP;
Sylvain Munautf1d33442011-04-23 15:34:11 +020076 }
Sylvain Munautc1e9be92012-12-06 08:23:02 +010077
78 return 0;
Sylvain Munautf1d33442011-04-23 15:34:11 +020079}
80
81
82/* ------------------------------------------------------------------------ */
83/* A5/1&2 common stuff */
84/* ------------------------------------------------------------------------ */
85
86#define A5_R1_LEN 19
87#define A5_R2_LEN 22
88#define A5_R3_LEN 23
89#define A5_R4_LEN 17 /* A5/2 only */
90
91#define A5_R1_MASK ((1<<A5_R1_LEN)-1)
92#define A5_R2_MASK ((1<<A5_R2_LEN)-1)
93#define A5_R3_MASK ((1<<A5_R3_LEN)-1)
94#define A5_R4_MASK ((1<<A5_R4_LEN)-1)
95
Sylvain Munaut2dafed52012-03-01 19:54:02 +010096#define A5_R1_TAPS 0x072000 /* x^19 + x^18 + x^17 + x^14 + 1 */
97#define A5_R2_TAPS 0x300000 /* x^22 + x^21 + 1 */
98#define A5_R3_TAPS 0x700080 /* x^23 + x^22 + x^21 + x^8 + 1 */
99#define A5_R4_TAPS 0x010800 /* x^17 + x^12 + 1 */
Sylvain Munautf1d33442011-04-23 15:34:11 +0200100
Sylvain Munaut2735ac42011-11-17 21:01:46 +0100101/*! \brief Computes parity of a 32-bit word
102 * \param[in] x 32 bit word
103 * \return Parity bit (xor of all bits) as 0 or 1
104 */
Sylvain Munautf1d33442011-04-23 15:34:11 +0200105static inline uint32_t
106_a5_12_parity(uint32_t x)
107{
108 x ^= x >> 16;
109 x ^= x >> 8;
110 x ^= x >> 4;
Sylvain Munautad4a6a82011-11-20 08:46:56 +0100111 x &= 0xf;
112 return (0x6996 >> x) & 1;
Sylvain Munautf1d33442011-04-23 15:34:11 +0200113}
114
Sylvain Munaut2735ac42011-11-17 21:01:46 +0100115/*! \brief Compute majority bit from 3 taps
116 * \param[in] v1 LFSR state ANDed with tap-bit
117 * \param[in] v2 LFSR state ANDed with tap-bit
118 * \param[in] v3 LFSR state ANDed with tap-bit
119 * \return The majority bit (0 or 1)
120 */
Sylvain Munautf1d33442011-04-23 15:34:11 +0200121static inline uint32_t
122_a5_12_majority(uint32_t v1, uint32_t v2, uint32_t v3)
123{
124 return (!!v1 + !!v2 + !!v3) >= 2;
125}
126
Sylvain Munaut2735ac42011-11-17 21:01:46 +0100127/*! \brief Compute the next LFSR state
128 * \param[in] r Current state
129 * \param[in] mask LFSR mask
130 * \param[in] taps LFSR taps
131 * \return Next state
132 */
Sylvain Munautf1d33442011-04-23 15:34:11 +0200133static inline uint32_t
134_a5_12_clock(uint32_t r, uint32_t mask, uint32_t taps)
135{
136 return ((r << 1) & mask) | _a5_12_parity(r & taps);
137}
138
139
140/* ------------------------------------------------------------------------ */
141/* A5/1 */
142/* ------------------------------------------------------------------------ */
143
144#define A51_R1_CLKBIT 0x000100
145#define A51_R2_CLKBIT 0x000400
146#define A51_R3_CLKBIT 0x000400
147
Sylvain Munaut2735ac42011-11-17 21:01:46 +0100148/*! \brief GSM A5/1 Clocking function
149 * \param[in] r Register state
150 * \param[in] force Non-zero value disable conditional clocking
151 */
Sylvain Munautf1d33442011-04-23 15:34:11 +0200152static inline void
153_a5_1_clock(uint32_t r[], int force)
154{
155 int cb[3], maj;
156
157 cb[0] = !!(r[0] & A51_R1_CLKBIT);
158 cb[1] = !!(r[1] & A51_R2_CLKBIT);
159 cb[2] = !!(r[2] & A51_R3_CLKBIT);
160
161 maj = _a5_12_majority(cb[0], cb[1], cb[2]);
162
163 if (force || (maj == cb[0]))
164 r[0] = _a5_12_clock(r[0], A5_R1_MASK, A5_R1_TAPS);
165
166 if (force || (maj == cb[1]))
167 r[1] = _a5_12_clock(r[1], A5_R2_MASK, A5_R2_TAPS);
168
169 if (force || (maj == cb[2]))
170 r[2] = _a5_12_clock(r[2], A5_R3_MASK, A5_R3_TAPS);
171}
172
Sylvain Munaut2735ac42011-11-17 21:01:46 +0100173/*! \brief GSM A5/1 Output function
174 * \param[in] r Register state
175 * \return The A5/1 output function bit
176 */
Sylvain Munautf1d33442011-04-23 15:34:11 +0200177static inline uint8_t
178_a5_1_get_output(uint32_t r[])
179{
180 return (r[0] >> (A5_R1_LEN-1)) ^
181 (r[1] >> (A5_R2_LEN-1)) ^
182 (r[2] >> (A5_R3_LEN-1));
183}
184
Sylvain Munaut2735ac42011-11-17 21:01:46 +0100185/*! \brief Generate a GSM A5/1 cipher stream
186 * \param[in] key 8 byte array for the key (as received from the SIM)
187 * \param[in] fn Frame number
188 * \param[out] dl Pointer to array of ubits to return Downlink cipher stream
189 * \param[out] ul Pointer to array of ubits to return Uplink cipher stream
190 *
191 * Either (or both) of dl/ul can be NULL if not needed.
192 */
Sylvain Munautf1d33442011-04-23 15:34:11 +0200193void
Sylvain Munaut3e387cb2011-11-17 19:45:42 +0100194osmo_a5_1(const uint8_t *key, uint32_t fn, ubit_t *dl, ubit_t *ul)
Sylvain Munautf1d33442011-04-23 15:34:11 +0200195{
196 uint32_t r[3] = {0, 0, 0};
197 uint32_t fn_count;
198 uint32_t b;
199 int i;
200
201 /* Key load */
202 for (i=0; i<64; i++)
203 {
204 b = ( key[7 - (i>>3)] >> (i&7) ) & 1;
205
206 _a5_1_clock(r, 1);
207
208 r[0] ^= b;
209 r[1] ^= b;
210 r[2] ^= b;
211 }
212
213 /* Frame count load */
214 fn_count = osmo_a5_fn_count(fn);
215
216 for (i=0; i<22; i++)
217 {
218 b = (fn_count >> i) & 1;
219
220 _a5_1_clock(r, 1);
221
222 r[0] ^= b;
223 r[1] ^= b;
224 r[2] ^= b;
225 }
226
227 /* Mix */
228 for (i=0; i<100; i++)
229 {
230 _a5_1_clock(r, 0);
231 }
232
233 /* Output */
234 for (i=0; i<114; i++) {
235 _a5_1_clock(r, 0);
236 if (dl)
237 dl[i] = _a5_1_get_output(r);
238 }
239
240 for (i=0; i<114; i++) {
241 _a5_1_clock(r, 0);
242 if (ul)
243 ul[i] = _a5_1_get_output(r);
244 }
245}
246
247
248/* ------------------------------------------------------------------------ */
249/* A5/2 */
250/* ------------------------------------------------------------------------ */
251
252#define A52_R4_CLKBIT0 0x000400
253#define A52_R4_CLKBIT1 0x000008
254#define A52_R4_CLKBIT2 0x000080
255
Sylvain Munaut2735ac42011-11-17 21:01:46 +0100256/*! \brief GSM A5/2 Clocking function
257 * \param[in] r Register state
258 * \param[in] force Non-zero value disable conditional clocking
259 */
Sylvain Munautf1d33442011-04-23 15:34:11 +0200260static inline void
261_a5_2_clock(uint32_t r[], int force)
262{
263 int cb[3], maj;
264
265 cb[0] = !!(r[3] & A52_R4_CLKBIT0);
266 cb[1] = !!(r[3] & A52_R4_CLKBIT1);
267 cb[2] = !!(r[3] & A52_R4_CLKBIT2);
268
269 maj = (cb[0] + cb[1] + cb[2]) >= 2;
270
271 if (force || (maj == cb[0]))
272 r[0] = _a5_12_clock(r[0], A5_R1_MASK, A5_R1_TAPS);
273
274 if (force || (maj == cb[1]))
275 r[1] = _a5_12_clock(r[1], A5_R2_MASK, A5_R2_TAPS);
276
277 if (force || (maj == cb[2]))
278 r[2] = _a5_12_clock(r[2], A5_R3_MASK, A5_R3_TAPS);
279
280 r[3] = _a5_12_clock(r[3], A5_R4_MASK, A5_R4_TAPS);
281}
282
Sylvain Munaut2735ac42011-11-17 21:01:46 +0100283/*! \brief GSM A5/2 Output function
284 * \param[in] r Register state
285 * \return The A5/2 output function bit
286 */
Sylvain Munautf1d33442011-04-23 15:34:11 +0200287static inline uint8_t
Sylvain Munaut7f975d22011-11-17 20:36:50 +0100288_a5_2_get_output(uint32_t r[])
Sylvain Munautf1d33442011-04-23 15:34:11 +0200289{
Sylvain Munaut7f975d22011-11-17 20:36:50 +0100290 uint8_t b;
Sylvain Munautf1d33442011-04-23 15:34:11 +0200291
Sylvain Munaut7f975d22011-11-17 20:36:50 +0100292 b = (r[0] >> (A5_R1_LEN-1)) ^
293 (r[1] >> (A5_R2_LEN-1)) ^
294 (r[2] >> (A5_R3_LEN-1)) ^
295 _a5_12_majority( r[0] & 0x08000, ~r[0] & 0x04000, r[0] & 0x1000) ^
296 _a5_12_majority(~r[1] & 0x10000, r[1] & 0x02000, r[1] & 0x0200) ^
297 _a5_12_majority( r[2] & 0x40000, r[2] & 0x10000, ~r[2] & 0x2000);
Sylvain Munautf1d33442011-04-23 15:34:11 +0200298
Sylvain Munaut7f975d22011-11-17 20:36:50 +0100299 return b;
Sylvain Munautf1d33442011-04-23 15:34:11 +0200300}
301
Sylvain Munaut2735ac42011-11-17 21:01:46 +0100302/*! \brief Generate a GSM A5/1 cipher stream
303 * \param[in] key 8 byte array for the key (as received from the SIM)
304 * \param[in] fn Frame number
305 * \param[out] dl Pointer to array of ubits to return Downlink cipher stream
306 * \param[out] ul Pointer to array of ubits to return Uplink cipher stream
307 *
308 * Either (or both) of dl/ul can be NULL if not needed.
309 */
Sylvain Munautf1d33442011-04-23 15:34:11 +0200310void
Sylvain Munaut3e387cb2011-11-17 19:45:42 +0100311osmo_a5_2(const uint8_t *key, uint32_t fn, ubit_t *dl, ubit_t *ul)
Sylvain Munautf1d33442011-04-23 15:34:11 +0200312{
313 uint32_t r[4] = {0, 0, 0, 0};
314 uint32_t fn_count;
315 uint32_t b;
Sylvain Munautf1d33442011-04-23 15:34:11 +0200316 int i;
317
318 /* Key load */
319 for (i=0; i<64; i++)
320 {
321 b = ( key[7 - (i>>3)] >> (i&7) ) & 1;
322
323 _a5_2_clock(r, 1);
324
325 r[0] ^= b;
326 r[1] ^= b;
327 r[2] ^= b;
328 r[3] ^= b;
329 }
330
331 /* Frame count load */
332 fn_count = osmo_a5_fn_count(fn);
333
334 for (i=0; i<22; i++)
335 {
336 b = (fn_count >> i) & 1;
337
338 _a5_2_clock(r, 1);
339
340 r[0] ^= b;
341 r[1] ^= b;
342 r[2] ^= b;
343 r[3] ^= b;
344 }
345
346 r[0] |= 1 << 15;
347 r[1] |= 1 << 16;
348 r[2] |= 1 << 18;
349 r[3] |= 1 << 10;
350
351 /* Mix */
Sylvain Munaut7f975d22011-11-17 20:36:50 +0100352 for (i=0; i<99; i++)
Sylvain Munautf1d33442011-04-23 15:34:11 +0200353 {
354 _a5_2_clock(r, 0);
355 }
356
Sylvain Munautf1d33442011-04-23 15:34:11 +0200357 /* Output */
358 for (i=0; i<114; i++) {
359 _a5_2_clock(r, 0);
Sylvain Munautf1d33442011-04-23 15:34:11 +0200360 if (dl)
Sylvain Munaut7f975d22011-11-17 20:36:50 +0100361 dl[i] = _a5_2_get_output(r);
Sylvain Munautf1d33442011-04-23 15:34:11 +0200362 }
363
364 for (i=0; i<114; i++) {
365 _a5_2_clock(r, 0);
Sylvain Munautf1d33442011-04-23 15:34:11 +0200366 if (ul)
Sylvain Munaut7f975d22011-11-17 20:36:50 +0100367 ul[i] = _a5_2_get_output(r);
Sylvain Munautf1d33442011-04-23 15:34:11 +0200368 }
369}
Sylvain Munaut2735ac42011-11-17 21:01:46 +0100370
Sylvain Munautdca7d2c2012-04-18 21:53:23 +0200371/*! @} */