blob: ea09e172f6bf1ad47d79399d4a16dfcec6404982 [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 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License along
17 * with this program; if not, write to the Free Software Foundation, Inc.,
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 */
20
Harald Welte8cc27672017-10-16 16:00:36 +020021/*! \addtogroup a5
Sylvain Munaut2735ac42011-11-17 21:01:46 +010022 * @{
Harald Welte8cc27672017-10-16 16:00:36 +020023 * Osmocom GSM ciphering algorithm implementation
Neels Hofmeyr17518fe2017-06-20 04:35:06 +020024 *
Harald Welte8cc27672017-10-16 16:00:36 +020025 * Full reimplementation of A5/1,2,3,4 (split and threadsafe).
26 *
27 * The logic behind the algorithm is taken from "A pedagogical implementation
28 * of the GSM A5/1 and A5/2 "voice privacy" encryption algorithms." by
29 * Marc Briceno, Ian Goldberg, and David Wagner.
30 */
Sylvain Munaut2735ac42011-11-17 21:01:46 +010031
Sylvain Munautc1e9be92012-12-06 08:23:02 +010032#include <errno.h>
Sylvain Munautf1d33442011-04-23 15:34:11 +020033#include <string.h>
Maxf8699ca2015-03-25 17:20:31 +010034#include <stdbool.h>
Sylvain Munautf1d33442011-04-23 15:34:11 +020035
36#include <osmocom/gsm/a5.h>
Maxf8699ca2015-03-25 17:20:31 +010037#include <osmocom/gsm/kasumi.h>
Maxceae1232016-06-27 18:12:49 +020038#include <osmocom/crypt/auth.h>
Sylvain Munautf1d33442011-04-23 15:34:11 +020039
Harald Weltee15ac062014-12-04 14:15:36 +010040/* Somme OS (like Nuttx) don't have ENOTSUP */
41#ifndef ENOTSUP
42#define ENOTSUP EINVAL
43#endif
44
Maxf8699ca2015-03-25 17:20:31 +010045/* ------------------------------------------------------------------------ */
46/* A5/3&4 */
47/* ------------------------------------------------------------------------ */
48
Neels Hofmeyr87e45502017-06-20 00:17:59 +020049/*! Generate a GSM A5/4 cipher stream
Maxf8699ca2015-03-25 17:20:31 +010050 * \param[in] key 16 byte array for the key (as received from the SIM)
51 * \param[in] fn Frame number
52 * \param[out] dl Pointer to array of ubits to return Downlink cipher stream
53 * \param[out] ul Pointer to array of ubits to return Uplink cipher stream
54 * \param[in] fn_correct true if fn is a real GSM frame number and thus requires internal conversion
55 *
56 * Either (or both) of dl/ul should be NULL if not needed.
57 *
58 * Implementation based on specifications from 3GPP TS 55.216, 3GPP TR 55.919 and ETSI TS 135 202
59 * with slight simplifications (CE hardcoded to 0).
60 */
61void
62_a5_4(const uint8_t *ck, uint32_t fn, ubit_t *dl, ubit_t *ul, bool fn_correct)
63{
64 uint8_t i, gamma[32], uplink[15];
65 uint32_t fn_count = (fn_correct) ? osmo_a5_fn_count(fn) : fn;
66
67 if (ul) {
68 _kasumi_kgcore(0xF, 0, fn_count, 0, ck, gamma, 228);
69 for(i = 0; i < 15; i++) uplink[i] = (gamma[i + 14] << 2) + (gamma[i + 15] >> 6);
70 osmo_pbit2ubit(ul, uplink, 114);
71 }
72 if (dl) {
73 _kasumi_kgcore(0xF, 0, fn_count, 0, ck, gamma, 114);
74 osmo_pbit2ubit(dl, gamma, 114);
75 }
76}
77
Neels Hofmeyr87e45502017-06-20 00:17:59 +020078/*! Generate a GSM A5/3 cipher stream
Maxf8699ca2015-03-25 17:20:31 +010079 * \param[in] key 8 byte array for the key (as received from the SIM)
80 * \param[in] fn Frame number
81 * \param[out] dl Pointer to array of ubits to return Downlink cipher stream
82 * \param[out] ul Pointer to array of ubits to return Uplink cipher stream
83 * \param[in] fn_correct true if fn is a real GSM frame number and thus requires internal conversion
84 *
85 * Either (or both) of dl/ul should be NULL if not needed.
86 *
87 * Implementation based on specifications from 3GPP TS 55.216, 3GPP TR 55.919 and ETSI TS 135 202
88 * with slight simplifications (CE hardcoded to 0).
89 */
90void
91_a5_3(const uint8_t *key, uint32_t fn, ubit_t *dl, ubit_t *ul, bool fn_correct)
92{
93 uint8_t ck[16];
Maxceae1232016-06-27 18:12:49 +020094 osmo_c4(ck, key);
Maxf8699ca2015-03-25 17:20:31 +010095 /* internal function require 128 bit key so we expand by concatenating supplied 64 bit key */
96 _a5_4(ck, fn, dl, ul, fn_correct);
97}
98
Sylvain Munautf1d33442011-04-23 15:34:11 +020099/* ------------------------------------------------------------------------ */
100/* A5/1&2 common stuff */
101/* ------------------------------------------------------------------------ */
102
103#define A5_R1_LEN 19
104#define A5_R2_LEN 22
105#define A5_R3_LEN 23
106#define A5_R4_LEN 17 /* A5/2 only */
107
108#define A5_R1_MASK ((1<<A5_R1_LEN)-1)
109#define A5_R2_MASK ((1<<A5_R2_LEN)-1)
110#define A5_R3_MASK ((1<<A5_R3_LEN)-1)
111#define A5_R4_MASK ((1<<A5_R4_LEN)-1)
112
Sylvain Munaut2dafed52012-03-01 19:54:02 +0100113#define A5_R1_TAPS 0x072000 /* x^19 + x^18 + x^17 + x^14 + 1 */
114#define A5_R2_TAPS 0x300000 /* x^22 + x^21 + 1 */
115#define A5_R3_TAPS 0x700080 /* x^23 + x^22 + x^21 + x^8 + 1 */
116#define A5_R4_TAPS 0x010800 /* x^17 + x^12 + 1 */
Sylvain Munautf1d33442011-04-23 15:34:11 +0200117
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200118/*! Computes parity of a 32-bit word
Sylvain Munaut2735ac42011-11-17 21:01:46 +0100119 * \param[in] x 32 bit word
120 * \return Parity bit (xor of all bits) as 0 or 1
121 */
Sylvain Munautf1d33442011-04-23 15:34:11 +0200122static inline uint32_t
123_a5_12_parity(uint32_t x)
124{
125 x ^= x >> 16;
126 x ^= x >> 8;
127 x ^= x >> 4;
Sylvain Munautad4a6a82011-11-20 08:46:56 +0100128 x &= 0xf;
129 return (0x6996 >> x) & 1;
Sylvain Munautf1d33442011-04-23 15:34:11 +0200130}
131
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200132/*! Compute majority bit from 3 taps
Sylvain Munaut2735ac42011-11-17 21:01:46 +0100133 * \param[in] v1 LFSR state ANDed with tap-bit
134 * \param[in] v2 LFSR state ANDed with tap-bit
135 * \param[in] v3 LFSR state ANDed with tap-bit
136 * \return The majority bit (0 or 1)
137 */
Sylvain Munautf1d33442011-04-23 15:34:11 +0200138static inline uint32_t
139_a5_12_majority(uint32_t v1, uint32_t v2, uint32_t v3)
140{
141 return (!!v1 + !!v2 + !!v3) >= 2;
142}
143
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200144/*! Compute the next LFSR state
Sylvain Munaut2735ac42011-11-17 21:01:46 +0100145 * \param[in] r Current state
146 * \param[in] mask LFSR mask
147 * \param[in] taps LFSR taps
148 * \return Next state
149 */
Sylvain Munautf1d33442011-04-23 15:34:11 +0200150static inline uint32_t
151_a5_12_clock(uint32_t r, uint32_t mask, uint32_t taps)
152{
153 return ((r << 1) & mask) | _a5_12_parity(r & taps);
154}
155
156
157/* ------------------------------------------------------------------------ */
158/* A5/1 */
159/* ------------------------------------------------------------------------ */
160
161#define A51_R1_CLKBIT 0x000100
162#define A51_R2_CLKBIT 0x000400
163#define A51_R3_CLKBIT 0x000400
164
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200165/*! GSM A5/1 Clocking function
Sylvain Munaut2735ac42011-11-17 21:01:46 +0100166 * \param[in] r Register state
167 * \param[in] force Non-zero value disable conditional clocking
168 */
Sylvain Munautf1d33442011-04-23 15:34:11 +0200169static inline void
170_a5_1_clock(uint32_t r[], int force)
171{
172 int cb[3], maj;
173
174 cb[0] = !!(r[0] & A51_R1_CLKBIT);
175 cb[1] = !!(r[1] & A51_R2_CLKBIT);
176 cb[2] = !!(r[2] & A51_R3_CLKBIT);
177
178 maj = _a5_12_majority(cb[0], cb[1], cb[2]);
179
180 if (force || (maj == cb[0]))
181 r[0] = _a5_12_clock(r[0], A5_R1_MASK, A5_R1_TAPS);
182
183 if (force || (maj == cb[1]))
184 r[1] = _a5_12_clock(r[1], A5_R2_MASK, A5_R2_TAPS);
185
186 if (force || (maj == cb[2]))
187 r[2] = _a5_12_clock(r[2], A5_R3_MASK, A5_R3_TAPS);
188}
189
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200190/*! GSM A5/1 Output function
Sylvain Munaut2735ac42011-11-17 21:01:46 +0100191 * \param[in] r Register state
192 * \return The A5/1 output function bit
193 */
Sylvain Munautf1d33442011-04-23 15:34:11 +0200194static inline uint8_t
195_a5_1_get_output(uint32_t r[])
196{
197 return (r[0] >> (A5_R1_LEN-1)) ^
198 (r[1] >> (A5_R2_LEN-1)) ^
199 (r[2] >> (A5_R3_LEN-1));
200}
201
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200202/*! Generate a GSM A5/1 cipher stream
Sylvain Munaut2735ac42011-11-17 21:01:46 +0100203 * \param[in] key 8 byte array for the key (as received from the SIM)
204 * \param[in] fn Frame number
205 * \param[out] dl Pointer to array of ubits to return Downlink cipher stream
206 * \param[out] ul Pointer to array of ubits to return Uplink cipher stream
207 *
208 * Either (or both) of dl/ul can be NULL if not needed.
209 */
Sylvain Munautf1d33442011-04-23 15:34:11 +0200210void
Maxfdb3d8c2016-04-21 16:51:04 +0200211_a5_1(const uint8_t *key, uint32_t fn, ubit_t *dl, ubit_t *ul)
Sylvain Munautf1d33442011-04-23 15:34:11 +0200212{
213 uint32_t r[3] = {0, 0, 0};
214 uint32_t fn_count;
215 uint32_t b;
216 int i;
217
218 /* Key load */
219 for (i=0; i<64; i++)
220 {
221 b = ( key[7 - (i>>3)] >> (i&7) ) & 1;
222
223 _a5_1_clock(r, 1);
224
225 r[0] ^= b;
226 r[1] ^= b;
227 r[2] ^= b;
228 }
229
230 /* Frame count load */
231 fn_count = osmo_a5_fn_count(fn);
232
233 for (i=0; i<22; i++)
234 {
235 b = (fn_count >> i) & 1;
236
237 _a5_1_clock(r, 1);
238
239 r[0] ^= b;
240 r[1] ^= b;
241 r[2] ^= b;
242 }
243
244 /* Mix */
245 for (i=0; i<100; i++)
246 {
247 _a5_1_clock(r, 0);
248 }
249
250 /* Output */
251 for (i=0; i<114; i++) {
252 _a5_1_clock(r, 0);
253 if (dl)
254 dl[i] = _a5_1_get_output(r);
255 }
256
257 for (i=0; i<114; i++) {
258 _a5_1_clock(r, 0);
259 if (ul)
260 ul[i] = _a5_1_get_output(r);
261 }
262}
263
Maxfdb3d8c2016-04-21 16:51:04 +0200264void osmo_a5_1(const uint8_t *key, uint32_t fn, ubit_t *dl, ubit_t *ul)
265{
266 osmo_a5(1, key, fn, dl, ul);
267}
Sylvain Munautf1d33442011-04-23 15:34:11 +0200268
269/* ------------------------------------------------------------------------ */
270/* A5/2 */
271/* ------------------------------------------------------------------------ */
272
273#define A52_R4_CLKBIT0 0x000400
274#define A52_R4_CLKBIT1 0x000008
275#define A52_R4_CLKBIT2 0x000080
276
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200277/*! GSM A5/2 Clocking function
Sylvain Munaut2735ac42011-11-17 21:01:46 +0100278 * \param[in] r Register state
279 * \param[in] force Non-zero value disable conditional clocking
280 */
Sylvain Munautf1d33442011-04-23 15:34:11 +0200281static inline void
282_a5_2_clock(uint32_t r[], int force)
283{
284 int cb[3], maj;
285
286 cb[0] = !!(r[3] & A52_R4_CLKBIT0);
287 cb[1] = !!(r[3] & A52_R4_CLKBIT1);
288 cb[2] = !!(r[3] & A52_R4_CLKBIT2);
289
290 maj = (cb[0] + cb[1] + cb[2]) >= 2;
291
292 if (force || (maj == cb[0]))
293 r[0] = _a5_12_clock(r[0], A5_R1_MASK, A5_R1_TAPS);
294
295 if (force || (maj == cb[1]))
296 r[1] = _a5_12_clock(r[1], A5_R2_MASK, A5_R2_TAPS);
297
298 if (force || (maj == cb[2]))
299 r[2] = _a5_12_clock(r[2], A5_R3_MASK, A5_R3_TAPS);
300
301 r[3] = _a5_12_clock(r[3], A5_R4_MASK, A5_R4_TAPS);
302}
303
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200304/*! GSM A5/2 Output function
Sylvain Munaut2735ac42011-11-17 21:01:46 +0100305 * \param[in] r Register state
306 * \return The A5/2 output function bit
307 */
Sylvain Munautf1d33442011-04-23 15:34:11 +0200308static inline uint8_t
Sylvain Munaut7f975d22011-11-17 20:36:50 +0100309_a5_2_get_output(uint32_t r[])
Sylvain Munautf1d33442011-04-23 15:34:11 +0200310{
Sylvain Munaut7f975d22011-11-17 20:36:50 +0100311 uint8_t b;
Sylvain Munautf1d33442011-04-23 15:34:11 +0200312
Sylvain Munaut7f975d22011-11-17 20:36:50 +0100313 b = (r[0] >> (A5_R1_LEN-1)) ^
314 (r[1] >> (A5_R2_LEN-1)) ^
315 (r[2] >> (A5_R3_LEN-1)) ^
316 _a5_12_majority( r[0] & 0x08000, ~r[0] & 0x04000, r[0] & 0x1000) ^
317 _a5_12_majority(~r[1] & 0x10000, r[1] & 0x02000, r[1] & 0x0200) ^
318 _a5_12_majority( r[2] & 0x40000, r[2] & 0x10000, ~r[2] & 0x2000);
Sylvain Munautf1d33442011-04-23 15:34:11 +0200319
Sylvain Munaut7f975d22011-11-17 20:36:50 +0100320 return b;
Sylvain Munautf1d33442011-04-23 15:34:11 +0200321}
322
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200323/*! Generate a GSM A5/1 cipher stream
Sylvain Munaut2735ac42011-11-17 21:01:46 +0100324 * \param[in] key 8 byte array for the key (as received from the SIM)
325 * \param[in] fn Frame number
326 * \param[out] dl Pointer to array of ubits to return Downlink cipher stream
327 * \param[out] ul Pointer to array of ubits to return Uplink cipher stream
328 *
329 * Either (or both) of dl/ul can be NULL if not needed.
330 */
Sylvain Munautf1d33442011-04-23 15:34:11 +0200331void
Maxfdb3d8c2016-04-21 16:51:04 +0200332_a5_2(const uint8_t *key, uint32_t fn, ubit_t *dl, ubit_t *ul)
Sylvain Munautf1d33442011-04-23 15:34:11 +0200333{
334 uint32_t r[4] = {0, 0, 0, 0};
335 uint32_t fn_count;
336 uint32_t b;
Sylvain Munautf1d33442011-04-23 15:34:11 +0200337 int i;
338
339 /* Key load */
340 for (i=0; i<64; i++)
341 {
342 b = ( key[7 - (i>>3)] >> (i&7) ) & 1;
343
344 _a5_2_clock(r, 1);
345
346 r[0] ^= b;
347 r[1] ^= b;
348 r[2] ^= b;
349 r[3] ^= b;
350 }
351
352 /* Frame count load */
353 fn_count = osmo_a5_fn_count(fn);
354
355 for (i=0; i<22; i++)
356 {
357 b = (fn_count >> i) & 1;
358
359 _a5_2_clock(r, 1);
360
361 r[0] ^= b;
362 r[1] ^= b;
363 r[2] ^= b;
364 r[3] ^= b;
365 }
366
367 r[0] |= 1 << 15;
368 r[1] |= 1 << 16;
369 r[2] |= 1 << 18;
370 r[3] |= 1 << 10;
371
372 /* Mix */
Sylvain Munaut7f975d22011-11-17 20:36:50 +0100373 for (i=0; i<99; i++)
Sylvain Munautf1d33442011-04-23 15:34:11 +0200374 {
375 _a5_2_clock(r, 0);
376 }
377
Sylvain Munautf1d33442011-04-23 15:34:11 +0200378 /* Output */
379 for (i=0; i<114; i++) {
380 _a5_2_clock(r, 0);
Sylvain Munautf1d33442011-04-23 15:34:11 +0200381 if (dl)
Sylvain Munaut7f975d22011-11-17 20:36:50 +0100382 dl[i] = _a5_2_get_output(r);
Sylvain Munautf1d33442011-04-23 15:34:11 +0200383 }
384
385 for (i=0; i<114; i++) {
386 _a5_2_clock(r, 0);
Sylvain Munautf1d33442011-04-23 15:34:11 +0200387 if (ul)
Sylvain Munaut7f975d22011-11-17 20:36:50 +0100388 ul[i] = _a5_2_get_output(r);
Sylvain Munautf1d33442011-04-23 15:34:11 +0200389 }
390}
Sylvain Munaut2735ac42011-11-17 21:01:46 +0100391
Maxfdb3d8c2016-04-21 16:51:04 +0200392void osmo_a5_2(const uint8_t *key, uint32_t fn, ubit_t *dl, ubit_t *ul)
393{
394 osmo_a5(2, key, fn, dl, ul);
395}
396
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200397/*! Main method to generate a A5/x cipher stream
Maxfdb3d8c2016-04-21 16:51:04 +0200398 * \param[in] n Which A5/x method to use
399 * \param[in] key 8 or 16 (for a5/4) byte array for the key (as received from the SIM)
400 * \param[in] fn Frame number
401 * \param[out] dl Pointer to array of ubits to return Downlink cipher stream
402 * \param[out] ul Pointer to array of ubits to return Uplink cipher stream
403 * \returns 0 for success, -ENOTSUP for invalid cipher selection.
404 *
405 * Currently A5/[0-4] are supported.
406 * Either (or both) of dl/ul can be NULL if not needed.
407 */
408int
409osmo_a5(int n, const uint8_t *key, uint32_t fn, ubit_t *dl, ubit_t *ul)
410{
411 switch (n)
412 {
413 case 0:
414 if (dl)
415 memset(dl, 0x00, 114);
416 if (ul)
417 memset(ul, 0x00, 114);
418 break;
419
420 case 1:
421 _a5_1(key, fn, dl, ul);
422 break;
423
424 case 2:
425 _a5_2(key, fn, dl, ul);
426 break;
427
428 case 3:
429 _a5_3(key, fn, dl, ul, true);
430 break;
431
432 case 4:
433 _a5_4(key, fn, dl, ul, true);
434 break;
435
436 default:
437 /* a5/[5..7] not supported here/yet */
438 return -ENOTSUP;
439 }
440
441 return 0;
442}
443
Sylvain Munautdca7d2c2012-04-18 21:53:23 +0200444/*! @} */