blob: dbba0f2081b9b8c551081db8c49b959410e5c398 [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>
Maxf8699ca2015-03-25 17:20:31 +010039#include <stdbool.h>
Sylvain Munautf1d33442011-04-23 15:34:11 +020040
41#include <osmocom/gsm/a5.h>
Maxf8699ca2015-03-25 17:20:31 +010042#include <osmocom/gsm/kasumi.h>
Maxceae1232016-06-27 18:12:49 +020043#include <osmocom/crypt/auth.h>
Sylvain Munautf1d33442011-04-23 15:34:11 +020044
Harald Weltee15ac062014-12-04 14:15:36 +010045/* Somme OS (like Nuttx) don't have ENOTSUP */
46#ifndef ENOTSUP
47#define ENOTSUP EINVAL
48#endif
49
Maxf8699ca2015-03-25 17:20:31 +010050/* ------------------------------------------------------------------------ */
51/* A5/3&4 */
52/* ------------------------------------------------------------------------ */
53
54/*! \brief Generate a GSM A5/4 cipher stream
55 * \param[in] key 16 byte array for the key (as received from the SIM)
56 * \param[in] fn Frame number
57 * \param[out] dl Pointer to array of ubits to return Downlink cipher stream
58 * \param[out] ul Pointer to array of ubits to return Uplink cipher stream
59 * \param[in] fn_correct true if fn is a real GSM frame number and thus requires internal conversion
60 *
61 * Either (or both) of dl/ul should be NULL if not needed.
62 *
63 * Implementation based on specifications from 3GPP TS 55.216, 3GPP TR 55.919 and ETSI TS 135 202
64 * with slight simplifications (CE hardcoded to 0).
65 */
66void
67_a5_4(const uint8_t *ck, uint32_t fn, ubit_t *dl, ubit_t *ul, bool fn_correct)
68{
69 uint8_t i, gamma[32], uplink[15];
70 uint32_t fn_count = (fn_correct) ? osmo_a5_fn_count(fn) : fn;
71
72 if (ul) {
73 _kasumi_kgcore(0xF, 0, fn_count, 0, ck, gamma, 228);
74 for(i = 0; i < 15; i++) uplink[i] = (gamma[i + 14] << 2) + (gamma[i + 15] >> 6);
75 osmo_pbit2ubit(ul, uplink, 114);
76 }
77 if (dl) {
78 _kasumi_kgcore(0xF, 0, fn_count, 0, ck, gamma, 114);
79 osmo_pbit2ubit(dl, gamma, 114);
80 }
81}
82
83/*! \brief Generate a GSM A5/3 cipher stream
84 * \param[in] key 8 byte array for the key (as received from the SIM)
85 * \param[in] fn Frame number
86 * \param[out] dl Pointer to array of ubits to return Downlink cipher stream
87 * \param[out] ul Pointer to array of ubits to return Uplink cipher stream
88 * \param[in] fn_correct true if fn is a real GSM frame number and thus requires internal conversion
89 *
90 * Either (or both) of dl/ul should be NULL if not needed.
91 *
92 * Implementation based on specifications from 3GPP TS 55.216, 3GPP TR 55.919 and ETSI TS 135 202
93 * with slight simplifications (CE hardcoded to 0).
94 */
95void
96_a5_3(const uint8_t *key, uint32_t fn, ubit_t *dl, ubit_t *ul, bool fn_correct)
97{
98 uint8_t ck[16];
Maxceae1232016-06-27 18:12:49 +020099 osmo_c4(ck, key);
Maxf8699ca2015-03-25 17:20:31 +0100100 /* internal function require 128 bit key so we expand by concatenating supplied 64 bit key */
101 _a5_4(ck, fn, dl, ul, fn_correct);
102}
103
Sylvain Munautf1d33442011-04-23 15:34:11 +0200104/* ------------------------------------------------------------------------ */
105/* A5/1&2 common stuff */
106/* ------------------------------------------------------------------------ */
107
108#define A5_R1_LEN 19
109#define A5_R2_LEN 22
110#define A5_R3_LEN 23
111#define A5_R4_LEN 17 /* A5/2 only */
112
113#define A5_R1_MASK ((1<<A5_R1_LEN)-1)
114#define A5_R2_MASK ((1<<A5_R2_LEN)-1)
115#define A5_R3_MASK ((1<<A5_R3_LEN)-1)
116#define A5_R4_MASK ((1<<A5_R4_LEN)-1)
117
Sylvain Munaut2dafed52012-03-01 19:54:02 +0100118#define A5_R1_TAPS 0x072000 /* x^19 + x^18 + x^17 + x^14 + 1 */
119#define A5_R2_TAPS 0x300000 /* x^22 + x^21 + 1 */
120#define A5_R3_TAPS 0x700080 /* x^23 + x^22 + x^21 + x^8 + 1 */
121#define A5_R4_TAPS 0x010800 /* x^17 + x^12 + 1 */
Sylvain Munautf1d33442011-04-23 15:34:11 +0200122
Sylvain Munaut2735ac42011-11-17 21:01:46 +0100123/*! \brief Computes parity of a 32-bit word
124 * \param[in] x 32 bit word
125 * \return Parity bit (xor of all bits) as 0 or 1
126 */
Sylvain Munautf1d33442011-04-23 15:34:11 +0200127static inline uint32_t
128_a5_12_parity(uint32_t x)
129{
130 x ^= x >> 16;
131 x ^= x >> 8;
132 x ^= x >> 4;
Sylvain Munautad4a6a82011-11-20 08:46:56 +0100133 x &= 0xf;
134 return (0x6996 >> x) & 1;
Sylvain Munautf1d33442011-04-23 15:34:11 +0200135}
136
Sylvain Munaut2735ac42011-11-17 21:01:46 +0100137/*! \brief Compute majority bit from 3 taps
138 * \param[in] v1 LFSR state ANDed with tap-bit
139 * \param[in] v2 LFSR state ANDed with tap-bit
140 * \param[in] v3 LFSR state ANDed with tap-bit
141 * \return The majority bit (0 or 1)
142 */
Sylvain Munautf1d33442011-04-23 15:34:11 +0200143static inline uint32_t
144_a5_12_majority(uint32_t v1, uint32_t v2, uint32_t v3)
145{
146 return (!!v1 + !!v2 + !!v3) >= 2;
147}
148
Sylvain Munaut2735ac42011-11-17 21:01:46 +0100149/*! \brief Compute the next LFSR state
150 * \param[in] r Current state
151 * \param[in] mask LFSR mask
152 * \param[in] taps LFSR taps
153 * \return Next state
154 */
Sylvain Munautf1d33442011-04-23 15:34:11 +0200155static inline uint32_t
156_a5_12_clock(uint32_t r, uint32_t mask, uint32_t taps)
157{
158 return ((r << 1) & mask) | _a5_12_parity(r & taps);
159}
160
161
162/* ------------------------------------------------------------------------ */
163/* A5/1 */
164/* ------------------------------------------------------------------------ */
165
166#define A51_R1_CLKBIT 0x000100
167#define A51_R2_CLKBIT 0x000400
168#define A51_R3_CLKBIT 0x000400
169
Sylvain Munaut2735ac42011-11-17 21:01:46 +0100170/*! \brief GSM A5/1 Clocking function
171 * \param[in] r Register state
172 * \param[in] force Non-zero value disable conditional clocking
173 */
Sylvain Munautf1d33442011-04-23 15:34:11 +0200174static inline void
175_a5_1_clock(uint32_t r[], int force)
176{
177 int cb[3], maj;
178
179 cb[0] = !!(r[0] & A51_R1_CLKBIT);
180 cb[1] = !!(r[1] & A51_R2_CLKBIT);
181 cb[2] = !!(r[2] & A51_R3_CLKBIT);
182
183 maj = _a5_12_majority(cb[0], cb[1], cb[2]);
184
185 if (force || (maj == cb[0]))
186 r[0] = _a5_12_clock(r[0], A5_R1_MASK, A5_R1_TAPS);
187
188 if (force || (maj == cb[1]))
189 r[1] = _a5_12_clock(r[1], A5_R2_MASK, A5_R2_TAPS);
190
191 if (force || (maj == cb[2]))
192 r[2] = _a5_12_clock(r[2], A5_R3_MASK, A5_R3_TAPS);
193}
194
Sylvain Munaut2735ac42011-11-17 21:01:46 +0100195/*! \brief GSM A5/1 Output function
196 * \param[in] r Register state
197 * \return The A5/1 output function bit
198 */
Sylvain Munautf1d33442011-04-23 15:34:11 +0200199static inline uint8_t
200_a5_1_get_output(uint32_t r[])
201{
202 return (r[0] >> (A5_R1_LEN-1)) ^
203 (r[1] >> (A5_R2_LEN-1)) ^
204 (r[2] >> (A5_R3_LEN-1));
205}
206
Sylvain Munaut2735ac42011-11-17 21:01:46 +0100207/*! \brief Generate a GSM A5/1 cipher stream
208 * \param[in] key 8 byte array for the key (as received from the SIM)
209 * \param[in] fn Frame number
210 * \param[out] dl Pointer to array of ubits to return Downlink cipher stream
211 * \param[out] ul Pointer to array of ubits to return Uplink cipher stream
212 *
213 * Either (or both) of dl/ul can be NULL if not needed.
214 */
Sylvain Munautf1d33442011-04-23 15:34:11 +0200215void
Maxfdb3d8c2016-04-21 16:51:04 +0200216_a5_1(const uint8_t *key, uint32_t fn, ubit_t *dl, ubit_t *ul)
Sylvain Munautf1d33442011-04-23 15:34:11 +0200217{
218 uint32_t r[3] = {0, 0, 0};
219 uint32_t fn_count;
220 uint32_t b;
221 int i;
222
223 /* Key load */
224 for (i=0; i<64; i++)
225 {
226 b = ( key[7 - (i>>3)] >> (i&7) ) & 1;
227
228 _a5_1_clock(r, 1);
229
230 r[0] ^= b;
231 r[1] ^= b;
232 r[2] ^= b;
233 }
234
235 /* Frame count load */
236 fn_count = osmo_a5_fn_count(fn);
237
238 for (i=0; i<22; i++)
239 {
240 b = (fn_count >> i) & 1;
241
242 _a5_1_clock(r, 1);
243
244 r[0] ^= b;
245 r[1] ^= b;
246 r[2] ^= b;
247 }
248
249 /* Mix */
250 for (i=0; i<100; i++)
251 {
252 _a5_1_clock(r, 0);
253 }
254
255 /* Output */
256 for (i=0; i<114; i++) {
257 _a5_1_clock(r, 0);
258 if (dl)
259 dl[i] = _a5_1_get_output(r);
260 }
261
262 for (i=0; i<114; i++) {
263 _a5_1_clock(r, 0);
264 if (ul)
265 ul[i] = _a5_1_get_output(r);
266 }
267}
268
Maxfdb3d8c2016-04-21 16:51:04 +0200269void osmo_a5_1(const uint8_t *key, uint32_t fn, ubit_t *dl, ubit_t *ul)
270{
271 osmo_a5(1, key, fn, dl, ul);
272}
Sylvain Munautf1d33442011-04-23 15:34:11 +0200273
274/* ------------------------------------------------------------------------ */
275/* A5/2 */
276/* ------------------------------------------------------------------------ */
277
278#define A52_R4_CLKBIT0 0x000400
279#define A52_R4_CLKBIT1 0x000008
280#define A52_R4_CLKBIT2 0x000080
281
Sylvain Munaut2735ac42011-11-17 21:01:46 +0100282/*! \brief GSM A5/2 Clocking function
283 * \param[in] r Register state
284 * \param[in] force Non-zero value disable conditional clocking
285 */
Sylvain Munautf1d33442011-04-23 15:34:11 +0200286static inline void
287_a5_2_clock(uint32_t r[], int force)
288{
289 int cb[3], maj;
290
291 cb[0] = !!(r[3] & A52_R4_CLKBIT0);
292 cb[1] = !!(r[3] & A52_R4_CLKBIT1);
293 cb[2] = !!(r[3] & A52_R4_CLKBIT2);
294
295 maj = (cb[0] + cb[1] + cb[2]) >= 2;
296
297 if (force || (maj == cb[0]))
298 r[0] = _a5_12_clock(r[0], A5_R1_MASK, A5_R1_TAPS);
299
300 if (force || (maj == cb[1]))
301 r[1] = _a5_12_clock(r[1], A5_R2_MASK, A5_R2_TAPS);
302
303 if (force || (maj == cb[2]))
304 r[2] = _a5_12_clock(r[2], A5_R3_MASK, A5_R3_TAPS);
305
306 r[3] = _a5_12_clock(r[3], A5_R4_MASK, A5_R4_TAPS);
307}
308
Sylvain Munaut2735ac42011-11-17 21:01:46 +0100309/*! \brief GSM A5/2 Output function
310 * \param[in] r Register state
311 * \return The A5/2 output function bit
312 */
Sylvain Munautf1d33442011-04-23 15:34:11 +0200313static inline uint8_t
Sylvain Munaut7f975d22011-11-17 20:36:50 +0100314_a5_2_get_output(uint32_t r[])
Sylvain Munautf1d33442011-04-23 15:34:11 +0200315{
Sylvain Munaut7f975d22011-11-17 20:36:50 +0100316 uint8_t b;
Sylvain Munautf1d33442011-04-23 15:34:11 +0200317
Sylvain Munaut7f975d22011-11-17 20:36:50 +0100318 b = (r[0] >> (A5_R1_LEN-1)) ^
319 (r[1] >> (A5_R2_LEN-1)) ^
320 (r[2] >> (A5_R3_LEN-1)) ^
321 _a5_12_majority( r[0] & 0x08000, ~r[0] & 0x04000, r[0] & 0x1000) ^
322 _a5_12_majority(~r[1] & 0x10000, r[1] & 0x02000, r[1] & 0x0200) ^
323 _a5_12_majority( r[2] & 0x40000, r[2] & 0x10000, ~r[2] & 0x2000);
Sylvain Munautf1d33442011-04-23 15:34:11 +0200324
Sylvain Munaut7f975d22011-11-17 20:36:50 +0100325 return b;
Sylvain Munautf1d33442011-04-23 15:34:11 +0200326}
327
Sylvain Munaut2735ac42011-11-17 21:01:46 +0100328/*! \brief Generate a GSM A5/1 cipher stream
329 * \param[in] key 8 byte array for the key (as received from the SIM)
330 * \param[in] fn Frame number
331 * \param[out] dl Pointer to array of ubits to return Downlink cipher stream
332 * \param[out] ul Pointer to array of ubits to return Uplink cipher stream
333 *
334 * Either (or both) of dl/ul can be NULL if not needed.
335 */
Sylvain Munautf1d33442011-04-23 15:34:11 +0200336void
Maxfdb3d8c2016-04-21 16:51:04 +0200337_a5_2(const uint8_t *key, uint32_t fn, ubit_t *dl, ubit_t *ul)
Sylvain Munautf1d33442011-04-23 15:34:11 +0200338{
339 uint32_t r[4] = {0, 0, 0, 0};
340 uint32_t fn_count;
341 uint32_t b;
Sylvain Munautf1d33442011-04-23 15:34:11 +0200342 int i;
343
344 /* Key load */
345 for (i=0; i<64; i++)
346 {
347 b = ( key[7 - (i>>3)] >> (i&7) ) & 1;
348
349 _a5_2_clock(r, 1);
350
351 r[0] ^= b;
352 r[1] ^= b;
353 r[2] ^= b;
354 r[3] ^= b;
355 }
356
357 /* Frame count load */
358 fn_count = osmo_a5_fn_count(fn);
359
360 for (i=0; i<22; i++)
361 {
362 b = (fn_count >> i) & 1;
363
364 _a5_2_clock(r, 1);
365
366 r[0] ^= b;
367 r[1] ^= b;
368 r[2] ^= b;
369 r[3] ^= b;
370 }
371
372 r[0] |= 1 << 15;
373 r[1] |= 1 << 16;
374 r[2] |= 1 << 18;
375 r[3] |= 1 << 10;
376
377 /* Mix */
Sylvain Munaut7f975d22011-11-17 20:36:50 +0100378 for (i=0; i<99; i++)
Sylvain Munautf1d33442011-04-23 15:34:11 +0200379 {
380 _a5_2_clock(r, 0);
381 }
382
Sylvain Munautf1d33442011-04-23 15:34:11 +0200383 /* Output */
384 for (i=0; i<114; i++) {
385 _a5_2_clock(r, 0);
Sylvain Munautf1d33442011-04-23 15:34:11 +0200386 if (dl)
Sylvain Munaut7f975d22011-11-17 20:36:50 +0100387 dl[i] = _a5_2_get_output(r);
Sylvain Munautf1d33442011-04-23 15:34:11 +0200388 }
389
390 for (i=0; i<114; i++) {
391 _a5_2_clock(r, 0);
Sylvain Munautf1d33442011-04-23 15:34:11 +0200392 if (ul)
Sylvain Munaut7f975d22011-11-17 20:36:50 +0100393 ul[i] = _a5_2_get_output(r);
Sylvain Munautf1d33442011-04-23 15:34:11 +0200394 }
395}
Sylvain Munaut2735ac42011-11-17 21:01:46 +0100396
Maxfdb3d8c2016-04-21 16:51:04 +0200397void osmo_a5_2(const uint8_t *key, uint32_t fn, ubit_t *dl, ubit_t *ul)
398{
399 osmo_a5(2, key, fn, dl, ul);
400}
401
402/*! \brief Main method to generate a A5/x cipher stream
403 * \param[in] n Which A5/x method to use
404 * \param[in] key 8 or 16 (for a5/4) byte array for the key (as received from the SIM)
405 * \param[in] fn Frame number
406 * \param[out] dl Pointer to array of ubits to return Downlink cipher stream
407 * \param[out] ul Pointer to array of ubits to return Uplink cipher stream
408 * \returns 0 for success, -ENOTSUP for invalid cipher selection.
409 *
410 * Currently A5/[0-4] are supported.
411 * Either (or both) of dl/ul can be NULL if not needed.
412 */
413int
414osmo_a5(int n, const uint8_t *key, uint32_t fn, ubit_t *dl, ubit_t *ul)
415{
416 switch (n)
417 {
418 case 0:
419 if (dl)
420 memset(dl, 0x00, 114);
421 if (ul)
422 memset(ul, 0x00, 114);
423 break;
424
425 case 1:
426 _a5_1(key, fn, dl, ul);
427 break;
428
429 case 2:
430 _a5_2(key, fn, dl, ul);
431 break;
432
433 case 3:
434 _a5_3(key, fn, dl, ul, true);
435 break;
436
437 case 4:
438 _a5_4(key, fn, dl, ul, true);
439 break;
440
441 default:
442 /* a5/[5..7] not supported here/yet */
443 return -ENOTSUP;
444 }
445
446 return 0;
447}
448
Sylvain Munautdca7d2c2012-04-18 21:53:23 +0200449/*! @} */