* 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