opensc/src/libopensc/asn1.c

2074 lines
55 KiB
C
Raw Normal View History

/*
* asn1.c: ASN.1 decoding functions (DER)
*
* Copyright (C) 2001, 2002 Juha Yrjölä <juha.yrjola@iki.fi>
*
* 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
*/
2015-04-22 21:55:33 +00:00
#if HAVE_CONFIG_H
#include "config.h"
2015-04-22 21:55:33 +00:00
#endif
#include <assert.h>
2017-11-13 14:12:43 +00:00
#include <ctype.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
2017-11-13 14:12:43 +00:00
#include <string.h>
#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;
if (left < 2)
return SC_ERROR_INVALID_ASN1_OBJECT;
*buf = NULL;
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 */
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;
}
/* 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;
}
*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;
}
2017-11-13 14:12:43 +00:00
static void print_indent(size_t depth)
{
2017-11-13 14:12:43 +00:00
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);
2017-11-13 14:12:43 +00:00
return;
}
2017-11-13 14:12:43 +00:00
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;
}
2017-11-13 14:12:43 +00:00
free(lines);
}
2017-11-13 14:12:43 +00:00
static void print_ascii(const u8 * buf, size_t buflen)
{
2017-11-13 14:12:43 +00:00
for (; 0 < buflen; buflen--, buf++) {
if (isprint(*buf))
printf("%c", *buf);
else
putchar('.');
}
}
2017-11-13 14:12:43 +00:00
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)
{
2017-11-13 14:12:43 +00:00
size_t a = 0;
if (buflen > sizeof(a)) {
2017-11-13 14:12:43 +00:00
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");
}
2017-11-13 14:12:43 +00:00
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) {
2017-11-13 14:12:43 +00:00
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 <openssl/objects.h>
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) {
2017-11-13 14:12:43 +00:00
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;
2017-11-13 14:12:43 +00:00
const char *sbuf;
if (sc_asn1_decode_object_id(buf, buflen, &oid)) {
printf("decode error");
return;
}
2017-11-13 14:12:43 +00:00
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;
}
2017-11-13 14:12:43 +00:00
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;
}
2017-11-13 14:12:43 +00:00
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)
{
2017-11-13 14:12:43 +00:00
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) {
printf("Error in decoding.\n");
return;
}
hlen = tagp - p;
if (cla == 0 && tag == 0) {
printf("Zero tag, finishing\n");
break;
}
2017-11-13 14:12:43 +00:00
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));
}
2017-11-13 14:12:43 +00:00
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;
}
2017-11-13 14:12:43 +00:00
switch (tag) {
case SC_ASN1_TAG_BIT_STRING:
2017-11-13 14:12:43 +00:00
printf(": ");
sc_asn1_print_bit_string(tagp, len, depth + 2*i + 1);
break;
case SC_ASN1_TAG_OCTET_STRING:
2017-11-13 14:12:43 +00:00
sc_asn1_print_octet_string(tagp, len, depth + 2*i + 1);
break;
case SC_ASN1_TAG_OBJECT:
2017-11-13 14:12:43 +00:00
printf(": ");
sc_asn1_print_object_id(tagp, len);
break;
case SC_ASN1_TAG_INTEGER:
case SC_ASN1_TAG_ENUMERATED:
2017-11-13 14:12:43 +00:00
printf(": ");
sc_asn1_print_integer(tagp, len);
break;
2017-11-13 14:12:43 +00:00
case SC_ASN1_TAG_IA5STRING:
case SC_ASN1_TAG_PRINTABLESTRING:
2017-11-13 14:12:43 +00:00
case SC_ASN1_TAG_T61STRING:
case SC_ASN1_TAG_UTF8STRING:
2017-11-13 14:12:43 +00:00
printf(": ");
sc_asn1_print_utf8string(tagp, len);
break;
case SC_ASN1_TAG_BOOLEAN:
2017-11-13 14:12:43 +00:00
printf(": ");
sc_asn1_print_boolean(tagp, len);
break;
case SC_ASN1_GENERALIZEDTIME:
2017-11-13 14:12:43 +00:00
printf(": ");
sc_asn1_print_generalizedtime(tagp, len);
break;
2017-11-13 14:12:43 +00:00
case SC_ASN1_UTCTIME:
printf(": ");
sc_asn1_print_utctime(tagp, len);
break;
}
2017-11-13 14:12:43 +00:00
if ((cla & SC_ASN1_TAG_CLASS) == SC_ASN1_TAG_APPLICATION) {
print_hex(tagp, len, depth + 2*i + 1);
}
2017-11-13 14:12:43 +00:00
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 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;
Do not cast the return value of malloc(3) and calloc(3) From http://en.wikipedia.org/wiki/Malloc#Casting_and_type_safety " Casting and type safety malloc returns a void pointer (void *), which indicates that it is a pointer to a region of unknown data type. One may "cast" (see type conversion) this pointer to a specific type, as in int *ptr = (int*)malloc(10 * sizeof (int)); When using C, this is considered bad practice; it is redundant under the C standard. Moreover, putting in a cast may mask failure to include the header stdlib.h, in which the prototype for malloc is found. In the absence of a prototype for malloc, the C compiler will assume that malloc returns an int, and will issue a warning in a context such as the above, provided the error is not masked by a cast. On certain architectures and data models (such as LP64 on 64 bit systems, where long and pointers are 64 bit and int is 32 bit), this error can actually result in undefined behavior, as the implicitly declared malloc returns a 32 bit value whereas the actually defined function returns a 64 bit value. Depending on calling conventions and memory layout, this may result in stack smashing. The returned pointer need not be explicitly cast to a more specific pointer type, since ANSI C defines an implicit conversion between the void pointer type and other pointers to objects. An explicit cast of malloc's return value is sometimes performed because malloc originally returned a char *, but this cast is unnecessary in standard C code.[4][5] Omitting the cast, however, creates an incompatibility with C++, which does require it. The lack of a specific pointer type returned from malloc is type-unsafe behaviour: malloc allocates based on byte count but not on type. This distinguishes it from the C++ new operator that returns a pointer whose type relies on the operand. (see C Type Safety). " See also http://www.opensc-project.org/pipermail/opensc-devel/2010-August/014586.html git-svn-id: https://www.opensc-project.org/svnp/opensc/trunk@4636 c6295689-39f2-0310-b995-f0e70906c6a9
2010-08-18 15:08:51 +00:00
*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;
2018-01-09 11:54:09 +00:00
if (inlen > sizeof(int) || inlen == 0)
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;
}
Do not cast the return value of malloc(3) and calloc(3) From http://en.wikipedia.org/wiki/Malloc#Casting_and_type_safety " Casting and type safety malloc returns a void pointer (void *), which indicates that it is a pointer to a region of unknown data type. One may "cast" (see type conversion) this pointer to a specific type, as in int *ptr = (int*)malloc(10 * sizeof (int)); When using C, this is considered bad practice; it is redundant under the C standard. Moreover, putting in a cast may mask failure to include the header stdlib.h, in which the prototype for malloc is found. In the absence of a prototype for malloc, the C compiler will assume that malloc returns an int, and will issue a warning in a context such as the above, provided the error is not masked by a cast. On certain architectures and data models (such as LP64 on 64 bit systems, where long and pointers are 64 bit and int is 32 bit), this error can actually result in undefined behavior, as the implicitly declared malloc returns a 32 bit value whereas the actually defined function returns a 64 bit value. Depending on calling conventions and memory layout, this may result in stack smashing. The returned pointer need not be explicitly cast to a more specific pointer type, since ANSI C defines an implicit conversion between the void pointer type and other pointers to objects. An explicit cast of malloc's return value is sometimes performed because malloc originally returned a char *, but this cast is unnecessary in standard C code.[4][5] Omitting the cast, however, creates an incompatibility with C++, which does require it. The lack of a specific pointer type returned from malloc is type-unsafe behaviour: malloc allocates based on byte count but not on type. This distinguishes it from the C++ new operator that returns a pointer whose type relies on the operand. (see C Type Safety). " See also http://www.opensc-project.org/pipermail/opensc-devel/2010-August/014586.html git-svn-id: https://www.opensc-project.org/svnp/opensc/trunk@4636 c6295689-39f2-0310-b995-f0e70906c6a9
2010-08-18 15:08:51 +00:00
*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 a;
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;
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) {
sc_init_oid(id);
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;
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)
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;
}
}
*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;
}
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<tag_len;ii++)
*p++ = tag_char[tag_len - ii - 1];
if (c > 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;
Do not cast the return value of malloc(3) and calloc(3) From http://en.wikipedia.org/wiki/Malloc#Casting_and_type_safety " Casting and type safety malloc returns a void pointer (void *), which indicates that it is a pointer to a region of unknown data type. One may "cast" (see type conversion) this pointer to a specific type, as in int *ptr = (int*)malloc(10 * sizeof (int)); When using C, this is considered bad practice; it is redundant under the C standard. Moreover, putting in a cast may mask failure to include the header stdlib.h, in which the prototype for malloc is found. In the absence of a prototype for malloc, the C compiler will assume that malloc returns an int, and will issue a warning in a context such as the above, provided the error is not masked by a cast. On certain architectures and data models (such as LP64 on 64 bit systems, where long and pointers are 64 bit and int is 32 bit), this error can actually result in undefined behavior, as the implicitly declared malloc returns a 32 bit value whereas the actually defined function returns a 64 bit value. Depending on calling conventions and memory layout, this may result in stack smashing. The returned pointer need not be explicitly cast to a more specific pointer type, since ANSI C defines an implicit conversion between the void pointer type and other pointers to objects. An explicit cast of malloc's return value is sometimes performed because malloc originally returned a char *, but this cast is unnecessary in standard C code.[4][5] Omitting the cast, however, creates an incompatibility with C++, which does require it. The lack of a specific pointer type returned from malloc is type-unsafe behaviour: malloc allocates based on byte count but not on type. This distinguishes it from the C++ new operator that returns a pointer whose type relies on the operand. (see C Type Safety). " See also http://www.opensc-project.org/pipermail/opensc-devel/2010-August/014586.html git-svn-id: https://www.opensc-project.org/svnp/opensc/trunk@4636 c6295689-39f2-0310-b995-f0e70906c6a9
2010-08-18 15:08:51 +00:00
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<tag_len;ii++)
*p++ = tag_char[tag_len - ii - 1];
if (c) {
*p++ = 0x80 | c;
while (c--)
*p++ = (datalen >> (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[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;
ses = calloc(SC_MAX_SE_NUM, sizeof(sc_pkcs15_sec_env_info_t *));
if (ses == NULL)
return 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);
}
return 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;
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; ii<SC_PKCS15_MAX_ACCESS_RULES; ii++)
sc_copy_asn1_entry(c_asn1_access_control_rule, asn1_ac_rule[ii]);
sc_copy_asn1_entry(c_asn1_access_control_rules, asn1_ac_rules);
sc_copy_asn1_entry(c_asn1_com_obj_attr, asn1_c_attr);
sc_copy_asn1_entry(c_asn1_p15_obj, asn1_p15_obj);
sc_format_asn1_entry(asn1_c_attr + 0, p15_obj->label, &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; ii<SC_PKCS15_MAX_ACCESS_RULES; ii++) {
sc_format_asn1_entry(asn1_ac_rule[ii] + 0, &p15_obj->access_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; ii<SC_PKCS15_MAX_ACCESS_RULES; ii++) {
sc_copy_asn1_entry(c_asn1_access_control_rule, asn1_ac_rule[ii]);
if (p15_obj.access_rules[ii].auth_id.len == 0) {
asn1_ac_rule[ii][1].type = SC_ASN1_NULL;
asn1_ac_rule[ii][1].tag = SC_ASN1_TAG_NULL;
}
}
sc_copy_asn1_entry(c_asn1_access_control_rules, asn1_ac_rules);
}
sc_copy_asn1_entry(c_asn1_com_obj_attr, asn1_c_attr);
sc_copy_asn1_entry(c_asn1_p15_obj, asn1_p15_obj);
if (label_len != 0)
sc_format_asn1_entry(asn1_c_attr + 0, (void *) p15_obj.label, &label_len, 1);
if (p15_obj.flags) {
flags_len = sizeof(p15_obj.flags);
sc_format_asn1_entry(asn1_c_attr + 1, (void *) &p15_obj.flags, &flags_len, 1);
}
if (p15_obj.auth_id.len)
sc_format_asn1_entry(asn1_c_attr + 2, (void *) &p15_obj.auth_id, NULL, 1);
if (p15_obj.user_consent)
sc_format_asn1_entry(asn1_c_attr + 3, (void *) &p15_obj.user_consent, NULL, 1);
if (p15_obj.access_rules[0].access_mode) {
for (ii=0; p15_obj.access_rules[ii].access_mode; ii++) {
access_mode_len = sizeof(p15_obj.access_rules[ii].access_mode);
sc_format_asn1_entry(asn1_ac_rule[ii] + 0, (void *) &p15_obj.access_rules[ii].access_mode, &access_mode_len, 1);
sc_format_asn1_entry(asn1_ac_rule[ii] + 1, (void *) &p15_obj.access_rules[ii].auth_id, NULL, 1);
sc_format_asn1_entry(asn1_ac_rules + ii, asn1_ac_rule[ii], NULL, 1);
}
sc_format_asn1_entry(asn1_c_attr + 4, asn1_ac_rules, NULL, 1);
}
sc_format_asn1_entry(asn1_p15_obj + 0, asn1_c_attr, NULL, 1);
sc_format_asn1_entry(asn1_p15_obj + 1, obj->asn1_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);
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;
Do not cast the return value of malloc(3) and calloc(3) From http://en.wikipedia.org/wiki/Malloc#Casting_and_type_safety " Casting and type safety malloc returns a void pointer (void *), which indicates that it is a pointer to a region of unknown data type. One may "cast" (see type conversion) this pointer to a specific type, as in int *ptr = (int*)malloc(10 * sizeof (int)); When using C, this is considered bad practice; it is redundant under the C standard. Moreover, putting in a cast may mask failure to include the header stdlib.h, in which the prototype for malloc is found. In the absence of a prototype for malloc, the C compiler will assume that malloc returns an int, and will issue a warning in a context such as the above, provided the error is not masked by a cast. On certain architectures and data models (such as LP64 on 64 bit systems, where long and pointers are 64 bit and int is 32 bit), this error can actually result in undefined behavior, as the implicitly declared malloc returns a 32 bit value whereas the actually defined function returns a 64 bit value. Depending on calling conventions and memory layout, this may result in stack smashing. The returned pointer need not be explicitly cast to a more specific pointer type, since ANSI C defines an implicit conversion between the void pointer type and other pointers to objects. An explicit cast of malloc's return value is sometimes performed because malloc originally returned a char *, but this cast is unnecessary in standard C code.[4][5] Omitting the cast, however, creates an incompatibility with C++, which does require it. The lack of a specific pointer type returned from malloc is type-unsafe behaviour: malloc allocates based on byte count but not on type. This distinguishes it from the C++ new operator that returns a pointer whose type relies on the operand. (see C Type Safety). " See also http://www.opensc-project.org/pipermail/opensc-devel/2010-August/014586.html git-svn-id: https://www.opensc-project.org/svnp/opensc/trunk@4636 c6295689-39f2-0310-b995-f0e70906c6a9
2010-08-18 15:08:51 +00:00
*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;
Do not cast the return value of malloc(3) and calloc(3) From http://en.wikipedia.org/wiki/Malloc#Casting_and_type_safety " Casting and type safety malloc returns a void pointer (void *), which indicates that it is a pointer to a region of unknown data type. One may "cast" (see type conversion) this pointer to a specific type, as in int *ptr = (int*)malloc(10 * sizeof (int)); When using C, this is considered bad practice; it is redundant under the C standard. Moreover, putting in a cast may mask failure to include the header stdlib.h, in which the prototype for malloc is found. In the absence of a prototype for malloc, the C compiler will assume that malloc returns an int, and will issue a warning in a context such as the above, provided the error is not masked by a cast. On certain architectures and data models (such as LP64 on 64 bit systems, where long and pointers are 64 bit and int is 32 bit), this error can actually result in undefined behavior, as the implicitly declared malloc returns a 32 bit value whereas the actually defined function returns a 64 bit value. Depending on calling conventions and memory layout, this may result in stack smashing. The returned pointer need not be explicitly cast to a more specific pointer type, since ANSI C defines an implicit conversion between the void pointer type and other pointers to objects. An explicit cast of malloc's return value is sometimes performed because malloc originally returned a char *, but this cast is unnecessary in standard C code.[4][5] Omitting the cast, however, creates an incompatibility with C++, which does require it. The lack of a specific pointer type returned from malloc is type-unsafe behaviour: malloc allocates based on byte count but not on type. This distinguishes it from the C++ new operator that returns a pointer whose type relies on the operand. (see C Type Safety). " See also http://www.opensc-project.org/pipermail/opensc-devel/2010-August/014586.html git-svn-id: https://www.opensc-project.org/svnp/opensc/trunk@4636 c6295689-39f2-0310-b995-f0e70906c6a9
2010-08-18 15:08:51 +00:00
*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;
Do not cast the return value of malloc(3) and calloc(3) From http://en.wikipedia.org/wiki/Malloc#Casting_and_type_safety " Casting and type safety malloc returns a void pointer (void *), which indicates that it is a pointer to a region of unknown data type. One may "cast" (see type conversion) this pointer to a specific type, as in int *ptr = (int*)malloc(10 * sizeof (int)); When using C, this is considered bad practice; it is redundant under the C standard. Moreover, putting in a cast may mask failure to include the header stdlib.h, in which the prototype for malloc is found. In the absence of a prototype for malloc, the C compiler will assume that malloc returns an int, and will issue a warning in a context such as the above, provided the error is not masked by a cast. On certain architectures and data models (such as LP64 on 64 bit systems, where long and pointers are 64 bit and int is 32 bit), this error can actually result in undefined behavior, as the implicitly declared malloc returns a 32 bit value whereas the actually defined function returns a 64 bit value. Depending on calling conventions and memory layout, this may result in stack smashing. The returned pointer need not be explicitly cast to a more specific pointer type, since ANSI C defines an implicit conversion between the void pointer type and other pointers to objects. An explicit cast of malloc's return value is sometimes performed because malloc originally returned a char *, but this cast is unnecessary in standard C code.[4][5] Omitting the cast, however, creates an incompatibility with C++, which does require it. The lack of a specific pointer type returned from malloc is type-unsafe behaviour: malloc allocates based on byte count but not on type. This distinguishes it from the C++ new operator that returns a pointer whose type relies on the operand. (see C Type Safety). " See also http://www.opensc-project.org/pipermail/opensc-devel/2010-August/014586.html git-svn-id: https://www.opensc-project.org/svnp/opensc/trunk@4636 c6295689-39f2-0310-b995-f0e70906c6a9
2010-08-18 15:08:51 +00:00
*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;
Do not cast the return value of malloc(3) and calloc(3) From http://en.wikipedia.org/wiki/Malloc#Casting_and_type_safety " Casting and type safety malloc returns a void pointer (void *), which indicates that it is a pointer to a region of unknown data type. One may "cast" (see type conversion) this pointer to a specific type, as in int *ptr = (int*)malloc(10 * sizeof (int)); When using C, this is considered bad practice; it is redundant under the C standard. Moreover, putting in a cast may mask failure to include the header stdlib.h, in which the prototype for malloc is found. In the absence of a prototype for malloc, the C compiler will assume that malloc returns an int, and will issue a warning in a context such as the above, provided the error is not masked by a cast. On certain architectures and data models (such as LP64 on 64 bit systems, where long and pointers are 64 bit and int is 32 bit), this error can actually result in undefined behavior, as the implicitly declared malloc returns a 32 bit value whereas the actually defined function returns a 64 bit value. Depending on calling conventions and memory layout, this may result in stack smashing. The returned pointer need not be explicitly cast to a more specific pointer type, since ANSI C defines an implicit conversion between the void pointer type and other pointers to objects. An explicit cast of malloc's return value is sometimes performed because malloc originally returned a char *, but this cast is unnecessary in standard C code.[4][5] Omitting the cast, however, creates an incompatibility with C++, which does require it. The lack of a specific pointer type returned from malloc is type-unsafe behaviour: malloc allocates based on byte count but not on type. This distinguishes it from the C++ new operator that returns a pointer whose type relies on the operand. (see C Type Safety). " See also http://www.opensc-project.org/pipermail/opensc-devel/2010-August/014586.html git-svn-id: https://www.opensc-project.org/svnp/opensc/trunk@4636 c6295689-39f2-0310-b995-f0e70906c6a9
2010-08-18 15:08:51 +00:00
*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" : "");
2015-02-05 16:43:14 +00:00
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:
Do not cast the return value of malloc(3) and calloc(3) From http://en.wikipedia.org/wiki/Malloc#Casting_and_type_safety " Casting and type safety malloc returns a void pointer (void *), which indicates that it is a pointer to a region of unknown data type. One may "cast" (see type conversion) this pointer to a specific type, as in int *ptr = (int*)malloc(10 * sizeof (int)); When using C, this is considered bad practice; it is redundant under the C standard. Moreover, putting in a cast may mask failure to include the header stdlib.h, in which the prototype for malloc is found. In the absence of a prototype for malloc, the C compiler will assume that malloc returns an int, and will issue a warning in a context such as the above, provided the error is not masked by a cast. On certain architectures and data models (such as LP64 on 64 bit systems, where long and pointers are 64 bit and int is 32 bit), this error can actually result in undefined behavior, as the implicitly declared malloc returns a 32 bit value whereas the actually defined function returns a 64 bit value. Depending on calling conventions and memory layout, this may result in stack smashing. The returned pointer need not be explicitly cast to a more specific pointer type, since ANSI C defines an implicit conversion between the void pointer type and other pointers to objects. An explicit cast of malloc's return value is sometimes performed because malloc originally returned a char *, but this cast is unnecessary in standard C code.[4][5] Omitting the cast, however, creates an incompatibility with C++, which does require it. The lack of a specific pointer type returned from malloc is type-unsafe behaviour: malloc allocates based on byte count but not on type. This distinguishes it from the C++ new operator that returns a pointer whose type relies on the operand. (see C Type Safety). " See also http://www.opensc-project.org/pipermail/opensc-devel/2010-August/014586.html git-svn-id: https://www.opensc-project.org/svnp/opensc/trunk@4636 c6295689-39f2-0310-b995-f0e70906c6a9
2010-08-18 15:08:51 +00:00
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:
2017-10-30 15:32:57 +00:00
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:
2017-10-30 15:32:57 +00:00
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:
2017-10-30 15:32:57 +00:00
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:
2017-10-30 15:32:57 +00:00
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;
Do not cast the return value of malloc(3) and calloc(3) From http://en.wikipedia.org/wiki/Malloc#Casting_and_type_safety " Casting and type safety malloc returns a void pointer (void *), which indicates that it is a pointer to a region of unknown data type. One may "cast" (see type conversion) this pointer to a specific type, as in int *ptr = (int*)malloc(10 * sizeof (int)); When using C, this is considered bad practice; it is redundant under the C standard. Moreover, putting in a cast may mask failure to include the header stdlib.h, in which the prototype for malloc is found. In the absence of a prototype for malloc, the C compiler will assume that malloc returns an int, and will issue a warning in a context such as the above, provided the error is not masked by a cast. On certain architectures and data models (such as LP64 on 64 bit systems, where long and pointers are 64 bit and int is 32 bit), this error can actually result in undefined behavior, as the implicitly declared malloc returns a 32 bit value whereas the actually defined function returns a 64 bit value. Depending on calling conventions and memory layout, this may result in stack smashing. The returned pointer need not be explicitly cast to a more specific pointer type, since ANSI C defines an implicit conversion between the void pointer type and other pointers to objects. An explicit cast of malloc's return value is sometimes performed because malloc originally returned a char *, but this cast is unnecessary in standard C code.[4][5] Omitting the cast, however, creates an incompatibility with C++, which does require it. The lack of a specific pointer type returned from malloc is type-unsafe behaviour: malloc allocates based on byte count but not on type. This distinguishes it from the C++ new operator that returns a pointer whose type relies on the operand. (see C Type Safety). " See also http://www.opensc-project.org/pipermail/opensc-devel/2010-August/014586.html git-svn-id: https://www.opensc-project.org/svnp/opensc/trunk@4636 c6295689-39f2-0310-b995-f0e70906c6a9
2010-08-18 15:08:51 +00:00
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;
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)
{
2015-02-05 16:43:14 +00:00
if (!dst)
return SC_ERROR_INVALID_ARGUMENTS;
memset(dst, 0, sizeof(*dst));
if (src->len) {
Do not cast the return value of malloc(3) and calloc(3) From http://en.wikipedia.org/wiki/Malloc#Casting_and_type_safety " Casting and type safety malloc returns a void pointer (void *), which indicates that it is a pointer to a region of unknown data type. One may "cast" (see type conversion) this pointer to a specific type, as in int *ptr = (int*)malloc(10 * sizeof (int)); When using C, this is considered bad practice; it is redundant under the C standard. Moreover, putting in a cast may mask failure to include the header stdlib.h, in which the prototype for malloc is found. In the absence of a prototype for malloc, the C compiler will assume that malloc returns an int, and will issue a warning in a context such as the above, provided the error is not masked by a cast. On certain architectures and data models (such as LP64 on 64 bit systems, where long and pointers are 64 bit and int is 32 bit), this error can actually result in undefined behavior, as the implicitly declared malloc returns a 32 bit value whereas the actually defined function returns a 64 bit value. Depending on calling conventions and memory layout, this may result in stack smashing. The returned pointer need not be explicitly cast to a more specific pointer type, since ANSI C defines an implicit conversion between the void pointer type and other pointers to objects. An explicit cast of malloc's return value is sometimes performed because malloc originally returned a char *, but this cast is unnecessary in standard C code.[4][5] Omitting the cast, however, creates an incompatibility with C++, which does require it. The lack of a specific pointer type returned from malloc is type-unsafe behaviour: malloc allocates based on byte count but not on type. This distinguishes it from the C++ new operator that returns a pointer whose type relies on the operand. (see C Type Safety). " See also http://www.opensc-project.org/pipermail/opensc-devel/2010-August/014586.html git-svn-id: https://www.opensc-project.org/svnp/opensc/trunk@4636 c6295689-39f2-0310-b995-f0e70906c6a9
2010-08-18 15:08:51 +00:00
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, 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, *s;
size_t r_len, s_len, 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_RET(ctx, rv, "ASN.1 decoding ECDSA-Sig-Value failed");
if (halflen < r_len || halflen < s_len) {
rv = SC_ERROR_BUFFER_TOO_SMALL;
goto done;
}
memset(buf, 0, buflen);
memcpy(buf + (halflen - r_len), r, r_len);
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;
done:
free(r);
free(s);
LOG_FUNC_RETURN(ctx, rv);
}