/* * asn1.c: ASN.1 decoding functions (DER) * * Copyright (C) 2001, 2002 Juha Yrjölä * * 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 */ #include "config.h" #include #include #include #include #include #include "internal.h" #include "asn1.h" static int asn1_decode(sc_context_t *ctx, struct sc_asn1_entry *asn1, const u8 *in, size_t len, const u8 **newp, size_t *len_left, int choice, int depth); static int asn1_encode(sc_context_t *ctx, const struct sc_asn1_entry *asn1, u8 **ptr, size_t *size, int depth); static const char *tag2str(unsigned int tag) { static const char *tags[] = { "EOC", "BOOLEAN", "INTEGER", "BIT STRING", "OCTET STRING", /* 0-4 */ "NULL", "OBJECT", "OBJECT DESCRIPTOR", "EXTERNAL", "REAL", /* 5-9 */ "ENUMERATED", "", "UTF8STRING", "", /* 10-13 */ "", "", "SEQUENCE", "SET", /* 15-17 */ "NUMERICSTRING", "PRINTABLESTRING", "T61STRING", /* 18-20 */ "VIDEOTEXSTRING", "IA5STRING", "UTCTIME", "GENERALIZEDTIME", /* 21-24 */ "GRAPHICSTRING", "VISIBLESTRING", "GENERALSTRING", /* 25-27 */ "UNIVERSALSTRING", "", "BMPSTRING" /* 28-30 */ }; if (tag > 30) return "(unknown)"; return tags[tag]; } int sc_asn1_read_tag(const u8 ** buf, size_t buflen, unsigned int *cla_out, unsigned int *tag_out, size_t *taglen) { const u8 *p = *buf; size_t left = buflen, len; unsigned int cla, tag, i; if (left < 2) return SC_ERROR_INVALID_ASN1_OBJECT; *buf = NULL; if (*p == 0xff || *p == 0) /* end of data reached */ return SC_SUCCESS; /* parse tag byte(s) */ cla = (*p & SC_ASN1_TAG_CLASS) | (*p & SC_ASN1_TAG_CONSTRUCTED); tag = *p & SC_ASN1_TAG_PRIMITIVE; p++; left--; if (tag == SC_ASN1_TAG_PRIMITIVE) { /* high tag number */ size_t n = sizeof(int) - 1; /* search the last tag octet */ while (left-- != 0 && n != 0) { tag <<= 8; tag |= *p; if ((*p++ & 0x80) == 0) break; n--; } if (left == 0 || n == 0) /* either an invalid tag or it doesn't fit in * unsigned int */ return SC_ERROR_INVALID_ASN1_OBJECT; } if (left == 0) return SC_ERROR_INVALID_ASN1_OBJECT; /* parse length byte(s) */ len = *p & 0x7f; if (*p++ & 0x80) { unsigned int a = 0; if (len > 4 || len > left) return SC_ERROR_INVALID_ASN1_OBJECT; left -= len; for (i = 0; i < len; i++) { a <<= 8; a |= *p; p++; } len = a; } if (len > left) return SC_ERROR_INVALID_ASN1_OBJECT; *cla_out = cla; *tag_out = tag; *taglen = len; *buf = p; return SC_SUCCESS; } void sc_format_asn1_entry(struct sc_asn1_entry *entry, void *parm, void *arg, int set_present) { entry->parm = parm; entry->arg = arg; if (set_present) entry->flags |= SC_ASN1_PRESENT; } void sc_copy_asn1_entry(const struct sc_asn1_entry *src, struct sc_asn1_entry *dest) { while (src->name != NULL) { *dest = *src; dest++; src++; } dest->name = NULL; } static void sc_asn1_print_octet_string(const u8 * buf, size_t buflen) { size_t i; for (i = 0; i < buflen; i++) printf("%02X", buf[i]); } static void sc_asn1_print_utf8string(const u8 * buf, size_t buflen) { size_t i; for (i = 0; i < buflen; i++) printf("%c", buf[i]); } static void sc_asn1_print_integer(const u8 * buf, size_t buflen) { #ifndef _WIN32 long long a = 0; #else __int64 a = 0; #endif size_t i; if (buflen > sizeof(a)) { printf("too long"); return; } for (i = 0; i < buflen; i++) { a <<= 8; a |= buf[i]; } printf("%lld", a); } static void sc_asn1_print_boolean(const u8 * buf, size_t buflen) { if (!buflen) return; if (buf[0]) printf("true"); else printf("false"); } static void sc_asn1_print_bit_string(const u8 * buf, size_t buflen) { #ifndef _WIN32 long long a = 0; #else __int64 a = 0; #endif int r, i; if (buflen > sizeof(a) + 1) { printf("too long"); return; } r = sc_asn1_decode_bit_string(buf, buflen, &a, sizeof(a)); if (r < 0) { printf("decode error"); return; } for (i = r - 1; i >= 0; i--) { printf("%c", ((a >> i) & 1) ? '1' : '0'); } } static void sc_asn1_print_object_id(const u8 * buf, size_t buflen) { int i = 0; struct sc_object_id oid; char sbuf[256]; if (sc_asn1_decode_object_id(buf, buflen, &oid)) { printf("decode error"); return; } sbuf[0] = 0; while (oid.value[i] >= 0) { char tmp[12]; if (i) strcat(sbuf, "."); sprintf(tmp, "%d", oid.value[i]); strcat(sbuf, tmp); i++; } printf("%s", sbuf); } static void print_tags_recursive(const u8 * buf0, const u8 * buf, size_t buflen, int depth) { int i, r; size_t bytesleft = buflen; const char *classes[4] = { "Univ", "Appl", "Cntx", "Priv" }; const u8 *p = buf; while (bytesleft >= 2) { unsigned int cla = 0, tag = 0, hlen; const u8 *tagp = p; size_t len; r = sc_asn1_read_tag(&tagp, bytesleft, &cla, &tag, &len); if (r != SC_SUCCESS) { printf("Error in decoding.\n"); return; } hlen = tagp - p; if (cla == 0 && tag == 0) { printf("Zero tag, finishing\n"); break; } for (i = 0; i < depth; i++) { putchar(' '); putchar(' '); } printf("%02X %s: tag 0x%02X, length %3d: ", cla | tag, classes[cla >> 6], tag & 0x1f, (int) len); if (len + hlen > bytesleft) { printf(" Illegal length!\n"); return; } p += hlen + len; bytesleft -= hlen + len; if ((cla & SC_ASN1_TAG_CLASS) == SC_ASN1_TAG_UNIVERSAL) printf("%s", tag2str(tag)); if (cla & SC_ASN1_TAG_CONSTRUCTED) { putchar('\n'); print_tags_recursive(buf0, tagp, len, depth + 1); continue; } if ((cla & SC_ASN1_TAG_CLASS) == SC_ASN1_TAG_UNIVERSAL) { printf(" ["); switch (tag) { case SC_ASN1_TAG_BIT_STRING: sc_asn1_print_bit_string(tagp, len); break; case SC_ASN1_TAG_OCTET_STRING: sc_asn1_print_octet_string(tagp, len); break; case SC_ASN1_TAG_OBJECT: sc_asn1_print_object_id(tagp, len); break; case SC_ASN1_TAG_INTEGER: case SC_ASN1_TAG_ENUMERATED: sc_asn1_print_integer(tagp, len); break; case SC_ASN1_TAG_T61STRING: case SC_ASN1_TAG_PRINTABLESTRING: case SC_ASN1_TAG_UTF8STRING: sc_asn1_print_utf8string(tagp, len); break; case SC_ASN1_TAG_BOOLEAN: sc_asn1_print_boolean(tagp, len); break; } printf("]"); } if ((cla & SC_ASN1_TAG_CLASS) == SC_ASN1_TAG_APPLICATION) printf(" [%s]", sc_dump_hex(tagp, len)); if ((cla & SC_ASN1_TAG_CLASS) == SC_ASN1_TAG_CONTEXT) printf(" [%s]", sc_dump_hex(tagp, len)); putchar('\n'); } return; } void sc_asn1_print_tags(const u8 * buf, size_t buflen) { printf("Printing tags for buffer of length %d\n", (int) buflen); print_tags_recursive(buf, buf, buflen, 0); } const u8 *sc_asn1_find_tag(sc_context_t *ctx, const u8 * buf, size_t buflen, unsigned int tag_in, size_t *taglen_in) { size_t left = buflen, taglen; const u8 *p = buf; *taglen_in = 0; while (left >= 2) { unsigned int cla, tag, mask = 0xff00; buf = p; /* read a tag */ if (sc_asn1_read_tag(&p, left, &cla, &tag, &taglen) != SC_SUCCESS) return NULL; if (left < (size_t)(p - buf)) { sc_debug(ctx, SC_LOG_DEBUG_ASN1, "invalid TLV object\n"); return NULL; } left -= (p - buf); /* we need to shift the class byte to the leftmost * byte of the tag */ while ((tag & mask) != 0) { cla <<= 8; mask <<= 8; } /* compare the read tag with the given tag */ if ((tag | cla) == tag_in) { /* we have a match => return length and value part */ if (taglen > left) return NULL; *taglen_in = taglen; return p; } /* otherwise continue reading tags */ if (left < taglen) { sc_debug(ctx, SC_LOG_DEBUG_ASN1, "invalid TLV object\n"); return NULL; } left -= taglen; p += taglen; } return NULL; } const u8 *sc_asn1_skip_tag(sc_context_t *ctx, const u8 ** buf, size_t *buflen, unsigned int tag_in, size_t *taglen_out) { const u8 *p = *buf; size_t len = *buflen, taglen; unsigned int cla, tag; if (sc_asn1_read_tag((const u8 **) &p, len, &cla, &tag, &taglen) != SC_SUCCESS) return NULL; switch (cla & 0xC0) { case SC_ASN1_TAG_UNIVERSAL: if ((tag_in & SC_ASN1_CLASS_MASK) != SC_ASN1_UNI) return NULL; break; case SC_ASN1_TAG_APPLICATION: if ((tag_in & SC_ASN1_CLASS_MASK) != SC_ASN1_APP) return NULL; break; case SC_ASN1_TAG_CONTEXT: if ((tag_in & SC_ASN1_CLASS_MASK) != SC_ASN1_CTX) return NULL; break; case SC_ASN1_TAG_PRIVATE: if ((tag_in & SC_ASN1_CLASS_MASK) != SC_ASN1_PRV) return NULL; break; } if (cla & SC_ASN1_TAG_CONSTRUCTED) { if ((tag_in & SC_ASN1_CONS) == 0) return NULL; } else if (tag_in & SC_ASN1_CONS) return NULL; if ((tag_in & SC_ASN1_TAG_MASK) != tag) return NULL; len -= (p - *buf); /* header size */ if (taglen > len) { sc_debug(ctx, SC_LOG_DEBUG_ASN1, "too long ASN.1 object (size %d while only %d available)\n", taglen, len); return NULL; } *buflen -= (p - *buf) + taglen; *buf = p + taglen; /* point to next tag */ *taglen_out = taglen; return p; } const u8 *sc_asn1_verify_tag(sc_context_t *ctx, const u8 * buf, size_t buflen, unsigned int tag_in, size_t *taglen_out) { return sc_asn1_skip_tag(ctx, &buf, &buflen, tag_in, taglen_out); } static int decode_bit_string(const u8 * inbuf, size_t inlen, void *outbuf, size_t outlen, int invert) { const u8 *in = inbuf; u8 *out = (u8 *) outbuf; int zero_bits = *in & 0x07; size_t octets_left = inlen - 1; int i, count = 0; memset(outbuf, 0, outlen); in++; if (outlen < octets_left) return SC_ERROR_BUFFER_TOO_SMALL; if (inlen < 1) return SC_ERROR_INVALID_ASN1_OBJECT; while (octets_left) { /* 1st octet of input: ABCDEFGH, where A is the MSB */ /* 1st octet of output: HGFEDCBA, where A is the LSB */ /* first bit in bit string is the LSB in first resulting octet */ int bits_to_go; *out = 0; if (octets_left == 1) bits_to_go = 8 - zero_bits; else bits_to_go = 8; if (invert) for (i = 0; i < bits_to_go; i++) { *out |= ((*in >> (7 - i)) & 1) << i; } else { *out = *in; } out++; in++; octets_left--; count++; } return (count * 8) - zero_bits; } int sc_asn1_decode_bit_string(const u8 * inbuf, size_t inlen, void *outbuf, size_t outlen) { return decode_bit_string(inbuf, inlen, outbuf, outlen, 1); } int sc_asn1_decode_bit_string_ni(const u8 * inbuf, size_t inlen, void *outbuf, size_t outlen) { return decode_bit_string(inbuf, inlen, outbuf, outlen, 0); } static int encode_bit_string(const u8 * inbuf, size_t bits_left, u8 **outbuf, size_t *outlen, int invert) { const u8 *in = inbuf; u8 *out; size_t bytes; int skipped = 0; bytes = (bits_left + 7)/8 + 1; *outbuf = out = malloc(bytes); if (out == NULL) return SC_ERROR_OUT_OF_MEMORY; *outlen = bytes; out += 1; while (bits_left) { int i, bits_to_go = 8; *out = 0; if (bits_left < 8) { bits_to_go = bits_left; skipped = 8 - bits_left; } if (invert) { for (i = 0; i < bits_to_go; i++) *out |= ((*in >> i) & 1) << (7 - i); } else { *out = *in; if (bits_left < 8) return SC_ERROR_NOT_SUPPORTED; /* FIXME */ } bits_left -= bits_to_go; out++, in++; } out = *outbuf; out[0] = skipped; return 0; } /* * Bitfields are just bit strings, stored in an unsigned int * (taking endianness into account) */ static int decode_bit_field(const u8 * inbuf, size_t inlen, void *outbuf, size_t outlen) { u8 data[sizeof(unsigned int)]; unsigned int field = 0; int i, n; if (outlen != sizeof(data)) return SC_ERROR_BUFFER_TOO_SMALL; n = decode_bit_string(inbuf, inlen, data, sizeof(data), 1); if (n < 0) return n; for (i = 0; i < n; i += 8) { field |= (data[i/8] << i); } memcpy(outbuf, &field, outlen); return 0; } static int encode_bit_field(const u8 *inbuf, size_t inlen, u8 **outbuf, size_t *outlen) { u8 data[sizeof(unsigned int)]; unsigned int field = 0; size_t i, bits; if (inlen != sizeof(data)) return SC_ERROR_BUFFER_TOO_SMALL; /* count the bits */ memcpy(&field, inbuf, inlen); for (bits = 0; field; bits++) field >>= 1; memcpy(&field, inbuf, inlen); for (i = 0; i < bits; i += 8) data[i/8] = field >> i; return encode_bit_string(data, bits, outbuf, outlen, 1); } int sc_asn1_decode_integer(const u8 * inbuf, size_t inlen, int *out) { int a = 0; size_t i; if (inlen > sizeof(int)) return SC_ERROR_INVALID_ASN1_OBJECT; if (inbuf[0] & 0x80) a = -1; for (i = 0; i < inlen; i++) { a <<= 8; a |= *inbuf++; } *out = a; return 0; } static int asn1_encode_integer(int in, u8 ** obj, size_t * objsize) { int i = sizeof(in) * 8, skip_zero, skip_sign; u8 *p, b; if (in < 0) { skip_sign = 1; skip_zero= 0; } else { skip_sign = 0; skip_zero= 1; } *obj = p = malloc(sizeof(in)+1); if (*obj == NULL) return SC_ERROR_OUT_OF_MEMORY; do { i -= 8; b = in >> i; if (skip_sign) { if (b != 0xff) skip_sign = 0; if (b & 0x80) { *p = b; if (0xff == b) continue; } else { p++; skip_sign = 0; } } if (b == 0 && skip_zero) continue; if (skip_zero) { skip_zero = 0; /* prepend 0x00 if MSb is 1 and integer positive */ if ((b & 0x80) != 0 && in > 0) *p++ = 0; } *p++ = b; } while (i > 0); if (skip_sign) p++; *objsize = p - *obj; if (*objsize == 0) { *objsize = 1; (*obj)[0] = 0; } return 0; } int sc_asn1_decode_object_id(const u8 * inbuf, size_t inlen, struct sc_object_id *id) { int i, a; const u8 *p = inbuf; int *octet; if (inlen == 0 || inbuf == NULL || id == NULL) return SC_ERROR_INVALID_ARGUMENTS; octet = id->value; for (i = 0; i < SC_MAX_OBJECT_ID_OCTETS; i++) id->value[i] = -1; a = *p; *octet++ = a / 40; *octet++ = a % 40; inlen--; while (inlen) { p++; a = *p & 0x7F; inlen--; while (inlen && *p & 0x80) { p++; a <<= 7; a |= *p & 0x7F; inlen--; } *octet++ = a; if (octet - id->value >= SC_MAX_OBJECT_ID_OCTETS-1) return SC_ERROR_INVALID_ASN1_OBJECT; }; return 0; } int sc_asn1_encode_object_id(u8 **buf, size_t *buflen, const struct sc_object_id *id) { u8 temp[SC_MAX_OBJECT_ID_OCTETS*5], *p = temp; size_t count = 0; int i; int value[SC_MAX_OBJECT_ID_OCTETS]; /* set the unused ID part to '-1' */ memcpy(value, &id->value[0], sizeof(value)); for (i = SC_MAX_OBJECT_ID_OCTETS - 1; i>=0; i--) if (!value[i]) value[i] = -1; for (i = 0; i < SC_MAX_OBJECT_ID_OCTETS && value[i] >= 0; i++) { unsigned int k, shift; k = value[i]; switch (i) { case 0: if (k > 2) return SC_ERROR_INVALID_ARGUMENTS; *p = k * 40; break; case 1: if (k > 39) return SC_ERROR_INVALID_ARGUMENTS; *p++ += k; break; default: shift = 28; while (shift && (k >> shift) == 0) shift -= 7; while (shift) { *p++ = 0x80 | ((k >> shift) & 0x7f); shift -= 7; } *p++ = k & 0x7F; break; } } if (i == 1) /* an OID must have at least two components */ return SC_ERROR_INVALID_ARGUMENTS; *buflen = count = p - temp; *buf = malloc(count); if (!*buf) return SC_ERROR_OUT_OF_MEMORY; memcpy(*buf, temp, count); return 0; } static int sc_asn1_decode_utf8string(const u8 *inbuf, size_t inlen, u8 *out, size_t *outlen) { if (inlen+1 > *outlen) return SC_ERROR_BUFFER_TOO_SMALL; *outlen = inlen+1; memcpy(out, inbuf, inlen); out[inlen] = 0; return 0; } int sc_asn1_put_tag(int tag, const u8 * data, size_t datalen, u8 * out, size_t outlen, u8 **ptr) { u8 *p = out; if (outlen < 2) return SC_ERROR_INVALID_ARGUMENTS; if (datalen > 127) return SC_ERROR_INVALID_ARGUMENTS; *p++ = tag & 0xFF; /* FIXME: Support longer tags */ outlen--; *p++ = datalen; outlen--; if (outlen < datalen) return SC_ERROR_INVALID_ARGUMENTS; memcpy(p, data, datalen); p += datalen; if (ptr != NULL) *ptr = p; return 0; } static int asn1_write_element(sc_context_t *ctx, unsigned int tag, const u8 * data, size_t datalen, u8 ** out, size_t * outlen) { unsigned char t; unsigned char *buf, *p; int c = 0; unsigned short_tag; unsigned char tag_char[3] = {0, 0, 0}; size_t tag_len, ii; short_tag = tag & SC_ASN1_TAG_MASK; for (tag_len = 0; short_tag >> (8 * tag_len); tag_len++) tag_char[tag_len] = (short_tag >> (8 * tag_len)) & 0xFF; if (!tag_len) tag_len = 1; if (tag_len > 1) { if ((tag_char[tag_len - 1] & SC_ASN1_TAG_PRIMITIVE) != SC_ASN1_TAG_ESCAPE_MARKER) SC_TEST_RET(ctx, SC_LOG_DEBUG_ASN1, SC_ERROR_INVALID_DATA, "First byte of the long tag is not 'escape marker'"); for (ii = 1; ii < tag_len - 1; ii++) if (!(tag_char[ii] & 0x80)) SC_TEST_RET(ctx, SC_LOG_DEBUG_ASN1, SC_ERROR_INVALID_DATA, "MS bit expected to be 'one'"); if (tag_char[0] & 0x80) SC_TEST_RET(ctx, SC_LOG_DEBUG_ASN1, SC_ERROR_INVALID_DATA, "MS bit of the last byte expected to be 'zero'"); } t = tag_char[tag_len - 1] & 0x1F; switch (tag & SC_ASN1_CLASS_MASK) { case SC_ASN1_UNI: break; case SC_ASN1_APP: t |= SC_ASN1_TAG_APPLICATION; break; case SC_ASN1_CTX: t |= SC_ASN1_TAG_CONTEXT; break; case SC_ASN1_PRV: t |= SC_ASN1_TAG_PRIVATE; break; } if (tag & SC_ASN1_CONS) t |= SC_ASN1_TAG_CONSTRUCTED; if (datalen > 127) { c = 1; while (datalen >> (c << 3)) c++; } *outlen = tag_len + 1 + c + datalen; buf = malloc(*outlen); if (buf == NULL) SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_ASN1, SC_ERROR_OUT_OF_MEMORY); *out = p = buf; *p++ = t; for (ii=1;ii> (c << 3)) & 0xFF; } else { *p++ = datalen & 0x7F; } memcpy(p, data, datalen); return SC_SUCCESS; } static const struct sc_asn1_entry c_asn1_path_ext[3] = { { "aid", SC_ASN1_OCTET_STRING, SC_ASN1_APP | 0x0F, 0, NULL, NULL }, { "path", SC_ASN1_OCTET_STRING, SC_ASN1_TAG_OCTET_STRING, 0, NULL, NULL }, { NULL, 0, 0, 0, NULL, NULL } }; static const struct sc_asn1_entry c_asn1_path[5] = { { "path", SC_ASN1_OCTET_STRING, SC_ASN1_TAG_OCTET_STRING, SC_ASN1_OPTIONAL, NULL, NULL }, { "index", SC_ASN1_INTEGER, SC_ASN1_TAG_INTEGER, SC_ASN1_OPTIONAL, NULL, NULL }, { "length", SC_ASN1_INTEGER, SC_ASN1_CTX | 0, SC_ASN1_OPTIONAL, NULL, NULL }, /* For some multi-applications PKCS#15 card the ODF records can hold the references to * the xDF files and objects placed elsewhere then under the application DF of the ODF itself. * In such a case the 'path' ASN1 data includes also the ID of the target application (AID). * This path extension do not make a part of PKCS#15 standard. */ { "pathExtended", SC_ASN1_STRUCT, SC_ASN1_CTX | 1 | SC_ASN1_CONS, SC_ASN1_OPTIONAL, NULL, NULL }, { NULL, 0, 0, 0, NULL, NULL } }; static int asn1_decode_path(sc_context_t *ctx, const u8 *in, size_t len, sc_path_t *path, int depth) { int idx, count, r; struct sc_asn1_entry asn1_path_ext[3], asn1_path[5]; unsigned char path_value[SC_MAX_PATH_SIZE], aid_value[SC_MAX_AID_SIZE]; size_t path_len = sizeof(path_value), aid_len = sizeof(aid_value); memset(path, 0, sizeof(struct sc_path)); sc_copy_asn1_entry(c_asn1_path_ext, asn1_path_ext); sc_copy_asn1_entry(c_asn1_path, asn1_path); sc_format_asn1_entry(asn1_path_ext + 0, aid_value, &aid_len, 0); sc_format_asn1_entry(asn1_path_ext + 1, path_value, &path_len, 0); sc_format_asn1_entry(asn1_path + 0, path_value, &path_len, 0); sc_format_asn1_entry(asn1_path + 1, &idx, NULL, 0); sc_format_asn1_entry(asn1_path + 2, &count, NULL, 0); sc_format_asn1_entry(asn1_path + 3, asn1_path_ext, NULL, 0); r = asn1_decode(ctx, asn1_path, in, len, NULL, NULL, 0, depth + 1); if (r) return r; if (asn1_path[3].flags & SC_ASN1_PRESENT) { /* extended path present: set 'path' and 'aid' */ memcpy(path->aid.value, aid_value, aid_len); path->aid.len = aid_len; memcpy(path->value, path_value, path_len); path->len = path_len; } else if (asn1_path[0].flags & SC_ASN1_PRESENT) { /* path present: set 'path' */ memcpy(path->value, path_value, path_len); path->len = path_len; } else { /* failed if both 'path' and 'pathExtended' are absent */ return SC_ERROR_ASN1_OBJECT_NOT_FOUND; } if (path->len == 2) path->type = SC_PATH_TYPE_FILE_ID; else if (path->aid.len && path->len > 2) path->type = SC_PATH_TYPE_FROM_CURRENT; else path->type = SC_PATH_TYPE_PATH; if ((asn1_path[1].flags & SC_ASN1_PRESENT) && (asn1_path[2].flags & SC_ASN1_PRESENT)) { path->index = idx; path->count = count; } else { path->index = 0; path->count = -1; } return SC_SUCCESS; } static int asn1_encode_path(sc_context_t *ctx, const sc_path_t *path, u8 **buf, size_t *bufsize, int depth, unsigned int parent_flags) { int r; struct sc_asn1_entry asn1_path[5]; sc_path_t tpath = *path; sc_copy_asn1_entry(c_asn1_path, asn1_path); sc_format_asn1_entry(asn1_path + 0, (void *) &tpath.value, (void *) &tpath.len, 1); asn1_path[0].flags |= parent_flags; if (path->count > 0) { sc_format_asn1_entry(asn1_path + 1, (void *) &tpath.index, NULL, 1); sc_format_asn1_entry(asn1_path + 2, (void *) &tpath.count, NULL, 1); } r = asn1_encode(ctx, asn1_path, buf, bufsize, depth + 1); return r; } static const struct sc_asn1_entry c_asn1_se_info[4] = { { "se", SC_ASN1_INTEGER, SC_ASN1_TAG_INTEGER, 0, NULL, NULL }, { "owner", SC_ASN1_OBJECT, SC_ASN1_TAG_OBJECT, SC_ASN1_OPTIONAL, NULL, NULL }, { "aid", SC_ASN1_OCTET_STRING, SC_ASN1_TAG_OCTET_STRING, SC_ASN1_OPTIONAL, NULL, NULL }, { NULL, 0, 0, 0, NULL, NULL } }; static int asn1_decode_se_info(sc_context_t *ctx, const u8 *obj, size_t objlen, sc_pkcs15_sec_env_info_t ***se, size_t *num, int depth) { sc_pkcs15_sec_env_info_t **ses; const unsigned char *p; size_t plen, idx = 0, size = 8, left = size; int ret = SC_SUCCESS; p = sc_asn1_find_tag(ctx, obj, objlen, 0x30, &plen); if (p == NULL) return SC_ERROR_INVALID_ASN1_OBJECT; ses = calloc(size, sizeof(sc_pkcs15_sec_env_info_t *)); if (ses == NULL) return SC_ERROR_OUT_OF_MEMORY; while (plen != 0) { struct sc_asn1_entry asn1_se_info[4]; sc_pkcs15_sec_env_info_t *si = calloc(1, sizeof(sc_pkcs15_sec_env_info_t)); if (si == NULL) { ret = SC_ERROR_OUT_OF_MEMORY; goto err; } si->aid.len = sizeof(si->aid.value); sc_copy_asn1_entry(c_asn1_se_info, asn1_se_info); sc_format_asn1_entry(asn1_se_info + 0, &si->se, NULL, 0); sc_format_asn1_entry(asn1_se_info + 1, &si->owner, NULL, 0); sc_format_asn1_entry(asn1_se_info + 2, &si->aid.value, &si->aid.len, 0); ret = asn1_decode(ctx, asn1_se_info, p, plen, &p, &plen, 0, depth+1); if (ret != SC_SUCCESS) { free(si); ret = SC_ERROR_INVALID_ASN1_OBJECT; goto err; } if (--left == 0) { sc_pkcs15_sec_env_info_t **np; size <<= 1; np = realloc(ses, sizeof(sc_pkcs15_sec_env_info_t *) * size); if (np == NULL) { free(si); ret = SC_ERROR_OUT_OF_MEMORY; goto err; } ses = np; left = size >> 1; } ses[idx++] = si; } err: if (ret == SC_SUCCESS) { *se = ses; *num = idx; } else { size_t i; for (i = 0; i < idx; i++) free(ses[i]); free(ses); } return ret; } static const struct sc_asn1_entry c_asn1_access_control_rule[3] = { { "accessMode", SC_ASN1_BIT_FIELD, SC_ASN1_TAG_BIT_STRING, SC_ASN1_OPTIONAL, NULL, NULL }, { "securityCondition", SC_ASN1_PKCS15_ID, SC_ASN1_TAG_OCTET_STRING, SC_ASN1_OPTIONAL, NULL, NULL }, { NULL, 0, 0, 0, NULL, NULL } }; /* * in src/libopensc/pkcs15.h SC_PKCS15_MAX_ACCESS_RULES defined as 8 */ static const struct sc_asn1_entry c_asn1_access_control_rules[SC_PKCS15_MAX_ACCESS_RULES + 1] = { { "accessControlRule", SC_ASN1_STRUCT, SC_ASN1_TAG_SEQUENCE | SC_ASN1_CONS, SC_ASN1_OPTIONAL, NULL, NULL }, { "accessControlRule", SC_ASN1_STRUCT, SC_ASN1_TAG_SEQUENCE | SC_ASN1_CONS, SC_ASN1_OPTIONAL, NULL, NULL }, { "accessControlRule", SC_ASN1_STRUCT, SC_ASN1_TAG_SEQUENCE | SC_ASN1_CONS, SC_ASN1_OPTIONAL, NULL, NULL }, { "accessControlRule", SC_ASN1_STRUCT, SC_ASN1_TAG_SEQUENCE | SC_ASN1_CONS, SC_ASN1_OPTIONAL, NULL, NULL }, { "accessControlRule", SC_ASN1_STRUCT, SC_ASN1_TAG_SEQUENCE | SC_ASN1_CONS, SC_ASN1_OPTIONAL, NULL, NULL }, { "accessControlRule", SC_ASN1_STRUCT, SC_ASN1_TAG_SEQUENCE | SC_ASN1_CONS, SC_ASN1_OPTIONAL, NULL, NULL }, { "accessControlRule", SC_ASN1_STRUCT, SC_ASN1_TAG_SEQUENCE | SC_ASN1_CONS, SC_ASN1_OPTIONAL, NULL, NULL }, { "accessControlRule", SC_ASN1_STRUCT, SC_ASN1_TAG_SEQUENCE | SC_ASN1_CONS, SC_ASN1_OPTIONAL, NULL, NULL }, { NULL, 0, 0, 0, NULL, NULL } }; static const struct sc_asn1_entry c_asn1_com_obj_attr[6] = { { "label", SC_ASN1_UTF8STRING, SC_ASN1_TAG_UTF8STRING, SC_ASN1_OPTIONAL, NULL, NULL }, { "flags", SC_ASN1_BIT_FIELD, SC_ASN1_TAG_BIT_STRING, SC_ASN1_OPTIONAL, NULL, NULL }, { "authId", SC_ASN1_PKCS15_ID, SC_ASN1_TAG_OCTET_STRING, SC_ASN1_OPTIONAL, NULL, NULL }, { "userConsent", SC_ASN1_INTEGER, SC_ASN1_TAG_INTEGER, SC_ASN1_OPTIONAL, NULL, NULL }, { "accessControlRules", SC_ASN1_STRUCT, SC_ASN1_TAG_SEQUENCE | SC_ASN1_CONS, SC_ASN1_OPTIONAL, NULL, NULL }, { NULL, 0, 0, 0, NULL, NULL } }; static const struct sc_asn1_entry c_asn1_p15_obj[5] = { { "commonObjectAttributes", SC_ASN1_STRUCT, SC_ASN1_TAG_SEQUENCE | SC_ASN1_CONS, 0, NULL, NULL }, { "classAttributes", SC_ASN1_STRUCT, SC_ASN1_TAG_SEQUENCE | SC_ASN1_CONS, 0, NULL, NULL }, { "subClassAttributes", SC_ASN1_STRUCT, SC_ASN1_CTX | 0 | SC_ASN1_CONS, SC_ASN1_OPTIONAL, NULL, NULL }, { "typeAttributes", SC_ASN1_STRUCT, SC_ASN1_CTX | 1 | SC_ASN1_CONS, 0, NULL, NULL }, { NULL, 0, 0, 0, NULL, NULL } }; static int asn1_decode_p15_object(sc_context_t *ctx, const u8 *in, size_t len, struct sc_asn1_pkcs15_object *obj, int depth) { struct sc_pkcs15_object *p15_obj = obj->p15_obj; struct sc_asn1_entry asn1_c_attr[6], asn1_p15_obj[5]; struct sc_asn1_entry asn1_ac_rules[SC_PKCS15_MAX_ACCESS_RULES + 1], asn1_ac_rule[SC_PKCS15_MAX_ACCESS_RULES][3]; size_t flags_len = sizeof(p15_obj->flags); size_t label_len = sizeof(p15_obj->label); size_t access_mode_len = sizeof(p15_obj->access_rules[0].access_mode); int r, ii; for (ii=0; iilabel, &label_len, 0); sc_format_asn1_entry(asn1_c_attr + 1, &p15_obj->flags, &flags_len, 0); sc_format_asn1_entry(asn1_c_attr + 2, &p15_obj->auth_id, NULL, 0); sc_format_asn1_entry(asn1_c_attr + 3, &p15_obj->user_consent, NULL, 0); for (ii=0; iiaccess_rules[ii].access_mode, &access_mode_len, 0); sc_format_asn1_entry(asn1_ac_rule[ii] + 1, &p15_obj->access_rules[ii].auth_id, NULL, 0); sc_format_asn1_entry(asn1_ac_rules + ii, asn1_ac_rule[ii], NULL, 0); } sc_format_asn1_entry(asn1_c_attr + 4, asn1_ac_rules, NULL, 0); sc_format_asn1_entry(asn1_p15_obj + 0, asn1_c_attr, NULL, 0); sc_format_asn1_entry(asn1_p15_obj + 1, obj->asn1_class_attr, NULL, 0); sc_format_asn1_entry(asn1_p15_obj + 2, obj->asn1_subclass_attr, NULL, 0); sc_format_asn1_entry(asn1_p15_obj + 3, obj->asn1_type_attr, NULL, 0); r = asn1_decode(ctx, asn1_p15_obj, in, len, NULL, NULL, 0, depth + 1); return r; } static int asn1_encode_p15_object(sc_context_t *ctx, const struct sc_asn1_pkcs15_object *obj, u8 **buf, size_t *bufsize, int depth) { struct sc_pkcs15_object p15_obj = *obj->p15_obj; struct sc_asn1_entry asn1_c_attr[6], asn1_p15_obj[5]; struct sc_asn1_entry asn1_ac_rules[SC_PKCS15_MAX_ACCESS_RULES + 1], asn1_ac_rule[SC_PKCS15_MAX_ACCESS_RULES][3]; size_t label_len = strlen(p15_obj.label); size_t flags_len; size_t access_mode_len; int r, ii; sc_debug(ctx, SC_LOG_DEBUG_ASN1, "encode p15 obj(type:0x%X,access_mode:0x%X)", p15_obj.type, p15_obj.access_rules[0].access_mode); if (p15_obj.access_rules[0].access_mode) { for (ii=0; iiasn1_class_attr, NULL, 1); if (obj->asn1_subclass_attr != NULL) sc_format_asn1_entry(asn1_p15_obj + 2, obj->asn1_subclass_attr, NULL, 1); sc_format_asn1_entry(asn1_p15_obj + 3, obj->asn1_type_attr, NULL, 1); r = asn1_encode(ctx, asn1_p15_obj, buf, bufsize, depth + 1); return r; } static int asn1_decode_entry(sc_context_t *ctx,struct sc_asn1_entry *entry, const u8 *obj, size_t objlen, int depth) { void *parm = entry->parm; int (*callback_func)(sc_context_t *nctx, void *arg, const u8 *nobj, size_t nobjlen, int ndepth); size_t *len = (size_t *) entry->arg; int r = 0; callback_func = parm; sc_debug(ctx, SC_LOG_DEBUG_ASN1, "%*.*sdecoding '%s'\n", depth, depth, "", entry->name); switch (entry->type) { case SC_ASN1_STRUCT: if (parm != NULL) r = asn1_decode(ctx, (struct sc_asn1_entry *) parm, obj, objlen, NULL, NULL, 0, depth + 1); break; case SC_ASN1_NULL: break; case SC_ASN1_BOOLEAN: if (parm != NULL) { if (objlen != 1) { sc_debug(ctx, SC_LOG_DEBUG_ASN1, "invalid ASN.1 object length: %d\n", objlen); r = SC_ERROR_INVALID_ASN1_OBJECT; } else *((int *) parm) = obj[0] ? 1 : 0; } break; case SC_ASN1_INTEGER: case SC_ASN1_ENUMERATED: if (parm != NULL) { r = sc_asn1_decode_integer(obj, objlen, (int *) entry->parm); sc_debug(ctx, SC_LOG_DEBUG_ASN1, "%*.*sdecoding '%s' returned %d\n", depth, depth, "", entry->name, *((int *) entry->parm)); } break; case SC_ASN1_BIT_STRING_NI: case SC_ASN1_BIT_STRING: if (parm != NULL) { int invert = entry->type == SC_ASN1_BIT_STRING ? 1 : 0; assert(len != NULL); if (objlen < 1) { r = SC_ERROR_INVALID_ASN1_OBJECT; break; } if (entry->flags & SC_ASN1_ALLOC) { u8 **buf = (u8 **) parm; *buf = malloc(objlen-1); if (*buf == NULL) { r = SC_ERROR_OUT_OF_MEMORY; break; } *len = objlen-1; parm = *buf; } r = decode_bit_string(obj, objlen, (u8 *) parm, *len, invert); if (r >= 0) { *len = r; r = 0; } } break; case SC_ASN1_BIT_FIELD: if (parm != NULL) r = decode_bit_field(obj, objlen, (u8 *) parm, *len); break; case SC_ASN1_OCTET_STRING: if (parm != NULL) { size_t c; assert(len != NULL); /* Strip off padding zero */ if ((entry->flags & SC_ASN1_UNSIGNED) && obj[0] == 0x00 && objlen > 1) { objlen--; obj++; } /* Allocate buffer if needed */ if (entry->flags & SC_ASN1_ALLOC) { u8 **buf = (u8 **) parm; *buf = malloc(objlen); if (*buf == NULL) { r = SC_ERROR_OUT_OF_MEMORY; break; } c = *len = objlen; parm = *buf; } else c = objlen > *len ? *len : objlen; memcpy(parm, obj, c); *len = c; } break; case SC_ASN1_GENERALIZEDTIME: if (parm != NULL) { size_t c; assert(len != NULL); if (entry->flags & SC_ASN1_ALLOC) { u8 **buf = (u8 **) parm; *buf = malloc(objlen); if (*buf == NULL) { r = SC_ERROR_OUT_OF_MEMORY; break; } c = *len = objlen; parm = *buf; } else c = objlen > *len ? *len : objlen; memcpy(parm, obj, c); *len = c; } break; case SC_ASN1_OBJECT: if (parm != NULL) r = sc_asn1_decode_object_id(obj, objlen, (struct sc_object_id *) parm); break; case SC_ASN1_PRINTABLESTRING: case SC_ASN1_UTF8STRING: if (parm != NULL) { assert(len != NULL); if (entry->flags & SC_ASN1_ALLOC) { u8 **buf = (u8 **) parm; *buf = malloc(objlen+1); if (*buf == NULL) { r = SC_ERROR_OUT_OF_MEMORY; break; } *len = objlen+1; parm = *buf; } r = sc_asn1_decode_utf8string(obj, objlen, (u8 *) parm, len); if (entry->flags & SC_ASN1_ALLOC) { *len -= 1; } } break; case SC_ASN1_PATH: if (entry->parm != NULL) r = asn1_decode_path(ctx, obj, objlen, (sc_path_t *) parm, depth); break; case SC_ASN1_PKCS15_ID: if (entry->parm != NULL) { struct sc_pkcs15_id *id = (struct sc_pkcs15_id *) parm; size_t c = objlen > sizeof(id->value) ? sizeof(id->value) : objlen; memcpy(id->value, obj, c); id->len = c; } break; case SC_ASN1_PKCS15_OBJECT: if (entry->parm != NULL) r = asn1_decode_p15_object(ctx, obj, objlen, (struct sc_asn1_pkcs15_object *) parm, depth); break; case SC_ASN1_ALGORITHM_ID: if (entry->parm != NULL) r = sc_asn1_decode_algorithm_id(ctx, obj, objlen, (struct sc_algorithm_id *) parm, depth); break; case SC_ASN1_SE_INFO: if (entry->parm != NULL) r = asn1_decode_se_info(ctx, obj, objlen, (sc_pkcs15_sec_env_info_t ***)entry->parm, len, depth); break; case SC_ASN1_CALLBACK: if (entry->parm != NULL) r = callback_func(ctx, entry->arg, obj, objlen, depth); break; default: sc_debug(ctx, SC_LOG_DEBUG_ASN1, "invalid ASN.1 type: %d\n", entry->type); return SC_ERROR_INVALID_ASN1_OBJECT; } if (r) { sc_debug(ctx, SC_LOG_DEBUG_ASN1, "decoding of ASN.1 object '%s' failed: %s\n", entry->name, sc_strerror(r)); return r; } entry->flags |= SC_ASN1_PRESENT; return 0; } static int asn1_decode(sc_context_t *ctx, struct sc_asn1_entry *asn1, const u8 *in, size_t len, const u8 **newp, size_t *len_left, int choice, int depth) { int r, idx = 0; const u8 *p = in, *obj; struct sc_asn1_entry *entry = asn1; size_t left = len, objlen; sc_debug(ctx, SC_LOG_DEBUG_ASN1, "%*.*scalled, left=%u, depth %d%s\n", depth, depth, "", left, depth, choice ? ", choice" : ""); if (left < 2) { while (asn1->name && (asn1->flags & SC_ASN1_OPTIONAL)) asn1++; /* If all elements were optional, there's nothing * to complain about */ if (asn1->name == NULL) return 0; sc_debug(ctx, SC_LOG_DEBUG_ASN1, "End of ASN.1 stream, " "non-optional field \"%s\" not found\n", asn1->name); return SC_ERROR_ASN1_OBJECT_NOT_FOUND; } if (p[0] == 0 || p[0] == 0xFF || len == 0) return SC_ERROR_ASN1_END_OF_CONTENTS; for (idx = 0; asn1[idx].name != NULL; idx++) { entry = &asn1[idx]; sc_debug(ctx, SC_LOG_DEBUG_ASN1, "Looking for '%s', tag 0x%x%s%s\n", entry->name, entry->tag, choice? ", CHOICE" : "", (entry->flags & SC_ASN1_OPTIONAL)? ", OPTIONAL": ""); /* Special case CHOICE has no tag */ if (entry->type == SC_ASN1_CHOICE) { r = asn1_decode(ctx, (struct sc_asn1_entry *) entry->parm, p, left, &p, &left, 1, depth + 1); if (r >= 0) r = 0; goto decode_ok; } obj = sc_asn1_skip_tag(ctx, &p, &left, entry->tag, &objlen); if (obj == NULL) { sc_debug(ctx, SC_LOG_DEBUG_ASN1, "not present\n"); if (choice) continue; if (entry->flags & SC_ASN1_OPTIONAL) continue; sc_debug(ctx, SC_LOG_DEBUG_ASN1, "mandatory ASN.1 object '%s' not found\n", entry->name); if (left) { u8 line[128], *linep = line; size_t i; line[0] = 0; for (i = 0; i < 10 && i < left; i++) { sprintf((char *) linep, "%02X ", p[i]); linep += 3; } sc_debug(ctx, SC_LOG_DEBUG_ASN1, "next tag: %s\n", line); } SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_ASN1, SC_ERROR_ASN1_OBJECT_NOT_FOUND); } r = asn1_decode_entry(ctx, entry, obj, objlen, depth); decode_ok: if (r) return r; if (choice) break; } if (choice && asn1[idx].name == NULL) /* No match */ SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_ASN1, SC_ERROR_ASN1_OBJECT_NOT_FOUND); if (newp != NULL) *newp = p; if (len_left != NULL) *len_left = left; if (choice) SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_ASN1, idx); SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_ASN1, 0); } int sc_asn1_decode(sc_context_t *ctx, struct sc_asn1_entry *asn1, const u8 *in, size_t len, const u8 **newp, size_t *len_left) { return asn1_decode(ctx, asn1, in, len, newp, len_left, 0, 0); } int sc_asn1_decode_choice(sc_context_t *ctx, struct sc_asn1_entry *asn1, const u8 *in, size_t len, const u8 **newp, size_t *len_left) { return asn1_decode(ctx, asn1, in, len, newp, len_left, 1, 0); } static int asn1_encode_entry(sc_context_t *ctx, const struct sc_asn1_entry *entry, u8 **obj, size_t *objlen, int depth) { void *parm = entry->parm; int (*callback_func)(sc_context_t *nctx, void *arg, u8 **nobj, size_t *nobjlen, int ndepth); const size_t *len = (const size_t *) entry->arg; int r = 0; u8 * buf = NULL; size_t buflen = 0; callback_func = parm; sc_debug(ctx, SC_LOG_DEBUG_ASN1, "%*.*sencoding '%s'%s\n", depth, depth, "", entry->name, (entry->flags & SC_ASN1_PRESENT)? "" : " (not present)"); if (!(entry->flags & SC_ASN1_PRESENT)) goto no_object; sc_debug(ctx, SC_LOG_DEBUG_ASN1, "%*.*stype=%d, tag=0x%02x, parm=%p, len=%u\n", depth, depth, "", entry->type, entry->tag, parm, len? *len : 0); if (entry->type == SC_ASN1_CHOICE) { const struct sc_asn1_entry *list, *choice = NULL; list = (const struct sc_asn1_entry *) parm; while (list->name != NULL) { if (list->flags & SC_ASN1_PRESENT) { if (choice) { sc_debug(ctx, SC_LOG_DEBUG_ASN1, "ASN.1 problem: more than " "one CHOICE when encoding %s: " "%s and %s both present\n", entry->name, choice->name, list->name); return SC_ERROR_INVALID_ASN1_OBJECT; } choice = list; } list++; } if (choice == NULL) goto no_object; return asn1_encode_entry(ctx, choice, obj, objlen, depth + 1); } if (entry->type != SC_ASN1_NULL && parm == NULL) { sc_debug(ctx, SC_LOG_DEBUG_ASN1, "unexpected parm == NULL\n"); return SC_ERROR_INVALID_ASN1_OBJECT; } switch (entry->type) { case SC_ASN1_STRUCT: r = asn1_encode(ctx, (const struct sc_asn1_entry *) parm, &buf, &buflen, depth + 1); break; case SC_ASN1_NULL: buf = NULL; buflen = 0; break; case SC_ASN1_BOOLEAN: buf = malloc(1); if (buf == NULL) { r = SC_ERROR_OUT_OF_MEMORY; break; } buf[0] = *((int *) parm) ? 0xFF : 0; buflen = 1; break; case SC_ASN1_INTEGER: case SC_ASN1_ENUMERATED: r = asn1_encode_integer(*((int *) entry->parm), &buf, &buflen); break; case SC_ASN1_BIT_STRING_NI: case SC_ASN1_BIT_STRING: assert(len != NULL); if (entry->type == SC_ASN1_BIT_STRING) r = encode_bit_string((const u8 *) parm, *len, &buf, &buflen, 1); else r = encode_bit_string((const u8 *) parm, *len, &buf, &buflen, 0); break; case SC_ASN1_BIT_FIELD: assert(len != NULL); r = encode_bit_field((const u8 *) parm, *len, &buf, &buflen); break; case SC_ASN1_PRINTABLESTRING: case SC_ASN1_OCTET_STRING: case SC_ASN1_UTF8STRING: assert(len != NULL); buf = malloc(*len + 1); if (buf == NULL) { r = SC_ERROR_OUT_OF_MEMORY; break; } buflen = 0; /* If the integer is supposed to be unsigned, insert * a padding byte if the MSB is one */ if ((entry->flags & SC_ASN1_UNSIGNED) && (((u8 *) parm)[0] & 0x80)) { buf[buflen++] = 0x00; } memcpy(buf + buflen, parm, *len); buflen += *len; break; case SC_ASN1_GENERALIZEDTIME: assert(len != NULL); buf = malloc(*len); if (buf == NULL) { r = SC_ERROR_OUT_OF_MEMORY; break; } memcpy(buf, parm, *len); buflen = *len; break; case SC_ASN1_OBJECT: r = sc_asn1_encode_object_id(&buf, &buflen, (struct sc_object_id *) parm); break; case SC_ASN1_PATH: r = asn1_encode_path(ctx, (const sc_path_t *) parm, &buf, &buflen, depth, entry->flags); break; case SC_ASN1_PKCS15_ID: { const struct sc_pkcs15_id *id = (const struct sc_pkcs15_id *) parm; buf = malloc(id->len); if (buf == NULL) { r = SC_ERROR_OUT_OF_MEMORY; break; } memcpy(buf, id->value, id->len); buflen = id->len; } break; case SC_ASN1_PKCS15_OBJECT: r = asn1_encode_p15_object(ctx, (const struct sc_asn1_pkcs15_object *) parm, &buf, &buflen, depth); break; case SC_ASN1_ALGORITHM_ID: r = sc_asn1_encode_algorithm_id(ctx, &buf, &buflen, (const struct sc_algorithm_id *) parm, depth); break; case SC_ASN1_CALLBACK: r = callback_func(ctx, entry->arg, &buf, &buflen, depth); break; default: sc_debug(ctx, SC_LOG_DEBUG_ASN1, "invalid ASN.1 type: %d\n", entry->type); return SC_ERROR_INVALID_ASN1_OBJECT; } if (r) { sc_debug(ctx, SC_LOG_DEBUG_ASN1, "encoding of ASN.1 object '%s' failed: %s\n", entry->name, sc_strerror(r)); if (buf) free(buf); return r; } /* Treatment of OPTIONAL elements: * - if the encoding has 0 length, and the element is OPTIONAL, * we don't write anything (unless it's an ASN1 NULL and the * SC_ASN1_PRESENT flag is set). * - if the encoding has 0 length, but the element is non-OPTIONAL, * constructed, we write a empty element (e.g. a SEQUENCE of * length 0). In case of an ASN1 NULL just write the tag and * length (i.e. 0x05,0x00). * - any other empty objects are considered bogus */ no_object: if (!buflen && entry->flags & SC_ASN1_OPTIONAL && !(entry->flags & SC_ASN1_PRESENT)) { /* This happens when we try to encode e.g. the * subClassAttributes, which may be empty */ *obj = NULL; *objlen = 0; r = 0; } else if (!buflen && (entry->flags & SC_ASN1_EMPTY_ALLOWED)) { *obj = NULL; *objlen = 0; r = asn1_write_element(ctx, entry->tag, buf, buflen, obj, objlen); if (r) sc_debug(ctx, SC_LOG_DEBUG_ASN1, "error writing ASN.1 tag and length: %s\n", sc_strerror(r)); } else if (buflen || entry->type == SC_ASN1_NULL || entry->tag & SC_ASN1_CONS) { r = asn1_write_element(ctx, entry->tag, buf, buflen, obj, objlen); if (r) sc_debug(ctx, SC_LOG_DEBUG_ASN1, "error writing ASN.1 tag and length: %s\n", sc_strerror(r)); } else if (!(entry->flags & SC_ASN1_PRESENT)) { sc_debug(ctx, SC_LOG_DEBUG_ASN1, "cannot encode non-optional ASN.1 object: not given by caller\n"); r = SC_ERROR_INVALID_ASN1_OBJECT; } else { sc_debug(ctx, SC_LOG_DEBUG_ASN1, "cannot encode empty non-optional ASN.1 object\n"); r = SC_ERROR_INVALID_ASN1_OBJECT; } if (buf) free(buf); if (r >= 0) sc_debug(ctx, SC_LOG_DEBUG_ASN1, "%*.*slength of encoded item=%u\n", depth, depth, "", *objlen); return r; } static int asn1_encode(sc_context_t *ctx, const struct sc_asn1_entry *asn1, u8 **ptr, size_t *size, int depth) { int r, idx = 0; u8 *obj = NULL, *buf = NULL, *tmp; size_t total = 0, objsize; for (idx = 0; asn1[idx].name != NULL; idx++) { r = asn1_encode_entry(ctx, &asn1[idx], &obj, &objsize, depth); if (r) { if (obj) free(obj); if (buf) free(buf); return r; } /* in case of an empty (optional) element continue with * the next asn1 element */ if (!objsize) continue; tmp = (u8 *) realloc(buf, total + objsize); if (!tmp) { if (obj) free(obj); if (buf) free(buf); return SC_ERROR_OUT_OF_MEMORY; } buf = tmp; memcpy(buf + total, obj, objsize); free(obj); obj = NULL; total += objsize; } *ptr = buf; *size = total; return 0; } int sc_asn1_encode(sc_context_t *ctx, const struct sc_asn1_entry *asn1, u8 **ptr, size_t *size) { return asn1_encode(ctx, asn1, ptr, size, 0); } int _sc_asn1_encode(sc_context_t *ctx, const struct sc_asn1_entry *asn1, u8 **ptr, size_t *size, int depth) { return asn1_encode(ctx, asn1, ptr, size, depth); } int _sc_asn1_decode(sc_context_t *ctx, struct sc_asn1_entry *asn1, const u8 *in, size_t len, const u8 **newp, size_t *left, int choice, int depth) { return asn1_decode(ctx, asn1, in, len, newp, left, choice, depth); } int sc_der_copy(sc_pkcs15_der_t *dst, const sc_pkcs15_der_t *src) { memset(dst, 0, sizeof(*dst)); if (src->len) { dst->value = malloc(src->len); if (!dst->value) return SC_ERROR_OUT_OF_MEMORY; dst->len = src->len; memcpy(dst->value, src->value, src->len); } return SC_SUCCESS; } int sc_encode_oid (struct sc_context *ctx, struct sc_object_id *id, unsigned char **out, size_t *size) { static const struct sc_asn1_entry c_asn1_object_id[2] = { { "oid", SC_ASN1_OBJECT, SC_ASN1_TAG_OBJECT, SC_ASN1_ALLOC, NULL, NULL }, { NULL, 0, 0, 0, NULL, NULL } }; struct sc_asn1_entry asn1_object_id[2]; int rv; sc_copy_asn1_entry(c_asn1_object_id, asn1_object_id); sc_format_asn1_entry(asn1_object_id + 0, id, NULL, 1); rv = _sc_asn1_encode(ctx, asn1_object_id, out, size, 1); LOG_TEST_RET(ctx, rv, "Cannot encode object ID"); return SC_SUCCESS; }