Signed-off-by: Max Suraev Max.Suraev@fairwaves.co --- include/osmocom/gsm/a5.h | 10 ++++--- src/gsm/a5.c | 69 ++++++++++++++++++++++++++++++++++++++++++++++-- tests/Makefile.am | 1 + tests/a5/a5_test.c | 67 ++++++++++++++++++++++++++++++++++++---------- tests/a5/a5_test.ok | 24 +++++++++++++++++ 5 files changed, 152 insertions(+), 19 deletions(-)
diff --git a/include/osmocom/gsm/a5.h b/include/osmocom/gsm/a5.h index c076734..e3e73f9 100644 --- a/include/osmocom/gsm/a5.h +++ b/include/osmocom/gsm/a5.h @@ -23,6 +23,7 @@ #pragma once
#include <stdint.h> +#include <stdbool.h>
#include <osmocom/core/bits.h>
@@ -48,13 +49,16 @@ osmo_a5_fn_count(uint32_t fn) }
/* Notes: - * - key must be 8 bytes long (or NULL for A5/0) + * - key must be 8/16 bytes long (or NULL for A5/0) depending on algorithm variant * - 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) + * - fn is the _real_ GSM frame number (unless fn_correct is false), + * converted internally to fn_count + * - only top-level osmo_a5 should be considered as part of public API */ int 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, bool fn_correct); +void _osmo_a5_4(const uint8_t *key, uint32_t fn, ubit_t *dl, ubit_t *ul, bool fn_correct);
/*! @} */ diff --git a/src/gsm/a5.c b/src/gsm/a5.c index de821e8..c24ef2d 100644 --- a/src/gsm/a5.c +++ b/src/gsm/a5.c @@ -36,8 +36,10 @@
#include <errno.h> #include <string.h> +#include <stdbool.h>
#include <osmocom/gsm/a5.h> +#include <osmocom/gsm/kasumi.h>
/*! \brief Main method to generate a A5/x cipher stream * \param[in] n Which A5/x method to use @@ -47,7 +49,7 @@ * \param[out] ul Pointer to array of ubits to return Uplink cipher stream * \returns 0 for success, -ENOTSUP for invalid cipher selection. * - * Currently A5/[0-2] are supported. + * Currently A5/[0-4] are supported. * Either (or both) of dl/ul can be NULL if not needed. */ int @@ -70,8 +72,16 @@ osmo_a5(int n, const uint8_t *key, uint32_t fn, ubit_t *dl, ubit_t *ul) osmo_a5_2(key, fn, dl, ul); break;
+ case 3: + _osmo_a5_3(key, fn, dl, ul, true); + break; + + case 4: + _osmo_a5_4(key, fn, dl, ul, true); + break; + default: - /* a5/[3..7] not supported here/yet */ + /* a5/[5..7] not supported here/yet */ return -ENOTSUP; }
@@ -368,4 +378,59 @@ osmo_a5_2(const uint8_t *key, uint32_t fn, ubit_t *dl, ubit_t *ul) } }
+/* ------------------------------------------------------------------------ */ +/* A5/3&4 */ +/* ------------------------------------------------------------------------ */ + +/*! \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 + * \param[in] fn_correct true if fn is a real GSM frame number and thus requires internal conversion + * + * 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, bool fn_correct) +{ + uint8_t ck[16]; + memcpy(ck, key, 8); + memcpy(ck + 8, key, 8); + /* internal function require 128 bit key so we expand by concatenating supplied 64 bit key */ + _osmo_a5_4(ck, fn, dl, ul, fn_correct); +} + +/*! \brief Generate a GSM A5/4 cipher stream + * \param[in] key 16 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 + * \param[in] fn_correct true if fn is a real GSM frame number and thus requires internal conversion + * + * 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_4(const uint8_t *ck, uint32_t fn, ubit_t *dl, ubit_t *ul, bool fn_correct) +{ + uint8_t i, gamma[32], uplink[15]; + uint32_t fn_count = (fn_correct) ? osmo_a5_fn_count(fn) : fn; + + if (ul) { + _kasumi_kgcore(0xF, 0, fn_count, 0, ck, gamma, 228); + 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_count, 0, ck, gamma, 114); + osmo_pbit2ubit(dl, gamma, 114); + } +} + /*! @} */ diff --git a/tests/Makefile.am b/tests/Makefile.am index ea4bf9c..baf11a2 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -18,6 +18,7 @@ utils_utils_test_LDADD = $(top_builddir)/src/libosmocore.la
a5_a5_test_SOURCES = a5/a5_test.c a5_a5_test_LDADD = $(top_builddir)/src/libosmocore.la $(top_builddir)/src/gsm/libosmogsm.la +a5_a5_test_LDFLAGS = -static
kasumi_kasumi_test_SOURCES = kasumi/kasumi_test.c kasumi_kasumi_test_LDADD = $(top_builddir)/src/libosmocore.la $(top_builddir)/src/gsm/libosmogsm.la diff --git a/tests/a5/a5_test.c b/tests/a5/a5_test.c index 14436f1..9425fe4 100644 --- a/tests/a5/a5_test.c +++ b/tests/a5/a5_test.c @@ -2,6 +2,7 @@ #include <stdlib.h> #include <stdint.h> #include <string.h> +#include <stdbool.h>
#include <osmocom/core/bits.h> #include <osmocom/core/utils.h> @@ -36,24 +37,48 @@ static const uint8_t ul[] = { 0x80, 0xba, 0xab, 0xc0, 0x59, 0x26, 0x40, };
-static const char * -binstr(ubit_t *d, int n) +static inline bool print_a5(int n, int k, const char * dir, const ubit_t * out, const char * block) { - static char str[256]; - int i; + 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\nGOT: [%d] %s\nEXP: [%d] %s\n", k, osmo_hexdump_nospc(buf, len), k, osmo_hexdump_nospc(res, len)); + return false; + } + printf("OK\n"); + return true; +} + +static inline bool test_a53(const char * kc, uint32_t count, const char * block1, const char * block2) +{ + ubit_t dlout[114], ulout[114]; + uint8_t key[8]; + + osmo_hexparse(kc, key, 8); + _osmo_a5_3(key, count, dlout, NULL, false); + _osmo_a5_3(key, count, NULL, ulout, false);
- for (i=0; i<n; i++) - str[i] = d[i] ? '1' : '0'; + return print_a5(3, 8, "DL", dlout, block1) & print_a5(3, 8, "UL", ulout, block2); +} + +static inline bool test_a54(const char * kc, uint32_t count, const char * block1, const char * block2) +{ + ubit_t dlout[114], ulout[114]; + uint8_t key[16];
- str[i] = '\0'; + osmo_hexparse(kc, key, 16); + _osmo_a5_4(key, count, dlout, NULL, false); + _osmo_a5_4(key, count, NULL, ulout, false);
- return str; + return print_a5(4, 8, "DL", dlout, block1) & print_a5(4, 8, "UL", ulout, block2); }
+ int main(int argc, char **argv) { - ubit_t exp[114]; - ubit_t out[114]; + ubit_t exp[114], out[114]; int n, i;
for (n=0; n<3; n++) { @@ -66,13 +91,13 @@ int main(int argc, char **argv)
osmo_a5(n, key, fn, out, NULL);
- printf("A5/%d - DL: %s", n, binstr(out, 114)); + 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", binstr(out, 114)); + printf(" Expected: %s", osmo_ubit_dump(out, 114)); fprintf(stderr, "[!] A5/%d DL failed", n); exit(1); } @@ -82,17 +107,31 @@ int main(int argc, char **argv)
osmo_a5(n, key, fn, NULL, out);
- printf("A5/%d - UL: %s", n, binstr(out, 114)); + 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", binstr(out, 114)); + 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_a53("2BD6459F82C5BC00", 0x24F20F, "889EEAAF9ED1BA1ABBD8436232E440", "5CA3406AA244CF69CF047AADA2DF40"); + test_a53("952C49104881FF48", 0x061272, "FB4D5FBCEE13A33389285686E9A5C0", "25090378E0540457C57E367662E440"); + test_a53("EFA8B2229E720C2A", 0x33FD3F, "0E4015755A336469C3DD8680E30340", "6F10669E2B4E18B042431A28E47F80"); + test_a53("952C49104881FF48", 0x061527, "AB7DB38A573A325DAA76E4CB800A40", "4C4B594FEA9D00FE8978B7B7BC1080"); + test_a53("3451F23A43BD2C87", 0x0E418C, "75F7C4C51560905DFBA05E46FB54C0", "192C95353CDF979E054186DF15BF00"); + test_a53("CAA2639BE82435CF", 0x2FF229, "301437E4D4D6565D4904C631606EC0", "F0A3B8795E264D3E1A82F684353DC0"); + test_a53("7AE67E87400B9FA6", 0x2F24E5, "F794290FEF643D2EA348A7796A2100", "CB6FA6C6B8A705AF9FEFE975818500"); + test_a53("58AF69935540698B", 0x05446B, "749CA4E6B691E5A598C461D5FE4740", "31C9E444CD04677ADAA8A082ADBC40"); + test_a53("017F81E5F236FE62", 0x156B26, "2A6976761E60CC4E8F9F52160276C0", "A544D8475F2C78C35614128F1179C0"); + test_a53("1ACA8B448B767B39", 0x0BC3B5, "A4F70DC5A2C9707F5FA1C60EB10640", "7780B597B328C1400B5C74823E8500"); + test_a54("3D43C388C9581E337FF1F97EB5C1F85E", 0x35D2CF, "A2FE3034B6B22CC4E33C7090BEC340", "170D7497432FF897B91BE8AECBA880"); + test_a54("A4496A64DF4F399F3B4506814A3E07A1", 0x212777, "89CDEE360DF9110281BCF57755A040", "33822C0C779598C9CBFC49183AF7C0"); + return 0; } diff --git a/tests/a5/a5_test.ok b/tests/a5/a5_test.ok index 4497e14..cefcdb6 100644 --- a/tests/a5/a5_test.ok +++ b/tests/a5/a5_test.ok @@ -4,3 +4,27 @@ A5/1 - DL: 110010111010001001010101011101100001011101011101001110110001110001111 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
Hi,
/* Notes:
* - key must be 8 bytes long (or NULL for A5/0)
* - key must be 8/16 bytes long (or NULL for A5/0) depending on algorithm variant * - 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)
* - fn is the _real_ GSM frame number (unless fn_correct is false),
* converted internally to fn_count
* - only top-level osmo_a5 should be considered as part of public API */
int 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, bool fn_correct); +void _osmo_a5_4(const uint8_t *key, uint32_t fn, ubit_t *dl, ubit_t *ul, bool fn_correct);
Why the _ prefix ? It's exported and in the header and none of the other have it. Unnecessary inconsistency.
Same for fn_correct, none of the other have it. The only use for it seem to be for the test ... but then the correct way is to make the test compute the real fn from the fn_count before the call, not pollute the API with a useless parameter.
a5_a5_test_SOURCES = a5/a5_test.c a5_a5_test_LDADD = $(top_builddir)/src/libosmocore.la $(top_builddir)/src/gsm/libosmogsm.la +a5_a5_test_LDFLAGS = -static
Why is that needed at all ?
If the functions are in the installed header they should be in the .map.
If you don't want the other osmo_a5_x function as part of the API then first remove them from the header (they shouldn't be used anywhere anyway) and from the map and make them static. And then don't add the 3/4 to the .h file either and make the test go through the osmo_a5 function.
Cheers,
Sylvain
16.06.2014 23:24, Sylvain Munaut пишет:
Why the _ prefix ? It's exported and in the header and none of the other have it. Unnecessary inconsistency.
Agree but I would rather hide implementation details of all osmo_a5_* from public API completely into separate non-installable header. Generic osmo_a5 should be fine for all use cases. Are there any reasons we expose those?
Signed-off-by: Max Suraev Max.Suraev@fairwaves.co --- include/Makefile.am | 3 +- include/osmocom/gsm/a34.h | 45 ++++++++++++++++++++++++++++++ src/gsm/a5.c | 70 +++++++++++++++++++++++++++++++++++++++++++++-- tests/Makefile.am | 1 + tests/a5/a5_test.c | 68 +++++++++++++++++++++++++++++++++++---------- tests/a5/a5_test.ok | 24 ++++++++++++++++ 6 files changed, 194 insertions(+), 17 deletions(-) create mode 100644 include/osmocom/gsm/a34.h
diff --git a/include/Makefile.am b/include/Makefile.am index 74396de..f7d77c0 100644 --- a/include/Makefile.am +++ b/include/Makefile.am @@ -110,7 +110,8 @@ endif
noinst_HEADERS = \ osmocom/core/timer_compat.h \ - osmocom/gsm/kasumi.h + osmocom/gsm/kasumi.h \ + osmocom/gsm/a34.h
osmocom/core/bit%gen.h: osmocom/core/bitXXgen.h.tpl $(AM_V_GEN)$(MKDIR_P) $(dir $@) diff --git a/include/osmocom/gsm/a34.h b/include/osmocom/gsm/a34.h new file mode 100644 index 0000000..39e32db --- /dev/null +++ b/include/osmocom/gsm/a34.h @@ -0,0 +1,45 @@ +/* + * a34.h + * + * Copyright (C) 2014 Max Suraev Max.Suraev@fairwaves.co + * + * 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. + */ + +#pragma once + +#include <stdint.h> +#include <stdbool.h> + +/*! \defgroup a5 GSM A5/3-4 ciphering algorithms + * @{ + */ + +/*! \file gsm/a34.h + * \brief Osmocom implementation of GSM A5/3 and A5/4 ciphering algorithms + */ + + /* Notes: + * - key must be 8/16 bytes long depending on algorithm variant + * - the dl and ul pointer must be either NULL or 114 bits long + * - fn is the _real_ GSM frame number (unless fn_correct is false), + * converted internally to fn_count + */ +void _a5_3(const uint8_t *key, uint32_t fn, ubit_t *dl, ubit_t *ul, bool fn_correct); +void _a5_4(const uint8_t *key, uint32_t fn, ubit_t *dl, ubit_t *ul, bool fn_correct); + +/*! @} */ diff --git a/src/gsm/a5.c b/src/gsm/a5.c index de821e8..fc5e64f 100644 --- a/src/gsm/a5.c +++ b/src/gsm/a5.c @@ -36,8 +36,11 @@
#include <errno.h> #include <string.h> +#include <stdbool.h>
#include <osmocom/gsm/a5.h> +#include <osmocom/gsm/a34.h> +#include <osmocom/gsm/kasumi.h>
/*! \brief Main method to generate a A5/x cipher stream * \param[in] n Which A5/x method to use @@ -47,7 +50,7 @@ * \param[out] ul Pointer to array of ubits to return Uplink cipher stream * \returns 0 for success, -ENOTSUP for invalid cipher selection. * - * Currently A5/[0-2] are supported. + * Currently A5/[0-4] are supported. * Either (or both) of dl/ul can be NULL if not needed. */ int @@ -70,8 +73,16 @@ osmo_a5(int n, const uint8_t *key, uint32_t fn, ubit_t *dl, ubit_t *ul) osmo_a5_2(key, fn, dl, ul); break;
+ case 3: + _a5_3(key, fn, dl, ul, true); + break; + + case 4: + _a5_4(key, fn, dl, ul, true); + break; + default: - /* a5/[3..7] not supported here/yet */ + /* a5/[5..7] not supported here/yet */ return -ENOTSUP; }
@@ -368,4 +379,59 @@ osmo_a5_2(const uint8_t *key, uint32_t fn, ubit_t *dl, ubit_t *ul) } }
+/* ------------------------------------------------------------------------ */ +/* A5/3&4 */ +/* ------------------------------------------------------------------------ */ + +/*! \brief Generate a GSM A5/4 cipher stream + * \param[in] key 16 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 + * \param[in] fn_correct true if fn is a real GSM frame number and thus requires internal conversion + * + * 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 +_a5_4(const uint8_t *ck, uint32_t fn, ubit_t *dl, ubit_t *ul, bool fn_correct) +{ + uint8_t i, gamma[32], uplink[15]; + uint32_t fn_count = (fn_correct) ? osmo_a5_fn_count(fn) : fn; + + if (ul) { + _kasumi_kgcore(0xF, 0, fn_count, 0, ck, gamma, 228); + 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_count, 0, ck, gamma, 114); + osmo_pbit2ubit(dl, gamma, 114); + } +} + +/*! \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 + * \param[in] fn_correct true if fn is a real GSM frame number and thus requires internal conversion + * + * 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 +_a5_3(const uint8_t *key, uint32_t fn, ubit_t *dl, ubit_t *ul, bool fn_correct) +{ + uint8_t ck[16]; + memcpy(ck, key, 8); + memcpy(ck + 8, key, 8); + /* internal function require 128 bit key so we expand by concatenating supplied 64 bit key */ + _a5_4(ck, fn, dl, ul, fn_correct); +} + /*! @} */ diff --git a/tests/Makefile.am b/tests/Makefile.am index ea4bf9c..baf11a2 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -18,6 +18,7 @@ utils_utils_test_LDADD = $(top_builddir)/src/libosmocore.la
a5_a5_test_SOURCES = a5/a5_test.c a5_a5_test_LDADD = $(top_builddir)/src/libosmocore.la $(top_builddir)/src/gsm/libosmogsm.la +a5_a5_test_LDFLAGS = -static
kasumi_kasumi_test_SOURCES = kasumi/kasumi_test.c kasumi_kasumi_test_LDADD = $(top_builddir)/src/libosmocore.la $(top_builddir)/src/gsm/libosmogsm.la diff --git a/tests/a5/a5_test.c b/tests/a5/a5_test.c index 14436f1..a40489e 100644 --- a/tests/a5/a5_test.c +++ b/tests/a5/a5_test.c @@ -2,10 +2,12 @@ #include <stdlib.h> #include <stdint.h> #include <string.h> +#include <stdbool.h>
#include <osmocom/core/bits.h> #include <osmocom/core/utils.h> #include <osmocom/gsm/a5.h> +#include <osmocom/gsm/a34.h>
static const uint8_t key[] = { 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef }; static const uint32_t fn = 123456; @@ -36,24 +38,48 @@ static const uint8_t ul[] = { 0x80, 0xba, 0xab, 0xc0, 0x59, 0x26, 0x40, };
-static const char * -binstr(ubit_t *d, int n) +static inline bool print_a5(int n, int k, const char * dir, const ubit_t * out, const char * block) { - static char str[256]; - int i; + 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\nGOT: [%d] %s\nEXP: [%d] %s\n", k, osmo_hexdump_nospc(buf, len), k, osmo_hexdump_nospc(res, len)); + return false; + } + printf("OK\n"); + return true; +} + +static inline bool test_a53(const char * kc, uint32_t count, const char * block1, const char * block2) +{ + ubit_t dlout[114], ulout[114]; + uint8_t key[8]; + + osmo_hexparse(kc, key, 8); + _a5_3(key, count, dlout, NULL, false); + _a5_3(key, count, NULL, ulout, false);
- for (i=0; i<n; i++) - str[i] = d[i] ? '1' : '0'; + return print_a5(3, 8, "DL", dlout, block1) & print_a5(3, 8, "UL", ulout, block2); +} + +static inline bool test_a54(const char * kc, uint32_t count, const char * block1, const char * block2) +{ + ubit_t dlout[114], ulout[114]; + uint8_t key[16];
- str[i] = '\0'; + osmo_hexparse(kc, key, 16); + _a5_4(key, count, dlout, NULL, false); + _a5_4(key, count, NULL, ulout, false);
- return str; + return print_a5(4, 8, "DL", dlout, block1) & print_a5(4, 8, "UL", ulout, block2); }
+ int main(int argc, char **argv) { - ubit_t exp[114]; - ubit_t out[114]; + ubit_t exp[114], out[114]; int n, i;
for (n=0; n<3; n++) { @@ -66,13 +92,13 @@ int main(int argc, char **argv)
osmo_a5(n, key, fn, out, NULL);
- printf("A5/%d - DL: %s", n, binstr(out, 114)); + 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", binstr(out, 114)); + printf(" Expected: %s", osmo_ubit_dump(out, 114)); fprintf(stderr, "[!] A5/%d DL failed", n); exit(1); } @@ -82,17 +108,31 @@ int main(int argc, char **argv)
osmo_a5(n, key, fn, NULL, out);
- printf("A5/%d - UL: %s", n, binstr(out, 114)); + 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", binstr(out, 114)); + 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_a53("2BD6459F82C5BC00", 0x24F20F, "889EEAAF9ED1BA1ABBD8436232E440", "5CA3406AA244CF69CF047AADA2DF40"); + test_a53("952C49104881FF48", 0x061272, "FB4D5FBCEE13A33389285686E9A5C0", "25090378E0540457C57E367662E440"); + test_a53("EFA8B2229E720C2A", 0x33FD3F, "0E4015755A336469C3DD8680E30340", "6F10669E2B4E18B042431A28E47F80"); + test_a53("952C49104881FF48", 0x061527, "AB7DB38A573A325DAA76E4CB800A40", "4C4B594FEA9D00FE8978B7B7BC1080"); + test_a53("3451F23A43BD2C87", 0x0E418C, "75F7C4C51560905DFBA05E46FB54C0", "192C95353CDF979E054186DF15BF00"); + test_a53("CAA2639BE82435CF", 0x2FF229, "301437E4D4D6565D4904C631606EC0", "F0A3B8795E264D3E1A82F684353DC0"); + test_a53("7AE67E87400B9FA6", 0x2F24E5, "F794290FEF643D2EA348A7796A2100", "CB6FA6C6B8A705AF9FEFE975818500"); + test_a53("58AF69935540698B", 0x05446B, "749CA4E6B691E5A598C461D5FE4740", "31C9E444CD04677ADAA8A082ADBC40"); + test_a53("017F81E5F236FE62", 0x156B26, "2A6976761E60CC4E8F9F52160276C0", "A544D8475F2C78C35614128F1179C0"); + test_a53("1ACA8B448B767B39", 0x0BC3B5, "A4F70DC5A2C9707F5FA1C60EB10640", "7780B597B328C1400B5C74823E8500"); + test_a54("3D43C388C9581E337FF1F97EB5C1F85E", 0x35D2CF, "A2FE3034B6B22CC4E33C7090BEC340", "170D7497432FF897B91BE8AECBA880"); + test_a54("A4496A64DF4F399F3B4506814A3E07A1", 0x212777, "89CDEE360DF9110281BCF57755A040", "33822C0C779598C9CBFC49183AF7C0"); + return 0; } diff --git a/tests/a5/a5_test.ok b/tests/a5/a5_test.ok index 4497e14..cefcdb6 100644 --- a/tests/a5/a5_test.ok +++ b/tests/a5/a5_test.ok @@ -4,3 +4,27 @@ A5/1 - DL: 110010111010001001010101011101100001011101011101001110110001110001111 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
This version avoids polluting external API with implementation details by introducing separate noinst header.
As I stated in the previous comment, I don't even see why you need that header. Just make your test go through the public API and test the whole chain. It gets rid of the header, the need to static link, that extraneous param and only add one new inline function in the test ...
On Tue, Jun 17, 2014 at 5:04 PM, ☎ Max.Suraev@fairwaves.co wrote:
This version avoids polluting external API with implementation details by introducing separate noinst header.
-- best regards, Max, http://fairwaves.co
17.06.2014 17:21, Sylvain Munaut пишет:
As I stated in the previous comment, I don't even see why you need that header. Just make your test go through the public API and test the whole chain. It gets rid of the header, the need to static link, that extraneous param and only add one new inline function in the test
Well, that's a function which will only be used once - and it got to be written, debugged and tested. And all that for the sake of? I do not consider extra non-public header to be of such great burden that it justifies extra work necessary to get rid of it.
On Tue, Jun 17, 2014 at 5:30 PM, ☎ Max.Suraev@fairwaves.co wrote:
17.06.2014 17:21, Sylvain Munaut пишет:
As I stated in the previous comment, I don't even see why you need that header. Just make your test go through the public API and test the whole chain. It gets rid of the header, the need to static link, that extraneous param and only add one new inline function in the test
Well, that's a function which will only be used once - and it got to be written, debugged and tested.
static inline uint32_t osmo_a5_fn(uint32_t fn_count) { int t1 = fn_count >> 11; int t2 = fn_count & 0x1f; int t3 = (fn_count >> 5) & 0x3f; return (t1 * 26 * 51) + ((t3 - t2 + 26) % 26) * 51 + t3; }
There, it's written. No need to debug it, it's correct. No need to test it, it's going to be in the test path of the A5/[3,4] test and tested automatically as part of them.
And all that for the sake of? I do not consider extra non-public header to be of such great burden that it justifies extra work necessary to get rid of it.
Writing it in the first place was more typing that this function.
Also there is an issue in it anyway, you used the Doxygen defgroup which is to define new groups while what you want is include it in an existing group.
Cheers,
Sylvain
Also forgot to say that the 'Notes:' in the public header should be updated to reflect the key length. So should be the osmo_a5 doxygen doc for the 'key' parameter.
Cheers,
Sylvain
17.06.2014 20:05, Sylvain Munaut пишет:
static inline uint32_t osmo_a5_fn(uint32_t fn_count) { int t1 = fn_count >> 11; int t2 = fn_count & 0x1f; int t3 = (fn_count >> 5) & 0x3f; return (t1 * 26 * 51) + ((t3 - t2 + 26) % 26) * 51 + t3; }
There, it's written. No need to debug it, it's correct. No need to test it, it's going to be in the test path of the A5/[3,4] test and tested automatically as part of them.
Surprisingly those tests fail for some of the test vectors for me. How exactly you've applied and tested this function? Could you share entire a5_test.c?
#include <stdio.h> #include <stdint.h>
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; }
static inline uint32_t osmo_a5_fn(uint32_t fn_count) { int t1 = fn_count >> 11; int t2 = fn_count & 0x1f; int t3 = (fn_count >> 5) & 0x3f; return (t1 * 26 * 51) + ((t3 - t2 + 26) % 26) * 51 + t3; }
#define FN_MAX 26*51*2048
int main(int argc, char *argv[]) { int i; for (i=0; i<FN_MAX; i++) if (i != osmo_a5_fn(osmo_a5_fn_count(i))) printf("%d\n", i);
}
Bunch of fucking idiots who chose as test data completely invalid FN count values that can't possibly happen in a GSM system ...
- Either just use different test vector you generate - Or use a marker bit like (1<<31) to indicate to _a5_{3,4} not to convert the values for testing
On Tue, Jun 17, 2014 at 9:35 PM, Sylvain Munaut 246tnt@gmail.com wrote:
#include <stdio.h> #include <stdint.h>
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; }
static inline uint32_t osmo_a5_fn(uint32_t fn_count) { int t1 = fn_count >> 11; int t2 = fn_count & 0x1f; int t3 = (fn_count >> 5) & 0x3f; return (t1 * 26 * 51) + ((t3 - t2 + 26) % 26) * 51 + t3; }
#define FN_MAX 26*51*2048
int main(int argc, char *argv[]) { int i; for (i=0; i<FN_MAX; i++) if (i != osmo_a5_fn(osmo_a5_fn_count(i))) printf("%d\n", i);
}
17.06.2014 21:46, Sylvain Munaut пишет:
Bunch of fucking idiots who chose as test data completely invalid FN count values that can't possibly happen in a GSM system ...
I presume the idea was not to test gsm use cases but to cover all the internal states of the cipher.
- Either just use different test vector you generate
I would rather stick to official test vectors published in standard.
- Or use a marker bit like (1<<31) to indicate to _a5_{3,4} not to
convert the values for testing
I really like my initial proposal: - it works already - it's simpler
Could you emphasize - why exactly introducing private header is such a disaster that we have to waste time and efforts trying to not let it happen?
Could you emphasize - why exactly introducing private header is such a disaster that we have to waste time and efforts trying to not let it happen?
- It's a useless file that's used at exactly 1 place and not even for code used in installed libs but just for the test. At that point you might just as well just pre-declare them in the test source file directly. - It prevents making those function static - It requires static linking for the test - For the A5/[3/4] you bypass the osmo_a5 wrapper and thus don't cover a breakage that would happen there.
The alternative solves all of the above and requires exactly 1 lines changes in the original _a5_4 function :
uint32_t fn_count = (fn_correct) ? osmo_a5_fn_count(fn) : fn;
becomes
uint32_t fn_count = (fn & (1<<31)) ? (fn & ~(1<<31)) : osmo_a5_fn_count(fn);
btw, in your test I also see
+ osmo_a5(4, key, osmo_a5_fn(count), dlout, NULL); + osmo_a5(4, key, osmo_a5_fn(count), NULL, ulout);
Why separate ? The case where both dlout and ulout are requested at the same time should work as well.
Cheers,
Sylvain
18.06.2014 00:15, Sylvain Munaut пишет:
Could you emphasize - why exactly introducing private header is such a disaster that we have to waste time and efforts trying to not let it happen?
- It's a useless file that's used at exactly 1 place and not even for
code used in installed libs but just for the test. At that point you might just as well just pre-declare them in the test source file directly.
- It prevents making those function static
- It requires static linking for the test
- For the A5/[3/4] you bypass the osmo_a5 wrapper and thus don't cover
a breakage that would happen there.
The alternative solves all of the above and requires exactly 1 lines changes in the original _a5_4 function :
uint32_t fn_count = (fn_correct) ? osmo_a5_fn_count(fn) : fn;
becomes
uint32_t fn_count = (fn & (1<<31)) ? (fn & ~(1<<31)) : osmo_a5_fn_count(fn);
I probably apply it wrongly - test still fails for me. Feel free to commit your variant though - I think it's less readable but as long as it works and covered by test suit it's fine.
Seems nice but it case of a5 test vectors it still fails. Could you have a look into attached patch?
baseband-devel@lists.osmocom.org