blob: 23cbe0c9389820bdd66729b8b0e5938b85a6e26f [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
Harald Welte96e2a002017-06-12 21:44:18 +020029/*! \addtogroup crypto
Sylvain Munaut2735ac42011-11-17 21:01:46 +010030 * @{
Harald Welte96e2a002017-06-12 21:44:18 +020031 * \brief Osmocom GSM/GPRS ciphering algorithm implementation
Sylvain Munaut2735ac42011-11-17 21:01:46 +010032 */
33
Harald Welte96e2a002017-06-12 21:44:18 +020034/*! \file gsm/a5.c */
Sylvain Munaut2735ac42011-11-17 21:01:46 +010035
Sylvain Munautc1e9be92012-12-06 08:23:02 +010036#include <errno.h>
Sylvain Munautf1d33442011-04-23 15:34:11 +020037#include <string.h>
Maxf8699ca2015-03-25 17:20:31 +010038#include <stdbool.h>
Sylvain Munautf1d33442011-04-23 15:34:11 +020039
40#include <osmocom/gsm/a5.h>
Maxf8699ca2015-03-25 17:20:31 +010041#include <osmocom/gsm/kasumi.h>
Maxceae1232016-06-27 18:12:49 +020042#include <osmocom/crypt/auth.h>
Sylvain Munautf1d33442011-04-23 15:34:11 +020043
Harald Weltee15ac062014-12-04 14:15:36 +010044/* Somme OS (like Nuttx) don't have ENOTSUP */
45#ifndef ENOTSUP
46#define ENOTSUP EINVAL
47#endif
48
Maxf8699ca2015-03-25 17:20:31 +010049/* ------------------------------------------------------------------------ */
50/* A5/3&4 */
51/* ------------------------------------------------------------------------ */
52
53/*! \brief Generate a GSM A5/4 cipher stream
54 * \param[in] key 16 byte array for the key (as received from the SIM)
55 * \param[in] fn Frame number
56 * \param[out] dl Pointer to array of ubits to return Downlink cipher stream
57 * \param[out] ul Pointer to array of ubits to return Uplink cipher stream
58 * \param[in] fn_correct true if fn is a real GSM frame number and thus requires internal conversion
59 *
60 * Either (or both) of dl/ul should be NULL if not needed.
61 *
62 * Implementation based on specifications from 3GPP TS 55.216, 3GPP TR 55.919 and ETSI TS 135 202
63 * with slight simplifications (CE hardcoded to 0).
64 */
65void
66_a5_4(const uint8_t *ck, uint32_t fn, ubit_t *dl, ubit_t *ul, bool fn_correct)
67{
68 uint8_t i, gamma[32], uplink[15];
69 uint32_t fn_count = (fn_correct) ? osmo_a5_fn_count(fn) : fn;
70
71 if (ul) {
72 _kasumi_kgcore(0xF, 0, fn_count, 0, ck, gamma, 228);
73 for(i = 0; i < 15; i++) uplink[i] = (gamma[i + 14] << 2) + (gamma[i + 15] >> 6);
74 osmo_pbit2ubit(ul, uplink, 114);
75 }
76 if (dl) {
77 _kasumi_kgcore(0xF, 0, fn_count, 0, ck, gamma, 114);
78 osmo_pbit2ubit(dl, gamma, 114);
79 }
80}
81
82/*! \brief Generate a GSM A5/3 cipher stream
83 * \param[in] key 8 byte array for the key (as received from the SIM)
84 * \param[in] fn Frame number
85 * \param[out] dl Pointer to array of ubits to return Downlink cipher stream
86 * \param[out] ul Pointer to array of ubits to return Uplink cipher stream
87 * \param[in] fn_correct true if fn is a real GSM frame number and thus requires internal conversion
88 *
89 * Either (or both) of dl/ul should be NULL if not needed.
90 *
91 * Implementation based on specifications from 3GPP TS 55.216, 3GPP TR 55.919 and ETSI TS 135 202
92 * with slight simplifications (CE hardcoded to 0).
93 */
94void
95_a5_3(const uint8_t *key, uint32_t fn, ubit_t *dl, ubit_t *ul, bool fn_correct)
96{
97 uint8_t ck[16];
Maxceae1232016-06-27 18:12:49 +020098 osmo_c4(ck, key);
Maxf8699ca2015-03-25 17:20:31 +010099 /* internal function require 128 bit key so we expand by concatenating supplied 64 bit key */
100 _a5_4(ck, fn, dl, ul, fn_correct);
101}
102
Sylvain Munautf1d33442011-04-23 15:34:11 +0200103/* ------------------------------------------------------------------------ */
104/* A5/1&2 common stuff */
105/* ------------------------------------------------------------------------ */
106
107#define A5_R1_LEN 19
108#define A5_R2_LEN 22
109#define A5_R3_LEN 23
110#define A5_R4_LEN 17 /* A5/2 only */
111
112#define A5_R1_MASK ((1<<A5_R1_LEN)-1)
113#define A5_R2_MASK ((1<<A5_R2_LEN)-1)
114#define A5_R3_MASK ((1<<A5_R3_LEN)-1)
115#define A5_R4_MASK ((1<<A5_R4_LEN)-1)
116
Sylvain Munaut2dafed52012-03-01 19:54:02 +0100117#define A5_R1_TAPS 0x072000 /* x^19 + x^18 + x^17 + x^14 + 1 */
118#define A5_R2_TAPS 0x300000 /* x^22 + x^21 + 1 */
119#define A5_R3_TAPS 0x700080 /* x^23 + x^22 + x^21 + x^8 + 1 */
120#define A5_R4_TAPS 0x010800 /* x^17 + x^12 + 1 */
Sylvain Munautf1d33442011-04-23 15:34:11 +0200121
Sylvain Munaut2735ac42011-11-17 21:01:46 +0100122/*! \brief Computes parity of a 32-bit word
123 * \param[in] x 32 bit word
124 * \return Parity bit (xor of all bits) as 0 or 1
125 */
Sylvain Munautf1d33442011-04-23 15:34:11 +0200126static inline uint32_t
127_a5_12_parity(uint32_t x)
128{
129 x ^= x >> 16;
130 x ^= x >> 8;
131 x ^= x >> 4;
Sylvain Munautad4a6a82011-11-20 08:46:56 +0100132 x &= 0xf;
133 return (0x6996 >> x) & 1;
Sylvain Munautf1d33442011-04-23 15:34:11 +0200134}
135
Sylvain Munaut2735ac42011-11-17 21:01:46 +0100136/*! \brief Compute majority bit from 3 taps
137 * \param[in] v1 LFSR state ANDed with tap-bit
138 * \param[in] v2 LFSR state ANDed with tap-bit
139 * \param[in] v3 LFSR state ANDed with tap-bit
140 * \return The majority bit (0 or 1)
141 */
Sylvain Munautf1d33442011-04-23 15:34:11 +0200142static inline uint32_t
143_a5_12_majority(uint32_t v1, uint32_t v2, uint32_t v3)
144{
145 return (!!v1 + !!v2 + !!v3) >= 2;
146}
147
Sylvain Munaut2735ac42011-11-17 21:01:46 +0100148/*! \brief Compute the next LFSR state
149 * \param[in] r Current state
150 * \param[in] mask LFSR mask
151 * \param[in] taps LFSR taps
152 * \return Next state
153 */
Sylvain Munautf1d33442011-04-23 15:34:11 +0200154static inline uint32_t
155_a5_12_clock(uint32_t r, uint32_t mask, uint32_t taps)
156{
157 return ((r << 1) & mask) | _a5_12_parity(r & taps);
158}
159
160
161/* ------------------------------------------------------------------------ */
162/* A5/1 */
163/* ------------------------------------------------------------------------ */
164
165#define A51_R1_CLKBIT 0x000100
166#define A51_R2_CLKBIT 0x000400
167#define A51_R3_CLKBIT 0x000400
168
Sylvain Munaut2735ac42011-11-17 21:01:46 +0100169/*! \brief GSM A5/1 Clocking function
170 * \param[in] r Register state
171 * \param[in] force Non-zero value disable conditional clocking
172 */
Sylvain Munautf1d33442011-04-23 15:34:11 +0200173static inline void
174_a5_1_clock(uint32_t r[], int force)
175{
176 int cb[3], maj;
177
178 cb[0] = !!(r[0] & A51_R1_CLKBIT);
179 cb[1] = !!(r[1] & A51_R2_CLKBIT);
180 cb[2] = !!(r[2] & A51_R3_CLKBIT);
181
182 maj = _a5_12_majority(cb[0], cb[1], cb[2]);
183
184 if (force || (maj == cb[0]))
185 r[0] = _a5_12_clock(r[0], A5_R1_MASK, A5_R1_TAPS);
186
187 if (force || (maj == cb[1]))
188 r[1] = _a5_12_clock(r[1], A5_R2_MASK, A5_R2_TAPS);
189
190 if (force || (maj == cb[2]))
191 r[2] = _a5_12_clock(r[2], A5_R3_MASK, A5_R3_TAPS);
192}
193
Sylvain Munaut2735ac42011-11-17 21:01:46 +0100194/*! \brief GSM A5/1 Output function
195 * \param[in] r Register state
196 * \return The A5/1 output function bit
197 */
Sylvain Munautf1d33442011-04-23 15:34:11 +0200198static inline uint8_t
199_a5_1_get_output(uint32_t r[])
200{
201 return (r[0] >> (A5_R1_LEN-1)) ^
202 (r[1] >> (A5_R2_LEN-1)) ^
203 (r[2] >> (A5_R3_LEN-1));
204}
205
Sylvain Munaut2735ac42011-11-17 21:01:46 +0100206/*! \brief Generate a GSM A5/1 cipher stream
207 * \param[in] key 8 byte array for the key (as received from the SIM)
208 * \param[in] fn Frame number
209 * \param[out] dl Pointer to array of ubits to return Downlink cipher stream
210 * \param[out] ul Pointer to array of ubits to return Uplink cipher stream
211 *
212 * Either (or both) of dl/ul can be NULL if not needed.
213 */
Sylvain Munautf1d33442011-04-23 15:34:11 +0200214void
Maxfdb3d8c2016-04-21 16:51:04 +0200215_a5_1(const uint8_t *key, uint32_t fn, ubit_t *dl, ubit_t *ul)
Sylvain Munautf1d33442011-04-23 15:34:11 +0200216{
217 uint32_t r[3] = {0, 0, 0};
218 uint32_t fn_count;
219 uint32_t b;
220 int i;
221
222 /* Key load */
223 for (i=0; i<64; i++)
224 {
225 b = ( key[7 - (i>>3)] >> (i&7) ) & 1;
226
227 _a5_1_clock(r, 1);
228
229 r[0] ^= b;
230 r[1] ^= b;
231 r[2] ^= b;
232 }
233
234 /* Frame count load */
235 fn_count = osmo_a5_fn_count(fn);
236
237 for (i=0; i<22; i++)
238 {
239 b = (fn_count >> i) & 1;
240
241 _a5_1_clock(r, 1);
242
243 r[0] ^= b;
244 r[1] ^= b;
245 r[2] ^= b;
246 }
247
248 /* Mix */
249 for (i=0; i<100; i++)
250 {
251 _a5_1_clock(r, 0);
252 }
253
254 /* Output */
255 for (i=0; i<114; i++) {
256 _a5_1_clock(r, 0);
257 if (dl)
258 dl[i] = _a5_1_get_output(r);
259 }
260
261 for (i=0; i<114; i++) {
262 _a5_1_clock(r, 0);
263 if (ul)
264 ul[i] = _a5_1_get_output(r);
265 }
266}
267
Maxfdb3d8c2016-04-21 16:51:04 +0200268void osmo_a5_1(const uint8_t *key, uint32_t fn, ubit_t *dl, ubit_t *ul)
269{
270 osmo_a5(1, key, fn, dl, ul);
271}
Sylvain Munautf1d33442011-04-23 15:34:11 +0200272
273/* ------------------------------------------------------------------------ */
274/* A5/2 */
275/* ------------------------------------------------------------------------ */
276
277#define A52_R4_CLKBIT0 0x000400
278#define A52_R4_CLKBIT1 0x000008
279#define A52_R4_CLKBIT2 0x000080
280
Sylvain Munaut2735ac42011-11-17 21:01:46 +0100281/*! \brief GSM A5/2 Clocking function
282 * \param[in] r Register state
283 * \param[in] force Non-zero value disable conditional clocking
284 */
Sylvain Munautf1d33442011-04-23 15:34:11 +0200285static inline void
286_a5_2_clock(uint32_t r[], int force)
287{
288 int cb[3], maj;
289
290 cb[0] = !!(r[3] & A52_R4_CLKBIT0);
291 cb[1] = !!(r[3] & A52_R4_CLKBIT1);
292 cb[2] = !!(r[3] & A52_R4_CLKBIT2);
293
294 maj = (cb[0] + cb[1] + cb[2]) >= 2;
295
296 if (force || (maj == cb[0]))
297 r[0] = _a5_12_clock(r[0], A5_R1_MASK, A5_R1_TAPS);
298
299 if (force || (maj == cb[1]))
300 r[1] = _a5_12_clock(r[1], A5_R2_MASK, A5_R2_TAPS);
301
302 if (force || (maj == cb[2]))
303 r[2] = _a5_12_clock(r[2], A5_R3_MASK, A5_R3_TAPS);
304
305 r[3] = _a5_12_clock(r[3], A5_R4_MASK, A5_R4_TAPS);
306}
307
Sylvain Munaut2735ac42011-11-17 21:01:46 +0100308/*! \brief GSM A5/2 Output function
309 * \param[in] r Register state
310 * \return The A5/2 output function bit
311 */
Sylvain Munautf1d33442011-04-23 15:34:11 +0200312static inline uint8_t
Sylvain Munaut7f975d22011-11-17 20:36:50 +0100313_a5_2_get_output(uint32_t r[])
Sylvain Munautf1d33442011-04-23 15:34:11 +0200314{
Sylvain Munaut7f975d22011-11-17 20:36:50 +0100315 uint8_t b;
Sylvain Munautf1d33442011-04-23 15:34:11 +0200316
Sylvain Munaut7f975d22011-11-17 20:36:50 +0100317 b = (r[0] >> (A5_R1_LEN-1)) ^
318 (r[1] >> (A5_R2_LEN-1)) ^
319 (r[2] >> (A5_R3_LEN-1)) ^
320 _a5_12_majority( r[0] & 0x08000, ~r[0] & 0x04000, r[0] & 0x1000) ^
321 _a5_12_majority(~r[1] & 0x10000, r[1] & 0x02000, r[1] & 0x0200) ^
322 _a5_12_majority( r[2] & 0x40000, r[2] & 0x10000, ~r[2] & 0x2000);
Sylvain Munautf1d33442011-04-23 15:34:11 +0200323
Sylvain Munaut7f975d22011-11-17 20:36:50 +0100324 return b;
Sylvain Munautf1d33442011-04-23 15:34:11 +0200325}
326
Sylvain Munaut2735ac42011-11-17 21:01:46 +0100327/*! \brief Generate a GSM A5/1 cipher stream
328 * \param[in] key 8 byte array for the key (as received from the SIM)
329 * \param[in] fn Frame number
330 * \param[out] dl Pointer to array of ubits to return Downlink cipher stream
331 * \param[out] ul Pointer to array of ubits to return Uplink cipher stream
332 *
333 * Either (or both) of dl/ul can be NULL if not needed.
334 */
Sylvain Munautf1d33442011-04-23 15:34:11 +0200335void
Maxfdb3d8c2016-04-21 16:51:04 +0200336_a5_2(const uint8_t *key, uint32_t fn, ubit_t *dl, ubit_t *ul)
Sylvain Munautf1d33442011-04-23 15:34:11 +0200337{
338 uint32_t r[4] = {0, 0, 0, 0};
339 uint32_t fn_count;
340 uint32_t b;
Sylvain Munautf1d33442011-04-23 15:34:11 +0200341 int i;
342
343 /* Key load */
344 for (i=0; i<64; i++)
345 {
346 b = ( key[7 - (i>>3)] >> (i&7) ) & 1;
347
348 _a5_2_clock(r, 1);
349
350 r[0] ^= b;
351 r[1] ^= b;
352 r[2] ^= b;
353 r[3] ^= b;
354 }
355
356 /* Frame count load */
357 fn_count = osmo_a5_fn_count(fn);
358
359 for (i=0; i<22; i++)
360 {
361 b = (fn_count >> i) & 1;
362
363 _a5_2_clock(r, 1);
364
365 r[0] ^= b;
366 r[1] ^= b;
367 r[2] ^= b;
368 r[3] ^= b;
369 }
370
371 r[0] |= 1 << 15;
372 r[1] |= 1 << 16;
373 r[2] |= 1 << 18;
374 r[3] |= 1 << 10;
375
376 /* Mix */
Sylvain Munaut7f975d22011-11-17 20:36:50 +0100377 for (i=0; i<99; i++)
Sylvain Munautf1d33442011-04-23 15:34:11 +0200378 {
379 _a5_2_clock(r, 0);
380 }
381
Sylvain Munautf1d33442011-04-23 15:34:11 +0200382 /* Output */
383 for (i=0; i<114; i++) {
384 _a5_2_clock(r, 0);
Sylvain Munautf1d33442011-04-23 15:34:11 +0200385 if (dl)
Sylvain Munaut7f975d22011-11-17 20:36:50 +0100386 dl[i] = _a5_2_get_output(r);
Sylvain Munautf1d33442011-04-23 15:34:11 +0200387 }
388
389 for (i=0; i<114; i++) {
390 _a5_2_clock(r, 0);
Sylvain Munautf1d33442011-04-23 15:34:11 +0200391 if (ul)
Sylvain Munaut7f975d22011-11-17 20:36:50 +0100392 ul[i] = _a5_2_get_output(r);
Sylvain Munautf1d33442011-04-23 15:34:11 +0200393 }
394}
Sylvain Munaut2735ac42011-11-17 21:01:46 +0100395
Maxfdb3d8c2016-04-21 16:51:04 +0200396void osmo_a5_2(const uint8_t *key, uint32_t fn, ubit_t *dl, ubit_t *ul)
397{
398 osmo_a5(2, key, fn, dl, ul);
399}
400
401/*! \brief Main method to generate a A5/x cipher stream
402 * \param[in] n Which A5/x method to use
403 * \param[in] key 8 or 16 (for a5/4) byte array for the key (as received from the SIM)
404 * \param[in] fn Frame number
405 * \param[out] dl Pointer to array of ubits to return Downlink cipher stream
406 * \param[out] ul Pointer to array of ubits to return Uplink cipher stream
407 * \returns 0 for success, -ENOTSUP for invalid cipher selection.
408 *
409 * Currently A5/[0-4] are supported.
410 * Either (or both) of dl/ul can be NULL if not needed.
411 */
412int
413osmo_a5(int n, const uint8_t *key, uint32_t fn, ubit_t *dl, ubit_t *ul)
414{
415 switch (n)
416 {
417 case 0:
418 if (dl)
419 memset(dl, 0x00, 114);
420 if (ul)
421 memset(ul, 0x00, 114);
422 break;
423
424 case 1:
425 _a5_1(key, fn, dl, ul);
426 break;
427
428 case 2:
429 _a5_2(key, fn, dl, ul);
430 break;
431
432 case 3:
433 _a5_3(key, fn, dl, ul, true);
434 break;
435
436 case 4:
437 _a5_4(key, fn, dl, ul, true);
438 break;
439
440 default:
441 /* a5/[5..7] not supported here/yet */
442 return -ENOTSUP;
443 }
444
445 return 0;
446}
447
Sylvain Munautdca7d2c2012-04-18 21:53:23 +0200448/*! @} */