adding public GPLv2 a53 library for use

git-svn-id: http://wush.net/svn/range/software/public/a53/trunk@6164 19bc5d8c-e614-43d4-8b26-e1612bc8e597
This commit is contained in:
Kurtis Heimerl 2013-08-13 22:43:52 +00:00
commit de2985721f
25 changed files with 2656 additions and 0 deletions

279
COPYING Normal file
View File

@ -0,0 +1,279 @@
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users. This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it. (Some other Free Software Foundation software is covered by
the GNU Lesser General Public License instead.) You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have. You must make sure that they, too, receive or can get the
source code. And you must show them these terms so they know their
rights.
We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.
Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software. If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.
Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary. To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at all.
The precise terms and conditions for copying, distribution and
modification follow.
GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License. The "Program", below,
refers to any such program or work, and a "work based on the Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language. (Hereinafter, translation is included without limitation in
the term "modification".) Each licensee is addressed as "you".
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.
1. You may copy and distribute verbatim copies of the Program's
source code as you receive it, in any medium, provided that you
conspicuously and appropriately publish on each copy an appropriate
copyright notice and disclaimer of warranty; keep intact all the
notices that refer to this License and to the absence of any warranty;
and give any other recipients of the Program a copy of this License
along with the Program.
You may charge a fee for the physical act of transferring a copy, and
you may at your option offer warranty protection in exchange for a fee.
2. You may modify your copy or copies of the Program or any portion
of it, thus forming a work based on the Program, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) You must cause the modified files to carry prominent notices
stating that you changed the files and the date of any change.
b) You must cause any work that you distribute or publish, that in
whole or in part contains or is derived from the Program or any
part thereof, to be licensed as a whole at no charge to all third
parties under the terms of this License.
c) If the modified program normally reads commands interactively
when run, you must cause it, when started running for such
interactive use in the most ordinary way, to print or display an
announcement including an appropriate copyright notice and a
notice that there is no warranty (or else, saying that you provide
a warranty) and that users may redistribute the program under
these conditions, and telling the user how to view a copy of this
License. (Exception: if the Program itself is interactive but
does not normally print such an announcement, your work based on
the Program is not required to print an announcement.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Program, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Program.
In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:
a) Accompany it with the complete corresponding machine-readable
source code, which must be distributed under the terms of Sections
1 and 2 above on a medium customarily used for software interchange; or,
b) Accompany it with a written offer, valid for at least three
years, to give any third party, for a charge no more than your
cost of physically performing source distribution, a complete
machine-readable copy of the corresponding source code, to be
distributed under the terms of Sections 1 and 2 above on a medium
customarily used for software interchange; or,
c) Accompany it with the information you received as to the offer
to distribute corresponding source code. (This alternative is
allowed only for noncommercial distribution and only if you
received the program in object code or executable form with such
an offer, in accord with Subsection b above.)
The source code for a work means the preferred form of the work for
making modifications to it. For an executable work, complete source
code means all the source code for all modules it contains, plus any
associated interface definition files, plus the scripts used to
control compilation and installation of the executable. However, as a
special exception, the source code distributed need not include
anything that is normally distributed (in either source or binary
form) with the major components (compiler, kernel, and so on) of the
operating system on which the executable runs, unless that component
itself accompanies the executable.
If distribution of executable or object code is made by offering
access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.
4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License. Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
void, and will automatically terminate your rights under this License.
However, parties who have received copies, or rights, from you under
this License will not have their licenses terminated so long as such
parties remain in full compliance.
5. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Program or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.
6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program subject to
these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.
7. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Program at all. For example, if a patent
license would not permit royalty-free redistribution of the Program by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Program.
If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply and the section as a whole is intended to apply in other
circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system, which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
may add an explicit geographical distribution limitation excluding
those countries, so that distribution is permitted only in or among
countries not thus excluded. In such case, this License incorporates
the limitation as if written in the body of this License.
9. The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the Program
specifies a version number of this License which applies to it and "any
later version", you have the option of following the terms and conditions
either of that version or of any later version published by the Free
Software Foundation. If the Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.
10. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
to ask for permission. For software which is copyrighted by the Free
Software Foundation, write to the Free Software Foundation; we sometimes
make exceptions for this. Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.
NO WARRANTY
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.

49
Makefile Normal file
View File

@ -0,0 +1,49 @@
SOURCE_FILES=a5.c bits.c gea.c kasumi.c utils.c ifc.cpp
OBJECT_FILES=a5.o bits.o gea.o kasumi.o utils.o ifc.o
INCLUDE_FILES=a5.h bits.h gea.h gprs_cipher.h kasumi.h linuxlist.h utils.h
# build A5/3 library
liba53.so.1.0: ${SOURCE_FILES} ${INCLUDE_FILES} Makefile
g++ -O3 -Wall -fPIC -c ${SOURCE_FILES}
g++ -shared -Wl,-soname,liba53.so.1 -o liba53.so.1.0 ${OBJECT_FILES}
ln -sf liba53.so.1.0 liba53.so.1
ln -sf liba53.so.1.0 liba53.so
# install A5/3 library
install: liba53.so.1.0
cp liba53.so.1.0 /usr/lib
ln -sf /usr/lib/liba53.so.1.0 /usr/lib/liba53.so.1
ln -sf /usr/lib/liba53.so.1.0 /usr/lib/liba53.so
cp a53.h /usr/include
# test installed A5/3 library
installtest: install
g++ -o a53test a53test.cpp -I/usr/include -L/usr/lib -la53
./a53test
clean:
rm -f ${OBJECT_FILES} liba53.so* a53test
# otest: a5_test kasumi_test gea_test a5_speed
# ./a5_test | diff - a5_test.ok
# # ./kasumi_test | diff - kasumi_test.ok
# # ./gea_test | diff - gea_test.ok
# ./a5_speed
#
# a5_speed: a5_speed.c ${SOURCE_FILES} ${INCLUDE_FILES} Makefile
# g++ ${OPT} -o a5_speed a5_speed.c ${SOURCE_FILES}
#
# a5_test: a5_test.c ${SOURCE_FILES} ${INCLUDE_FILES} Makefile
# g++ ${OPT} -o a5_test a5_test.c ${SOURCE_FILES}
#
# # kasumi_test: kasumi_test.c ${SOURCE_FILES} ${INCLUDE_FILES} Makefile
# # g++ ${OPT} -o kasumi_test kasumi_test.c ${SOURCE_FILES}
#
# # gea_test: gea_test.c ${SOURCE_FILES} ${INCLUDE_FILES} Makefile
# # g++ ${OPT} -o gea_test gea_test.c ${SOURCE_FILES}
#
# clean:
# rm a5_test kasumi_test gea_test a5_speed
#
#
#

14
README Normal file
View File

@ -0,0 +1,14 @@
to build A5/3 library:
make
to install A5/3 library:
make install
to test installed A5/3 library:
make installtest
installed files:
/usr/lib/liba53.so
/usr/lib/liba53.so.1
/usr/lib/liba53.so.1.0
/usr/include/a53.h

423
a5.c Normal file
View File

@ -0,0 +1,423 @@
/*
* a5.c
*
* Full reimplementation of A5/1,2 (split and threadsafe)
*
* The logic behind the algorithm is taken from "A pedagogical implementation
* of the GSM A5/1 and A5/2 "voice privacy" encryption algorithms." by
* Marc Briceno, Ian Goldberg, and David Wagner.
*
* Copyright (C) 2011 Sylvain Munaut <tnt@246tNt.com>
*
* 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.
*/
/*! \addtogroup a5
* @{
*/
/*! \file gsm/a5.c
* \brief Osmocom GSM A5 ciphering algorithm implementation
*/
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <stdbool.h>
#include "bits.h"
#include "a5.h"
#include "kasumi.h"
/*! \brief Main method to generate a A5/x cipher stream
* \param[in] n Which A5/x method to use
* \param[in] key 8 byte array for the key (as received from the SIM)
* \param[in] fn Frame number
* \param[out] dl Pointer to array of ubits to return Downlink cipher stream
* \param[out] ul Pointer to array of ubits to return Uplink cipher stream
*
* Currently A5/[0-4] are supported: -ENOTSUP returned in this case, 0 returned for supported ciphers.
* Either (or both) of dl/ul can be NULL if not needed.
*/
void
osmo_a5(int n, const uint8_t *key, uint32_t fn, ubit_t *dl, ubit_t *ul)
{
switch (n)
{
case 0:
if (dl)
memset(dl, 0x00, 114);
if (ul)
memset(ul, 0x00, 114);
break;
case 1:
osmo_a5_1(key, fn, dl, ul);
break;
case 2:
osmo_a5_2(key, fn, dl, ul);
break;
case 3:
osmo_a5_3(key, fn, dl, ul);
break;
case 4:
osmo_a5_4(key, fn, dl, ul);
break;
default:
/* a5/[5..7] not supported here/yet */
break;
}
}
/* ------------------------------------------------------------------------ */
/* A5/1&2 common stuff */
/* ------------------------------------------------------------------------ */
#define A5_R1_LEN 19
#define A5_R2_LEN 22
#define A5_R3_LEN 23
#define A5_R4_LEN 17 /* A5/2 only */
#define A5_R1_MASK ((1<<A5_R1_LEN)-1)
#define A5_R2_MASK ((1<<A5_R2_LEN)-1)
#define A5_R3_MASK ((1<<A5_R3_LEN)-1)
#define A5_R4_MASK ((1<<A5_R4_LEN)-1)
#define A5_R1_TAPS 0x072000 /* x^19 + x^18 + x^17 + x^14 + 1 */
#define A5_R2_TAPS 0x300000 /* x^22 + x^21 + 1 */
#define A5_R3_TAPS 0x700080 /* x^23 + x^22 + x^21 + x^8 + 1 */
#define A5_R4_TAPS 0x010800 /* x^17 + x^12 + 1 */
/*! \brief Computes parity of a 32-bit word
* \param[in] x 32 bit word
* \return Parity bit (xor of all bits) as 0 or 1
*/
static inline uint32_t
_a5_12_parity(uint32_t x)
{
x ^= x >> 16;
x ^= x >> 8;
x ^= x >> 4;
x &= 0xf;
return (0x6996 >> x) & 1;
}
/*! \brief Compute majority bit from 3 taps
* \param[in] v1 LFSR state ANDed with tap-bit
* \param[in] v2 LFSR state ANDed with tap-bit
* \param[in] v3 LFSR state ANDed with tap-bit
* \return The majority bit (0 or 1)
*/
static inline uint32_t
_a5_12_majority(uint32_t v1, uint32_t v2, uint32_t v3)
{
return (!!v1 + !!v2 + !!v3) >= 2;
}
/*! \brief Compute the next LFSR state
* \param[in] r Current state
* \param[in] mask LFSR mask
* \param[in] taps LFSR taps
* \return Next state
*/
static inline uint32_t
_a5_12_clock(uint32_t r, uint32_t mask, uint32_t taps)
{
return ((r << 1) & mask) | _a5_12_parity(r & taps);
}
/* ------------------------------------------------------------------------ */
/* A5/1 */
/* ------------------------------------------------------------------------ */
#define A51_R1_CLKBIT 0x000100
#define A51_R2_CLKBIT 0x000400
#define A51_R3_CLKBIT 0x000400
/*! \brief GSM A5/1 Clocking function
* \param[in] r Register state
* \param[in] force Non-zero value disable conditional clocking
*/
static inline void
_a5_1_clock(uint32_t r[], int force)
{
int cb[3], maj;
cb[0] = !!(r[0] & A51_R1_CLKBIT);
cb[1] = !!(r[1] & A51_R2_CLKBIT);
cb[2] = !!(r[2] & A51_R3_CLKBIT);
maj = _a5_12_majority(cb[0], cb[1], cb[2]);
if (force || (maj == cb[0]))
r[0] = _a5_12_clock(r[0], A5_R1_MASK, A5_R1_TAPS);
if (force || (maj == cb[1]))
r[1] = _a5_12_clock(r[1], A5_R2_MASK, A5_R2_TAPS);
if (force || (maj == cb[2]))
r[2] = _a5_12_clock(r[2], A5_R3_MASK, A5_R3_TAPS);
}
/*! \brief GSM A5/1 Output function
* \param[in] r Register state
* \return The A5/1 output function bit
*/
static inline uint8_t
_a5_1_get_output(uint32_t r[])
{
return (r[0] >> (A5_R1_LEN-1)) ^
(r[1] >> (A5_R2_LEN-1)) ^
(r[2] >> (A5_R3_LEN-1));
}
/*! \brief Generate a GSM A5/1 cipher stream
* \param[in] key 8 byte array for the key (as received from the SIM)
* \param[in] fn Frame number
* \param[out] dl Pointer to array of ubits to return Downlink cipher stream
* \param[out] ul Pointer to array of ubits to return Uplink cipher stream
*
* Either (or both) of dl/ul can be NULL if not needed.
*/
void
osmo_a5_1(const uint8_t *key, uint32_t fn, ubit_t *dl, ubit_t *ul)
{
uint32_t r[3] = {0, 0, 0};
uint32_t fn_count;
uint32_t b;
int i;
/* Key load */
for (i=0; i<64; i++)
{
b = ( key[7 - (i>>3)] >> (i&7) ) & 1;
_a5_1_clock(r, 1);
r[0] ^= b;
r[1] ^= b;
r[2] ^= b;
}
/* Frame count load */
fn_count = osmo_a5_fn_count(fn);
for (i=0; i<22; i++)
{
b = (fn_count >> i) & 1;
_a5_1_clock(r, 1);
r[0] ^= b;
r[1] ^= b;
r[2] ^= b;
}
/* Mix */
for (i=0; i<100; i++)
{
_a5_1_clock(r, 0);
}
/* Output */
for (i=0; i<114; i++) {
_a5_1_clock(r, 0);
if (dl)
dl[i] = _a5_1_get_output(r);
}
for (i=0; i<114; i++) {
_a5_1_clock(r, 0);
if (ul)
ul[i] = _a5_1_get_output(r);
}
}
/* ------------------------------------------------------------------------ */
/* A5/2 */
/* ------------------------------------------------------------------------ */
#define A52_R4_CLKBIT0 0x000400
#define A52_R4_CLKBIT1 0x000008
#define A52_R4_CLKBIT2 0x000080
/*! \brief GSM A5/2 Clocking function
* \param[in] r Register state
* \param[in] force Non-zero value disable conditional clocking
*/
static inline void
_a5_2_clock(uint32_t r[], int force)
{
int cb[3], maj;
cb[0] = !!(r[3] & A52_R4_CLKBIT0);
cb[1] = !!(r[3] & A52_R4_CLKBIT1);
cb[2] = !!(r[3] & A52_R4_CLKBIT2);
maj = (cb[0] + cb[1] + cb[2]) >= 2;
if (force || (maj == cb[0]))
r[0] = _a5_12_clock(r[0], A5_R1_MASK, A5_R1_TAPS);
if (force || (maj == cb[1]))
r[1] = _a5_12_clock(r[1], A5_R2_MASK, A5_R2_TAPS);
if (force || (maj == cb[2]))
r[2] = _a5_12_clock(r[2], A5_R3_MASK, A5_R3_TAPS);
r[3] = _a5_12_clock(r[3], A5_R4_MASK, A5_R4_TAPS);
}
/*! \brief GSM A5/2 Output function
* \param[in] r Register state
* \return The A5/2 output function bit
*/
static inline uint8_t
_a5_2_get_output(uint32_t r[])
{
uint8_t b;
b = (r[0] >> (A5_R1_LEN-1)) ^
(r[1] >> (A5_R2_LEN-1)) ^
(r[2] >> (A5_R3_LEN-1)) ^
_a5_12_majority( r[0] & 0x08000, ~r[0] & 0x04000, r[0] & 0x1000) ^
_a5_12_majority(~r[1] & 0x10000, r[1] & 0x02000, r[1] & 0x0200) ^
_a5_12_majority( r[2] & 0x40000, r[2] & 0x10000, ~r[2] & 0x2000);
return b;
}
/*! \brief Generate a GSM A5/1 cipher stream
* \param[in] key 8 byte array for the key (as received from the SIM)
* \param[in] fn Frame number
* \param[out] dl Pointer to array of ubits to return Downlink cipher stream
* \param[out] ul Pointer to array of ubits to return Uplink cipher stream
*
* Either (or both) of dl/ul can be NULL if not needed.
*/
void
osmo_a5_2(const uint8_t *key, uint32_t fn, ubit_t *dl, ubit_t *ul)
{
uint32_t r[4] = {0, 0, 0, 0};
uint32_t fn_count;
uint32_t b;
int i;
/* Key load */
for (i=0; i<64; i++)
{
b = ( key[7 - (i>>3)] >> (i&7) ) & 1;
_a5_2_clock(r, 1);
r[0] ^= b;
r[1] ^= b;
r[2] ^= b;
r[3] ^= b;
}
/* Frame count load */
fn_count = osmo_a5_fn_count(fn);
for (i=0; i<22; i++)
{
b = (fn_count >> i) & 1;
_a5_2_clock(r, 1);
r[0] ^= b;
r[1] ^= b;
r[2] ^= b;
r[3] ^= b;
}
r[0] |= 1 << 15;
r[1] |= 1 << 16;
r[2] |= 1 << 18;
r[3] |= 1 << 10;
/* Mix */
for (i=0; i<99; i++)
{
_a5_2_clock(r, 0);
}
/* Output */
for (i=0; i<114; i++) {
_a5_2_clock(r, 0);
if (dl)
dl[i] = _a5_2_get_output(r);
}
for (i=0; i<114; i++) {
_a5_2_clock(r, 0);
if (ul)
ul[i] = _a5_2_get_output(r);
}
}
/* ------------------------------------------------------------------------ */
/* A5/3 */
/* ------------------------------------------------------------------------ */
/*! \brief Generate a GSM A5/3 cipher stream
* \param[in] key 8 byte array for the key (as received from the SIM)
* \param[in] fn Frame number
* \param[out] dl Pointer to array of ubits to return Downlink cipher stream
* \param[out] ul Pointer to array of ubits to return Uplink cipher stream
*
* Either (or both) of dl/ul should be NULL if not needed.
*
* Implementation based on specifications from 3GPP TS 55.216, 3GPP TR 55.919 and ETSI TS 135 202
* with slight simplifications (CE hardcoded to 0).
*/
void
osmo_a5_3(const uint8_t *key, uint32_t fn, ubit_t *dl, ubit_t *ul)
{
/* internal function require 128 bit key so we expand by concatenating supplied 64 bit key */
uint8_t ck[16];
memcpy(ck, key, 8);
memcpy(ck + 8, key, 8);
osmo_a5_4(ck, fn, dl, ul);
}
void
osmo_a5_4(const uint8_t *ck, uint32_t fn, ubit_t *dl, ubit_t *ul)
{
uint8_t i, gamma[32];
if (ul) {
_kasumi_kgcore(0xF, 0, fn, 0, ck, gamma, 228);
uint8_t uplink[15];
for(i = 0; i < 15; i++) uplink[i] = (gamma[i + 14] << 2) + (gamma[i + 15] >> 6);
osmo_pbit2ubit(ul, uplink, 114);
}
if (dl) {
_kasumi_kgcore(0xF, 0, fn, 0, ck, gamma, 114);
osmo_pbit2ubit(dl, gamma, 114);
}
}
/*! @} */

66
a5.h Normal file
View File

@ -0,0 +1,66 @@
/*
* a5.h
*
* Copyright (C) 2011 Sylvain Munaut <tnt@246tNt.com>
*
* 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.
*/
#ifndef __OSMO_A5_H__
#define __OSMO_A5_H__
#include <stdint.h>
#include <stdbool.h>
#include "bits.h"
/*! \defgroup a5 GSM A5 ciphering algorithm
* @{
*/
/*! \file gsm/a5.h
* \brief Osmocom GSM A5 ciphering algorithm header
*/
/*! \brief Converts a frame number into the 22 bit number used in A5/x
* \param[in] fn The true framenumber
* \return 22 bit word
*/
static inline uint32_t
osmo_a5_fn_count(uint32_t fn)
{
int t1 = fn / (26 * 51);
int t2 = fn % 26;
int t3 = fn % 51;
return (t1 << 11) | (t3 << 5) | t2;
}
/* Notes:
* - key must be 8 bytes long (or NULL for A5/0)
* - the dl and ul pointer must be either NULL or 114 bits long
* - fn is the _real_ GSM frame number.
* (converted internally to fn_count)
*/
void osmo_a5(int n, const uint8_t *key, uint32_t fn, ubit_t *dl, ubit_t *ul);
void osmo_a5_1(const uint8_t *key, uint32_t fn, ubit_t *dl, ubit_t *ul);
void osmo_a5_2(const uint8_t *key, uint32_t fn, ubit_t *dl, ubit_t *ul);
void osmo_a5_3(const uint8_t *key, uint32_t fn, ubit_t *dl, ubit_t *ul);
void osmo_a5_4(const uint8_t *ck, uint32_t fn, ubit_t *dl, ubit_t *ul);
/*! @} */
#endif /* __OSMO_A5_H__ */

5
a53.h Normal file
View File

@ -0,0 +1,5 @@
typedef unsigned char u8;
typedef unsigned short u16;
typedef unsigned int u32;
void A53_GSM( u8 *key, int klen, int count, u8 *block1, u8 *block2 );

99
a53test.cpp Normal file
View File

@ -0,0 +1,99 @@
#include <iostream>
#include <iomanip>
#include <string.h>
#include <stdlib.h>
#include <a53.h>
using namespace std;
void printer(int which, const char *label, u8 *vector, int lth)
{
cout << " block" << which << " " << label << " = ";
int i;
for (i = 0; i < lth; i++) {
cout << hex << std::setw(2) << setfill('0') << (int)vector[i] << dec;
}
}
void check(int which, u8 *exp, u8 *got)
{
printer(which, "exp", exp, 15);
printer(which, "got", got, 15);
int err = 0;
int i;
for (i = 0; i < 15; i++) {
if (exp[i] == got[i]) continue;
err = 1;
break;
}
if (err) {
cout << " ERROR" << endl;
} else {
cout << " ok" << endl;
}
}
int unhex(const char c)
{
if (c >= '0' && c <= '9') return c - '0';
if (c >= 'A' && c <= 'F') return c - 'A' + 10;
cout << "oops" << endl;
exit(1);
}
int seti(const char *src)
{
int r = 0;
for ( ; *src != 0; src++) {
r = (r << 4) + unhex(*src);
}
return r;
}
void setu8(u8 *dst, const char *src)
{
for ( ; *src != 0; src += 2) {
*dst++ = (unhex(src[0]) << 4) | (unhex(src[1]));
}
}
int main()
{
int i, keylth;
unsigned int count;
u8 key[16], exp1[15], exp2[15], got1[15], got2[15];
const char *data[52] = {
"2BD6459F82C5BC00", "24F20F", "889EEAAF9ED1BA1ABBD8436232E440", "5CA3406AA244CF69CF047AADA2DF40",
"952C49104881FF48", "061527", "AB7DB38A573A325DAA76E4CB800A40", "4C4B594FEA9D00FE8978B7B7BC1080",
"EFA8B2229E720C2A", "33FD3F", "0E4015755A336469C3DD8680E30340", "6F10669E2B4E18B042431A28E47F80",
"3451F23A43BD2C87", "0E418C", "75F7C4C51560905DFBA05E46FB54C0", "192C95353CDF979E054186DF15BF00",
"CAA2639BE82435CF", "2FF229", "301437E4D4D6565D4904C631606EC0", "F0A3B8795E264D3E1A82F684353DC0",
"7AE67E87400B9FA6", "2F24E5", "F794290FEF643D2EA348A7796A2100", "CB6FA6C6B8A705AF9FEFE975818500",
"58AF69935540698B", "05446B", "749CA4E6B691E5A598C461D5FE4740", "31C9E444CD04677ADAA8A082ADBC40",
"017F81E5F236FE62", "156B26", "2A6976761E60CC4E8F9F52160276C0", "A544D8475F2C78C35614128F1179C0",
"1ACA8B448B767B39", "0BC3B5", "A4F70DC5A2C9707F5FA1C60EB10640", "7780B597B328C1400B5C74823E8500",
// "5ACB1D644C0D512041A5", "1D5157", "8EFAEC49C355CCD995C2BF649FD480", "F3A2910CAEDF587E976171AAF33B80",
// "9315819243A043BEBE6E", "2E196F", "AA08DB46DD3DED78A612085C529D00", "0250463DA0E3886F9BC2E3BB0D73C0",
// "3D43C388C9581E337FF1F97EB5C1F85E", "35D2CF", "A2FE3034B6B22CC4E33C7090BEC340", "170D7497432FF897B91BE8AECBA880",
// "A4496A64DF4F399F3B4506814A3E07A1", "212777", "89CDEE360DF9110281BCF57755A040", "33822C0C779598C9CBFC49183AF7C0",
};
for (i = 32; i>=0; i-=4) {
cout << "test set " << (1+i/4) << endl;
setu8(key, data[i]);
keylth = strlen(data[i])*4;
count = seti(data[i+1]);
A53_GSM(key, keylth, count, got1, got2);
setu8(exp1, data[i+2]);
setu8(exp2, data[i+3]);
check(1, exp1, got1);
check(2, exp2, got2);
}
cout << "time test" << endl;
int n = 10000;
float t = clock();
for (i = 0; i < n; i++) {
A53_GSM(key, keylth, count, got1, got2);
}
t = (clock() - t) / (CLOCKS_PER_SEC * (float)n);
cout << "GSM takes " << t << " seconds per iteration" << endl;
exit(0);
}

72
a5_speed.c Normal file
View File

@ -0,0 +1,72 @@
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <stdbool.h>
#include <time.h>
#include "bits.h"
#include "utils.h"
#include "a5.h"
static const uint8_t key[] = { 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef };
static const uint32_t fn = 123456;
static const uint8_t dl[] = {
/* A5/0 */
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
/* A5/1 */
0xcb, 0xa2, 0x55, 0x76, 0x17, 0x5d, 0x3b, 0x1c,
0x7b, 0x2f, 0x29, 0xa8, 0xc1, 0xb6, 0x00,
/* A5/2 */
0x45, 0x9c, 0x88, 0xc3, 0x82, 0xb7, 0xff, 0xb3,
0x98, 0xd2, 0xf9, 0x6e, 0x0f, 0x14, 0x80,
};
static const uint8_t ul[] = {
/* A5/0 */
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
/* A5/1 */
0xd9, 0x03, 0x5e, 0x0f, 0x2a, 0xec, 0x13, 0x9a,
0x05, 0xd4, 0xa8, 0x7b, 0xb1, 0x64, 0x80,
/* A5/2 */
0xf0, 0x3a, 0xac, 0xde, 0xe3, 0x5b, 0x5e, 0x65,
0x80, 0xba, 0xab, 0xc0, 0x59, 0x26, 0x40,
};
void test_a5(int n, char * kc, uint32_t count, char * block1, char * block2) {
ubit_t out[114];
int k = (n == 4) ? 16 : 8;
int ntrials;
float t;
int i;
uint8_t key[k];
osmo_hexparse(kc, key, k);
ntrials = 10000;
t = clock();
for (i = 0; i < ntrials; i++) {
osmo_a5(n, key, count, out, NULL);
}
t = (clock() - t) / (CLOCKS_PER_SEC * (float)ntrials);
printf("osmo_a5 DL takes %g seconds per iteration\n", t);
t = clock();
for (i = 0; i < ntrials; i++) {
osmo_a5(n, key, count, NULL, out);
}
t = (clock() - t) / (CLOCKS_PER_SEC * (float)ntrials);
printf("osmo_a5 UL takes %g seconds per iteration\n", t);
}
int main(int argc, char **argv)
{
test_a5(3, "2BD6459F82C5BC00", 0x24F20F, "889EEAAF9ED1BA1ABBD8436232E440", "5CA3406AA244CF69CF047AADA2DF40");
return 0;
}

128
a5_test.c Normal file
View File

@ -0,0 +1,128 @@
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <stdbool.h>
#include "bits.h"
#include "utils.h"
#include "a5.h"
static const uint8_t key[] = { 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef };
static const uint32_t fn = 123456;
static const uint8_t dl[] = {
/* A5/0 */
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
/* A5/1 */
0xcb, 0xa2, 0x55, 0x76, 0x17, 0x5d, 0x3b, 0x1c,
0x7b, 0x2f, 0x29, 0xa8, 0xc1, 0xb6, 0x00,
/* A5/2 */
0x45, 0x9c, 0x88, 0xc3, 0x82, 0xb7, 0xff, 0xb3,
0x98, 0xd2, 0xf9, 0x6e, 0x0f, 0x14, 0x80,
};
static const uint8_t ul[] = {
/* A5/0 */
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
/* A5/1 */
0xd9, 0x03, 0x5e, 0x0f, 0x2a, 0xec, 0x13, 0x9a,
0x05, 0xd4, 0xa8, 0x7b, 0xb1, 0x64, 0x80,
/* A5/2 */
0xf0, 0x3a, 0xac, 0xde, 0xe3, 0x5b, 0x5e, 0x65,
0x80, 0xba, 0xab, 0xc0, 0x59, 0x26, 0x40,
};
inline bool print_a5(int n, int k, char * dir, ubit_t * out, char * block) {
uint8_t len = 114 / 8 + 1, buf[len], res[len];
printf("A5/%d - %s: %s => ", n, dir, osmo_ubit_dump(out, 114));
osmo_hexparse(block, res, len);
osmo_ubit2pbit(buf, out, 114);
if (0 != memcmp(buf, res, len)) {
printf("FAIL");
printf("\nGOT: [%d] %s", k, osmo_hexdump_nospc(buf, len));
printf("\nEXP: [%d] %s\n", k, osmo_hexdump_nospc(res, len));
return false;
}
printf("OK\n");
return true;
}
inline bool test_a5(int n, char * kc, uint32_t count, char * block1, char * block2) {
ubit_t out[114];
int k = (n == 4) ? 16 : 8;
uint8_t key[k];
osmo_hexparse(kc, key, k);
osmo_a5(n, key, count, out, NULL);
bool d = print_a5(n, k, "DL", out, block1);
osmo_a5(n, key, count, NULL, out);
bool u = print_a5(n, k, "UL", out, block2);
return d & u;
}
int main(int argc, char **argv)
{
ubit_t exp[114], out[114];
int n, i;
for (n=0; n<3; n++) {
/* "Randomize" */
for (i=0; i<114; i++)
out[i] = i & 1;
/* DL */
osmo_pbit2ubit(exp, &dl[15*n], 114);
osmo_a5(n, key, fn, out, NULL);
printf("A5/%d - DL: %s", n, osmo_ubit_dump(out, 114));
if (!memcmp(exp, out, 114))
printf(" => OK\n");
else {
printf(" => BAD\n");
printf(" Expected: %s", osmo_ubit_dump(out, 114));
fprintf(stderr, "[!] A5/%d DL failed", n);
exit(1);
}
/* UL */
osmo_pbit2ubit(exp, &ul[15*n], 114);
osmo_a5(n, key, fn, NULL, out);
printf("A5/%d - UL: %s", n, osmo_ubit_dump(out, 114));
if (!memcmp(exp, out, 114))
printf(" => OK\n");
else {
printf(" => BAD\n");
printf(" Expected: %s", osmo_ubit_dump(out, 114));
fprintf(stderr, "[!] A5/%d UL failed", n);
exit(1);
}
}
// test vectors from 3GPP TS 55.217 and TS 55.218
test_a5(3, "2BD6459F82C5BC00", 0x24F20F, "889EEAAF9ED1BA1ABBD8436232E440", "5CA3406AA244CF69CF047AADA2DF40");
test_a5(3, "952C49104881FF48", 0x061272, "FB4D5FBCEE13A33389285686E9A5C0", "25090378E0540457C57E367662E440");
test_a5(3, "EFA8B2229E720C2A", 0x33FD3F, "0E4015755A336469C3DD8680E30340", "6F10669E2B4E18B042431A28E47F80");
test_a5(3, "952C49104881FF48", 0x061527, "AB7DB38A573A325DAA76E4CB800A40", "4C4B594FEA9D00FE8978B7B7BC1080");
test_a5(3, "3451F23A43BD2C87", 0x0E418C, "75F7C4C51560905DFBA05E46FB54C0", "192C95353CDF979E054186DF15BF00");
test_a5(3, "CAA2639BE82435CF", 0x2FF229, "301437E4D4D6565D4904C631606EC0", "F0A3B8795E264D3E1A82F684353DC0");
test_a5(3, "7AE67E87400B9FA6", 0x2F24E5, "F794290FEF643D2EA348A7796A2100", "CB6FA6C6B8A705AF9FEFE975818500");
test_a5(3, "58AF69935540698B", 0x05446B, "749CA4E6B691E5A598C461D5FE4740", "31C9E444CD04677ADAA8A082ADBC40");
test_a5(3, "017F81E5F236FE62", 0x156B26, "2A6976761E60CC4E8F9F52160276C0", "A544D8475F2C78C35614128F1179C0");
test_a5(3, "1ACA8B448B767B39", 0x0BC3B5, "A4F70DC5A2C9707F5FA1C60EB10640", "7780B597B328C1400B5C74823E8500");
test_a5(4, "3D43C388C9581E337FF1F97EB5C1F85E", 0x35D2CF, "A2FE3034B6B22CC4E33C7090BEC340", "170D7497432FF897B91BE8AECBA880");
test_a5(4, "A4496A64DF4F399F3B4506814A3E07A1", 0x212777, "89CDEE360DF9110281BCF57755A040", "33822C0C779598C9CBFC49183AF7C0");
return 0;
}

30
a5_test.ok Normal file
View File

@ -0,0 +1,30 @@
A5/0 - DL: 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 => OK
A5/0 - UL: 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 => OK
A5/1 - DL: 110010111010001001010101011101100001011101011101001110110001110001111011001011110010100110101000110000011011011000 => OK
A5/1 - UL: 110110010000001101011110000011110010101011101100000100111001101000000101110101001010100001111011101100010110010010 => OK
A5/2 - DL: 010001011001110010001000110000111000001010110111111111111011001110011000110100101111100101101110000011110001010010 => OK
A5/2 - UL: 111100000011101010101100110111101110001101011011010111100110010110000000101110101010101111000000010110010010011001 => OK
A5/3 - DL: 100010001001111011101010101011111001111011010001101110100001101010111011110110000100001101100010001100101110010001 => OK
A5/3 - UL: 010111001010001101000000011010101010001001000100110011110110100111001111000001000111101010101101101000101101111101 => OK
A5/3 - DL: 111110110100110101011111101111001110111000010011101000110011001110001001001010000101011010000110111010011010010111 => OK
A5/3 - UL: 001001010000100100000011011110001110000001010100000001000101011111000101011111100011011001110110011000101110010001 => OK
A5/3 - DL: 000011100100000000010101011101010101101000110011011001000110100111000011110111011000011010000000111000110000001101 => OK
A5/3 - UL: 011011110001000001100110100111100010101101001110000110001011000001000010010000110001101000101000111001000111111110 => OK
A5/3 - DL: 101010110111110110110011100010100101011100111010001100100101110110101010011101101110010011001011100000000000101001 => OK
A5/3 - UL: 010011000100101101011001010011111110101010011101000000001111111010001001011110001011011110110111101111000001000010 => OK
A5/3 - DL: 011101011111011111000100110001010001010101100000100100000101110111111011101000000101111001000110111110110101010011 => OK
A5/3 - UL: 000110010010110010010101001101010011110011011111100101111001111000000101010000011000011011011111000101011011111100 => OK
A5/3 - DL: 001100000001010000110111111001001101010011010110010101100101110101001001000001001100011000110001011000000110111011 => OK
A5/3 - UL: 111100001010001110111000011110010101111000100110010011010011111000011010100000101111011010000100001101010011110111 => OK
A5/3 - DL: 111101111001010000101001000011111110111101100100001111010010111010100011010010001010011101111001011010100010000100 => OK
A5/3 - UL: 110010110110111110100110110001101011100010100111000001011010111110011111111011111110100101110101100000011000010100 => OK
A5/3 - DL: 011101001001110010100100111001101011011010010001111001011010010110011000110001000110000111010101111111100100011101 => OK
A5/3 - UL: 001100011100100111100100010001001100110100000100011001110111101011011010101010001010000010000010101011011011110001 => OK
A5/3 - DL: 001010100110100101110110011101100001111001100000110011000100111010001111100111110101001000010110000000100111011011 => OK
A5/3 - UL: 101001010100010011011000010001110101111100101100011110001100001101010110000101000001001010001111000100010111100111 => OK
A5/3 - DL: 101001001111011100001101110001011010001011001001011100000111111101011111101000011100011000001110101100010000011001 => OK
A5/3 - UL: 011101111000000010110101100101111011001100101000110000010100000000001011010111000111010010000010001111101000010100 => OK
A5/4 - DL: 101000101111111000110000001101001011011010110010001011001100010011100011001111000111000010010000101111101100001101 => OK
A5/4 - UL: 000101110000110101110100100101110100001100101111111110001001011110111001000110111110100010101110110010111010100010 => OK
A5/4 - DL: 100010011100110111101110001101100000110111111001000100010000001010000001101111001111010101110111010101011010000001 => OK
A5/4 - UL: 001100111000001000101100000011000111011110010101100110001100100111001011111111000100100100011000001110101111011111 => OK

221
bits.c Normal file
View File

@ -0,0 +1,221 @@
#include <stdint.h>
#include "bits.h"
/*! \addtogroup bits
* @{
*/
/*! \file bits.c
* \brief Osmocom bit level support code
*/
/*! \brief convert unpacked bits to packed bits, return length in bytes
* \param[out] out output buffer of packed bits
* \param[in] in input buffer of unpacked bits
* \param[in] num_bits number of bits
*/
int osmo_ubit2pbit(pbit_t *out, const ubit_t *in, unsigned int num_bits)
{
unsigned int i;
uint8_t curbyte = 0;
pbit_t *outptr = out;
for (i = 0; i < num_bits; i++) {
uint8_t bitnum = 7 - (i % 8);
curbyte |= (in[i] << bitnum);
if(i % 8 == 7){
*outptr++ = curbyte;
curbyte = 0;
}
}
/* we have a non-modulo-8 bitcount */
if (i % 8)
*outptr++ = curbyte;
return outptr - out;
}
/*! \brief convert packed bits to unpacked bits, return length in bytes
* \param[out] out output buffer of unpacked bits
* \param[in] in input buffer of packed bits
* \param[in] num_bits number of bits
*/
int osmo_pbit2ubit(ubit_t *out, const pbit_t *in, unsigned int num_bits)
{
unsigned int i;
ubit_t *cur = out;
ubit_t *limit = out + num_bits;
for (i = 0; i < (num_bits/8)+1; i++) {
pbit_t byte = in[i];
*cur++ = (byte >> 7) & 1;
if (cur >= limit)
break;
*cur++ = (byte >> 6) & 1;
if (cur >= limit)
break;
*cur++ = (byte >> 5) & 1;
if (cur >= limit)
break;
*cur++ = (byte >> 4) & 1;
if (cur >= limit)
break;
*cur++ = (byte >> 3) & 1;
if (cur >= limit)
break;
*cur++ = (byte >> 2) & 1;
if (cur >= limit)
break;
*cur++ = (byte >> 1) & 1;
if (cur >= limit)
break;
*cur++ = (byte >> 0) & 1;
if (cur >= limit)
break;
}
return cur - out;
}
/*! \brief convert unpacked bits to packed bits (extended options)
* \param[out] out output buffer of packed bits
* \param[in] out_ofs offset into output buffer
* \param[in] in input buffer of unpacked bits
* \param[in] in_ofs offset into input buffer
* \param[in] num_bits number of bits
* \param[in] lsb_mode Encode bits in LSB orde instead of MSB
* \returns length in bytes (max written offset of output buffer + 1)
*/
int osmo_ubit2pbit_ext(pbit_t *out, unsigned int out_ofs,
const ubit_t *in, unsigned int in_ofs,
unsigned int num_bits, int lsb_mode)
{
int op, bn;
for (unsigned i=0; i<num_bits; i++) {
op = out_ofs + i;
bn = lsb_mode ? (op&7) : (7-(op&7));
if (in[in_ofs+i])
out[op>>3] |= 1 << bn;
else
out[op>>3] &= ~(1 << bn);
}
return ((out_ofs + num_bits - 1) >> 3) + 1;
}
/*! \brief convert packed bits to unpacked bits (extended options)
* \param[out] out output buffer of unpacked bits
* \param[in] out_ofs offset into output buffer
* \param[in] in input buffer of packed bits
* \param[in] in_ofs offset into input buffer
* \param[in] num_bits number of bits
* \param[in] lsb_mode Encode bits in LSB orde instead of MSB
* \returns length in bytes (max written offset of output buffer + 1)
*/
int osmo_pbit2ubit_ext(ubit_t *out, unsigned int out_ofs,
const pbit_t *in, unsigned int in_ofs,
unsigned int num_bits, int lsb_mode)
{
int ip, bn;
for (unsigned i=0; i<num_bits; i++) {
ip = in_ofs + i;
bn = lsb_mode ? (ip&7) : (7-(ip&7));
out[out_ofs+i] = !!(in[ip>>3] & (1<<bn));
}
return out_ofs + num_bits;
}
/* generalized bit reversal function, Chapter 7 "Hackers Delight" */
uint32_t osmo_bit_reversal(uint32_t x, enum osmo_br_mode k)
{
if (k & 1) x = (x & 0x55555555) << 1 | (x & 0xAAAAAAAA) >> 1;
if (k & 2) x = (x & 0x33333333) << 2 | (x & 0xCCCCCCCC) >> 2;
if (k & 4) x = (x & 0x0F0F0F0F) << 4 | (x & 0xF0F0F0F0) >> 4;
if (k & 8) x = (x & 0x00FF00FF) << 8 | (x & 0xFF00FF00) >> 8;
if (k & 16) x = (x & 0x0000FFFF) << 16 | (x & 0xFFFF0000) >> 16;
return x;
}
/* generalized bit reversal function, Chapter 7 "Hackers Delight" */
uint32_t osmo_revbytebits_32(uint32_t x)
{
x = (x & 0x55555555) << 1 | (x & 0xAAAAAAAA) >> 1;
x = (x & 0x33333333) << 2 | (x & 0xCCCCCCCC) >> 2;
x = (x & 0x0F0F0F0F) << 4 | (x & 0xF0F0F0F0) >> 4;
return x;
}
uint32_t osmo_revbytebits_8(uint8_t x)
{
x = (x & 0x55) << 1 | (x & 0xAA) >> 1;
x = (x & 0x33) << 2 | (x & 0xCC) >> 2;
x = (x & 0x0F) << 4 | (x & 0xF0) >> 4;
return x;
}
void osmo_revbytebits_buf(uint8_t *buf, int ilen)
{
unsigned int unaligned_cnt;
unsigned len_remain = ilen;
unsigned len = ilen;
unaligned_cnt = ((unsigned long)buf & 3);
for (unsigned i = 0; i < unaligned_cnt; i++) {
buf[i] = osmo_revbytebits_8(buf[i]);
len_remain--;
if (len_remain <= 0)
return;
}
for (unsigned i = unaligned_cnt; i < len; i += 4) {
uint32_t *cur = (uint32_t *) (buf + i);
*cur = osmo_revbytebits_32(*cur);
len_remain -= 4;
}
for (unsigned i = len - len_remain; i < len; i++) {
buf[i] = osmo_revbytebits_8(buf[i]);
len_remain--;
}
}
void osmo_revbytes_buf(uint8_t *buf, size_t len)
{
uint8_t *end = buf + len - 1, tmp;
while (buf < end) {
tmp = *buf;
*buf++ = *end;
*end-- = tmp;
}
}
/* left circular shift */
uint16_t rol16(uint16_t in, unsigned shift)
{
return (in << shift) | (in >> (16 - shift));
}
/* return 2 bytes from a given array glued into single uint16_t */
uint16_t osmo_get2bytes(const uint8_t *a)
{ /* UNSAFE! NO out-of-bounds access check. Do NOT use unless you know what you are doing! */
return (uint16_t)((((uint16_t)a[0]) << 8) + (uint16_t)a[1]);
}
/* convert uint64_t into array of 8 bytes in out */
void osmo_64pack2pbit(uint64_t in, pbit_t *out)
{
int i;
for (i = 7; i >=0; i--) {
out[i] = in & 0xFF;
in >>= 8;
}
}
/*! @} */

90
bits.h Normal file
View File

@ -0,0 +1,90 @@
#ifndef _OSMO_BITS_H
#define _OSMO_BITS_H
#include <stdint.h>
#include <stddef.h>
/*! \defgroup bits soft, unpacked and packed bits
* @{
*/
/*! \file bits.h
* \brief Osmocom bit level support code
*/
typedef int8_t sbit_t; /*!< \brief soft bit (-127...127) */
typedef uint8_t ubit_t; /*!< \brief unpacked bit (0 or 1) */
typedef uint8_t pbit_t; /*!< \brief packed bis (8 bits in a byte) */
/*
NOTE on the endianess of pbit_t:
Bits in a pbit_t are ordered MSB first, i.e. 0x80 is the first bit.
Bit i in a pbit_t array is array[i/8] & (1<<(7-i%8))
*/
/*! \brief determine how many bytes we would need for \a num_bits packed bits
* \param[in] num_bits Number of packed bits
*/
static inline unsigned int osmo_pbit_bytesize(unsigned int num_bits)
{
unsigned int pbit_bytesize = num_bits / 8;
if (num_bits % 8)
pbit_bytesize++;
return pbit_bytesize;
}
int osmo_ubit2pbit(pbit_t *out, const ubit_t *in, unsigned int num_bits);
int osmo_pbit2ubit(ubit_t *out, const pbit_t *in, unsigned int num_bits);
int osmo_ubit2pbit_ext(pbit_t *out, unsigned int out_ofs,
const ubit_t *in, unsigned int in_ofs,
unsigned int num_bits, int lsb_mode);
int osmo_pbit2ubit_ext(ubit_t *out, unsigned int out_ofs,
const pbit_t *in, unsigned int in_ofs,
unsigned int num_bits, int lsb_mode);
/* BIT REVERSAL */
/*! \brief bit-reversal mode for osmo_bit_reversal() */
enum osmo_br_mode {
/*! \brief reverse all bits in a 32bit dword */
OSMO_BR_BITS_IN_DWORD = 31,
/*! \brief reverse byte order in a 32bit dword */
OSMO_BR_BYTES_IN_DWORD = 24,
/*! \brief reverse bits of each byte in a 32bit dword */
OSMO_BR_BITS_IN_BYTE = 7,
/*! \brief swap the two 16bit words in a 32bit dword */
OSMO_BR_WORD_SWAP = 16,
};
/*! \brief generic bit reversal function */
uint32_t osmo_bit_reversal(uint32_t x, enum osmo_br_mode k);
/* \brief reverse the bits within each byte of a 32bit word */
uint32_t osmo_revbytebits_32(uint32_t x);
/* \brief reverse the bits within a byte */
uint32_t osmo_revbytebits_8(uint8_t x);
/* \brief reverse the bits of each byte in a given buffer */
void osmo_revbytebits_buf(uint8_t *buf, int len);
/* \brief reverse the order of the bytes in a given buffer */
void osmo_revbytes_buf(uint8_t *buf, size_t len);
/* \brief left circular shift */
uint16_t rol16(uint16_t in, unsigned shift);
/* return 2 bytes from a given array glued into single uint16_t */
uint16_t osmo_get2bytes(const uint8_t *a);
/* convert uint64_t into array of 8 bytes in out */
void osmo_64pack2pbit(uint64_t in, pbit_t *out);
/*! @} */
#endif /* _OSMO_BITS_H */

45
gea.c Normal file
View File

@ -0,0 +1,45 @@
/*
* gea.c
*
* Full reimplementation of GEA3
*
* Copyright (C) 2013 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 "bits.h"
#include "gprs_cipher.h"
#include "kasumi.h"
int osmo_gea4(uint8_t *out, uint16_t len, uint8_t * kc, uint32_t iv, enum gprs_cipher_direction direction) {
_kasumi_kgcore(0xFF, 0, iv, direction, kc, out, len * 8);
return 0;
}
int osmo_gea3(uint8_t *out, uint16_t len, uint64_t kc, uint32_t iv, enum gprs_cipher_direction direction) {
uint8_t ck[16];
osmo_64pack2pbit(kc, ck);
osmo_64pack2pbit(kc, ck + 8);
// _kasumi_kgcore(0xFF, 0, iv, direction, ck, out, len * 8);
return osmo_gea4(out, len, ck, iv, direction);
}

28
gea.h Normal file
View File

@ -0,0 +1,28 @@
/*
* GEA3 header
*
* See gea.c for details
*/
#ifndef __GEA_H__
#define __GEA_H__
#include <stdint.h>
#include "gprs_cipher.h"
/*
* Performs the GEA3 algorithm (used in GPRS)
* out : uint8_t []
* len : uint16_t
* kc : uint64_t
* iv : uint32_t
* direct: 0 or 1
*/
int osmo_gea3(uint8_t *out, uint16_t len, uint64_t kc, uint32_t iv, enum gprs_cipher_direction direct);
int osmo_gea4(uint8_t *out, uint16_t len, uint8_t * kc, uint32_t iv, enum gprs_cipher_direction direct);
#endif /* __GEA_H__ */

54
gea_test.c Normal file
View File

@ -0,0 +1,54 @@
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <stdbool.h>
#include "bits.h"
#include "utils.h"
#include "gprs_cipher.h"
#include "gea.h"
void print_check(char * res, uint8_t * out, uint16_t len) {
uint8_t buf[len];
osmo_hexparse(res, buf, len);
if (0 != memcmp(buf, out, len)) {
printf("FAIL:\n");
printf("OUT: %s\n", osmo_hexdump_nospc(out, len));
printf("EXP: %s\n", osmo_hexdump_nospc(buf, len));
}
else printf("OK\n");
}
void test_gea3(uint64_t kc, uint32_t iv, int dir, uint16_t len, char * res) {
printf("%d: %d -> 0x%X ", len, dir, iv);
uint8_t out[len];
osmo_gea3(out, len, kc, iv, dir);
print_check(res, out, len);
}
void test_gea4(char * kc, uint32_t iv, int dir, uint16_t len, char * res) {
printf("%d: %d -> 0x%X ", len, dir, iv);
uint8_t out[len], ck[256];
osmo_hexparse(kc, ck, len);
osmo_gea4(out, len, ck, iv, dir);
print_check(res, out, len);
}
int main(int argc, char **argv)
{
// printf("GEA3 support: %d\n", gprs_cipher_supported(GPRS_ALGO_GEA3));
// printf("GEA4 support: %d\n", gprs_cipher_supported(GPRS_ALGO_GEA4));
// test vectors according to 3GPP TS 55.217 and TS 55.218
test_gea3(0x2BD6459F82C5BC00LL, 0x8E9421A3, 0, 59, "5F359709DE950D0105B17B6C90194280F880B48DCCDC2AFEED415DBEF4354EEBB21D073CCBBFB2D706BD7AFFD371FC96E3970D143DCB2624054826");
test_gea3(0x952C49104881FF48LL, 0x5064DB71, 0, 59, "FDC03D738C8E14FF0320E59AAF75760799E9DA78DD8F888471C4AEAAC1849633A26CD84F459D265B83D7D9B9A0B1E54F4D75E331640DF19E0DB0E0");
test_gea3(0xEFA8B2229E720C2ALL, 0x4BDBD5E5, 1, 59, "4718A2ADFC90590949DDADAB406EC3B925F1AF1214673909DAAB96BB4C18B1374BB1E99445A81CC856E47C6E49E9DBB9873D0831B2175CA1E109BA");
test_gea3(0x3451F23A43BD2C87LL, 0x893FE14F, 0, 59, "B46B1E284E3F8B63B86D9DF0915CFCEDDF2F061895BF9F82BF2593AE4847E94A4626C393CF8941CE15EA7812690D8415B88C5730FE1F5D410E16A2");
test_gea3(0xCAA2639BE82435CFLL, 0x8FE17885, 1, 59, "9FEFAF155A26CF35603E727CDAA87BA067FD84FF98A50B7FF0EC8E95A0FB70E79CB93DEE2B7E9AB59D050E1262401571F349C68229DDF0DECC4E85");
test_gea3(0x1ACA8B448B767B39LL, 0x4F7BC3B5, 0, 59, "514F6C3A3B5A55CA190092F7BB6E80EF3EDB738FCDCE2FF90BB387DDE75BBC32A04A67B898A3DFB8198FFFC37D437CF69E7F9C13B51A868720E750");
test_gea4("D3C5D592327FB11C4035C6680AF8C6D1", 0x0A3A59B4, 0, 51, "6E217CE41EBEFB5EC8094C15974290065E42BABC9AE35654A53085CE68DFA4426A2FF0AD4AF3341006A3F84B7613ACB4FBDC34");
test_gea4("3D43C388C9581E337FF1F97EB5C1F85E", 0x48571AB9, 0, 59, "FC7314EF00A63ED0116F236C5D25C54EEC56A5B71F9F18B4D7941F84E422ACBDE5EEA9A204679002D14F312F3DEE2A1AC917C3FBDC3696143C0F5D");
test_gea4("A4496A64DF4F399F3B4506814A3E07A1", 0xEB04ADE2, 1, 59, "2AEB5970FB06B718027D048488AAF24FB3B74EA4A6B1242FF85B108FF816A303C72757D9AAD862B835D1D287DBC141D0A28D79D87BB137CD1198CD");
return 0;
}

9
gea_test.ok Normal file
View File

@ -0,0 +1,9 @@
59: 0 -> 0x8E9421A3 OK
59: 0 -> 0x5064DB71 OK
59: 1 -> 0x4BDBD5E5 OK
59: 0 -> 0x893FE14F OK
59: 1 -> 0x8FE17885 OK
59: 0 -> 0x4F7BC3B5 OK
51: 0 -> 0xA3A59B4 OK
59: 0 -> 0x48571AB9 OK
59: 1 -> 0xEB04ADE2 OK

55
gprs_cipher.h Normal file
View File

@ -0,0 +1,55 @@
#ifndef _GPRS_CIPHER_H
#define _GPRS_CIPHER_H
#include "linuxlist.h"
#define GSM0464_CIPH_MAX_BLOCK 1523
enum gprs_ciph_algo {
GPRS_ALGO_GEA0,
GPRS_ALGO_GEA1,
GPRS_ALGO_GEA2,
GPRS_ALGO_GEA3,
GPRS_ALGO_GEA4,
_GPRS_ALGO_NUM
};
enum gprs_cipher_direction {
GPRS_CIPH_MS2SGSN,
GPRS_CIPH_SGSN2MS,
};
/* An implementation of a GPRS cipher */
struct gprs_cipher_impl {
struct llist_head list;
enum gprs_ciph_algo algo;
const char *name;
unsigned int priority;
/* As specified in 04.64 Annex A. Uses Kc, IV and direction
* to generate the 1523 bytes cipher stream that need to be
* XORed wit the plaintext for encrypt / ciphertext for decrypt */
int (*run)(uint8_t *out, uint16_t len, uint64_t kc, uint32_t iv,
enum gprs_cipher_direction direction);
};
/* register a cipher with the core (from a plugin) */
int gprs_cipher_register(struct gprs_cipher_impl *ciph);
/* load all available GPRS cipher plugins */
int gprs_cipher_load(const char *path);
/* function to be called by core code */
int gprs_cipher_run(uint8_t *out, uint16_t len, enum gprs_ciph_algo algo,
uint64_t kc, uint32_t iv, enum gprs_cipher_direction dir);
/* Do we have an implementation for this cipher? */
int gprs_cipher_supported(enum gprs_ciph_algo algo);
/* GSM TS 04.64 / Section A.2.1 : Generation of 'input' */
uint32_t gprs_cipher_gen_input_ui(uint32_t iov_ui, uint8_t sapi, uint32_t lfn, uint32_t oc);
/* GSM TS 04.64 / Section A.2.1 : Generation of 'input' */
uint32_t gprs_cipher_gen_input_i(uint32_t iov_i, uint32_t lfn, uint32_t oc);
#endif /* _GPRS_CIPHER_H */

27
ifc.cpp Normal file
View File

@ -0,0 +1,27 @@
#include "a53.h"
#include "a5.h"
#include <stdio.h>
void A53_GSM( u8 *key, int klen, int count, u8 *block1, u8 *block2 )
{
ubit_t dl[120];
ubit_t ul[120];
static bool first = true;
if (first) {
printf("public A5/3\n");
first = false;
}
osmo_a5_3(key, (uint32_t)count, dl, ul);
for (int i = 0; i < 15; i++) {
unsigned char acc1 = 0;
unsigned char acc2 = 0;
for (int j = 0; j < 8; j++) {
acc1 |= (dl[i*8+j] & 1) << (7-j);
acc2 |= (ul[i*8+j] & 1) << (7-j);
}
block1[i] = acc1;
block2[i] = acc2;
}
block1[14] &= 0xC0;
block2[14] &= 0xC0;
}

193
kasumi.c Normal file
View File

@ -0,0 +1,193 @@
/* 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 "bits.h"
#include "kasumi.h"
static uint16_t
_kasumi_FI(uint16_t I, uint16_t skey)
{
static 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 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;
}
static uint32_t
_kasumi_FO(uint32_t I, uint16_t *KOi1, uint16_t *KOi2, uint16_t *KOi3, uint16_t *KIi1, uint16_t *KIi2, 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;
}
static uint32_t
_kasumi_FL(uint32_t I, uint16_t *KLi1, 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 ^= rol16(tmp, 1);
tmp = R | KLi2[i];
L ^= rol16(tmp, 1);
return (((uint32_t)L) << 16) + R;
}
uint64_t
_kasumi(uint64_t P, uint16_t *KLi1, uint16_t *KLi2, uint16_t *KOi1, uint16_t *KOi2, uint16_t *KOi3, uint16_t *KIi1, uint16_t *KIi2, 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 };
for (i = 0; i < 8; i++) /* Work with 16 bit subkeys and create prime subkeys */
{
C[i] ^= osmo_get2bytes(key + i * 2);
}
/* C[] now stores K-prime[] */
for (i = 0; i < 8; i++) /* Create round-specific subkeys */
{
KLi1[i] = rol16(osmo_get2bytes(key + i * 2), 1);
KLi2[i] = C[(i + 2) & 0x7];
KOi1[i] = rol16(osmo_get2bytes(key + ((2 * (i + 1)) & 0xE)), 5);
KOi2[i] = rol16(osmo_get2bytes(key + ((2 * (i + 5)) & 0xE)), 8);
KOi3[i] = rol16(osmo_get2bytes(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);
for (i = 0; i < cl / 64 + 1; i++) /* i is a block counter */
{
BLK = _kasumi(A ^ i ^ BLK, KLi1, KLi2, KOi1, KOi2, KOi3, KIi1, KIi2, KIi3);
osmo_64pack2pbit(BLK, co + (i * 8));
}
}

36
kasumi.h Normal file
View File

@ -0,0 +1,36 @@
/*
* KASUMI header
*
* See kasumi.c for details
*/
#ifndef __KASUMI_H__
#define __KASUMI_H__
#include <stdint.h>
/*
* Single iteration of KASUMI cipher
*/
uint64_t _kasumi(uint64_t P, uint16_t *KLi1, uint16_t *KLi2, uint16_t *KOi1, uint16_t *KOi2, uint16_t *KOi3, uint16_t *KIi1, uint16_t *KIi2, uint16_t *KIi3);
/*
* Implementation of the KGCORE algorithm (used by A5/3, A5/4, GEA3, GEA4 and ECSD)
*
* CA : uint8_t
* cb : uint8_t
* cc : uint32_t
* cd : uint8_t
* ck : uint8_t [8]
* co : uint8_t [output, cl-dependent]
* cl : uint16_t
*/
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);
#endif /* __KASUMI_H__ */

128
kasumi_test.c Normal file
View File

@ -0,0 +1,128 @@
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <stdbool.h>
#include "bits.h"
#include "utils.h"
#include "kasumi.h" // for testing internal A5/3 functions
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);
if (passed) printf(" OK. "); else printf("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 (0xDF1F9B251C0BF45FLL == _kasumi(0xEA024714AD5C4D84LL, _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 (0xDE551988CEB2F9B7LL == _kasumi(0xD3C5D592327FB11CLL, _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 (0x4592B0E78690F71BLL == _kasumi(0x62A540981BA6F9B7LL, _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 = 0xCA49C1C75771AB0BLL, 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 (0x738BAD4C4A690802LL == 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;
}

10
kasumi_test.ok Normal file
View File

@ -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

360
linuxlist.h Normal file
View File

@ -0,0 +1,360 @@
#ifndef _LINUX_LLIST_H
#define _LINUX_LLIST_H
#include <stddef.h>
#ifndef inline
#define inline __inline__
#endif
static inline void prefetch(__attribute__((unused)) const void *x) {;}
/**
* container_of - cast a member of a structure out to the containing structure
*
* @ptr: the pointer to the member.
* @type: the type of the container struct this is embedded in.
* @member: the name of the member within the struct.
*
*/
#define container_of(ptr, type, member) ({ \
const typeof( ((type *)0)->member ) *__mptr = (typeof( ((type *)0)->member ) *)(ptr); \
(type *)( (char *)__mptr - offsetof(type, member) );})
/*
* These are non-NULL pointers that will result in page faults
* under normal circumstances, used to verify that nobody uses
* non-initialized llist entries.
*/
#define LLIST_POISON1 ((void *) 0x00100100)
#define LLIST_POISON2 ((void *) 0x00200200)
/*
* Simple doubly linked llist implementation.
*
* Some of the internal functions ("__xxx") are useful when
* manipulating whole llists rather than single entries, as
* sometimes we already know the next/prev entries and we can
* generate better code by using them directly rather than
* using the generic single-entry routines.
*/
struct llist_head {
struct llist_head *next, *prev;
};
#define LLIST_HEAD_INIT(name) { &(name), &(name) }
#define LLIST_HEAD(name) \
struct llist_head name = LLIST_HEAD_INIT(name)
#define INIT_LLIST_HEAD(ptr) do { \
(ptr)->next = (ptr); (ptr)->prev = (ptr); \
} while (0)
/*
* Insert a new entry between two known consecutive entries.
*
* This is only for internal llist manipulation where we know
* the prev/next entries already!
*/
static inline void __llist_add(struct llist_head *_new,
struct llist_head *prev,
struct llist_head *next)
{
next->prev = _new;
_new->next = next;
_new->prev = prev;
prev->next = _new;
}
/**
* llist_add - add a new entry
* @new: new entry to be added
* @head: llist head to add it after
*
* Insert a new entry after the specified head.
* This is good for implementing stacks.
*/
static inline void llist_add(struct llist_head *_new, struct llist_head *head)
{
__llist_add(_new, head, head->next);
}
/**
* llist_add_tail - add a new entry
* @new: new entry to be added
* @head: llist head to add it before
*
* Insert a new entry before the specified head.
* This is useful for implementing queues.
*/
static inline void llist_add_tail(struct llist_head *_new, struct llist_head *head)
{
__llist_add(_new, head->prev, head);
}
/*
* Delete a llist entry by making the prev/next entries
* point to each other.
*
* This is only for internal llist manipulation where we know
* the prev/next entries already!
*/
static inline void __llist_del(struct llist_head * prev, struct llist_head * next)
{
next->prev = prev;
prev->next = next;
}
/**
* llist_del - deletes entry from llist.
* @entry: the element to delete from the llist.
* Note: llist_empty on entry does not return true after this, the entry is
* in an undefined state.
*/
static inline void llist_del(struct llist_head *entry)
{
__llist_del(entry->prev, entry->next);
entry->next = (struct llist_head *)LLIST_POISON1;
entry->prev = (struct llist_head *)LLIST_POISON2;
}
/**
* llist_del_init - deletes entry from llist and reinitialize it.
* @entry: the element to delete from the llist.
*/
static inline void llist_del_init(struct llist_head *entry)
{
__llist_del(entry->prev, entry->next);
INIT_LLIST_HEAD(entry);
}
/**
* llist_move - delete from one llist and add as another's head
* @llist: the entry to move
* @head: the head that will precede our entry
*/
static inline void llist_move(struct llist_head *llist, struct llist_head *head)
{
__llist_del(llist->prev, llist->next);
llist_add(llist, head);
}
/**
* llist_move_tail - delete from one llist and add as another's tail
* @llist: the entry to move
* @head: the head that will follow our entry
*/
static inline void llist_move_tail(struct llist_head *llist,
struct llist_head *head)
{
__llist_del(llist->prev, llist->next);
llist_add_tail(llist, head);
}
/**
* llist_empty - tests whether a llist is empty
* @head: the llist to test.
*/
static inline int llist_empty(const struct llist_head *head)
{
return head->next == head;
}
static inline void __llist_splice(struct llist_head *llist,
struct llist_head *head)
{
struct llist_head *first = llist->next;
struct llist_head *last = llist->prev;
struct llist_head *at = head->next;
first->prev = head;
head->next = first;
last->next = at;
at->prev = last;
}
/**
* llist_splice - join two llists
* @llist: the new llist to add.
* @head: the place to add it in the first llist.
*/
static inline void llist_splice(struct llist_head *llist, struct llist_head *head)
{
if (!llist_empty(llist))
__llist_splice(llist, head);
}
/**
* llist_splice_init - join two llists and reinitialise the emptied llist.
* @llist: the new llist to add.
* @head: the place to add it in the first llist.
*
* The llist at @llist is reinitialised
*/
static inline void llist_splice_init(struct llist_head *llist,
struct llist_head *head)
{
if (!llist_empty(llist)) {
__llist_splice(llist, head);
INIT_LLIST_HEAD(llist);
}
}
/**
* llist_entry - get the struct for this entry
* @ptr: the &struct llist_head pointer.
* @type: the type of the struct this is embedded in.
* @member: the name of the llist_struct within the struct.
*/
#define llist_entry(ptr, type, member) \
container_of(ptr, type, member)
/**
* llist_for_each - iterate over a llist
* @pos: the &struct llist_head to use as a loop counter.
* @head: the head for your llist.
*/
#define llist_for_each(pos, head) \
for (pos = (head)->next, prefetch(pos->next); pos != (head); \
pos = pos->next, prefetch(pos->next))
/**
* __llist_for_each - iterate over a llist
* @pos: the &struct llist_head to use as a loop counter.
* @head: the head for your llist.
*
* This variant differs from llist_for_each() in that it's the
* simplest possible llist iteration code, no prefetching is done.
* Use this for code that knows the llist to be very short (empty
* or 1 entry) most of the time.
*/
#define __llist_for_each(pos, head) \
for (pos = (head)->next; pos != (head); pos = pos->next)
/**
* llist_for_each_prev - iterate over a llist backwards
* @pos: the &struct llist_head to use as a loop counter.
* @head: the head for your llist.
*/
#define llist_for_each_prev(pos, head) \
for (pos = (head)->prev, prefetch(pos->prev); pos != (head); \
pos = pos->prev, prefetch(pos->prev))
/**
* llist_for_each_safe - iterate over a llist safe against removal of llist entry
* @pos: the &struct llist_head to use as a loop counter.
* @n: another &struct llist_head to use as temporary storage
* @head: the head for your llist.
*/
#define llist_for_each_safe(pos, n, head) \
for (pos = (head)->next, n = pos->next; pos != (head); \
pos = n, n = pos->next)
/**
* llist_for_each_entry - iterate over llist of given type
* @pos: the type * to use as a loop counter.
* @head: the head for your llist.
* @member: the name of the llist_struct within the struct.
*/
#define llist_for_each_entry(pos, head, member) \
for (pos = llist_entry((head)->next, typeof(*pos), member), \
prefetch(pos->member.next); \
&pos->member != (head); \
pos = llist_entry(pos->member.next, typeof(*pos), member), \
prefetch(pos->member.next))
/**
* llist_for_each_entry_reverse - iterate backwards over llist of given type.
* @pos: the type * to use as a loop counter.
* @head: the head for your llist.
* @member: the name of the llist_struct within the struct.
*/
#define llist_for_each_entry_reverse(pos, head, member) \
for (pos = llist_entry((head)->prev, typeof(*pos), member), \
prefetch(pos->member.prev); \
&pos->member != (head); \
pos = llist_entry(pos->member.prev, typeof(*pos), member), \
prefetch(pos->member.prev))
/**
* llist_for_each_entry_continue - iterate over llist of given type
* continuing after existing point
* @pos: the type * to use as a loop counter.
* @head: the head for your llist.
* @member: the name of the llist_struct within the struct.
*/
#define llist_for_each_entry_continue(pos, head, member) \
for (pos = llist_entry(pos->member.next, typeof(*pos), member), \
prefetch(pos->member.next); \
&pos->member != (head); \
pos = llist_entry(pos->member.next, typeof(*pos), member), \
prefetch(pos->member.next))
/**
* llist_for_each_entry_safe - iterate over llist of given type safe against removal of llist entry
* @pos: the type * to use as a loop counter.
* @n: another type * to use as temporary storage
* @head: the head for your llist.
* @member: the name of the llist_struct within the struct.
*/
#define llist_for_each_entry_safe(pos, n, head, member) \
for (pos = llist_entry((head)->next, typeof(*pos), member), \
n = llist_entry(pos->member.next, typeof(*pos), member); \
&pos->member != (head); \
pos = n, n = llist_entry(n->member.next, typeof(*n), member))
/**
* llist_for_each_rcu - iterate over an rcu-protected llist
* @pos: the &struct llist_head to use as a loop counter.
* @head: the head for your llist.
*/
#define llist_for_each_rcu(pos, head) \
for (pos = (head)->next, prefetch(pos->next); pos != (head); \
pos = pos->next, ({ smp_read_barrier_depends(); 0;}), prefetch(pos->next))
#define __llist_for_each_rcu(pos, head) \
for (pos = (head)->next; pos != (head); \
pos = pos->next, ({ smp_read_barrier_depends(); 0;}))
/**
* llist_for_each_safe_rcu - iterate over an rcu-protected llist safe
* against removal of llist entry
* @pos: the &struct llist_head to use as a loop counter.
* @n: another &struct llist_head to use as temporary storage
* @head: the head for your llist.
*/
#define llist_for_each_safe_rcu(pos, n, head) \
for (pos = (head)->next, n = pos->next; pos != (head); \
pos = n, ({ smp_read_barrier_depends(); 0;}), n = pos->next)
/**
* llist_for_each_entry_rcu - iterate over rcu llist of given type
* @pos: the type * to use as a loop counter.
* @head: the head for your llist.
* @member: the name of the llist_struct within the struct.
*/
#define llist_for_each_entry_rcu(pos, head, member) \
for (pos = llist_entry((head)->next, typeof(*pos), member), \
prefetch(pos->member.next); \
&pos->member != (head); \
pos = llist_entry(pos->member.next, typeof(*pos), member), \
({ smp_read_barrier_depends(); 0;}), \
prefetch(pos->member.next))
/**
* llist_for_each_continue_rcu - iterate over an rcu-protected llist
* continuing after existing point.
* @pos: the &struct llist_head to use as a loop counter.
* @head: the head for your llist.
*/
#define llist_for_each_continue_rcu(pos, head) \
for ((pos) = (pos)->next, prefetch((pos)->next); (pos) != (head); \
(pos) = (pos)->next, ({ smp_read_barrier_depends(); 0;}), prefetch((pos)->next))
#endif

213
utils.c Normal file
View File

@ -0,0 +1,213 @@
#include <string.h>
#include <stdint.h>
#include <errno.h>
#include <stdio.h>
#include "utils.h"
/*! \addtogroup utils
* @{
*/
/*! \file utils.c */
static char namebuf[255];
/*! \brief get human-readable string for given value
* \param[in] vs Array of value_string tuples
* \param[in] val Value to be converted
* \returns pointer to human-readable string
*/
const char *get_value_string(const struct value_string *vs, uint32_t val)
{
int i;
for (i = 0;; i++) {
if (vs[i].value == 0 && vs[i].str == NULL)
break;
if (vs[i].value == val)
return vs[i].str;
}
snprintf(namebuf, sizeof(namebuf), "unknown 0x%x", val);
return namebuf;
}
/*! \brief get numeric value for given human-readable string
* \param[in] vs Array of value_string tuples
* \param[in] str human-readable string
* \returns numeric value (>0) or negative numer in case of error
*/
int get_string_value(const struct value_string *vs, const char *str)
{
int i;
for (i = 0;; i++) {
if (vs[i].value == 0 && vs[i].str == NULL)
break;
if (!strcasecmp(vs[i].str, str))
return vs[i].value;
}
return -EINVAL;
}
/*! \brief Convert BCD-encoded digit into printable character
* \param[in] bcd A single BCD-encoded digit
* \returns single printable character
*/
char osmo_bcd2char(uint8_t bcd)
{
if (bcd < 0xa)
return '0' + bcd;
else
return 'A' + (bcd - 0xa);
}
/* only works for numbers in ascii */
uint8_t osmo_char2bcd(char c)
{
return c - 0x30;
}
int osmo_hexparse(const char *str, uint8_t *b, int max_len)
{
int i, l, v;
l = strlen(str);
if ((l&1) || ((l>>1) > max_len))
return -1;
memset(b, 0x00, max_len);
for (i=0; i<l; i++) {
char c = str[i];
if (c >= '0' && c <= '9')
v = c - '0';
else if (c >= 'a' && c <= 'f')
v = 10 + (c - 'a');
else if (c >= 'A' && c <= 'F')
v = 10 + (c - 'A');
else
return -1;
b[i>>1] |= v << (i&1 ? 0 : 4);
}
return i>>1;
}
static char hexd_buff[4096];
static char *_osmo_hexdump(const unsigned char *buf, int len, const char *delim)
{
int i;
char *cur = hexd_buff;
hexd_buff[0] = 0;
for (i = 0; i < len; i++) {
int len_remain = sizeof(hexd_buff) - (cur - hexd_buff);
if (len_remain <= 0)
break;
int rc = snprintf(cur, len_remain, "%02x%s", buf[i], delim);
if (rc <= 0)
break;
cur += rc;
}
hexd_buff[sizeof(hexd_buff)-1] = 0;
return hexd_buff;
}
/*! \brief Convert a sequence of unpacked bits to ASCII string
* \param[in] bits A sequence of unpacked bits
* \param[in] len Length of bits
*/
char *osmo_ubit_dump(const uint8_t *bits, unsigned int len)
{
if (len > sizeof(hexd_buff)-1)
len = sizeof(hexd_buff)-1;
memset(hexd_buff, 0, sizeof(hexd_buff));
for (unsigned i = 0; i < len; i++) {
char outch;
switch (bits[i]) {
case 0:
outch = '0';
break;
case 0xff:
outch = '?';
break;
case 1:
outch = '1';
break;
default:
outch = 'E';
break;
}
hexd_buff[i] = outch;
}
hexd_buff[sizeof(hexd_buff)-1] = 0;
return hexd_buff;
}
/*! \brief Convert binary sequence to hexadecimal ASCII string
* \param[in] buf pointer to sequence of bytes
* \param[in] len length of buf in number of bytes
* \returns pointer to zero-terminated string
*
* This function will print a sequence of bytes as hexadecimal numbers,
* adding one space character between each byte (e.g. "1a ef d9")
*/
char *osmo_hexdump(const unsigned char *buf, int len)
{
return _osmo_hexdump(buf, len, " ");
}
/*! \brief Convert binary sequence to hexadecimal ASCII string
* \param[in] buf pointer to sequence of bytes
* \param[in] len length of buf in number of bytes
* \returns pointer to zero-terminated string
*
* This function will print a sequence of bytes as hexadecimal numbers,
* without any space character between each byte (e.g. "1aefd9")
*/
char *osmo_hexdump_nospc(const unsigned char *buf, int len)
{
return _osmo_hexdump(buf, len, "");
}
/* Compat with previous typo to preserve abi */
// char *osmo_osmo_hexdump_nospc(const unsigned char *buf, int len)
// __attribute__((weak, alias("osmo_hexdump_nospc")));
// #include "../config.h"
#ifdef HAVE_CTYPE_H
#include <ctype.h>
/*! \brief Convert an entire string to lower case
* \param[out] out output string, caller-allocated
* \param[in] in input string
*/
void osmo_str2lower(char *out, const char *in)
{
unsigned int i;
for (i = 0; i < strlen(in); i++)
out[i] = tolower(in[i]);
out[strlen(in)] = '\0';
}
/*! \brief Convert an entire string to upper case
* \param[out] out output string, caller-allocated
* \param[in] in input string
*/
void osmo_str2upper(char *out, const char *in)
{
unsigned int i;
for (i = 0; i < strlen(in); i++)
out[i] = toupper(in[i]);
out[strlen(in)] = '\0';
}
#endif /* HAVE_CTYPE_H */
/*! @} */

22
utils.h Normal file
View File

@ -0,0 +1,22 @@
#ifndef OSMOCORE_UTIL_H
#define OSMOCORE_UTIL_H
#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
#include <stdint.h>
struct value_string {
unsigned int value;
const char *str;
};
const char *get_value_string(const struct value_string *vs, uint32_t val);
int get_string_value(const struct value_string *vs, const char *str);
char *osmo_ubit_dump(const uint8_t *bits, unsigned int len);
char *osmo_hexdump_nospc(const unsigned char *buf, int len);
int osmo_hexparse(const char *str, uint8_t *b, int max_len);
#endif