mirror of https://github.com/PentHertz/liba53.git
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:
commit
de2985721f
|
@ -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.
|
||||
|
|
@ -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
|
||||
#
|
||||
#
|
||||
#
|
|
@ -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
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
/*! @} */
|
|
@ -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__ */
|
|
@ -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 );
|
|
@ -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);
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
/*! @} */
|
|
@ -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 */
|
|
@ -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);
|
||||
}
|
|
@ -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__ */
|
||||
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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 */
|
|
@ -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;
|
||||
}
|
|
@ -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));
|
||||
}
|
||||
}
|
|
@ -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__ */
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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
|
|
@ -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 */
|
||||
|
||||
/*! @} */
|
|
@ -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
|
Loading…
Reference in New Issue