[Spice-devel] [PATCH libcacard 04/45] Basic SimpleTLV encoding and decoding methods
Jakub Jelen
jjelen at redhat.com
Tue Jul 31 14:49:58 UTC 2018
* The source code is originally based on the OpenSC cac card driver
* The SimpleTLV encoding is used in various places of the CAC
cards to encode most of buffers in the card
* The implementation is extended of structures representing the
SimpleTLV objects, that can be either static or dynamically
allocated. The dynamic one need to be recursivelly freed.
* Dynamic structures can be created by mergig other provided structures,
which is common in ACA, where all the responses are prefixed with
applet information.
Signed-off-by: Jakub Jelen <jjelen at redhat.com>
Reviewed-by: Robert Relyea <rrelyea at redhat.com>
---
Makefile.am | 4 +
docs/libcacard.txt | 4 +
src/common.c | 49 ++++++++
src/common.h | 30 +++++
src/simpletlv.c | 272 +++++++++++++++++++++++++++++++++++++++++++++
src/simpletlv.h | 117 +++++++++++++++++++
6 files changed, 476 insertions(+)
create mode 100644 src/common.c
create mode 100644 src/common.h
create mode 100644 src/simpletlv.c
create mode 100644 src/simpletlv.h
diff --git a/Makefile.am b/Makefile.am
index eaae2c5..68aa16e 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -15,6 +15,8 @@ libcacard_la_SOURCES = \
src/vcardt.c \
src/vcardt_internal.h \
src/vreader.c \
+ src/common.c \
+ src/simpletlv.c \
$(NULL)
if ENABLE_PCSC
@@ -36,6 +38,8 @@ libcacard_include_HEADERS = \
src/vreader.h \
src/vreadert.h \
src/vscard_common.h \
+ src/common.h \
+ src/simpletlv.h \
$(NULL)
libcacard_la_LIBADD = $(CACARD_LIBS) $(PCSC_LIBS)
diff --git a/docs/libcacard.txt b/docs/libcacard.txt
index f7d812c..650102b 100644
--- a/docs/libcacard.txt
+++ b/docs/libcacard.txt
@@ -480,5 +480,9 @@ src/vcard_emul_nss.c - virtual card emulator implementation for nss.
src/vscclient.c - socket connection to guest qemu usb driver.
src/vscard_common.h - common header with the guest qemu usb driver.
src/mutex.h - header file for machine independent mutexes.
+src/common.c - Utilities functions
+src/common.h - header file utilities functions
+src/simpletlv.c - Simple TLV encoding functions
+src/simpletlv.h - header file for Simple TLV encoding helpers
tests/libcacard.c - Test for the whole smart card emulation
diff --git a/src/common.c b/src/common.c
new file mode 100644
index 0000000..521ef51
--- /dev/null
+++ b/src/common.c
@@ -0,0 +1,49 @@
+/*
+ * common.c: Utility functions for libcacard
+ *
+ * Copyright (C) 2016 - 2018 Red Hat, Inc.
+ *
+ * Authors: Robert Relyea <rrelyea at redhat.com>
+ * Jakub Jelen <jjelen at redhat.com>
+ *
+ * This library 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 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#if HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stddef.h>
+
+#include "common.h"
+
+unsigned char *
+ushort2lebytes(unsigned char *buf, unsigned short x)
+{
+ if (buf != NULL) {
+ buf[0] = (unsigned char) (x & 0xff);
+ buf[1] = (unsigned char) ((x >> 8) & 0xff);
+ }
+ return buf;
+}
+
+unsigned short
+lebytes2ushort(const unsigned char *buf)
+{
+ if (buf == NULL)
+ return 0U;
+ return (unsigned short)buf[1] << 8 | (unsigned short)buf[0];
+}
+/* vim: set ts=4 sw=4 tw=0 noet expandtab: */
diff --git a/src/common.h b/src/common.h
new file mode 100644
index 0000000..83c8f33
--- /dev/null
+++ b/src/common.h
@@ -0,0 +1,30 @@
+/*
+ * common.h: Utility functions for libcacard
+ *
+ * Copyright (C) 2016 - 2018 Red Hat, Inc.
+ *
+ * Authors: Robert Relyea <rrelyea at redhat.com>
+ * Jakub Jelen <jjelen at redhat.com>
+ *
+ * This library 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 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _COMMON_H
+#define _COMMON_H
+
+unsigned char *ushort2lebytes(unsigned char *buf, unsigned short x);
+unsigned short lebytes2ushort(const unsigned char *buf);
+
+#endif
diff --git a/src/simpletlv.c b/src/simpletlv.c
new file mode 100644
index 0000000..0a20056
--- /dev/null
+++ b/src/simpletlv.c
@@ -0,0 +1,272 @@
+/*
+ * simpletlv.c: Simple TLV encoding and decoding functions
+ *
+ * Copyright (C) 2016 - 2018 Red Hat, Inc.
+ *
+ * Authors: Robert Relyea <rrelyea at redhat.com>
+ * Jakub Jelen <jjelen at redhat.com>
+ *
+ * This library 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 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#if HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <assert.h>
+#include <stdlib.h>
+
+#include "simpletlv.h"
+#include "common.h"
+
+int
+simpletlv_get_length(struct simpletlv_member *tlv, size_t tlv_len,
+ enum simpletlv_buffer_type buffer_type)
+{
+ size_t i, len = 0;
+ int child_length;
+
+ for (i = 0; i < tlv_len; i++) {
+ /* We can not unambiguously split the buffers
+ * for recursive structures
+ */
+ if (tlv[i].type != SIMPLETLV_TYPE_LEAF
+ && buffer_type != SIMPLETLV_BOTH)
+ return -1;
+
+ child_length = tlv[i].length;
+ if (tlv[i].type == SIMPLETLV_TYPE_COMPOUND) {
+ child_length = simpletlv_get_length(tlv[i].value.child,
+ tlv[i].length, SIMPLETLV_BOTH);
+ }
+ if (buffer_type & SIMPLETLV_TL) {
+ len += 1/*TAG*/;
+ if (child_length < 255)
+ len += 1;
+ else
+ len += 3;
+ }
+ if (buffer_type & SIMPLETLV_VALUE) {
+ len += child_length;
+ }
+ }
+ return len;
+}
+
+static int
+simpletlv_encode_internal(struct simpletlv_member *tlv, size_t tlv_len,
+ unsigned char **out, size_t outlen,
+ unsigned char **newptr, int buffer_type)
+{
+ unsigned char *tmp = NULL, *a = NULL, *p, *newp;
+ size_t tmp_len = 0, p_len, i;
+ int expect_len = 0, rv;
+
+ expect_len = simpletlv_get_length(tlv, tlv_len, buffer_type);
+ if (expect_len <= 0)
+ return expect_len;
+
+ if (outlen == 0) {
+ /* allocate a new buffer */
+ a = malloc(expect_len);
+ if (a == NULL) {
+ return -1;
+ }
+ tmp = a;
+ tmp_len = expect_len;
+ } else if ((int)outlen >= expect_len) {
+ tmp = *out;
+ tmp_len = outlen;
+ } else {
+ /* we can not fit the data */
+ return -1;
+ }
+ p = tmp;
+ p_len = tmp_len;
+ for (i = 0; i < tlv_len; i++) {
+ size_t child_length = tlv[i].length;
+ if (tlv[i].type == SIMPLETLV_TYPE_COMPOUND) {
+ child_length = simpletlv_get_length(tlv[i].value.child,
+ tlv[i].length, SIMPLETLV_BOTH);
+ }
+ if (buffer_type & SIMPLETLV_TL) {
+ rv = simpletlv_put_tag(tlv[i].tag, child_length,
+ p, p_len, &newp);
+ if (rv < 0)
+ goto failure;
+ p = newp;
+ }
+ if (buffer_type & SIMPLETLV_VALUE) {
+ if (tlv[i].type == SIMPLETLV_TYPE_LEAF) {
+ memcpy(p, tlv[i].value.value, tlv[i].length);
+ p += tlv[i].length;
+ } else {
+ /* recurse */
+ rv = simpletlv_encode_internal(tlv[i].value.child,
+ tlv[i].length, &p, p_len, &newp, buffer_type);
+ if (rv < 0)
+ goto failure;
+ p = newp;
+ }
+ }
+ p_len = tmp_len - (p - tmp);
+ }
+ if (newptr)
+ *newptr = p;
+ if (out)
+ *out = tmp;
+ return tmp_len - p_len;
+
+failure:
+ free(a);
+ return -1;
+}
+
+int
+simpletlv_encode(struct simpletlv_member *tlv, size_t tlv_len,
+ unsigned char **out, size_t outlen, unsigned char **newptr)
+{
+ return simpletlv_encode_internal(tlv, tlv_len, out, outlen, newptr,
+ SIMPLETLV_BOTH);
+}
+
+int
+simpletlv_encode_tl(struct simpletlv_member *tlv, size_t tlv_len,
+ unsigned char **out, size_t outlen, unsigned char **newptr)
+{
+ return simpletlv_encode_internal(tlv, tlv_len, out, outlen, newptr,
+ SIMPLETLV_TL);
+}
+
+int
+simpletlv_encode_val(struct simpletlv_member *tlv, size_t tlv_len,
+ unsigned char **out, size_t outlen, unsigned char **newptr)
+{
+ return simpletlv_encode_internal(tlv, tlv_len, out, outlen, newptr,
+ SIMPLETLV_VALUE);
+}
+
+
+/*
+ * Put a tag/length record to a file in Simple TLV based on the datalen
+ * content length.
+ */
+int
+simpletlv_put_tag(unsigned char tag, size_t datalen, unsigned char *out,
+ size_t outlen, unsigned char **ptr)
+{
+ unsigned char *p = out;
+
+ if (outlen < 2 || (outlen < 4 && datalen >= 0xff))
+ return -1;
+
+ /* tag is just number between 0x01 and 0xFE */
+ if (tag == 0x00 || tag == 0xff)
+ return -1;
+
+ *p++ = tag; /* tag is single byte */
+ if (datalen < 0xff) {
+ /* short value up to 255 */
+ *p++ = (unsigned char)datalen; /* is in the second byte */
+ } else if (datalen < 0xffff) {
+ /* longer values up to 65535 */
+ *p++ = (unsigned char)0xff; /* first byte is 0xff */
+ *p++ = (unsigned char)datalen & 0xff;
+ *p++ = (unsigned char)(datalen >> 8) & 0xff; /* LE */
+ } else {
+ /* we can't store more than two bytes in Simple TLV */
+ return -1;
+ }
+ if (ptr != NULL)
+ *ptr = p;
+ return 0;
+}
+
+/* Read the TL file and return appropriate tag and the length of associated
+ * content.
+ */
+int
+simpletlv_read_tag(unsigned char **buf, size_t buflen, unsigned char *tag_out,
+ size_t *taglen)
+{
+ size_t len;
+ unsigned char *p = *buf;
+
+ if (buflen < 2) {
+ *buf = p+buflen;
+ return -1;
+ }
+
+ *tag_out = *p++;
+ len = *p++;
+ if (len == 0xff) {
+ /* don't crash on bad data */
+ if (buflen < 4) {
+ *taglen = 0;
+ return -1;
+ }
+ /* skip two bytes (the size) */
+ len = lebytes2ushort(p);
+ p+=2;
+ }
+ *taglen = len;
+ *buf = p;
+ return 0;
+}
+
+/*
+ * Merges two structures into one, creating a new shallow copy of both
+ * of the structures.
+ * Resulting length is the sum of a_len and b_len arguemnts.
+ */
+struct simpletlv_member *
+simpletlv_merge(const struct simpletlv_member *a, size_t a_len,
+ const struct simpletlv_member *b, size_t b_len)
+{
+ int offset;
+ struct simpletlv_member *r;
+ size_t r_len = a_len + b_len;
+
+ r = malloc(r_len * sizeof(struct simpletlv_member));
+ if (r == NULL)
+ return NULL;
+
+ /* the uggly way */
+ offset = a_len * sizeof(struct simpletlv_member);
+ memcpy(r, a, offset);
+ memcpy(&r[a_len], b, b_len * sizeof(struct simpletlv_member));
+ return r;
+}
+
+void
+simpletlv_free(struct simpletlv_member *tlv, size_t tlvlen)
+{
+ size_t i;
+ if (tlv == NULL)
+ return;
+
+ for (i = 0; i < tlvlen; i++) {
+ if (tlv[i].type == SIMPLETLV_TYPE_COMPOUND) {
+ simpletlv_free(tlv[i].value.child, tlv[i].length);
+ } else {
+ free(tlv[i].value.value);
+ }
+ }
+ free(tlv);
+}
+/* vim: set ts=4 sw=4 tw=0 noet expandtab: */
diff --git a/src/simpletlv.h b/src/simpletlv.h
new file mode 100644
index 0000000..6d0e229
--- /dev/null
+++ b/src/simpletlv.h
@@ -0,0 +1,117 @@
+/*
+ * simpletlv.h: Simple TLV header file
+ *
+ * Copyright (C) 2016 Red Hat, Inc.
+ *
+ * Authors: Robert Relyea <rrelyea at redhat.com>
+ * Jakub Jelen <jjelen at redhat.com>
+ *
+ * This library 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 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _SIMPLETLV_H
+#define _SIMPLETLV_H
+
+enum simpletlv_type {
+ SIMPLETLV_TYPE_LEAF = 0,
+ SIMPLETLV_TYPE_COMPOUND = 1
+};
+
+enum simpletlv_buffer_type {
+ SIMPLETLV_TL = 0x01,
+ SIMPLETLV_VALUE = 0x02,
+ SIMPLETLV_BOTH = 0x03
+};
+
+struct simpletlv_member {
+ unsigned char tag;
+ unsigned int length;
+ union {
+ unsigned char *value;
+ struct simpletlv_member *child;
+ } value;
+ enum simpletlv_type type;
+};
+
+/*
+ * Calculate expected length of TLV buffer
+ * @param ltv array of LTV structres to encode
+ * @param tlvlen number of members in the array to encode
+ * @param buffer_type Encode only tags + lengths, values or both
+ */
+int
+simpletlv_get_length(struct simpletlv_member *, size_t,
+ enum simpletlv_buffer_type);
+
+/*
+ * Deallocate all parts of dynamically allocated SimpleTLV structure
+ */
+void
+simpletlv_free(struct simpletlv_member *tlv, size_t tlvlen);
+
+/*
+ * Merges two structures into one, creating a new shallow copy of both
+ * of the structures.
+ * Resulting length is the sum of a_len and b_len arguemnts.
+ */
+struct simpletlv_member *
+simpletlv_merge(const struct simpletlv_member *a, size_t a_len,
+ const struct simpletlv_member *b, size_t b_len);
+
+/*
+ * Encode strucure into SimpleLTV format, TL together with V
+ * @param tlv array of TLV structures to encode
+ * @param tlvlen number of members in the array to encode
+ * @param out Byte array to write into
+ * @param outlen The length of output array
+ * @param ptr The end of TLV record
+ */
+int
+simpletlv_encode(struct simpletlv_member *tlv, size_t tlv_len,
+ unsigned char **out, size_t outlen, unsigned char **ptr);
+
+int
+simpletlv_encode_tl(struct simpletlv_member *tlv, size_t tlv_len,
+ unsigned char **out, size_t outlen, unsigned char **newptr);
+
+int
+simpletlv_encode_val(struct simpletlv_member *tlv, size_t tlv_len,
+ unsigned char **out, size_t outlen, unsigned char **newptr);
+
+/*
+ * Create a tag/length file in Simple TLV based on the val_len content length
+ * @param tag Tag to store into the TL file
+ * @param datalen Data length to store into the TL file
+ * @param out TL byte array to write into
+ * @param outlen The length of the output array
+ * @param ptr The end of the TL record written
+ * @return SC_SUCCESS for correct input
+ */
+int
+simpletlv_put_tag(unsigned char tag, size_t datalen, unsigned char *out,
+ size_t outlen, unsigned char **ptr);
+
+/* get the Simple TLV tag and length.
+ * @param buf Pointer to the TL file
+ * @param buflen The length of TL file
+ * @param tag_out The tag from the TL file
+ * @param taglen The length of the V record
+ * @return SC_SUCCESS on valid input
+ */
+int
+simpletlv_read_tag(unsigned char **buf, size_t buflen,
+ unsigned char *tag_out, size_t *taglen);
+
+#endif
--
2.17.1
More information about the Spice-devel
mailing list