blob: 10a063e27ce62dd73eac5a80087738e02234c719 [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
Harald Weltee15ac062014-12-04 14:15:36 +010042/* Somme OS (like Nuttx) don't have ENOTSUP */
43#ifndef ENOTSUP
44#define ENOTSUP EINVAL
45#endif
46
Sylvain Munaut2735ac42011-11-17 21:01:46 +010047/*! \brief Main method to generate a A5/x cipher stream
48 * \param[in] n Which A5/x method to use
49 * \param[in] key 8 byte array for the key (as received from the SIM)
50 * \param[in] fn Frame number
51 * \param[out] dl Pointer to array of ubits to return Downlink cipher stream
52 * \param[out] ul Pointer to array of ubits to return Uplink cipher stream
Sylvain Munautc1e9be92012-12-06 08:23:02 +010053 * \returns 0 for success, -ENOTSUP for invalid cipher selection.
Sylvain Munaut2735ac42011-11-17 21:01:46 +010054 *
55 * Currently A5/[0-2] are supported.
56 * Either (or both) of dl/ul can be NULL if not needed.
57 */
Sylvain Munautc1e9be92012-12-06 08:23:02 +010058int
Sylvain Munaut3e387cb2011-11-17 19:45:42 +010059osmo_a5(int n, const uint8_t *key, uint32_t fn, ubit_t *dl, ubit_t *ul)
Sylvain Munautf1d33442011-04-23 15:34:11 +020060{
61 switch (n)
62 {
63 case 0:
64 if (dl)
65 memset(dl, 0x00, 114);
66 if (ul)
67 memset(ul, 0x00, 114);
68 break;
69
70 case 1:
71 osmo_a5_1(key, fn, dl, ul);
72 break;
73
74 case 2:
75 osmo_a5_2(key, fn, dl, ul);
76 break;
77
78 default:
79 /* a5/[3..7] not supported here/yet */
Sylvain Munautc1e9be92012-12-06 08:23:02 +010080 return -ENOTSUP;
Sylvain Munautf1d33442011-04-23 15:34:11 +020081 }
Sylvain Munautc1e9be92012-12-06 08:23:02 +010082
83 return 0;
Sylvain Munautf1d33442011-04-23 15:34:11 +020084}
85
86
87/* ------------------------------------------------------------------------ */
88/* A5/1&2 common stuff */
89/* ------------------------------------------------------------------------ */
90
91#define A5_R1_LEN 19
92#define A5_R2_LEN 22
93#define A5_R3_LEN 23
94#define A5_R4_LEN 17 /* A5/2 only */
95
96#define A5_R1_MASK ((1<<A5_R1_LEN)-1)
97#define A5_R2_MASK ((1<<A5_R2_LEN)-1)
98#define A5_R3_MASK ((1<<A5_R3_LEN)-1)
99#define A5_R4_MASK ((1<<A5_R4_LEN)-1)
100
Sylvain Munaut2dafed52012-03-01 19:54:02 +0100101#define A5_R1_TAPS 0x072000 /* x^19 + x^18 + x^17 + x^14 + 1 */
102#define A5_R2_TAPS 0x300000 /* x^22 + x^21 + 1 */
103#define A5_R3_TAPS 0x700080 /* x^23 + x^22 + x^21 + x^8 + 1 */
104#define A5_R4_TAPS 0x010800 /* x^17 + x^12 + 1 */
Sylvain Munautf1d33442011-04-23 15:34:11 +0200105
Sylvain Munaut2735ac42011-11-17 21:01:46 +0100106/*! \brief Computes parity of a 32-bit word
107 * \param[in] x 32 bit word
108 * \return Parity bit (xor of all bits) as 0 or 1
109 */
Sylvain Munautf1d33442011-04-23 15:34:11 +0200110static inline uint32_t
111_a5_12_parity(uint32_t x)
112{
113 x ^= x >> 16;
114 x ^= x >> 8;
115 x ^= x >> 4;
Sylvain Munautad4a6a82011-11-20 08:46:56 +0100116 x &= 0xf;
117 return (0x6996 >> x) & 1;
Sylvain Munautf1d33442011-04-23 15:34:11 +0200118}
119
Sylvain Munaut2735ac42011-11-17 21:01:46 +0100120/*! \brief Compute majority bit from 3 taps
121 * \param[in] v1 LFSR state ANDed with tap-bit
122 * \param[in] v2 LFSR state ANDed with tap-bit
123 * \param[in] v3 LFSR state ANDed with tap-bit
124 * \return The majority bit (0 or 1)
125 */
Sylvain Munautf1d33442011-04-23 15:34:11 +0200126static inline uint32_t
127_a5_12_majority(uint32_t v1, uint32_t v2, uint32_t v3)
128{
129 return (!!v1 + !!v2 + !!v3) >= 2;
130}
131
Sylvain Munaut2735ac42011-11-17 21:01:46 +0100132/*! \brief Compute the next LFSR state
133 * \param[in] r Current state
134 * \param[in] mask LFSR mask
135 * \param[in] taps LFSR taps
136 * \return Next state
137 */
Sylvain Munautf1d33442011-04-23 15:34:11 +0200138static inline uint32_t
139_a5_12_clock(uint32_t r, uint32_t mask, uint32_t taps)
140{
141 return ((r << 1) & mask) | _a5_12_parity(r & taps);
142}
143
144
145/* ------------------------------------------------------------------------ */
146/* A5/1 */
147/* ------------------------------------------------------------------------ */
148
149#define A51_R1_CLKBIT 0x000100
150#define A51_R2_CLKBIT 0x000400
151#define A51_R3_CLKBIT 0x000400
152
Sylvain Munaut2735ac42011-11-17 21:01:46 +0100153/*! \brief GSM A5/1 Clocking function
154 * \param[in] r Register state
155 * \param[in] force Non-zero value disable conditional clocking
156 */
Sylvain Munautf1d33442011-04-23 15:34:11 +0200157static inline void
158_a5_1_clock(uint32_t r[], int force)
159{
160 int cb[3], maj;
161
162 cb[0] = !!(r[0] & A51_R1_CLKBIT);
163 cb[1] = !!(r[1] & A51_R2_CLKBIT);
164 cb[2] = !!(r[2] & A51_R3_CLKBIT);
165
166 maj = _a5_12_majority(cb[0], cb[1], cb[2]);
167
168 if (force || (maj == cb[0]))
169 r[0] = _a5_12_clock(r[0], A5_R1_MASK, A5_R1_TAPS);
170
171 if (force || (maj == cb[1]))
172 r[1] = _a5_12_clock(r[1], A5_R2_MASK, A5_R2_TAPS);
173
174 if (force || (maj == cb[2]))
175 r[2] = _a5_12_clock(r[2], A5_R3_MASK, A5_R3_TAPS);
176}
177
Sylvain Munaut2735ac42011-11-17 21:01:46 +0100178/*! \brief GSM A5/1 Output function
179 * \param[in] r Register state
180 * \return The A5/1 output function bit
181 */
Sylvain Munautf1d33442011-04-23 15:34:11 +0200182static inline uint8_t
183_a5_1_get_output(uint32_t r[])
184{
185 return (r[0] >> (A5_R1_LEN-1)) ^
186 (r[1] >> (A5_R2_LEN-1)) ^
187 (r[2] >> (A5_R3_LEN-1));
188}
189
Sylvain Munaut2735ac42011-11-17 21:01:46 +0100190/*! \brief Generate a GSM A5/1 cipher stream
191 * \param[in] key 8 byte array for the key (as received from the SIM)
192 * \param[in] fn Frame number
193 * \param[out] dl Pointer to array of ubits to return Downlink cipher stream
194 * \param[out] ul Pointer to array of ubits to return Uplink cipher stream
195 *
196 * Either (or both) of dl/ul can be NULL if not needed.
197 */
Sylvain Munautf1d33442011-04-23 15:34:11 +0200198void
Sylvain Munaut3e387cb2011-11-17 19:45:42 +0100199osmo_a5_1(const uint8_t *key, uint32_t fn, ubit_t *dl, ubit_t *ul)
Sylvain Munautf1d33442011-04-23 15:34:11 +0200200{
201 uint32_t r[3] = {0, 0, 0};
202 uint32_t fn_count;
203 uint32_t b;
204 int i;
205
206 /* Key load */
207 for (i=0; i<64; i++)
208 {
209 b = ( key[7 - (i>>3)] >> (i&7) ) & 1;
210
211 _a5_1_clock(r, 1);
212
213 r[0] ^= b;
214 r[1] ^= b;
215 r[2] ^= b;
216 }
217
218 /* Frame count load */
219 fn_count = osmo_a5_fn_count(fn);
220
221 for (i=0; i<22; i++)
222 {
223 b = (fn_count >> i) & 1;
224
225 _a5_1_clock(r, 1);
226
227 r[0] ^= b;
228 r[1] ^= b;
229 r[2] ^= b;
230 }
231
232 /* Mix */
233 for (i=0; i<100; i++)
234 {
235 _a5_1_clock(r, 0);
236 }
237
238 /* Output */
239 for (i=0; i<114; i++) {
240 _a5_1_clock(r, 0);
241 if (dl)
242 dl[i] = _a5_1_get_output(r);
243 }
244
245 for (i=0; i<114; i++) {
246 _a5_1_clock(r, 0);
247 if (ul)
248 ul[i] = _a5_1_get_output(r);
249 }
250}
251
252
253/* ------------------------------------------------------------------------ */
254/* A5/2 */
255/* ------------------------------------------------------------------------ */
256
257#define A52_R4_CLKBIT0 0x000400
258#define A52_R4_CLKBIT1 0x000008
259#define A52_R4_CLKBIT2 0x000080
260
Sylvain Munaut2735ac42011-11-17 21:01:46 +0100261/*! \brief GSM A5/2 Clocking function
262 * \param[in] r Register state
263 * \param[in] force Non-zero value disable conditional clocking
264 */
Sylvain Munautf1d33442011-04-23 15:34:11 +0200265static inline void
266_a5_2_clock(uint32_t r[], int force)
267{
268 int cb[3], maj;
269
270 cb[0] = !!(r[3] & A52_R4_CLKBIT0);
271 cb[1] = !!(r[3] & A52_R4_CLKBIT1);
272 cb[2] = !!(r[3] & A52_R4_CLKBIT2);
273
274 maj = (cb[0] + cb[1] + cb[2]) >= 2;
275
276 if (force || (maj == cb[0]))
277 r[0] = _a5_12_clock(r[0], A5_R1_MASK, A5_R1_TAPS);
278
279 if (force || (maj == cb[1]))
280 r[1] = _a5_12_clock(r[1], A5_R2_MASK, A5_R2_TAPS);
281
282 if (force || (maj == cb[2]))
283 r[2] = _a5_12_clock(r[2], A5_R3_MASK, A5_R3_TAPS);
284
285 r[3] = _a5_12_clock(r[3], A5_R4_MASK, A5_R4_TAPS);
286}
287
Sylvain Munaut2735ac42011-11-17 21:01:46 +0100288/*! \brief GSM A5/2 Output function
289 * \param[in] r Register state
290 * \return The A5/2 output function bit
291 */
Sylvain Munautf1d33442011-04-23 15:34:11 +0200292static inline uint8_t
Sylvain Munaut7f975d22011-11-17 20:36:50 +0100293_a5_2_get_output(uint32_t r[])
Sylvain Munautf1d33442011-04-23 15:34:11 +0200294{
Sylvain Munaut7f975d22011-11-17 20:36:50 +0100295 uint8_t b;
Sylvain Munautf1d33442011-04-23 15:34:11 +0200296
Sylvain Munaut7f975d22011-11-17 20:36:50 +0100297 b = (r[0] >> (A5_R1_LEN-1)) ^
298 (r[1] >> (A5_R2_LEN-1)) ^
299 (r[2] >> (A5_R3_LEN-1)) ^
300 _a5_12_majority( r[0] & 0x08000, ~r[0] & 0x04000, r[0] & 0x1000) ^
301 _a5_12_majority(~r[1] & 0x10000, r[1] & 0x02000, r[1] & 0x0200) ^
302 _a5_12_majority( r[2] & 0x40000, r[2] & 0x10000, ~r[2] & 0x2000);
Sylvain Munautf1d33442011-04-23 15:34:11 +0200303
Sylvain Munaut7f975d22011-11-17 20:36:50 +0100304 return b;
Sylvain Munautf1d33442011-04-23 15:34:11 +0200305}
306
Sylvain Munaut2735ac42011-11-17 21:01:46 +0100307/*! \brief Generate a GSM A5/1 cipher stream
308 * \param[in] key 8 byte array for the key (as received from the SIM)
309 * \param[in] fn Frame number
310 * \param[out] dl Pointer to array of ubits to return Downlink cipher stream
311 * \param[out] ul Pointer to array of ubits to return Uplink cipher stream
312 *
313 * Either (or both) of dl/ul can be NULL if not needed.
314 */
Sylvain Munautf1d33442011-04-23 15:34:11 +0200315void
Sylvain Munaut3e387cb2011-11-17 19:45:42 +0100316osmo_a5_2(const uint8_t *key, uint32_t fn, ubit_t *dl, ubit_t *ul)
Sylvain Munautf1d33442011-04-23 15:34:11 +0200317{
318 uint32_t r[4] = {0, 0, 0, 0};
319 uint32_t fn_count;
320 uint32_t b;
Sylvain Munautf1d33442011-04-23 15:34:11 +0200321 int i;
322
323 /* Key load */
324 for (i=0; i<64; i++)
325 {
326 b = ( key[7 - (i>>3)] >> (i&7) ) & 1;
327
328 _a5_2_clock(r, 1);
329
330 r[0] ^= b;
331 r[1] ^= b;
332 r[2] ^= b;
333 r[3] ^= b;
334 }
335
336 /* Frame count load */
337 fn_count = osmo_a5_fn_count(fn);
338
339 for (i=0; i<22; i++)
340 {
341 b = (fn_count >> i) & 1;
342
343 _a5_2_clock(r, 1);
344
345 r[0] ^= b;
346 r[1] ^= b;
347 r[2] ^= b;
348 r[3] ^= b;
349 }
350
351 r[0] |= 1 << 15;
352 r[1] |= 1 << 16;
353 r[2] |= 1 << 18;
354 r[3] |= 1 << 10;
355
356 /* Mix */
Sylvain Munaut7f975d22011-11-17 20:36:50 +0100357 for (i=0; i<99; i++)
Sylvain Munautf1d33442011-04-23 15:34:11 +0200358 {
359 _a5_2_clock(r, 0);
360 }
361
Sylvain Munautf1d33442011-04-23 15:34:11 +0200362 /* Output */
363 for (i=0; i<114; i++) {
364 _a5_2_clock(r, 0);
Sylvain Munautf1d33442011-04-23 15:34:11 +0200365 if (dl)
Sylvain Munaut7f975d22011-11-17 20:36:50 +0100366 dl[i] = _a5_2_get_output(r);
Sylvain Munautf1d33442011-04-23 15:34:11 +0200367 }
368
369 for (i=0; i<114; i++) {
370 _a5_2_clock(r, 0);
Sylvain Munautf1d33442011-04-23 15:34:11 +0200371 if (ul)
Sylvain Munaut7f975d22011-11-17 20:36:50 +0100372 ul[i] = _a5_2_get_output(r);
Sylvain Munautf1d33442011-04-23 15:34:11 +0200373 }
374}
Sylvain Munaut2735ac42011-11-17 21:01:46 +0100375
Sylvain Munautdca7d2c2012-04-18 21:53:23 +0200376/*! @} */