blob: 223d3ad8e1bd3e4ba6055a26a5ab6db46c5374da [file] [log] [blame]
Neels Hofmeyr17518fe2017-06-20 04:35:06 +02001/*
Sylvain Munautf1d33442011-04-23 15:34:11 +02002 * Copyright (C) 2011 Sylvain Munaut <tnt@246tNt.com>
3 *
4 * All Rights Reserved
5 *
Harald Weltee08da972017-11-13 01:00:26 +09006 * SPDX-License-Identifier: GPL-2.0+
7 *
Sylvain Munautf1d33442011-04-23 15:34:11 +02008 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License along
19 * with this program; if not, write to the Free Software Foundation, Inc.,
20 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21 */
22
Harald Welte8cc27672017-10-16 16:00:36 +020023/*! \addtogroup a5
Sylvain Munaut2735ac42011-11-17 21:01:46 +010024 * @{
Harald Welte8cc27672017-10-16 16:00:36 +020025 * Osmocom GSM ciphering algorithm implementation
Neels Hofmeyr17518fe2017-06-20 04:35:06 +020026 *
Harald Welte8cc27672017-10-16 16:00:36 +020027 * Full reimplementation of A5/1,2,3,4 (split and threadsafe).
28 *
29 * The logic behind the algorithm is taken from "A pedagogical implementation
30 * of the GSM A5/1 and A5/2 "voice privacy" encryption algorithms." by
31 * Marc Briceno, Ian Goldberg, and David Wagner.
32 */
Sylvain Munaut2735ac42011-11-17 21:01:46 +010033
Sylvain Munautc1e9be92012-12-06 08:23:02 +010034#include <errno.h>
Sylvain Munautf1d33442011-04-23 15:34:11 +020035#include <string.h>
Maxf8699ca2015-03-25 17:20:31 +010036#include <stdbool.h>
Sylvain Munautf1d33442011-04-23 15:34:11 +020037
38#include <osmocom/gsm/a5.h>
Maxf8699ca2015-03-25 17:20:31 +010039#include <osmocom/gsm/kasumi.h>
Maxceae1232016-06-27 18:12:49 +020040#include <osmocom/crypt/auth.h>
Sylvain Munautf1d33442011-04-23 15:34:11 +020041
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
Maxf8699ca2015-03-25 17:20:31 +010047/* ------------------------------------------------------------------------ */
48/* A5/3&4 */
49/* ------------------------------------------------------------------------ */
50
Neels Hofmeyr87e45502017-06-20 00:17:59 +020051/*! Generate a GSM A5/4 cipher stream
Maxf8699ca2015-03-25 17:20:31 +010052 * \param[in] key 16 byte array for the key (as received from the SIM)
53 * \param[in] fn Frame number
54 * \param[out] dl Pointer to array of ubits to return Downlink cipher stream
55 * \param[out] ul Pointer to array of ubits to return Uplink cipher stream
56 * \param[in] fn_correct true if fn is a real GSM frame number and thus requires internal conversion
57 *
58 * Either (or both) of dl/ul should be NULL if not needed.
59 *
60 * Implementation based on specifications from 3GPP TS 55.216, 3GPP TR 55.919 and ETSI TS 135 202
61 * with slight simplifications (CE hardcoded to 0).
62 */
63void
64_a5_4(const uint8_t *ck, uint32_t fn, ubit_t *dl, ubit_t *ul, bool fn_correct)
65{
66 uint8_t i, gamma[32], uplink[15];
67 uint32_t fn_count = (fn_correct) ? osmo_a5_fn_count(fn) : fn;
68
69 if (ul) {
70 _kasumi_kgcore(0xF, 0, fn_count, 0, ck, gamma, 228);
71 for(i = 0; i < 15; i++) uplink[i] = (gamma[i + 14] << 2) + (gamma[i + 15] >> 6);
72 osmo_pbit2ubit(ul, uplink, 114);
73 }
74 if (dl) {
75 _kasumi_kgcore(0xF, 0, fn_count, 0, ck, gamma, 114);
76 osmo_pbit2ubit(dl, gamma, 114);
77 }
78}
79
Neels Hofmeyr87e45502017-06-20 00:17:59 +020080/*! Generate a GSM A5/3 cipher stream
Maxf8699ca2015-03-25 17:20:31 +010081 * \param[in] key 8 byte array for the key (as received from the SIM)
82 * \param[in] fn Frame number
83 * \param[out] dl Pointer to array of ubits to return Downlink cipher stream
84 * \param[out] ul Pointer to array of ubits to return Uplink cipher stream
85 * \param[in] fn_correct true if fn is a real GSM frame number and thus requires internal conversion
86 *
87 * Either (or both) of dl/ul should be NULL if not needed.
88 *
89 * Implementation based on specifications from 3GPP TS 55.216, 3GPP TR 55.919 and ETSI TS 135 202
90 * with slight simplifications (CE hardcoded to 0).
91 */
92void
93_a5_3(const uint8_t *key, uint32_t fn, ubit_t *dl, ubit_t *ul, bool fn_correct)
94{
95 uint8_t ck[16];
Maxceae1232016-06-27 18:12:49 +020096 osmo_c4(ck, key);
Maxf8699ca2015-03-25 17:20:31 +010097 /* internal function require 128 bit key so we expand by concatenating supplied 64 bit key */
98 _a5_4(ck, fn, dl, ul, fn_correct);
99}
100
Sylvain Munautf1d33442011-04-23 15:34:11 +0200101/* ------------------------------------------------------------------------ */
102/* A5/1&2 common stuff */
103/* ------------------------------------------------------------------------ */
104
105#define A5_R1_LEN 19
106#define A5_R2_LEN 22
107#define A5_R3_LEN 23
108#define A5_R4_LEN 17 /* A5/2 only */
109
110#define A5_R1_MASK ((1<<A5_R1_LEN)-1)
111#define A5_R2_MASK ((1<<A5_R2_LEN)-1)
112#define A5_R3_MASK ((1<<A5_R3_LEN)-1)
113#define A5_R4_MASK ((1<<A5_R4_LEN)-1)
114
Sylvain Munaut2dafed52012-03-01 19:54:02 +0100115#define A5_R1_TAPS 0x072000 /* x^19 + x^18 + x^17 + x^14 + 1 */
116#define A5_R2_TAPS 0x300000 /* x^22 + x^21 + 1 */
117#define A5_R3_TAPS 0x700080 /* x^23 + x^22 + x^21 + x^8 + 1 */
118#define A5_R4_TAPS 0x010800 /* x^17 + x^12 + 1 */
Sylvain Munautf1d33442011-04-23 15:34:11 +0200119
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200120/*! Computes parity of a 32-bit word
Sylvain Munaut2735ac42011-11-17 21:01:46 +0100121 * \param[in] x 32 bit word
122 * \return Parity bit (xor of all bits) as 0 or 1
123 */
Sylvain Munautf1d33442011-04-23 15:34:11 +0200124static inline uint32_t
125_a5_12_parity(uint32_t x)
126{
127 x ^= x >> 16;
128 x ^= x >> 8;
129 x ^= x >> 4;
Sylvain Munautad4a6a82011-11-20 08:46:56 +0100130 x &= 0xf;
131 return (0x6996 >> x) & 1;
Sylvain Munautf1d33442011-04-23 15:34:11 +0200132}
133
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200134/*! Compute majority bit from 3 taps
Sylvain Munaut2735ac42011-11-17 21:01:46 +0100135 * \param[in] v1 LFSR state ANDed with tap-bit
136 * \param[in] v2 LFSR state ANDed with tap-bit
137 * \param[in] v3 LFSR state ANDed with tap-bit
138 * \return The majority bit (0 or 1)
139 */
Sylvain Munautf1d33442011-04-23 15:34:11 +0200140static inline uint32_t
141_a5_12_majority(uint32_t v1, uint32_t v2, uint32_t v3)
142{
143 return (!!v1 + !!v2 + !!v3) >= 2;
144}
145
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200146/*! Compute the next LFSR state
Sylvain Munaut2735ac42011-11-17 21:01:46 +0100147 * \param[in] r Current state
148 * \param[in] mask LFSR mask
149 * \param[in] taps LFSR taps
150 * \return Next state
151 */
Sylvain Munautf1d33442011-04-23 15:34:11 +0200152static inline uint32_t
153_a5_12_clock(uint32_t r, uint32_t mask, uint32_t taps)
154{
155 return ((r << 1) & mask) | _a5_12_parity(r & taps);
156}
157
158
159/* ------------------------------------------------------------------------ */
160/* A5/1 */
161/* ------------------------------------------------------------------------ */
162
163#define A51_R1_CLKBIT 0x000100
164#define A51_R2_CLKBIT 0x000400
165#define A51_R3_CLKBIT 0x000400
166
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200167/*! GSM A5/1 Clocking function
Sylvain Munaut2735ac42011-11-17 21:01:46 +0100168 * \param[in] r Register state
169 * \param[in] force Non-zero value disable conditional clocking
170 */
Sylvain Munautf1d33442011-04-23 15:34:11 +0200171static inline void
172_a5_1_clock(uint32_t r[], int force)
173{
174 int cb[3], maj;
175
176 cb[0] = !!(r[0] & A51_R1_CLKBIT);
177 cb[1] = !!(r[1] & A51_R2_CLKBIT);
178 cb[2] = !!(r[2] & A51_R3_CLKBIT);
179
180 maj = _a5_12_majority(cb[0], cb[1], cb[2]);
181
182 if (force || (maj == cb[0]))
183 r[0] = _a5_12_clock(r[0], A5_R1_MASK, A5_R1_TAPS);
184
185 if (force || (maj == cb[1]))
186 r[1] = _a5_12_clock(r[1], A5_R2_MASK, A5_R2_TAPS);
187
188 if (force || (maj == cb[2]))
189 r[2] = _a5_12_clock(r[2], A5_R3_MASK, A5_R3_TAPS);
190}
191
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200192/*! GSM A5/1 Output function
Sylvain Munaut2735ac42011-11-17 21:01:46 +0100193 * \param[in] r Register state
194 * \return The A5/1 output function bit
195 */
Sylvain Munautf1d33442011-04-23 15:34:11 +0200196static inline uint8_t
197_a5_1_get_output(uint32_t r[])
198{
199 return (r[0] >> (A5_R1_LEN-1)) ^
200 (r[1] >> (A5_R2_LEN-1)) ^
201 (r[2] >> (A5_R3_LEN-1));
202}
203
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200204/*! Generate a GSM A5/1 cipher stream
Sylvain Munaut2735ac42011-11-17 21:01:46 +0100205 * \param[in] key 8 byte array for the key (as received from the SIM)
206 * \param[in] fn Frame number
207 * \param[out] dl Pointer to array of ubits to return Downlink cipher stream
208 * \param[out] ul Pointer to array of ubits to return Uplink cipher stream
209 *
210 * Either (or both) of dl/ul can be NULL if not needed.
211 */
Sylvain Munautf1d33442011-04-23 15:34:11 +0200212void
Maxfdb3d8c2016-04-21 16:51:04 +0200213_a5_1(const uint8_t *key, uint32_t fn, ubit_t *dl, ubit_t *ul)
Sylvain Munautf1d33442011-04-23 15:34:11 +0200214{
215 uint32_t r[3] = {0, 0, 0};
216 uint32_t fn_count;
217 uint32_t b;
218 int i;
219
220 /* Key load */
221 for (i=0; i<64; i++)
222 {
223 b = ( key[7 - (i>>3)] >> (i&7) ) & 1;
224
225 _a5_1_clock(r, 1);
226
227 r[0] ^= b;
228 r[1] ^= b;
229 r[2] ^= b;
230 }
231
232 /* Frame count load */
233 fn_count = osmo_a5_fn_count(fn);
234
235 for (i=0; i<22; i++)
236 {
237 b = (fn_count >> i) & 1;
238
239 _a5_1_clock(r, 1);
240
241 r[0] ^= b;
242 r[1] ^= b;
243 r[2] ^= b;
244 }
245
246 /* Mix */
247 for (i=0; i<100; i++)
248 {
249 _a5_1_clock(r, 0);
250 }
251
252 /* Output */
253 for (i=0; i<114; i++) {
254 _a5_1_clock(r, 0);
255 if (dl)
256 dl[i] = _a5_1_get_output(r);
257 }
258
259 for (i=0; i<114; i++) {
260 _a5_1_clock(r, 0);
261 if (ul)
262 ul[i] = _a5_1_get_output(r);
263 }
264}
265
Maxfdb3d8c2016-04-21 16:51:04 +0200266void osmo_a5_1(const uint8_t *key, uint32_t fn, ubit_t *dl, ubit_t *ul)
267{
268 osmo_a5(1, key, fn, dl, ul);
269}
Sylvain Munautf1d33442011-04-23 15:34:11 +0200270
271/* ------------------------------------------------------------------------ */
272/* A5/2 */
273/* ------------------------------------------------------------------------ */
274
275#define A52_R4_CLKBIT0 0x000400
276#define A52_R4_CLKBIT1 0x000008
277#define A52_R4_CLKBIT2 0x000080
278
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200279/*! GSM A5/2 Clocking function
Sylvain Munaut2735ac42011-11-17 21:01:46 +0100280 * \param[in] r Register state
281 * \param[in] force Non-zero value disable conditional clocking
282 */
Sylvain Munautf1d33442011-04-23 15:34:11 +0200283static inline void
284_a5_2_clock(uint32_t r[], int force)
285{
286 int cb[3], maj;
287
288 cb[0] = !!(r[3] & A52_R4_CLKBIT0);
289 cb[1] = !!(r[3] & A52_R4_CLKBIT1);
290 cb[2] = !!(r[3] & A52_R4_CLKBIT2);
291
292 maj = (cb[0] + cb[1] + cb[2]) >= 2;
293
294 if (force || (maj == cb[0]))
295 r[0] = _a5_12_clock(r[0], A5_R1_MASK, A5_R1_TAPS);
296
297 if (force || (maj == cb[1]))
298 r[1] = _a5_12_clock(r[1], A5_R2_MASK, A5_R2_TAPS);
299
300 if (force || (maj == cb[2]))
301 r[2] = _a5_12_clock(r[2], A5_R3_MASK, A5_R3_TAPS);
302
303 r[3] = _a5_12_clock(r[3], A5_R4_MASK, A5_R4_TAPS);
304}
305
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200306/*! GSM A5/2 Output function
Sylvain Munaut2735ac42011-11-17 21:01:46 +0100307 * \param[in] r Register state
308 * \return The A5/2 output function bit
309 */
Sylvain Munautf1d33442011-04-23 15:34:11 +0200310static inline uint8_t
Sylvain Munaut7f975d22011-11-17 20:36:50 +0100311_a5_2_get_output(uint32_t r[])
Sylvain Munautf1d33442011-04-23 15:34:11 +0200312{
Sylvain Munaut7f975d22011-11-17 20:36:50 +0100313 uint8_t b;
Sylvain Munautf1d33442011-04-23 15:34:11 +0200314
Sylvain Munaut7f975d22011-11-17 20:36:50 +0100315 b = (r[0] >> (A5_R1_LEN-1)) ^
316 (r[1] >> (A5_R2_LEN-1)) ^
317 (r[2] >> (A5_R3_LEN-1)) ^
318 _a5_12_majority( r[0] & 0x08000, ~r[0] & 0x04000, r[0] & 0x1000) ^
319 _a5_12_majority(~r[1] & 0x10000, r[1] & 0x02000, r[1] & 0x0200) ^
320 _a5_12_majority( r[2] & 0x40000, r[2] & 0x10000, ~r[2] & 0x2000);
Sylvain Munautf1d33442011-04-23 15:34:11 +0200321
Sylvain Munaut7f975d22011-11-17 20:36:50 +0100322 return b;
Sylvain Munautf1d33442011-04-23 15:34:11 +0200323}
324
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200325/*! Generate a GSM A5/1 cipher stream
Sylvain Munaut2735ac42011-11-17 21:01:46 +0100326 * \param[in] key 8 byte array for the key (as received from the SIM)
327 * \param[in] fn Frame number
328 * \param[out] dl Pointer to array of ubits to return Downlink cipher stream
329 * \param[out] ul Pointer to array of ubits to return Uplink cipher stream
330 *
331 * Either (or both) of dl/ul can be NULL if not needed.
332 */
Sylvain Munautf1d33442011-04-23 15:34:11 +0200333void
Maxfdb3d8c2016-04-21 16:51:04 +0200334_a5_2(const uint8_t *key, uint32_t fn, ubit_t *dl, ubit_t *ul)
Sylvain Munautf1d33442011-04-23 15:34:11 +0200335{
336 uint32_t r[4] = {0, 0, 0, 0};
337 uint32_t fn_count;
338 uint32_t b;
Sylvain Munautf1d33442011-04-23 15:34:11 +0200339 int i;
340
341 /* Key load */
342 for (i=0; i<64; i++)
343 {
344 b = ( key[7 - (i>>3)] >> (i&7) ) & 1;
345
346 _a5_2_clock(r, 1);
347
348 r[0] ^= b;
349 r[1] ^= b;
350 r[2] ^= b;
351 r[3] ^= b;
352 }
353
354 /* Frame count load */
355 fn_count = osmo_a5_fn_count(fn);
356
357 for (i=0; i<22; i++)
358 {
359 b = (fn_count >> i) & 1;
360
361 _a5_2_clock(r, 1);
362
363 r[0] ^= b;
364 r[1] ^= b;
365 r[2] ^= b;
366 r[3] ^= b;
367 }
368
369 r[0] |= 1 << 15;
370 r[1] |= 1 << 16;
371 r[2] |= 1 << 18;
372 r[3] |= 1 << 10;
373
374 /* Mix */
Sylvain Munaut7f975d22011-11-17 20:36:50 +0100375 for (i=0; i<99; i++)
Sylvain Munautf1d33442011-04-23 15:34:11 +0200376 {
377 _a5_2_clock(r, 0);
378 }
379
Sylvain Munautf1d33442011-04-23 15:34:11 +0200380 /* Output */
381 for (i=0; i<114; i++) {
382 _a5_2_clock(r, 0);
Sylvain Munautf1d33442011-04-23 15:34:11 +0200383 if (dl)
Sylvain Munaut7f975d22011-11-17 20:36:50 +0100384 dl[i] = _a5_2_get_output(r);
Sylvain Munautf1d33442011-04-23 15:34:11 +0200385 }
386
387 for (i=0; i<114; i++) {
388 _a5_2_clock(r, 0);
Sylvain Munautf1d33442011-04-23 15:34:11 +0200389 if (ul)
Sylvain Munaut7f975d22011-11-17 20:36:50 +0100390 ul[i] = _a5_2_get_output(r);
Sylvain Munautf1d33442011-04-23 15:34:11 +0200391 }
392}
Sylvain Munaut2735ac42011-11-17 21:01:46 +0100393
Maxfdb3d8c2016-04-21 16:51:04 +0200394void osmo_a5_2(const uint8_t *key, uint32_t fn, ubit_t *dl, ubit_t *ul)
395{
396 osmo_a5(2, key, fn, dl, ul);
397}
398
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200399/*! Main method to generate a A5/x cipher stream
Maxfdb3d8c2016-04-21 16:51:04 +0200400 * \param[in] n Which A5/x method to use
401 * \param[in] key 8 or 16 (for a5/4) byte array for the key (as received from the SIM)
402 * \param[in] fn Frame number
403 * \param[out] dl Pointer to array of ubits to return Downlink cipher stream
404 * \param[out] ul Pointer to array of ubits to return Uplink cipher stream
405 * \returns 0 for success, -ENOTSUP for invalid cipher selection.
406 *
407 * Currently A5/[0-4] are supported.
408 * Either (or both) of dl/ul can be NULL if not needed.
409 */
410int
411osmo_a5(int n, const uint8_t *key, uint32_t fn, ubit_t *dl, ubit_t *ul)
412{
413 switch (n)
414 {
415 case 0:
416 if (dl)
417 memset(dl, 0x00, 114);
418 if (ul)
419 memset(ul, 0x00, 114);
420 break;
421
422 case 1:
423 _a5_1(key, fn, dl, ul);
424 break;
425
426 case 2:
427 _a5_2(key, fn, dl, ul);
428 break;
429
430 case 3:
431 _a5_3(key, fn, dl, ul, true);
432 break;
433
434 case 4:
435 _a5_4(key, fn, dl, ul, true);
436 break;
437
438 default:
439 /* a5/[5..7] not supported here/yet */
440 return -ENOTSUP;
441 }
442
443 return 0;
444}
445
Sylvain Munautdca7d2c2012-04-18 21:53:23 +0200446/*! @} */