diff options
Diffstat (limited to 'skeletons')
88 files changed, 9180 insertions, 0 deletions
diff --git a/skeletons/BIT_STRING.c b/skeletons/BIT_STRING.c new file mode 100644 index 00000000..19b6ff2e --- /dev/null +++ b/skeletons/BIT_STRING.c @@ -0,0 +1,96 @@ +/*- + * Copyright (c) 2003, 2004 Lev Walkin <vlm@lionet.info>. All rights reserved. + * Redistribution and modifications are permitted subject to BSD license. + */ +#include <BIT_STRING.h> + +/* + * BIT STRING basic type description. + */ +static ber_tlv_tag_t asn1_DEF_BIT_STRING_tags[] = { + (ASN_TAG_CLASS_UNIVERSAL | (3 << 2)) +}; +asn1_TYPE_descriptor_t asn1_DEF_BIT_STRING = { + "BIT STRING", + BIT_STRING_constraint, + OCTET_STRING_decode_ber, /* Implemented in terms of OCTET STRING */ + OCTET_STRING_encode_der, /* Implemented in terms of OCTET STRING */ + BIT_STRING_print, + OCTET_STRING_free, /* Implemented in terms of OCTET STRING */ + 0, /* Use generic outmost tag fetcher */ + asn1_DEF_BIT_STRING_tags, + sizeof(asn1_DEF_BIT_STRING_tags) + / sizeof(asn1_DEF_BIT_STRING_tags[0]), + 1, /* Single UNIVERSAL tag may be implicitly overriden */ + -1, /* Both ways are fine */ + (void *)-1 /* Special indicator that this is a BIT STRING */ +}; + +/* + * BIT STRING generic constraint. + */ +int +BIT_STRING_constraint(asn1_TYPE_descriptor_t *td, const void *sptr, + asn_app_consume_bytes_f *app_errlog, void *app_key) { + const BIT_STRING_t *st = sptr; + + if(st && st->buf) { + if(st->size) { + if(st->size == 1 && st->buf[0] != 0) { + _ASN_ERRLOG("%s: invalid padding byte", + td->name); + return -1; + } + } else { + _ASN_ERRLOG("%s: no padding byte", td->name); + return -1; + } + } else { + _ASN_ERRLOG("%s: value not given", td->name); + return -1; + } + + return 0; +} + +/* + * BIT STRING specific contents printer. + */ +int +BIT_STRING_print(asn1_TYPE_descriptor_t *td, const void *sptr, int ilevel, + asn_app_consume_bytes_f *cb, void *app_key) { + static char h2c[16] = "0123456789ABCDEF"; + char scratch[64]; + const BIT_STRING_t *st = sptr; + uint8_t *buf; + uint8_t *end; + char *p = scratch; + + if(!st || !st->buf) return cb("<absent>", 8, app_key); + + ilevel += 4; + buf = st->buf; + end = buf + st->size; + + /* + * Hexadecimal dump. + */ + for(buf++; buf < end; buf++) { + if(((buf - st->buf) - 1) % 16 == 0) { + int i; + /* Indentation */ + if(cb("\n", 1, app_key)) return -1; + for(i = 0; i < ilevel; i++) cb(" ", 1, app_key); + /* Dump the string */ + if(cb(scratch, p - scratch, app_key)) return -1; + p = scratch; + } + *p++ = h2c[*buf >> 4]; + *p++ = h2c[*buf & 0x0F]; + *p++ = 0x20; + } + + /* Dump the incomplete 16-bytes row */ + return cb(scratch, p - scratch, app_key); +} + diff --git a/skeletons/BIT_STRING.h b/skeletons/BIT_STRING.h new file mode 100644 index 00000000..1def8bb2 --- /dev/null +++ b/skeletons/BIT_STRING.h @@ -0,0 +1,18 @@ +/*- + * Copyright (c) 2003 Lev Walkin <vlm@lionet.info>. All rights reserved. + * Redistribution and modifications are permitted subject to BSD license. + */ +#ifndef _BIT_STRING_H_ +#define _BIT_STRING_H_ + +#include <constr_TYPE.h> +#include <OCTET_STRING.h> + +typedef OCTET_STRING_t BIT_STRING_t; /* Implemented in terms of OCTET STRING */ + +extern asn1_TYPE_descriptor_t asn1_DEF_BIT_STRING; + +asn_struct_print_f BIT_STRING_print; /* Human-readable output */ +asn_constr_check_f BIT_STRING_constraint; + +#endif /* _BIT_STRING_H_ */ diff --git a/skeletons/BMPString.c b/skeletons/BMPString.c new file mode 100644 index 00000000..596974ea --- /dev/null +++ b/skeletons/BMPString.c @@ -0,0 +1,65 @@ +/*- + * Copyright (c) 2003 Lev Walkin <vlm@lionet.info>. All rights reserved. + * Redistribution and modifications are permitted subject to BSD license. + */ +#include <BMPString.h> + +/* + * BMPString basic type description. + */ +static ber_tlv_tag_t asn1_DEF_BMPString_tags[] = { + (ASN_TAG_CLASS_UNIVERSAL | (30 << 2)) +}; +asn1_TYPE_descriptor_t asn1_DEF_BMPString = { + "BMPString", + asn_generic_no_constraint, /* No constraint by default */ + OCTET_STRING_decode_ber, /* Implemented in terms of OCTET STRING */ + OCTET_STRING_encode_der, /* Implemented in terms of OCTET STRING */ + BMPString_print, + OCTET_STRING_free, /* -//- */ + 0, /* Use generic outmost tag fetcher */ + asn1_DEF_BMPString_tags, + sizeof(asn1_DEF_BMPString_tags) + / sizeof(asn1_DEF_BMPString_tags[0]), + 1, /* Single UNIVERSAL tag may be implicitly overriden */ + -1, /* Both ways are fine */ +}; + +/* + * BMPString specific contents printer. + */ +int +BMPString_print(asn1_TYPE_descriptor_t *td, const void *sptr, int ilevel, + asn_app_consume_bytes_f *cb, void *app_key) { + const BMPString_t *st = sptr; + uint16_t *wchar; + uint16_t *wend; + char scratch[128]; /* Scratchpad buffer */ + char *p; + + if(!st || !st->buf) return cb("<absent>", 8, app_key); + + wchar = (uint16_t *)st->buf; + wend = (uint16_t *)(st->buf + st->size); + for(p = scratch; wchar < wend; wchar++) { + uint16_t wc = (((uint8_t *)wchar)[0] << 8) + | ((uint8_t *)wchar)[1]; /* 2 bytes */ + if(sizeof(scratch) - (p - scratch) < 3) { + if(cb(scratch, p - scratch, app_key)) + return -1; + p = scratch; + } + if(wc < 0x80) { + *p++ = (char)wc; + } else if(wc < 0x800) { + *p++ = 0xc0 | ((wc >> 6)); + *p++ = 0x80 | ((wc & 0x3f)); + } else { + *p++ = 0xe0 | ((wc >> 12)); + *p++ = 0x80 | ((wc >> 6) & 0x3f); + *p++ = 0x80 | ((wc & 0x3f)); + } + } + + return cb(scratch, p - scratch, app_key); +} diff --git a/skeletons/BMPString.h b/skeletons/BMPString.h new file mode 100644 index 00000000..e3957ca0 --- /dev/null +++ b/skeletons/BMPString.h @@ -0,0 +1,17 @@ +/*- + * Copyright (c) 2003 Lev Walkin <vlm@lionet.info>. All rights reserved. + * Redistribution and modifications are permitted subject to BSD license. + */ +#ifndef _BMPString_H_ +#define _BMPString_H_ + +#include <constr_TYPE.h> +#include <OCTET_STRING.h> + +typedef OCTET_STRING_t BMPString_t; /* Implemented in terms of OCTET STRING */ + +extern asn1_TYPE_descriptor_t asn1_DEF_BMPString; + +asn_struct_print_f BMPString_print; /* Human-readable output */ + +#endif /* _BMPString_H_ */ diff --git a/skeletons/BOOLEAN.c b/skeletons/BOOLEAN.c new file mode 100644 index 00000000..705d37e0 --- /dev/null +++ b/skeletons/BOOLEAN.c @@ -0,0 +1,147 @@ +/*- + * Copyright (c) 2003 Lev Walkin <vlm@lionet.info>. All rights reserved. + * Redistribution and modifications are permitted subject to BSD license. + */ +#include <BOOLEAN.h> + +/* + * BOOLEAN basic type description. + */ +static ber_tlv_tag_t asn1_DEF_BOOLEAN_tags[] = { + (ASN_TAG_CLASS_UNIVERSAL | (1 << 2)) +}; +asn1_TYPE_descriptor_t asn1_DEF_BOOLEAN = { + "BOOLEAN", + asn_generic_no_constraint, + BOOLEAN_decode_ber, + BOOLEAN_encode_der, + BOOLEAN_print, + BOOLEAN_free, + 0, /* Use generic outmost tag fetcher */ + asn1_DEF_BOOLEAN_tags, + sizeof(asn1_DEF_BOOLEAN_tags)/sizeof(asn1_DEF_BOOLEAN_tags[0]), + 1, /* Single UNIVERSAL tag may be implicitly overriden */ + 0 /* Always in primitive form */ +}; + +/* + * Decode BOOLEAN type. + */ +ber_dec_rval_t +BOOLEAN_decode_ber(asn1_TYPE_descriptor_t *td, + void **bool_structure, void *buf_ptr, size_t size, + int tag_mode) { + BOOLEAN_t *st = *bool_structure; + ber_dec_rval_t rval; + ber_dec_ctx_t ctx = { 0 }; + ber_tlv_len_t length; + ber_tlv_len_t lidx; + + if(st == NULL) { + st = *bool_structure = CALLOC(1, sizeof(*st)); + if(st == NULL) { + rval.code = RC_FAIL; + rval.consumed = 0; + return rval; + } + } + + ASN_DEBUG("Decoding %s as BOOLEAN (tm=%d)", + td->name, tag_mode); + + /* + * Check tags. + */ + rval = ber_check_tags(td, &ctx, + buf_ptr, size, tag_mode, &length, 0); + if(rval.code != RC_OK) + return rval; + + ASN_DEBUG("Boolean length is %d bytes", (int)length); + + buf_ptr += rval.consumed; + size -= rval.consumed; + if(length > size) { + rval.code = RC_WMORE; + rval.consumed = 0; + return rval; + } + + /* + * Compute boolean value. + */ + for(st->value = 0, lidx = 0; + (lidx < length) && st->value == 0; lidx++) { + /* + * Very simple approach: read bytes until the end or + * value is already TRUE. + * BOOLEAN is not supposed to contain meaningful data anyway. + */ + st->value |= ((uint8_t *)buf_ptr)[lidx]; + } + + rval.code = RC_OK; + rval.consumed += length; + + ASN_DEBUG("Took %ld/%ld bytes to encode %s, value=%ld", + (long)rval.consumed, (long)length, + td->name, (long)st->value); + + return rval; +} + +der_enc_rval_t +BOOLEAN_encode_der(asn1_TYPE_descriptor_t *td, void *sptr, + int tag_mode, ber_tlv_tag_t tag, + asn_app_consume_bytes_f *cb, void *app_key) { + der_enc_rval_t erval; + BOOLEAN_t *st = sptr; + + erval.encoded = der_write_tags(td, 1, tag_mode, tag, cb, app_key); + if(erval.encoded == -1) { + erval.failed_type = td; + erval.structure_ptr = sptr; + return erval; + } + + if(cb) { + uint8_t bool_value; + ssize_t ret; + + bool_value = st->value?0xff:0; /* 0xff mandated by DER */ + ret = cb(&bool_value, 1, app_key); + if(ret == -1) { + erval.encoded = -1; + erval.failed_type = td; + erval.structure_ptr = sptr; + return erval; + } + } + + erval.encoded += 1; + + return erval; +} + +int +BOOLEAN_print(asn1_TYPE_descriptor_t *td, const void *sptr, int ilevel, + asn_app_consume_bytes_f *cb, void *app_key) { + const BOOLEAN_t *st = sptr; + + if(st) { + if(st->value) + return cb("TRUE", 4, app_key); + else + return cb("FALSE", 5, app_key); + } else { + return cb("<absent>", 8, app_key); + } +} + +void +BOOLEAN_free(asn1_TYPE_descriptor_t *td, void *ptr, int contents_only) { + if(td && ptr && !contents_only) { + FREEMEM(ptr); + } +} + diff --git a/skeletons/BOOLEAN.h b/skeletons/BOOLEAN.h new file mode 100644 index 00000000..234e9f8e --- /dev/null +++ b/skeletons/BOOLEAN.h @@ -0,0 +1,21 @@ +/*- + * Copyright (c) 2003 Lev Walkin <vlm@lionet.info>. All rights reserved. + * Redistribution and modifications are permitted subject to BSD license. + */ +#ifndef _BOOLEAN_H_ +#define _BOOLEAN_H_ + +#include <constr_TYPE.h> + +typedef struct BOOLEAN { + int value; +} BOOLEAN_t; + +extern asn1_TYPE_descriptor_t asn1_DEF_BOOLEAN; + +ber_type_decoder_f BOOLEAN_decode_ber; +der_type_encoder_f BOOLEAN_encode_der; +asn_struct_print_f BOOLEAN_print; +asn_struct_free_f BOOLEAN_free; + +#endif /* _BOOLEAN_H_ */ diff --git a/skeletons/ENUMERATED.c b/skeletons/ENUMERATED.c new file mode 100644 index 00000000..1f1ae896 --- /dev/null +++ b/skeletons/ENUMERATED.c @@ -0,0 +1,26 @@ +/*- + * Copyright (c) 2003 Lev Walkin <vlm@lionet.info>. All rights reserved. + * Redistribution and modifications are permitted subject to BSD license. + */ +#include <ENUMERATED.h> + +/* + * ENUMERATED basic type description. + */ +static ber_tlv_tag_t asn1_DEF_ENUMERATED_tags[] = { + (ASN_TAG_CLASS_UNIVERSAL | (10 << 2)) +}; +asn1_TYPE_descriptor_t asn1_DEF_ENUMERATED = { + "ENUMERATED", + asn_generic_no_constraint, + INTEGER_decode_ber, /* Implemented in terms of INTEGER */ + INTEGER_encode_der, /* Implemented in terms of INTEGER */ + INTEGER_print, /* Implemented in terms of INTEGER */ + INTEGER_free, /* Implemented in terms of INTEGER */ + 0, /* Use generic outmost tag fetcher */ + asn1_DEF_ENUMERATED_tags, + sizeof(asn1_DEF_ENUMERATED_tags)/sizeof(asn1_DEF_ENUMERATED_tags[0]), + 1, /* Single UNIVERSAL tag may be implicitly overriden */ + 0 /* Primitive */ +}; + diff --git a/skeletons/ENUMERATED.h b/skeletons/ENUMERATED.h new file mode 100644 index 00000000..a0948ed0 --- /dev/null +++ b/skeletons/ENUMERATED.h @@ -0,0 +1,15 @@ +/*- + * Copyright (c) 2003 Lev Walkin <vlm@lionet.info>. All rights reserved. + * Redistribution and modifications are permitted subject to BSD license. + */ +#ifndef _ENUMERATED_H_ +#define _ENUMERATED_H_ + +#include <constr_TYPE.h> +#include <INTEGER.h> + +typedef INTEGER_t ENUMERATED_t; /* Implemented in terms of INTEGER */ + +extern asn1_TYPE_descriptor_t asn1_DEF_ENUMERATED; + +#endif /* _ENUMERATED_H_ */ diff --git a/skeletons/GeneralString.c b/skeletons/GeneralString.c new file mode 100644 index 00000000..d69c5f16 --- /dev/null +++ b/skeletons/GeneralString.c @@ -0,0 +1,27 @@ +/*- + * Copyright (c) 2003 Lev Walkin <vlm@lionet.info>. All rights reserved. + * Redistribution and modifications are permitted subject to BSD license. + */ +#include <GeneralString.h> + +/* + * GeneralString basic type description. + */ +static ber_tlv_tag_t asn1_DEF_GeneralString_tags[] = { + (ASN_TAG_CLASS_UNIVERSAL | (27 << 2)) +}; +asn1_TYPE_descriptor_t asn1_DEF_GeneralString = { + "GeneralString", + asn_generic_unknown_constraint, + OCTET_STRING_decode_ber, /* Implemented in terms of OCTET STRING */ + OCTET_STRING_encode_der, /* Implemented in terms of OCTET STRING */ + OCTET_STRING_print, /* non-ascii string */ + OCTET_STRING_free, + 0, /* Use generic outmost tag fetcher */ + asn1_DEF_GeneralString_tags, + sizeof(asn1_DEF_GeneralString_tags) + / sizeof(asn1_DEF_GeneralString_tags[0]), + 1, /* Single UNIVERSAL tag may be implicitly overriden */ + -1, /* Both ways are fine */ +}; + diff --git a/skeletons/GeneralString.h b/skeletons/GeneralString.h new file mode 100644 index 00000000..6d5bfdf6 --- /dev/null +++ b/skeletons/GeneralString.h @@ -0,0 +1,15 @@ +/*- + * Copyright (c) 2003 Lev Walkin <vlm@lionet.info>. All rights reserved. + * Redistribution and modifications are permitted subject to BSD license. + */ +#ifndef _GeneralString_H_ +#define _GeneralString_H_ + +#include <constr_TYPE.h> +#include <OCTET_STRING.h> + +typedef OCTET_STRING_t GeneralString_t; /* Implemented in terms of OCTET STRING */ + +extern asn1_TYPE_descriptor_t asn1_DEF_GeneralString; + +#endif /* _GeneralString_H_ */ diff --git a/skeletons/GeneralizedTime.c b/skeletons/GeneralizedTime.c new file mode 100644 index 00000000..01d7a736 --- /dev/null +++ b/skeletons/GeneralizedTime.c @@ -0,0 +1,302 @@ +/*- + * Copyright (c) 2003, 2004 Lev Walkin <vlm@lionet.info>. All rights reserved. + * Redistribution and modifications are permitted subject to BSD license. + */ +#include <GeneralizedTime.h> +#include <time.h> +#include <errno.h> +#ifndef __NO_ASSERT_H__ +#include <assert.h> +#endif /* __NO_ASSERT_H__ */ + +#ifndef __NO_ASN_TABLE__ + +/* + * GeneralizedTime basic type description. + */ +static ber_tlv_tag_t asn1_DEF_GeneralizedTime_tags[] = { + (ASN_TAG_CLASS_UNIVERSAL | (24 << 2)) +}; +asn1_TYPE_descriptor_t asn1_DEF_GeneralizedTime = { + "GeneralizedTime", + GeneralizedTime_constraint, /* Check validity of time */ + OCTET_STRING_decode_ber, /* Implemented in terms of OCTET STRING */ + OCTET_STRING_encode_der, /* Implemented in terms of OCTET STRING */ + GeneralizedTime_print, + OCTET_STRING_free, + 0, /* Use generic outmost tag fetcher */ + asn1_DEF_GeneralizedTime_tags, + sizeof(asn1_DEF_GeneralizedTime_tags) + / sizeof(asn1_DEF_GeneralizedTime_tags[0]), + 1, /* Single UNIVERSAL tag may be implicitly overriden */ + -1, /* Both ways are fine */ +}; + +#endif /* __NO_ASN_TABLE__ */ + +/* + * Check that the time looks like the time. + */ +int +GeneralizedTime_constraint(asn1_TYPE_descriptor_t *td, const void *sptr, + asn_app_consume_bytes_f *app_errlog, void *app_key) { + const GeneralizedTime_t *st = sptr; + time_t tloc; + + errno = EPERM; /* Just an unlikely error code */ + tloc = asn_GT2time(st, 0); + if(tloc == -1 && errno != EPERM) { + _ASN_ERRLOG("%s: Invalid time format: %s", + td->name, strerror(errno)); + return -1; + } + + return 0; +} + +int +GeneralizedTime_print(asn1_TYPE_descriptor_t *td, const void *sptr, int ilevel, + asn_app_consume_bytes_f *cb, void *app_key) { + const GeneralizedTime_t *st = sptr; + + if(st && st->buf) { + char buf[32]; + struct tm tm; + int ret; + + errno = EPERM; + if(asn_GT2time(st, &tm) == -1 && errno != EPERM) + return cb("<bad-value>", 11, app_key); + + ret = snprintf(buf, sizeof(buf), + "%04d-%02d-%02d %02d:%02d%02d", + tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, + tm.tm_hour, tm.tm_min, tm.tm_sec); + assert(ret > 0 && ret < sizeof(buf)); + return cb(buf, ret, app_key); + } else { + return cb("<absent>", 8, app_key); + } +} + +/* + * Where to look for offset from GMT, Phase I. + * Several platforms are known. + */ +#if defined(__FreeBSD__) || (defined(__GNUC__) && defined(__APPLE_CC__)) +#undef HAVE_TM_ZONE +#define HAVE_TM_ZONE +#endif /* BSDs */ + +/* + * Where to look for offset from GMT, Phase II. + */ +#ifdef HAVE_TM_ZONE +#define GMTOFF (tm_s.tm_gmtoff) +#else /* HAVE_TM_ZONE */ +#define GMTOFF (-timezone) +#endif /* HAVE_TM_ZONE */ + +time_t +asn_GT2time(const GeneralizedTime_t *st, struct tm *_tm) { + struct tm tm_s; + uint8_t *buf; + uint8_t *end; + int tm_gmtoff_h = 0; + int tm_gmtoff_m = 0; + int tm_gmtoff = 0; /* h + m */ + int offset_specified = 0; + time_t tloc; + + if(!st || !st->buf) { + errno = EINVAL; + return -1; + } else { + buf = st->buf; + end = buf + st->size; + } + + if(st->size < 10) { + errno = EINVAL; + return -1; + } + + /* + * Decode first 10 bytes: "AAAAMMJJhh" + */ + memset(&tm_s, 0, sizeof(tm_s)); +#undef B2F +#undef B2T +#define B2F(var) do { \ + unsigned ch = *buf; \ + if(ch < 0x30 && ch > 0x39) { \ + errno = EINVAL; \ + return -1; \ + } else { \ + var = var * 10 + (ch - 0x30); \ + buf++; \ + } \ + } while(0) +#define B2T(var) B2F(tm_s.var) + + B2T(tm_year); /* 1: A */ + B2T(tm_year); /* 2: A */ + B2T(tm_year); /* 3: A */ + B2T(tm_year); /* 4: A */ + B2T(tm_mon); /* 5: M */ + B2T(tm_mon); /* 6: M */ + B2T(tm_mday); /* 7: J */ + B2T(tm_mday); /* 8: J */ + B2T(tm_hour); /* 9: h */ + B2T(tm_hour); /* 0: h */ + + if(buf == end) goto local_finish; + + /* + * Parse [mm[ss[(.|,)ffff]]] + * ^^ + */ + switch(*buf) { + case 0x30: case 0x31: case 0x32: case 0x33: case 0x34: + case 0x35: case 0x36: case 0x37: case 0x38: case 0x39: + tm_s.tm_min = (*buf++) - 0x30; + if(buf == end) { errno = EINVAL; return -1; } + B2T(tm_min); + break; + case 0x2B: case 0x2D: /* +, - */ + goto offset; + case 0x5A: /* Z */ + goto utc_finish; + default: + errno = EINVAL; + return -1; + } + + if(buf == end) goto local_finish; + + /* + * Parse [mm[ss[(.|,)ffff]]] + * ^^ + */ + switch(*buf) { + case 0x30: case 0x31: case 0x32: case 0x33: case 0x34: + case 0x35: case 0x36: case 0x37: case 0x38: case 0x39: + tm_s.tm_sec = (*buf++) - 0x30; + if(buf == end) { errno = EINVAL; return -1; } + B2T(tm_sec); + break; + case 0x2B: case 0x2D: /* +, - */ + goto offset; + case 0x5A: /* Z */ + goto utc_finish; + default: + errno = EINVAL; + return -1; + } + + if(buf == end) goto local_finish; + + /* + * Parse [mm[ss[(.|,)ffff]]] + * ^ ^ + */ + switch(*buf) { + case 0x2C: case 0x2E: /* (.|,) */ + /* Fractions of seconds are not supported + * by time_t or struct tm. Skip them */ + for(buf++; buf < end; buf++) { + switch(*buf) { + case 0x30: case 0x31: case 0x32: case 0x33: case 0x34: + case 0x35: case 0x36: case 0x37: case 0x38: case 0x39: + continue; + default: + break; + } + break; + } + } + + if(buf == end) goto local_finish; + + switch(*buf) { + case 0x2B: case 0x2D: /* +, - */ + goto offset; + case 0x5A: /* Z */ + goto utc_finish; + default: + errno = EINVAL; + return -1; + } + + +offset: + + if(end - buf < 3) { + errno = EINVAL; + return -1; + } + buf++; + B2F(tm_gmtoff_h); + B2F(tm_gmtoff_h); + if(buf[-3] == 0x2D) /* Negative */ + tm_gmtoff = -1; + else + tm_gmtoff = 1; + + if((end - buf) == 2) { + B2F(tm_gmtoff_m); + B2F(tm_gmtoff_m); + } else if(end != buf) { + errno = EINVAL; + return -1; + } + + tm_gmtoff = tm_gmtoff * (3600 * tm_gmtoff_h + 60 * tm_gmtoff_m); + + /* Fall through */ +utc_finish: + + offset_specified = 1; + + /* Fall through */ +local_finish: + + /* + * Validation. + */ + if((tm_s.tm_mon > 12 || tm_s.tm_mon < 1) + || (tm_s.tm_mday > 31 || tm_s.tm_mday < 1) + || (tm_s.tm_hour > 23) + || (tm_s.tm_sec > 60) + ) { + errno = EINVAL; + return -1; + } + + /* Canonicalize */ + tm_s.tm_mon -= 1; /* 0 - 11 */ + tm_s.tm_year -= 1900; + tm_s.tm_isdst = -1; + + tloc = mktime(&tm_s); + if(tloc == -1) { + errno = EINVAL; + return -1; + } + + if(offset_specified) { + /* + * Offset from GMT is specified in the time expression. + */ + tloc += GMTOFF - tm_gmtoff; + if(_tm && (localtime_r(&tloc, &tm_s) == NULL)) { + /* Could not reconstruct the time */ + return -1; + } + } + + if(_tm) memcpy(_tm, &tm_s, sizeof(struct tm)); + + return tloc; +} + diff --git a/skeletons/GeneralizedTime.h b/skeletons/GeneralizedTime.h new file mode 100644 index 00000000..d8176be0 --- /dev/null +++ b/skeletons/GeneralizedTime.h @@ -0,0 +1,26 @@ +/*- + * Copyright (c) 2003, 2004 Lev Walkin <vlm@lionet.info>. All rights reserved. + * Redistribution and modifications are permitted subject to BSD license. + */ +#ifndef _GeneralizedTime_H_ +#define _GeneralizedTime_H_ + +#include <constr_TYPE.h> +#include <OCTET_STRING.h> + +typedef OCTET_STRING_t GeneralizedTime_t; /* Implemented using OCTET STRING */ + +extern asn1_TYPE_descriptor_t asn1_DEF_GeneralizedTime; + +asn_constr_check_f GeneralizedTime_constraint; +asn_struct_print_f GeneralizedTime_print; + +/*********************** + * Some handy helpers. * + ***********************/ + +/* On error returns -1 and errno set to EINVAL */ +struct tm; /* <time.h> */ +time_t asn_GT2time(const GeneralizedTime_t *, struct tm *_optional_tm4fill); + +#endif /* _GeneralizedTime_H_ */ diff --git a/skeletons/GraphicString.c b/skeletons/GraphicString.c new file mode 100644 index 00000000..5c89685e --- /dev/null +++ b/skeletons/GraphicString.c @@ -0,0 +1,27 @@ +/*- + * Copyright (c) 2003 Lev Walkin <vlm@lionet.info>. All rights reserved. + * Redistribution and modifications are permitted subject to BSD license. + */ +#include <GraphicString.h> + +/* + * GraphicString basic type description. + */ +static ber_tlv_tag_t asn1_DEF_GraphicString_tags[] = { + (ASN_TAG_CLASS_UNIVERSAL | (25 << 2)) +}; +asn1_TYPE_descriptor_t asn1_DEF_GraphicString = { + "GraphicString", + asn_generic_unknown_constraint, + OCTET_STRING_decode_ber, /* Implemented in terms of OCTET STRING */ + OCTET_STRING_encode_der, /* Implemented in terms of OCTET STRING */ + OCTET_STRING_print, /* non-ascii string */ + OCTET_STRING_free, + 0, /* Use generic outmost tag fetcher */ + asn1_DEF_GraphicString_tags, + sizeof(asn1_DEF_GraphicString_tags) + / sizeof(asn1_DEF_GraphicString_tags[0]), + 1, /* Single UNIVERSAL tag may be implicitly overriden */ + -1, /* Both ways are fine */ +}; + diff --git a/skeletons/GraphicString.h b/skeletons/GraphicString.h new file mode 100644 index 00000000..b37b2c7e --- /dev/null +++ b/skeletons/GraphicString.h @@ -0,0 +1,15 @@ +/*- + * Copyright (c) 2003 Lev Walkin <vlm@lionet.info>. All rights reserved. + * Redistribution and modifications are permitted subject to BSD license. + */ +#ifndef _GraphicString_H_ +#define _GraphicString_H_ + +#include <constr_TYPE.h> +#include <OCTET_STRING.h> + +typedef OCTET_STRING_t GraphicString_t; /* Implemented in terms of OCTET STRING */ + +extern asn1_TYPE_descriptor_t asn1_DEF_GraphicString; + +#endif /* _GraphicString_H_ */ diff --git a/skeletons/IA5String.c b/skeletons/IA5String.c new file mode 100644 index 00000000..c4afcb00 --- /dev/null +++ b/skeletons/IA5String.c @@ -0,0 +1,57 @@ +/*- + * Copyright (c) 2003 Lev Walkin <vlm@lionet.info>. All rights reserved. + * Redistribution and modifications are permitted subject to BSD license. + */ +#include <IA5String.h> + +/* + * IA5String basic type description. + */ +static ber_tlv_tag_t asn1_DEF_IA5String_tags[] = { + (ASN_TAG_CLASS_UNIVERSAL | (22 << 2)) +}; +asn1_TYPE_descriptor_t asn1_DEF_IA5String = { + "IA5String", + IA5String_constraint, /* Constraint on the alphabet */ + OCTET_STRING_decode_ber, /* Implemented in terms of OCTET STRING */ + OCTET_STRING_encode_der, /* Implemented in terms of OCTET STRING */ + OCTET_STRING_print_ascii, /* ASCII subset */ + OCTET_STRING_free, + 0, /* Use generic outmost tag fetcher */ + asn1_DEF_IA5String_tags, + sizeof(asn1_DEF_IA5String_tags) + / sizeof(asn1_DEF_IA5String_tags[0]), + 1, /* Single UNIVERSAL tag may be implicitly overriden */ + -1, /* Both ways are fine */ +}; + +int +IA5String_constraint(asn1_TYPE_descriptor_t *td, const void *sptr, + asn_app_consume_bytes_f *app_errlog, void *app_key) { + const IA5String_t *st = sptr; + + if(st && st->buf) { + uint8_t *buf = st->buf; + uint8_t *end = buf + st->size; + /* + * IA5String is generally equivalent to 7bit ASCII. + * ISO/ITU-T T.50, 1963. + */ + for(; buf < end; buf++) { + if(*buf > 0x7F) { + _ASN_ERRLOG("%s: value byte %d out of range: " + "%d > 127", + td->name, + (buf - st->buf) + 1, + *buf + ); + return -1; + } + } + } else { + _ASN_ERRLOG("%s: value not given", td->name); + return -1; + } + + return 0; +} diff --git a/skeletons/IA5String.h b/skeletons/IA5String.h new file mode 100644 index 00000000..a80004e8 --- /dev/null +++ b/skeletons/IA5String.h @@ -0,0 +1,20 @@ +/*- + * Copyright (c) 2003 Lev Walkin <vlm@lionet.info>. All rights reserved. + * Redistribution and modifications are permitted subject to BSD license. + */ +#ifndef _IA5String_H_ +#define _IA5String_H_ + +#include <constr_TYPE.h> +#include <OCTET_STRING.h> + +typedef OCTET_STRING_t IA5String_t; /* Implemented in terms of OCTET STRING */ + +/* + * IA5String ASN.1 type definition. + */ +extern asn1_TYPE_descriptor_t asn1_DEF_IA5String; + +asn_constr_check_f IA5String_constraint; + +#endif /* _IA5String_H_ */ diff --git a/skeletons/INTEGER.c b/skeletons/INTEGER.c new file mode 100644 index 00000000..b1a8c29b --- /dev/null +++ b/skeletons/INTEGER.c @@ -0,0 +1,305 @@ +/*- + * Copyright (c) 2003 Lev Walkin <vlm@lionet.info>. All rights reserved. + * Redistribution and modifications are permitted subject to BSD license. + */ +#include <INTEGER.h> +#include <assert.h> +#include <errno.h> + +/* + * INTEGER basic type description. + */ +static ber_tlv_tag_t asn1_DEF_INTEGER_tags[] = { + (ASN_TAG_CLASS_UNIVERSAL | (2 << 2)) +}; +asn1_TYPE_descriptor_t asn1_DEF_INTEGER = { + "INTEGER", + asn_generic_no_constraint, + INTEGER_decode_ber, + INTEGER_encode_der, + INTEGER_print, + INTEGER_free, + 0, /* Use generic outmost tag fetcher */ + asn1_DEF_INTEGER_tags, + sizeof(asn1_DEF_INTEGER_tags)/sizeof(asn1_DEF_INTEGER_tags[0]), + 1, /* Single UNIVERSAL tag may be implicitly overriden */ + 0 /* Always in primitive form */ +}; + +/* + * Decode INTEGER type. + */ +ber_dec_rval_t +INTEGER_decode_ber(asn1_TYPE_descriptor_t *td, + void **int_structure, void *buf_ptr, size_t size, int tag_mode) { + INTEGER_t *st = *int_structure; + ber_dec_rval_t rval; + ber_dec_ctx_t ctx = { 0 }; + ber_tlv_len_t length; + + /* + * If the structure is not there, allocate it. + */ + if(st == NULL) { + st = *int_structure = CALLOC(1, sizeof(*st)); + if(st == NULL) { + rval.code = RC_FAIL; + rval.consumed = 0; + return rval; + } + } + + ASN_DEBUG("Decoding %s as INTEGER (tm=%d)", + td->name, tag_mode); + + /* + * Check tags. + */ + rval = ber_check_tags(td, &ctx, + buf_ptr, size, tag_mode, &length, 0); + if(rval.code != RC_OK) + return rval; + + ASN_DEBUG("%s length is %d bytes", td->name, (int)length); + + /* + * Make sure we have this length. + */ + buf_ptr += rval.consumed; + size -= rval.consumed; + if(length > size) { + rval.code = RC_WMORE; + rval.consumed = 0; + return rval; + } + + st->buf = MALLOC(length); + if(st->buf) { + st->size = length; + } else { + rval.code = RC_FAIL; + rval.consumed = 0; + return rval; + } + + memcpy(st->buf, buf_ptr, st->size); + + rval.code = RC_OK; + rval.consumed += length; + + ASN_DEBUG("Took %ld/%ld bytes to encode %s", + (long)rval.consumed, + (long)length, td->name); + + return rval; +} + +/* + * Encode INTEGER type using DER. + */ +der_enc_rval_t +INTEGER_encode_der(asn1_TYPE_descriptor_t *sd, void *ptr, + int tag_mode, ber_tlv_tag_t tag, + asn_app_consume_bytes_f *cb, void *app_key) { + der_enc_rval_t erval; + INTEGER_t *st = ptr; + + ASN_DEBUG("%s %s as INTEGER (tm=%d)", + cb?"Encoding":"Estimating", sd->name, tag_mode); + + /* + * Canonicalize integer in the buffer. + * (Remove too long sign extension, remove some first 0x00 bytes) + */ + if(st->buf) { + uint8_t *buf = st->buf; + uint8_t *end1 = buf + st->size - 1; + int shift; + + /* Compute the number of superfluous leading bytes */ + for(; buf < end1; buf++) { + /* + * If the contents octets of an integer value encoding + * consist of more than one octet, then the bits of the + * first octet and bit 8 of the second octet: + * a) shall not all be ones; and + * b) shall not all be zero. + */ + switch(*buf) { + case 0x00: if((buf[1] & 0x80) == 0) + continue; + break; + case 0xff: if((buf[1] & 0x80)) + continue; + break; + } + break; + } + + /* Remove leading superfluous bytes from the integer */ + shift = buf - st->buf; + if(shift) { + uint8_t *nb = st->buf; + uint8_t *end; + + st->size -= shift; /* New size, minus bad bytes */ + end = nb + st->size; + + for(; nb < end; nb++, buf++) + *nb = *buf; + } + + } /* if(1) */ + + erval.encoded = der_write_tags(sd, st->size, tag_mode, tag, + cb, app_key); + ASN_DEBUG("INTEGER %s wrote tags %d", sd->name, (int)erval.encoded); + if(erval.encoded == -1) { + erval.failed_type = sd; + erval.structure_ptr = ptr; + return erval; + } + + if(cb && st->buf) { + ssize_t ret; + + ret = cb(st->buf, st->size, app_key); + if(ret == -1) { + erval.encoded = -1; + erval.failed_type = sd; + erval.structure_ptr = ptr; + return erval; + } + } else { + assert(st->buf || st->size == 0); + } + + erval.encoded += st->size; + + return erval; +} + +/* + * INTEGER specific human-readable output. + */ +int +INTEGER_print(asn1_TYPE_descriptor_t *td, const void *sptr, int ilevel, + asn_app_consume_bytes_f *cb, void *app_key) { + const INTEGER_t *st = sptr; + char scratch[32]; + uint8_t *buf = st->buf; + uint8_t *buf_end = st->buf + st->size; + signed long accum; + char *p; + int ret; + + if(!st && !st->buf) return cb("<absent>", 8, app_key); + + if(st->size == 0) + return cb("0", 1, app_key); + + /* Simple case: the integer size is small */ + if(st->size < sizeof(accum) || (st->buf[0] & 0x80)) { + accum = (st->buf[0] & 0x80) ? -1 : 0; + for(; buf < buf_end; buf++) + accum = (accum << 8) | *buf; + ret = snprintf(scratch, sizeof(scratch), "%ld", accum); + assert(ret > 0 && ret < sizeof(scratch)); + return cb(scratch, ret, app_key); + } + + /* Output in the long xx:yy:zz... format */ + for(p = scratch; buf < buf_end; buf++) { + static char h2c[16] = "0123456789ABCDEF"; + if((p - scratch) >= (sizeof(scratch) / 2)) { + /* Flush buffer */ + if(cb(scratch, p - scratch, app_key)) + return -1; + p = scratch; + } + *p++ = h2c[*buf >> 4]; + *p++ = h2c[*buf & 0x0F]; + *p++ = ':'; + } + + return cb(scratch, p - scratch, app_key); +} + +void +INTEGER_free(asn1_TYPE_descriptor_t *td, void *ptr, int contents_only) { + INTEGER_t *st = ptr; + + if(!td || !st) + return; + + ASN_DEBUG("Freeing %s as INTEGER (%d, %p, %p)", + td->name, contents_only, st, st->buf); + + if(st->buf) { + FREEMEM(st->buf); + } + + if(!contents_only) { + FREEMEM(st); + } +} + +int +asn1_INTEGER2long(const INTEGER_t *iptr, long *lptr) { + uint8_t *b, *end; + size_t size; + long l; + + /* Sanity checking */ + if(!iptr || !iptr->buf || !lptr) { + errno = EINVAL; + return -1; + } + + /* Cache the begin/end of the buffer */ + b = iptr->buf; /* Start of the INTEGER buffer */ + size = iptr->size; + end = b + size; /* Where to stop */ + + if(size > sizeof(long)) { + uint8_t *end1 = end - 1; + /* + * Slightly more advanced processing, + * able to >sizeof(long) bytes, + * when the actual value is small + * (0x0000000000abcdef would yield a fine 0x00abcdef) + */ + /* Skip out the insignificant leading bytes */ + for(; b < end1; b++) { + switch(*b) { + case 0x00: if((b[1] & 0x80) == 0) continue; break; + case 0xff: if((b[1] & 0x80) != 0) continue; break; + } + break; + } + + size = end - b; + if(size > sizeof(long)) { + /* Still cannot fit the long */ + errno = ERANGE; + return -1; + } + } + + /* Shortcut processing of a corner case */ + if(end == b) { + *lptr = 0; + return 0; + } + + /* Perform the sign initialization */ + /* Actually l = -(*b >> 7); gains nothing, yet unreadable! */ + if((*b >> 7)) l = -1; else l = 0; + + /* Conversion engine */ + for(; b < end; b++) + l = (l << 8) | *b; + + *lptr = l; + return 0; +} diff --git a/skeletons/INTEGER.h b/skeletons/INTEGER.h new file mode 100644 index 00000000..ef4d3f25 --- /dev/null +++ b/skeletons/INTEGER.h @@ -0,0 +1,33 @@ +/*- + * Copyright (c) 2003 Lev Walkin <vlm@lionet.info>. All rights reserved. + * Redistribution and modifications are permitted subject to BSD license. + */ +#ifndef _INTEGER_H_ +#define _INTEGER_H_ + +#include <constr_TYPE.h> + +typedef struct INTEGER { + uint8_t *buf; /* Buffer with consecutive INTEGER bits (big-endian) */ + int size; /* Size of the buffer */ +} INTEGER_t; + +extern asn1_TYPE_descriptor_t asn1_DEF_INTEGER; + +ber_type_decoder_f INTEGER_decode_ber; +der_type_encoder_f INTEGER_encode_der; +asn_struct_print_f INTEGER_print; +asn_struct_free_f INTEGER_free; + +/*********************************** + * Some handy conversion routines. * + ***********************************/ + +/* + * Returns 0 if it was possible to convert, -1 otherwise. + * -1/EINVAL: Mandatory argument missing + * -1/ERANGE: Value encoded is out of range for long representation + */ +int asn1_INTEGER2long(const INTEGER_t *i, long *l); + +#endif /* _INTEGER_H_ */ diff --git a/skeletons/ISO646String.c b/skeletons/ISO646String.c new file mode 100644 index 00000000..08b31698 --- /dev/null +++ b/skeletons/ISO646String.c @@ -0,0 +1,27 @@ +/*- + * Copyright (c) 2003, 2004 Lev Walkin <vlm@lionet.info>. All rights reserved. + * Redistribution and modifications are permitted subject to BSD license. + */ +#include <ISO646String.h> + +/* + * ISO646String basic type description. + */ +static ber_tlv_tag_t asn1_DEF_ISO646String_tags[] = { + (ASN_TAG_CLASS_UNIVERSAL | (26 << 2)) +}; +asn1_TYPE_descriptor_t asn1_DEF_ISO646String = { + "ISO646String", + VisibleString_constraint, + OCTET_STRING_decode_ber, /* Implemented in terms of OCTET STRING */ + OCTET_STRING_encode_der, /* Implemented in terms of OCTET STRING */ + OCTET_STRING_print_ascii, /* ASCII subset */ + OCTET_STRING_free, + 0, /* Use generic outmost tag fetcher */ + asn1_DEF_ISO646String_tags, + sizeof(asn1_DEF_ISO646String_tags) + / sizeof(asn1_DEF_ISO646String_tags[0]), + 1, /* Single UNIVERSAL tag may be implicitly overriden */ + -1, /* Both ways are fine */ +}; + diff --git a/skeletons/ISO646String.h b/skeletons/ISO646String.h new file mode 100644 index 00000000..566ce3be --- /dev/null +++ b/skeletons/ISO646String.h @@ -0,0 +1,15 @@ +/*- + * Copyright (c) 2003, 2004 Lev Walkin <vlm@lionet.info>. All rights reserved. + * Redistribution and modifications are permitted subject to BSD license. + */ +#ifndef _ISO646String_H_ +#define _ISO646String_H_ + +#include <constr_TYPE.h> +#include <VisibleString.h> + +typedef VisibleString_t ISO646String_t; /* Implemented using VisibleString */ + +extern asn1_TYPE_descriptor_t asn1_DEF_ISO646String; + +#endif /* _ISO646String_H_ */ diff --git a/skeletons/Makefile.am b/skeletons/Makefile.am new file mode 100644 index 00000000..4a01bf3d --- /dev/null +++ b/skeletons/Makefile.am @@ -0,0 +1,7 @@ + +SUBDIRS = tests + +dist_pkgdata_DATA = *.[ch] +uninstall-local: + -@echo -n " " + -rm -f -r $(DESTDIR)$(pkgdatadir) diff --git a/skeletons/Makefile.in b/skeletons/Makefile.in new file mode 100644 index 00000000..e8f42241 --- /dev/null +++ b/skeletons/Makefile.in @@ -0,0 +1,364 @@ +# Makefile.in generated automatically by automake 1.5 from Makefile.am. + +# Copyright 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001 +# Free Software Foundation, Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +SHELL = @SHELL@ + +srcdir = @srcdir@ +top_srcdir = @top_srcdir@ +VPATH = @srcdir@ +prefix = @prefix@ +exec_prefix = @exec_prefix@ + +bindir = @bindir@ +sbindir = @sbindir@ +libexecdir = @libexecdir@ +datadir = @datadir@ +sysconfdir = @sysconfdir@ +sharedstatedir = @sharedstatedir@ +localstatedir = @localstatedir@ +libdir = @libdir@ +infodir = @infodir@ +mandir = @mandir@ +includedir = @includedir@ +oldincludedir = /usr/include +pkgdatadir = $(datadir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +top_builddir = .. + +ACLOCAL = @ACLOCAL@ +AUTOCONF = @AUTOCONF@ +AUTOMAKE = @AUTOMAKE@ +AUTOHEADER = @AUTOHEADER@ + +INSTALL = @INSTALL@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_HEADER = $(INSTALL_DATA) +transform = @program_transform_name@ +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_alias = @build_alias@ +build_triplet = @build@ +host_alias = @host_alias@ +host_triplet = @host@ +target_alias = @target_alias@ +target_triplet = @target@ +ADD_CFLAGS = @ADD_CFLAGS@ +AMTAR = @AMTAR@ +AR = @AR@ +AS = @AS@ +AWK = @AWK@ +CC = @CC@ +CONFIGURE_DEPENDS = @CONFIGURE_DEPENDS@ +CPP = @CPP@ +DEPDIR = @DEPDIR@ +DLLTOOL = @DLLTOOL@ +EXEEXT = @EXEEXT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +LEX = @LEX@ +LIBTOOL = @LIBTOOL@ +LN_S = @LN_S@ +MAINT = @MAINT@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +PACKAGE = @PACKAGE@ +PATH = @PATH@ +RANLIB = @RANLIB@ +VERSION = @VERSION@ +YACC = @YACC@ +am__include = @am__include@ +am__quote = @am__quote@ +install_sh = @install_sh@ + +SUBDIRS = tests + +dist_pkgdata_DATA = *.[ch] +subdir = skeletons +mkinstalldirs = $(SHELL) $(top_srcdir)/mkinstalldirs +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +DIST_SOURCES = +DATA = $(dist_pkgdata_DATA) + + +RECURSIVE_TARGETS = info-recursive dvi-recursive install-info-recursive \ + uninstall-info-recursive all-recursive install-data-recursive \ + install-exec-recursive installdirs-recursive install-recursive \ + uninstall-recursive check-recursive installcheck-recursive +DIST_COMMON = README $(dist_pkgdata_DATA) Makefile.am Makefile.in +DIST_SUBDIRS = $(SUBDIRS) +all: all-recursive + +.SUFFIXES: + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs + +distclean-libtool: + -rm -f libtool +$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ Makefile.am $(top_srcdir)/configure.in $(ACLOCAL_M4) + cd $(top_srcdir) && \ + $(AUTOMAKE) --gnu skeletons/Makefile +Makefile: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.in $(top_builddir)/config.status + cd $(top_builddir) && \ + CONFIG_HEADERS= CONFIG_LINKS= \ + CONFIG_FILES=$(subdir)/$@ $(SHELL) ./config.status +uninstall-info-am: +install-dist_pkgdataDATA: $(dist_pkgdata_DATA) + @$(NORMAL_INSTALL) + $(mkinstalldirs) $(DESTDIR)$(pkgdatadir) + @list='$(dist_pkgdata_DATA)'; for p in $$list; do \ + if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ + f="`echo $$p | sed -e 's|^.*/||'`"; \ + echo " $(INSTALL_DATA) $$d$$p $(DESTDIR)$(pkgdatadir)/$$f"; \ + $(INSTALL_DATA) $$d$$p $(DESTDIR)$(pkgdatadir)/$$f; \ + done + +uninstall-dist_pkgdataDATA: + @$(NORMAL_UNINSTALL) + @list='$(dist_pkgdata_DATA)'; for p in $$list; do \ + f="`echo $$p | sed -e 's|^.*/||'`"; \ + echo " rm -f $(DESTDIR)$(pkgdatadir)/$$f"; \ + rm -f $(DESTDIR)$(pkgdatadir)/$$f; \ + done + +# This directory's subdirectories are mostly independent; you can cd +# into them and run `make' without going through this Makefile. +# To change the values of `make' variables: instead of editing Makefiles, +# (1) if the variable is set in `config.status', edit `config.status' +# (which will cause the Makefiles to be regenerated when you run `make'); +# (2) otherwise, pass the desired values on the `make' command line. +$(RECURSIVE_TARGETS): + @set fnord $(MAKEFLAGS); amf=$$2; \ + dot_seen=no; \ + target=`echo $@ | sed s/-recursive//`; \ + list='$(SUBDIRS)'; for subdir in $$list; do \ + echo "Making $$target in $$subdir"; \ + if test "$$subdir" = "."; then \ + dot_seen=yes; \ + local_target="$$target-am"; \ + else \ + local_target="$$target"; \ + fi; \ + (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ + || case "$$amf" in *=*) exit 1;; *k*) fail=yes;; *) exit 1;; esac; \ + done; \ + if test "$$dot_seen" = "no"; then \ + $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \ + fi; test -z "$$fail" + +mostlyclean-recursive clean-recursive distclean-recursive \ +maintainer-clean-recursive: + @set fnord $(MAKEFLAGS); amf=$$2; \ + dot_seen=no; \ + case "$@" in \ + distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \ + *) list='$(SUBDIRS)' ;; \ + esac; \ + rev=''; for subdir in $$list; do \ + if test "$$subdir" = "."; then :; else \ + rev="$$subdir $$rev"; \ + fi; \ + done; \ + rev="$$rev ."; \ + target=`echo $@ | sed s/-recursive//`; \ + for subdir in $$rev; do \ + echo "Making $$target in $$subdir"; \ + if test "$$subdir" = "."; then \ + local_target="$$target-am"; \ + else \ + local_target="$$target"; \ + fi; \ + (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ + || case "$$amf" in *=*) exit 1;; *k*) fail=yes;; *) exit 1;; esac; \ + done && test -z "$$fail" +tags-recursive: + list='$(SUBDIRS)'; for subdir in $$list; do \ + test "$$subdir" = . || (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) tags); \ + done + +tags: TAGS + +ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) + list='$(SOURCES) $(HEADERS) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + mkid -fID $$unique $(LISP) + +TAGS: tags-recursive $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + tags=; \ + here=`pwd`; \ + list='$(SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + test -f $$subdir/TAGS && tags="$$tags -i $$here/$$subdir/TAGS"; \ + fi; \ + done; \ + list='$(SOURCES) $(HEADERS) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + test -z "$(ETAGS_ARGS)$$unique$(LISP)$$tags" \ + || etags $(ETAGS_ARGS) $$tags $$unique $(LISP) + +GTAGS: + here=`CDPATH=: && cd $(top_builddir) && pwd` \ + && cd $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) $$here + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH + +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) + +top_distdir = .. +distdir = $(top_distdir)/$(PACKAGE)-$(VERSION) + +distdir: $(DISTFILES) + @for file in $(DISTFILES); do \ + if test -f $$file; then d=.; else d=$(srcdir); fi; \ + dir=`echo "$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test "$$dir" != "$$file" && test "$$dir" != "."; then \ + $(mkinstalldirs) "$(distdir)/$$dir"; \ + fi; \ + if test -d $$d/$$file; then \ + cp -pR $$d/$$file $(distdir) \ + || exit 1; \ + else \ + test -f $(distdir)/$$file \ + || cp -p $$d/$$file $(distdir)/$$file \ + || exit 1; \ + fi; \ + done + for subdir in $(SUBDIRS); do \ + if test "$$subdir" = .; then :; else \ + test -d $(distdir)/$$subdir \ + || mkdir $(distdir)/$$subdir \ + || exit 1; \ + (cd $$subdir && \ + $(MAKE) $(AM_MAKEFLAGS) \ + top_distdir="$(top_distdir)" \ + distdir=../$(distdir)/$$subdir \ + distdir) \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-recursive +all-am: Makefile $(DATA) +installdirs: installdirs-recursive +installdirs-am: + $(mkinstalldirs) $(DESTDIR)$(pkgdatadir) + +install: install-recursive +install-exec: install-exec-recursive +install-data: install-data-recursive +uninstall: uninstall-recursive + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-recursive +install-strip: + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + `test -z '$(STRIP)' || \ + echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -rm -f Makefile $(CONFIG_CLEAN_FILES) stamp-h stamp-h[0-9]* + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-recursive + +clean-am: clean-generic clean-libtool mostlyclean-am + +distclean: distclean-recursive + +distclean-am: clean-am distclean-generic distclean-libtool \ + distclean-tags + +dvi: dvi-recursive + +dvi-am: + +info: info-recursive + +info-am: + +install-data-am: install-dist_pkgdataDATA + +install-exec-am: + +install-info: install-info-recursive + +install-man: + +installcheck-am: + +maintainer-clean: maintainer-clean-recursive + +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-recursive + +mostlyclean-am: mostlyclean-generic mostlyclean-libtool + +uninstall-am: uninstall-dist_pkgdataDATA uninstall-info-am \ + uninstall-local + +uninstall-info: uninstall-info-recursive + +.PHONY: $(RECURSIVE_TARGETS) GTAGS all all-am check check-am clean \ + clean-generic clean-libtool clean-recursive distclean \ + distclean-generic distclean-libtool distclean-recursive \ + distclean-tags distdir dvi dvi-am dvi-recursive info info-am \ + info-recursive install install-am install-data install-data-am \ + install-data-recursive install-dist_pkgdataDATA install-exec \ + install-exec-am install-exec-recursive install-info \ + install-info-am install-info-recursive install-man \ + install-recursive install-strip installcheck installcheck-am \ + installdirs installdirs-am installdirs-recursive \ + maintainer-clean maintainer-clean-generic \ + maintainer-clean-recursive mostlyclean mostlyclean-generic \ + mostlyclean-libtool mostlyclean-recursive tags tags-recursive \ + uninstall uninstall-am uninstall-dist_pkgdataDATA \ + uninstall-info-am uninstall-info-recursive uninstall-local \ + uninstall-recursive + +uninstall-local: + -@echo -n " " + -rm -f -r $(DESTDIR)$(pkgdatadir) +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/skeletons/NULL.c b/skeletons/NULL.c new file mode 100644 index 00000000..9027e2c1 --- /dev/null +++ b/skeletons/NULL.c @@ -0,0 +1,51 @@ +/*- + * Copyright (c) 2003 Lev Walkin <vlm@lionet.info>. All rights reserved. + * Redistribution and modifications are permitted subject to BSD license. + */ +#include <NULL.h> +#include <BOOLEAN.h> /* Implemented in terms of BOOLEAN type */ + +/* + * NULL basic type description. + */ +static ber_tlv_tag_t asn1_DEF_NULL_tags[] = { + (ASN_TAG_CLASS_UNIVERSAL | (5 << 2)) +}; +asn1_TYPE_descriptor_t asn1_DEF_NULL = { + "NULL", + asn_generic_no_constraint, + BOOLEAN_decode_ber, /* Implemented in terms of BOOLEAN */ + NULL_encode_der, /* Special handling of DER encoding */ + NULL_print, + BOOLEAN_free, + 0, /* Use generic outmost tag fetcher */ + asn1_DEF_NULL_tags, + sizeof(asn1_DEF_NULL_tags)/sizeof(asn1_DEF_NULL_tags[0]), + 1, /* Single UNIVERSAL tag may be implicitly overriden */ + 0 /* Always in primitive form */ +}; + +der_enc_rval_t +NULL_encode_der(asn1_TYPE_descriptor_t *sd, void *ptr, + int tag_mode, ber_tlv_tag_t tag, + asn_app_consume_bytes_f *cb, void *app_key) { + der_enc_rval_t erval; + + erval.encoded = der_write_tags(sd, 0, tag_mode, tag, cb, app_key); + if(erval.encoded == -1) { + erval.failed_type = sd; + erval.structure_ptr = ptr; + } + + return erval; +} + +int +NULL_print(asn1_TYPE_descriptor_t *td, const void *sptr, int ilevel, + asn_app_consume_bytes_f *cb, void *app_key) { + if(sptr) { + return cb("<present>", 9, app_key); + } else { + return cb("<absent>", 8, app_key); + } +} diff --git a/skeletons/NULL.h b/skeletons/NULL.h new file mode 100644 index 00000000..14f5ffab --- /dev/null +++ b/skeletons/NULL.h @@ -0,0 +1,19 @@ +/*- + * Copyright (c) 2003 Lev Walkin <vlm@lionet.info>. All rights reserved. + * Redistribution and modifications are permitted subject to BSD license. + */ +#ifndef _NULL_H_ +#define _NULL_H_ + +#include <constr_TYPE.h> + +typedef struct NULL_s { + int value; +} NULL_t; + +extern asn1_TYPE_descriptor_t asn1_DEF_NULL; + +der_type_encoder_f NULL_encode_der; +asn_struct_print_f NULL_print; + +#endif /* _NULL_H_ */ diff --git a/skeletons/NativeEnumerated.c b/skeletons/NativeEnumerated.c new file mode 100644 index 00000000..b6d21ed2 --- /dev/null +++ b/skeletons/NativeEnumerated.c @@ -0,0 +1,32 @@ +/*- + * Copyright (c) 2004 Lev Walkin <vlm@lionet.info>. All rights reserved. + * Redistribution and modifications are permitted subject to BSD license. + */ +/* + * Please read the NativeInteger.h for the explanation wrt. differences between + * INTEGER and NativeInteger. + * Basically, both are decoders and encoders of ASN.1 INTEGER type, but this + * implementation deals with the standard (machine-specific) representation + * of them instead of using the platform-independent buffer. + */ +#include <NativeEnumerated.h> + +/* + * NativeEnumerated basic type description. + */ +static ber_tlv_tag_t asn1_DEF_NativeEnumerated_tags[] = { + (ASN_TAG_CLASS_UNIVERSAL | (10 << 2)) +}; +asn1_TYPE_descriptor_t asn1_DEF_NativeEnumerated = { + "ENUMERATED", /* The ASN.1 type is still ENUMERATED */ + asn_generic_no_constraint, + NativeInteger_decode_ber, + NativeInteger_encode_der, + NativeInteger_print, + NativeInteger_free, + 0, /* Use generic outmost tag fetcher */ + asn1_DEF_NativeEnumerated_tags, + sizeof(asn1_DEF_NativeEnumerated_tags)/sizeof(asn1_DEF_NativeEnumerated_tags[0]), + 1, /* Single UNIVERSAL tag may be implicitly overriden */ + 0 /* Always in primitive form */ +}; diff --git a/skeletons/NativeEnumerated.h b/skeletons/NativeEnumerated.h new file mode 100644 index 00000000..681147c1 --- /dev/null +++ b/skeletons/NativeEnumerated.h @@ -0,0 +1,19 @@ +/*- + * Copyright (c) 2004 Lev Walkin <vlm@lionet.info>. All rights reserved. + * Redistribution and modifications are permitted subject to BSD license. + */ +/* + * This type differs from the standard ENUMERATED in that it is modelled using + * the fixed machine type (long, int, short), so it can hold only values of + * limited length. There is no type (i.e., NativeEnumerated_t, any integer type + * will do). + * This type may be used when integer range is limited by subtype constraints. + */ +#ifndef _NativeEnumerated_H_ +#define _NativeEnumerated_H_ + +#include <NativeInteger.h> + +extern asn1_TYPE_descriptor_t asn1_DEF_NativeEnumerated; + +#endif /* _NativeEnumerated_H_ */ diff --git a/skeletons/NativeInteger.c b/skeletons/NativeInteger.c new file mode 100644 index 00000000..d42540ef --- /dev/null +++ b/skeletons/NativeInteger.c @@ -0,0 +1,193 @@ +/*- + * Copyright (c) 2004 Lev Walkin <vlm@lionet.info>. All rights reserved. + * Redistribution and modifications are permitted subject to BSD license. + */ +/* + * Please read the NativeInteger.h for the explanation wrt. differences between + * INTEGER and NativeInteger. + * Basically, both are decoders and encoders of ASN.1 INTEGER type, but this + * implementation deals with the standard (machine-specific) representation + * of them instead of using the platform-independent buffer. + */ +#include <NativeInteger.h> +#include <INTEGER.h> +#include <assert.h> + +/* + * NativeInteger basic type description. + */ +static ber_tlv_tag_t asn1_DEF_NativeInteger_tags[] = { + (ASN_TAG_CLASS_UNIVERSAL | (2 << 2)) +}; +asn1_TYPE_descriptor_t asn1_DEF_NativeInteger = { + "INTEGER", /* The ASN.1 type is still INTEGER */ + asn_generic_no_constraint, + NativeInteger_decode_ber, + NativeInteger_encode_der, + NativeInteger_print, + NativeInteger_free, + 0, /* Use generic outmost tag fetcher */ + asn1_DEF_NativeInteger_tags, + sizeof(asn1_DEF_NativeInteger_tags)/sizeof(asn1_DEF_NativeInteger_tags[0]), + 1, /* Single UNIVERSAL tag may be implicitly overriden */ + 0 /* Always in primitive form */ +}; + +/* + * Decode INTEGER type. + */ +ber_dec_rval_t +NativeInteger_decode_ber(asn1_TYPE_descriptor_t *td, + void **int_ptr, void *buf_ptr, size_t size, int tag_mode) { + int *Int = *int_ptr; + ber_dec_rval_t rval; + ber_dec_ctx_t ctx = { 0 }; + ber_tlv_len_t length; + + /* + * If the structure is not there, allocate it. + */ + if(Int == NULL) { + Int = *int_ptr = CALLOC(1, sizeof(*Int)); + if(Int == NULL) { + rval.code = RC_FAIL; + rval.consumed = 0; + return rval; + } + } + + ASN_DEBUG("Decoding %s as INTEGER (tm=%d)", + td->name, tag_mode); + + /* + * Check tags. + */ + rval = ber_check_tags(td, &ctx, + buf_ptr, size, tag_mode, &length, 0); + if(rval.code != RC_OK) + return rval; + + ASN_DEBUG("%s length is %d bytes", td->name, (int)length); + + /* + * Make sure we have this length. + */ + buf_ptr += rval.consumed; + size -= rval.consumed; + if(length > size) { + rval.code = RC_WMORE; + rval.consumed = 0; + return rval; + } + + /* + * ASN.1 encoded INTEGER: buf_ptr, length + * Fill the Int, at the same time checking for overflow. + * If overflow occured, return with RC_FAIL. + */ + { + INTEGER_t tmp; + long l; + tmp.buf = buf_ptr; + tmp.size = length; + + if(asn1_INTEGER2long(&tmp, &l)) { + rval.code = RC_FAIL; + rval.consumed = 0; + return rval; + } + + *Int = l; + + /* + * Note that int might be shorter than long. + * This expression hopefully will be optimized away + * by compiler. + */ + if(sizeof(int) != sizeof(long) && (*Int != l)) { + *Int = 0; /* Safe value */ + rval.code = RC_FAIL; + rval.consumed = 0; + return rval; + } + } + + rval.code = RC_OK; + rval.consumed += length; + + ASN_DEBUG("Took %ld/%ld bytes to encode %s (%d)", + (long)rval.consumed, (long)length, td->name, *Int); + + return rval; +} + +/* + * Encode the NativeInteger using the standard INTEGER type DER encoder. + */ +der_enc_rval_t +NativeInteger_encode_der(asn1_TYPE_descriptor_t *sd, void *ptr, + int tag_mode, ber_tlv_tag_t tag, + asn_app_consume_bytes_f *cb, void *app_key) { + unsigned int Int = *(unsigned int *)ptr; /* Disable sign ext. */ + der_enc_rval_t erval; + INTEGER_t tmp; + +#ifdef WORDS_BIGENDIAN /* Opportunistic optimization */ + + tmp.buf = ∬ + tmp.size = sizeof(Int); + +#else /* Works even if WORDS_BIGENDIAN is not set where should've been */ + uint8_t buf[sizeof(int)]; + uint8_t *p; + + /* Prepare fake INTEGER */ + for(p = buf + sizeof(buf) - 1; p >= buf; p--, Int >>= 8) + *p = Int & 0xff; + + tmp.buf = buf; + tmp.size = sizeof(buf); +#endif /* WORDS_BIGENDIAN */ + + /* Encode fake INTEGER */ + erval = INTEGER_encode_der(sd, &tmp, tag_mode, tag, cb, app_key); + if(erval.encoded == -1) { + assert(erval.structure_ptr == &tmp); + erval.structure_ptr = ptr; + } + return erval; +} + +/* + * INTEGER specific human-readable output. + */ +int +NativeInteger_print(asn1_TYPE_descriptor_t *td, const void *sptr, int ilevel, + asn_app_consume_bytes_f *cb, void *app_key) { + const int *Int = sptr; + char scratch[32]; + int ret; + + if(Int) { + ret = snprintf(scratch, sizeof(scratch), "%d", *Int); + assert(ret > 0 && ret < sizeof(scratch)); + return cb(scratch, ret, app_key); + } else { + return cb("<absent>", 8, app_key); + } +} + +void +NativeInteger_free(asn1_TYPE_descriptor_t *td, void *ptr, int contents_only) { + + if(!td || !ptr) + return; + + ASN_DEBUG("Freeing %s as INTEGER (%d, %p, Native)", + td->name, contents_only, ptr); + + if(!contents_only) { + FREEMEM(ptr); + } +} + diff --git a/skeletons/NativeInteger.h b/skeletons/NativeInteger.h new file mode 100644 index 00000000..e0f26c78 --- /dev/null +++ b/skeletons/NativeInteger.h @@ -0,0 +1,24 @@ +/*- + * Copyright (c) 2004 Lev Walkin <vlm@lionet.info>. All rights reserved. + * Redistribution and modifications are permitted subject to BSD license. + */ +/* + * This type differs from the standard INTEGER in that it is modelled using + * the fixed machine type (long, int, short), so it can hold only values of + * limited length. There is no type (i.e., NativeInteger_t, any integer type + * will do). + * This type may be used when integer range is limited by subtype constraints. + */ +#ifndef _NativeInteger_H_ +#define _NativeInteger_H_ + +#include <constr_TYPE.h> + +extern asn1_TYPE_descriptor_t asn1_DEF_NativeInteger; + +ber_type_decoder_f NativeInteger_decode_ber; +der_type_encoder_f NativeInteger_encode_der; +asn_struct_print_f NativeInteger_print; +asn_struct_free_f NativeInteger_free; + +#endif /* _NativeInteger_H_ */ diff --git a/skeletons/NumericString.c b/skeletons/NumericString.c new file mode 100644 index 00000000..a007496c --- /dev/null +++ b/skeletons/NumericString.c @@ -0,0 +1,62 @@ +/*- + * Copyright (c) 2003 Lev Walkin <vlm@lionet.info>. All rights reserved. + * Redistribution and modifications are permitted subject to BSD license. + */ +#include <NumericString.h> + +/* + * NumericString basic type description. + */ +static ber_tlv_tag_t asn1_DEF_NumericString_tags[] = { + (ASN_TAG_CLASS_UNIVERSAL | (18 << 2)) +}; +asn1_TYPE_descriptor_t asn1_DEF_NumericString = { + "NumericString", + NumericString_constraint, + OCTET_STRING_decode_ber, /* Implemented in terms of OCTET STRING */ + OCTET_STRING_encode_der, /* Implemented in terms of OCTET STRING */ + OCTET_STRING_print_ascii, /* ASCII subset */ + OCTET_STRING_free, + 0, /* Use generic outmost tag fetcher */ + asn1_DEF_NumericString_tags, + sizeof(asn1_DEF_NumericString_tags) + / sizeof(asn1_DEF_NumericString_tags[0]), + 1, /* Single UNIVERSAL tag may be implicitly overriden */ + -1, /* Both ways are fine */ +}; + +int +NumericString_constraint(asn1_TYPE_descriptor_t *td, const void *sptr, + asn_app_consume_bytes_f *app_errlog, void *app_key) { + const NumericString_t *st = sptr; + + if(st && st->buf) { + uint8_t *buf = st->buf; + uint8_t *end = buf + st->size; + + /* + * Check the alphabet of the NumericString. + * ASN.1:1984 (X.409) + */ + for(; buf < end; buf++) { + switch(*buf) { + case 0x20: + case 0x30: case 0x31: case 0x32: case 0x33: case 0x34: + case 0x35: case 0x36: case 0x37: case 0x38: case 0x39: + continue; + } + _ASN_ERRLOG("%s: value byte %d " + "not in NumericString alphabet (%d)", + td->name, + (buf - st->buf) + 1, + *buf + ); + return -1; + } + } else { + _ASN_ERRLOG("%s: value not given", td->name); + return -1; + } + + return 0; +} diff --git a/skeletons/NumericString.h b/skeletons/NumericString.h new file mode 100644 index 00000000..71f72c76 --- /dev/null +++ b/skeletons/NumericString.h @@ -0,0 +1,17 @@ +/*- + * Copyright (c) 2003 Lev Walkin <vlm@lionet.info>. All rights reserved. + * Redistribution and modifications are permitted subject to BSD license. + */ +#ifndef _NumericString_H_ +#define _NumericString_H_ + +#include <constr_TYPE.h> +#include <OCTET_STRING.h> + +typedef OCTET_STRING_t NumericString_t; /* Implemented in terms of OCTET STRING */ + +extern asn1_TYPE_descriptor_t asn1_DEF_NumericString; + +asn_constr_check_f NumericString_constraint; + +#endif /* _NumericString_H_ */ diff --git a/skeletons/OBJECT_IDENTIFIER.c b/skeletons/OBJECT_IDENTIFIER.c new file mode 100644 index 00000000..bcbcdcaf --- /dev/null +++ b/skeletons/OBJECT_IDENTIFIER.c @@ -0,0 +1,356 @@ +/*- + * Copyright (c) 2003, 2004 Lev Walkin <vlm@lionet.info>. All rights reserved. + * Redistribution and modifications are permitted subject to BSD license. + */ +#include <OBJECT_IDENTIFIER.h> +#include <assert.h> +#include <errno.h> + +/* + * OBJECT IDENTIFIER basic type description. + */ +static ber_tlv_tag_t asn1_DEF_OBJECT_IDENTIFIER_tags[] = { + (ASN_TAG_CLASS_UNIVERSAL | (6 << 2)) +}; +asn1_TYPE_descriptor_t asn1_DEF_OBJECT_IDENTIFIER = { + "OBJECT IDENTIFIER", + OBJECT_IDENTIFIER_constraint, + INTEGER_decode_ber, /* Implemented in terms of INTEGER type */ + OBJECT_IDENTIFIER_encode_der, + OBJECT_IDENTIFIER_print, + INTEGER_free, + 0, /* Use generic outmost tag fetcher */ + asn1_DEF_OBJECT_IDENTIFIER_tags, + sizeof(asn1_DEF_OBJECT_IDENTIFIER_tags) + / sizeof(asn1_DEF_OBJECT_IDENTIFIER_tags[0]), + 1, /* Single UNIVERSAL tag may be implicitly overriden */ + 0 /* Always in primitive form */ +}; + + +/* + * Encode OBJECT IDENTIFIER type using DER. + */ +der_enc_rval_t +OBJECT_IDENTIFIER_encode_der(asn1_TYPE_descriptor_t *sd, void *ptr, + int tag_mode, ber_tlv_tag_t tag, + asn_app_consume_bytes_f *cb, void *app_key) { + der_enc_rval_t erval; + OBJECT_IDENTIFIER_t *st = ptr; + + ASN_DEBUG("%s %s as OBJECT IDENTIFIER (tm=%d)", + cb?"Encoding":"Estimating", sd->name, tag_mode); + + erval.encoded = der_write_tags(sd, st->size, tag_mode, tag, + cb, app_key); + ASN_DEBUG("OBJECT IDENTIFIER %s wrote tags %d", + sd->name, (int)erval.encoded); + if(erval.encoded == -1) { + erval.failed_type = sd; + erval.structure_ptr = ptr; + return erval; + } + + if(cb && st->buf) { + ssize_t ret; + + ret = cb(st->buf, st->size, app_key); + if(ret == -1) { + erval.encoded = -1; + erval.failed_type = sd; + erval.structure_ptr = ptr; + return erval; + } + } else { + assert(st->buf || st->size == 0); + } + + erval.encoded += st->size; + + return erval; +} + +int +OBJECT_IDENTIFIER_constraint(asn1_TYPE_descriptor_t *td, const void *sptr, + asn_app_consume_bytes_f *app_errlog, void *app_key) { + const OBJECT_IDENTIFIER_t *st = sptr; + + if(st && st->buf) { + if(st->size < 1) { + _ASN_ERRLOG("%s: at least one numerical value expected", + td->name); + return -1; + } + } else { + _ASN_ERRLOG("%s: value not given", td->name); + return -1; + } + + return 0; +} + +int +OBJECT_IDENTIFIER_get_arc_l(uint8_t *arcbuf, int arclen, int add, unsigned long *rvalue) { + unsigned long accum; + uint8_t *arcend = arcbuf + arclen; + + if(arclen * 7 > 8 * sizeof(accum)) { + if(arclen * 7 <= 8 * (sizeof(accum) + 1)) { + if((*arcbuf & ~0x8f)) { + errno = ERANGE; /* Overflow */ + return -1; + } + } else { + errno = ERANGE; /* Overflow */ + return -1; + } + } + + /* Gather all bits into the accumulator */ + for(accum = 0; arcbuf < arcend; arcbuf++) + accum = (accum << 7) | (*arcbuf & ~0x80); + + accum += add; /* Actually, a negative value */ + assert(accum >= 0); + + *rvalue = accum; + + return 0; +} + +int +OBJECT_IDENTIFIER_print_arc(uint8_t *arcbuf, int arclen, int add, + asn_app_consume_bytes_f *cb, void *app_key) { + char scratch[64]; /* Conservative estimate */ + unsigned long accum; /* Bits accumulator */ + char *p; /* Position in the scratch buffer */ + + if(OBJECT_IDENTIFIER_get_arc_l(arcbuf, arclen, add, &accum)) + return -1; + + /* Fill the scratch buffer in reverse. */ + p = scratch + sizeof(scratch); + for(; accum; accum /= 10) + *(--p) = (accum % 10) + 0x30; + + return cb(p, sizeof(scratch) - (p - scratch), app_key); +} + +int +OBJECT_IDENTIFIER_print(asn1_TYPE_descriptor_t *td, const void *sptr, + int ilevel, asn_app_consume_bytes_f *cb, void *app_key) { + const OBJECT_IDENTIFIER_t *st = sptr; + int startn; + int add = 0; + int i; + + if(!st || !st->buf) + return cb("<absent>", 8, app_key); + + /* Dump preamble */ + if(cb("{ ", 2, app_key)) + return -1; + + for(i = 0, startn = 0; i < st->size; i++) { + uint8_t b = st->buf[i]; + if((b & 0x80)) /* Continuation expected */ + continue; + + if(startn == 0) { + /* + * First two arcs are encoded through the backdoor. + */ + if(i) { + add = -80; + if(cb("2", 1, app_key)) return -1; + } else if(b <= 39) { + add = 0; + if(cb("0", 1, app_key)) return -1; + } else if(b < 79) { + add = -40; + if(cb("1", 1, app_key)) return -1; + } else { + add = -80; + if(cb("2", 1, app_key)) return -1; + } + } + + if(cb(" ", 1, app_key)) /* Separate arcs */ + return -1; + + if(OBJECT_IDENTIFIER_print_arc(&st->buf[startn], + i - startn + 1, add, + cb, app_key)) + return -1; + startn = i + 1; + add = 0; + } + + return cb(" }", 2, app_key); +} + +int +OBJECT_IDENTIFIER_get_arcs_l(OBJECT_IDENTIFIER_t *oid, + unsigned long *arcs, int arcs_slots) { + unsigned long arc_value; + int cur_arc = 0; + int startn = 0; + int add = 0; + int i; + + if(!oid || !oid->buf) { + errno = EINVAL; + return -1; + } + + for(i = 0; i < oid->size; i++) { + uint8_t b = oid->buf[i]; + if((b & 0x80)) /* Continuation expected */ + continue; + + if(startn == 0) { + /* + * First two arcs are encoded through the backdoor. + */ + if(i) { + add = -80; + if(cur_arc < arcs_slots) arcs[cur_arc] = 2; + cur_arc++; + } else if(b <= 39) { + add = 0; + if(cur_arc < arcs_slots) arcs[cur_arc] = 0; + cur_arc++; + } else if(b < 79) { + add = -40; + if(cur_arc < arcs_slots) arcs[cur_arc] = 1; + cur_arc++; + } else { + add = -80; + if(cur_arc < arcs_slots) arcs[cur_arc] = 2; + cur_arc++; + } + } + + /* Do not fill */ + if(cur_arc >= arcs_slots) { + startn = i + 1; + continue; + } + + if(OBJECT_IDENTIFIER_get_arc_l(&oid->buf[startn], + i - startn + 1, + add, &arc_value)) + return -1; + arcs[cur_arc++] = arc_value; + startn = i + 1; + add = 0; + } + + return cur_arc; +} + +int +OBJECT_IDENTIFIER_set_arcs_l(OBJECT_IDENTIFIER_t *oid, unsigned long *arcs, int arcs_slots) { + uint8_t *buf; + uint8_t *bp; + unsigned long long first_value; + int size; + int i; + + if(oid == NULL || arcs == NULL || arcs_slots < 2) { + errno = EINVAL; + return -1; + } + + if(arcs[0] <= 1) { + if(arcs[1] >= 39) { + /* 8.19.4: At most 39 subsequent values (including 0) */ + errno = ERANGE; + return -1; + } + } else if(arcs[0] > 2) { + /* 8.19.4: Only three values are allocated from the root node */ + errno = ERANGE; + return -1; + } + + first_value = arcs[0] * 40 + arcs[1]; + + /* + * Roughly estimate the maximum size necessary to encode these arcs. + */ + size = ((sizeof(arcs[0]) + 1) * 8 / 7) * arcs_slots; + bp = buf = MALLOC(size + 1); + if(!buf) { + /* ENOMEM */ + return -1; + } + + /* + * Encode the arcs and refine the encoding size. + */ + size = 0; + + { + uint8_t tbuf[sizeof(first_value) * 2]; + uint8_t *tp = tbuf; + int arc_len = 0; + int add = 0; + + for(; first_value; first_value >>= 7) { + unsigned int b7 = first_value & 0x7f; + *tp++ = 0x80 | b7; + add++; + if(b7) { arc_len += add; add = 0; } + } + + if(arc_len) { + tp = &tbuf[arc_len - 1]; + /* The last octet does not have bit 8 set. */ + *tbuf &= 0x7f; + for(; tp >= tbuf; tp--) + *bp++ = *tp; + size += arc_len; + } else { + *bp++ = 0; + size++; + } + } + + for(i = 2; i < arcs_slots; i++) { + unsigned long value = arcs[i]; + uint8_t tbuf[sizeof(value) * 2]; /* Conservatively sized */ + uint8_t *tp = tbuf; + int arc_len = 0; + int add = 0; + + for(; value; value >>= 7) { + unsigned int b7 = value & 0x7F; + *tp++ = 0x80 | b7; + add++; + if(b7) { arc_len += add; add = 0; } + } + + if(arc_len) { + tp = &tbuf[arc_len - 1]; + /* The last octet does not have bit 8 set. */ + *tbuf &= 0x7f; + for(; tp >= tbuf; tp--) + *bp++ = *tp; + size += arc_len; + } else { + *bp++ = 0; + size++; + } + } + + /* + * Replace buffer. + */ + oid->size = size; + bp = oid->buf; + oid->buf = buf; + if(bp) FREEMEM(bp); + + return 0; +} diff --git a/skeletons/OBJECT_IDENTIFIER.h b/skeletons/OBJECT_IDENTIFIER.h new file mode 100644 index 00000000..3e71f316 --- /dev/null +++ b/skeletons/OBJECT_IDENTIFIER.h @@ -0,0 +1,95 @@ +/*- + * Copyright (c) 2003, 2004 Lev Walkin <vlm@lionet.info>. All rights reserved. + * Redistribution and modifications are permitted subject to BSD license. + */ +#ifndef _OBJECT_IDENTIFIER_H_ +#define _OBJECT_IDENTIFIER_H_ + +#include <constr_TYPE.h> +#include <INTEGER.h> + +typedef INTEGER_t OBJECT_IDENTIFIER_t; /* Implemented in terms of INTEGER */ + +extern asn1_TYPE_descriptor_t asn1_DEF_OBJECT_IDENTIFIER; + +der_type_encoder_f OBJECT_IDENTIFIER_encode_der; +asn_constr_check_f OBJECT_IDENTIFIER_constraint; +asn_struct_print_f OBJECT_IDENTIFIER_print; + +/********************************** + * Some handy conversion routines * + **********************************/ + +/* + * Print the specified OBJECT IDENTIFIER arc. + */ +int OBJECT_IDENTIFIER_print_arc(uint8_t *arcbuf, int arclen, + int add, /* Arbitrary offset, required to process the first two arcs */ + asn_app_consume_bytes_f *cb, void *app_key); + +/* + * This function fills an (_arcs) array with OBJECT IDENTIFIER arcs + * up to specified (_arcs_slots) elements. + * The function always returns the real number of arcs, even if there is no + * sufficient (_arcs_slots) provided. + * + * EXAMPLE: + * void print_arcs(OBJECT_IDENTIFIER_t *oid) { + * unsigned long fixed_arcs[10]; // Try with fixed space first + * unsigned long *arcs = fixed_arcs; + * int arcs_slots = sizeof(fixed_arcs)/sizeof(fixed_arcs[0]); // 10 + * int count; // Real number of arcs. + * int i; + * + * count = OBJECT_IDENTIFIER_get_arcs_l(oid, arcs, arcs_slots); + * // If necessary, reallocate arcs array and try again. + * if(count > arcs_slots) { + * arcs_slots = count; + * arcs = malloc(arcs_slots * sizeof(arcs[0])); + * if(!arcs) return; + * count = OBJECT_IDENTIFIER_get_arcs_l(oid, + * arcs, arcs_slots); + * assert(count == arcs_slots); + * } + * + * // Print the contents of the arcs array. + * for(i = 0; i < count; i++) + * printf("%d\n", arcs[i]); + * + * // Avoid memory leak. + * if(arcs != fixed_arcs) free(arcs); + * } + * + * RETURN VALUES: + * -1/EINVAL: Invalid arguments (oid is missing) + * -1/ERANGE: One or more arcs have value out of array cell type range. + * >=0: Number of arcs contained in the OBJECT IDENTIFIER + */ +int OBJECT_IDENTIFIER_get_arcs_l(OBJECT_IDENTIFIER_t *_oid, + unsigned long *_arcs, int _arcs_slots); +/* +int OBJECT_IDENTIFIER_get_arcs_im(OBJECT_IDENTIFIER_t *_oid, + uintmax_t *_arcs, int _arcs_slots); + */ + + +/* + * This functions initializes the OBJECT IDENTIFIER object with + * the given set of arcs. + * The minimum of two arcs must be present; some restrictions apply. + * RETURN VALUES: + * -1/EINVAL: Invalid arguments + * -1/ERANGE: The first two arcs do not conform to ASN.1 restrictions. + * -1/ENOMEM: Memory allocation failed + * 0: The object was initialized with new arcs. + */ +int OBJECT_IDENTIFIER_set_arcs_l(OBJECT_IDENTIFIER_t *_oid, + unsigned long *arcs, int arcs_slots); + +/* + * Internal functions. + */ +int OBJECT_IDENTIFIER_get_arc_l(uint8_t *arcbuf, int arclen, + int add, unsigned long *value); + +#endif /* _OBJECT_IDENTIFIER_H_ */ diff --git a/skeletons/OCTET_STRING.c b/skeletons/OCTET_STRING.c new file mode 100644 index 00000000..fc5c532c --- /dev/null +++ b/skeletons/OCTET_STRING.c @@ -0,0 +1,588 @@ +/*- + * Copyright (c) 2003 Lev Walkin <vlm@lionet.info>. All rights reserved. + * Redistribution and modifications are permitted subject to BSD license. + */ +#include <OCTET_STRING.h> +#include <assert.h> +#include <errno.h> + +/* + * OCTET STRING basic type description. + */ +static ber_tlv_tag_t asn1_DEF_OCTET_STRING_tags[] = { + (ASN_TAG_CLASS_UNIVERSAL | (4 << 2)) +}; +asn1_TYPE_descriptor_t asn1_DEF_OCTET_STRING = { + "OCTET STRING", + asn_generic_no_constraint, + OCTET_STRING_decode_ber, + OCTET_STRING_encode_der, + OCTET_STRING_print, /* non-ascii stuff, generally */ + OCTET_STRING_free, + 0, /* Use generic outmost tag fetcher */ + asn1_DEF_OCTET_STRING_tags, + sizeof(asn1_DEF_OCTET_STRING_tags) + / sizeof(asn1_DEF_OCTET_STRING_tags[0]), + 1, /* Single UNIVERSAL tag may be implicitly overriden */ + -1, /* Both ways are fine (primitive and constructed) */ +}; + +#define _CH_PHASE(ctx, inc) do { \ + if(ctx->phase == 0) \ + ctx->step = 0; \ + ctx->phase += inc; \ + } while(0) +#define NEXT_PHASE(ctx) _CH_PHASE(ctx, +1) +#define PREV_PHASE(ctx) _CH_PHASE(ctx, -1) + +#define ADVANCE(num_bytes) do { \ + size_t num = num_bytes; \ + buf_ptr += num; \ + size -= num; \ + consumed_myself += num; \ + } while(0) + +#define RETURN(_code) do { \ + rval.code = _code; \ + rval.consumed = consumed_myself;\ + return rval; \ + } while(0) + +#define APPEND(bufptr, bufsize) do { \ + int _ns = ctx->step; /* Allocated */ \ + if(_ns <= (st->size + bufsize)) { \ + void *ptr; \ + do { _ns = _ns ? _ns<<2 : 16; } \ + while(_ns <= (st->size + bufsize)); \ + ptr = REALLOC(st->buf, _ns); \ + if(ptr) { \ + st->buf = ptr; \ + ctx->step = _ns; \ + } else { \ + RETURN(RC_FAIL); \ + } \ + } \ + memcpy(st->buf + st->size, bufptr, bufsize); \ + st->size += bufsize; \ + if(st->size < 0) \ + /* Why even care?.. JIC */ \ + RETURN(RC_FAIL); \ + /* Convenient nul-termination */ \ + st->buf[st->size] = '\0'; \ + } while(0) + +/* + * The main reason why ASN.1 is still alive is that too much time and effort + * is necessary for learning it more or less adequately, thus creating a gut + * necessity to demonstrate that aquired skill everywhere afterwards. + * No, I am not going to explain what the following stuff is. + */ +struct _stack_el { + ber_tlv_len_t left; /* What's left to read */ + int want_nulls; /* Want null "end of content" octets? */ + int bits_chopped; /* Flag in BIT STRING mode */ + struct _stack_el *prev; + struct _stack_el *next; +}; +struct _stack { + struct _stack_el *tail; + struct _stack_el *cur_ptr; +}; + +static struct _stack_el * +_add_stack_el(struct _stack *st) { + struct _stack_el *nel; + + if(st->cur_ptr && st->cur_ptr->next) { + nel = st->cur_ptr->next; + nel->left = 0; + nel->want_nulls = 0; + nel->bits_chopped = 0; + } else { + nel = CALLOC(1, sizeof(struct _stack_el)); + if(nel == NULL) + return NULL; + + if(st->tail) { + st->tail->next = nel; + } + nel->prev = st->tail; + st->tail = nel; + } + + st->cur_ptr = nel; + + return nel; +} + +static struct _stack * +_new_stack() { + struct _stack *st; + st = CALLOC(1, sizeof(struct _stack)); + if(st == NULL) + return NULL; + + st->cur_ptr = _add_stack_el(st); + if(st->cur_ptr == NULL) { + FREEMEM(st); + return NULL; + } + + return st; +} + +/* + * Decode OCTET STRING type. + */ +ber_dec_rval_t +OCTET_STRING_decode_ber(asn1_TYPE_descriptor_t *td, + void **os_structure, void *buf_ptr, size_t size, int tag_mode) { + OCTET_STRING_t *st = *os_structure; + ber_dec_rval_t rval; + ber_dec_ctx_t *ctx; + ssize_t consumed_myself = 0; + struct _stack *stck; /* A stack structure */ + struct _stack_el *sel; /* Stack element */ + int tlv_constr; + /* + * This is a some sort of a hack. + * The OCTET STRING decoder is being used in BIT STRING mode. + */ + int is_bit_str = td->specifics?1:0; + + ASN_DEBUG("Decoding %s as %s (%ld)", + td->name, + is_bit_str ? "BIT STRING" : "OCTET STRING", + (long)size); + + /* + * Create the string if does not exist. + */ + if(st == NULL) { + st = *os_structure = CALLOC(1, sizeof(*st)); + if(st == NULL) + RETURN(RC_FAIL); + } + + /* Restore parsing context */ + ctx = &st->_ber_dec_ctx; + + switch(ctx->phase) { + case 0: + /* + * Check tags. + */ + rval = ber_check_tags(td, ctx, + buf_ptr, size, tag_mode, + &ctx->left, &tlv_constr); + if(rval.code != RC_OK) { + RETURN(rval.code); + } + + ASN_DEBUG("OS length is %d bytes, form %d", + (int)ctx->left, tlv_constr); + + if(tlv_constr) { + /* + * Complex operation, requires stack of expectations. + */ + ctx->ptr = _new_stack(); + if(ctx->ptr) { + stck = ctx->ptr; + if(ctx->left < 0) { + stck->cur_ptr->want_nulls = -ctx->left; + stck->cur_ptr->left = -1; + } else { + stck->cur_ptr->want_nulls = 0; + stck->cur_ptr->left = ctx->left; + } + ASN_DEBUG("Expectation left=%d wn=%d added", + stck->cur_ptr->left, + stck->cur_ptr->want_nulls); + if(is_bit_str) { + APPEND("\0", 1); + } + } else { + RETURN(RC_FAIL); + } + } else { + /* + * Jump into stackless primitive decoding. + */ + _CH_PHASE(ctx, 3); + ADVANCE(rval.consumed); + goto phase3; + } + + ADVANCE(rval.consumed); + NEXT_PHASE(ctx); + /* Fall through */ + case 1: + phase1: + /* + * Fill the stack with expectations. + */ + stck = ctx->ptr; + sel = stck->cur_ptr; + do { + ber_tlv_tag_t tlv_tag; + ber_tlv_len_t tlv_len; + ssize_t tl, ll; + + tl = ber_fetch_tag(buf_ptr, size, &tlv_tag); + switch(tl) { + case -1: RETURN(RC_FAIL); + case 0: RETURN(RC_WMORE); + } + + tlv_constr = BER_TLV_CONSTRUCTED(buf_ptr); + + ll = ber_fetch_length(tlv_constr, + buf_ptr + tl, size - tl, &tlv_len); + ASN_DEBUG("Got tag=%s, tl=%d, len=%d, ll=%d, {%d, %d}", + ber_tlv_tag_string(tlv_tag), tl, tlv_len, ll, + ((uint8_t *)buf_ptr)[0], + ((uint8_t *)buf_ptr)[1]); + switch(ll) { + case -1: RETURN(RC_FAIL); + case 0: RETURN(RC_WMORE); + } + + if(sel->want_nulls + && ((uint8_t *)buf_ptr)[0] == 0 + && ((uint8_t *)buf_ptr)[1] == 0) + { + sel->want_nulls--; + if(sel->want_nulls == 0) { + /* Move to the next expectation */ + sel = stck->cur_ptr = sel->prev; + if(sel == NULL) { + ADVANCE(2); + break; + } + } + if(sel->want_nulls) { + /* + * Simulate while(TRUE) for this loop. + * This is necessary to fetch the next + * "end of content" expectation. + */ + ADVANCE(2); + tlv_constr = 1; + continue; + } + } else if(tlv_tag != td->tags[td->tags_count-1]) { + char buf[2][32]; + ber_tlv_tag_snprint(tlv_tag, + buf[0], sizeof(buf[0])); + ber_tlv_tag_snprint(td->tags[td->tags_count-1], + buf[1], sizeof(buf[1])); + ASN_DEBUG("Tag does not match expectation: %s != %s", + buf[0], buf[1]); + RETURN(RC_FAIL); + } + + /* + * Append a new expectation. + */ + sel = _add_stack_el(stck); + if(sel) { + sel->want_nulls = (tlv_len==-1); + sel->left = tlv_len; + ASN_DEBUG("Expectation %d %d added", + sel->left, sel->want_nulls); + } else { + RETURN(RC_FAIL); + } + + ADVANCE(tl+ll); + } while(tlv_constr); + if(sel == NULL) { + /* Finished operation, "phase out" */ + _CH_PHASE(ctx, +3); + break; + } + + NEXT_PHASE(ctx); + /* Fall through */ + case 2: + stck = ctx->ptr; + sel = stck->cur_ptr; + ASN_DEBUG("Phase 2: Need %ld bytes, size=%ld", + (long)sel->left, (long)size); + { + ber_tlv_len_t len; + + assert(sel->left >= 0); + + len = (size < sel->left) ? size : sel->left; + if(len > 0) { + if(is_bit_str && sel->bits_chopped == 0) { + /* + * Finalize the previous chunk: + * strip down unused bits. + */ + st->buf[st->size-1] &= 0xff << st->buf[0]; + + APPEND((buf_ptr+1), (len - 1)); + st->buf[0] = *(uint8_t *)buf_ptr; + sel->bits_chopped = 1; + } else { + APPEND(buf_ptr, len); + } + ADVANCE(len); + sel->left -= len; + } + + if(sel->left) { + RETURN(RC_WMORE); + } else { + sel->left = 0; + if(sel->prev) + sel = stck->cur_ptr = sel->prev; + PREV_PHASE(ctx); + goto phase1; + } + } + break; + case 3: + phase3: + /* + * Primitive form, no stack required. + */ + if(size < ctx->left) { + APPEND(buf_ptr, size); + ctx->left -= size; + ADVANCE(size); + RETURN(RC_WMORE); + } else { + APPEND(buf_ptr, ctx->left); + ADVANCE(ctx->left); + ctx->left = 0; + + NEXT_PHASE(ctx); + } + break; + } + + /* + * BIT STRING-specific processing. + */ + if(is_bit_str && st->size >= 2) { + /* Finalize BIT STRING: zero out unused bits. */ + st->buf[st->size-1] &= 0xff << st->buf[0]; + } + + ASN_DEBUG("Took %d bytes to encode %s: [%s]:%d", + consumed_myself, td->name, st->buf, st->size); + + rval.code = RC_OK; + rval.consumed = consumed_myself; + + return rval; +} + +/* + * Encode OCTET STRING type using DER. + */ +der_enc_rval_t +OCTET_STRING_encode_der(asn1_TYPE_descriptor_t *sd, void *ptr, + int tag_mode, ber_tlv_tag_t tag, + asn_app_consume_bytes_f *cb, void *app_key) { + der_enc_rval_t erval; + OCTET_STRING_t *st = ptr; + int add_byte = 0; + + ASN_DEBUG("%s %s as OCTET STRING", + cb?"Estimating":"Encoding", sd->name); + + /* + * Canonicalize BIT STRING. + */ + if(sd->specifics && st->buf) { + switch(st->size) { + case 0: add_byte = 1; break; + case 1: st->buf[0] = 0; break; + default: + /* Finalize BIT STRING: zero out unused bits. */ + st->buf[st->size-1] &= 0xff << st->buf[0]; + } + } + + erval.encoded = der_write_tags(sd, st->size + add_byte, tag_mode, tag, + cb, app_key); + if(erval.encoded == -1) { + erval.failed_type = sd; + erval.structure_ptr = ptr; + return erval; + } + + if(cb) { + uint8_t zero; + uint8_t *buf; + int size; + ssize_t ret; + + /* BIT STRING-aware handling */ + if(add_byte) { + zero = 0; + buf = &zero; + size = 1; + } else if(st->buf) { + buf = st->buf; + size = st->size; + } else { + assert(st->size == 0); + buf = 0; /* Not used */ + size = 0; + } + + if(size) { + ret = cb(buf, size, app_key); + if(ret == -1) { + erval.encoded = -1; + erval.failed_type = sd; + erval.structure_ptr = ptr; + return erval; + } + } + } + + erval.encoded += st->size + add_byte; + + return erval; +} + +int +OCTET_STRING_print(asn1_TYPE_descriptor_t *td, const void *sptr, int ilevel, + asn_app_consume_bytes_f *cb, void *app_key) { + static char h2c[16] = "0123456789ABCDEF"; + const OCTET_STRING_t *st = sptr; + char scratch[16 * 3 + 4]; + char *p = scratch; + uint8_t *buf; + uint8_t *end; + size_t i; + int ret; + + if(!st || !st->buf) return cb("<absent>", 8, app_key); + + /* + * Dump the contents of the buffer in hexadecimal. + */ + buf = st->buf; + end = buf + st->size; + for(i = 0; buf < end; buf++, i++) { + if(!(i % 16) && (i || st->size > 16)) { + if(cb(scratch, p - scratch, app_key) + || cb("\n", 1, app_key)) + return -1; + for(ret = 0; ret < ilevel; ret++) + cb(" ", 1, app_key); + p = scratch; + } + *p++ = h2c[(*buf >> 4) & 0x0F]; + *p++ = h2c[*buf & 0x0F]; + *p++ = ' '; + } + + return cb(scratch, p - scratch, app_key); +} + +int +OCTET_STRING_print_ascii(asn1_TYPE_descriptor_t *td, const void *sptr, + int ilevel, asn_app_consume_bytes_f *cb, void *app_key) { + const OCTET_STRING_t *st = sptr; + + if(st && st->buf) { + return cb(st->buf, st->size, app_key); + } else { + return cb("<absent>", 8, app_key); + } +} + +void +OCTET_STRING_free(asn1_TYPE_descriptor_t *td, void *sptr, int contents_only) { + OCTET_STRING_t *st = sptr; + struct _stack *stck = st->_ber_dec_ctx.ptr; + + if(!td || !st) + return; + + ASN_DEBUG("Freeing %s as OCTET STRING", td->name); + + if(st->buf) { + FREEMEM(st->buf); + } + + /* + * Remove decode-time stack. + */ + if(stck) { + while(stck->tail) { + struct _stack_el *sel = stck->tail; + stck->tail = sel->prev; + FREEMEM(sel); + } + FREEMEM(stck); + } + + if(!contents_only) { + FREEMEM(st); + } +} + +/* + * Conversion routines. + */ +int +OCTET_STRING_fromBuf(OCTET_STRING_t *st, const char *str, int len) { + void *buf; + + if(st == 0 || (str == 0 && len)) { + errno = EINVAL; + return -1; + } + + /* + * Clear the OCTET STRING. + */ + if(str == NULL) { + if(st->buf) + FREEMEM(st->buf); + st->size = 0; + return 0; + } + + /* Determine the original string size, if not explicitly given */ + if(len < 0) + len = strlen(str); + + /* Allocate and fill the memory */ + buf = MALLOC(len + 1); + if(buf == NULL) { + return -1; + } else { + st->buf = buf; + st->size = len; + } + + memcpy(buf, str, len); + st->buf[st->size] = '\0'; /* Couldn't use memcpy(len+1)! */ + + return 0; +} + +OCTET_STRING_t * +OCTET_STRING_new_fromBuf(const char *str, int len) { + OCTET_STRING_t *st; + + st = CALLOC(1, sizeof(*st)); + if(st && str && OCTET_STRING_fromBuf(st, str, len)) { + free(st); + st = NULL; + } + + return st; +} + diff --git a/skeletons/OCTET_STRING.h b/skeletons/OCTET_STRING.h new file mode 100644 index 00000000..1aed9a35 --- /dev/null +++ b/skeletons/OCTET_STRING.h @@ -0,0 +1,51 @@ +/*- + * Copyright (c) 2003 Lev Walkin <vlm@lionet.info>. All rights reserved. + * Redistribution and modifications are permitted subject to BSD license. + */ +#ifndef _OCTET_STRING_H_ +#define _OCTET_STRING_H_ + +#include <constr_TYPE.h> + +typedef struct OCTET_STRING { + uint8_t *buf; /* Buffer with consecutive OCTET_STRING bits */ + int size; /* Size of the buffer */ + + ber_dec_ctx_t _ber_dec_ctx; /* Parsing across buffer boundaries */ +} OCTET_STRING_t; + +extern asn1_TYPE_descriptor_t asn1_DEF_OCTET_STRING; + +ber_type_decoder_f OCTET_STRING_decode_ber; +der_type_encoder_f OCTET_STRING_encode_der; +asn_struct_print_f OCTET_STRING_print; +asn_struct_print_f OCTET_STRING_print_ascii; +asn_struct_free_f OCTET_STRING_free; + +/*********************************** + * Some handy conversion routines. * + ***********************************/ + +/* + * This function clears the previous value of the OCTET STRING (if any) + * and then allocates a new memory and makes s point to the newly allocated + * memory. If size = -1, the size of the original string will be determined + * using strlen(str). + * If str equals to NULL, the function will silently clear the + * current contents of the OCTET STRING. + * Returns 0 if it was possible to perform operation, -1 otherwise. + */ +int OCTET_STRING_fromBuf(OCTET_STRING_t *s, const char *str, int size); + +/* + * Allocate and fill the new OCTET STRING and return a pointer to the newly + * allocated object. NULL is permitted in str: the function will just allocate + * empty OCTET STRING. + */ +OCTET_STRING_t *OCTET_STRING_new_fromBuf(const char *str, int size); + +/* Handy conversion from the C string into the OCTET STRING. */ +#define OCTET_STRING_fromString(s, str) OCTET_STRING_fromBuf(s, str, -1); +#define OCTET_STRING_fromString(s, str) OCTET_STRING_fromBuf(s, str, -1); + +#endif /* _OCTET_STRING_H_ */ diff --git a/skeletons/ObjectDescriptor.c b/skeletons/ObjectDescriptor.c new file mode 100644 index 00000000..5a39380c --- /dev/null +++ b/skeletons/ObjectDescriptor.c @@ -0,0 +1,27 @@ +/*- + * Copyright (c) 2003, 2004 Lev Walkin <vlm@lionet.info>. All rights reserved. + * Redistribution and modifications are permitted subject to BSD license. + */ +#include <ObjectDescriptor.h> + +/* + * ObjectDescriptor basic type description. + */ +static ber_tlv_tag_t asn1_DEF_ObjectDescriptor_tags[] = { + (ASN_TAG_CLASS_UNIVERSAL | (7 << 2)) +}; +asn1_TYPE_descriptor_t asn1_DEF_ObjectDescriptor = { + "ObjectDescriptor", + asn_generic_unknown_constraint, + OCTET_STRING_decode_ber, /* Implemented in terms of OCTET STRING */ + OCTET_STRING_encode_der, /* Implemented in terms of OCTET STRING */ + OCTET_STRING_print_ascii, /* Treat as ASCII subset (it's not) */ + OCTET_STRING_free, + 0, /* Use generic outmost tag fetcher */ + asn1_DEF_ObjectDescriptor_tags, + sizeof(asn1_DEF_ObjectDescriptor_tags) + / sizeof(asn1_DEF_ObjectDescriptor_tags[0]), + 1, /* Single UNIVERSAL tag may be implicitly overriden */ + -1, /* Both ways are fine */ +}; + diff --git a/skeletons/ObjectDescriptor.h b/skeletons/ObjectDescriptor.h new file mode 100644 index 00000000..67addf24 --- /dev/null +++ b/skeletons/ObjectDescriptor.h @@ -0,0 +1,15 @@ +/*- + * Copyright (c) 2003, 2004 Lev Walkin <vlm@lionet.info>. All rights reserved. + * Redistribution and modifications are permitted subject to BSD license. + */ +#ifndef _ObjectDescriptor_H_ +#define _ObjectDescriptor_H_ + +#include <constr_TYPE.h> +#include <GraphicString.h> + +typedef GraphicString_t ObjectDescriptor_t; /* Implemented in terms of. */ + +extern asn1_TYPE_descriptor_t asn1_DEF_ObjectDescriptor; + +#endif /* _ObjectDescriptor_H_ */ diff --git a/skeletons/PrintableString.c b/skeletons/PrintableString.c new file mode 100644 index 00000000..32ee7da5 --- /dev/null +++ b/skeletons/PrintableString.c @@ -0,0 +1,81 @@ +/*- + * Copyright (c) 2003, 2004 Lev Walkin <vlm@lionet.info>. All rights reserved. + * Redistribution and modifications are permitted subject to BSD license. + */ +#include <PrintableString.h> + +/* + * PrintableString basic type description. + */ +static ber_tlv_tag_t asn1_DEF_PrintableString_tags[] = { + (ASN_TAG_CLASS_UNIVERSAL | (19 << 2)) +}; +asn1_TYPE_descriptor_t asn1_DEF_PrintableString = { + "PrintableString", + PrintableString_constraint, + OCTET_STRING_decode_ber, /* Implemented in terms of OCTET STRING */ + OCTET_STRING_encode_der, /* Implemented in terms of OCTET STRING */ + OCTET_STRING_print_ascii, /* ASCII subset */ + OCTET_STRING_free, + 0, /* Use generic outmost tag fetcher */ + asn1_DEF_PrintableString_tags, + sizeof(asn1_DEF_PrintableString_tags) + / sizeof(asn1_DEF_PrintableString_tags[0]), + 1, /* Single UNIVERSAL tag may be implicitly overriden */ + -1, /* Both ways are fine */ +}; + + +/* + * ASN.1:1984 (X.409) + */ +static int _PrintableString_alphabet[256] = { +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, /* ' */ +0x41, 0x42, 0x00, 0x43, 0x44, 0x45, 0x46, 0x47, /* ( ) + , - . / */ +0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, /* 0 1 2 3 4 5 6 7 */ +0x3d, 0x3e, 0x48, 0x00, 0x00, 0x49, 0x00, 0x4a, /* 8 9 : = ? */ +0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* A B C D E F G */ +0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, /* H I J K L M N O */ +0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, /* P Q R S T U V W */ +0x18, 0x19, 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00, /* X Y Z */ +0x00, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, /* a b c d e f g */ +0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, /* h i j k l m n o */ +0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, /* p q r s t u v w */ +0x32, 0x33, 0x34, 0x00, 0x00, 0x00, 0x00, 0x00, /* x y z */ +}; + +int +PrintableString_constraint(asn1_TYPE_descriptor_t *td, const void *sptr, + asn_app_consume_bytes_f *app_errlog, void *app_key) { + const PrintableString_t *st = sptr; + + if(st && st->buf) { + uint8_t *buf = st->buf; + uint8_t *end = buf + st->size; + + /* + * Check the alphabet of the PrintableString. + * ASN.1:1984 (X.409) + */ + for(; buf < end; buf++) { + if(!_PrintableString_alphabet[*buf]) { + _ASN_ERRLOG("%s: value byte %d " + "not in PrintableString alphabet (%d)", + td->name, + (buf - st->buf) + 1, + *buf + ); + return -1; + } + } + } else { + _ASN_ERRLOG("%s: value not given", td->name); + return -1; + } + + return 0; +} diff --git a/skeletons/PrintableString.h b/skeletons/PrintableString.h new file mode 100644 index 00000000..b44a61ec --- /dev/null +++ b/skeletons/PrintableString.h @@ -0,0 +1,17 @@ +/*- + * Copyright (c) 2003 Lev Walkin <vlm@lionet.info>. All rights reserved. + * Redistribution and modifications are permitted subject to BSD license. + */ +#ifndef _PrintableString_H_ +#define _PrintableString_H_ + +#include <constr_TYPE.h> +#include <OCTET_STRING.h> + +typedef OCTET_STRING_t PrintableString_t; /* Implemented in terms of OCTET STRING */ + +extern asn1_TYPE_descriptor_t asn1_DEF_PrintableString; + +asn_constr_check_f PrintableString_constraint; + +#endif /* _PrintableString_H_ */ diff --git a/skeletons/README b/skeletons/README new file mode 100644 index 00000000..29dbae63 --- /dev/null +++ b/skeletons/README @@ -0,0 +1,6 @@ + +Here are the canonical encoder/decoder algorithms that work with the tables +constructed by the compiler. The compiler itself does not generate code, +it just creates those tables and then copies (links) over these files +which contain generic algorithms. + diff --git a/skeletons/RELATIVE-OID.c b/skeletons/RELATIVE-OID.c new file mode 100644 index 00000000..f6572105 --- /dev/null +++ b/skeletons/RELATIVE-OID.c @@ -0,0 +1,155 @@ +/*- + * Copyright (c) 2003, 2004 Lev Walkin <vlm@lionet.info>. All rights reserved. + * Redistribution and modifications are permitted subject to BSD license. + */ +#include <RELATIVE-OID.h> +#include <assert.h> +#include <errno.h> + +/* + * RELATIVE-OID basic type description. + */ +static ber_tlv_tag_t asn1_DEF_RELATIVE_OID_tags[] = { + (ASN_TAG_CLASS_UNIVERSAL | (13 << 2)) +}; +asn1_TYPE_descriptor_t asn1_DEF_RELATIVE_OID = { + "RELATIVE-OID", + asn_generic_no_constraint, + INTEGER_decode_ber, /* Implemented in terms of INTEGER type */ + OBJECT_IDENTIFIER_encode_der, + RELATIVE_OID_print, + INTEGER_free, + 0, /* Use generic outmost tag fetcher */ + asn1_DEF_RELATIVE_OID_tags, + sizeof(asn1_DEF_RELATIVE_OID_tags) + / sizeof(asn1_DEF_RELATIVE_OID_tags[0]), + 1, /* Single UNIVERSAL tag may be implicitly overriden */ + 0 /* Always in primitive form */ +}; + +int +RELATIVE_OID_print(asn1_TYPE_descriptor_t *td, const void *sptr, int ilevel, + asn_app_consume_bytes_f *cb, void *app_key) { + const RELATIVE_OID_t *st = sptr; + int startn; + int i; + + if(!st || !st->buf) + return cb("<absent>", 8, app_key); + + /* Dump preamble */ + if(cb("{ ", 2, app_key)) + return -1; + + for(i = 0, startn = 0; i < st->size; i++) { + uint8_t b = st->buf[i]; + if((b & 0x80)) /* Continuation expected */ + continue; + if(startn && cb(" ", 1, app_key)) /* Separate arcs */ + return -1; + if(OBJECT_IDENTIFIER_print_arc(&st->buf[startn], + i - startn + 1, 0, cb, app_key)) + return -1; + startn = i + 1; + } + + return cb(" }", 2, app_key); +} + + +int +RELATIVE_OID_get_arcs_l(RELATIVE_OID_t *roid, + unsigned long *arcs, int arcs_slots) { + unsigned long arc_value; + int cur_arc = 0; + int startn = 0; + int i; + + if(!roid || !roid->buf) { + errno = EINVAL; + return -1; + } + + for(i = 0; i < roid->size; i++) { + uint8_t b = roid->buf[i]; + if((b & 0x80)) /* Continuation expected */ + continue; + + if(cur_arc < arcs_slots) { + if(OBJECT_IDENTIFIER_get_arc_l(&roid->buf[startn], + i - startn + 1, 0, &arc_value)) + return -1; + arcs[cur_arc++] = arc_value; + } + + startn = i + 1; + } + + return cur_arc; +} + +int +RELATIVE_OID_set_arcs_l(RELATIVE_OID_t *roid, unsigned long *arcs, int arcs_slots) { + uint8_t *buf; + uint8_t *bp; + int size; + int i; + + if(roid == NULL || arcs == NULL || arcs_slots < 0) { + errno = EINVAL; + return -1; + } + + /* + * Roughly estimate the maximum size necessary to encode these arcs. + */ + size = ((sizeof(arcs[0]) + 1) * 8 / 7) * arcs_slots; + bp = buf = MALLOC(size + 1); + if(!buf) { + /* ENOMEM */ + return -1; + } + + /* + * Encode the arcs and refine the encoding size. + */ + size = 0; + for(i = 0; i < arcs_slots; i++) { + unsigned long value = arcs[i]; + uint8_t tbuf[sizeof(value) * 2]; /* Conservatively sized */ + uint8_t *tp = tbuf; + int arc_len = 0; + int add; + + for(add = 1; value; value >>= 7, add++) { + unsigned int b7 = value & 0x7F; + *tp++ = 0x80 | b7; + if(b7) { + arc_len += add; + add = 0; + } + } + + if(arc_len) { + tp = &tbuf[arc_len - 1]; + /* The last octet does not have bit 8 set. */ + *tbuf &= 0x7f; + for(; tp >= tbuf; tp--) + *bp++ = *tp; + size += arc_len; + } else { + *bp++ = 0; + size++; + } + } + + /* + * Replace buffer. + */ + roid->size = size; + bp = roid->buf; + roid->buf = buf; + if(bp) FREEMEM(bp); + + return 0; +} diff --git a/skeletons/RELATIVE-OID.h b/skeletons/RELATIVE-OID.h new file mode 100644 index 00000000..c0bc680e --- /dev/null +++ b/skeletons/RELATIVE-OID.h @@ -0,0 +1,30 @@ +/*- + * Copyright (c) 2003, 2004 Lev Walkin <vlm@lionet.info>. All rights reserved. + * Redistribution and modifications are permitted subject to BSD license. + */ +#ifndef _RELATIVE_OID_H_ +#define _RELATIVE_OID_H_ + +#include <constr_TYPE.h> +#include <OBJECT_IDENTIFIER.h> + +/* Implemented in terms of OBJECT IDENTIFIER */ +typedef OBJECT_IDENTIFIER_t RELATIVE_OID_t; + +extern asn1_TYPE_descriptor_t asn1_DEF_RELATIVE_OID; + +asn_struct_print_f RELATIVE_OID_print; + +/********************************** + * Some handy conversion routines * + **********************************/ + +/* See OBJECT_IDENTIFIER_set_arcs_l() function in OBJECT_IDENTIFIER.h */ +int RELATIVE_OID_set_arcs_l(RELATIVE_OID_t *_roid, + unsigned long *arcs, int arcs_slots); + +/* See OBJECT_IDENTIFIER_get_arcs_l() function in OBJECT_IDENTIFIER.h */ +int RELATIVE_OID_get_arcs_l(RELATIVE_OID_t *_roid, + unsigned long *arcs, int arcs_slots); + +#endif /* _RELATIVE_OID_H_ */ diff --git a/skeletons/T61String.c b/skeletons/T61String.c new file mode 100644 index 00000000..e6dceb78 --- /dev/null +++ b/skeletons/T61String.c @@ -0,0 +1,27 @@ +/*- + * Copyright (c) 2003 Lev Walkin <vlm@lionet.info>. All rights reserved. + * Redistribution and modifications are permitted subject to BSD license. + */ +#include <T61String.h> + +/* + * T61String basic type description. + */ +static ber_tlv_tag_t asn1_DEF_T61String_tags[] = { + (ASN_TAG_CLASS_UNIVERSAL | (20 << 2)) +}; +asn1_TYPE_descriptor_t asn1_DEF_T61String = { + "T61String", + asn_generic_unknown_constraint, + OCTET_STRING_decode_ber, /* Implemented in terms of OCTET STRING */ + OCTET_STRING_encode_der, /* Implemented in terms of OCTET STRING */ + OCTET_STRING_print, /* non-ascii string */ + OCTET_STRING_free, + 0, /* Use generic outmost tag fetcher */ + asn1_DEF_T61String_tags, + sizeof(asn1_DEF_T61String_tags) + / sizeof(asn1_DEF_T61String_tags[0]), + 1, /* Single UNIVERSAL tag may be implicitly overriden */ + -1, /* Both ways are fine */ +}; + diff --git a/skeletons/T61String.h b/skeletons/T61String.h new file mode 100644 index 00000000..1485577e --- /dev/null +++ b/skeletons/T61String.h @@ -0,0 +1,15 @@ +/*- + * Copyright (c) 2003 Lev Walkin <vlm@lionet.info>. All rights reserved. + * Redistribution and modifications are permitted subject to BSD license. + */ +#ifndef _T61String_H_ +#define _T61String_H_ + +#include <constr_TYPE.h> +#include <OCTET_STRING.h> + +typedef OCTET_STRING_t T61String_t; /* Implemented in terms of OCTET STRING */ + +extern asn1_TYPE_descriptor_t asn1_DEF_T61String; + +#endif /* _T61String_H_ */ diff --git a/skeletons/TeletexString.c b/skeletons/TeletexString.c new file mode 100644 index 00000000..1076c8af --- /dev/null +++ b/skeletons/TeletexString.c @@ -0,0 +1,27 @@ +/*- + * Copyright (c) 2003 Lev Walkin <vlm@lionet.info>. All rights reserved. + * Redistribution and modifications are permitted subject to BSD license. + */ +#include <TeletexString.h> + +/* + * TeletexString basic type description. + */ +static ber_tlv_tag_t asn1_DEF_TeletexString_tags[] = { + (ASN_TAG_CLASS_UNIVERSAL | (20 << 2)) +}; +asn1_TYPE_descriptor_t asn1_DEF_TeletexString = { + "TeletexString", + asn_generic_unknown_constraint, + OCTET_STRING_decode_ber, /* Implemented in terms of OCTET STRING */ + OCTET_STRING_encode_der, /* Implemented in terms of OCTET STRING */ + OCTET_STRING_print, /* non-ascii string */ + OCTET_STRING_free, + 0, /* Use generic outmost tag fetcher */ + asn1_DEF_TeletexString_tags, + sizeof(asn1_DEF_TeletexString_tags) + / sizeof(asn1_DEF_TeletexString_tags[0]), + 1, /* Single UNIVERSAL tag may be implicitly overriden */ + -1, /* Both ways are fine */ +}; + diff --git a/skeletons/TeletexString.h b/skeletons/TeletexString.h new file mode 100644 index 00000000..6f513581 --- /dev/null +++ b/skeletons/TeletexString.h @@ -0,0 +1,15 @@ +/*- + * Copyright (c) 2003 Lev Walkin <vlm@lionet.info>. All rights reserved. + * Redistribution and modifications are permitted subject to BSD license. + */ +#ifndef _TeletexString_H_ +#define _TeletexString_H_ + +#include <constr_TYPE.h> +#include <OCTET_STRING.h> + +typedef OCTET_STRING_t TeletexString_t; /* Implemented in terms of OCTET STRING */ + +extern asn1_TYPE_descriptor_t asn1_DEF_TeletexString; + +#endif /* _TeletexString_H_ */ diff --git a/skeletons/UTCTime.c b/skeletons/UTCTime.c new file mode 100644 index 00000000..e2f758ec --- /dev/null +++ b/skeletons/UTCTime.c @@ -0,0 +1,105 @@ +/*- + * Copyright (c) 2003, 2004 Lev Walkin <vlm@lionet.info>. All rights reserved. + * Redistribution and modifications are permitted subject to BSD license. + */ +#include <UTCTime.h> +#include <GeneralizedTime.h> +#include <time.h> +#include <errno.h> +#include <assert.h> + +#ifndef __NO_ASN_TABLE__ + +/* + * UTCTime basic type description. + */ +static ber_tlv_tag_t asn1_DEF_UTCTime_tags[] = { + (ASN_TAG_CLASS_UNIVERSAL | (23 << 2)) +}; +asn1_TYPE_descriptor_t asn1_DEF_UTCTime = { + "UTCTime", + UTCTime_constraint, + OCTET_STRING_decode_ber, /* Implemented in terms of OCTET STRING */ + OCTET_STRING_encode_der, /* Implemented in terms of OCTET STRING */ + UTCTime_print, + OCTET_STRING_free, + 0, /* Use generic outmost tag fetcher */ + asn1_DEF_UTCTime_tags, + sizeof(asn1_DEF_UTCTime_tags) + / sizeof(asn1_DEF_UTCTime_tags[0]), + 1, /* Single UNIVERSAL tag may be implicitly overriden */ + -1, /* Both ways are fine */ +}; + +#endif /* __NO_ASN_TABLE__ */ + +/* + * Check that the time looks like the time. + */ +int +UTCTime_constraint(asn1_TYPE_descriptor_t *td, const void *sptr, + asn_app_consume_bytes_f *app_errlog, void *app_key) { + const UTCTime_t *st = sptr; + time_t tloc; + + errno = EPERM; /* Just an unlikely error code */ + tloc = asn_UT2time(st, 0); + if(tloc == -1 && errno != EPERM) { + _ASN_ERRLOG("%s: Invalid time format: %s", + td->name, strerror(errno)); + return -1; + } + + return 0; +} + +int +UTCTime_print(asn1_TYPE_descriptor_t *td, const void *sptr, int ilevel, + asn_app_consume_bytes_f *cb, void *app_key) { + const UTCTime_t *st = sptr; + + if(st && st->buf) { + char buf[32]; + struct tm tm; + int ret; + + errno = EPERM; + if(asn_UT2time(st, &tm) == -1 && errno != EPERM) + return cb("<bad-value>", 11, app_key); + + ret = snprintf(buf, sizeof(buf), + "%04d-%02d-%02d %02d:%02d%02d", + tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, + tm.tm_hour, tm.tm_min, tm.tm_sec); + assert(ret > 0 && ret < sizeof(buf)); + return cb(buf, ret, app_key); + } else { + return cb("<absent>", 8, app_key); + } +} + +time_t +asn_UT2time(const UTCTime_t *st, struct tm *_tm) { + char buf[17+2]; /* "AAMMJJhhmmss+hhmm" = 17, + 2 = 19 */ + GeneralizedTime_t gt; + + if(!st || !st->buf || st->size < 11 || st->size > (sizeof(buf) - 2)) { + errno = EINVAL; + return -1; + } + + gt.buf = buf; + gt.size = st->size + 2; + memcpy(gt.buf + 2, st->buf, st->size); + if(st->buf[0] > 0x35) { + /* 19xx */ + gt.buf[0] = 0x31; + gt.buf[1] = 0x39; + } else { + /* 20xx */ + gt.buf[0] = 0x32; + gt.buf[1] = 0x30; + } + + return asn_GT2time(>, _tm); +} diff --git a/skeletons/UTCTime.h b/skeletons/UTCTime.h new file mode 100644 index 00000000..1e52388e --- /dev/null +++ b/skeletons/UTCTime.h @@ -0,0 +1,26 @@ +/*- + * Copyright (c) 2003, 2004 Lev Walkin <vlm@lionet.info>. All rights reserved. + * Redistribution and modifications are permitted subject to BSD license. + */ +#ifndef _UTCTime_H_ +#define _UTCTime_H_ + +#include <constr_TYPE.h> +#include <OCTET_STRING.h> + +typedef OCTET_STRING_t UTCTime_t; /* Implemented using OCTET STRING */ + +extern asn1_TYPE_descriptor_t asn1_DEF_UTCTime; + +asn_constr_check_f UTCTime_constraint; +asn_struct_print_f UTCTime_print; + +/*********************** + * Some handy helpers. * + ***********************/ + +/* On error returns -1 and errno set to EINVAL */ +struct tm; /* <time.h> */ +time_t asn_UT2time(const UTCTime_t *, struct tm *_optional_tm4fill); + +#endif /* _UTCTime_H_ */ diff --git a/skeletons/UTF8String.c b/skeletons/UTF8String.c new file mode 100644 index 00000000..fdd13d87 --- /dev/null +++ b/skeletons/UTF8String.c @@ -0,0 +1,112 @@ +/*- + * Copyright (c) 2003, 2004 Lev Walkin <vlm@lionet.info>. All rights reserved. + * Redistribution and modifications are permitted subject to BSD license. + */ +#include <UTF8String.h> + +/* + * UTF8String basic type description. + */ +static ber_tlv_tag_t asn1_DEF_UTF8String_tags[] = { + (ASN_TAG_CLASS_UNIVERSAL | (12 << 2)) +}; +asn1_TYPE_descriptor_t asn1_DEF_UTF8String = { + "UTF8String", + UTF8String_constraint, /* Check for invalid codes, etc. */ + OCTET_STRING_decode_ber, /* Implemented in terms of OCTET STRING */ + OCTET_STRING_encode_der, /* Implemented in terms of OCTET STRING */ + UTF8String_print, + OCTET_STRING_free, + 0, /* Use generic outmost tag fetcher */ + asn1_DEF_UTF8String_tags, + sizeof(asn1_DEF_UTF8String_tags) + / sizeof(asn1_DEF_UTF8String_tags[0]), + 1, /* Single UNIVERSAL tag may be implicitly overriden */ + -1, /* Both ways are fine */ +}; + +static int _UTF8String_h1[16] = { + 1, 1, 1, 1, 1, 1, 1, 1, /* 0x0 ... 0x7 */ + 0, 0, 0, 0, 2, 2, 3, -1 +}; +static int _UTF8String_h2[16] = { + 4, 4, 4, 4, 4, 4, 4, 4, /* 0xF0 .. 0xF7 */ + 5, 5, 5, 5, 6, 6, -1, -1 +}; + +int +UTF8String_constraint(asn1_TYPE_descriptor_t *td, const void *sptr, + asn_app_consume_bytes_f *app_errlog, void *app_key) { + ssize_t len; + len = UTF8String_length(sptr, td->name, app_errlog, app_key); + if(len > 0) len = 0; + return len; +} + +ssize_t +UTF8String_length(const UTF8String_t *st, const char *opt_type_name, + asn_app_consume_bytes_f *app_errlog, void *app_key) { + + if(st && st->buf) { + size_t length = 0; + uint8_t *buf = st->buf; + uint8_t *end = buf + st->size; + int want; /* Number of bytes wanted */ + + for(want = 0; buf < end; buf++) { + uint8_t ch = *buf; + int w = _UTF8String_h1[ch >> 4]; + if(want) { /* Continuation expected */ + if(w) { + _ASN_ERRLOG("%s: UTF-8 expectation " + "failed at byte %d", + opt_type_name, + (buf - st->buf) + 1); + return -1; + } + want--; + } else { + switch(w) { + case -1: /* Long UTF-8 */ + w = _UTF8String_h2[ch & 0xF0]; + if(w != -1) + break; + /* Fall through */ + case 0: + _ASN_ERRLOG( + "%s: UTF-8 expectation" + "failed at byte %d", + opt_type_name, + (buf - st->buf) + 1); + return -1; + } + want = w - 1; /* Expect this much */ + } + if(!want) length++; + } + + /* If still want something, then something is wrong */ + if(want) { + _ASN_ERRLOG("%s: truncated UTF-8 sequence", + opt_type_name); + return -1; + } + + return length; + } else { + _ASN_ERRLOG("%s: value not given", opt_type_name); + return -1; + } +} + +int +UTF8String_print(asn1_TYPE_descriptor_t *td, const void *sptr, int ilevel, + asn_app_consume_bytes_f *cb, void *app_key) { + const UTF8String_t *st = sptr; + + if(st && st->buf) { + return cb(st->buf, st->size, app_key); + } else { + return cb("<absent>", 8, app_key); + } +} diff --git a/skeletons/UTF8String.h b/skeletons/UTF8String.h new file mode 100644 index 00000000..f18bf703 --- /dev/null +++ b/skeletons/UTF8String.h @@ -0,0 +1,22 @@ +/*- + * Copyright (c) 2003, 2004 Lev Walkin <vlm@lionet.info>. All rights reserved. + * Redistribution and modifications are permitted subject to BSD license. + */ +#ifndef _UTF8String_H_ +#define _UTF8String_H_ + +#include <constr_TYPE.h> +#include <OCTET_STRING.h> + +typedef OCTET_STRING_t UTF8String_t; /* Implemented in terms of OCTET STRING */ + +extern asn1_TYPE_descriptor_t asn1_DEF_UTF8String; + +asn_constr_check_f UTF8String_constraint; +asn_struct_print_f UTF8String_print; + +/* Returns length of UTF-8 string in characters or -1 if error. */ +ssize_t UTF8String_length(const UTF8String_t *st, const char *opt_type_name, + asn_app_consume_bytes_f *app_errlog, void *app_key); + +#endif /* _UTF8String_H_ */ diff --git a/skeletons/UniversalString.c b/skeletons/UniversalString.c new file mode 100644 index 00000000..7d75e079 --- /dev/null +++ b/skeletons/UniversalString.c @@ -0,0 +1,83 @@ +/*- + * Copyright (c) 2003 Lev Walkin <vlm@lionet.info>. All rights reserved. + * Redistribution and modifications are permitted subject to BSD license. + */ +#include <UniversalString.h> + +/* + * UniversalString basic type description. + */ +static ber_tlv_tag_t asn1_DEF_UniversalString_tags[] = { + (ASN_TAG_CLASS_UNIVERSAL | (28 << 2)) +}; +asn1_TYPE_descriptor_t asn1_DEF_UniversalString = { + "UniversalString", + asn_generic_no_constraint, + OCTET_STRING_decode_ber, /* Implemented in terms of OCTET STRING */ + OCTET_STRING_encode_der, /* Implemented in terms of OCTET STRING */ + UniversalString_print, /* Convert into UTF8 and print */ + OCTET_STRING_free, + 0, /* Use generic outmost tag fetcher */ + asn1_DEF_UniversalString_tags, + sizeof(asn1_DEF_UniversalString_tags) + / sizeof(asn1_DEF_UniversalString_tags[0]), + 1, /* Single UNIVERSAL tag may be implicitly overriden */ + -1, /* Both ways are fine */ +}; + + +int +UniversalString_print(asn1_TYPE_descriptor_t *td, const void *sptr, int ilevel, + asn_app_consume_bytes_f *cb, void *app_key) { + const UniversalString_t *st = sptr; + uint32_t *wchar; + uint32_t *wend; + char scratch[128]; /* Scratchpad buffer */ + char *p; + + if(!st || !st->buf) return cb("<absent>", 8, app_key); + + wchar = (uint32_t *)st->buf; + wend = (uint32_t *)(st->buf + st->size); + for(p = scratch; wchar < wend; wchar++) { + uint32_t wc = (((uint8_t *)wchar)[0] << 24) + | (((uint8_t *)wchar)[1] << 16) + | (((uint8_t *)wchar)[2] << 8) + | ((uint8_t *)wchar)[3]; /* 4 bytes */ + if(sizeof(scratch) - (p - scratch) < 6) { + if(cb(scratch, p - scratch, app_key)) + return -1; + p = scratch; + } + if(wc < 0x80) { + *p++ = (char)wc; + } else if(wc < 0x800) { + *p++ = 0xc0 | ((wc >> 6)); + *p++ = 0x80 | ((wc & 0x3f)); + } else if(wc < 0x10000) { + *p++ = 0xe0 | ((wc >> 12)); + *p++ = 0x80 | ((wc >> 6) & 0x3f); + *p++ = 0x80 | ((wc & 0x3f)); + } else if(wc < 0x200000) { + *p++ = 0xf0 | ((wc >> 18)); + *p++ = 0x80 | ((wc >> 12) & 0x3f); + *p++ = 0x80 | ((wc >> 6) & 0x3f); + *p++ = 0x80 | ((wc & 0x3f)); + } else if(wc < 0x4000000) { + *p++ = 0xf8 | ((wc >> 24)); + *p++ = 0x80 | ((wc >> 18) & 0x3f); + *p++ = 0x80 | ((wc >> 12) & 0x3f); + *p++ = 0x80 | ((wc >> 6) & 0x3f); + *p++ = 0x80 | ((wc & 0x3f)); + } else { + *p++ = 0xfc | ((wc >> 30) & 0x1); + *p++ = 0x80 | ((wc >> 24) & 0x3f); + *p++ = 0x80 | ((wc >> 18) & 0x3f); + *p++ = 0x80 | ((wc >> 12) & 0x3f); + *p++ = 0x80 | ((wc >> 6) & 0x3f); + *p++ = 0x80 | ((wc & 0x3f)); + } + } + + return cb(scratch, p - scratch, app_key); +} diff --git a/skeletons/UniversalString.h b/skeletons/UniversalString.h new file mode 100644 index 00000000..cafbc80c --- /dev/null +++ b/skeletons/UniversalString.h @@ -0,0 +1,17 @@ +/*- + * Copyright (c) 2003 Lev Walkin <vlm@lionet.info>. All rights reserved. + * Redistribution and modifications are permitted subject to BSD license. + */ +#ifndef _UniversalString_H_ +#define _UniversalString_H_ + +#include <constr_TYPE.h> +#include <OCTET_STRING.h> + +typedef OCTET_STRING_t UniversalString_t; /* Implemented in terms of OCTET STRING */ + +extern asn1_TYPE_descriptor_t asn1_DEF_UniversalString; + +asn_struct_print_f UniversalString_print; /* Human-readable output */ + +#endif /* _UniversalString_H_ */ diff --git a/skeletons/VideotexString.c b/skeletons/VideotexString.c new file mode 100644 index 00000000..ab92ebd9 --- /dev/null +++ b/skeletons/VideotexString.c @@ -0,0 +1,27 @@ +/*- + * Copyright (c) 2003 Lev Walkin <vlm@lionet.info>. All rights reserved. + * Redistribution and modifications are permitted subject to BSD license. + */ +#include <VideotexString.h> + +/* + * VideotexString basic type description. + */ +static ber_tlv_tag_t asn1_DEF_VideotexString_tags[] = { + (ASN_TAG_CLASS_UNIVERSAL | (21 << 2)) +}; +asn1_TYPE_descriptor_t asn1_DEF_VideotexString = { + "VideotexString", + asn_generic_unknown_constraint, + OCTET_STRING_decode_ber, /* Implemented in terms of OCTET STRING */ + OCTET_STRING_encode_der, /* Implemented in terms of OCTET STRING */ + OCTET_STRING_print, /* non-ascii string */ + OCTET_STRING_free, + 0, /* Use generic outmost tag fetcher */ + asn1_DEF_VideotexString_tags, + sizeof(asn1_DEF_VideotexString_tags) + / sizeof(asn1_DEF_VideotexString_tags[0]), + 1, /* Single UNIVERSAL tag may be implicitly overriden */ + -1, /* Both ways are fine */ +}; + diff --git a/skeletons/VideotexString.h b/skeletons/VideotexString.h new file mode 100644 index 00000000..4dd7c8e7 --- /dev/null +++ b/skeletons/VideotexString.h @@ -0,0 +1,15 @@ +/*- + * Copyright (c) 2003 Lev Walkin <vlm@lionet.info>. All rights reserved. + * Redistribution and modifications are permitted subject to BSD license. + */ +#ifndef _VideotexString_H_ +#define _VideotexString_H_ + +#include <constr_TYPE.h> +#include <OCTET_STRING.h> + +typedef OCTET_STRING_t VideotexString_t; /* Implemented in terms of OCTET STRING */ + +extern asn1_TYPE_descriptor_t asn1_DEF_VideotexString; + +#endif /* _VideotexString_H_ */ diff --git a/skeletons/VisibleString.c b/skeletons/VisibleString.c new file mode 100644 index 00000000..af9525f8 --- /dev/null +++ b/skeletons/VisibleString.c @@ -0,0 +1,81 @@ +/*- + * Copyright (c) 2003 Lev Walkin <vlm@lionet.info>. All rights reserved. + * Redistribution and modifications are permitted subject to BSD license. + */ +#include <VisibleString.h> + +/* + * VisibleString basic type description. + */ +static ber_tlv_tag_t asn1_DEF_VisibleString_tags[] = { + (ASN_TAG_CLASS_UNIVERSAL | (26 << 2)) +}; +asn1_TYPE_descriptor_t asn1_DEF_VisibleString = { + "VisibleString", + VisibleString_constraint, + OCTET_STRING_decode_ber, /* Implemented in terms of OCTET STRING */ + OCTET_STRING_encode_der, /* Implemented in terms of OCTET STRING */ + OCTET_STRING_print_ascii, /* ASCII subset */ + OCTET_STRING_free, + 0, /* Use generic outmost tag fetcher */ + asn1_DEF_VisibleString_tags, + sizeof(asn1_DEF_VisibleString_tags) + / sizeof(asn1_DEF_VisibleString_tags[0]), + 1, /* Single UNIVERSAL tag may be implicitly overriden */ + -1, /* Both ways are fine */ +}; + + +/* + * ISO646, ISOReg#6 + */ +static int _VisibleString_alphabet[256] = { +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, /* ! " # $ % & ' */ +0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, /* ( ) * + , - . / */ +0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, /* 0 1 2 3 4 5 6 7 */ +0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, /* 8 9 : ; < = > ? */ +0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, /* @ A B C D E F G */ +0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, /* H I J K L M N O */ +0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, /* P Q R S T U V W */ +0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, /* X Y Z [ \ ] ^ _ */ +0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, /* ` a b c d e f g */ +0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, /* h i j k l m n o */ +0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, /* p q r s t u v w */ +0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x00, /* x y z { | } ~ */ +}; + +int +VisibleString_constraint(asn1_TYPE_descriptor_t *td, const void *sptr, + asn_app_consume_bytes_f *app_errlog, void *app_key) { + const VisibleString_t *st = sptr; + + if(st && st->buf) { + uint8_t *buf = st->buf; + uint8_t *end = buf + st->size; + + /* + * Check the alphabet of the VisibleString. + * ISO646, ISOReg#6 + */ + for(; buf < end; buf++) { + if(!_VisibleString_alphabet[*buf]) { + _ASN_ERRLOG("%s: value byte %d " + "not in VisibleString alphabet (%d)", + td->name, + (buf - st->buf) + 1, + *buf + ); + return -1; + } + } + } else { + _ASN_ERRLOG("%s: value not given", td->name); + return -1; + } + + return 0; +} diff --git a/skeletons/VisibleString.h b/skeletons/VisibleString.h new file mode 100644 index 00000000..3c1df420 --- /dev/null +++ b/skeletons/VisibleString.h @@ -0,0 +1,17 @@ +/*- + * Copyright (c) 2003 Lev Walkin <vlm@lionet.info>. All rights reserved. + * Redistribution and modifications are permitted subject to BSD license. + */ +#ifndef _VisibleString_H_ +#define _VisibleString_H_ + +#include <constr_TYPE.h> +#include <OCTET_STRING.h> + +typedef OCTET_STRING_t VisibleString_t; /* Implemented in terms of OCTET STRING */ + +extern asn1_TYPE_descriptor_t asn1_DEF_VisibleString; + +asn_constr_check_f VisibleString_constraint; + +#endif /* _VisibleString_H_ */ diff --git a/skeletons/asn_SEQUENCE_OF.c b/skeletons/asn_SEQUENCE_OF.c new file mode 100644 index 00000000..71f6f0cd --- /dev/null +++ b/skeletons/asn_SEQUENCE_OF.c @@ -0,0 +1,41 @@ +/*- + * Copyright (c) 2003, 2004 Lev Walkin <vlm@lionet.info>. All rights reserved. + * Redistribution and modifications are permitted subject to BSD license. + */ +#include <asn_types.h> /* for MALLOC/REALLOC/FREEMEM */ +#include <asn_SEQUENCE_OF.h> + +typedef A_SEQUENCE_OF(void) asn_sequence; + +void +asn_sequence_del(void *asn_sequence_of_x, int number, int _do_free) { + asn_sequence *as = asn_sequence_of_x; + + if(as) { + void *ptr; + int n; + + if(number < 0 || number >= as->count) + return; /* Nothing to delete */ + + if(_do_free && as->free) { + ptr = as->array[number]; + } else { + ptr = 0; + } + + /* + * Shift all elements to the left to hide the gap. + */ + --as->count; + for(n = number; n < as->count; n++) + as->array[n] = as->array[n+1]; + + /* + * Invoke the third-party function only when the state + * of the parent structure is consistent. + */ + if(ptr) as->free(ptr); + } +} + diff --git a/skeletons/asn_SEQUENCE_OF.h b/skeletons/asn_SEQUENCE_OF.h new file mode 100644 index 00000000..e9967340 --- /dev/null +++ b/skeletons/asn_SEQUENCE_OF.h @@ -0,0 +1,36 @@ +/*- + * Copyright (c) 2003, 2004 Lev Walkin <vlm@lionet.info>. All rights reserved. + * Redistribution and modifications are permitted subject to BSD license. + */ +#ifndef ASN_SEQUENCE_OF_H +#define ASN_SEQUENCE_OF_H + +#include <asn_SET_OF.h> + +/* + * SEQUENCE OF is the same as SET OF with a tiny difference: + * the delete operation preserves the initial order of elements + * and thus MAY operate in non-constant time. + */ +#define A_SEQUENCE_OF(type) A_SET_OF(type) + +#define ASN_SEQUENCE_ADD(headptr, ptr) \ + asn_sequence_add((headptr), (ptr)) + +/*********************************************** + * Implementation of the SEQUENCE OF structure. + */ + +#define asn_sequence_add asn_set_add +#define asn_sequence_empty asn_set_empty + +/* + * Delete the element from the set by its number (base 0). + * This is NOT a constant-time operation. + * The order of elements is preserved. + * If _do_free is given AND the (*free) is initialized, the element + * will be freed using the custom (*free) function as well. + */ +void asn_sequence_del(void *asn_sequence_of_x, int number, int _do_free); + +#endif /* ASN_SEQUENCE_OF_H */ diff --git a/skeletons/asn_SET_OF.c b/skeletons/asn_SET_OF.c new file mode 100644 index 00000000..dfa99067 --- /dev/null +++ b/skeletons/asn_SET_OF.c @@ -0,0 +1,89 @@ +/*- + * Copyright (c) 2003, 2004 Lev Walkin <vlm@lionet.info>. All rights reserved. + * Redistribution and modifications are permitted subject to BSD license. + */ +#include <asn_types.h> /* for MALLOC/REALLOC/FREEMEM */ +#include <asn_SET_OF.h> +#include <errno.h> + +typedef A_SET_OF(void) asn_set; + +/* + * Add another element into the set. + */ +int +asn_set_add(void *asn_set_of_x, void *ptr) { + asn_set *as = asn_set_of_x; + + if(as == 0 || ptr == 0) { + errno = EINVAL; /* Invalid arguments */ + return -1; + } + + /* + * Make sure there's enough space to insert an element. + */ + if(as->count == as->size) { + int _newsize = as->size ? (as->size << 1) : 4; + void *_new_arr; + _new_arr = REALLOC(as->array, _newsize * sizeof(as->array[0])); + if(_new_arr) { + as->array = _new_arr; + as->size = _newsize; + } else { + /* ENOMEM */ + return -1; + } + } + + as->array[as->count++] = ptr; + + return 0; +} + +void +asn_set_del(void *asn_set_of_x, int number, int _do_free) { + asn_set *as = asn_set_of_x; + + if(as) { + void *ptr; + if(number < 0 || number >= as->count) + return; + + if(_do_free && as->free) { + ptr = as->array[number]; + } else { + ptr = 0; + } + + as->array[number] = as->array[--as->count]; + + /* + * Invoke the third-party function only when the state + * of the parent structure is consistent. + */ + if(ptr) as->free(ptr); + } +} + +/* + * Free the contents of the set, do not free the set itself. + */ +void +asn_set_empty(void *asn_set_of_x) { + asn_set *as = asn_set_of_x; + + if(as) { + if(as->array) { + if(as->free) { + while(as->count--) + as->free(as->array[as->count]); + } + free(as->array); + } + as->count = 0; + as->size = 0; + } + +} + diff --git a/skeletons/asn_SET_OF.h b/skeletons/asn_SET_OF.h new file mode 100644 index 00000000..1443a7b8 --- /dev/null +++ b/skeletons/asn_SET_OF.h @@ -0,0 +1,46 @@ +/*- + * Copyright (c) 2003, 2004 Lev Walkin <vlm@lionet.info>. All rights reserved. + * Redistribution and modifications are permitted subject to BSD license. + */ +#ifndef ASN_SET_OF_H +#define ASN_SET_OF_H + +#define A_SET_OF(type) \ + struct { \ + type **array; \ + int count; /* Meaningful size */ \ + int size; /* Allocated size */ \ + void (*free)(type *); \ + } + +#define ASN_SET_ADD(headptr, ptr) \ + asn_set_add((headptr), (ptr)) + +/******************************************* + * Implementation of the SET OF structure. + */ + +/* + * Add another structure into the set by its pointer. + * RETURN VALUES: + * 0 for success and -1/errno for failure. + */ +int asn_set_add(void *asn_set_of_x, void *ptr); + +/* + * Delete the element from the set by its number (base 0). + * This is a constant-time operation. The order of elements before the + * deleted ones is guaranteed, the order of elements after the deleted + * one is NOT guaranteed. + * If _do_free is given AND the (*free) is initialized, the element + * will be freed using the custom (*free) function as well. + */ +void asn_set_del(void *asn_set_of_x, int number, int _do_free); + +/* + * Empty the contents of the set. Will free the elements, if (*free) is given. + * Will NOT free the set itself. + */ +void asn_set_empty(void *asn_set_of_x); + +#endif /* ASN_SET_OF_H */ diff --git a/skeletons/asn_types.h b/skeletons/asn_types.h new file mode 100644 index 00000000..6d426673 --- /dev/null +++ b/skeletons/asn_types.h @@ -0,0 +1,63 @@ +/*- + * Copyright (c) 2003, 2004 Lev Walkin <vlm@lionet.info>. All rights reserved. + * Redistribution and modifications are permitted subject to BSD license. + */ +/* + * Miscellaneous system-dependent types. + */ +#ifndef _ASN_TYPES_H_ +#define _ASN_TYPES_H_ + +#include <stdio.h> /* For fprintf() */ +#include <stdlib.h> /* For *alloc(3) */ +#include <string.h> /* For memcpy(3) */ +#include <sys/types.h> /* For size_t */ +#include <stdarg.h> /* For va_start */ +#include <inttypes.h> /* C99 Standard specifies this file, for uintXX_t */ +#include <stddef.h> /* for offsetof and ptrdiff_t */ + +#ifndef offsetof +#define offsetof(s, m) ((ptrdiff_t)&(((s *)0)->m) - (ptrdiff_t)((s *)0)) +#endif /* offsetof */ + +#define CALLOC(nmemb, size) calloc(nmemb, size) +#define MALLOC(size) malloc(size) +#define REALLOC(oldptr, size) realloc(oldptr, size) +#define FREEMEM(ptr) free(ptr) + +#ifndef MIN /* Suitable for comparing primitive types (integers) */ +#if defined(__GNUC__) +#define MIN(a,b) ({ __typeof a _a = a; __typeof b _b = b; \ + ((_a)<(_b)?(_a):(_b)); }) +#else /* !__GNUC__ */ +#define MIN(a,b) ((a)<(b)?(a):(b)) /* Unsafe variant */ +#endif /* __GNUC__ */ +#endif /* MIN */ + +/* + * A macro for debugging the ASN.1 internals. + * You may enable or override it. + */ +#ifndef ASN_DEBUG /* If debugging code is not defined elsewhere... */ +#if EMIT_ASN_DEBUG == 1 /* And it was asked to emit this code... */ +#define ASN_DEBUG(fmt, args...) do { \ + fprintf(stderr, fmt, ##args); \ + fprintf(stderr, "\n"); \ + } while(0) +#else /* EMIT_ASN_DEBUG */ +#define ASN_DEBUG(fmt, args...) ((void)0) /* Emit a no-op operator */ +#endif /* EMIT_ASN_DEBUG */ +#endif /* ASN_DEBUG */ + + +/* + * Generic type of an application-defined callback to return various + * types of data to the application. + * EXPECTED RETURN VALUES: + * -1: Failed to consume bytes. Abort the mission. + * Other return values indicate success, and ignored. + */ +typedef int (asn_app_consume_bytes_f)(const void *buffer, size_t size, + void *application_specific_key); + +#endif /* _ASN_TYPES_H_ */ diff --git a/skeletons/ber_decoder.c b/skeletons/ber_decoder.c new file mode 100644 index 00000000..135e7910 --- /dev/null +++ b/skeletons/ber_decoder.c @@ -0,0 +1,230 @@ +/*- + * Copyright (c) 2003 Lev Walkin <vlm@lionet.info>. All rights reserved. + * Redistribution and modifications are permitted subject to BSD license. + */ +#include <constr_TYPE.h> +#include <assert.h> + +#define ADVANCE(num_bytes) do { \ + size_t num = num_bytes; \ + ptr += num; \ + size -= num; \ + consumed_myself += num; \ + } while(0) +#define RETURN(_code) do { \ + ber_dec_rval_t rval; \ + rval.code = _code; \ + rval.consumed = consumed_myself; \ + return rval; \ + } while(0) + +/* + * The BER decoder of any type. + */ +ber_dec_rval_t +ber_decode(asn1_TYPE_descriptor_t *type_descriptor, + void **struct_ptr, void *ptr, size_t size) { + + /* + * Invoke type-specific decoder. + */ + return type_descriptor->ber_decoder(type_descriptor, + struct_ptr, /* Pointer to the destination structure */ + ptr, size, /* Buffer and its size */ + 0 /* Default tag mode is 0 */ + ); +} + +/* + * Check the set of <TL<TL<TL...>>> tags matches the definition. + */ +ber_dec_rval_t +ber_check_tags(asn1_TYPE_descriptor_t *head, ber_dec_ctx_t *ctx, + void *ptr, size_t size, int tag_mode, + ber_tlv_len_t *last_length, int *opt_tlv_form) { + ssize_t consumed_myself = 0; + ssize_t tag_len; + ssize_t len_len; + ber_tlv_tag_t tlv_tag; + ber_tlv_len_t tlv_len; + ber_tlv_len_t limit_len = -1; + int expect_00_terminators = 0; + int tlv_constr = -1; /* If CHOICE, opt_tlv_form is not given */ + int tagno; + + /* + * So what does all this tags_impl_skip stuff mean? + * Imagine two types, + * A ::= [5] IMPLICIT T + * B ::= [2] EXPLICIT T + * Where T is defined as + * T ::= [4] IMPLICIT SEQUENCE { ... } + * + * Let's say, we are starting to decode type A, given the + * following TLV stream: <5> <0>. What does this mean? + * It means that the type A contains type T which is, + * in turn, empty. + * Remember though, that we are still in A. We cannot + * just pass control to the type T decoder. Why? Because + * the type T decoder expects <4> <0>, not <5> <0>. + * So, we must make sure we are going to receive <5> while + * still in A, then pass control to the T decoder, indicating + * that the tag <4> was implicitly skipped. The decoder of T + * hence will be prepared to treat <4> as valid tag, and decode + * it appropriately. + */ + + /* + * We have a list of tags that must occur in the stream: + * {A,B,C} + * However, it may be indicated that the type is + * implicitly tagged in the caller, so it really boils down to the + * {I,B,C} or even {I,C} + * This is because the implicit tag at above structure may replace + * zero or more (or every) tags which follow it. We don't care + * about the precise number, as it is already computed for us + * by the ASN.1 compiler and placed into head->tags_impl_skip. + * So let's suppose the only tag left after implicit tagging is {I}. + * Yet, the table we have is {A,B,C} and head->tags_impl_skip=3. + * We need to check at least one tag in the loop, so the loop range + * is modified so it will be invoked at least one time. + */ + tagno = ctx->step /* Continuing where left previously */ + + (tag_mode==-1?(head->tags_impl_skip-1):0) + + (tag_mode==1?-1:0) + ; + //assert(head->tags_count >= 1); ?May not be the case for CHOICE! + assert(tagno < head->tags_count); /* At least one loop */ + for((void)tagno; tagno < head->tags_count; tagno++, ctx->step++) { + + /* + * Fetch and process T from TLV. + */ + tag_len = ber_fetch_tag(ptr, size, &tlv_tag); + ASN_DEBUG("Fetching tag from {%p,%ld} %02X: " + "len %ld, tag %s", + ptr, (long)size, + *(uint8_t *)ptr, (long)tag_len, + ber_tlv_tag_string(tlv_tag)); + switch(tag_len) { + case -1: RETURN(RC_FAIL); + case 0: RETURN(RC_WMORE); + } + + tlv_constr = BER_TLV_CONSTRUCTED(ptr); + + /* + * If {I}, don't check anything. + * If {I,B,C}, check B and C unless we're at I. + */ + if(tag_mode != 0 && ctx->step == 0) { + /* + * We don't expect tag to match here. + * It's just because we don't know how the tag + * is supposed to look like. + */ + } else { + assert(tagno >= 0); /* Guaranteed by the code above */ + if(tlv_tag != head->tags[tagno]) { + /* + * Unexpected tag. Too bad. + */ + ASN_DEBUG("Expected: %s, expectation failed", + ber_tlv_tag_string(head->tags[tagno])); + RETURN(RC_FAIL); + } + } + + /* + * Attention: if there are more tags expected, + * ensure that the current tag is presented + * in constructed form (it contains other tags!). + * If this one is the last one, check that the tag form + * matches the one given in descriptor. + */ + if(tagno < (head->tags_count - 1)) { + if(tlv_constr == 0) { + RETURN(RC_FAIL); + } + } else { + if(head->last_tag_form != tlv_constr + && head->last_tag_form != -1) { + RETURN(RC_FAIL); + } + } + + /* + * Fetch and process L from TLV. + */ + len_len = ber_fetch_length(tlv_constr, + ptr + tag_len, size - tag_len, &tlv_len); + switch(len_len) { + case -1: RETURN(RC_FAIL); + case 0: RETURN(RC_WMORE); + } + + /* + * FIXME + * As of today, the chain of tags + * must either contain several indefinite length TLVs, + * or several definite length ones. + * No mixing is allowed. + */ + if(tlv_len == -1) { + /* + * Indefinite length. + */ + if(limit_len == -1) { + expect_00_terminators++; + } else { + ASN_DEBUG("Unexpected indefinite length " + "in a chain of definite lengths"); + RETURN(RC_FAIL); + } + ADVANCE(tag_len + len_len); + continue; + } else { + if(expect_00_terminators) { + ASN_DEBUG("Unexpected definite length " + "in a chain of indefinite lengths"); + RETURN(RC_FAIL); + } + } + + /* + * Check that multiple TLVs specify ever decreasing length, + * which is consistent. + */ + if(limit_len == -1) { + limit_len = tlv_len + tag_len + len_len; + } else if(limit_len != tlv_len + tag_len + len_len) { + /* + * Inner TLV specifies length which is inconsistent + * with the outer TLV's length value. + */ + ASN_DEBUG("Outer TLV is %d and inner is %d", + limit_len, tlv_len); + RETURN(RC_FAIL); + } + + ADVANCE(tag_len + len_len); + + limit_len -= (tag_len + len_len); + if(size > limit_len) { + /* + * Make sure that we won't consume more bytes + * from the large buffer than the inferred limit. + */ + size = limit_len; + } + } + + if(opt_tlv_form) + *opt_tlv_form = tlv_constr; + if(expect_00_terminators) + *last_length = -expect_00_terminators; + else + *last_length = tlv_len; + + RETURN(RC_OK); +} diff --git a/skeletons/ber_decoder.h b/skeletons/ber_decoder.h new file mode 100644 index 00000000..8240270a --- /dev/null +++ b/skeletons/ber_decoder.h @@ -0,0 +1,77 @@ +/*- + * Copyright (c) 2003, 2004 Lev Walkin <vlm@lionet.info>. All rights reserved. + * Redistribution and modifications are permitted subject to BSD license. + */ +#ifndef _BER_DECODER_H_ +#define _BER_DECODER_H_ + +#include <constr_TYPE.h> + +struct asn1_TYPE_descriptor_s; /* Forward declaration */ + +/* + * This structure describes the return value common across the + * various BER decoders. + * + * Please note that the number of consumed bytes is ALWAYS meaningful, + * even if code!=RC_OK. This is so to indicate the number of successfully + * decoded bytes, hence provide a possibility, to fail with more diagnostics + * (i.e., print the offending remainder of the buffer). + */ +typedef struct ber_dec_rval_s { + enum { + RC_OK, /* Decoded successfully */ + RC_WMORE, /* More data expected, call again */ + RC_FAIL, /* Failure to decode data */ + } code; + + size_t consumed; /* Number of bytes consumed */ +} ber_dec_rval_t; + +/* + * A context for decoding BER across buffer boundaries. + */ +typedef struct ber_dec_ctx_s { + int phase; /* Decoding phase */ + int step; /* Elementary step of a phase */ + ber_tlv_len_t left; /* Number of bytes left, -1 for indefinite */ + void *ptr; /* Decoder-specific stuff */ +} ber_dec_ctx_t; + +/* + * The BER decoder of any type. + * This function may be invoked directly from the application. + */ +ber_dec_rval_t ber_decode(struct asn1_TYPE_descriptor_s *type_descriptor, + void **struct_ptr, /* Pointer to a target structure's pointer */ + void *buffer, /* Data to be decoded */ + size_t size /* Size of that buffer */ + ); + +/* + * Type of generic function which decodes the byte stream into the structure. + */ +typedef ber_dec_rval_t (ber_type_decoder_f)( + struct asn1_TYPE_descriptor_s *type_descriptor, + void **type_structure, void *buf_ptr, size_t size, + int tag_mode); + +/******************************* + * INTERNALLY USEFUL FUNCTIONS * + *******************************/ + +/* + * Check that all tags correspond to the type definition (as given in head). + * On return, last_length would contain either a non-negative length of the + * value part of the last TLV, or the negative number of expected + * "end of content" sequences. The number may only be negative if the + * head->last_tag_form is non-zero. + */ +ber_dec_rval_t ber_check_tags(struct asn1_TYPE_descriptor_s *type_dsc, + ber_dec_ctx_t *ctx, /* saved context */ + void *ptr, size_t size, + int tag_mode, /* {-1,0,1}: IMPLICIT, no, EXPLICIT */ + ber_tlv_len_t *last_length, + int *opt_tlv_form); + +#endif /* _BER_DECODER_H_ */ diff --git a/skeletons/ber_tlv_length.c b/skeletons/ber_tlv_length.c new file mode 100644 index 00000000..cd08f6ab --- /dev/null +++ b/skeletons/ber_tlv_length.c @@ -0,0 +1,159 @@ +/*- + * Copyright (c) 2003, 2004 Lev Walkin <vlm@lionet.info>. All rights reserved. + * Redistribution and modifications are permitted subject to BSD license. + */ +#include <constr_TYPE.h> +#include <ber_tlv_length.h> +#include <ber_tlv_tag.h> + +ssize_t +ber_fetch_length(int _is_constructed, void *bufptr, size_t size, + ber_tlv_len_t *len_r) { + uint8_t *buf = bufptr; + unsigned oct; + + if(size == 0) + return 0; /* Want more */ + + oct = *(uint8_t *)buf; + if((oct & 0x80) == 0) { + /* + * Short definite length. + */ + *len_r = (oct & 0x7F); + return 1; + } else { + ber_tlv_len_t len; + ssize_t skipped; + + if(_is_constructed && oct == 0x80) { + *len_r = -1; /* Indefinite length */ + return 1; + } + + if(oct == 0xff) { + /* Reserved in standard for future use. */ + return -1; + } + + oct &= 0x7F; /* Leave only the 7 LS bits */ + for(len = 0, buf++, skipped = 1; + oct && (++skipped < size); buf++, oct--) { + + len = (len << 8) | *buf; + if(len < 0 + || (len >> ((8 * sizeof(len)) - 8) && oct > 1)) { + /* + * Too large length value. + */ + return -1; + } + } + + if(oct == 0) { + *len_r = len; + return skipped; + } + + return 0; /* Want more */ + } + +} + +ssize_t +ber_skip_length(int _is_constructed, void *ptr, size_t size) { + ber_tlv_len_t vlen; /* Length of V in TLV */ + ssize_t tl; /* Length of L in TLV */ + ssize_t ll; /* Length of L in TLV */ + ssize_t skip; + + /* + * Determine the size of L in TLV. + */ + ll = ber_fetch_length(_is_constructed, ptr, size, &vlen); + if(ll <= 0) return ll; + + /* + * Definite length. + */ + if(vlen >= 0) { + skip = ll + vlen; + if(skip > size) + return 0; /* Want more */ + return skip; + } + + /* + * Indefinite length! + */ + ASN_DEBUG("Skipping indefinite length"); + for(skip = ll, ptr += ll, size -= ll;;) { + ber_tlv_tag_t tag; + + /* Fetch the tag */ + tl = ber_fetch_tag(ptr, size, &tag); + if(tl <= 0) return tl; + + ll = ber_skip_length(BER_TLV_CONSTRUCTED(ptr), + ptr + tl, size - tl); + if(ll <= 0) return ll; + + skip += tl + ll; + + /* + * This may be the end of the indefinite length structure, + * two consecutive 0 octets. + * Check if it is true. + */ + if(((uint8_t *)ptr)[0] == 0 + && ((uint8_t *)ptr)[1] == 0) + return skip; + + ptr += tl + ll; + size -= tl + ll; + } + + /* UNREACHABLE */ +} + +ssize_t +der_tlv_length_serialize(ber_tlv_len_t len, void *bufp, size_t size) { + ssize_t computed_size; /* Size of len encoding */ + uint8_t *buf = bufp; + uint8_t *end; + int i; + + if(len <= 127) { + /* Encoded in 1 octet */ + if(size) *buf = len; + return 1; + } + + /* + * Compute the size of the subsequent bytes. + */ + computed_size = sizeof(len); /* assert(sizeof(len)<128), n.p. */ + for(i = (8*(sizeof(len)-1)); i > 0; i -= 8) { + if((len >> i) & 0xFF) break; + computed_size--; + } + + if(size) { + *buf++ = 0x80 | computed_size; /* Length of the encoding */ + size--; + } + + /* + * Produce the len encoding, space permitting. + */ + if(size > computed_size) + end = buf + computed_size; + else + end = buf + size; + for((void)i /* Reuse bits count */; buf < end; i -= 8, buf++) { + *buf = (len >> i) & 0xFF; + } + + return computed_size + 1; +} + diff --git a/skeletons/ber_tlv_length.h b/skeletons/ber_tlv_length.h new file mode 100644 index 00000000..7e5d5f8a --- /dev/null +++ b/skeletons/ber_tlv_length.h @@ -0,0 +1,40 @@ +/*- + * Copyright (c) 2003 Lev Walkin <vlm@lionet.info>. All rights reserved. + * Redistribution and modifications are permitted subject to BSD license. + */ +#ifndef _BER_TLV_LENGTH_H_ +#define _BER_TLV_LENGTH_H_ + +typedef ssize_t ber_tlv_len_t; + +/* + * This function tries to fetch the length of the BER TLV value and place it + * in *len_r. + * RETURN VALUES: + * 0: More data expected than bufptr contains. + * -1: Fatal error deciphering length. + * >0: Number of bytes used from bufptr. + * On return with >0, len_r is constrained as -1..MAX, where -1 mean + * that the value is of indefinite length. + */ +ssize_t ber_fetch_length(int _is_constructed, void *bufptr, size_t size, + ber_tlv_len_t *len_r); + +/* + * This function expects bufptr to be positioned over L in TLV. + * It returns number of bytes occupied by L and V together, suitable + * for skipping. The function properly handles indefinite length. + * RETURN VALUES: + * Standard {-1,0,>0} convention. + */ +ssize_t ber_skip_length(int _is_constructed, void *bufptr, size_t size); + +/* + * This function serializes the length (L from TLV) in DER format. + * It always return number of bytes necessary to represent the length, + * it is a caller's responsibility to check the return value + * against the supplied buffer's size. + */ +ssize_t der_tlv_length_serialize(ber_tlv_len_t len, void *bufptr, size_t size); + +#endif /* _BER_TLV_LENGTH_H_ */ diff --git a/skeletons/ber_tlv_tag.c b/skeletons/ber_tlv_tag.c new file mode 100644 index 00000000..50c9e69b --- /dev/null +++ b/skeletons/ber_tlv_tag.c @@ -0,0 +1,145 @@ +/*- + * Copyright (c) 2003, 2004 Lev Walkin <vlm@lionet.info>. All rights reserved. + * Redistribution and modifications are permitted subject to BSD license. + */ +#include <constr_TYPE.h> +#include <ber_tlv_tag.h> +#include <errno.h> + +ssize_t +ber_fetch_tag(void *ptr, size_t size, ber_tlv_tag_t *tag_r) { + ber_tlv_tag_t val; + ber_tlv_tag_t tclass; + ssize_t skipped; + + if(size == 0) + return 0; + + val = *(uint8_t *)ptr; + tclass = (val >> 6); + if((val &= 31) != 31) { + /* + * Simple form: everything encoded in a single octet. + * Tag Class is encoded using two least significant bits. + */ + *tag_r = (val << 2) | tclass; + return 1; + } + + /* + * Each octet contains 7 bits of useful information. + * The MSB is 0 if it is the last octet of the tag. + */ + for(val = 0, ptr++, skipped = 2; skipped < size; ptr++, skipped++) { + unsigned oct = *(uint8_t *)ptr; + if(oct & 0x80) { + val = (val << 7) | (oct & 0x7F); + /* + * Make sure there are at least 9 bits spare + * at the MS side of a value. + */ + if(val >> ((8 * sizeof(val)) - 9)) { + /* + * We would not be able to accomodate + * any more tag bits. + */ + return -1; + } + } else { + *tag_r = (val << 9) | (oct << 2) | tclass; + return skipped; + } + } + + return 0; /* Want more */ +} + + +ssize_t +ber_tlv_tag_fwrite(ber_tlv_tag_t tag, FILE *f) { + char buf[sizeof("[APPLICATION ]") + 32]; + ssize_t ret; + + ret = ber_tlv_tag_snprint(tag, buf, sizeof(buf)); + if(ret >= sizeof(buf) || ret < 2) { + errno = EPERM; + return -1; + } + + return fwrite(buf, 1, ret, f); +} + +ssize_t +ber_tlv_tag_snprint(ber_tlv_tag_t tag, char *buf, size_t size) { + char *type = 0; + int ret; + + switch(tag & 0x3) { + case ASN_TAG_CLASS_UNIVERSAL: type = "UNIVERSAL "; break; + case ASN_TAG_CLASS_APPLICATION: type = "APPLICATION "; break; + case ASN_TAG_CLASS_CONTEXT: type = ""; break; + case ASN_TAG_CLASS_PRIVATE: type = "PRIVATE "; break; + } + + ret = snprintf(buf, size, "[%s%u]", type, ((unsigned)tag) >> 2); + if(ret <= 0 && size) buf[0] = '\0'; /* against broken libc's */ + + return ret; +} + +char * +ber_tlv_tag_string(ber_tlv_tag_t tag) { + static char buf[sizeof("[APPLICATION ]") + 32]; + + (void)ber_tlv_tag_snprint(tag, buf, sizeof(buf)); + + return buf; +} + + +ssize_t +der_tlv_tag_serialize(ber_tlv_tag_t tag, void *bufp, size_t size) { + int tclass = BER_TAG_CLASS(tag); + ber_tlv_tag_t tval = BER_TAG_VALUE(tag); + uint8_t *buf = bufp; + uint8_t *end; + ssize_t computed_size; + int i; + + if(tval <= 30) { + /* Encoded in 1 octet */ + if(size) buf[0] = (tclass << 6) | tval; + return 1; + } else if(size) { + *buf++ = (tclass << 6) | 0x1F; + size--; + } + + /* + * Compute the size of the subsequent bytes. + * The routine is written so every floating-point + * operation is done at compile time. + * Note, there is a subtle problem lurking here, + * could you guess where it is? :) + * Hint: what happens when ((8*sizeof(tag))%7) == 0? + */ + computed_size = 1 + 8 * sizeof(tag) / 7; + for(i = (8*sizeof(tag)) - ((8*sizeof(tag))%7); i >= 7; i -= 7) { + if((tval >> i) & 0x7F) break; + computed_size--; + } + + /* + * Fill in the buffer, space permitting. + */ + if(size > computed_size) + end = buf + computed_size; + else + end = buf + size; + for((void)i; buf < end; i -= 7, buf++) { + *buf = 0x80 | ((tval>>i) & 0x7F); + } + + return computed_size + 1; +} + diff --git a/skeletons/ber_tlv_tag.h b/skeletons/ber_tlv_tag.h new file mode 100644 index 00000000..50419702 --- /dev/null +++ b/skeletons/ber_tlv_tag.h @@ -0,0 +1,52 @@ +/*- + * Copyright (c) 2003, 2004 Lev Walkin <vlm@lionet.info>. All rights reserved. + * Redistribution and modifications are permitted subject to BSD license. + */ +#ifndef _BER_TLV_TAG_H_ +#define _BER_TLV_TAG_H_ + +enum asn_tag_class { + ASN_TAG_CLASS_UNIVERSAL = 0, /* 0b00 */ + ASN_TAG_CLASS_APPLICATION = 1, /* 0b01 */ + ASN_TAG_CLASS_CONTEXT = 2, /* 0b10 */ + ASN_TAG_CLASS_PRIVATE = 3, /* 0b11 */ +}; +typedef unsigned ber_tlv_tag_t; /* BER TAG from Tag-Length-Value */ + +/* + * Tag class is encoded together with tag value for optimization purposes. + */ +#define BER_TAG_CLASS(tag) ((tag) & 0x3) +#define BER_TAG_VALUE(tag) ((tag) >> 2) +#define BER_TLV_CONSTRUCTED(tagptr) (((*(uint8_t *)tagptr) & 0x20)?1:0) + +#define BER_TAGS_EQUAL(tag1, tag2) ((tag1) == (tag2)) + +/* + * Several functions for printing the TAG in the canonical form + * (i.e. "[PRIVATE 0]"). + * Return values correspond to their libc counterparts (if any). + */ +ssize_t ber_tlv_tag_snprint(ber_tlv_tag_t tag, char *buf, size_t buflen); +ssize_t ber_tlv_tag_fwrite(ber_tlv_tag_t tag, FILE *); +char *ber_tlv_tag_string(ber_tlv_tag_t tag); + + +/* + * This function tries to fetch the tag from the input stream. + * RETURN VALUES: + * 0: More data expected than bufptr contains. + * -1: Fatal error deciphering tag. + * >0: Number of bytes used from bufptr. tag_r will contain the tag. + */ +ssize_t ber_fetch_tag(void *bufptr, size_t size, ber_tlv_tag_t *tag_r); + +/* + * This function serializes the tag (T from TLV) in DER format. + * It always return number of bytes necessary to represent the tag, + * it is a caller's responsibility to check the return value + * against the supplied buffer's size. + */ +ssize_t der_tlv_tag_serialize(ber_tlv_tag_t tag, void *bufptr, size_t size); + +#endif /* _BER_TLV_TAG_H_ */ diff --git a/skeletons/constr_CHOICE.c b/skeletons/constr_CHOICE.c new file mode 100644 index 00000000..2cfac3b6 --- /dev/null +++ b/skeletons/constr_CHOICE.c @@ -0,0 +1,627 @@ +/*- + * Copyright (c) 2003, 2004 Lev Walkin <vlm@lionet.info>. All rights reserved. + * Redistribution and modifications are permitted subject to BSD license. + */ +#include <constr_CHOICE.h> +#include <netinet/in.h> /* for ntohl */ +#include <assert.h> + +/* + * Number of bytes left for this structure. + * (ctx->left) indicates the number of bytes _transferred_ for the structure. + * (size) contains the number of bytes in the buffer passed. + */ +#define LEFT ((size<ctx->left)?size:ctx->left) + +/* + * If the subprocessor function returns with an indication that it wants + * more data, it may well be a fatal decoding problem, because the + * size is constrained by the <TLV>'s L, even if the buffer size allows + * reading more data. + * For example, consider the buffer containing the following TLVs: + * <T:5><L:1><V> <T:6>... + * The TLV length clearly indicates that one byte is expected in V, but + * if the V processor returns with "want more data" even if the buffer + * contains way more data than the V processor have seen. + */ +#define SIZE_VIOLATION (ctx->left >= 0 && ctx->left <= size) + +/* + * This macro "eats" the part of the buffer which is definitely "consumed", + * i.e. was correctly converted into local representation or rightfully skipped. + */ +#define ADVANCE(num_bytes) do { \ + size_t num = num_bytes; \ + ptr += num; \ + size -= num; \ + if(ctx->left >= 0) \ + ctx->left -= num; \ + consumed_myself += num; \ + } while(0) + +/* + * Switch to the next phase of parsing. + */ +#define NEXT_PHASE(ctx) do { \ + ctx->phase++; \ + ctx->step = 0; \ + } while(0) + +/* + * Return a standardized complex structure. + */ +#define RETURN(_code) do { \ + rval.code = _code; \ + rval.consumed = consumed_myself;\ + return rval; \ + } while(0) + +/* + * See the definitions. + */ +static inline int _fetch_present_idx(const void *struct_ptr, int off, int size); +static inline void _set_present_idx(void *sptr, int offset, int size, int pres); + +/* + * Tags are canonically sorted in the tag to member table. + */ +static int +_search4tag(const void *ap, const void *bp) { + const asn1_CHOICE_tag2member_t *a = ap; + const asn1_CHOICE_tag2member_t *b = bp; + int a_class = BER_TAG_CLASS(a->el_tag); + int b_class = BER_TAG_CLASS(b->el_tag); + + if(a_class == b_class) { + ber_tlv_tag_t a_value = BER_TAG_VALUE(a->el_tag); + ber_tlv_tag_t b_value = BER_TAG_VALUE(b->el_tag); + + if(a_value == b_value) + return 0; + else if(a_value < b_value) + return -1; + else + return 1; + } else if(a_class < b_class) { + return -1; + } else { + return 1; + } +} + +/* + * The decoder of the CHOICE type. + */ +ber_dec_rval_t +CHOICE_decode_ber(asn1_TYPE_descriptor_t *sd, + void **struct_ptr, void *ptr, size_t size, int tag_mode) { + /* + * Bring closer parts of structure description. + */ + asn1_CHOICE_specifics_t *specs = sd->specifics; + asn1_CHOICE_element_t *elements = specs->elements; + + /* + * Parts of the structure being constructed. + */ + void *st = *struct_ptr; /* Target structure. */ + ber_dec_ctx_t *ctx; /* Decoder context */ + + ber_tlv_tag_t tlv_tag; /* T from TLV */ + ssize_t tag_len; /* Length of TLV's T */ + //ber_tlv_len_t tlv_len; /* L from TLV */ + ber_dec_rval_t rval; /* Return code from subparsers */ + + ssize_t consumed_myself = 0; /* Consumed bytes from ptr */ + + ASN_DEBUG("Decoding %s as CHOICE", sd->name); + + /* + * Create the target structure if it is not present already. + */ + if(st == 0) { + st = *struct_ptr = CALLOC(1, specs->struct_size); + if(st == 0) { + RETURN(RC_FAIL); + } + } + + /* + * Restore parsing context. + */ + ctx = (st + specs->ctx_offset); + + /* + * Start to parse where left previously + */ + switch(ctx->phase) { + case 0: + /* + * PHASE 0. + * Check that the set of tags associated with given structure + * perfectly fits our expectations. + */ + + if(tag_mode || sd->tags_count) { + rval = ber_check_tags(sd, ctx, ptr, size, + tag_mode, &ctx->left, 0); + if(rval.code != RC_OK) { + ASN_DEBUG("%s tagging check failed: %d", + sd->name, rval.code); + consumed_myself += rval.consumed; + RETURN(rval.code); + } + + if(ctx->left >= 0) { + /* ?Substracted below! */ + ctx->left += rval.consumed; + } + ADVANCE(rval.consumed); + } else { + ctx->left = -1; + } + + NEXT_PHASE(ctx); + + ASN_DEBUG("Structure consumes %ld bytes, buffer %ld", + (long)ctx->left, (long)size); + + /* Fall through */ + case 1: + /* + * Fetch the T from TLV. + */ + tag_len = ber_fetch_tag(ptr, LEFT, &tlv_tag); + ASN_DEBUG("In %s CHOICE tag length %d", sd->name, (int)tag_len); + switch(tag_len) { + case 0: if(!SIZE_VIOLATION) RETURN(RC_WMORE); + /* Fall through */ + case -1: RETURN(RC_FAIL); + } + + do { + asn1_CHOICE_tag2member_t *t2m; + asn1_CHOICE_tag2member_t key; + + key.el_tag = tlv_tag; + t2m = bsearch(&key, specs->tag2el, specs->tag2el_count, + sizeof(specs->tag2el[0]), _search4tag); + if(t2m) { + /* + * Found the element corresponding to the tag. + */ + NEXT_PHASE(ctx); + ctx->step = t2m->el_no; + break; + } else if(specs->extensible == 0) { + ASN_DEBUG("Unexpected tag %s " + "in non-extensible CHOICE %s", + ber_tlv_tag_string(tlv_tag), sd->name); + RETURN(RC_FAIL); + } else { + /* Skip this tag */ + ssize_t skip; + + ASN_DEBUG("Skipping unknown tag %s", + ber_tlv_tag_string(tlv_tag)); + + skip = ber_skip_length( + BER_TLV_CONSTRUCTED(ptr), + ptr + tag_len, LEFT - tag_len); + + switch(skip) { + case 0: if(!SIZE_VIOLATION) RETURN(RC_WMORE); + /* Fall through */ + case -1: RETURN(RC_FAIL); + } + + ADVANCE(skip + tag_len); + RETURN(RC_OK); + } + } while(0); + + case 2: + /* + * PHASE 2. + * Read in the element. + */ + do { + asn1_CHOICE_element_t *elm; /* CHOICE's element */ + void *memb_ptr; /* Pointer to the member */ + void *memb_ptr2; /* Pointer to that pointer */ + + elm = &elements[ctx->step]; + + /* + * Compute the position of the member inside a structure, + * and also a type of containment (it may be contained + * as pointer or using inline inclusion). + */ + if(elm->optional) { + /* Optional member, hereby, a simple pointer */ + memb_ptr2 = (char *)st + elm->memb_offset; + } else { + /* + * A pointer to a pointer + * holding the start of the structure + */ + memb_ptr = (char *)st + elm->memb_offset; + memb_ptr2 = &memb_ptr; + } + /* + * Invoke the member fetch routine according to member's type + */ + rval = elm->type->ber_decoder( + (void *)elm->type, + memb_ptr2, ptr, LEFT, + elm->tag_mode); + switch(rval.code) { + case RC_OK: + _set_present_idx(st, specs->pres_offset, + specs->pres_size, ctx->step + 1); + break; + case RC_WMORE: /* More data expected */ + if(!SIZE_VIOLATION) { + ADVANCE(rval.consumed); + RETURN(RC_WMORE); + } + RETURN(RC_FAIL); + case RC_FAIL: /* Fatal error */ + RETURN(rval.code); + } /* switch(rval) */ + + ADVANCE(rval.consumed); + } while(0); + + NEXT_PHASE(ctx); + + /* Fall through */ + case 3: + ASN_DEBUG("CHOICE %s Leftover: %ld, size = %ld, tm=%d, tc=%d", + sd->name, (long)ctx->left, (long)size, + tag_mode, sd->tags_count); + + if(ctx->left > 0) { + /* + * The type must be fully decoded + * by the CHOICE member-specific decoder. + */ + RETURN(RC_FAIL); + } + + if(ctx->left == -1 + && !(tag_mode || sd->tags_count)) { + /* + * This is an untagged CHOICE. + * It doesn't contain nothing + * except for the member itself, including all its tags. + * The decoding is completed. + */ + NEXT_PHASE(ctx); + break; + } + + /* + * Read in the "end of data chunks"'s. + */ + while(ctx->left < 0) { + ssize_t tl; + + tl = ber_fetch_tag(ptr, LEFT, &tlv_tag); + switch(tl) { + case 0: if(!SIZE_VIOLATION) RETURN(RC_WMORE); + /* Fall through */ + case -1: RETURN(RC_FAIL); + } + + /* + * Expected <0><0>... + */ + if(((uint8_t *)ptr)[0] == 0) { + if(LEFT < 2) { + if(SIZE_VIOLATION) + RETURN(RC_FAIL); + else + RETURN(RC_WMORE); + } else if(((uint8_t *)ptr)[1] == 0) { + /* + * Correctly finished with <0><0>. + */ + continue; + } + } else { + ASN_DEBUG("Unexpected continuation in %s", + sd->name); + RETURN(RC_FAIL); + } + + ADVANCE(2); + ctx->left++; + } + + NEXT_PHASE(ctx); + case 4: + /* No meaningful work here */ + break; + } + + RETURN(RC_OK); +} + +der_enc_rval_t +CHOICE_encode_der(asn1_TYPE_descriptor_t *sd, + void *struct_ptr, + int tag_mode, ber_tlv_tag_t tag, + asn_app_consume_bytes_f *cb, void *app_key) { + asn1_CHOICE_specifics_t *specs = sd->specifics; + asn1_CHOICE_element_t *elm; /* CHOICE element */ + der_enc_rval_t erval; + void *memb_ptr; + size_t computed_size = 0; + int present; + + ASN_DEBUG("%s %s as CHOICE", + cb?"Encoding":"Estimating", sd->name); + + present = _fetch_present_idx(struct_ptr, + specs->pres_offset, specs->pres_size); + + /* + * If the structure was not initialized, it cannot be encoded: + * can't deduce what to encode in the choice type. + */ + if(present <= 0 || present > specs->elements_count) { + if(present == 0 && specs->elements_count == 0) { + /* The CHOICE is empty?! */ + erval.encoded = 0; + return erval; + } + erval.encoded = -1; + erval.failed_type = sd; + erval.structure_ptr = struct_ptr; + return erval; + } + + /* + * Seek over the present member of the structure. + */ + elm = &specs->elements[present-1]; + if(elm->optional) { + memb_ptr = *(void **)((char *)struct_ptr + elm->memb_offset); + if(memb_ptr == 0) { + erval.encoded = 0; + return erval; + } + } else { + memb_ptr = (void *)((char *)struct_ptr + elm->memb_offset); + } + + /* + * If the CHOICE itself is tagged EXPLICIT: + * T ::= [2] EXPLICIT CHOICE { ... } + * Then emit the appropriate tags. + */ + if(tag_mode == 1 || sd->tags_count) { + /* + * For this, we need to pre-compute the member. + */ + ssize_t ret; + + /* Encode member with its tag */ + erval = elm->type->der_encoder(elm->type, memb_ptr, + elm->tag_mode, elm->tag, 0, 0); + if(erval.encoded == -1) + return erval; + + /* Encode CHOICE with parent or my own tag */ + ret = der_write_tags(sd, erval.encoded, tag_mode, tag, + cb, app_key); + if(ret == -1) { + erval.encoded = -1; + erval.failed_type = sd; + erval.structure_ptr = struct_ptr; + return erval; + } + computed_size += ret; + } + + /* + * Encode the single underlying member. + */ + erval = elm->type->der_encoder(elm->type, memb_ptr, + elm->tag_mode, elm->tag, cb, app_key); + if(erval.encoded == -1) + return erval; + + ASN_DEBUG("Encoded CHOICE member in %ld bytes (+%ld)", + (long)erval.encoded, (long)computed_size); + + erval.encoded += computed_size; + + return erval; +} + +ber_tlv_tag_t +CHOICE_outmost_tag(asn1_TYPE_descriptor_t *td, const void *ptr, int tag_mode, ber_tlv_tag_t tag) { + asn1_CHOICE_specifics_t *specs = td->specifics; + int present; + + assert(tag_mode == 0); + assert(tag == 0); + + /* + * Figure out which CHOICE element is encoded. + */ + present = _fetch_present_idx(ptr, specs->pres_offset, specs->pres_size); + + if(present > 0 || present <= specs->elements_count) { + asn1_CHOICE_element_t *elm = &specs->elements[present-1]; + void *memb_ptr; + + if(elm->optional) { + memb_ptr = *(void **)((char *)ptr + elm->memb_offset); + } else { + memb_ptr = (void *)((char *)ptr + elm->memb_offset); + } + + return asn1_TYPE_outmost_tag(elm->type, memb_ptr, + elm->tag_mode, elm->tag); + } else { + return -1; + } +} + +int +CHOICE_constraint(asn1_TYPE_descriptor_t *td, const void *sptr, + asn_app_consume_bytes_f *app_errlog, void *app_key) { + asn1_CHOICE_specifics_t *specs = td->specifics; + int present; + + if(!sptr) { + _ASN_ERRLOG("%s: value not given", td->name); + return -1; + } + + /* + * Figure out which CHOICE element is encoded. + */ + present = _fetch_present_idx(sptr, specs->pres_offset,specs->pres_size); + if(present > 0 && present <= specs->elements_count) { + asn1_CHOICE_element_t *elm = &specs->elements[present-1]; + const void *memb_ptr; + + if(elm->optional) { + memb_ptr = *(const void * const *)((const char *)sptr + elm->memb_offset); + if(!memb_ptr) return 0; + } else { + memb_ptr = (const void *)((const char *)sptr + elm->memb_offset); + } + + return elm->type->check_constraints(elm->type, memb_ptr, + app_errlog, app_key); + } else { + _ASN_ERRLOG("%s: no CHOICE element given", td->name); + return -1; + } +} + +int +CHOICE_print(asn1_TYPE_descriptor_t *td, const void *sptr, int ilevel, + asn_app_consume_bytes_f *cb, void *app_key) { + asn1_CHOICE_specifics_t *specs = td->specifics; + int present; + + if(!sptr) return cb("<absent>", 8, app_key); + + /* + * Figure out which CHOICE element is encoded. + */ + present = _fetch_present_idx(sptr, specs->pres_offset,specs->pres_size); + + /* + * Free that element. + */ + if(present > 0 && present <= specs->elements_count) { + asn1_CHOICE_element_t *elm = &specs->elements[present-1]; + const void *memb_ptr; + + if(elm->optional) { + memb_ptr = *(const void * const *)((const char *)sptr + elm->memb_offset); + if(!memb_ptr) return cb("<absent>", 8, app_key); + } else { + memb_ptr = (const void *)((const char *)sptr + elm->memb_offset); + } + + /* Print member's name and stuff */ + if(cb(elm->name, strlen(elm->name), app_key) + || cb(": ", 2, app_key)) + return -1; + + return elm->type->print_struct(elm->type, memb_ptr, ilevel, + cb, app_key); + } else { + return cb("<absent>", 8, app_key); + } +} + +void +CHOICE_free(asn1_TYPE_descriptor_t *td, void *ptr, int contents_only) { + asn1_CHOICE_specifics_t *specs = td->specifics; + int present; + + if(!td || !ptr) + return; + + ASN_DEBUG("Freeing %s as CHOICE", td->name); + + /* + * Figure out which CHOICE element is encoded. + */ + present = _fetch_present_idx(ptr, specs->pres_offset, specs->pres_size); + + /* + * Free that element. + */ + if(present > 0 && present <= specs->elements_count) { + asn1_CHOICE_element_t *elm = &specs->elements[present-1]; + void *memb_ptr; + + if(elm->optional) { + memb_ptr = *(void **)((char *)ptr + elm->memb_offset); + if(memb_ptr) + elm->type->free_struct(elm->type, memb_ptr, 0); + } else { + memb_ptr = (void *)((char *)ptr + elm->memb_offset); + elm->type->free_struct(elm->type, memb_ptr, 1); + } + } + + if(!contents_only) { + FREEMEM(ptr); + } +} + + +/* + * The following functions functions offer protection against -fshort-enums, + * compatible with little- and big-endian machines. + * If assertion is triggered, either disable -fshort-enums, or add an entry + * here with the ->pres_size of your target stracture. + * Unless the target structure is packed, the ".present" member + * is guaranteed to be aligned properly. ASN.1 compiler itself does not + * produce packed code. + */ +static inline int +_fetch_present_idx(const void *struct_ptr, int pres_offset, int pres_size) { + const void *present_ptr; + int present; + + present_ptr = ((const char *)struct_ptr) + pres_offset; + + switch(pres_size) { + case sizeof(int): present = *(const int *)present_ptr; break; + case sizeof(short): present = *(const short *)present_ptr; break; + case sizeof(char): present = *(const char *)present_ptr; break; + default: + /* ANSI C mandates enum to be equivalent to integer */ + assert(pres_size != sizeof(int)); + return 0; /* If not aborted, pass back safe value */ + } + + return present; +} + +static inline void +_set_present_idx(void *struct_ptr, int pres_offset, int pres_size, int present) { + void *present_ptr; + present_ptr = ((char *)struct_ptr) + pres_offset; + + switch(pres_size) { + case sizeof(int): *(int *)present_ptr = present; break; + case sizeof(short): *(short *)present_ptr = present; break; + case sizeof(char): *(char *)present_ptr = present; break; + default: + /* ANSI C mandates enum to be equivalent to integer */ + assert(pres_size != sizeof(int)); + } +} diff --git a/skeletons/constr_CHOICE.h b/skeletons/constr_CHOICE.h new file mode 100644 index 00000000..70766e9d --- /dev/null +++ b/skeletons/constr_CHOICE.h @@ -0,0 +1,65 @@ +/*- + * Copyright (c) 2003, 2004 Lev Walkin <vlm@lionet.info>. All rights reserved. + * Redistribution and modifications are permitted subject to BSD license. + */ +#ifndef _CONSTR_CHOICE_H_ +#define _CONSTR_CHOICE_H_ + +#include <constr_TYPE.h> + +/* + * A single element of the CHOICE type. + */ +typedef struct asn1_CHOICE_element_s { + int memb_offset; /* Offset of the element */ + int optional; /* Whether the element is optional */ + ber_tlv_tag_t tag; /* Outmost (most immediate) tag */ + int tag_mode; /* IMPLICIT/no/EXPLICIT tag at current level */ + asn1_TYPE_descriptor_t + *type; /* Member type descriptor */ + char *name; /* ASN.1 identifier of the element */ +} asn1_CHOICE_element_t; + +typedef struct asn1_CHOICE_tag2member_s { + ber_tlv_tag_t el_tag; /* Outmost tag of the member */ + int el_no; /* Index of the associated member, base 0 */ +} asn1_CHOICE_tag2member_t; + +typedef struct asn1_CHOICE_specifics_s { + /* + * Target structure description. + */ + int struct_size; /* Size of the target structure. */ + int ctx_offset; /* Offset of the ber_dec_ctx_t member */ + int pres_offset; /* Identifier of the present member */ + int pres_size; /* Size of the identifier (enum) */ + + /* + * Members of the CHOICE structure. + */ + asn1_CHOICE_element_t *elements; + int elements_count; + + /* + * Tags to members mapping table. + */ + asn1_CHOICE_tag2member_t *tag2el; + int tag2el_count; + + /* + * Extensions-related stuff. + */ + int extensible; /* Whether CHOICE is extensible */ +} asn1_CHOICE_specifics_t; + +/* + * A set specialized functions dealing with the CHOICE type. + */ +asn_constr_check_f CHOICE_constraint; +ber_type_decoder_f CHOICE_decode_ber; +der_type_encoder_f CHOICE_encode_der; +asn_outmost_tag_f CHOICE_outmost_tag; +asn_struct_print_f CHOICE_print; +asn_struct_free_f CHOICE_free; + +#endif /* _CONSTR_CHOICE_H_ */ diff --git a/skeletons/constr_SEQUENCE.c b/skeletons/constr_SEQUENCE.c new file mode 100644 index 00000000..6704dd84 --- /dev/null +++ b/skeletons/constr_SEQUENCE.c @@ -0,0 +1,588 @@ +/*- + * Copyright (c) 2003, 2004 Lev Walkin <vlm@lionet.info>. All rights reserved. + * Redistribution and modifications are permitted subject to BSD license. + */ +#include <constr_SEQUENCE.h> + +/* + * Number of bytes left for this structure. + * (ctx->left) indicates the number of bytes _transferred_ for the structure. + * (size) contains the number of bytes in the buffer passed. + */ +#define LEFT ((size<ctx->left)?size:ctx->left) + +/* + * If the subprocessor function returns with an indication that it wants + * more data, it may well be a fatal decoding problem, because the + * size is constrained by the <TLV>'s L, even if the buffer size allows + * reading more data. + * For example, consider the buffer containing the following TLVs: + * <T:5><L:1><V> <T:6>... + * The TLV length clearly indicates that one byte is expected in V, but + * if the V processor returns with "want more data" even if the buffer + * contains way more data than the V processor have seen. + */ +#define SIZE_VIOLATION (ctx->left >= 0 && ctx->left <= size) + +/* + * This macro "eats" the part of the buffer which is definitely "consumed", + * i.e. was correctly converted into local representation or rightfully skipped. + */ +#define ADVANCE(num_bytes) do { \ + size_t num = num_bytes; \ + ptr += num; \ + size -= num; \ + if(ctx->left >= 0) \ + ctx->left -= num; \ + consumed_myself += num; \ + } while(0) + +/* + * Switch to the next phase of parsing. + */ +#define NEXT_PHASE(ctx) do { \ + ctx->phase++; \ + ctx->step = 0; \ + } while(0) +#define PHASE_OUT(ctx) do { ctx->phase = 10; } while(0) + +/* + * Return a standardized complex structure. + */ +#define RETURN(_code) do { \ + rval.code = _code; \ + rval.consumed = consumed_myself;\ + return rval; \ + } while(0) + +/* + * Check whether we are inside the extensions group. + */ +#define IN_EXTENSION_GROUP(specs, memb_idx) \ + ( ((memb_idx) > (specs)->ext_after) \ + &&((memb_idx) < (specs)->ext_before)) + +/* + * The decoder of the SEQUENCE type. + */ +ber_dec_rval_t +SEQUENCE_decode_ber(asn1_TYPE_descriptor_t *sd, + void **struct_ptr, void *ptr, size_t size, int tag_mode) { + /* + * Bring closer parts of structure description. + */ + asn1_SEQUENCE_specifics_t *specs = sd->specifics; + asn1_SEQUENCE_element_t *elements = specs->elements; + + /* + * Parts of the structure being constructed. + */ + void *st = *struct_ptr; /* Target structure. */ + ber_dec_ctx_t *ctx; /* Decoder context */ + + ber_tlv_tag_t tlv_tag; /* T from TLV */ + //ber_tlv_len_t tlv_len; /* L from TLV */ + ber_dec_rval_t rval; /* Return code from subparsers */ + + ssize_t consumed_myself = 0; /* Consumed bytes from ptr */ + int edx; /* SEQUENCE element's index */ + + ASN_DEBUG("Decoding %s as SEQUENCE", sd->name); + + /* + * Create the target structure if it is not present already. + */ + if(st == 0) { + st = *struct_ptr = CALLOC(1, specs->struct_size); + if(st == 0) { + RETURN(RC_FAIL); + } + } + + /* + * Restore parsing context. + */ + ctx = (st + specs->ctx_offset); + + /* + * Start to parse where left previously + */ + switch(ctx->phase) { + case 0: + /* + * PHASE 0. + * Check that the set of tags associated with given structure + * perfectly fits our expectations. + */ + + rval = ber_check_tags(sd, ctx, ptr, size, + tag_mode, &ctx->left, 0); + if(rval.code != RC_OK) { + ASN_DEBUG("%s tagging check failed: %d", + sd->name, rval.code); + consumed_myself += rval.consumed; + RETURN(rval.code); + } + + if(ctx->left >= 0) + ctx->left += rval.consumed; /* ?Substracted below! */ + ADVANCE(rval.consumed); + + NEXT_PHASE(ctx); + + ASN_DEBUG("Structure consumes %ld bytes, buffer %ld", + (long)ctx->left, (long)size); + + /* Fall through */ + case 1: + /* + * PHASE 1. + * From the place where we've left it previously, + * try to decode the next member from the list of + * this structure's elements. + * (ctx->step) stores the member being processed + * between invocations and the microphase {0,1} of parsing + * that member: + * step = (<member_number> * 2 + <microphase>). + */ + for(edx = (ctx->step >> 1); edx < specs->elements_count; + edx++, ctx->step = (ctx->step & ~1) + 2) { + void *memb_ptr; /* Pointer to the member */ + void *memb_ptr2; /* Pointer to that pointer */ + ssize_t tag_len; /* Length of TLV's T */ + int opt_edx_end; /* Next non-optional element */ + int n; + + if(ctx->step & 1) + goto microphase2; + + /* + * MICROPHASE 1: Synchronize decoding. + */ + ASN_DEBUG("In %s SEQUENCE left %d, edx=%d opt=%d ec=%d", + sd->name, (int)ctx->left, + edx, elements[edx].optional, specs->elements_count); + + if(ctx->left == 0 /* No more stuff is expected */ + && ( + /* Explicit OPTIONAL specification reaches the end */ + (edx + elements[edx].optional == specs->elements_count) + || + /* All extensions are optional */ + (IN_EXTENSION_GROUP(specs, edx) + && specs->ext_before > specs->elements_count) + ) + ) { + ASN_DEBUG("End of SEQUENCE %s", sd->name); + /* + * Found the legitimate end of the structure. + */ + PHASE_OUT(ctx); + RETURN(RC_OK); + } + + /* + * Fetch the T from TLV. + */ + tag_len = ber_fetch_tag(ptr, LEFT, &tlv_tag); + ASN_DEBUG("In %s SEQUENCE for %d %s next tag length %d", + sd->name, edx, elements[edx].name, (int)tag_len); + switch(tag_len) { + case 0: if(!SIZE_VIOLATION) RETURN(RC_WMORE); + /* Fall through */ + case -1: RETURN(RC_FAIL); + } + + /* + * Find the next available type with this tag. + */ + opt_edx_end = edx + elements[edx].optional + 1; + if(opt_edx_end > specs->elements_count) + opt_edx_end = specs->elements_count; /* Cap */ + for(n = edx; n < opt_edx_end; n++) { + if(BER_TAGS_EQUAL(tlv_tag, elements[n].tag)) { + /* + * Found element corresponding to the tag + * being looked at. + * Reposition over the right element. + */ + edx = n; + ctx->step = 2 * edx; /* Remember! */ + break; + } + } + if(n == opt_edx_end) { + /* + * If tag is unknown, it may be either + * an unknown (thus, incorrect) tag, + * or an extension (...), + * or an end of the indefinite-length structure. + */ + + if(!IN_EXTENSION_GROUP(specs, edx)) { + ASN_DEBUG("Unexpected tag %s", + ber_tlv_tag_string(tlv_tag)); + ASN_DEBUG("Expected tag %s%s", + ber_tlv_tag_string(elements[edx].tag), + elements[edx].optional + ?" or alternatives":""); + RETURN(RC_FAIL); + } + + if(ctx->left < 0 + && ((uint8_t *)ptr)[0] == 0) { + if(LEFT < 2) { + if(SIZE_VIOLATION) + RETURN(RC_FAIL); + else + RETURN(RC_WMORE); + } else if(((uint8_t *)ptr)[1] == 0) { + /* + * Yeah, baby! Found the terminator + * of the indefinite length structure. + */ + /* + * Proceed to the canonical + * finalization function. + * No advancing is necessary. + */ + goto phase3; + } + } else { + /* Skip this tag */ + ssize_t skip; + + skip = ber_skip_length( + BER_TLV_CONSTRUCTED(ptr), + ptr + tag_len, LEFT - tag_len); + ASN_DEBUG("Skip length %d in %s", + (int)skip, sd->name); + switch(skip) { + case 0: if(!SIZE_VIOLATION) RETURN(RC_WMORE); + /* Fall through */ + case -1: RETURN(RC_FAIL); + } + + ADVANCE(skip + tag_len); + ctx->step -= 2; + edx--; + continue; /* Try again with the next tag */ + } + } + + /* + * MICROPHASE 2: Invoke the member-specific decoder. + */ + ctx->step |= 1; /* Confirm entering next microphase */ + microphase2: + ASN_DEBUG("Inside SEQUENCE %s MF2", sd->name); + + /* + * Compute the position of the member inside a structure, + * and also a type of containment (it may be contained + * as pointer or using inline inclusion). + */ + if(elements[edx].optional) { + /* Optional member, hereby, a simple pointer */ + memb_ptr2 = (char *)st + elements[edx].memb_offset; + } else { + /* + * A pointer to a pointer + * holding the start of the structure + */ + memb_ptr = (char *)st + elements[edx].memb_offset; + memb_ptr2 = &memb_ptr; + } + /* + * Invoke the member fetch routine according to member's type + */ + rval = elements[edx].type->ber_decoder( + (void *)elements[edx].type, + memb_ptr2, ptr, LEFT, + elements[edx].tag_mode); + ASN_DEBUG("In %s SEQUENCE decoded %d %s in %d bytes code %d", + sd->name, edx, elements[edx].type->name, + (int)rval.consumed, rval.code); + switch(rval.code) { + case RC_OK: + break; + case RC_WMORE: /* More data expected */ + if(!SIZE_VIOLATION) { + ADVANCE(rval.consumed); + RETURN(RC_WMORE); + } + /* Fall through */ + case RC_FAIL: /* Fatal error */ + RETURN(RC_FAIL); + } /* switch(rval) */ + + ADVANCE(rval.consumed); + } /* for(all structure members) */ + + phase3: + ctx->phase = 3; + case 3: /* 00 and other tags expected */ + case 4: /* only 00's expected */ + + ASN_DEBUG("SEQUENCE %s Leftover: %ld, size = %ld", + sd->name, (long)ctx->left, (long)size); + + /* + * Skip everything until the end of the SEQUENCE. + */ + while(ctx->left) { + ssize_t tl, ll; + + tl = ber_fetch_tag(ptr, LEFT, &tlv_tag); + switch(tl) { + case 0: if(!SIZE_VIOLATION) RETURN(RC_WMORE); + /* Fall through */ + case -1: RETURN(RC_FAIL); + } + + /* + * If expected <0><0>... + */ + if(ctx->left < 0 + && ((uint8_t *)ptr)[0] == 0) { + if(LEFT < 2) { + if(SIZE_VIOLATION) + RETURN(RC_FAIL); + else + RETURN(RC_WMORE); + } else if(((uint8_t *)ptr)[1] == 0) { + /* + * Correctly finished with <0><0>. + */ + ADVANCE(2); + ctx->left++; + ctx->phase = 4; + continue; + } + } + + if(!IN_EXTENSION_GROUP(specs, specs->elements_count) + || ctx->phase == 4) { + ASN_DEBUG("Unexpected continuation " + "of a non-extensible type " + "%s (SEQUENCE): %s", + sd->name, + ber_tlv_tag_string(tlv_tag)); + RETURN(RC_FAIL); + } + + ll = ber_skip_length( + BER_TLV_CONSTRUCTED(ptr), + ptr + tl, LEFT - tl); + switch(ll) { + case 0: if(!SIZE_VIOLATION) RETURN(RC_WMORE); + /* Fall through */ + case -1: RETURN(RC_FAIL); + } + + ADVANCE(tl + ll); + } + + PHASE_OUT(ctx); + } + + RETURN(RC_OK); +} + +/* + * The DER encoder of the SEQUENCE type. + */ +der_enc_rval_t +SEQUENCE_encode_der(asn1_TYPE_descriptor_t *sd, + void *ptr, int tag_mode, ber_tlv_tag_t tag, + asn_app_consume_bytes_f *cb, void *app_key) { + asn1_SEQUENCE_specifics_t *specs = sd->specifics; + size_t computed_size = 0; + der_enc_rval_t erval; + ssize_t ret; + int edx; + + ASN_DEBUG("%s %s as SEQUENCE", + cb?"Encoding":"Estimating", sd->name); + + /* + * Gather the length of the underlying members sequence. + */ + for(edx = 0; edx < specs->elements_count; edx++) { + asn1_SEQUENCE_element_t *elm = &specs->elements[edx]; + void *memb_ptr; + if(elm->optional) { + memb_ptr = *(void **)((char *)ptr + elm->memb_offset); + if(!memb_ptr) continue; + } else { + memb_ptr = (void *)((char *)ptr + elm->memb_offset); + } + erval = elm->type->der_encoder(elm->type, memb_ptr, + elm->tag_mode, elm->tag, + 0, 0); + if(erval.encoded == -1) + return erval; + computed_size += erval.encoded; + ASN_DEBUG("Member %d %s estimated %ld bytes", + edx, elm->name, (long)erval.encoded); + } + + /* + * Encode the TLV for the sequence itself. + */ + ret = der_write_tags(sd, computed_size, tag_mode, tag, cb, app_key); + ASN_DEBUG("Wrote tags: %ld (+%ld)", (long)ret, (long)computed_size); + if(ret == -1) { + erval.encoded = -1; + erval.failed_type = sd; + erval.structure_ptr = ptr; + return erval; + } + erval.encoded = computed_size + ret; + + if(!cb) return erval; + + /* + * Encode all members. + */ + for(edx = 0; edx < specs->elements_count; edx++) { + asn1_SEQUENCE_element_t *elm = &specs->elements[edx]; + der_enc_rval_t tmperval; + void *memb_ptr; + + if(elm->optional) { + memb_ptr = *(void **)((char *)ptr + elm->memb_offset); + if(!memb_ptr) continue; + } else { + memb_ptr = (void *)((char *)ptr + elm->memb_offset); + } + tmperval = elm->type->der_encoder(elm->type, memb_ptr, + elm->tag_mode, elm->tag, + cb, app_key); + if(tmperval.encoded == -1) + return tmperval; + computed_size -= tmperval.encoded; + ASN_DEBUG("Member %d %s of SEQUENCE %s encoded in %d bytes", + edx, elm->name, sd->name, tmperval.encoded); + } + + if(computed_size != 0) { + /* + * Encoded size is not equal to the computed size. + */ + erval.encoded = -1; + erval.failed_type = sd; + erval.structure_ptr = ptr; + } + + return erval; +} + +int +SEQUENCE_print(asn1_TYPE_descriptor_t *td, const void *sptr, int ilevel, + asn_app_consume_bytes_f *cb, void *app_key) { + asn1_SEQUENCE_specifics_t *specs = td->specifics; + int edx; + int ret; + + if(!sptr) return cb("<absent>", 8, app_key); + + /* Dump preamble */ + if(cb(td->name, strlen(td->name), app_key) + || cb(" ::= {\n", 7, app_key)) + return -1; + + for(edx = 0; edx < specs->elements_count; edx++) { + asn1_SEQUENCE_element_t *elm = &specs->elements[edx]; + const void *memb_ptr; + + if(elm->optional) { + memb_ptr = *(const void * const *)((const char *)sptr + elm->memb_offset); + if(!memb_ptr) continue; + } else { + memb_ptr = (const void *)((const char *)sptr + elm->memb_offset); + } + + /* Indentation */ + for(ret = 0; ret < ilevel; ret++) cb(" ", 1, app_key); + + /* Print the member's name and stuff */ + if(cb(elm->name, strlen(elm->name), app_key) + || cb(": ", 2, app_key)) + return -1; + + /* Print the member itself */ + ret = elm->type->print_struct(elm->type, memb_ptr, ilevel + 4, + cb, app_key); + if(ret) return ret; + + /* Print out the terminator */ + ret = cb("\n", 1, app_key); + if(ret) return ret; + } + + /* Indentation */ + for(ret = 0; ret < ilevel - 4; ret++) cb(" ", 1, app_key); + + return cb("}", 1, app_key); +} + +void +SEQUENCE_free(asn1_TYPE_descriptor_t *td, void *sptr, int contents_only) { + asn1_SEQUENCE_specifics_t *specs = td->specifics; + int edx; + + if(!td || !sptr) + return; + + ASN_DEBUG("Freeing %s as SEQUENCE", td->name); + + for(edx = 0; edx < specs->elements_count; edx++) { + asn1_SEQUENCE_element_t *elm = &specs->elements[edx]; + void *memb_ptr; + if(elm->optional) { + memb_ptr = *(void **)((char *)sptr + elm->memb_offset); + if(memb_ptr) + elm->type->free_struct(elm->type, memb_ptr, 0); + } else { + memb_ptr = (void *)((char *)sptr + elm->memb_offset); + elm->type->free_struct(elm->type, memb_ptr, 1); + } + } + + if(!contents_only) { + FREEMEM(sptr); + } +} + +int +SEQUENCE_constraint(asn1_TYPE_descriptor_t *td, const void *sptr, + asn_app_consume_bytes_f *app_errlog, void *app_key) { + asn1_SEQUENCE_specifics_t *specs = td->specifics; + int edx; + + if(!sptr) { + _ASN_ERRLOG("%s: value not given", td->name); + return -1; + } + + /* + * Iterate over structure members and check their validity. + */ + for(edx = 0; edx < specs->elements_count; edx++) { + asn1_SEQUENCE_element_t *elm = &specs->elements[edx]; + const void *memb_ptr; + + if(elm->optional) { + memb_ptr = *(const void **)((const char *)sptr + elm->memb_offset); + if(!memb_ptr) continue; + } else { + memb_ptr = (const void *)((const char *)sptr + elm->memb_offset); + } + + return elm->type->check_constraints(elm->type, memb_ptr, + app_errlog, app_key); + } + + return 0; +} diff --git a/skeletons/constr_SEQUENCE.h b/skeletons/constr_SEQUENCE.h new file mode 100644 index 00000000..c15729ca --- /dev/null +++ b/skeletons/constr_SEQUENCE.h @@ -0,0 +1,53 @@ +/*- + * Copyright (c) 2003, 2004 Lev Walkin <vlm@lionet.info>. All rights reserved. + * Redistribution and modifications are permitted subject to BSD license. + */ +#ifndef _CONSTR_SEQUENCE_H_ +#define _CONSTR_SEQUENCE_H_ + +#include <constr_TYPE.h> + +/* + * A single element of the SEQUENCE type. + */ +typedef struct asn1_SEQUENCE_element_s { + int memb_offset; /* Offset of the element */ + int optional; /* Whether the element is optional */ + ber_tlv_tag_t tag; /* Outmost (most immediate) tag */ + int tag_mode; /* IMPLICIT/no/EXPLICIT tag at current level */ + asn1_TYPE_descriptor_t + *type; /* Member type descriptor */ + char *name; /* ASN.1 identifier of the element */ +} asn1_SEQUENCE_element_t; + +typedef struct asn1_SEQUENCE_specifics_s { + /* + * Target structure description. + */ + int struct_size; /* Size of the target structure. */ + int ctx_offset; /* Offset of the ber_dec_ctx_t member */ + + /* + * Members of the SEQUENCE structure. + */ + asn1_SEQUENCE_element_t *elements; + int elements_count; + + /* + * Description of an extensions group. + */ + int ext_after; /* Extensions start after this member */ + int ext_before; /* Extensions stop before this member */ +} asn1_SEQUENCE_specifics_t; + + +/* + * A set specialized functions dealing with the SEQUENCE type. + */ +asn_constr_check_f SEQUENCE_constraint; +ber_type_decoder_f SEQUENCE_decode_ber; +der_type_encoder_f SEQUENCE_encode_der; +asn_struct_print_f SEQUENCE_print; +asn_struct_free_f SEQUENCE_free; + +#endif /* _CONSTR_SEQUENCE_H_ */ diff --git a/skeletons/constr_SEQUENCE_OF.c b/skeletons/constr_SEQUENCE_OF.c new file mode 100644 index 00000000..cdddf607 --- /dev/null +++ b/skeletons/constr_SEQUENCE_OF.c @@ -0,0 +1,84 @@ +/*- + * Copyright (c) 2003, 2004 Lev Walkin <vlm@lionet.info>. All rights reserved. + * Redistribution and modifications are permitted subject to BSD license. + */ +#include <constr_SEQUENCE_OF.h> +#include <asn_SEQUENCE_OF.h> + +/* + * The DER encoder of the SEQUENCE OF type. + */ +der_enc_rval_t +SEQUENCE_OF_encode_der(asn1_TYPE_descriptor_t *sd, void *ptr, + int tag_mode, ber_tlv_tag_t tag, + asn_app_consume_bytes_f *cb, void *app_key) { + asn1_SET_OF_specifics_t *specs = sd->specifics; + asn1_SET_OF_element_t *elm = specs->element; + A_SEQUENCE_OF(void) *list = ptr; + size_t computed_size = 0; + ssize_t encoding_size = 0; + der_enc_rval_t erval; + int edx; + + ASN_DEBUG("Estimating size of SEQUENCE OF %s", sd->name); + + /* + * Gather the length of the underlying members sequence. + */ + for(edx = 0; edx < list->count; edx++) { + void *memb_ptr = list->array[edx]; + erval = elm->type->der_encoder(elm->type, memb_ptr, + 0, elm->tag, + 0, 0); + if(erval.encoded == -1) + return erval; + computed_size += erval.encoded; + } + + /* + * Encode the TLV for the sequence itself. + */ + encoding_size = der_write_tags(sd, computed_size, tag_mode, tag, + cb, app_key); + if(encoding_size == -1) { + erval.encoded = -1; + erval.failed_type = sd; + erval.structure_ptr = ptr; + return erval; + } + + computed_size += encoding_size; + if(!cb) { + erval.encoded = computed_size; + return erval; + } + + ASN_DEBUG("Encoding members of SEQUENCE OF %s", sd->name); + + /* + * Encode all members. + */ + for(edx = 0; edx < list->count; edx++) { + void *memb_ptr = list->array[edx]; + erval = elm->type->der_encoder(elm->type, memb_ptr, + 0, elm->tag, + cb, app_key); + if(erval.encoded == -1) + return erval; + encoding_size += erval.encoded; + } + + if(computed_size != encoding_size) { + /* + * Encoded size is not equal to the computed size. + */ + erval.encoded = -1; + erval.failed_type = sd; + erval.structure_ptr = ptr; + } else { + erval.encoded = computed_size; + } + + return erval; +} + diff --git a/skeletons/constr_SEQUENCE_OF.h b/skeletons/constr_SEQUENCE_OF.h new file mode 100644 index 00000000..d2560c6a --- /dev/null +++ b/skeletons/constr_SEQUENCE_OF.h @@ -0,0 +1,21 @@ +/*- + * Copyright (c) 2003 Lev Walkin <vlm@lionet.info>. All rights reserved. + * Redistribution and modifications are permitted subject to BSD license. + */ +#ifndef _CONSTR_SEQUENCE_OF_H_ +#define _CONSTR_SEQUENCE_OF_H_ + +#include <constr_TYPE.h> +#include <constr_SET_OF.h> /* Implemented using SET OF */ + +/* + * A set specialized functions dealing with the SEQUENCE OF type. + * Implemented using SET OF. + */ +#define SEQUENCE_OF_constraint SET_OF_constraint +#define SEQUENCE_OF_decode_ber SET_OF_decode_ber +der_type_encoder_f SEQUENCE_OF_encode_der; +#define SEQUENCE_OF_print SET_OF_print +#define SEQUENCE_OF_free SET_OF_free + +#endif /* _CONSTR_SET_OF_H_ */ diff --git a/skeletons/constr_SET.c b/skeletons/constr_SET.c new file mode 100644 index 00000000..f1df9c1b --- /dev/null +++ b/skeletons/constr_SET.c @@ -0,0 +1,682 @@ +/*- + * Copyright (c) 2003, 2004 Lev Walkin <vlm@lionet.info>. All rights reserved. + * Redistribution and modifications are permitted subject to BSD license. + */ +#include <constr_SET.h> +#include <netinet/in.h> /* for ntohl() */ +#include <assert.h> /* for assert() */ + +/* + * Number of bytes left for this structure. + * (ctx->left) indicates the number of bytes _transferred_ for the structure. + * (size) contains the number of bytes in the buffer passed. + */ +#define LEFT ((size<ctx->left)?size:ctx->left) + +/* + * If the subprocessor function returns with an indication that it wants + * more data, it may well be a fatal decoding problem, because the + * size is constrained by the <TLV>'s L, even if the buffer size allows + * reading more data. + * For example, consider the buffer containing the following TLVs: + * <T:5><L:1><V> <T:6>... + * The TLV length clearly indicates that one byte is expected in V, but + * if the V processor returns with "want more data" even if the buffer + * contains way more data than the V processor have seen. + */ +#define SIZE_VIOLATION (ctx->left >= 0 && ctx->left <= size) + +/* + * This macro "eats" the part of the buffer which is definitely "consumed", + * i.e. was correctly converted into local representation or rightfully skipped. + */ +#define ADVANCE(num_bytes) do { \ + size_t num = num_bytes; \ + ptr += num; \ + size -= num; \ + if(ctx->left >= 0) \ + ctx->left -= num; \ + consumed_myself += num; \ + } while(0) + +/* + * Switch to the next phase of parsing. + */ +#define NEXT_PHASE(ctx) do { \ + ctx->phase++; \ + ctx->step = 0; \ + } while(0) + +/* + * Return a standardized complex structure. + */ +#define RETURN(_code) do { \ + rval.code = _code; \ + rval.consumed = consumed_myself;\ + return rval; \ + } while(0) + +/* + * Tags are canonically sorted in the tag2element map. + */ +static int +_t2e_cmp(const void *ap, const void *bp) { + const asn1_SET_tag2member_t *a = ap; + const asn1_SET_tag2member_t *b = bp; + int a_class = BER_TAG_CLASS(a->el_tag); + int b_class = BER_TAG_CLASS(b->el_tag); + + if(a_class == b_class) { + ber_tlv_tag_t a_value = BER_TAG_VALUE(a->el_tag); + ber_tlv_tag_t b_value = BER_TAG_VALUE(b->el_tag); + + if(a_value == b_value) + return 0; + else if(a_value < b_value) + return -1; + else + return 1; + } else if(a_class < b_class) { + return -1; + } else { + return 1; + } +} + +/* + * The decoder of the SET type. + */ +ber_dec_rval_t +SET_decode_ber(asn1_TYPE_descriptor_t *sd, + void **struct_ptr, void *ptr, size_t size, int tag_mode) { + /* + * Bring closer parts of structure description. + */ + asn1_SET_specifics_t *specs = sd->specifics; + asn1_SET_element_t *elements = specs->elements; + + /* + * Parts of the structure being constructed. + */ + void *st = *struct_ptr; /* Target structure. */ + ber_dec_ctx_t *ctx; /* Decoder context */ + + ber_tlv_tag_t tlv_tag; /* T from TLV */ + //ber_tlv_len_t tlv_len; /* L from TLV */ + ber_dec_rval_t rval; /* Return code from subparsers */ + + ssize_t consumed_myself = 0; /* Consumed bytes from ptr */ + int edx; /* SET element's index */ + + ASN_DEBUG("Decoding %s as SET", sd->name); + + /* + * Create the target structure if it is not present already. + */ + if(st == 0) { + st = *struct_ptr = CALLOC(1, specs->struct_size); + if(st == 0) { + RETURN(RC_FAIL); + } + } + + /* + * Restore parsing context. + */ + ctx = (st + specs->ctx_offset); + + /* + * Start to parse where left previously + */ + switch(ctx->phase) { + case 0: + /* + * PHASE 0. + * Check that the set of tags associated with given structure + * perfectly fits our expectations. + */ + + rval = ber_check_tags(sd, ctx, ptr, size, + tag_mode, &ctx->left, 0); + if(rval.code != RC_OK) { + ASN_DEBUG("%s tagging check failed: %d", + sd->name, rval.code); + consumed_myself += rval.consumed; + RETURN(rval.code); + } + + if(ctx->left >= 0) + ctx->left += rval.consumed; /* ?Substracted below! */ + ADVANCE(rval.consumed); + + NEXT_PHASE(ctx); + + ASN_DEBUG("Structure advertised %ld bytes, " + "buffer contains %ld", (long)ctx->left, (long)size); + + /* Fall through */ + case 1: + /* + * PHASE 1. + * From the place where we've left it previously, + * try to decode the next member from the list of + * this structure's elements. + * (ctx->step) stores the member being processed + * between invocations and the microphase {0,1} of parsing + * that member: + * step = (2 * <member_number> + <microphase>). + * Note, however, that the elements in BER may arrive out of + * order, yet DER mandates that they shall arive in the + * canonical order of their tags. So, there is a room + * for optimization. + */ + for(edx = (ctx->step >> 1); edx < specs->elements_count; + ctx->step = (ctx->step & ~1) + 2, + edx = (ctx->step >> 1)) { + void *memb_ptr; /* Pointer to the member */ + void *memb_ptr2; /* Pointer to that pointer */ + ssize_t tag_len; /* Length of TLV's T */ + + if(ctx->step & 1) + goto microphase2; + + /* + * MICROPHASE 1: Synchronize decoding. + */ + + if(ctx->left == 0) + /* + * No more things to decode. + * Exit out of here and check whether all mandatory + * elements have been received (in the next phase). + */ + break; + + /* + * Fetch the T from TLV. + */ + tag_len = ber_fetch_tag(ptr, LEFT, &tlv_tag); + switch(tag_len) { + case 0: if(!SIZE_VIOLATION) RETURN(RC_WMORE); + /* Fall through */ + case -1: RETURN(RC_FAIL); + } + + if(ctx->left < 0 && ((uint8_t *)ptr)[0] == 0) { + if(LEFT < 2) { + if(SIZE_VIOLATION) + RETURN(RC_FAIL); + else + RETURN(RC_WMORE); + } else if(((uint8_t *)ptr)[1] == 0) { + /* + * Found the terminator of the + * indefinite length structure. + * Invoke the generic finalization function. + */ + goto phase3; + } + } + + if(BER_TAGS_EQUAL(tlv_tag, elements[edx].tag)) { + /* + * The elements seem to go in order. + * This is not particularly strange, + * but is not strongly anticipated either. + */ + } else { + asn1_SET_tag2member_t *t2m; + asn1_SET_tag2member_t key; + + key.el_tag = tlv_tag; + t2m = bsearch(&key, specs->tag2el, specs->tag2el_count, + sizeof(specs->tag2el[0]), _t2e_cmp); + if(t2m) { + /* + * Found the element corresponding to the tag. + */ + edx = t2m->el_no; + ctx->step = 2 * edx; + } else if(specs->extensible == 0) { + ASN_DEBUG("Unexpected tag %s " + "in non-extensible SET %s", + ber_tlv_tag_string(tlv_tag), sd->name); + RETURN(RC_FAIL); + } else { + /* Skip this tag */ + ssize_t skip; + + ASN_DEBUG("Skipping unknown tag %s", + ber_tlv_tag_string(tlv_tag)); + + skip = ber_skip_length( + BER_TLV_CONSTRUCTED(ptr), + ptr + tag_len, LEFT - tag_len); + + switch(skip) { + case 0: if(!SIZE_VIOLATION) RETURN(RC_WMORE); + /* Fall through */ + case -1: RETURN(RC_FAIL); + } + + ADVANCE(skip + tag_len); + ctx->step -= 2; + edx--; + continue; /* Try again with the next tag */ + } + } + + /* + * MICROPHASE 2: Invoke the member-specific decoder. + */ + ctx->step |= 1; /* Confirm entering next microphase */ + microphase2: + + /* + * Check for duplications: must not overwrite + * already decoded elements. + */ + if(ASN_SET_ISPRESENT2(st + specs->pres_offset, edx)) { + ASN_DEBUG("Duplicate element %d", edx); + RETURN(RC_FAIL); + } + + /* + * Compute the position of the member inside a structure, + * and also a type of containment (it may be contained + * as pointer or using inline inclusion). + */ + if(elements[edx].optional) { + /* Optional member, hereby, a simple pointer */ + memb_ptr2 = (char *)st + elements[edx].memb_offset; + } else { + /* + * A pointer to a pointer + * holding the start of the structure + */ + memb_ptr = (char *)st + elements[edx].memb_offset; + memb_ptr2 = &memb_ptr; + } + /* + * Invoke the member fetch routine according to member's type + */ + rval = elements[edx].type->ber_decoder( + (void *)elements[edx].type, + memb_ptr2, ptr, LEFT, + elements[edx].tag_mode); + switch(rval.code) { + case RC_OK: + ASN_SET_MKPRESENT(st + specs->pres_offset, edx); + break; + case RC_WMORE: /* More data expected */ + if(!SIZE_VIOLATION) { + ADVANCE(rval.consumed); + RETURN(RC_WMORE); + } + /* Fail through */ + case RC_FAIL: /* Fatal error */ + RETURN(RC_FAIL); + } /* switch(rval) */ + + ADVANCE(rval.consumed); + } /* for(all structure members) */ + + phase3: + ctx->phase = 3; + /* Fall through */ + case 3: + case 4: /* Only 00 is expected */ + ASN_DEBUG("SET %s Leftover: %ld, size = %ld", + sd->name, (long)ctx->left, (long)size); + + /* + * Skip everything until the end of the SET. + */ + while(ctx->left) { + ssize_t tl, ll; + + tl = ber_fetch_tag(ptr, LEFT, &tlv_tag); + switch(tl) { + case 0: if(!SIZE_VIOLATION) RETURN(RC_WMORE); + /* Fall through */ + case -1: RETURN(RC_FAIL); + } + + /* + * If expected <0><0>... + */ + if(ctx->left < 0 + && ((uint8_t *)ptr)[0] == 0) { + if(LEFT < 2) { + if(SIZE_VIOLATION) + RETURN(RC_FAIL); + else + RETURN(RC_WMORE); + } else if(((uint8_t *)ptr)[1] == 0) { + /* + * Correctly finished with <0><0>. + */ + ADVANCE(2); + ctx->left++; + ctx->phase = 4; + continue; + } + } + + if(specs->extensible == 0 || ctx->phase == 4) { + ASN_DEBUG("Unexpected continuation " + "of a non-extensible type %s", + sd->name); + RETURN(RC_FAIL); + } + + ll = ber_skip_length( + BER_TLV_CONSTRUCTED(ptr), + ptr + tl, LEFT - tl); + switch(ll) { + case 0: if(!SIZE_VIOLATION) RETURN(RC_WMORE); + /* Fall through */ + case -1: RETURN(RC_FAIL); + } + + ADVANCE(tl + ll); + } + + ctx->phase = 5; + case 5: + /* + * Check that all mandatory elements are present. + */ + for(edx = 0; edx < specs->elements_count; + edx += (8 * sizeof(specs->_mandatory_elements[0]))) { + unsigned int midx, pres, must; + + midx = edx/(8 * sizeof(specs->_mandatory_elements[0])); + pres = ((unsigned int *)(st+specs->pres_offset))[midx]; + must = ntohl(specs->_mandatory_elements[midx]); + + if((pres & must) == must) { + /* + * Yes, everything seems to be in place. + */ + } else { + ASN_DEBUG("One or more mandatory elements " + "of a SET %s %d (%08x.%08x)=%08x " + "are not present", + sd->name, + midx, + pres, + must, + (~(pres & must) & must) + ); + RETURN(RC_FAIL); + } + } + + NEXT_PHASE(ctx); + } + + RETURN(RC_OK); +} + +/* + * The DER encoder of the SET type. + */ +der_enc_rval_t +SET_encode_der(asn1_TYPE_descriptor_t *sd, + void *ptr, int tag_mode, ber_tlv_tag_t tag, + asn_app_consume_bytes_f *cb, void *app_key) { + asn1_SET_specifics_t *specs = sd->specifics; + size_t computed_size = 0; + der_enc_rval_t my_erval; + int t2m_build_own = (specs->tag2el_count != specs->elements_count); + asn1_SET_tag2member_t *t2m; + int t2m_count; + ssize_t ret; + int edx; + + /* + * Use existing, or build our own tags map. + */ + if(t2m_build_own) { + t2m = alloca(specs->elements_count * sizeof(t2m[0])); + t2m_count = 0; + } else { + /* + * There is no untagged CHOICE in this SET. + * Employ existing table. + */ + t2m = specs->tag2el; + t2m_count = specs->tag2el_count; + } + + /* + * Gather the length of the underlying members sequence. + */ + for(edx = 0; edx < specs->elements_count; edx++) { + asn1_SET_element_t *elm = &specs->elements[edx]; + der_enc_rval_t erval; + void *memb_ptr; + + /* + * Compute the length of the encoding of this member. + */ + if(elm->optional) { + memb_ptr = *(void **)((char *)ptr + elm->memb_offset); + if(!memb_ptr) { + if(t2m_build_own) { + t2m[t2m_count].el_no = edx; + t2m[t2m_count].el_tag = 0; + t2m_count++; + } + continue; + } + } else { + memb_ptr = (void *)((char *)ptr + elm->memb_offset); + } + erval = elm->type->der_encoder(elm->type, memb_ptr, + elm->tag_mode, elm->tag, + 0, 0); + if(erval.encoded == -1) + return erval; + computed_size += erval.encoded; + + /* + * Remember the outmost tag of this member. + */ + if(t2m_build_own) { + t2m[t2m_count].el_no = edx; + t2m[t2m_count].el_tag = asn1_TYPE_outmost_tag( + elm->type, memb_ptr, elm->tag_mode, elm->tag); + t2m_count++; + } else { + /* + * No dynamic sorting is necessary. + */ + } + } + + /* + * Finalize order of the components. + */ + assert(t2m_count == specs->elements_count); + if(t2m_build_own) { + /* + * Sort the underlying members according to their + * canonical tags order. DER encoding mandates it. + */ + qsort(t2m, t2m_count, sizeof(specs->tag2el[0]), _t2e_cmp); + } else { + /* + * Tags are already sorted by the compiler. + */ + } + + /* + * Encode the TLV for the sequence itself. + */ + ret = der_write_tags(sd, computed_size, tag_mode, tag, cb, app_key); + if(ret == -1) { + my_erval.encoded = -1; + my_erval.failed_type = sd; + my_erval.structure_ptr = ptr; + return my_erval; + } + my_erval.encoded = computed_size + ret; + + if(!cb) return my_erval; + + /* + * Encode all members. + */ + for(edx = 0; edx < specs->elements_count; edx++) { + asn1_SET_element_t *elm; + der_enc_rval_t erval; + void *memb_ptr; + + /* Encode according to the tag order */ + elm = &specs->elements[t2m[edx].el_no]; + + if(elm->optional) { + memb_ptr = *(void **)((char *)ptr + elm->memb_offset); + if(!memb_ptr) continue; + } else { + memb_ptr = (void *)((char *)ptr + elm->memb_offset); + } + erval = elm->type->der_encoder(elm->type, memb_ptr, + elm->tag_mode, elm->tag, + cb, app_key); + if(erval.encoded == -1) + return erval; + computed_size -= erval.encoded; + } + + if(computed_size != 0) { + /* + * Encoded size is not equal to the computed size. + */ + my_erval.encoded = -1; + my_erval.failed_type = sd; + my_erval.structure_ptr = ptr; + } + + return my_erval; +} + +int +SET_print(asn1_TYPE_descriptor_t *td, const void *sptr, int ilevel, + asn_app_consume_bytes_f *cb, void *app_key) { + asn1_SET_specifics_t *specs = td->specifics; + int edx; + int ret; + + if(!sptr) return cb("<absent>", 8, app_key); + + /* Dump preamble */ + if(cb(td->name, strlen(td->name), app_key) + || cb(" ::= {\n", 7, app_key)) + return -1; + + for(edx = 0; edx < specs->elements_count; edx++) { + asn1_SET_element_t *elm = &specs->elements[edx]; + const void *memb_ptr; + + if(elm->optional) { + memb_ptr = *(const void * const *)((const char *)sptr + elm->memb_offset); + if(!memb_ptr) continue; + } else { + memb_ptr = (const void *)((const char *)sptr + elm->memb_offset); + } + + /* Indentation */ + for(ret = 0; ret < ilevel; ret++) cb(" ", 1, app_key); + + /* Print the member's name and stuff */ + if(cb(elm->name, strlen(elm->name), app_key) + || cb(": ", 2, app_key)) + return -1; + + /* Print the member itself */ + ret = elm->type->print_struct(elm->type, memb_ptr, ilevel + 4, + cb, app_key); + if(ret) return ret; + + ret = cb("\n", 1, app_key); + if(ret) return ret; + } + + /* Indentation */ + for(ret = 0; ret < ilevel - 4; ret++) cb(" ", 1, app_key); + + return cb("}", 1, app_key); +} + +void +SET_free(asn1_TYPE_descriptor_t *td, void *ptr, int contents_only) { + asn1_SET_specifics_t *specs = td->specifics; + int edx; + + if(!td || !ptr) + return; + + ASN_DEBUG("Freeing %s as SET", td->name); + + for(edx = 0; edx < specs->elements_count; edx++) { + asn1_SET_element_t *elm = &specs->elements[edx]; + void *memb_ptr; + if(elm->optional) { + memb_ptr = *(void **)((char *)ptr + elm->memb_offset); + if(memb_ptr) + elm->type->free_struct(elm->type, memb_ptr, 0); + } else { + memb_ptr = (void *)((char *)ptr + elm->memb_offset); + elm->type->free_struct(elm->type, memb_ptr, 1); + } + } + + if(!contents_only) { + FREEMEM(ptr); + } +} + +int +SET_constraint(asn1_TYPE_descriptor_t *td, const void *sptr, + asn_app_consume_bytes_f *app_errlog, void *app_key) { + asn1_SET_specifics_t *specs = td->specifics; + int edx; + + if(!sptr) { + _ASN_ERRLOG("%s: value not given", td->name); + return -1; + } + + /* + * Iterate over structure members and check their validity. + */ + for(edx = 0; edx < specs->elements_count; edx++) { + asn1_SET_element_t *elm = &specs->elements[edx]; + const void *memb_ptr; + + if(elm->optional) { + memb_ptr = *(const void * const *)((const char *)sptr + elm->memb_offset); + if(!memb_ptr) { + if(ASN_SET_ISPRESENT2( + &(specs->_mandatory_elements), edx)) { + _ASN_ERRLOG( + "%s: mandatory element " + "%s absent", + td->name, elm->name); + return -1; + } + continue; + } + } else { + memb_ptr = (const void *)((const char *)sptr + elm->memb_offset); + } + + return elm->type->check_constraints(elm->type, memb_ptr, + app_errlog, app_key); + } + + return 0; +} diff --git a/skeletons/constr_SET.h b/skeletons/constr_SET.h new file mode 100644 index 00000000..c0ac07f5 --- /dev/null +++ b/skeletons/constr_SET.h @@ -0,0 +1,90 @@ +/*- + * Copyright (c) 2003, 2004 Lev Walkin <vlm@lionet.info>. All rights reserved. + * Redistribution and modifications are permitted subject to BSD license. + */ +#ifndef _CONSTR_SET_H_ +#define _CONSTR_SET_H_ + +#include <constr_TYPE.h> + +/* + * Description of a single element of the SET type. + */ +typedef struct asn1_SET_element_s { + int memb_offset; /* Offset of the element */ + int optional; /* Whether the element is optional */ + ber_tlv_tag_t tag; /* Outmost (most immediate) tag */ + int tag_mode; /* IMPLICIT/no/EXPLICIT tag at current level */ + asn1_TYPE_descriptor_t + *type; /* Member type descriptor */ + char *name; /* ASN.1 identifier of the element */ +} asn1_SET_element_t; + +/* + * Map between the outmost tag of the element and the corresponding + * element's index. + */ +typedef struct asn1_SET_tag2member_s { + ber_tlv_tag_t el_tag; /* Outmost tag of the member */ + int el_no; /* Index of the associated member, base 0 */ +} asn1_SET_tag2member_t; + +typedef struct asn1_SET_specifics_s { + /* + * Target structure description. + */ + int struct_size; /* Size of the target structure. */ + int ctx_offset; /* Offset of the ber_dec_ctx_t member */ + int pres_offset; /* Offset of _presence_map member */ + + /* + * Members of the SET structure. + */ + asn1_SET_element_t *elements; + int elements_count; + + /* + * Tags to members mapping table (sorted). + */ + asn1_SET_tag2member_t *tag2el; + int tag2el_count; + + /* + * Extensions-related stuff. + */ + int extensible; /* Whether SET is extensible */ + unsigned int *_mandatory_elements; /* Bitmask of mandatory ones */ +} asn1_SET_specifics_t; + +/* + * A set specialized functions dealing with the SET type. + */ +asn_constr_check_f SET_constraint; +ber_type_decoder_f SET_decode_ber; +der_type_encoder_f SET_encode_der; +asn_struct_print_f SET_print; +asn_struct_free_f SET_free; + +/*********************** + * Some handy helpers. * + ***********************/ + +/* + * Figure out whether the SET member indicated by PR_x has already been decoded. + * It is very simple bitfield test, despite its visual complexity. + */ +#define ASN_SET_ISPRESENT(set_ptr, PR_x) \ + ASN_SET_ISPRESENT2(&((set_ptr)->_presence_map)) +#define ASN_SET_ISPRESENT2(map_ptr, PR_x) \ + (((unsigned int *)(map_ptr)) \ + [(PR_x) / (8 * sizeof(unsigned int))] \ + & (1 << ((8 * sizeof(unsigned int)) - 1 \ + - ((PR_x) % (8 * sizeof(unsigned int)))))) + +#define ASN_SET_MKPRESENT(map_ptr, PR_x) \ + (((unsigned int *)(map_ptr)) \ + [(PR_x) / (8 * sizeof(unsigned int))] \ + |= (1 << ((8 * sizeof(unsigned int)) - 1 \ + - ((PR_x) % (8 * sizeof(unsigned int)))))) + +#endif /* _CONSTR_SET_H_ */ diff --git a/skeletons/constr_SET_OF.c b/skeletons/constr_SET_OF.c new file mode 100644 index 00000000..9dcf638f --- /dev/null +++ b/skeletons/constr_SET_OF.c @@ -0,0 +1,530 @@ +/*- + * Copyright (c) 2003, 2004 Lev Walkin <vlm@lionet.info>. All rights reserved. + * Redistribution and modifications are permitted subject to BSD license. + */ +#include <constr_SET_OF.h> +#include <asn_SET_OF.h> + +/* + * Number of bytes left for this structure. + * (ctx->left) indicates the number of bytes _transferred_ for the structure. + * (size) contains the number of bytes in the buffer passed. + */ +#define LEFT ((size<ctx->left)?size:ctx->left) + +/* + * If the subprocessor function returns with an indication that it wants + * more data, it may well be a fatal decoding problem, because the + * size is constrained by the <TLV>'s L, even if the buffer size allows + * reading more data. + * For example, consider the buffer containing the following TLVs: + * <T:5><L:1><V> <T:6>... + * The TLV length clearly indicates that one byte is expected in V, but + * if the V processor returns with "want more data" even if the buffer + * contains way more data than the V processor have seen. + */ +#define SIZE_VIOLATION (ctx->left != -1 && ctx->left <= size) + +/* + * This macro "eats" the part of the buffer which is definitely "consumed", + * i.e. was correctly converted into local representation or rightfully skipped. + */ +#define ADVANCE(num_bytes) do { \ + size_t num = num_bytes; \ + ptr += num; \ + size -= num; \ + if(ctx->left >= 0) \ + ctx->left -= num; \ + consumed_myself += num; \ + } while(0) + +/* + * Switch to the next phase of parsing. + */ +#define NEXT_PHASE(ctx) do { \ + ctx->phase++; \ + ctx->step = 0; \ + } while(0) +#define PHASE_OUT(ctx) do { ctx->phase = 10; } while(0) + +/* + * Return a standardized complex structure. + */ +#define RETURN(_code) do { \ + rval.code = _code; \ + rval.consumed = consumed_myself;\ + return rval; \ + } while(0) + +/* + * The decoder of the SET OF type. + */ +ber_dec_rval_t +SET_OF_decode_ber(asn1_TYPE_descriptor_t *sd, + void **struct_ptr, void *ptr, size_t size, int tag_mode) { + /* + * Bring closer parts of structure description. + */ + asn1_SET_OF_specifics_t *specs = sd->specifics; + asn1_SET_OF_element_t *element = specs->element; + + /* + * Parts of the structure being constructed. + */ + void *st = *struct_ptr; /* Target structure. */ + ber_dec_ctx_t *ctx; /* Decoder context */ + + ber_tlv_tag_t tlv_tag; /* T from TLV */ + //ber_tlv_len_t tlv_len; /* L from TLV */ + ber_dec_rval_t rval; /* Return code from subparsers */ + + ssize_t consumed_myself = 0; /* Consumed bytes from ptr */ + + ASN_DEBUG("Decoding %s as SET OF", sd->name); + + /* + * Create the target structure if it is not present already. + */ + if(st == 0) { + st = *struct_ptr = CALLOC(1, specs->struct_size); + if(st == 0) { + RETURN(RC_FAIL); + } + } + + /* + * Restore parsing context. + */ + ctx = (st + specs->ctx_offset); + + /* + * Start to parse where left previously + */ + switch(ctx->phase) { + case 0: + /* + * PHASE 0. + * Check that the set of tags associated with given structure + * perfectly fits our expectations. + */ + + rval = ber_check_tags(sd, ctx, ptr, size, + tag_mode, &ctx->left, 0); + if(rval.code != RC_OK) { + ASN_DEBUG("%s tagging check failed: %d", + sd->name, rval.code); + consumed_myself += rval.consumed; + RETURN(rval.code); + } + + if(ctx->left >= 0) + ctx->left += rval.consumed; /* ?Substracted below! */ + ADVANCE(rval.consumed); + + ASN_DEBUG("Structure consumes %ld bytes, " + "buffer %ld", (long)ctx->left, (long)size); + + NEXT_PHASE(ctx); + /* Fall through */ + case 1: + /* + * PHASE 1. + * From the place where we've left it previously, + * try to decode the next item. + */ + for(;; ctx->step = 0) { + ssize_t tag_len; /* Length of TLV's T */ + + if(ctx->step & 1) + goto microphase2; + + /* + * MICROPHASE 1: Synchronize decoding. + */ + + if(ctx->left == 0) { + ASN_DEBUG("End of SET OF %s", sd->name); + /* + * No more things to decode. + * Exit out of here. + */ + PHASE_OUT(ctx); + RETURN(RC_OK); + } + + /* + * Fetch the T from TLV. + */ + tag_len = ber_fetch_tag(ptr, LEFT, &tlv_tag); + switch(tag_len) { + case 0: if(!SIZE_VIOLATION) RETURN(RC_WMORE); + /* Fall through */ + case -1: RETURN(RC_FAIL); + } + + if(ctx->left < 0 && ((uint8_t *)ptr)[0] == 0) { + if(LEFT < 2) { + if(SIZE_VIOLATION) + RETURN(RC_FAIL); + else + RETURN(RC_WMORE); + } else if(((uint8_t *)ptr)[1] == 0) { + /* + * Found the terminator of the + * indefinite length structure. + */ + break; + } + } + + /* Outmost tag may be unknown and cannot be fetched/compared */ + if(element->tag != -1) { + if(BER_TAGS_EQUAL(tlv_tag, element->tag)) { + /* + * The new list member of expected type has arrived. + */ + } else { + ASN_DEBUG("Unexpected tag %s fixed SET OF %s", + ber_tlv_tag_string(tlv_tag), sd->name); + ASN_DEBUG("%s SET OF has tag %s", + sd->name, ber_tlv_tag_string(element->tag)); + RETURN(RC_FAIL); + } + } + + /* + * MICROPHASE 2: Invoke the member-specific decoder. + */ + ctx->step |= 1; /* Confirm entering next microphase */ + microphase2: + + /* + * Invoke the member fetch routine according to member's type + */ + rval = element->type->ber_decoder( + (void *)element->type, + &ctx->ptr, ptr, LEFT, 0); + ASN_DEBUG("In %s SET OF %s code %d consumed %d", + sd->name, element->type->name, + rval.code, (int)rval.consumed); + switch(rval.code) { + case RC_OK: + { + A_SET_OF(void) *list = st; + if(ASN_SET_ADD(list, ctx->ptr) != 0) + RETURN(RC_FAIL); + else + ctx->ptr = 0; + } + break; + case RC_WMORE: /* More data expected */ + if(!SIZE_VIOLATION) { + ADVANCE(rval.consumed); + RETURN(RC_WMORE); + } + /* Fall through */ + case RC_FAIL: /* Fatal error */ + RETURN(RC_FAIL); + } /* switch(rval) */ + + ADVANCE(rval.consumed); + } /* for(all list members) */ + + NEXT_PHASE(ctx); + case 2: + /* + * Read in all "end of content" TLVs. + */ + while(ctx->left < 0) { + if(LEFT < 2) { + if(LEFT > 0 && ((char *)ptr)[0] != 0) { + /* Unexpected tag */ + RETURN(RC_FAIL); + } else { + RETURN(RC_WMORE); + } + } + if(((char *)ptr)[0] == 0 + && ((char *)ptr)[1] == 0) { + ADVANCE(2); + ctx->left++; + } else { + RETURN(RC_FAIL); + } + } + + PHASE_OUT(ctx); + } + + RETURN(RC_OK); +} + +/* + * Internally visible buffer holding a single encoded element. + */ +struct _el_buffer { + uint8_t *buf; + size_t length; + size_t size; +}; +/* Append bytes to the above structure */ +static int _el_addbytes(const void *buffer, size_t size, void *el_buf_ptr) { + struct _el_buffer *el_buf = el_buf_ptr; + + if(el_buf->length + size > el_buf->size) + return -1; + + memcpy(el_buf->buf + el_buf->length, buffer, size); + + el_buf->length += size; + return 0; +} +static int _el_buf_cmp(const void *ap, const void *bp) { + const struct _el_buffer *a = ap; + const struct _el_buffer *b = bp; + int ret; + size_t common_len; + + if(a->length < b->length) + common_len = a->length; + else + common_len = b->length; + + ret = memcmp(a->buf, b->buf, common_len); + if(ret == 0) { + if(a->length < b->length) + ret = -1; + else if(a->length > b->length) + ret = 1; + } + + return ret; +} + +/* + * The DER encoder of the SET OF type. + */ +der_enc_rval_t +SET_OF_encode_der(asn1_TYPE_descriptor_t *sd, void *ptr, + int tag_mode, ber_tlv_tag_t tag, + asn_app_consume_bytes_f *cb, void *app_key) { + asn1_SET_OF_specifics_t *specs = sd->specifics; + asn1_SET_OF_element_t *elm = specs->element; + asn1_TYPE_descriptor_t *elm_type = elm->type; + der_type_encoder_f *der_encoder = elm_type->der_encoder; + A_SET_OF(void) *list = ptr; + size_t computed_size = 0; + ssize_t encoding_size = 0; + struct _el_buffer *encoded_els; + size_t max_encoded_len = 1; + der_enc_rval_t erval; + int ret; + int edx; + + ASN_DEBUG("Estimating size for SET OF %s", sd->name); + + /* + * Gather the length of the underlying members sequence. + */ + for(edx = 0; edx < list->count; edx++) { + void *memb_ptr = list->array[edx]; + erval = der_encoder(elm_type, memb_ptr, 0, elm->tag, 0, 0); + if(erval.encoded == -1) + return erval; + computed_size += erval.encoded; + + /* Compute maximum encoding's size */ + if(max_encoded_len < erval.encoded) + max_encoded_len = erval.encoded; + } + + /* + * Encode the TLV for the sequence itself. + */ + encoding_size = der_write_tags(sd, computed_size, tag_mode, tag, + cb, app_key); + if(encoding_size == -1) { + erval.encoded = -1; + erval.failed_type = sd; + erval.structure_ptr = ptr; + return erval; + } + computed_size += encoding_size; + + if(!cb) { + erval.encoded = computed_size; + return erval; + } + + /* + * DER mandates dynamic sorting of the SET OF elements + * according to their encodings. Build an array of the + * encoded elements. + */ + encoded_els = MALLOC(list->count * sizeof(encoded_els[0])); + if(encoded_els == NULL) { + erval.encoded = -1; + erval.failed_type = sd; + erval.structure_ptr = ptr; + return erval; + } + + ASN_DEBUG("Encoding members of %s SET OF", sd->name); + + /* + * Encode all members. + */ + for(edx = 0; edx < list->count; edx++) { + void *memb_ptr = list->array[edx]; + struct _el_buffer *encoded_el = &encoded_els[edx]; + + /* + * Prepare space for encoding. + */ + encoded_el->buf = MALLOC(max_encoded_len); + if(encoded_el->buf) { + encoded_el->length = 0; + encoded_el->size = max_encoded_len; + } else { + for(edx--; edx >= 0; edx--) + FREEMEM(encoded_els[edx].buf); + FREEMEM(encoded_els); + erval.encoded = -1; + erval.failed_type = sd; + erval.structure_ptr = ptr; + return erval; + } + + /* + * Encode the member into the prepared space. + */ + erval = der_encoder(elm_type, memb_ptr, 0, elm->tag, + _el_addbytes, encoded_el); + if(erval.encoded == -1) { + for(; edx >= 0; edx--) + FREEMEM(encoded_els[edx].buf); + FREEMEM(encoded_els); + return erval; + } + encoding_size += erval.encoded; + } + + /* + * Sort the encoded elements according to their encoding. + */ + qsort(encoded_els, list->count, sizeof(encoded_els[0]), _el_buf_cmp); + + /* + * Report encoded elements to the application. + * Dispose of temporary sorted members table. + */ + ret = 0; + for(edx = 0; edx < list->count; edx++) { + struct _el_buffer *encoded_el = &encoded_els[edx]; + /* Report encoded chunks to the application */ + if(ret == 0 + && cb(encoded_el->buf, encoded_el->length, app_key) == -1) + ret = -1; + FREEMEM(encoded_el->buf); + } + FREEMEM(encoded_els); + + if(ret || computed_size != encoding_size) { + /* + * Standard callback failed, or + * encoded size is not equal to the computed size. + */ + erval.encoded = -1; + erval.failed_type = sd; + erval.structure_ptr = ptr; + } else { + erval.encoded = computed_size; + } + + return erval; +} + +int +SET_OF_print(asn1_TYPE_descriptor_t *td, const void *sptr, int ilevel, + asn_app_consume_bytes_f *cb, void *app_key) { + asn1_SET_OF_specifics_t *specs = td->specifics; + asn1_SET_OF_element_t *element = specs->element; + const A_SET_OF(void) *list = sptr; + int ret; + int i; + + if(!sptr) return cb("<absent>", 8, app_key); + + /* Dump preamble */ + if(cb(td->name, strlen(td->name), app_key) + || cb(" ::= {\n", 7, app_key)) + return -1; + + for(i = 0; i < list->count; i++) { + const void *memb_ptr = list->array[i]; + if(!memb_ptr) continue; + + /* Indentation */ + for(ret = 0; ret < ilevel; ret++) cb(" ", 1, app_key); + + ret = element->type->print_struct(element->type, memb_ptr, + ilevel + 4, cb, app_key); + if(ret) return ret; + + ret = cb("\n", 1, app_key); + if(ret) return ret; + } + + /* Indentation */ + for(ret = 0; ret < ilevel - 4; ret++) cb(" ", 1, app_key); + + return cb("}", 1, app_key); +} + +void +SET_OF_free(asn1_TYPE_descriptor_t *td, void *ptr, int contents_only) { + if(td && ptr) { + asn1_SET_OF_specifics_t *specs = td->specifics; + asn1_SET_OF_element_t *element = specs->element; + A_SET_OF(void) *list = ptr; + int i; + + /* + * Could not use set_of_empty() because of (*free) + * incompatibility. + */ + for(i = 0; i < list->count; i++) { + void *memb_ptr = list->array[i]; + if(memb_ptr) + element->type->free_struct(element->type, memb_ptr, 0); + } + list->count = 0; /* Just in case */ + + if(!contents_only) { + FREEMEM(ptr); + } + } +} + +int +SET_OF_constraint(asn1_TYPE_descriptor_t *td, const void *sptr, + asn_app_consume_bytes_f *app_errlog, void *app_key) { + asn1_SET_OF_specifics_t *specs = td->specifics; + asn1_SET_OF_element_t *element = specs->element; + const A_SET_OF(void) *list = sptr; + int i; + + if(!sptr) { + _ASN_ERRLOG("%s: value not given", td->name); + return -1; + } + + for(i = 0; i < list->count; i++) { + const void *memb_ptr = list->array[i]; + if(!memb_ptr) continue; + return element->type->check_constraints(element->type, memb_ptr, + app_errlog, app_key); + } + + return 0; +} diff --git a/skeletons/constr_SET_OF.h b/skeletons/constr_SET_OF.h new file mode 100644 index 00000000..814774dc --- /dev/null +++ b/skeletons/constr_SET_OF.h @@ -0,0 +1,38 @@ +/*- + * Copyright (c) 2003 Lev Walkin <vlm@lionet.info>. All rights reserved. + * Redistribution and modifications are permitted subject to BSD license. + */ +#ifndef _CONSTR_SET_OF_H_ +#define _CONSTR_SET_OF_H_ + +#include <constr_TYPE.h> + +typedef struct asn1_SET_OF_element_s { + ber_tlv_tag_t tag; /* Outmost (most immediate) tag */ + asn1_TYPE_descriptor_t + *type; /* Member type descriptor */ +} asn1_SET_OF_element_t; + +typedef struct asn1_SET_OF_specifics_s { + /* + * Target structure description. + */ + int struct_size; /* Size of the target structure. */ + int ctx_offset; /* Offset of the ber_dec_ctx_t member */ + + /* + * Members of the SET OF list. + */ + asn1_SET_OF_element_t *element; +} asn1_SET_OF_specifics_t; + +/* + * A set specialized functions dealing with the SET OF type. + */ +asn_constr_check_f SET_OF_constraint; +ber_type_decoder_f SET_OF_decode_ber; +der_type_encoder_f SET_OF_encode_der; +asn_struct_print_f SET_OF_print; +asn_struct_free_f SET_OF_free; + +#endif /* _CONSTR_SET_OF_H_ */ diff --git a/skeletons/constr_TYPE.c b/skeletons/constr_TYPE.c new file mode 100644 index 00000000..9ab1bdc5 --- /dev/null +++ b/skeletons/constr_TYPE.c @@ -0,0 +1,58 @@ +/*- + * Copyright (c) 2003, 2004 Lev Walkin <vlm@lionet.info>. All rights reserved. + * Redistribution and modifications are permitted subject to BSD license. + */ +#include <constr_TYPE.h> +#include <errno.h> + +static asn_app_consume_bytes_f _print2fp; + +/* + * Return the outmost tag of the type. + */ +ber_tlv_tag_t +asn1_TYPE_outmost_tag(asn1_TYPE_descriptor_t *type_descriptor, + const void *struct_ptr, int tag_mode, ber_tlv_tag_t tag) { + + if(tag_mode) + return tag; + + if(type_descriptor->tags_count) + return type_descriptor->tags[0]; + + return type_descriptor->outmost_tag(type_descriptor, struct_ptr, 0, 0); +} + +/* + * Print the target language's structure in human readable form. + */ +int +asn_fprint(FILE *stream, asn1_TYPE_descriptor_t *td, const void *struct_ptr) { + if(!stream) stream = stdout; + if(!td || !struct_ptr) { + errno = EINVAL; + return -1; + } + + /* Invoke type-specific printer */ + if(td->print_struct(td, struct_ptr, 4, _print2fp, stream)) + return -1; + + /* Terminate the output */ + if(_print2fp("\n", 1, stream)) + return -1; + + return fflush(stream); +} + +/* Dump the data into the specified stdio stream */ +static int +_print2fp(const void *buffer, size_t size, void *app_key) { + FILE *stream = app_key; + + if(fwrite(buffer, 1, size, stream) != size) + return -1; + + return 0; +} + diff --git a/skeletons/constr_TYPE.h b/skeletons/constr_TYPE.h new file mode 100644 index 00000000..c7cfb22b --- /dev/null +++ b/skeletons/constr_TYPE.h @@ -0,0 +1,98 @@ +/*- + * Copyright (c) 2003, 2004 Lev Walkin <vlm@lionet.info>. All rights reserved. + * Redistribution and modifications are permitted subject to BSD license. + */ +#ifndef _CONSTR_TYPE_H_ +#define _CONSTR_TYPE_H_ + +#include <asn_types.h> /* System-dependent types */ +#include <ber_tlv_length.h> +#include <ber_tlv_tag.h> +#include <ber_decoder.h> +#include <der_encoder.h> +#include <constraints.h> + +struct asn1_TYPE_descriptor_s; /* Forward declaration */ + +/* + * Free the structure according to its specification. + * If (free_contents_only) is set, the wrapper structure itself (struct_ptr) + * will not be freed. (It may be useful in case the structure is allocated + * statically or arranged on the stack, yet its elements are allocated + * dynamically.) + */ +typedef void (asn_struct_free_f)( + struct asn1_TYPE_descriptor_s *type_descriptor, + void *struct_ptr, int free_contents_only); + +/* + * Print the structure according to its specification. + */ +typedef int (asn_struct_print_f)( + struct asn1_TYPE_descriptor_s *type_descriptor, + const void *struct_ptr, + int level, /* Indentation level */ + asn_app_consume_bytes_f *callback, void *app_key); + +/* + * Return the outmost tag of the type. + * If the type is untagged CHOICE, the dynamic operation is performed. + * NOTE: This function pointer type is only useful internally. + * Do not use it in your application. + */ +typedef ber_tlv_tag_t (asn_outmost_tag_f)( + struct asn1_TYPE_descriptor_s *type_descriptor, + const void *struct_ptr, int tag_mode, ber_tlv_tag_t tag); +/* The instance of the above function type */ +asn_outmost_tag_f asn1_TYPE_outmost_tag; + + +/* + * The definitive description of the destination language's structure. + */ +typedef struct asn1_TYPE_descriptor_s { + char *name; /* A name of the ASN.1 type */ + + /* + * Generalized functions for dealing with the specific type. + * May be directly invoked by applications. + */ + asn_constr_check_f *check_constraints; /* Constraints validator */ + ber_type_decoder_f *ber_decoder; /* Free-form BER decoder */ + der_type_encoder_f *der_encoder; /* Canonical DER encoder */ + asn_struct_print_f *print_struct; /* Human readable output */ + asn_struct_free_f *free_struct; /* Free the structure */ + + /* + * Functions used internally. Should not be used by applications. + */ + asn_outmost_tag_f *outmost_tag; /* <optional, internal> */ + + /* + * Tags that are expected, with some of their vital properties. + */ + ber_tlv_tag_t *tags; /* At least one tag must be specified */ + int tags_count; /* Number of tags which are expected */ + int tags_impl_skip; /* Tags to skip in implicit mode */ + int last_tag_form; /* Acceptable form of the tag (prim, constr) */ + + /* + * Additional information describing the type, used by appropriate + * functions above. + */ + void *specifics; +} asn1_TYPE_descriptor_t; + +/* + * This function is a wrapper around (td)->print_struct, which prints out + * the contents of the target language's structure (struct_ptr) into the + * file pointer (stream) in human readable form. + * RETURN VALUES: + * 0: The structure is printed. + * -1: Problem dumping the structure. + */ +int asn_fprint(FILE *stream, /* Destination stream descriptor */ + asn1_TYPE_descriptor_t *td, /* ASN.1 type descriptor */ + const void *struct_ptr); /* Structure to be printed */ + +#endif /* _CONSTR_TYPE_H_ */ diff --git a/skeletons/constraints.c b/skeletons/constraints.c new file mode 100644 index 00000000..13a1b402 --- /dev/null +++ b/skeletons/constraints.c @@ -0,0 +1,111 @@ +#include <constraints.h> +#include <constr_TYPE.h> + +int +asn_generic_no_constraint(asn1_TYPE_descriptor_t *type_descriptor, + const void *struct_ptr, asn_app_consume_bytes_f *cb, void *key) { + /* Nothing to check */ + return 0; +} + +int +asn_generic_unknown_constraint(asn1_TYPE_descriptor_t *type_descriptor, + const void *struct_ptr, asn_app_consume_bytes_f *cb, void *key) { + /* Unknown how to check */ + return 0; +} + +struct __fill_errbuf_arg { + char *errbuf; + size_t errlen; + size_t erroff; +}; + +static int +__fill_errbuf(const void *buffer, size_t size, void *app_key) { + struct __fill_errbuf_arg *arg = app_key; + size_t avail = arg->errlen - arg->erroff; + + if(avail > size) + avail = size + 1; + + switch(avail) { + default: + memcpy(arg->errbuf + arg->erroff, buffer, avail - 1); + arg->erroff += avail - 1; + case 1: + arg->errbuf[arg->erroff] = '\0'; + case 0: + return 0; + } + +} + +int +asn_check_constraints(asn1_TYPE_descriptor_t *type_descriptor, + const void *struct_ptr, char *errbuf, size_t *errlen) { + + if(errlen) { + struct __fill_errbuf_arg arg; + int ret; + + arg.errbuf = errbuf; + arg.errlen = *errlen; + arg.erroff = 0; + + ret = type_descriptor->check_constraints(type_descriptor, + struct_ptr, __fill_errbuf, &arg); + + if(ret == -1) + *errlen = arg.erroff; + + return ret; + } else { + return type_descriptor->check_constraints(type_descriptor, + struct_ptr, 0, 0); + } +} + +void +_asn_i_log_error(asn_app_consume_bytes_f *cb, void *key, const char *fmt, ...) { + char buf[64]; + char *p; + va_list ap; + ssize_t ret; + size_t len; + + va_start(ap, fmt); + ret = vsnprintf(buf, sizeof(buf), fmt, ap); + va_end(ap); + if(ret < 0) { + /* + * The libc on this system is broken. + */ + ret = sizeof("<broken vsnprintf>") - 1; + memcpy(buf, "<broken vsnprintf>", ret + 1); + /* Fall through */ + } + + if(ret < sizeof(buf)) { + cb(buf, ret, key); + return; + } + + /* + * More space required to hold the message. + */ + len = ret + 1; + p = alloca(len); + if(!p) return; /* Can't be though. */ + + + va_start(ap, fmt); + ret = vsnprintf(buf, len, fmt, ap); + va_end(ap); + if(ret < 0 || ret >= len) { + ret = sizeof("<broken vsnprintf>") - 1; + memcpy(buf, "<broken vsnprintf>", ret + 1); + } + + cb(buf, ret, key); +} diff --git a/skeletons/constraints.h b/skeletons/constraints.h new file mode 100644 index 00000000..255b6ae2 --- /dev/null +++ b/skeletons/constraints.h @@ -0,0 +1,57 @@ +/*- + * Copyright (c) 2004 Lev Walkin <vlm@lionet.info>. All rights reserved. + * Redistribution and modifications are permitted subject to BSD license. + */ +#ifndef _ASN1_CONSTRAINTS_VALIDATOR_H_ +#define _ASN1_CONSTRAINTS_VALIDATOR_H_ + +#include <asn_types.h> /* System-dependent types */ + +struct asn1_TYPE_descriptor_s; /* Forward declaration */ + +/* + * Validate the structure according to the ASN.1 constraints. + * If errbuf and errlen are given, they shall be pointing to the appropriate + * buffer space and its length before calling this function. Alternatively, + * they could be passed as NULL's. If constraints validation fails, + * errlen will contain the actual number of bytes taken from the errbuf + * to encode an error message (properly 0-terminated). + */ +int +asn_check_constraints(struct asn1_TYPE_descriptor_s *type_descriptor, + const void *struct_ptr, /* Target language's structure */ + char *errbuf, /* Returned error description */ + size_t *errlen /* Length of the error description */ + ); + +/* + * Generic type for constraint checking callback, + * associated with every type descriptor. + */ +typedef int (asn_constr_check_f)( + struct asn1_TYPE_descriptor_s *type_descriptor, + const void *struct_ptr, + asn_app_consume_bytes_f *optional_app_errlog, /* Log the error */ + void *optional_app_key /* Opaque key passed to app_errlog */ + ); + +/******************************* + * INTERNALLY USEFUL FUNCTIONS * + *******************************/ + +asn_constr_check_f asn_generic_no_constraint; /* No constraint whatsoever */ +asn_constr_check_f asn_generic_unknown_constraint; /* Not fully supported */ + +/* + * Invoke the callback with a complete error message. + */ +#define _ASN_ERRLOG(fmt, args...) do { \ + if(app_errlog) \ + _asn_i_log_error(app_errlog, \ + app_key, fmt, ##args); \ + break; \ + } while(0); +void _asn_i_log_error(asn_app_consume_bytes_f *, void *key, + const char *fmt, ...) __attribute__ ((format(printf, 3, 4))); + +#endif /* _ASN1_CONSTRAINTS_VALIDATOR_H_ */ diff --git a/skeletons/der_encoder.c b/skeletons/der_encoder.c new file mode 100644 index 00000000..f275f9c1 --- /dev/null +++ b/skeletons/der_encoder.c @@ -0,0 +1,143 @@ +/*- + * Copyright (c) 2003, 2004 Lev Walkin <vlm@lionet.info>. All rights reserved. + * Redistribution and modifications are permitted subject to BSD license. + */ +#include <constr_TYPE.h> +#include <assert.h> + +static ssize_t der_write_TL(ber_tlv_tag_t tag, ber_tlv_len_t len, + asn_app_consume_bytes_f *cb, void *app_key, int constructed); + +/* + * The DER encoder of any type. + */ +der_enc_rval_t +der_encode(asn1_TYPE_descriptor_t *type_descriptor, void *struct_ptr, + asn_app_consume_bytes_f *consume_bytes, void *app_key) { + + ASN_DEBUG("DER encoder invoked for %s", + type_descriptor->name); + + /* + * Invoke type-specific encoder. + */ + return type_descriptor->der_encoder(type_descriptor, + struct_ptr, /* Pointer to the destination structure */ + 0, 0, + consume_bytes, app_key); +} + +/* + * Write out leading TL[v] sequence according to the type definition. + */ +ssize_t +der_write_tags(asn1_TYPE_descriptor_t *sd, + size_t struct_length, + int tag_mode, + ber_tlv_tag_t tag, /* EXPLICIT or IMPLICIT tag */ + asn_app_consume_bytes_f *cb, + void *app_key) { + ber_tlv_tag_t *tags; /* Copy of tags stream */ + int tags_count; /* Number of tags */ + size_t overall_length; + ssize_t *lens; + int i; + + if(tag_mode) { + /* + * Instead of doing shaman dance like we do in ber_check_tags(), + * allocate a small array on the stack + * and initialize it appropriately. + */ + tags = alloca((sd->tags_count + (tag_mode?1:0)) + * sizeof(ber_tlv_tag_t)); + if(tags == NULL) return -1; /* Impossible on i386 */ + tags_count = sd->tags_count + + 1 /* EXPLICIT or IMPLICIT tag is given */ + - ((tag_mode==-1)?sd->tags_impl_skip:0); + /* Copy tags over */ + tags[0] = tag; + for(i = 1; i < tags_count; i++) + tags[i] = sd->tags[i - 1 + sd->tags_impl_skip]; + } else { + tags = sd->tags; + tags_count = sd->tags_count; + } + + /* No tags to write */ + if(tags_count == 0) + return 0; + + lens = alloca(tags_count * sizeof(lens[0])); + if(lens == NULL) return -1; + + /* + * Array of tags is initialized. + * Now, compute the size of the TLV pairs, from right to left. + */ + overall_length = struct_length; + for(i = tags_count - 1; i >= 0; --i) { + lens[i] = der_write_TL(tags[i], overall_length, 0, 0, 0); + if(lens[i] == -1) return -1; + overall_length += lens[i]; + lens[i] = overall_length - lens[i]; + } + + if(!cb) return overall_length - struct_length; + + ASN_DEBUG("%s %s TL sequence (%d elements)", + cb?"Encoding":"Estimating", sd->name, tags_count); + + /* + * Encode the TL sequence for real. + */ + for(i = 0; i < tags_count; i++) { + ssize_t len; + int _constr; + + /* If this one happens to be constructed, do it. */ + if(i < (tags_count - 1) || sd->last_tag_form == 1) + _constr = 1; + else _constr = 0; + + len = der_write_TL(tags[i], lens[i], cb, app_key, _constr); + if(len == -1) return -1; + } + + return overall_length - struct_length; +} + +static ssize_t +der_write_TL(ber_tlv_tag_t tag, ber_tlv_len_t len, + asn_app_consume_bytes_f *cb, void *app_key, + int constructed) { + uint8_t buf[32]; + size_t size = 0; + int buf_size = cb?sizeof(buf):0; + ssize_t tmp; + + /* Serialize tag (T from TLV) into possibly zero-length buffer */ + tmp = der_tlv_tag_serialize(tag, buf, buf_size); + if(tmp == -1 || tmp > sizeof(buf)) return -1; + size += tmp; + + /* Serialize length (L from TLV) into possibly zero-length buffer */ + tmp = der_tlv_length_serialize(len, buf+size, buf_size?buf_size-size:0); + if(tmp == -1) return -1; + size += tmp; + + if(size > sizeof(buf)) + return -1; + + /* + * If callback is specified, invoke it, and check its return value. + */ + if(cb) { + if(constructed) *buf |= 0x20; + if(cb(buf, size, app_key) == -1) { + return -1; + } + } + + return size; +} diff --git a/skeletons/der_encoder.h b/skeletons/der_encoder.h new file mode 100644 index 00000000..052d9f4c --- /dev/null +++ b/skeletons/der_encoder.h @@ -0,0 +1,73 @@ +/*- + * Copyright (c) 2003, 2004 Lev Walkin <vlm@lionet.info>. All rights reserved. + * Redistribution and modifications are permitted subject to BSD license. + */ +#ifndef _DER_ENCODER_H_ +#define _DER_ENCODER_H_ + +#include <constr_TYPE.h> + +struct asn1_TYPE_descriptor_s; /* Forward declaration */ + +/* + * Type of the return value of the der_encode function. + */ +typedef struct der_enc_rval_s { + /* + * Number of bytes encoded. + * -1 indicates failure to encode the structure. + * In this case, the members below this one are meaningful. + */ + ssize_t encoded; + + /* + * Members meaningful when (encoded == -1), for post-mortem analysis. + */ + + /* Type which cannot be encoded */ + struct asn1_TYPE_descriptor_s *failed_type; + + /* Pointer to the structure of that type */ + void *structure_ptr; +} der_enc_rval_t; + + +/* + * The DER encoder of any type. May be invoked by the application. + */ +der_enc_rval_t der_encode(struct asn1_TYPE_descriptor_s *type_descriptor, + void *struct_ptr, /* Structure to be encoded */ + asn_app_consume_bytes_f *consume_bytes_cb, + void *app_key /* Arbitrary callback argument */ + ); + +/* + * Type of the generic DER encoder. + */ +typedef der_enc_rval_t (der_type_encoder_f)( + struct asn1_TYPE_descriptor_s *type_descriptor, + void *struct_ptr, /* Structure to be encoded */ + int tag_mode, /* {-1,0,1}: IMPLICIT, no, EXPLICIT */ + ber_tlv_tag_t tag, + asn_app_consume_bytes_f *consume_bytes_cb, /* Callback */ + void *app_key /* Arbitrary callback argument */ + ); + + +/******************************* + * INTERNALLY USEFUL FUNCTIONS * + *******************************/ + +/* + * Write out leading TL[v] sequence according to the type definition. + */ +ssize_t der_write_tags( + struct asn1_TYPE_descriptor_s *type_descriptor, + size_t struct_length, + int tag_mode, /* {-1,0,1}: IMPLICIT, no, EXPLICIT */ + ber_tlv_tag_t tag, + asn_app_consume_bytes_f *consume_bytes_cb, + void *app_key + ); + +#endif /* _DER_ENCODER_H_ */ diff --git a/skeletons/tests/Makefile.am b/skeletons/tests/Makefile.am new file mode 100644 index 00000000..e620ade6 --- /dev/null +++ b/skeletons/tests/Makefile.am @@ -0,0 +1,9 @@ +AM_CPPFLAGS = -I${top_srcdir}/skeletons + +check_PROGRAMS = \ + check-GeneralizedTime \ + check-UTCTime \ + check-INTEGER \ + check-OIDs + +TESTS = ${check_PROGRAMS} diff --git a/skeletons/tests/Makefile.in b/skeletons/tests/Makefile.in new file mode 100644 index 00000000..779f93a5 --- /dev/null +++ b/skeletons/tests/Makefile.in @@ -0,0 +1,411 @@ +# Makefile.in generated automatically by automake 1.5 from Makefile.am. + +# Copyright 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001 +# Free Software Foundation, Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +SHELL = @SHELL@ + +srcdir = @srcdir@ +top_srcdir = @top_srcdir@ +VPATH = @srcdir@ +prefix = @prefix@ +exec_prefix = @exec_prefix@ + +bindir = @bindir@ +sbindir = @sbindir@ +libexecdir = @libexecdir@ +datadir = @datadir@ +sysconfdir = @sysconfdir@ +sharedstatedir = @sharedstatedir@ +localstatedir = @localstatedir@ +libdir = @libdir@ +infodir = @infodir@ +mandir = @mandir@ +includedir = @includedir@ +oldincludedir = /usr/include +pkgdatadir = $(datadir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +top_builddir = ../.. + +ACLOCAL = @ACLOCAL@ +AUTOCONF = @AUTOCONF@ +AUTOMAKE = @AUTOMAKE@ +AUTOHEADER = @AUTOHEADER@ + +INSTALL = @INSTALL@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_HEADER = $(INSTALL_DATA) +transform = @program_transform_name@ +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_alias = @build_alias@ +build_triplet = @build@ +host_alias = @host_alias@ +host_triplet = @host@ +target_alias = @target_alias@ +target_triplet = @target@ +ADD_CFLAGS = @ADD_CFLAGS@ +AMTAR = @AMTAR@ +AR = @AR@ +AS = @AS@ +AWK = @AWK@ +CC = @CC@ +CONFIGURE_DEPENDS = @CONFIGURE_DEPENDS@ +CPP = @CPP@ +DEPDIR = @DEPDIR@ +DLLTOOL = @DLLTOOL@ +EXEEXT = @EXEEXT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +LEX = @LEX@ +LIBTOOL = @LIBTOOL@ +LN_S = @LN_S@ +MAINT = @MAINT@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +PACKAGE = @PACKAGE@ +PATH = @PATH@ +RANLIB = @RANLIB@ +VERSION = @VERSION@ +YACC = @YACC@ +am__include = @am__include@ +am__quote = @am__quote@ +install_sh = @install_sh@ + +AM_CPPFLAGS = -I${top_srcdir}/skeletons + +check_PROGRAMS = \ + check-GeneralizedTime \ + check-UTCTime \ + check-INTEGER \ + check-OIDs + + +TESTS = ${check_PROGRAMS} +subdir = skeletons/tests +mkinstalldirs = $(SHELL) $(top_srcdir)/mkinstalldirs +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +check_PROGRAMS = check-GeneralizedTime$(EXEEXT) check-UTCTime$(EXEEXT) \ + check-INTEGER$(EXEEXT) check-OIDs$(EXEEXT) +check_GeneralizedTime_SOURCES = check-GeneralizedTime.c +check_GeneralizedTime_OBJECTS = check-GeneralizedTime.$(OBJEXT) +check_GeneralizedTime_LDADD = $(LDADD) +check_GeneralizedTime_DEPENDENCIES = +check_GeneralizedTime_LDFLAGS = +check_INTEGER_SOURCES = check-INTEGER.c +check_INTEGER_OBJECTS = check-INTEGER.$(OBJEXT) +check_INTEGER_LDADD = $(LDADD) +check_INTEGER_DEPENDENCIES = +check_INTEGER_LDFLAGS = +check_OIDs_SOURCES = check-OIDs.c +check_OIDs_OBJECTS = check-OIDs.$(OBJEXT) +check_OIDs_LDADD = $(LDADD) +check_OIDs_DEPENDENCIES = +check_OIDs_LDFLAGS = +check_UTCTime_SOURCES = check-UTCTime.c +check_UTCTime_OBJECTS = check-UTCTime.$(OBJEXT) +check_UTCTime_LDADD = $(LDADD) +check_UTCTime_DEPENDENCIES = +check_UTCTime_LDFLAGS = + +DEFS = @DEFS@ +DEFAULT_INCLUDES = -I. -I$(srcdir) -I$(top_builddir) +CPPFLAGS = @CPPFLAGS@ +LDFLAGS = @LDFLAGS@ +LIBS = @LIBS@ +depcomp = $(SHELL) $(top_srcdir)/depcomp +@AMDEP_TRUE@DEP_FILES = $(DEPDIR)/check-GeneralizedTime.Po \ +@AMDEP_TRUE@ $(DEPDIR)/check-INTEGER.Po $(DEPDIR)/check-OIDs.Po \ +@AMDEP_TRUE@ $(DEPDIR)/check-UTCTime.Po +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +LTCOMPILE = $(LIBTOOL) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) \ + $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +CCLD = $(CC) +LINK = $(LIBTOOL) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(AM_LDFLAGS) $(LDFLAGS) -o $@ +CFLAGS = @CFLAGS@ +DIST_SOURCES = check-GeneralizedTime.c check-INTEGER.c check-OIDs.c \ + check-UTCTime.c +DIST_COMMON = Makefile.am Makefile.in +SOURCES = check-GeneralizedTime.c check-INTEGER.c check-OIDs.c check-UTCTime.c + +all: all-am + +.SUFFIXES: +.SUFFIXES: .c .lo .o .obj + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs + +distclean-libtool: + -rm -f libtool +$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ Makefile.am $(top_srcdir)/configure.in $(ACLOCAL_M4) + cd $(top_srcdir) && \ + $(AUTOMAKE) --gnu skeletons/tests/Makefile +Makefile: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.in $(top_builddir)/config.status + cd $(top_builddir) && \ + CONFIG_HEADERS= CONFIG_LINKS= \ + CONFIG_FILES=$(subdir)/$@ $(SHELL) ./config.status + +clean-checkPROGRAMS: + -test -z "$(check_PROGRAMS)" || rm -f $(check_PROGRAMS) +check-GeneralizedTime$(EXEEXT): $(check_GeneralizedTime_OBJECTS) $(check_GeneralizedTime_DEPENDENCIES) + @rm -f check-GeneralizedTime$(EXEEXT) + $(LINK) $(check_GeneralizedTime_LDFLAGS) $(check_GeneralizedTime_OBJECTS) $(check_GeneralizedTime_LDADD) $(LIBS) +check-INTEGER$(EXEEXT): $(check_INTEGER_OBJECTS) $(check_INTEGER_DEPENDENCIES) + @rm -f check-INTEGER$(EXEEXT) + $(LINK) $(check_INTEGER_LDFLAGS) $(check_INTEGER_OBJECTS) $(check_INTEGER_LDADD) $(LIBS) +check-OIDs$(EXEEXT): $(check_OIDs_OBJECTS) $(check_OIDs_DEPENDENCIES) + @rm -f check-OIDs$(EXEEXT) + $(LINK) $(check_OIDs_LDFLAGS) $(check_OIDs_OBJECTS) $(check_OIDs_LDADD) $(LIBS) +check-UTCTime$(EXEEXT): $(check_UTCTime_OBJECTS) $(check_UTCTime_DEPENDENCIES) + @rm -f check-UTCTime$(EXEEXT) + $(LINK) $(check_UTCTime_LDFLAGS) $(check_UTCTime_OBJECTS) $(check_UTCTime_LDADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) core *.core + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/check-GeneralizedTime.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/check-INTEGER.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/check-OIDs.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/check-UTCTime.Po@am__quote@ + +distclean-depend: + -rm -rf $(DEPDIR) + +.c.o: +@AMDEP_TRUE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@ depfile='$(DEPDIR)/$*.Po' tmpdepfile='$(DEPDIR)/$*.TPo' @AMDEPBACKSLASH@ +@AMDEP_TRUE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ + $(COMPILE) -c `test -f $< || echo '$(srcdir)/'`$< + +.c.obj: +@AMDEP_TRUE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@ depfile='$(DEPDIR)/$*.Po' tmpdepfile='$(DEPDIR)/$*.TPo' @AMDEPBACKSLASH@ +@AMDEP_TRUE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ + $(COMPILE) -c `cygpath -w $<` + +.c.lo: +@AMDEP_TRUE@ source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@ depfile='$(DEPDIR)/$*.Plo' tmpdepfile='$(DEPDIR)/$*.TPlo' @AMDEPBACKSLASH@ +@AMDEP_TRUE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ + $(LTCOMPILE) -c -o $@ `test -f $< || echo '$(srcdir)/'`$< +CCDEPMODE = @CCDEPMODE@ +uninstall-info-am: + +tags: TAGS + +ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) + list='$(SOURCES) $(HEADERS) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + mkid -fID $$unique $(LISP) + +TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + tags=; \ + here=`pwd`; \ + list='$(SOURCES) $(HEADERS) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + test -z "$(ETAGS_ARGS)$$unique$(LISP)$$tags" \ + || etags $(ETAGS_ARGS) $$tags $$unique $(LISP) + +GTAGS: + here=`CDPATH=: && cd $(top_builddir) && pwd` \ + && cd $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) $$here + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH + +check-TESTS: $(TESTS) + @failed=0; all=0; xfail=0; xpass=0; \ + srcdir=$(srcdir); export srcdir; \ + list='$(TESTS)'; \ + if test -n "$$list"; then \ + for tst in $$list; do \ + if test -f ./$$tst; then dir=./; \ + elif test -f $$tst; then dir=; \ + else dir="$(srcdir)/"; fi; \ + if $(TESTS_ENVIRONMENT) $${dir}$$tst; then \ + all=`expr $$all + 1`; \ + case " $(XFAIL_TESTS) " in \ + *" $$tst "*) \ + xpass=`expr $$xpass + 1`; \ + failed=`expr $$failed + 1`; \ + echo "XPASS: $$tst"; \ + ;; \ + *) \ + echo "PASS: $$tst"; \ + ;; \ + esac; \ + elif test $$? -ne 77; then \ + all=`expr $$all + 1`; \ + case " $(XFAIL_TESTS) " in \ + *" $$tst "*) \ + xfail=`expr $$xfail + 1`; \ + echo "XFAIL: $$tst"; \ + ;; \ + *) \ + failed=`expr $$failed + 1`; \ + echo "FAIL: $$tst"; \ + ;; \ + esac; \ + fi; \ + done; \ + if test "$$failed" -eq 0; then \ + if test "$$xfail" -eq 0; then \ + banner="All $$all tests passed"; \ + else \ + banner="All $$all tests behaved as expected ($$xfail expected failures)"; \ + fi; \ + else \ + if test "$$xpass" -eq 0; then \ + banner="$$failed of $$all tests failed"; \ + else \ + banner="$$failed of $$all tests did not behave as expected ($$xpass unexpected passes)"; \ + fi; \ + fi; \ + dashes=`echo "$$banner" | sed s/./=/g`; \ + echo "$$dashes"; \ + echo "$$banner"; \ + echo "$$dashes"; \ + test "$$failed" -eq 0; \ + fi + +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) + +top_distdir = ../.. +distdir = $(top_distdir)/$(PACKAGE)-$(VERSION) + +distdir: $(DISTFILES) + @for file in $(DISTFILES); do \ + if test -f $$file; then d=.; else d=$(srcdir); fi; \ + dir=`echo "$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test "$$dir" != "$$file" && test "$$dir" != "."; then \ + $(mkinstalldirs) "$(distdir)/$$dir"; \ + fi; \ + if test -d $$d/$$file; then \ + cp -pR $$d/$$file $(distdir) \ + || exit 1; \ + else \ + test -f $(distdir)/$$file \ + || cp -p $$d/$$file $(distdir)/$$file \ + || exit 1; \ + fi; \ + done +check-am: all-am + $(MAKE) $(AM_MAKEFLAGS) $(check_PROGRAMS) + $(MAKE) $(AM_MAKEFLAGS) check-TESTS +check: check-am +all-am: Makefile + +installdirs: + +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + `test -z '$(STRIP)' || \ + echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -rm -f Makefile $(CONFIG_CLEAN_FILES) stamp-h stamp-h[0-9]* + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-am + +clean-am: clean-checkPROGRAMS clean-generic clean-libtool mostlyclean-am + +distclean: distclean-am + +distclean-am: clean-am distclean-compile distclean-depend \ + distclean-generic distclean-libtool distclean-tags + +dvi: dvi-am + +dvi-am: + +info: info-am + +info-am: + +install-data-am: + +install-exec-am: + +install-info: install-info-am + +install-man: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool + +uninstall-am: uninstall-info-am + +.PHONY: GTAGS all all-am check check-TESTS check-am clean \ + clean-checkPROGRAMS clean-generic clean-libtool distclean \ + distclean-compile distclean-depend distclean-generic \ + distclean-libtool distclean-tags distdir dvi dvi-am info \ + info-am install install-am install-data install-data-am \ + install-exec install-exec-am install-info install-info-am \ + install-man install-strip installcheck installcheck-am \ + installdirs maintainer-clean maintainer-clean-generic \ + mostlyclean mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool tags uninstall uninstall-am \ + uninstall-info-am + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/skeletons/tests/check-GeneralizedTime.c b/skeletons/tests/check-GeneralizedTime.c new file mode 100644 index 00000000..3451cffe --- /dev/null +++ b/skeletons/tests/check-GeneralizedTime.c @@ -0,0 +1,54 @@ +#define __NO_ASN_TABLE__ +#include "../GeneralizedTime.c" +#include "../constraints.c" + +static void +check(char *time_str, time_t sample) { + GeneralizedTime_t gt; + struct tm tm; + time_t tloc; + + gt.buf = time_str; + gt.size = strlen(time_str); + + tloc = asn_GT2time(>, &tm); + printf("[%s] -> %ld == %ld\n", time_str, (long)tloc, (long)sample); + if(tloc != -1) + printf("\t%d-%d-%dT%02d:%02d:%02d %ld\n", + tm.tm_year + 1900, + tm.tm_mon + 1, + tm.tm_mday, + tm.tm_hour, + tm.tm_min, + tm.tm_sec, + tm.tm_gmtoff + ); + assert(tloc == sample); +} + +int +main(int ac, char **av) { + + check("200401250", -1); + check("2004012509300", -1); + check("20040125093000-", -1); + check("20040125093007-0", -1); + check("20040125093007-080", -1); + check("200401250930.01Z", -1); + + check("20040125093007Z", 1075023007); + check("20040125093007+00", 1075023007); + check("20040125093007.01+0000", 1075023007); + check("20040125093007,1+0000", 1075023007); + check("20040125093007-0800", 1075051807); + + if(ac > 1) { + /* These will be valid only inside PST time zone */ + check("20040125093007", 1075051807); + check("200401250930", 1075051800); + check("20040125093000,01", 1075051800); + check("20040125093000,1234", 1075051800); + } + + return 0; +} diff --git a/skeletons/tests/check-INTEGER.c b/skeletons/tests/check-INTEGER.c new file mode 100644 index 00000000..4053075e --- /dev/null +++ b/skeletons/tests/check-INTEGER.c @@ -0,0 +1,56 @@ +#include "../INTEGER.c" +#include "../ber_decoder.c" +#include "../ber_tlv_length.c" +#include "../ber_tlv_tag.c" +#include "../der_encoder.c" +#include "../constraints.c" + +static void +check(uint8_t *buf, int size, long check_long, int check_ret) { + INTEGER_t val; + int ret; + long rlong = 123; + + assert(buf); + assert(size >= 0); + + val.buf = buf; + val.size = size; + + + ret = asn1_INTEGER2long(&val, &rlong); + printf("Testing (%ld, %d) vs (%ld, %d)\n", + rlong, ret, check_long, check_ret); + assert(ret == check_ret); + if(ret == -1) return; + assert(rlong == check_long); +} + +int +main(int ac, char **av) { + uint8_t buf1[] = { 1 }; + uint8_t buf2[] = { 0xff }; + uint8_t buf3[] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; + uint8_t buf4[] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0 }; + uint8_t buf5[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1 }; + uint8_t buf6[] = { 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }; + uint8_t buf7[] = { 0xff, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }; + uint8_t buf8[] = { 0x7f, 0x7e, 0x7d, 0x7c }; + uint8_t buf9[] = { 0, 0x7f, 0x7e, 0x7d, 0x7c }; + uint8_t buf10[] = { 0, 0, 0, 0, 0, 0, 0x7f, 0x7e, 0x7d, 0x7c }; + +#define CHECK(buf, val, ret) check(buf, sizeof(buf), val, ret) + + CHECK(buf1, 1, 0); + CHECK(buf2, -1, 0); + CHECK(buf3, -1, 0); + CHECK(buf4, -16, 0); + CHECK(buf5, 257, 0); + CHECK(buf6, 123, -1); + CHECK(buf7, 123, -1); + CHECK(buf8, 0x7F7E7D7C, 0); + CHECK(buf9, 0x7F7E7D7C, 0); + CHECK(buf10, 0x7F7E7D7C, 0); + + return 0; +} diff --git a/skeletons/tests/check-OIDs.c b/skeletons/tests/check-OIDs.c new file mode 100644 index 00000000..a97a910a --- /dev/null +++ b/skeletons/tests/check-OIDs.c @@ -0,0 +1,228 @@ +#include "../RELATIVE-OID.c" +#include "../OBJECT_IDENTIFIER.c" +#include "../INTEGER.c" +#include "../ber_decoder.c" +#include "../ber_tlv_length.c" +#include "../ber_tlv_tag.c" +#include "../der_encoder.c" +#include "../constraints.c" + +static int +_print(const void *buffer, size_t size, void *app_key) { + fwrite(buffer, size, 1, stdout); + return 0; +} + +static void +check_OID(uint8_t *buf, size_t len, int *ck_buf, int ck_len) { + OBJECT_IDENTIFIER_t *oid; + ber_dec_rval_t rval; + unsigned long arcs[10]; + int alen; + int i; + + printf("Checking {"); + for(i = 0; i < len; i++) { printf("%s%02x", i?" ":"", buf[i]); } + printf("} against {"); + for(i = 0; i < ck_len; i++) { printf("%s%d", i?" ":"", ck_buf[i]); } + printf("}\n"); + + oid = NULL; + rval = ber_decode(&asn1_DEF_OBJECT_IDENTIFIER, (void *)&oid, buf, len); + assert(rval.code == RC_OK); + + assert(oid->size == len - 2); + + /* + * Print the contents for visual debugging. + */ + printf("OBJECT_IDENTIFIER_print() => "); + OBJECT_IDENTIFIER_print(&asn1_DEF_OBJECT_IDENTIFIER, oid, 0, _print, 0); + printf("\n"); + + alen = OBJECT_IDENTIFIER_get_arcs_l(oid, + arcs, sizeof(arcs)/sizeof(arcs[0])); + assert(alen > 0); + assert(alen == ck_len); + + /* + * Make sure they are equivalent. + */ + printf("OBJECT_IDENTIFIER_get_arcs() => {"); + for(i = 0; i < alen; i++) { + printf(" %lu", arcs[i]); + assert(arcs[i] == ck_buf[i]); + } + printf(" }\n"); +} + +static void +check_ROID(uint8_t *buf, size_t len, int *ck_buf, int ck_len) { + RELATIVE_OID_t *oid; + ber_dec_rval_t rval; + unsigned long arcs[10]; + int alen; + int i; + + printf("Checking {"); + for(i = 0; i < len; i++) { printf("%s%02x", i?" ":"", buf[i]); } + printf("} against {"); + for(i = 0; i < ck_len; i++) { printf("%s%d", i?" ":"", ck_buf[i]); } + printf("}\n"); + + oid = NULL; + rval = ber_decode(&asn1_DEF_RELATIVE_OID, (void *)&oid, buf, len); + assert(rval.code == RC_OK); + + assert(oid->size == len - 2); + + /* + * Print the contents for visual debugging. + */ + printf("RELATIVE_OID_print() => "); + RELATIVE_OID_print(&asn1_DEF_RELATIVE_OID, oid, 0, _print, 0); + printf("\n"); + + alen = RELATIVE_OID_get_arcs_l(oid, + arcs, sizeof(arcs)/sizeof(arcs[0])); + assert(alen > 0); + assert(alen == ck_len); + + /* + * Make sure they are equivalent. + */ + printf("RELATIVE_OID_get_arcs() => {"); + for(i = 0; i < alen; i++) { + printf(" %lu", (unsigned long)arcs[i]); + assert(arcs[i] == ck_buf[i]); + } + printf(" }\n"); +} + +/* + * Encode the specified array of arcs as RELATIVE-OID, decode it and compare. + */ +static void +check_REGEN(int *arcs, int acount) { + static RELATIVE_OID_t oid; + unsigned long tmp_arcs[10]; + int tmp_alen = 10; + int alen; + int ret; + int i; + + printf("Encoding {"); + for(i = 0; i < acount; i++) { + printf(" %u", arcs[i]); + } + printf(" }\n"); + + ret = RELATIVE_OID_set_arcs_l(&oid, (unsigned long *)arcs, acount); + assert(ret == 0); + + alen = RELATIVE_OID_get_arcs_l(&oid, tmp_arcs, tmp_alen); + assert(alen >= 0); + assert(alen < tmp_alen); + + printf("Encoded {"); + for(i = 0; i < alen; i++) { + printf(" %lu", tmp_arcs[i]); + assert(arcs[i] == tmp_arcs[i]); + } + printf(" }\n"); +} + +/* + * Encode the specified array of arcs as OBJECT IDENTIFIER, + * decode it and compare. + */ +static void +check_REGEN_OID(int *arcs, int acount) { + static OBJECT_IDENTIFIER_t oid; + unsigned long tmp_arcs[10]; + int tmp_alen = 10; + int alen; + int ret; + int i; + + printf("Encoding {"); + for(i = 0; i < acount; i++) { + printf(" %u", arcs[i]); + } + printf(" }\n"); + + ret = OBJECT_IDENTIFIER_set_arcs_l(&oid, (unsigned long *)arcs, acount); + assert(ret == 0); + + alen = OBJECT_IDENTIFIER_get_arcs_l(&oid, tmp_arcs, tmp_alen); + assert(alen >= 0); + assert(alen < tmp_alen); + + printf("Encoded {"); + for(i = 0; i < alen; i++) { + printf(" %lu", tmp_arcs[i]); + assert(arcs[i] == tmp_arcs[i]); + } + printf(" }\n"); +} + +#define CHECK_OID(n) check_OID(buf ## n, sizeof(buf ## n), \ + buf ## n ## _check, \ + sizeof(buf ## n ## _check)/sizeof(buf ## n ## _check[0])) +#define CHECK_ROID(n) check_ROID(buf ## n, sizeof(buf ## n), \ + buf ## n ## _check, \ + sizeof(buf ## n ## _check)/sizeof(buf ## n ## _check[0])) +#define CHECK_REGEN(n) check_REGEN(buf ## n ## _check, \ + sizeof(buf ## n ## _check)/sizeof(buf ## n ## _check[0])) +#define CHECK_REGEN_OID(n) check_REGEN_OID(buf ## n ## _check, \ + sizeof(buf ## n ## _check)/sizeof(buf ## n ## _check[0])) + +int +main(int ac, char **av) { + /* {joint-iso-itu-t 100 3} */ + uint8_t buf1[] = { + 0x06, /* OBJECT IDENTIFIER */ + 0x03, /* Length */ + 0x81, 0x34, 0x03 + }; + int buf1_check[] = { 2, 100, 3 }; + + /* {8571 3 2} */ + uint8_t buf2[] = { + 0x0D, /* RELATIVE-OID */ + 0x04, /* Length */ + 0xC2, 0x7B, 0x03, 0x02 + }; + int buf2_check[] = { 8571, 3, 2 }; + + int buf3_check[] = { 0 }; + int buf4_check[] = { 1 }; + int buf5_check[] = { 80, 40 }; + int buf6_check[] = { 127 }; + int buf7_check[] = { 128 }; + int buf8_check[] = { 65535, 65536 }; + int buf9_check[] = { 100000, 0x20000, 1234, 256, 127, 128 }; + int buf10_check[] = { 0, 0xffffffff, 0xff00ff00, 0 }; + int buf11_check[] = { 0, 1, 2 }; + int buf12_check[] = { 1, 38, 3 }; + int buf13_check[] = { 0, 0, 0xf000 }; + + + CHECK_OID(1); /* buf1, buf1_check */ + CHECK_ROID(2); /* buf2, buf2_check */ + + CHECK_REGEN(3); /* Regenerate RELATIVE-OID */ + CHECK_REGEN(4); + CHECK_REGEN(5); + CHECK_REGEN(6); + CHECK_REGEN(7); + CHECK_REGEN(8); + CHECK_REGEN(9); + CHECK_REGEN(10); + CHECK_REGEN_OID(1); /* Regenerate OBJECT IDENTIFIER */ + CHECK_REGEN_OID(11); + CHECK_REGEN_OID(12); + CHECK_REGEN_OID(13); + + return 0; +} diff --git a/skeletons/tests/check-UTCTime.c b/skeletons/tests/check-UTCTime.c new file mode 100644 index 00000000..7522140e --- /dev/null +++ b/skeletons/tests/check-UTCTime.c @@ -0,0 +1,54 @@ +#define __NO_ASN_TABLE__ +#include "../UTCTime.c" +#define __NO_ASSERT_H__ +#include "../GeneralizedTime.c" +#include "../constraints.c" + +static void +check(char *time_str, time_t sample) { + UTCTime_t gt; + struct tm tm; + time_t tloc; + + gt.buf = time_str; + gt.size = strlen(time_str); + + tloc = asn_UT2time(>, &tm); + printf("[%s] -> %ld == %ld\n", time_str, (long)tloc, (long)sample); + if(tloc != -1) + printf("\t%d-%d-%dT%02d:%02d:%02d %ld\n", + tm.tm_year + 1900, + tm.tm_mon + 1, + tm.tm_mday, + tm.tm_hour, + tm.tm_min, + tm.tm_sec, + tm.tm_gmtoff + ); + assert(tloc == sample); +} + +int +main(int ac, char **av) { + + check("0401250", -1); + check("0401250930", -1); /* "Z" or "(+|-)hhmm" required */ + check("04012509300", -1); + check("040125093000-", -1); + check("040125093007-0", -1); + check("040125093007-080", -1); + check("0401250930.01Z", -1); + + check("040125093007Z", 1075023007); + check("040125093007+00", 1075023007); + check("040125093007-0800", 1075051807); + + if(ac > 1) { + /* These will be valid only inside PST time zone */ + check("040125093007", 1075051807); + check("040125093000,01", 1075051800); + check("040125093000,1234", 1075051800); + } + + return 0; +} |