#if HAVE_CONFIG_H
#include "config.h"
#endif
#include <assert.h>
#include <ctype.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <limits.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",
"NULL", "OBJECT IDENTIFIER", "OBJECT DESCRIPTOR", "EXTERNAL", "REAL",
"ENUMERATED", "Universal 11", "UTF8String", "Universal 13",
"Universal 14", "Universal 15", "SEQUENCE", "SET",
"NumericString", "PrintableString", "T61String",
"VideotexString", "IA5String", "UTCTIME", "GENERALIZEDTIME",
"GraphicString", "VisibleString", "GeneralString",
"UniversalString", "Universal 29", "BMPString"
};
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) {
*taglen = 0;
*tag_out = SC_ASN1_TAG_EOC;
return SC_SUCCESS;
}
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) {
size_t n = SC_ASN1_TAGNUM_SIZE - 1;
do {
if (left == 0 || n == 0)
return SC_ERROR_INVALID_ASN1_OBJECT;
tag <<= 8;
tag |= *p;
p++;
left--;
n--;
} while (tag & 0x80);
}
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) {
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)
{
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 <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) {
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);
putchar('-');
print_ascii(buf+2, 2);
putchar('-');
print_ascii(buf+4, 2);
putchar(' ');
print_ascii(buf+6, 2);
buf += 8;
buflen -= 8;
if (buflen >= 2 && isdigit(buf[0]) && isdigit(buf[1])) {
putchar(':');
print_ascii(buf, 2);
buf += 2;
buflen -= 2;
}
if (buflen >= 2 && isdigit(buf[0]) && isdigit(buf[1])) {
putchar(':');
print_ascii(buf, 2);
buf += 2;
buflen -= 2;
}
if (buflen >= 4 && '.' == buf[0]) {
print_ascii(buf, 4);
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);
putchar(':');
print_ascii(buf+3, 2);
}
}
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) {
printf("Error in decoding.\n");
return;
}
hlen = tagp - p;
if (cla == 0 && tag == 0) {
printf("Zero tag, finishing\n");
break;
}
print_indent(depth);
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;
if (sc_asn1_read_tag(&p, left, &cla, &tag, &taglen) != SC_SUCCESS
|| p == NULL)
return NULL;
left -= (p - buf);
while ((tag & mask) != 0) {
cla <<= 8;
mask <<= 8;
}
if ((tag | cla) == tag_in) {
if (taglen > left)
return NULL;
*taglen_in = taglen;
return p;
}
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);
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;
*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;
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) {
int bits_to_go;
*out = 0;
if (octets_left == 1)
bits_to_go = 8 - zero_bits;
else
bits_to_go = 8;
if (invert)
for (i = 0; i < bits_to_go; i++) {
*out |= ((*in >> (7 - i)) & 1) << i;
}
else {
*out = *in;
}
out++;
in++;
octets_left--;
count++;
}
return (count * 8) - zero_bits;
}
int sc_asn1_decode_bit_string(const u8 * inbuf, size_t inlen,
void *outbuf, size_t outlen)
{
return decode_bit_string(inbuf, inlen, outbuf, outlen, 1);
}
int sc_asn1_decode_bit_string_ni(const u8 * inbuf, size_t inlen,
void *outbuf, size_t outlen)
{
return decode_bit_string(inbuf, inlen, outbuf, outlen, 0);
}
static int encode_bit_string(const u8 * inbuf, size_t bits_left, u8 **outbuf,
size_t *outlen, int invert)
{
const u8 *in = inbuf;
u8 *out;
size_t bytes;
int skipped = 0;
bytes = (bits_left + 7)/8 + 1;
*outbuf = out = malloc(bytes);
if (out == NULL)
return SC_ERROR_OUT_OF_MEMORY;
*outlen = bytes;
out += 1;
while (bits_left) {
int i, bits_to_go = 8;
*out = 0;
if (bits_left < 8) {
bits_to_go = bits_left;
skipped = 8 - bits_left;
}
if (invert) {
for (i = 0; i < bits_to_go; i++)
*out |= ((*in >> i) & 1) << (7 - i);
} else {
*out = *in;
if (bits_left < 8)
return SC_ERROR_NOT_SUPPORTED;
}
bits_left -= bits_to_go;
out++, in++;
}
out = *outbuf;
out[0] = skipped;
return 0;
}
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;
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, is_negative = 0;
size_t i = 0;
if (inlen > sizeof(int) || inlen == 0)
return SC_ERROR_INVALID_ASN1_OBJECT;
if (inbuf[0] & 0x80) {
is_negative = 1;
a |= 0xff^(*inbuf++);
i = 1;
}
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) {
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;
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) {
if (a > (INT_MAX>>7)) {
sc_init_oid(id);
return SC_ERROR_NOT_SUPPORTED;
}
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;
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};
if (tag == 0 || tag > 0xFFFFFFFF) {
return SC_ERROR_INVALID_DATA;
}
for (tag_len = 0; tag; tag >>= 8) {
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) {
return SC_ERROR_INVALID_DATA;
}
for (ii = 1; ii < tag_len - 1; ii++) {
if ((tag_char[ii] & 0x80) != 0x80) {
return SC_ERROR_INVALID_DATA;
}
}
if ((tag_char[0] & 0x80) != 0x00) {
return SC_ERROR_INVALID_DATA;
}
}
if (datalen > 127) {
c = 1;
while (datalen >> (c << 3))
c++;
}
if (outlen == 0 || out == NULL) {
return tag_len + (c+1) + datalen;
}
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;
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;
}
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 },
{ "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) {
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) {
memcpy(path->value, path_value, path_len);
path->len = path_len;
}
else {
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;
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;
}
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 }
};
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;
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);
if ((entry->flags & SC_ASN1_UNSIGNED)
&& objlen > 1 && obj[0] == 0x00) {
objlen--;
obj++;
}
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 (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": "");
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)
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 ((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;
}
no_object:
if (!buflen && entry->flags & SC_ASN1_OPTIONAL && !(entry->flags & SC_ASN1_PRESENT)) {
*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;
}
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);
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);
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;
err:
free(r);
free(s);
LOG_FUNC_RETURN(ctx, rv);
}