Hi On Tue, Jul 31, 2018 at 4:49 PM, Jakub Jelen <jjelen@xxxxxxxxxx> 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@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 \ 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@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 -- Marc-André Lureau _______________________________________________ Spice-devel mailing list Spice-devel@xxxxxxxxxxxxxxxxxxxxx https://lists.freedesktop.org/mailman/listinfo/spice-devel