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);
+}