[PATCH libcacard 04/45] Basic SimpleTLV encoding and decoding methods

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



 * 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@xxxxxxxxxx>
Reviewed-by: Robert Relyea <rrelyea@xxxxxxxxxx>
---
 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@xxxxxxxxxx>
+ *          Jakub Jelen <jjelen@xxxxxxxxxx>
+ *
+ * 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@xxxxxxxxxx>
+ *          Jakub Jelen <jjelen@xxxxxxxxxx>
+ *
+ * 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@xxxxxxxxxx>
+ *          Jakub Jelen <jjelen@xxxxxxxxxx>
+ *
+ * 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@xxxxxxxxxx>
+ *          Jakub Jelen <jjelen@xxxxxxxxxx>
+ *
+ * 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

_______________________________________________
Spice-devel mailing list
Spice-devel@xxxxxxxxxxxxxxxxxxxxx
https://lists.freedesktop.org/mailman/listinfo/spice-devel




[Index of Archives]     [Linux Virtualization]     [Linux Virtualization]     [Linux ARM Kernel]     [Linux ARM]     [Linux Omap]     [Fedora ARM]     [IETF Annouce]     [Security]     [Bugtraq]     [Linux OMAP]     [Linux MIPS]     [ECOS]     [Asterisk Internet PBX]     [Linux API]     [Monitors]