diff mbox

[3/3] Add Kasumi cipher implementation

Message ID 1401901663-16418-3-git-send-email-Max.Suraev@fairwaves.co
State Accepted, archived
Headers show

Commit Message

Max June 4, 2014, 5:07 p.m. UTC
---
 .gitignore                   |   1 +
 include/osmocom/gsm/kasumi.h |  41 ++++++++++
 src/gsm/Makefile.am          |   2 +-
 src/gsm/kasumi.c             | 190 +++++++++++++++++++++++++++++++++++++++++++
 src/gsm/libosmogsm.map       |   4 +
 tests/Makefile.am            |   7 +-
 tests/kasumi/kasumi_test.c   | 136 +++++++++++++++++++++++++++++++
 tests/kasumi/kasumi_test.ok  |  10 +++
 tests/testsuite.at           |   6 ++
 9 files changed, 394 insertions(+), 3 deletions(-)
 create mode 100644 include/osmocom/gsm/kasumi.h
 create mode 100644 src/gsm/kasumi.c
 create mode 100644 tests/kasumi/kasumi_test.c
 create mode 100644 tests/kasumi/kasumi_test.ok

Comments

Sylvain Munaut June 15, 2014, 8:49 p.m. UTC | #1
Hi Max,

> +
> +#pragma once

All the other files use the classic #ifdef #define, so either replace
all before that, or stick to the current way of doing it.

> +
> +#include <stdint.h>
> +
> +/*! \brief Single iteration of KASUMI cipher
> + * \param[in] P Block, 64 bits to be processed in this round
> + * \param[in] KLi1 Expanded subkeys
> + * \param[in] KLi2 Expanded subkeys
> + * \param[in] KOi1 Expanded subkeys
> + * \param[in] KOi2 Expanded subkeys
> + * \param[in] KOi3 Expanded subkeys
> + * \param[in] KIi1 Expanded subkeys
> + * \param[in] KIi2 Expanded subkeys
> + * \param[in] KIi3 Expanded subkeys
> + * \returns processed block of 64 bits
> + */

We usually put the documentation only in the .c file and not the
header. (except for stuff that's only in the header obviously).

If it's picked up by doxygen all the same then I don't have big
objections against putting it in the header (to be checked though) if
you insist, but then be consistent ... you have some of the doc
duplicated for some of the methods


> +uint64_t _kasumi(uint64_t P, const uint16_t *KLi1, const uint16_t *KLi2, const uint16_t *KOi1, const uint16_t *KOi2, const uint16_t *KOi3, const uint16_t *KIi1, const uint16_t *KIi2, const uint16_t *KIi3);

Either the methods are meant to be part of the public API and then
they should carry the osmo prefix.
Or they're meant to be internal, then the symbol should not be
exported in the map file and the header should not be installed.


> +inline int _compare_mem(uint8_t * x, uint8_t * y, size_t len)
> +{
> +       if (0 != memcmp(x, y, len)) {
> +               printf ("X: %s\t", osmo_hexdump_nospc(x, len));
> +               printf ("Y: %s\n", osmo_hexdump_nospc(y, len));
> +               return 0;
> +       }
> +       return 1;
> +}

Not of much importance here I admit (and not a blocker), but for
future reference, make sure all that can be static is marked static.
'inline' doesn't make it static so you'll end up with both the
function being inlined at every use in the file and a symbol
_compare_mem that can be called from other .o (and obviously won't be
and so the linker will strip it but still it's a good habit to take).


Cheers,

   Sylvain
Max June 15, 2014, 9:43 p.m. UTC | #2
15.06.2014 22:49, Sylvain Munaut пишет:
> Hi Max,
> 
>> +
>> +#pragma once
> 
> All the other files use the classic #ifdef #define, so either replace
> all before that, or stick to the current way of doing it.

What do you mean by "all before that"?

> Either the methods are meant to be part of the public API and then
> they should carry the osmo prefix.
> Or they're meant to be internal, then the symbol should not be
> exported in the map file and the header should not be installed.

I think I've asked it already but I don't recall an answer: I do not want to make it
a part of public API (hence - no osmo_ prefix) but I still need to call it explicitly
in test/ - how can I do that without adding it to map file?
Sylvain Munaut June 15, 2014, 10:33 p.m. UTC | #3
Hi,

> What do you mean by "all before that"?

Like change all libosmocore headers to use #pragma once.

We pretty much only target gcc/llvm AFAIK so that shouldn't be a problem.

Any one see an objection to that ?


> I think I've asked it already but I don't recall an answer: I do not want to make it
> a part of public API (hence - no osmo_ prefix) but I still need to call it explicitly
> in test/ - how can I do that without adding it to map file?

I think the header should be in noinst_HEADER then (don't remember the
exact name).

As for the testing, linking the test statically should do right ? I
didn't test but since this only changes the presence of the symbol in
the dynamic symbol table, linking to the static lib should work.

Cheers,

   Sylvain
Holger Freyther June 16, 2014, 5:41 a.m. UTC | #4
On Sun, Jun 15, 2014 at 10:49:45PM +0200, Sylvain Munaut wrote:

Hi,

> > +#pragma once
> 
> All the other files use the classic #ifdef #define, so either replace
> all before that, or stick to the current way of doing it.

do we really want to be that strong? I think all future header files
should use "#pragma once".
Sylvain Munaut June 16, 2014, 8:19 a.m. UTC | #5
>> > +#pragma once
>>
>> All the other files use the classic #ifdef #define, so either replace
>> all before that, or stick to the current way of doing it.
>
> do we really want to be that strong? I think all future header files
> should use "#pragma once".

I like consistency :p

Especially since such a change won't really lead to conflict since it
only change a couple lines at the beginning/end of each file and it's
also pretty easy to do with a script. (I did it in like 10 min).


Cheers,

   Sylvain
Max June 16, 2014, 8:23 a.m. UTC | #6
16.06.2014 10:19, Sylvain Munaut пишет:

> Especially since such a change won't really lead to conflict since it
> only change a couple lines at the beginning/end of each file and it's
> also pretty easy to do with a script. (I did it in like 10 min).
> 

Good. Can you commit this?
Sylvain Munaut June 16, 2014, 11:55 a.m. UTC | #7
>> Especially since such a change won't really lead to conflict since it
>> only change a couple lines at the beginning/end of each file and it's
>> also pretty easy to do with a script. (I did it in like 10 min).
>>
>
> Good. Can you commit this?


Holger, can I get a ack on this ?


Cheers,

   Sylvain
Holger Freyther June 16, 2014, 12:08 p.m. UTC | #8
On Mon, Jun 16, 2014 at 01:55:03PM +0200, Sylvain Munaut wrote:
> >> Especially since such a change won't really lead to conflict since it
> >> only change a couple lines at the beginning/end of each file and it's
> >> also pretty easy to do with a script. (I did it in like 10 min).
> >>
> >
> > Good. Can you commit this?
> 
> 
> Holger, can I get a ack on this ?

sure. it looked fine.
Max June 16, 2014, 12:54 p.m. UTC | #9
16.06.2014 00:33, Sylvain Munaut пишет:

> I think the header should be in noinst_HEADER then (don't remember the
> exact name).

noinst_HEADERS it is, thanks.

> As for the testing, linking the test statically should do right ? I
> didn't test but since this only changes the presence of the symbol in
> the dynamic symbol table, linking to the static lib should work.

Where should I pass -static to gcc linker? Is there some statically-linked test
available as an example?
Sylvain Munaut June 16, 2014, 1:37 p.m. UTC | #10
Hi,

>> As for the testing, linking the test statically should do right ? I
>> didn't test but since this only changes the presence of the symbol in
>> the dynamic symbol table, linking to the static lib should work.
>
> Where should I pass -static to gcc linker? Is there some statically-linked test
> available as an example?

testname_LDFLAGS = -static

I fixed it up myself and pushed the 3 patches.


Cheers,

    Sylvain
diff mbox

Patch

diff --git a/.gitignore b/.gitignore
index 3fa1bbc..1299028 100644
--- a/.gitignore
+++ b/.gitignore
@@ -56,6 +56,7 @@  tests/testsuite.dir/
 tests/testsuite.log
 
 tests/utils/utils_test
+tests/kasumi/kasumi_test
 tests/sms/sms_test
 tests/timer/timer_test
 tests/msgfile/msgfile_test
diff --git a/include/osmocom/gsm/kasumi.h b/include/osmocom/gsm/kasumi.h
new file mode 100644
index 0000000..6acc959
--- /dev/null
+++ b/include/osmocom/gsm/kasumi.h
@@ -0,0 +1,41 @@ 
+/*
+ * KASUMI header
+ *
+ * See kasumi.c for details
+ * The parameters are described in TS 135 202.
+ */
+
+#pragma once
+
+#include <stdint.h>
+
+/*! \brief Single iteration of KASUMI cipher
+ * \param[in] P Block, 64 bits to be processed in this round
+ * \param[in] KLi1 Expanded subkeys
+ * \param[in] KLi2 Expanded subkeys
+ * \param[in] KOi1 Expanded subkeys
+ * \param[in] KOi2 Expanded subkeys
+ * \param[in] KOi3 Expanded subkeys
+ * \param[in] KIi1 Expanded subkeys
+ * \param[in] KIi2 Expanded subkeys
+ * \param[in] KIi3 Expanded subkeys
+ * \returns processed block of 64 bits
+ */
+uint64_t _kasumi(uint64_t P, const uint16_t *KLi1, const uint16_t *KLi2, const uint16_t *KOi1, const uint16_t *KOi2, const uint16_t *KOi3, const uint16_t *KIi1, const uint16_t *KIi2, const uint16_t *KIi3);
+
+/*! \brief Implementation of the KGCORE algorithm (used by A5/3, A5/4, GEA3, GEA4 and ECSD)
+ *  \param[in] CA
+ *  \param[in] cb
+ *  \param[in] cc
+ *  \param[in] cd
+ *  \param[in] ck 8-bytes long key
+ *  \param[out] co cl-dependent
+ *  \param[in] cl
+ */
+void _kasumi_kgcore(uint8_t CA, uint8_t cb, uint32_t cc, uint8_t cd, const uint8_t *ck, uint8_t *co, uint16_t cl);
+
+/*! \brief Expand key into set of subkeys
+ *  \param[in] key (128 bits) as array of bytes
+ *  \param[out] arrays of round-specific subkeys - see TS 135 202 for details
+ */
+void _kasumi_key_expand(const uint8_t *key, uint16_t *KLi1, uint16_t *KLi2, uint16_t *KOi1, uint16_t *KOi2, uint16_t *KOi3, uint16_t *KIi1, uint16_t *KIi2, uint16_t *KIi3);
diff --git a/src/gsm/Makefile.am b/src/gsm/Makefile.am
index 46698cd..94729c9 100644
--- a/src/gsm/Makefile.am
+++ b/src/gsm/Makefile.am
@@ -15,7 +15,7 @@  libosmogsm_la_SOURCES = a5.c rxlev_stat.c tlv_parser.c comp128.c comp128v23.c \
 			gsm_utils.c rsl.c gsm48.c gsm48_ie.c gsm0808.c sysinfo.c \
 			gprs_cipher_core.c gsm0480.c abis_nm.c gsm0502.c \
 			gsm0411_utils.c gsm0411_smc.c gsm0411_smr.c \
-			lapd_core.c lapdm.c \
+			lapd_core.c lapdm.c kasumi.c \
 			auth_core.c auth_comp128v1.c auth_comp128v23.c \
 			auth_milenage.c milenage/aes-encblock.c \
 			milenage/aes-internal.c milenage/aes-internal-enc.c \
diff --git a/src/gsm/kasumi.c b/src/gsm/kasumi.c
new file mode 100644
index 0000000..c56f3ed
--- /dev/null
+++ b/src/gsm/kasumi.c
@@ -0,0 +1,190 @@ 
+/* Kasumi cipher and KGcore functions */
+
+/* (C) 2013 by Max <Max.Suraev@fairwaves.ru>
+ *
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#include <stdint.h>
+#include <osmocom/core/bits.h>
+#include <osmocom/gsm/kasumi.h>
+
+/* See TS 135 202 for constants and full Kasumi spec. */
+inline static uint16_t kasumi_FI(uint16_t I, uint16_t skey)
+{
+	static const uint16_t S7[] = {
+		54, 50, 62, 56, 22, 34, 94, 96, 38, 6, 63, 93, 2, 18, 123, 33,
+		55, 113, 39, 114, 21, 67, 65, 12, 47, 73, 46, 27, 25, 111, 124, 81,
+		53, 9, 121, 79, 52, 60, 58, 48, 101, 127, 40, 120, 104, 70, 71, 43,
+		20, 122, 72, 61, 23, 109, 13, 100, 77, 1, 16, 7, 82, 10, 105, 98,
+		117, 116, 76, 11, 89, 106, 0,125,118, 99, 86, 69, 30, 57, 126, 87,
+		112, 51, 17, 5, 95, 14, 90, 84, 91, 8, 35,103, 32, 97, 28, 66,
+		102, 31, 26, 45, 75, 4, 85, 92, 37, 74, 80, 49, 68, 29, 115, 44,
+		64, 107, 108, 24, 110, 83, 36, 78, 42, 19, 15, 41, 88, 119, 59, 3
+	};
+	static const uint16_t S9[] = {
+		167, 239, 161, 379, 391, 334,  9, 338, 38, 226, 48, 358, 452, 385, 90, 397,
+		183, 253, 147, 331, 415, 340, 51, 362, 306, 500, 262, 82, 216, 159, 356, 177,
+		175, 241, 489, 37, 206, 17, 0, 333, 44, 254, 378, 58, 143, 220, 81, 400,
+		95, 3, 315, 245, 54, 235, 218, 405, 472, 264, 172, 494, 371, 290, 399, 76,
+		165, 197, 395, 121, 257, 480, 423, 212, 240, 28, 462, 176, 406, 507, 288, 223,
+		501, 407, 249, 265, 89, 186, 221, 428,164, 74, 440, 196, 458, 421, 350, 163,
+		232, 158, 134, 354, 13, 250, 491, 142,191, 69, 193, 425, 152, 227, 366, 135,
+		344, 300, 276, 242, 437, 320, 113, 278, 11, 243, 87, 317, 36, 93, 496, 27,
+		487, 446, 482, 41, 68, 156, 457, 131, 326, 403, 339, 20, 39, 115, 442, 124,
+		475, 384, 508, 53, 112, 170, 479, 151, 126, 169, 73, 268, 279, 321, 168, 364,
+		363, 292, 46, 499, 393, 327, 324, 24, 456, 267, 157, 460, 488, 426, 309, 229,
+		439, 506, 208, 271, 349, 401, 434, 236, 16, 209, 359, 52, 56, 120, 199, 277,
+		465, 416, 252, 287, 246,  6, 83, 305, 420, 345, 153,502, 65, 61, 244, 282,
+		173, 222, 418, 67, 386, 368, 261, 101, 476, 291, 195,430, 49, 79, 166, 330,
+		280, 383, 373, 128, 382, 408, 155, 495, 367, 388, 274, 107, 459, 417, 62, 454,
+		132, 225, 203, 316, 234, 14, 301, 91, 503, 286, 424, 211, 347, 307, 140, 374,
+		35, 103, 125, 427, 19, 214, 453, 146, 498, 314, 444, 230, 256, 329, 198, 285,
+		50, 116, 78, 410, 10, 205, 510, 171, 231, 45, 139, 467, 29, 86, 505, 32,
+		72, 26, 342, 150, 313, 490, 431, 238, 411, 325, 149, 473, 40, 119, 174, 355,
+		185, 233, 389, 71, 448, 273, 372, 55, 110, 178, 322, 12, 469, 392, 369, 190,
+		1, 109, 375, 137, 181, 88, 75, 308, 260, 484, 98, 272, 370, 275, 412, 111,
+		336, 318, 4, 504, 492, 259, 304, 77, 337, 435, 21, 357, 303, 332, 483, 18,
+		47, 85, 25, 497, 474, 289, 100, 269, 296, 478, 270, 106, 31, 104, 433, 84,
+		414, 486, 394, 96, 99, 154, 511, 148, 413, 361, 409, 255, 162, 215, 302, 201,
+		266, 351, 343, 144, 441, 365, 108, 298, 251, 34, 182, 509, 138, 210, 335, 133,
+		311, 352, 328, 141, 396, 346, 123, 319, 450, 281, 429, 228, 443, 481, 92, 404,
+		485, 422, 248, 297, 23, 213, 130, 466, 22, 217, 283, 70, 294, 360, 419, 127,
+		312, 377, 7, 468, 194, 2, 117, 295, 463, 258, 224, 447, 247, 187, 80, 398,
+		284, 353, 105, 390, 299, 471, 470, 184, 57, 200, 348, 63, 204, 188, 33, 451,
+		97, 30, 310, 219, 94, 160, 129, 493, 64, 179, 263, 102, 189, 207, 114, 402,
+		438, 477, 387, 122, 192, 42, 381, 5, 145, 118, 180, 449, 293, 323, 136, 380,
+		43, 66, 60, 455, 341, 445, 202, 432, 8, 237, 15, 376, 436, 464, 59, 461
+	};
+	uint16_t L, R;
+
+	/* Split 16 bit input into two unequal halves: 9 and 7 bits, same for subkey */
+	L = I >> 7; /* take 9 bits */
+	R = I & 0x7F; /* take 7 bits */
+
+	L = S9[L]  ^ R;
+	R = S7[R] ^ (L & 0x7F);
+
+	L ^= (skey & 0x1FF);
+	R ^= (skey >> 9);
+
+	L = S9[L]  ^ R;
+	R = S7[R] ^ (L & 0x7F);
+
+	return (R << 9) + L;
+}
+
+inline static uint32_t kasumi_FO(uint32_t I, const uint16_t *KOi1, const uint16_t *KOi2, const uint16_t *KOi3, const uint16_t *KIi1, const uint16_t *KIi2, const uint16_t *KIi3, unsigned i)
+{
+	uint16_t L = I >> 16, R = I; /* Split 32 bit input into Left and Right parts */
+
+	L ^= KOi1[i];
+	L = kasumi_FI(L, KIi1[i]);
+	L ^= R;
+
+	R ^= KOi2[i];
+	R = kasumi_FI(R, KIi2[i]);
+	R ^= L;
+
+	L ^= KOi3[i];
+	L = kasumi_FI(L, KIi3[i]);
+	L ^= R;
+
+	return (((uint32_t)R) << 16) + L;
+}
+
+inline static uint32_t kasumi_FL(uint32_t I, const uint16_t *KLi1, const uint16_t *KLi2, unsigned i)
+{
+	uint16_t L = I >> 16, R = I, tmp; /* Split 32 bit input into Left and Right parts */
+
+	tmp = L & KLi1[i];
+	R ^= osmo_rol16(tmp, 1);
+
+	tmp = R | KLi2[i];
+	L ^= osmo_rol16(tmp, 1);
+
+	return (((uint32_t)L) << 16) + R;
+}
+
+uint64_t _kasumi(uint64_t P, const uint16_t *KLi1, const uint16_t *KLi2, const uint16_t *KOi1, const uint16_t *KOi2, const uint16_t *KOi3, const uint16_t *KIi1, const uint16_t *KIi2, const uint16_t *KIi3)
+{
+	uint32_t i, L = P >> 32, R = P; /* Split 64 bit input into Left and Right parts */
+
+	for (i = 0; i < 8; i++) {
+		R ^= kasumi_FO(kasumi_FL(L, KLi1, KLi2, i), KOi1, KOi2, KOi3, KIi1, KIi2, KIi3, i); /* odd round */
+		i++;
+		L ^= kasumi_FL(kasumi_FO(R, KOi1, KOi2, KOi3, KIi1, KIi2, KIi3, i), KLi1, KLi2, i); /* even round */
+	}
+	return (((uint64_t)L) << 32) + R; /* Concatenate Left and Right 32 bits into 64 bit ciphertext */
+}
+
+/*! \brief Expand key into set of subkeys
+ *  \param[in] key (128 bits) as array of bytes
+ *  \param[out] arrays of round-specific subkeys - see TS 135 202 for details
+ */
+void _kasumi_key_expand(const uint8_t *key, uint16_t *KLi1, uint16_t *KLi2, uint16_t *KOi1, uint16_t *KOi2, uint16_t *KOi3, uint16_t *KIi1, uint16_t *KIi2, uint16_t *KIi3)
+{
+	uint16_t i, C[] = { 0x0123, 0x4567, 0x89AB, 0xCDEF, 0xFEDC, 0xBA98, 0x7654, 0x3210 };
+
+	/* Work with 16 bit subkeys and create prime subkeys */
+	for (i = 0; i < 8; i++)
+		C[i] ^= osmo_load16be(key + i * 2);
+	/* C[] now stores K-prime[] */
+
+	/* Create round-specific subkeys */
+	for (i = 0; i < 8; i++) {
+		KLi1[i] = osmo_rol16(osmo_load16be(key + i * 2), 1);
+		KLi2[i] = C[(i + 2) & 0x7];
+
+		KOi1[i] = osmo_rol16(osmo_load16be(key + ((2 * (i + 1)) & 0xE)), 5);
+		KOi2[i] = osmo_rol16(osmo_load16be(key + ((2 * (i + 5)) & 0xE)), 8);
+		KOi3[i] = osmo_rol16(osmo_load16be(key + ((2 * (i + 6)) & 0xE)), 13);
+
+		KIi1[i] = C[(i + 4) & 0x7];
+		KIi2[i] = C[(i + 3) & 0x7];
+		KIi3[i] = C[(i + 7) & 0x7];
+	}
+}
+
+void _kasumi_kgcore(uint8_t CA, uint8_t cb, uint32_t cc, uint8_t cd, const uint8_t *ck, uint8_t *co, uint16_t cl)
+{
+	uint16_t KLi1[8], KLi2[8], KOi1[8], KOi2[8], KOi3[8], KIi1[8], KIi2[8], KIi3[8], i;
+	uint64_t A = ((uint64_t)cc) << 32, BLK = 0, _ca = ((uint64_t)CA << 16) ;
+	A |= _ca;
+	_ca = (uint64_t)((cb << 3) | (cd << 2)) << 24;
+	A |= _ca;
+	/* Register loading complete: see TR 55.919 8.2 and TS 55.216 3.2 */
+
+	uint8_t ck_km[16];
+	for (i = 0; i < 16; i++)
+		ck_km[i] = ck[i] ^ 0x55;
+	/* Modified key established */
+
+	/* preliminary round with modified key */
+	_kasumi_key_expand(ck_km, KLi1, KLi2, KOi1, KOi2, KOi3, KIi1, KIi2, KIi3);
+	A = _kasumi(A, KLi1, KLi2, KOi1, KOi2, KOi3, KIi1, KIi2, KIi3);
+
+	/* Run Kasumi in OFB to obtain enough data for gamma. */
+	_kasumi_key_expand(ck, KLi1, KLi2, KOi1, KOi2, KOi3, KIi1, KIi2, KIi3);
+
+	/* i is a block counter */
+	for (i = 0; i < cl / 64 + 1; i++) {
+		BLK = _kasumi(A ^ i ^ BLK, KLi1, KLi2, KOi1, KOi2, KOi3, KIi1, KIi2, KIi3);
+		osmo_store64be(BLK, co + (i * 8));
+	}
+}
diff --git a/src/gsm/libosmogsm.map b/src/gsm/libosmogsm.map
index cab4fc4..5f0d877 100644
--- a/src/gsm/libosmogsm.map
+++ b/src/gsm/libosmogsm.map
@@ -198,6 +198,10 @@  osmo_a5;
 osmo_a5_1;
 osmo_a5_2;
 
+_kasumi;
+_kasumi_key_expand;
+_kasumi_kgcore;
+
 osmo_auth_alg_name;
 osmo_auth_alg_parse;
 osmo_auth_gen_vec;
diff --git a/tests/Makefile.am b/tests/Makefile.am
index c6216d5..ddc13dc 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -4,7 +4,7 @@  check_PROGRAMS = timer/timer_test sms/sms_test ussd/ussd_test		\
                  smscb/smscb_test bits/bitrev_test a5/a5_test		\
                  conv/conv_test auth/milenage_test lapd/lapd_test	\
                  gsm0808/gsm0808_test gsm0408/gsm0408_test		\
-		 gb/bssgp_fc_test gb/gprs_ns_test                       \
+		 gb/bssgp_fc_test gb/gprs_ns_test kasumi/kasumi_test    \
 		 logging/logging_test fr/fr_test	                \
 		 loggingrb/loggingrb_test strrb/strrb_test              \
 		 vty/vty_test comp128/comp128_test utils/utils_test
@@ -19,6 +19,9 @@  utils_utils_test_LDADD = $(top_builddir)/src/libosmocore.la
 a5_a5_test_SOURCES = a5/a5_test.c
 a5_a5_test_LDADD = $(top_builddir)/src/libosmocore.la $(top_builddir)/src/gsm/libosmogsm.la
 
+kasumi_kasumi_test_SOURCES = kasumi/kasumi_test.c
+kasumi_kasumi_test_LDADD = $(top_builddir)/src/libosmocore.la $(top_builddir)/src/gsm/libosmogsm.la
+
 comp128_comp128_test_SOURCES = comp128/comp128_test.c
 comp128_comp128_test_LDADD = $(top_builddir)/src/libosmocore.la $(top_builddir)/src/gsm/libosmogsm.la
 
@@ -102,7 +105,7 @@  EXTRA_DIST = testsuite.at $(srcdir)/package.m4 $(TESTSUITE)		\
              lapd/lapd_test.ok gsm0408/gsm0408_test.ok			\
              gsm0808/gsm0808_test.ok gb/bssgp_fc_tests.err		\
              gb/bssgp_fc_tests.ok gb/bssgp_fc_tests.sh			\
-             gb/gprs_ns_test.ok						\
+             gb/gprs_ns_test.ok kasumi/kasumi_test.ok			\
              msgfile/msgfile_test.ok msgfile/msgconfig.cfg		\
              logging/logging_test.ok logging/logging_test.err		\
              fr/fr_test.ok loggingrb/logging_test.ok			\
diff --git a/tests/kasumi/kasumi_test.c b/tests/kasumi/kasumi_test.c
new file mode 100644
index 0000000..9101407
--- /dev/null
+++ b/tests/kasumi/kasumi_test.c
@@ -0,0 +1,136 @@ 
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include <stdbool.h>
+
+#include <osmocom/core/bits.h>
+#include <osmocom/core/utils.h>
+#include <osmocom/gsm/kasumi.h>
+
+/* Test vectors are taken from TS 135 202 */
+
+inline int _compare_mem(uint8_t * x, uint8_t * y, size_t len)
+{
+	if (0 != memcmp(x, y, len)) {
+		printf ("X: %s\t", osmo_hexdump_nospc(x, len));
+		printf ("Y: %s\n", osmo_hexdump_nospc(y, len));
+		return 0;
+	}
+	return 1;
+}
+
+inline static void test_expansion(uint8_t * test_key, uint16_t * _KLi1, uint16_t * _KLi2, uint16_t * _KOi1, uint16_t * _KOi2, uint16_t * _KOi3, uint16_t * _KIi1, uint16_t * _KIi2, uint16_t * _KIi3,  uint16_t * _KLi1_r, uint16_t * _KLi2_r, uint16_t * _KOi1_r, uint16_t * _KOi2_r, uint16_t * _KOi3_r, uint16_t * _KIi1_r, uint16_t * _KIi2_r, uint16_t * _KIi3_r)
+{
+	_kasumi_key_expand(test_key, _KLi1, _KLi2, _KOi1, _KOi2, _KOi3, _KIi1, _KIi2, _KIi3);
+	int passed = 1;
+	passed = _compare_mem((uint8_t *)_KLi1, (uint8_t *)_KLi1_r, 16);
+	passed = _compare_mem((uint8_t *)_KLi2, (uint8_t *)_KLi2_r, 16);
+	passed = _compare_mem((uint8_t *)_KOi1, (uint8_t *)_KOi1_r, 16);
+	passed = _compare_mem((uint8_t *)_KOi2, (uint8_t *)_KOi2_r, 16);
+	passed = _compare_mem((uint8_t *)_KOi3, (uint8_t *)_KOi3_r, 16);
+	passed = _compare_mem((uint8_t *)_KIi1, (uint8_t *)_KIi1_r, 16);
+	passed = _compare_mem((uint8_t *)_KIi2, (uint8_t *)_KIi2_r, 16);
+	passed = _compare_mem((uint8_t *)_KIi3, (uint8_t *)_KIi3_r, 16);
+	printf(passed ? " OK. " : "FAILED!");
+}
+
+int main(int argc, char **argv)
+{
+	uint16_t _KLi1[8], _KLi2[8], _KOi1[8], _KOi2[8], _KOi3[8], _KIi1[8], _KIi2[8], _KIi3[8], _KLi1_r[8], _KLi2_r[8], _KOi1_r[8], _KOi2_r[8], _KOi3_r[8], _KIi1_r[8], _KIi2_r[8], _KIi3_r[8];
+
+	printf("testing KASUMI key expansion and encryption (ETSI TS 135 203):\n");
+	printf("KASUMI Test Set 1...");
+
+	uint8_t _test_key1[] = {0x2B, 0xD6, 0x45, 0x9F, 0x82, 0xC5, 0xB3, 0x00, 0x95, 0x2C, 0x49, 0x10, 0x48, 0x81, 0xFF, 0x48};
+	_KLi1_r[0] = 0x57AC; _KLi1_r[1] = 0x8B3E; _KLi1_r[2] = 0x058B; _KLi1_r[3] = 0x6601; _KLi1_r[4] = 0x2A59; _KLi1_r[5] = 0x9220; _KLi1_r[6] = 0x9102; _KLi1_r[7] = 0xFE91;
+	_KLi2_r[0] = 0x0B6E; _KLi2_r[1] = 0x7EEF; _KLi2_r[2] = 0x6BF0; _KLi2_r[3] = 0xF388; _KLi2_r[4] = 0x3ED5; _KLi2_r[5] = 0xCD58; _KLi2_r[6] = 0x2AF5; _KLi2_r[7] = 0x00F8;
+	_KOi1_r[0] = 0xB3E8; _KOi1_r[1] = 0x58B0; _KOi1_r[2] = 0x6016; _KOi1_r[3] = 0xA592; _KOi1_r[4] = 0x2209; _KOi1_r[5] = 0x1029; _KOi1_r[6] = 0xE91F; _KOi1_r[7] = 0x7AC5;
+	_KOi2_r[0] = 0x1049; _KOi2_r[1] = 0x8148; _KOi2_r[2] = 0x48FF; _KOi2_r[3] = 0xD62B; _KOi2_r[4] = 0x9F45; _KOi2_r[5] = 0xC582; _KOi2_r[6] = 0x00B3; _KOi2_r[7] = 0x2C95;
+	_KOi3_r[0] = 0x2910; _KOi3_r[1] = 0x1FE9; _KOi3_r[2] = 0xC57A; _KOi3_r[3] = 0xE8B3; _KOi3_r[4] = 0xB058; _KOi3_r[5] = 0x1660; _KOi3_r[6] = 0x92A5; _KOi3_r[7] = 0x0922;
+	_KIi1_r[0] = 0x6BF0; _KIi1_r[1] = 0xF388; _KIi1_r[2] = 0x3ED5; _KIi1_r[3] = 0xCD58; _KIi1_r[4] = 0x2AF5; _KIi1_r[5] = 0x00F8; _KIi1_r[6] = 0x0B6E; _KIi1_r[7] = 0x7EEF;
+	_KIi2_r[0] = 0x7EEF; _KIi2_r[1] = 0x6BF0; _KIi2_r[2] = 0xF388; _KIi2_r[3] = 0x3ED5; _KIi2_r[4] = 0xCD58; _KIi2_r[5] = 0x2AF5; _KIi2_r[6] = 0x00F8; _KIi2_r[7] = 0x0B6E;
+	_KIi3_r[0] = 0xCD58; _KIi3_r[1] = 0x2AF5; _KIi3_r[2] = 0x00F8; _KIi3_r[3] = 0x0B6E; _KIi3_r[4] = 0x7EEF; _KIi3_r[5] = 0x6BF0; _KIi3_r[6] = 0xF388; _KIi3_r[7] = 0x3ED5;
+	test_expansion(_test_key1, _KLi1, _KLi2, _KOi1, _KOi2, _KOi3, _KIi1, _KIi2, _KIi3, _KLi1_r, _KLi2_r, _KOi1_r, _KOi2_r, _KOi3_r, _KIi1_r, _KIi2_r, _KIi3_r);
+
+	if (0xDF1F9B251C0BF45F == _kasumi(0xEA024714AD5C4D84, _KLi1, _KLi2, _KOi1, _KOi2, _KOi3, _KIi1, _KIi2, _KIi3))
+		printf("OK.");
+	else
+		printf("FAILED!");
+
+	printf("\nKASUMI Test Set 2...");
+
+	uint8_t _test_key2[] = {0x8C, 0xE3, 0x3E, 0x2C, 0xC3, 0xC0, 0xB5, 0xFC, 0x1F, 0x3D, 0xE8, 0xA6, 0xDC, 0x66, 0xB1, 0xF3};
+	_KLi1_r[0] = 0x19C7; _KLi1_r[1] = 0x7C58; _KLi1_r[2] = 0x8781; _KLi1_r[3] = 0x6BF9; _KLi1_r[4] = 0x3E7A; _KLi1_r[5] = 0xD14D; _KLi1_r[6] = 0xB8CD; _KLi1_r[7] = 0x63E7;
+	_KLi2_r[0] = 0x4A6B; _KLi2_r[1] = 0x7813; _KLi2_r[2] = 0xE1E1; _KLi2_r[3] = 0x523E; _KLi2_r[4] = 0xAA32; _KLi2_r[5] = 0x83E3; _KLi2_r[6] = 0x8DC0; _KLi2_r[7] = 0x7B4B;
+	_KOi1_r[0] = 0xC587; _KOi1_r[1] = 0x7818; _KOi1_r[2] = 0xBF96; _KOi1_r[3] = 0xE7A3; _KOi1_r[4] = 0x14DD; _KOi1_r[5] = 0x8CDB; _KOi1_r[6] = 0x3E76; _KOi1_r[7] = 0x9C71;
+	_KOi2_r[0] = 0xA6E8; _KOi2_r[1] = 0x66DC; _KOi2_r[2] = 0xF3B1; _KOi2_r[3] = 0xE38C; _KOi2_r[4] = 0x2C3E; _KOi2_r[5] = 0xC0C3; _KOi2_r[6] = 0xFCB5; _KOi2_r[7] = 0x3D1F;
+	_KOi3_r[0] = 0xDB8C; _KOi3_r[1] = 0x763E; _KOi3_r[2] = 0x719C; _KOi3_r[3] = 0x87C5; _KOi3_r[4] = 0x1878; _KOi3_r[5] = 0x96BF; _KOi3_r[6] = 0xA3E7; _KOi3_r[7] = 0xDD14;
+	_KIi1_r[0] = 0xE1E1; _KIi1_r[1] = 0x523E; _KIi1_r[2] = 0xAA32; _KIi1_r[3] = 0x83E3; _KIi1_r[4] = 0x8DC0; _KIi1_r[5] = 0x7B4B; _KIi1_r[6] = 0x4A6B; _KIi1_r[7] = 0x7813;
+	_KIi2_r[0] = 0x7813; _KIi2_r[1] = 0xE1E1; _KIi2_r[2] = 0x523E; _KIi2_r[3] = 0xAA32; _KIi2_r[4] = 0x83E3; _KIi2_r[5] = 0x8DC0; _KIi2_r[6] = 0x7B4B; _KIi2_r[7] = 0x4A6B;
+	_KIi3_r[0] = 0x83E3; _KIi3_r[1] = 0x8DC0; _KIi3_r[2] = 0x7B4B; _KIi3_r[3] = 0x4A6B; _KIi3_r[4] = 0x7813; _KIi3_r[5] = 0xE1E1; _KIi3_r[6] = 0x523E; _KIi3_r[7] = 0xAA32;
+	test_expansion(_test_key2, _KLi1, _KLi2, _KOi1, _KOi2, _KOi3, _KIi1, _KIi2, _KIi3, _KLi1_r, _KLi2_r, _KOi1_r, _KOi2_r, _KOi3_r, _KIi1_r, _KIi2_r, _KIi3_r);
+
+	if (0xDE551988CEB2F9B7 == _kasumi(0xD3C5D592327FB11C, _KLi1, _KLi2, _KOi1, _KOi2, _KOi3, _KIi1, _KIi2, _KIi3))
+		printf("OK.");
+	else
+		printf("FAILED!");
+
+	printf("\nKASUMI Test Set 3...");
+
+	uint8_t _test_key3[] = {0x40, 0x35, 0xC6, 0x68, 0x0A, 0xF8, 0xC6, 0xD1, 0xA8, 0xFF, 0x86, 0x67, 0xB1, 0x71, 0x40, 0x13};
+	_KLi1_r[0] = 0x806A; _KLi1_r[1] = 0x8CD1; _KLi1_r[2] = 0x15F0; _KLi1_r[3] = 0x8DA3; _KLi1_r[4] = 0x51FF; _KLi1_r[5] = 0x0CCF; _KLi1_r[6] = 0x62E3; _KLi1_r[7] = 0x8026;
+	_KLi2_r[0] = 0x8353; _KLi2_r[1] = 0x0B3E; _KLi2_r[2] = 0x5623; _KLi2_r[3] = 0x3CFF; _KLi2_r[4] = 0xC725; _KLi2_r[5] = 0x7203; _KLi2_r[6] = 0x4116; _KLi2_r[7] = 0x830F;
+	_KOi1_r[0] = 0xCD18; _KOi1_r[1] = 0x5F01; _KOi1_r[2] = 0xDA38; _KOi1_r[3] = 0x1FF5; _KOi1_r[4] = 0xCCF0; _KOi1_r[5] = 0x2E36; _KOi1_r[6] = 0x0268; _KOi1_r[7] = 0x06A8;
+	_KOi2_r[0] = 0x6786; _KOi2_r[1] = 0x71B1; _KOi2_r[2] = 0x1340; _KOi2_r[3] = 0x3540; _KOi2_r[4] = 0x68C6; _KOi2_r[5] = 0xF80A; _KOi2_r[6] = 0xD1C6; _KOi2_r[7] = 0xFFA8;
+	_KOi3_r[0] = 0x362E; _KOi3_r[1] = 0x6802; _KOi3_r[2] = 0xA806; _KOi3_r[3] = 0x18CD; _KOi3_r[4] = 0x015F; _KOi3_r[5] = 0x38DA; _KOi3_r[6] = 0xF51F; _KOi3_r[7] = 0xF0CC;
+	_KIi1_r[0] = 0x5623; _KIi1_r[1] = 0x3CFF; _KIi1_r[2] = 0xC725; _KIi1_r[3] = 0x7203; _KIi1_r[4] = 0x4116; _KIi1_r[5] = 0x830F; _KIi1_r[6] = 0x8353; _KIi1_r[7] = 0x0B3E;
+	_KIi2_r[0] = 0x0B3E; _KIi2_r[1] = 0x5623; _KIi2_r[2] = 0x3CFF; _KIi2_r[3] = 0xC725; _KIi2_r[4] = 0x7203; _KIi2_r[5] = 0x4116; _KIi2_r[6] = 0x830F; _KIi2_r[7] = 0x8353;
+	_KIi3_r[0] = 0x7203; _KIi3_r[1] = 0x4116; _KIi3_r[2] = 0x830F; _KIi3_r[3] = 0x8353; _KIi3_r[4] = 0x0B3E; _KIi3_r[5] = 0x5623; _KIi3_r[6] = 0x3CFF; _KIi3_r[7] = 0xC725;
+	test_expansion(_test_key3, _KLi1, _KLi2, _KOi1, _KOi2, _KOi3, _KIi1, _KIi2, _KIi3, _KLi1_r, _KLi2_r, _KOi1_r, _KOi2_r, _KOi3_r, _KIi1_r, _KIi2_r, _KIi3_r);
+
+	if (0x4592B0E78690F71B == _kasumi(0x62A540981BA6F9B7, _KLi1, _KLi2, _KOi1, _KOi2, _KOi3, _KIi1, _KIi2, _KIi3))
+		printf("OK.");
+	else
+		printf("FAILED!");
+
+	printf("\nKASUMI Test Set 4...");
+	uint8_t _test_key4[] = {0x3A, 0x3B, 0x39, 0xB5, 0xC3, 0xF2, 0x37, 0x6D, 0x69, 0xF7, 0xD5, 0x46, 0xE5, 0xF8, 0x5D, 0x43};
+	uint64_t I4 = 0xCA49C1C75771AB0B, i;
+	_kasumi_key_expand(_test_key4, _KLi1, _KLi2, _KOi1, _KOi2, _KOi3, _KIi1, _KIi2, _KIi3);
+
+	for (i = 0; i < 50; i++)
+		I4 = _kasumi(I4, _KLi1, _KLi2, _KOi1, _KOi2, _KOi3, _KIi1, _KIi2, _KIi3);
+
+	if (0x738BAD4C4A690802 == I4) printf(" OK.\n"); else printf("FAILED!");
+
+
+	uint8_t gamma[32];
+
+	uint8_t _Key1[] = {0x2B, 0xD6, 0x45, 0x9F, 0x82, 0xC5, 0xBC, 0x00, 0x2B, 0xD6, 0x45, 0x9F, 0x82, 0xC5, 0xBC, 0x00},
+	_gamma1[] = {0x88, 0x9E, 0xEA, 0xAF, 0x9E, 0xD1, 0xBA, 0x1A, 0xBB, 0xD8, 0x43, 0x62, 0x32, 0xE4, 0x57, 0x28, 0xD0, 0x1A, 0xA8, 0x91, 0x33, 0xDA, 0x73, 0xC1, 0x1E, 0xAB, 0x68, 0xB7, 0xD8, 0x9B, 0xC8, 0x41};
+	_kasumi_kgcore(0xF, 0, 0x0024F20F, 0, _Key1, gamma, 228);
+	printf ("KGCORE Test Set 1: %d\n", _compare_mem(gamma, _gamma1, 32));
+
+	uint8_t _Key2[] = {0x95, 0x2C, 0x49, 0x10, 0x48, 0x81, 0xFF, 0x48, 0x95, 0x2C, 0x49, 0x10, 0x48, 0x81, 0xFF, 0x48},
+	_gamma2[] = {0xFB, 0x4D, 0x5F, 0xBC, 0xEE, 0x13, 0xA3, 0x33, 0x89, 0x28, 0x56, 0x86, 0xE9, 0xA5, 0xC9, 0x42, 0x40, 0xDE, 0x38, 0x15, 0x01, 0x15, 0xF1, 0x5F, 0x8D, 0x9D, 0x98, 0xB9, 0x1A, 0x94, 0xB2, 0x96};
+	_kasumi_kgcore(0xF, 0, 0x00061272, 0, _Key2, gamma, 228);
+	printf ("KGCORE Test Set 2: %d\n", _compare_mem(gamma, _gamma2, 32));
+
+	uint8_t _Key3[] = {0xEF, 0xA8, 0xB2, 0x22, 0x9E, 0x72, 0x0C, 0x2A, 0xEF, 0xA8, 0xB2, 0x22, 0x9E, 0x72, 0x0C, 0x2A},
+	_gamma3[] = {0x0E, 0x40, 0x15, 0x75, 0x5A, 0x33, 0x64, 0x69, 0xC3, 0xDD, 0x86, 0x80, 0xE3, 0x03, 0x5B, 0xC4, 0x19, 0xA7, 0x8A, 0xD3, 0x86, 0x2C, 0x10, 0x90, 0xC6, 0x8A, 0x39, 0x1F, 0xE8, 0xA6, 0xAD, 0xEB};
+	_kasumi_kgcore(0xF, 0, 0x0033FD3F, 0, _Key3, gamma, 228);
+	printf ("KGCORE Test Set 3: %d\n", _compare_mem(gamma, _gamma3, 32));
+
+	uint8_t _Key4[] = {0x5A, 0xCB, 0x1D, 0x64, 0x4C, 0x0D, 0x51, 0x20, 0x4E, 0xA5, 0x5A, 0xCB, 0x1D, 0x64, 0x4C, 0x0D},
+	_gamma4[] = {0xE0, 0x95, 0x30, 0x6A, 0xD5, 0x08, 0x6E, 0x2E, 0xAC, 0x7F, 0x31, 0x07, 0xDE, 0x4F, 0xA2, 0x2D, 0xC1, 0xDF, 0xC9, 0x7D, 0x5B, 0xC5, 0x66, 0x1D, 0xD6, 0x09, 0x6F, 0x47, 0x6A, 0xED, 0xC6, 0x4B};
+	_kasumi_kgcore(0xF, 0, 0x00156B26, 0, _Key4, gamma, 228);
+	printf ("KGCORE Test Set 4: %d\n", _compare_mem(gamma, _gamma4, 32));
+
+	uint8_t _Key5[] = {0xD3, 0xC5, 0xD5, 0x92, 0x32, 0x7F, 0xB1, 0x1C, 0x40, 0x35, 0xC6, 0x68, 0x0A, 0xF8, 0xC6, 0xD1},
+	_gamma5[] = {0xDC, 0xE6, 0x43, 0x62, 0xAB, 0x5F, 0x89, 0xC1, 0x1E, 0xF0, 0xB3, 0x05, 0x16, 0x65, 0x70, 0xF4, 0x88, 0x9D, 0x55, 0x11, 0xE9, 0xE3, 0x57, 0x5D, 0x06, 0x2B, 0x5C, 0xED, 0x60, 0x39, 0x50, 0x6A};
+	_kasumi_kgcore(0xF, 0, 0x000A59B4, 0, _Key5, gamma, 228);
+	printf ("KGCORE Test Set 5: %d\n", _compare_mem(gamma, _gamma5, 32));
+
+	return 0;
+}
diff --git a/tests/kasumi/kasumi_test.ok b/tests/kasumi/kasumi_test.ok
new file mode 100644
index 0000000..2c2af4c
--- /dev/null
+++ b/tests/kasumi/kasumi_test.ok
@@ -0,0 +1,10 @@ 
+testing KASUMI key expansion and encryption (ETSI TS 135 203):
+KASUMI Test Set 1... OK. OK.
+KASUMI Test Set 2... OK. OK.
+KASUMI Test Set 3... OK. OK.
+KASUMI Test Set 4... OK.
+KGCORE Test Set 1: 1
+KGCORE Test Set 2: 1
+KGCORE Test Set 3: 1
+KGCORE Test Set 4: 1
+KGCORE Test Set 5: 1
diff --git a/tests/testsuite.at b/tests/testsuite.at
index 9124f25..7ce2ee8 100644
--- a/tests/testsuite.at
+++ b/tests/testsuite.at
@@ -9,6 +9,12 @@  cat $abs_srcdir/a5/a5_test.ok > expout
 AT_CHECK([$abs_top_builddir/tests/a5/a5_test], [0], [expout])
 AT_CLEANUP
 
+AT_SETUP([kasumi])
+AT_KEYWORDS([kasumi])
+cat $abs_srcdir/kasumi/kasumi_test.ok > expout
+AT_CHECK([$abs_top_builddir/tests/kasumi/kasumi_test], [0], [expout])
+AT_CLEANUP
+
 AT_SETUP([bits])
 AT_KEYWORDS([bits])
 cat $abs_srcdir/bits/bitrev_test.ok > expout