[Spice-devel] [PATCH libcacard 04/45] Basic SimpleTLV encoding and decoding methods
Marc-André Lureau
marcandre.lureau at gmail.com
Tue Jul 31 18:29:16 UTC 2018
Hi
On Tue, Jul 31, 2018 at 4:49 PM, Jakub Jelen <jjelen at redhat.com> wrote:
> * 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 \
Since the symbols are not explicitely exported, I don't think you want
to install the headers.
Can touch on commit
> $(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
>
> _______________________________________________
> Spice-devel mailing list
> Spice-devel at lists.freedesktop.org
> https://lists.freedesktop.org/mailman/listinfo/spice-devel
--
Marc-André Lureau
More information about the Spice-devel
mailing list