/* * 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 */ #if HAVE_CONFIG_H #include "config.h" #endif #include #include #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 int asn1_write_element(sc_context_t *ctx, unsigned int tag, const u8 * data, size_t datalen, u8 ** out, size_t * outlen); static const char *tag2str(unsigned int tag) { static const char *tags[] = { "EOC", "BOOLEAN", "INTEGER", "BIT STRING", "OCTET STRING", /* 0-4 */ "NULL", "OBJECT IDENTIFIER", "OBJECT DESCRIPTOR", "EXTERNAL", "REAL", /* 5-9 */ "ENUMERATED", "Universal 11", "UTF8String", "Universal 13", /* 10-13 */ "Universal 14", "Universal 15", "SEQUENCE", "SET", /* 15-17 */ "NumericString", "PrintableString", "T61String", /* 18-20 */ "VideotexString", "IA5String", "UTCTIME", "GENERALIZEDTIME", /* 21-24 */ "GraphicString", "VisibleString", "GeneralString", /* 25-27 */ "UniversalString", "Universal 29", "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; *buf = NULL; if (left == 0 || !p) return SC_ERROR_INVALID_ASN1_OBJECT; if (*p == 0xff || *p == 0) { /* end of data reached */ *taglen = 0; *tag_out = SC_ASN1_TAG_EOC; return SC_SUCCESS; } /* parse tag byte(s) * Resulted tag is presented by integer that has not to be * confused with the 'tag number' part of ASN.1 tag. */ 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 = SC_ASN1_TAGNUM_SIZE - 1; /* search the last tag octet */ do { if (left == 0 || n == 0) /* either an invalid tag or it doesn't fit in * unsigned int */ return SC_ERROR_INVALID_ASN1_OBJECT; tag <<= 8; tag |= *p; p++; left--; n--; } while (tag & 0x80); } /* parse length byte(s) */ if (left == 0) return SC_ERROR_INVALID_ASN1_OBJECT; len = *p; p++; left--; if (len & 0x80) { len &= 0x7f; unsigned int a = 0; if (len > sizeof a || len > left) return SC_ERROR_INVALID_ASN1_OBJECT; for (i = 0; i < len; i++) { a <<= 8; a |= *p; p++; left--; } len = a; } *cla_out = cla; *tag_out = tag; *taglen = len; *buf = p; if (len > left) return SC_ERROR_ASN1_END_OF_CONTENTS; 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 print_indent(size_t depth) { for (; depth > 0; depth--) { putchar(' '); } } static void print_hex(const u8 * buf, size_t buflen, size_t depth) { size_t lines_len = buflen * 5 + 128; char *lines = malloc(lines_len); char *line = lines; if (buf == NULL || buflen == 0 || lines == NULL) { free(lines); return; } sc_hex_dump(buf, buflen, lines, lines_len); while (*line != '\0') { char *line_end = strchr(line, '\n'); ptrdiff_t width = line_end - line; if (!line_end || width <= 1) { /* don't print empty lines */ break; } if (buflen > 8) { putchar('\n'); print_indent(depth); } else { printf(": "); } printf("%.*s", (int) width, line); line = line_end + 1; } free(lines); } static void print_ascii(const u8 * buf, size_t buflen) { for (; 0 < buflen; buflen--, buf++) { if (isprint(*buf)) printf("%c", *buf); else putchar('.'); } } static void sc_asn1_print_octet_string(const u8 * buf, size_t buflen, size_t depth) { print_hex(buf, buflen, depth); } static void sc_asn1_print_utf8string(const u8 * buf, size_t buflen) { /* FIXME UTF-8 is not ASCII */ print_ascii(buf, buflen); } static void sc_asn1_print_integer(const u8 * buf, size_t buflen) { size_t a = 0; if (buflen > sizeof(a)) { printf("0x%s", sc_dump_hex(buf, buflen)); } else { size_t i; for (i = 0; i < buflen; i++) { a <<= 8; a |= buf[i]; } printf("%"SC_FORMAT_LEN_SIZE_T"u", 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, size_t depth) { #ifndef _WIN32 long long a = 0; #else __int64 a = 0; #endif int r, i; if (buflen > sizeof(a) + 1) { print_hex(buf, buflen, depth); } else { 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'); } } } #ifdef ENABLE_OPENSSL #include static void openssl_print_object_sn(const char *s) { ASN1_OBJECT *obj = OBJ_txt2obj(s, 0); if (obj) { int nid = OBJ_obj2nid(obj); if (nid != NID_undef) { printf(", %s", OBJ_nid2sn(nid)); } ASN1_OBJECT_free(obj); } } #else static void openssl_print_object_sn(const char *s) { } #endif static void sc_asn1_print_object_id(const u8 * buf, size_t buflen) { struct sc_object_id oid; const char *sbuf; if (sc_asn1_decode_object_id(buf, buflen, &oid)) { printf("decode error"); return; } sbuf = sc_dump_oid(&oid); printf(" %s", sbuf); openssl_print_object_sn(sbuf); } static void sc_asn1_print_utctime(const u8 * buf, size_t buflen) { if (buflen < 8) { printf("Error in decoding.\n"); return; } print_ascii(buf, 2); /* YY */ putchar('-'); print_ascii(buf+2, 2); /* MM */ putchar('-'); print_ascii(buf+4, 2); /* DD */ putchar(' '); print_ascii(buf+6, 2); /* hh */ buf += 8; buflen -= 8; if (buflen >= 2 && isdigit(buf[0]) && isdigit(buf[1])) { putchar(':'); print_ascii(buf, 2); /* mm */ buf += 2; buflen -= 2; } if (buflen >= 2 && isdigit(buf[0]) && isdigit(buf[1])) { putchar(':'); print_ascii(buf, 2); /* ss */ buf += 2; buflen -= 2; } if (buflen >= 4 && '.' == buf[0]) { print_ascii(buf, 4); /* fff */ buf += 4; buflen -= 4; } if (buflen >= 1 && 'Z' == buf[0]) { printf(" UTC"); } else if (buflen >= 5 && ('-' == buf[0] || '+' == buf[0])) { putchar(' '); print_ascii(buf, 3); /* +/-hh */ putchar(':'); print_ascii(buf+3, 2); /* mm */ } } static void sc_asn1_print_generalizedtime(const u8 * buf, size_t buflen) { if (buflen < 8) { printf("Error in decoding.\n"); return; } print_ascii(buf, 2); sc_asn1_print_utctime(buf + 2, buflen - 2); } static void print_tags_recursive(const u8 * buf0, const u8 * buf, size_t buflen, size_t depth) { int r; size_t i; size_t bytesleft = buflen; const char *classes[4] = { "Universal", "Application", "Context", "Private" }; 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 || (tagp == NULL && tag != SC_ASN1_TAG_EOC)) { printf("Error in decoding.\n"); return; } hlen = tagp - p; if (cla == 0 && tag == 0) { printf("Zero tag, finishing\n"); break; } print_indent(depth); /* let i be the length of the tag in bytes */ for (i = 1; i < sizeof tag - 1; i++) { if (!(tag >> 8*i)) break; } printf("%02X", cla<<(i-1)*8 | tag); if ((cla & SC_ASN1_TAG_CLASS) == SC_ASN1_TAG_UNIVERSAL) { printf(" %s", tag2str(tag)); } else { printf(" %s %-2u", classes[cla >> 6], i == 1 ? tag & SC_ASN1_TAG_PRIMITIVE : tag & (((unsigned int) ~0) >> (i-1)*8)); } if (!((cla & SC_ASN1_TAG_CLASS) == SC_ASN1_TAG_UNIVERSAL && tag == SC_ASN1_TAG_NULL && len == 0)) { printf(" (%"SC_FORMAT_LEN_SIZE_T"u byte%s)", len, len != 1 ? "s" : ""); } if (len + hlen > bytesleft) { printf(" Illegal length!\n"); return; } p += hlen + len; bytesleft -= hlen + len; if (cla & SC_ASN1_TAG_CONSTRUCTED) { putchar('\n'); print_tags_recursive(buf0, tagp, len, depth + 2*i + 1); continue; } switch (tag) { case SC_ASN1_TAG_BIT_STRING: printf(": "); sc_asn1_print_bit_string(tagp, len, depth + 2*i + 1); break; case SC_ASN1_TAG_OCTET_STRING: sc_asn1_print_octet_string(tagp, len, depth + 2*i + 1); break; case SC_ASN1_TAG_OBJECT: printf(": "); sc_asn1_print_object_id(tagp, len); break; case SC_ASN1_TAG_INTEGER: case SC_ASN1_TAG_ENUMERATED: printf(": "); sc_asn1_print_integer(tagp, len); break; case SC_ASN1_TAG_IA5STRING: case SC_ASN1_TAG_PRINTABLESTRING: case SC_ASN1_TAG_T61STRING: case SC_ASN1_TAG_UTF8STRING: printf(": "); sc_asn1_print_utf8string(tagp, len); break; case SC_ASN1_TAG_BOOLEAN: printf(": "); sc_asn1_print_boolean(tagp, len); break; case SC_ASN1_GENERALIZEDTIME: printf(": "); sc_asn1_print_generalizedtime(tagp, len); break; case SC_ASN1_UTCTIME: printf(": "); sc_asn1_print_utctime(tagp, len); break; } if ((cla & SC_ASN1_TAG_CLASS) == SC_ASN1_TAG_APPLICATION) { print_hex(tagp, len, depth + 2*i + 1); } if ((cla & SC_ASN1_TAG_CLASS) == SC_ASN1_TAG_CONTEXT) { print_hex(tagp, len, depth + 2*i + 1); } putchar('\n'); } } void sc_asn1_print_tags(const u8 * buf, size_t 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 = 0, tag, mask = 0xff00; buf = p; /* read a tag */ if (sc_asn1_read_tag(&p, left, &cla, &tag, &taglen) != SC_SUCCESS || p == NULL) 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 */ 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 = 0, tag; if (sc_asn1_read_tag((const u8 **) &p, len, &cla, &tag, &taglen) != SC_SUCCESS || p == NULL) 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 %"SC_FORMAT_LEN_SIZE_T"u while only %"SC_FORMAT_LEN_SIZE_T"u 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 i, count = 0; int zero_bits; size_t octets_left; if (inlen < 1) return SC_ERROR_INVALID_ASN1_OBJECT; /* 8.6.2.3 If the bitstring is empty, there shall be no subsequent octets, * and the initial octet shall be zero. */ if (inlen == 1 && *in != 0) return SC_ERROR_INVALID_ASN1_OBJECT; /* ITU-T Rec. X.690 8.6.2.2: The number shall be in the range zero to seven. */ if ((*in & ~0x07) != 0) return SC_ERROR_INVALID_ASN1_OBJECT; memset(outbuf, 0, outlen); zero_bits = *in & 0x07; in++; octets_left = inlen - 1; if (outlen < octets_left) return SC_ERROR_BUFFER_TOO_SMALL; 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 && zero_bits > 0) { bits_to_go = 8 - zero_bits; /* Verify the padding is zero bits */ if (*in & (1 << (zero_bits-1))) { return SC_ERROR_INVALID_ASN1_OBJECT; } } 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 |= ((unsigned int) 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 strict) { int a = 0, is_negative = 0; size_t i = 0; if (inlen == 0) { return SC_ERROR_INVALID_ASN1_OBJECT; } if (inlen > sizeof(int)) { return SC_ERROR_NOT_SUPPORTED; } if (inbuf[0] & 0x80) { if (strict && inlen > 1 && inbuf[0] == 0xff && (inbuf[1] & 0x80)) { return SC_ERROR_INVALID_ASN1_OBJECT; } is_negative = 1; a |= 0xff^(*inbuf++); i = 1; } else { if (strict && inlen > 1 && inbuf[0] == 0x00 && (inbuf[1] & 0x80) == 0) { return SC_ERROR_INVALID_ASN1_OBJECT; } } for (; i < inlen; i++) { if (a > (INT_MAX >> 8) || a < (INT_MIN + (1<<8))) { return SC_ERROR_NOT_SUPPORTED; } a <<= 8; if (is_negative) { a |= 0xff^(*inbuf++); } else { a |= *inbuf++; } } if (is_negative) { /* Calculate Two's complement from previously positive number */ a = (-1 * a) - 1; } *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 large_second_octet = 0; unsigned int a = 0; const u8 *p = inbuf; int *octet; if (inlen == 0 || inbuf == NULL || id == NULL) return SC_ERROR_INVALID_ARGUMENTS; sc_init_oid(id); octet = id->value; /* The first octet can be 0, 1 or 2 and is derived from the first byte */ a = MIN(*p / 40, 2); *octet++ = a; /* The second octet fits here if the previous was 0 or 1 and second one is smaller than 40. * for the value 2 we can go up to 47. Otherwise the first bit needs to be set * and we continue reading further */ if ((*p & 0x80) == 0) { *octet++ = *p - (a * 40); inlen--; } else { large_second_octet = 1; } while (inlen) { if (!large_second_octet) p++; /* This signalizes empty most significant bits, which means * the unsigned integer encoding is not minimal */ if (*p == 0x80) { sc_init_oid(id); return SC_ERROR_INVALID_ASN1_OBJECT; } /* Use unsigned type here so we can process the whole INT range. * Values can not be negative */ a = *p & 0x7F; inlen--; while (inlen && *p & 0x80) { /* Limit the OID values to int size and do not overflow */ if (a > (UINT_MAX>>7)) { sc_init_oid(id); return SC_ERROR_NOT_SUPPORTED; } p++; a <<= 7; a |= *p & 0x7F; inlen--; } if (*p & 0x80) { /* We dropped out from previous cycle on the end of * data while still expecting continuation of value */ sc_init_oid(id); return SC_ERROR_INVALID_ASN1_OBJECT; } if (large_second_octet) { a -= (2 * 40); } if (a > INT_MAX) { sc_init_oid(id); return SC_ERROR_NOT_SUPPORTED; } *octet++ = a; if (octet - id->value >= SC_MAX_OBJECT_ID_OCTETS) { sc_init_oid(id); return SC_ERROR_INVALID_ASN1_OBJECT; } large_second_octet = 0; } 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; int i; if (!buflen || !id) return SC_ERROR_INVALID_ARGUMENTS; /* an OID must have at least two components */ if (id->value[0] == -1 || id->value[1] == -1) return SC_ERROR_INVALID_ARGUMENTS; for (i = 0; i < SC_MAX_OBJECT_ID_OCTETS; i++) { unsigned int k, shift; if (id->value[i] == -1) break; k = id->value[i]; switch (i) { case 0: if (k > 2) return SC_ERROR_INVALID_ARGUMENTS; *p = k * 40; break; case 1: if (k > 39 && id->value[0] < 2) { return SC_ERROR_INVALID_ARGUMENTS; } /* We can encode larger IDs to multiple bytes * similarly as the following IDs */ k += *p; /* fall through */ default: shift = 28; while (shift && (k >> shift) == 0) shift -= 7; while (shift) { *p++ = 0x80 | ((k >> shift) & 0x7f); shift -= 7; } *p++ = k & 0x7F; break; } } *buflen = p - temp; if (buf) { *buf = malloc(*buflen); if (!*buf) return SC_ERROR_OUT_OF_MEMORY; memcpy(*buf, temp, *buflen); } 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; } /* * This assumes the tag is already encoded */ int sc_asn1_put_tag(unsigned int tag, const u8 * data, size_t datalen, u8 * out, size_t outlen, u8 **ptr) { size_t c = 0; size_t tag_len; size_t ii; u8 *p = out; u8 tag_char[4] = {0, 0, 0, 0}; /* Check tag */ if (tag == 0 || tag > 0xFFFFFFFF) { /* A tag of 0x00 is not valid and at most 4-byte tag names are supported. */ return SC_ERROR_INVALID_DATA; } for (tag_len = 0; tag; tag >>= 8) { /* Note: tag char will be reversed order. */ tag_char[tag_len++] = tag & 0xFF; } if (tag_len > 1) { if ((tag_char[tag_len - 1] & SC_ASN1_TAG_PRIMITIVE) != SC_ASN1_TAG_ESCAPE_MARKER) { /* First byte is not escape marker. */ return SC_ERROR_INVALID_DATA; } for (ii = 1; ii < tag_len - 1; ii++) { if ((tag_char[ii] & 0x80) != 0x80) { /* MS bit is not 'one'. */ return SC_ERROR_INVALID_DATA; } } if ((tag_char[0] & 0x80) != 0x00) { /* MS bit of the last byte is not 'zero'. */ return SC_ERROR_INVALID_DATA; } } /* Calculate the number of additional bytes necessary to encode the length. */ /* c+1 is the size of the length field. */ if (datalen > 127) { c = 1; while (datalen >> (c << 3)) c++; } if (outlen == 0 || out == NULL) { /* Caller only asks for the length that would be written. */ return tag_len + (c+1) + datalen; } /* We will write the tag, so check the length. */ if (outlen < tag_len + (c+1) + datalen) return SC_ERROR_BUFFER_TOO_SMALL; for (ii=0;ii 0) { *p++ = 0x80 | c; while (c--) *p++ = (datalen >> (c << 3)) & 0xFF; } else { *p++ = datalen & 0x7F; } if(data && datalen > 0) { memcpy(p, data, datalen); p += datalen; } if (ptr != NULL) *ptr = p; return 0; } int sc_asn1_write_element(sc_context_t *ctx, unsigned int tag, const u8 * data, size_t datalen, u8 ** out, size_t * outlen) { return asn1_write_element(ctx, tag, data, datalen, out, outlen); } 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; } if (datalen && data) { 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[2] = { { "seInfo", SC_ASN1_STRUCT, SC_ASN1_TAG_SEQUENCE | SC_ASN1_CONS, 0, NULL, NULL }, { NULL, 0, 0, 0, NULL, NULL } }; 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) { struct sc_pkcs15_sec_env_info **ses; const unsigned char *ptr = obj; size_t idx, ptrlen = objlen; int ret; LOG_FUNC_CALLED(ctx); ses = calloc(SC_MAX_SE_NUM, sizeof(sc_pkcs15_sec_env_info_t *)); if (ses == NULL) { SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_ASN1, SC_ERROR_OUT_OF_MEMORY); } for (idx=0; idx < SC_MAX_SE_NUM && ptrlen; ) { struct sc_asn1_entry asn1_se[2]; struct sc_asn1_entry asn1_se_info[4]; struct sc_pkcs15_sec_env_info si; sc_copy_asn1_entry(c_asn1_se, asn1_se); sc_copy_asn1_entry(c_asn1_se_info, asn1_se_info); si.aid.len = sizeof(si.aid.value); 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); sc_format_asn1_entry(asn1_se + 0, asn1_se_info, NULL, 0); ret = asn1_decode(ctx, asn1_se, ptr, ptrlen, &ptr, &ptrlen, 0, depth+1); if (ret != SC_SUCCESS) goto err; if (!(asn1_se_info[1].flags & SC_ASN1_PRESENT)) sc_init_oid(&si.owner); ses[idx] = calloc(1, sizeof(sc_pkcs15_sec_env_info_t)); if (ses[idx] == NULL) { ret = SC_ERROR_OUT_OF_MEMORY; goto err; } memcpy(ses[idx], &si, sizeof(struct sc_pkcs15_sec_env_info)); idx++; } *se = ses; *num = idx; ret = SC_SUCCESS; err: if (ret != SC_SUCCESS) { size_t i; for (i = 0; i < idx; i++) if (ses[i]) free(ses[i]); free(ses); } SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_ASN1, ret); } static int asn1_encode_se_info(sc_context_t *ctx, struct sc_pkcs15_sec_env_info **se, size_t se_num, unsigned char **buf, size_t *bufsize, int depth) { unsigned char *ptr = NULL, *out = NULL, *p; size_t ptrlen = 0, outlen = 0, idx; int ret; for (idx=0; idx < se_num; idx++) { struct sc_asn1_entry asn1_se[2]; struct sc_asn1_entry asn1_se_info[4]; sc_copy_asn1_entry(c_asn1_se, asn1_se); sc_copy_asn1_entry(c_asn1_se_info, asn1_se_info); sc_format_asn1_entry(asn1_se_info + 0, &se[idx]->se, NULL, 1); if (sc_valid_oid(&se[idx]->owner)) sc_format_asn1_entry(asn1_se_info + 1, &se[idx]->owner, NULL, 1); if (se[idx]->aid.len) sc_format_asn1_entry(asn1_se_info + 2, &se[idx]->aid.value, &se[idx]->aid.len, 1); sc_format_asn1_entry(asn1_se + 0, asn1_se_info, NULL, 1); ret = sc_asn1_encode(ctx, asn1_se, &ptr, &ptrlen); if (ret != SC_SUCCESS) goto err; if (!ptrlen) continue; p = (unsigned char *) realloc(out, outlen + ptrlen); if (!p) { ret = SC_ERROR_OUT_OF_MEMORY; goto err; } out = p; memcpy(out + outlen, ptr, ptrlen); outlen += ptrlen; free(ptr); ptr = NULL; ptrlen = 0; } *buf = out; *bufsize = outlen; ret = SC_SUCCESS; err: if (ret != SC_SUCCESS && out != NULL) free(out); return ret; } /* TODO: According to specification type of 'SecurityCondition' is 'CHOICE'. * Do it at least for SC_ASN1_PKCS15_ID(authId), SC_ASN1_STRUCT(authReference) and NULL(always). */ 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 && obj->asn1_subclass_attr->name) 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', raw data:%s%s\n", depth, depth, "", entry->name, sc_dump_hex(obj, objlen > 16 ? 16 : objlen), objlen > 16 ? "..." : ""); 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: %"SC_FORMAT_LEN_SIZE_T"u\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, 0); 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; if (objlen > 1) { *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) && objlen > 1 && obj[0] == 0x00) { objlen--; obj++; } /* Allocate buffer if needed */ if (entry->flags & SC_ASN1_ALLOC) { u8 **buf = (u8 **) parm; if (objlen > 0) { *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; if (objlen > 0) { *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=%"SC_FORMAT_LEN_SIZE_T"u, depth %d%s\n", depth, depth, "", left, depth, choice ? ", choice" : ""); if (!p) return SC_ERROR_ASN1_OBJECT_NOT_FOUND; 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, "'%s' not present\n", entry->name); 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=%"SC_FORMAT_LEN_SIZE_T"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: if (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); } else { r = SC_ERROR_INVALID_ARGUMENTS; } break; case SC_ASN1_BIT_FIELD: if (len != NULL) { r = encode_bit_field((const u8 *) parm, *len, &buf, &buflen); } else { r = SC_ERROR_INVALID_ARGUMENTS; } break; case SC_ASN1_PRINTABLESTRING: case SC_ASN1_OCTET_STRING: case SC_ASN1_UTF8STRING: if (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; } else { r = SC_ERROR_INVALID_ARGUMENTS; } break; case SC_ASN1_GENERALIZEDTIME: if (len != NULL) { buf = malloc(*len); if (buf == NULL) { r = SC_ERROR_OUT_OF_MEMORY; break; } memcpy(buf, parm, *len); buflen = *len; } else { r = SC_ERROR_INVALID_ARGUMENTS; } 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_SE_INFO: if (!len) return SC_ERROR_INVALID_ASN1_OBJECT; r = asn1_encode_se_info(ctx, (struct sc_pkcs15_sec_env_info **)parm, *len, &buf, &buflen, 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=%"SC_FORMAT_LEN_SIZE_T"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; if (asn1 == NULL) { return SC_ERROR_INVALID_ARGUMENTS; } 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) { if (!dst) return SC_ERROR_INVALID_ARGUMENTS; 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; } #define C_ASN1_SIG_VALUE_SIZE 2 static struct sc_asn1_entry c_asn1_sig_value[C_ASN1_SIG_VALUE_SIZE] = { { "ECDSA-Sig-Value", SC_ASN1_STRUCT, SC_ASN1_TAG_SEQUENCE | SC_ASN1_CONS, 0, NULL, NULL }, { NULL, 0, 0, 0, NULL, NULL } }; #define C_ASN1_SIG_VALUE_COEFFICIENTS_SIZE 3 static struct sc_asn1_entry c_asn1_sig_value_coefficients[C_ASN1_SIG_VALUE_COEFFICIENTS_SIZE] = { { "r", SC_ASN1_OCTET_STRING, SC_ASN1_TAG_INTEGER, SC_ASN1_ALLOC|SC_ASN1_UNSIGNED, NULL, NULL }, { "s", SC_ASN1_OCTET_STRING, SC_ASN1_TAG_INTEGER, SC_ASN1_ALLOC|SC_ASN1_UNSIGNED, NULL, NULL }, { NULL, 0, 0, 0, NULL, NULL } }; int sc_asn1_sig_value_rs_to_sequence(struct sc_context *ctx, unsigned char *in, size_t inlen, unsigned char **buf, size_t *buflen) { struct sc_asn1_entry asn1_sig_value[C_ASN1_SIG_VALUE_SIZE]; struct sc_asn1_entry asn1_sig_value_coefficients[C_ASN1_SIG_VALUE_COEFFICIENTS_SIZE]; unsigned char *r = in, *s = in + inlen/2; size_t r_len = inlen/2, s_len = inlen/2; int rv; LOG_FUNC_CALLED(ctx); /* R/S are filled up with zeroes, we do not want that in sequence format */ while(r_len > 1 && *r == 0x00) { r++; r_len--; } while(s_len > 1 && *s == 0x00) { s++; s_len--; } sc_copy_asn1_entry(c_asn1_sig_value, asn1_sig_value); sc_format_asn1_entry(asn1_sig_value + 0, asn1_sig_value_coefficients, NULL, 1); sc_copy_asn1_entry(c_asn1_sig_value_coefficients, asn1_sig_value_coefficients); sc_format_asn1_entry(asn1_sig_value_coefficients + 0, r, &r_len, 1); sc_format_asn1_entry(asn1_sig_value_coefficients + 1, s, &s_len, 1); rv = sc_asn1_encode(ctx, asn1_sig_value, buf, buflen); LOG_TEST_RET(ctx, rv, "ASN.1 encoding ECDSA-SIg-Value failed"); LOG_FUNC_RETURN(ctx, SC_SUCCESS); } int sc_asn1_sig_value_sequence_to_rs(struct sc_context *ctx, const unsigned char *in, size_t inlen, unsigned char *buf, size_t buflen) { struct sc_asn1_entry asn1_sig_value[C_ASN1_SIG_VALUE_SIZE]; struct sc_asn1_entry asn1_sig_value_coefficients[C_ASN1_SIG_VALUE_COEFFICIENTS_SIZE]; unsigned char *r = NULL, *s = NULL; size_t r_len = 0, s_len = 0, halflen = buflen/2; int rv; LOG_FUNC_CALLED(ctx); if (!buf || !buflen) LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_ARGUMENTS); sc_copy_asn1_entry(c_asn1_sig_value, asn1_sig_value); sc_format_asn1_entry(asn1_sig_value + 0, asn1_sig_value_coefficients, NULL, 0); sc_copy_asn1_entry(c_asn1_sig_value_coefficients, asn1_sig_value_coefficients); sc_format_asn1_entry(asn1_sig_value_coefficients + 0, &r, &r_len, 0); sc_format_asn1_entry(asn1_sig_value_coefficients + 1, &s, &s_len, 0); rv = sc_asn1_decode(ctx, asn1_sig_value, in, inlen, NULL, NULL); LOG_TEST_GOTO_ERR(ctx, rv, "ASN.1 decoding ECDSA-Sig-Value failed"); if (halflen < r_len || halflen < s_len) { rv = SC_ERROR_BUFFER_TOO_SMALL; goto err; } memset(buf, 0, buflen); if (r_len > 0) memcpy(buf + (halflen - r_len), r, r_len); if (s_len > 0) memcpy(buf + (buflen - s_len), s, s_len); sc_log(ctx, "r(%"SC_FORMAT_LEN_SIZE_T"u): %s", halflen, sc_dump_hex(buf, halflen)); sc_log(ctx, "s(%"SC_FORMAT_LEN_SIZE_T"u): %s", halflen, sc_dump_hex(buf + halflen, halflen)); rv = SC_SUCCESS; err: free(r); free(s); LOG_FUNC_RETURN(ctx, rv); }