From b8049f110d22ff14f26548cb9b96a37ebf5414a7 Mon Sep 17 00:00:00 2001 From: Romain Tartiere Date: Wed, 23 Jun 2010 02:03:13 +0000 Subject: [PATCH] Add support for TLV streams. - New API functions: tlv_encode(), tlv_decode(); - Documentation (man page); - Unit tests. --- libfreefare/Makefile.am | 10 ++- libfreefare/freefare.h | 4 ++ libfreefare/tlv.3 | 115 +++++++++++++++++++++++++++++++++++ libfreefare/tlv.c | 96 +++++++++++++++++++++++++++++ test/Makefile.am | 6 +- test/test_tlv.c | 131 ++++++++++++++++++++++++++++++++++++++++ 6 files changed, 358 insertions(+), 4 deletions(-) create mode 100644 libfreefare/tlv.3 create mode 100644 libfreefare/tlv.c create mode 100644 test/test_tlv.c diff --git a/libfreefare/Makefile.am b/libfreefare/Makefile.am index d3f5065..8745438 100644 --- a/libfreefare/Makefile.am +++ b/libfreefare/Makefile.am @@ -8,7 +8,8 @@ libfreefare_la_SOURCES = freefare.c \ mifare_classic.c \ mifare_ultralight.c \ mad.c \ - mifare_application.c + mifare_application.c \ + tlv.c libfreefare_la_HEADERS = freefare.h libfreefare_ladir = $(includedir) @@ -16,7 +17,8 @@ man_MANS = freefare.3 \ mifare_ultralight.3 \ mifare_classic.3 \ mad.3 \ - mifare_application.3 + mifare_application.3 \ + tlv.3 linkedman = \ freefare.3 freefare_get_tags.3 \ @@ -57,7 +59,9 @@ linkedman = \ mad.3 mad_free.3 \ mifare_application.3 mifare_application_alloc.3 \ mifare_application.3 mifare_application_free.3 \ - mifare_application.3 mifare_application_find.3 + mifare_application.3 mifare_application_find.3 \ + tlv.3 tlv_encode.3 \ + tlv.3 tlv_decode.3 install-data-hook: (cd $(DESTDIR)$(man3dir); for i in `echo $(linkedman) | xargs -n2 echo | awk '{print $$2}'`; do rm -f $$i; done; echo $(linkedman) | xargs -n2 $(LN_S)) diff --git a/libfreefare/freefare.h b/libfreefare/freefare.h index f9ef4bb..cdb30e4 100644 --- a/libfreefare/freefare.h +++ b/libfreefare/freefare.h @@ -141,6 +141,10 @@ void mifare_application_free (Mad mad, MadAid aid); MifareSectorNumber *mifare_application_find (Mad mad, MadAid aid); + +uint8_t *tlv_encode (const uint8_t type, const uint8_t *istream, uint16_t isize, size_t *osize); +uint8_t *tlv_decode (const uint8_t *istream, uint8_t *type, uint16_t *size); + #ifdef __cplusplus } #endif // __cplusplus diff --git a/libfreefare/tlv.3 b/libfreefare/tlv.3 new file mode 100644 index 0000000..c308ef6 --- /dev/null +++ b/libfreefare/tlv.3 @@ -0,0 +1,115 @@ +.\" Copyright (C) 2010 Romain Tartiere +.\" +.\" This program is free software: you can redistribute it and/or modify it +.\" under the terms of the GNU Lesser General Public License as published by the +.\" Free Software Foundation, either version 3 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 Lesser General Public License +.\" along with this program. If not, see +.\" +.\" $Id$ +.\" +.Dd March 30, 2010 +.Dt TLV 3 +.Os +.\" _ _ +.\" | \ | | __ _ _ __ ___ ___ +.\" | \| |/ _` | '_ ` _ \ / _ \ +.\" | |\ | (_| | | | | | | __/ +.\" |_| \_|\__,_|_| |_| |_|\___| +.\" +.Sh NAME +.Nm tlv_encode , +.Nm tlv_decode +.Nd TLV Manipulation Functions +.\" _ _ _ +.\" | | (_) |__ _ __ __ _ _ __ _ _ +.\" | | | | '_ \| '__/ _` | '__| | | | +.\" | |___| | |_) | | | (_| | | | |_| | +.\" |_____|_|_.__/|_| \__,_|_| \__, | +.\" |___/ +.Sh LIBRARY +Mifare card manipulation library (libfreefare, \-lfreefare) +.\" ____ _ +.\" / ___| _ _ _ __ ___ _ __ ___(_)___ +.\" \___ \| | | | '_ \ / _ \| '_ \/ __| / __| +.\" ___) | |_| | | | | (_) | |_) \__ \ \__ \ +.\" |____/ \__, |_| |_|\___/| .__/|___/_|___/ +.\" |___/ |_| +.Sh SYNOPSIS +.In freefare.h +.Ft "uint8_t *" +.Fn tlv_encode "const uint8_t type" "const uint8_t *istream" "uint16_t isize" "size_t *osize" +.Ft "uint8_t *" +.Fn tlv_decode "const uint8_t *istream" "const uint8_t *type" "uint16_t *size" +.\" ____ _ _ _ +.\" | _ \ ___ ___ ___ _ __(_)_ __ | |_(_) ___ _ __ +.\" | | | |/ _ \/ __|/ __| '__| | '_ \| __| |/ _ \| '_ \ +.\" | |_| | __/\__ \ (__| | | | |_) | |_| | (_) | | | | +.\" |____/ \___||___/\___|_| |_| .__/ \__|_|\___/|_| |_| +.\" |_| +.Sh DESCRIPTION +The +.Fn tlv_encode +and +.Fn tlv_decode +functions are helpers to manipulate TLV (Text-Length-Value) data. +.Pp +The +.Fn tlv_encode +function converts the +.Ar isize +bytes long +.Ar istream +message into a TLV stream of type +.Ar type +and set the value of +.Ar osize +to the length of the returned stream. +.Pp +The +.Fn tlv_decode +function converts the +.Ar istream +TLV stream and set the +.Ar type +argument according to the type of the stream, and set the +.Ar size +argument to the length of the returned stream. +.\" ____ _ _ +.\" | _ \ ___| |_ _ _ _ __ _ __ __ ____ _| |_ _ ___ ___ +.\" | |_) / _ \ __| | | | '__| '_ \ \ \ / / _` | | | | |/ _ \/ __| +.\" | _ < __/ |_| |_| | | | | | | \ V / (_| | | |_| | __/\__ \ +.\" |_| \_\___|\__|\__,_|_| |_| |_| \_/ \__,_|_|\__,_|\___||___/ +.\" +.Sh RETURN VALUES +Both functions return memory allocated using +.Xr malloc 3 +which should be reclaimed using +.Xr free 3 +after usage. +.\" ____ _ +.\" / ___| ___ ___ __ _| |___ ___ +.\" \___ \ / _ \/ _ \ / _` | / __|/ _ \ +.\" ___) | __/ __/ | (_| | \__ \ (_) | +.\" |____/ \___|\___| \__,_|_|___/\___/ +.\" +.Sh SEE ALSO +.Xr freefare 3 , +.Xr malloc 3 , +.Xr free 3 +.\" _ _ _ +.\" / \ _ _| |_| |__ ___ _ __ ___ +.\" / _ \| | | | __| '_ \ / _ \| '__/ __| +.\" / ___ \ |_| | |_| | | | (_) | | \__ \ +.\" /_/ \_\__,_|\__|_| |_|\___/|_| |___/ +.\" +.Sh AUTHORS +.An Romain Tartiere Aq romain@blogreen.org +.An Romuald Conty Aq rconty@il4p.fr diff --git a/libfreefare/tlv.c b/libfreefare/tlv.c new file mode 100644 index 0000000..e1162da --- /dev/null +++ b/libfreefare/tlv.c @@ -0,0 +1,96 @@ +/*- + * Copyright (C) 2010, Romain Tartiere, Romuald Conty. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 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 Lesser General Public License + * along with this program. If not, see + * + * $Id$ + */ + +#include "config.h" +#include +#include +#include +#include + +#include + +/* + * TLV (Type Length Value) Manipulation Functions. + */ + +/* + * Encode data stream into TLV. + */ +uint8_t * +tlv_encode (const uint8_t type, const uint8_t *istream, uint16_t isize, size_t *osize) +{ + uint8_t *res; + off_t n = 0; + + if (osize) + *osize = 0; + + if (isize == 0xffff) /* RFU */ + return NULL; + + if ((res = malloc (1 + ((isize > 254) ? 3 : 1) + isize))) { + res[n++] = type; + + if (isize > 254) { + res[n++] = 0xff; + uint16_t size_be = htobe16 (isize); + memcpy (res + n, &size_be, sizeof (uint16_t)); + n += 2; + } else { + res[n++] = (uint8_t)isize; + } + + memcpy (res + n, istream, isize); + if (osize) + *osize = isize + n; + } + return res; +} + +/* + * Decode TLV from data stream. + */ +uint8_t * +tlv_decode (const uint8_t *istream, uint8_t *type, uint16_t *size) +{ + size_t s; + off_t o = 1; + uint8_t *res = NULL; + + if (type) + *type = istream[0]; + + if (istream[1] == 0xff) { + uint16_t be_size; + memcpy (&be_size, istream + 2, sizeof (uint16_t)); + s = be16toh(be_size); + o += 3; + } else { + s = istream[1]; + o += 1; + } + if (size) { + *size = s; + } + + if ((res = malloc (s))) { + memcpy (res, istream + o, s); + } + return res; +} diff --git a/test/Makefile.am b/test/Makefile.am index a266390..02e04d3 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -16,7 +16,8 @@ noinst_LTLIBRARIES = \ test_mifare_classic.la \ test_mifare_classic_create_trailer_block.la \ test_mifare_classic_application.la \ - test_mifare_ultralight.la + test_mifare_ultralight.la \ + test_tlv.la AM_LDFLAGS = -module -rpath $(libdir) -avoid-version -no-undefined @@ -39,6 +40,9 @@ test_mifare_ultralight_la_SOURCES = test_mifare_ultralight.c \ mifare_ultralight_fixture.h test_mifare_ultralight_la_LIBADD = $(top_builddir)/libfreefare/libfreefare.la +test_tlv_la_SOURCES = test_tlv.c +test_tlv_la_LIBADD = $(top_builddir)/libfreefare/libfreefare.la + echo-cutter: @echo $(CUTTER) diff --git a/test/test_tlv.c b/test/test_tlv.c new file mode 100644 index 0000000..6cdd0b1 --- /dev/null +++ b/test/test_tlv.c @@ -0,0 +1,131 @@ +/*- + * Copyright (C) 2010, Romain Tartiere, Romuald Conty. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 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 Lesser General Public License + * along with this program. If not, see + * + * $Id$ + */ + +#include + +#include + +const uint8_t shortdata[] = "elephant"; /* read: "elephant\0" = 9 bytes! */ +const uint8_t eshortdata[] = "\x03" "\x09" "elephant"; + + /* + * Many thanks to Charles Baudelaire for helping me + * test things and helping you realize your f**king + * OS / compiler does not support UTF-8 ;-) + */ +const uint8_t longdata[] = "Dans une terre grasse et pleine d'escargots\n" + "Je veux creuser moi-même une fosse profonde,\n" + "Où je puisse à loisir étaler mes vieux os\n" + "Et dormir dans l'oubli comme un requin dans l'onde.\n" + "Je hais les testaments et je hais les tombeaux;\n" + "Plutôt que d'implorer une larme du monde,\n" + "Vivant, j'aimerais mieux inviter les corbeaux\n" + "À saigner tous les bouts de ma carcasse immonde.\n" + "Ô vers! noirs compagnons sans oreille et sans yeux,\n" + "Voyez venir à vous un mort libre et joyeux;\n" + "Philosophes viveurs, fils de la pourriture,\n" + "À travers ma ruine allez donc sans remords,\n" + "Et dites-moi s'il est encor quelque torture\n" + "Pour ce vieux corps sans âme et mort parmi les morts!\n"; + +const uint8_t elongdata[] = "\x07" "\xff\x02\x95" + "Dans une terre grasse et pleine d'escargots\n" + "Je veux creuser moi-même une fosse profonde,\n" + "Où je puisse à loisir étaler mes vieux os\n" + "Et dormir dans l'oubli comme un requin dans l'onde.\n" + "Je hais les testaments et je hais les tombeaux;\n" + "Plutôt que d'implorer une larme du monde,\n" + "Vivant, j'aimerais mieux inviter les corbeaux\n" + "À saigner tous les bouts de ma carcasse immonde.\n" + "Ô vers! noirs compagnons sans oreille et sans yeux,\n" + "Voyez venir à vous un mort libre et joyeux;\n" + "Philosophes viveurs, fils de la pourriture,\n" + "À travers ma ruine allez donc sans remords,\n" + "Et dites-moi s'il est encor quelque torture\n" + "Pour ce vieux corps sans âme et mort parmi les morts!\n"; + +void +test_tlv_encode_short (void) +{ + uint8_t *res; + size_t osize; + + res = tlv_encode (3, shortdata, sizeof (shortdata), &osize); + cut_assert_equal_int (11, osize, cut_message ("Wrong encoded message length.")); + cut_assert_equal_int (3, res[0], cut_message ("Wrong type")); + cut_assert_equal_int (9, res[1], cut_message ("Wrong value length")); + cut_assert_equal_memory (res, osize, eshortdata, sizeof (eshortdata), cut_message ("Wrong encoded value")); + free (res); +} + +void +test_tlv_encode_long (void) +{ + uint8_t *res; + size_t osize; + + res = tlv_encode (7, longdata, sizeof (longdata), &osize); + cut_assert_equal_int (665, osize, cut_message ("Wrong encoded message length.")); + cut_assert_equal_int (7, res[0], cut_message ("Wrong type")); + cut_assert_equal_int (0xff, res[1], cut_message ("Wrong value length")); + cut_assert_equal_int (0x02, res[2], cut_message ("Wrong value length")); + cut_assert_equal_int (0x95, res[3], cut_message ("Wrong value length")); + cut_assert_equal_memory (res, osize, elongdata, sizeof (elongdata), cut_message ("Wrong encoded value")); + free (res); +} + +void +test_tlv_decode_short (void) +{ + uint8_t *res; + uint16_t size; + uint8_t type; + + res = tlv_decode (eshortdata, &type, &size); + cut_assert_equal_int (3, type, cut_message ("Wrong type")); + cut_assert_equal_int (9, size, cut_message ("Wrong value length")); + cut_assert_equal_memory (res, size, shortdata, sizeof (shortdata), cut_message ("Wrong decoded value")); + free (res); +} + +void +test_tlv_decode_long (void) +{ + uint8_t *res; + uint16_t size; + uint8_t type; + + res = tlv_decode (elongdata, &type, &size); + cut_assert_equal_int (7, type, cut_message ("Wrong type")); + cut_assert_equal_int (0x295, size, cut_message ("Wrong value length")); + cut_assert_equal_memory (res, size, longdata, sizeof (longdata), cut_message ("Wrong decoded value")); + free (res); +} + +void +test_tlv_rfu (void) +{ + uint8_t *data = malloc (0xffff); + cut_assert_not_null (data, cut_message ("Out of memory")); + + uint8_t *res = tlv_encode (7, data, 0xffff, NULL); + cut_assert_null (res, cut_message ("Size reserved for future use")); + + free (data); +}