[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