diff --git a/asn1_test_data/ANY.c b/asn1_test_data/ANY.c new file mode 100644 index 0000000..77024bd --- /dev/null +++ b/asn1_test_data/ANY.c @@ -0,0 +1,158 @@ +/*- + * Copyright (c) 2004 Lev Walkin . All rights reserved. + * Redistribution and modifications are permitted subject to BSD license. + */ +#include +#include +#include + +static asn_OCTET_STRING_specifics_t asn_DEF_ANY_specs = { + sizeof(ANY_t), + offsetof(ANY_t, _asn_ctx), + ASN_OSUBV_ANY +}; +asn_TYPE_descriptor_t asn_DEF_ANY = { + "ANY", + "ANY", + OCTET_STRING_free, + OCTET_STRING_print, + asn_generic_no_constraint, + OCTET_STRING_decode_ber, + OCTET_STRING_encode_der, + OCTET_STRING_decode_xer_hex, + ANY_encode_xer, + 0, 0, + 0, /* Use generic outmost tag fetcher */ + 0, 0, 0, 0, + 0, /* No PER visible constraints */ + 0, 0, /* No members */ + &asn_DEF_ANY_specs, +}; + + +asn_enc_rval_t +ANY_encode_xer(asn_TYPE_descriptor_t *td, void *sptr, + int ilevel, enum xer_encoder_flags_e flags, + asn_app_consume_bytes_f *cb, void *app_key) { + + if(flags & XER_F_CANONICAL) { + /* + * Canonical XER-encoding of ANY type is not supported. + */ + ASN__ENCODE_FAILED; + } + + /* Dump as binary */ + return OCTET_STRING_encode_xer(td, sptr, ilevel, flags, cb, app_key); +} + +struct _callback_arg { + uint8_t *buffer; + size_t offset; + size_t size; +}; + +static int ANY__consume_bytes(const void *buffer, size_t size, void *key); + +int +ANY_fromType(ANY_t *st, asn_TYPE_descriptor_t *td, void *sptr) { + struct _callback_arg arg; + asn_enc_rval_t erval; + + if(!st || !td) { + errno = EINVAL; + return -1; + } + + if(!sptr) { + if(st->buf) FREEMEM(st->buf); + st->size = 0; + return 0; + } + + arg.offset = arg.size = 0; + arg.buffer = 0; + + erval = der_encode(td, sptr, ANY__consume_bytes, &arg); + if(erval.encoded == -1) { + if(arg.buffer) FREEMEM(arg.buffer); + return -1; + } + assert((size_t)erval.encoded == arg.offset); + + if(st->buf) FREEMEM(st->buf); + st->buf = arg.buffer; + st->size = arg.offset; + + return 0; +} + +ANY_t * +ANY_new_fromType(asn_TYPE_descriptor_t *td, void *sptr) { + ANY_t tmp; + ANY_t *st; + + if(!td || !sptr) { + errno = EINVAL; + return 0; + } + + memset(&tmp, 0, sizeof(tmp)); + + if(ANY_fromType(&tmp, td, sptr)) return 0; + + st = (ANY_t *)CALLOC(1, sizeof(ANY_t)); + if(st) { + *st = tmp; + return st; + } else { + FREEMEM(tmp.buf); + return 0; + } +} + +int +ANY_to_type(ANY_t *st, asn_TYPE_descriptor_t *td, void **struct_ptr) { + asn_dec_rval_t rval; + void *newst = 0; + + if(!st || !td || !struct_ptr) { + errno = EINVAL; + return -1; + } + + if(st->buf == 0) { + /* Nothing to convert, make it empty. */ + *struct_ptr = (void *)0; + return 0; + } + + rval = ber_decode(0, td, (void **)&newst, st->buf, st->size); + if(rval.code == RC_OK) { + *struct_ptr = newst; + return 0; + } else { + /* Remove possibly partially decoded data. */ + ASN_STRUCT_FREE(*td, newst); + return -1; + } +} + +static int ANY__consume_bytes(const void *buffer, size_t size, void *key) { + struct _callback_arg *arg = (struct _callback_arg *)key; + + if((arg->offset + size) >= arg->size) { + size_t nsize = (arg->size ? arg->size << 2 : 16) + size; + void *p = REALLOC(arg->buffer, nsize); + if(!p) return -1; + arg->buffer = (uint8_t *)p; + arg->size = nsize; + } + + memcpy(arg->buffer + arg->offset, buffer, size); + arg->offset += size; + assert(arg->offset < arg->size); + + return 0; +} + diff --git a/asn1_test_data/ANY.h b/asn1_test_data/ANY.h new file mode 100644 index 0000000..b7d92fa --- /dev/null +++ b/asn1_test_data/ANY.h @@ -0,0 +1,48 @@ +/*- + * Copyright (c) 2004 Lev Walkin . All rights reserved. + * Redistribution and modifications are permitted subject to BSD license. + */ +#ifndef ASN_TYPE_ANY_H +#define ASN_TYPE_ANY_H + +#include /* Implemented via OCTET STRING type */ + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct ANY { + uint8_t *buf; /* BER-encoded ANY contents */ + int size; /* Size of the above buffer */ + + asn_struct_ctx_t _asn_ctx; /* Parsing across buffer boundaries */ +} ANY_t; + +extern asn_TYPE_descriptor_t asn_DEF_ANY; + +asn_struct_free_f ANY_free; +asn_struct_print_f ANY_print; +ber_type_decoder_f ANY_decode_ber; +der_type_encoder_f ANY_encode_der; +xer_type_encoder_f ANY_encode_xer; + +/****************************** + * Handy conversion routines. * + ******************************/ + +/* Convert another ASN.1 type into the ANY. This implies DER encoding. */ +int ANY_fromType(ANY_t *, asn_TYPE_descriptor_t *td, void *struct_ptr); +ANY_t *ANY_new_fromType(asn_TYPE_descriptor_t *td, void *struct_ptr); + +/* Convert the contents of the ANY type into the specified type. */ +int ANY_to_type(ANY_t *, asn_TYPE_descriptor_t *td, void **struct_ptr); + +#define ANY_fromBuf(s, buf, size) OCTET_STRING_fromBuf((s), (buf), (size)) +#define ANY_new_fromBuf(buf, size) OCTET_STRING_new_fromBuf( \ + &asn_DEF_ANY, (buf), (size)) + +#ifdef __cplusplus +} +#endif + +#endif /* ASN_TYPE_ANY_H */ diff --git a/asn1_test_data/Address.c b/asn1_test_data/Address.c new file mode 100644 index 0000000..f5ac710 --- /dev/null +++ b/asn1_test_data/Address.c @@ -0,0 +1,256 @@ +/* + * Generated by asn1c-0.9.28 (http://lionet.info/asn1c) + * From ASN.1 module "MyShopPurchaseOrders" + * found in "productExample1.asn" + */ + +#include "Address.h" + +static int check_permitted_alphabet_2(const void *sptr) { + /* The underlying type is VisibleString */ + const VisibleString_t *st = (const VisibleString_t *)sptr; + const uint8_t *ch = st->buf; + const uint8_t *end = ch + st->size; + + for(; ch < end; ch++) { + uint8_t cv = *ch; + if(!(cv >= 32 && cv <= 126)) return -1; + } + return 0; +} + +static int check_permitted_alphabet_3(const void *sptr) { + /* The underlying type is VisibleString */ + const VisibleString_t *st = (const VisibleString_t *)sptr; + const uint8_t *ch = st->buf; + const uint8_t *end = ch + st->size; + + for(; ch < end; ch++) { + uint8_t cv = *ch; + if(!(cv >= 32 && cv <= 126)) return -1; + } + return 0; +} + +static int check_permitted_alphabet_4(const void *sptr) { + /* The underlying type is VisibleString */ + const VisibleString_t *st = (const VisibleString_t *)sptr; + const uint8_t *ch = st->buf; + const uint8_t *end = ch + st->size; + + for(; ch < end; ch++) { + uint8_t cv = *ch; + if(!(cv >= 65 && cv <= 90)) return -1; + } + return 0; +} + +static const int permitted_alphabet_table_5[256] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* */ + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* . */ + 2, 3, 4, 5, 6, 7, 8, 9,10,11, 0, 0, 0, 0, 0, 0, /* 0123456789 */ +}; + +static int check_permitted_alphabet_5(const void *sptr) { + const int *table = permitted_alphabet_table_5; + /* The underlying type is NumericString */ + const NumericString_t *st = (const NumericString_t *)sptr; + const uint8_t *ch = st->buf; + const uint8_t *end = ch + st->size; + + for(; ch < end; ch++) { + uint8_t cv = *ch; + if(!table[cv]) return -1; + } + return 0; +} + +static int +memb_street_constraint_1(asn_TYPE_descriptor_t *td, const void *sptr, + asn_app_constraint_failed_f *ctfailcb, void *app_key) { + const VisibleString_t *st = (const VisibleString_t *)sptr; + size_t size; + + if(!sptr) { + ASN__CTFAIL(app_key, td, sptr, + "%s: value not given (%s:%d)", + td->name, __FILE__, __LINE__); + return -1; + } + + size = st->size; + + if((size >= 5 && size <= 50) + && !check_permitted_alphabet_2(st)) { + /* Constraint check succeeded */ + return 0; + } else { + ASN__CTFAIL(app_key, td, sptr, + "%s: constraint failed (%s:%d)", + td->name, __FILE__, __LINE__); + return -1; + } +} + +static int +memb_city_constraint_1(asn_TYPE_descriptor_t *td, const void *sptr, + asn_app_constraint_failed_f *ctfailcb, void *app_key) { + const VisibleString_t *st = (const VisibleString_t *)sptr; + size_t size; + + if(!sptr) { + ASN__CTFAIL(app_key, td, sptr, + "%s: value not given (%s:%d)", + td->name, __FILE__, __LINE__); + return -1; + } + + size = st->size; + + if((size >= 2 && size <= 30) + && !check_permitted_alphabet_3(st)) { + /* Constraint check succeeded */ + return 0; + } else { + ASN__CTFAIL(app_key, td, sptr, + "%s: constraint failed (%s:%d)", + td->name, __FILE__, __LINE__); + return -1; + } +} + +static int +memb_state_constraint_1(asn_TYPE_descriptor_t *td, const void *sptr, + asn_app_constraint_failed_f *ctfailcb, void *app_key) { + const VisibleString_t *st = (const VisibleString_t *)sptr; + size_t size; + + if(!sptr) { + ASN__CTFAIL(app_key, td, sptr, + "%s: value not given (%s:%d)", + td->name, __FILE__, __LINE__); + return -1; + } + + size = st->size; + + if((size == 2) + && !check_permitted_alphabet_4(st)) { + /* Constraint check succeeded */ + return 0; + } else { + ASN__CTFAIL(app_key, td, sptr, + "%s: constraint failed (%s:%d)", + td->name, __FILE__, __LINE__); + return -1; + } +} + +static int +memb_zipCode_constraint_1(asn_TYPE_descriptor_t *td, const void *sptr, + asn_app_constraint_failed_f *ctfailcb, void *app_key) { + const NumericString_t *st = (const NumericString_t *)sptr; + size_t size; + + if(!sptr) { + ASN__CTFAIL(app_key, td, sptr, + "%s: value not given (%s:%d)", + td->name, __FILE__, __LINE__); + return -1; + } + + size = st->size; + + if(((size == 5) || (size == 9)) + && !check_permitted_alphabet_5(st)) { + /* Constraint check succeeded */ + return 0; + } else { + ASN__CTFAIL(app_key, td, sptr, + "%s: constraint failed (%s:%d)", + td->name, __FILE__, __LINE__); + return -1; + } +} + +static asn_TYPE_member_t asn_MBR_Address_1[] = { + { ATF_POINTER, 1, offsetof(struct Address, street), + (ASN_TAG_CLASS_CONTEXT | (0 << 2)), + -1, /* IMPLICIT tag at current level */ + &asn_DEF_VisibleString, + memb_street_constraint_1, + 0, /* PER is not compiled, use -gen-PER */ + 0, + "street" + }, + { ATF_NOFLAGS, 0, offsetof(struct Address, city), + (ASN_TAG_CLASS_CONTEXT | (1 << 2)), + -1, /* IMPLICIT tag at current level */ + &asn_DEF_VisibleString, + memb_city_constraint_1, + 0, /* PER is not compiled, use -gen-PER */ + 0, + "city" + }, + { ATF_NOFLAGS, 0, offsetof(struct Address, state), + (ASN_TAG_CLASS_CONTEXT | (2 << 2)), + -1, /* IMPLICIT tag at current level */ + &asn_DEF_VisibleString, + memb_state_constraint_1, + 0, /* PER is not compiled, use -gen-PER */ + 0, + "state" + }, + { ATF_NOFLAGS, 0, offsetof(struct Address, zipCode), + (ASN_TAG_CLASS_CONTEXT | (3 << 2)), + -1, /* IMPLICIT tag at current level */ + &asn_DEF_NumericString, + memb_zipCode_constraint_1, + 0, /* PER is not compiled, use -gen-PER */ + 0, + "zipCode" + }, +}; +static const ber_tlv_tag_t asn_DEF_Address_tags_1[] = { + (ASN_TAG_CLASS_UNIVERSAL | (16 << 2)) +}; +static const asn_TYPE_tag2member_t asn_MAP_Address_tag2el_1[] = { + { (ASN_TAG_CLASS_CONTEXT | (0 << 2)), 0, 0, 0 }, /* street */ + { (ASN_TAG_CLASS_CONTEXT | (1 << 2)), 1, 0, 0 }, /* city */ + { (ASN_TAG_CLASS_CONTEXT | (2 << 2)), 2, 0, 0 }, /* state */ + { (ASN_TAG_CLASS_CONTEXT | (3 << 2)), 3, 0, 0 } /* zipCode */ +}; +static asn_SEQUENCE_specifics_t asn_SPC_Address_specs_1 = { + sizeof(struct Address), + offsetof(struct Address, _asn_ctx), + asn_MAP_Address_tag2el_1, + 4, /* Count of tags in the map */ + 0, 0, 0, /* Optional elements (not needed) */ + -1, /* Start extensions */ + -1 /* Stop extensions */ +}; +asn_TYPE_descriptor_t asn_DEF_Address = { + "Address", + "Address", + SEQUENCE_free, + SEQUENCE_print, + SEQUENCE_constraint, + SEQUENCE_decode_ber, + SEQUENCE_encode_der, + SEQUENCE_decode_xer, + SEQUENCE_encode_xer, + 0, 0, /* No PER support, use "-gen-PER" to enable */ + 0, /* Use generic outmost tag fetcher */ + asn_DEF_Address_tags_1, + sizeof(asn_DEF_Address_tags_1) + /sizeof(asn_DEF_Address_tags_1[0]), /* 1 */ + asn_DEF_Address_tags_1, /* Same as above */ + sizeof(asn_DEF_Address_tags_1) + /sizeof(asn_DEF_Address_tags_1[0]), /* 1 */ + 0, /* No PER visible constraints */ + asn_MBR_Address_1, + 4, /* Elements count */ + &asn_SPC_Address_specs_1 /* Additional specs */ +}; + diff --git a/asn1_test_data/Address.h b/asn1_test_data/Address.h new file mode 100644 index 0000000..a7809e0 --- /dev/null +++ b/asn1_test_data/Address.h @@ -0,0 +1,41 @@ +/* + * Generated by asn1c-0.9.28 (http://lionet.info/asn1c) + * From ASN.1 module "MyShopPurchaseOrders" + * found in "productExample1.asn" + */ + +#ifndef _Address_H_ +#define _Address_H_ + + +#include + +/* Including external dependencies */ +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* Address */ +typedef struct Address { + VisibleString_t *street /* OPTIONAL */; + VisibleString_t city; + VisibleString_t state; + NumericString_t zipCode; + + /* Context for parsing across buffer boundaries */ + asn_struct_ctx_t _asn_ctx; +} Address_t; + +/* Implementation */ +extern asn_TYPE_descriptor_t asn_DEF_Address; + +#ifdef __cplusplus +} +#endif + +#endif /* _Address_H_ */ +#include diff --git a/asn1_test_data/AlgorithmIdentifier.c b/asn1_test_data/AlgorithmIdentifier.c new file mode 100644 index 0000000..b84e87a --- /dev/null +++ b/asn1_test_data/AlgorithmIdentifier.c @@ -0,0 +1,67 @@ +/* + * Generated by asn1c-0.9.28 (http://lionet.info/asn1c) + * From ASN.1 module "COMPOSITESIGALGS" + * found in "pqASN1_compiling.asn" + */ + +#include "AlgorithmIdentifier.h" + +static asn_TYPE_member_t asn_MBR_AlgorithmIdentifier_1[] = { + { ATF_NOFLAGS, 0, offsetof(struct AlgorithmIdentifier, algorithm), + (ASN_TAG_CLASS_UNIVERSAL | (6 << 2)), + 0, + &asn_DEF_OBJECT_IDENTIFIER, + 0, /* Defer constraints checking to the member type */ + 0, /* PER is not compiled, use -gen-PER */ + 0, + "algorithm" + }, + { ATF_OPEN_TYPE | ATF_POINTER, 1, offsetof(struct AlgorithmIdentifier, parameters), + -1 /* Ambiguous tag (ANY?) */, + 0, + &asn_DEF_ANY, + 0, /* Defer constraints checking to the member type */ + 0, /* PER is not compiled, use -gen-PER */ + 0, + "parameters" + }, +}; +static const ber_tlv_tag_t asn_DEF_AlgorithmIdentifier_tags_1[] = { + (ASN_TAG_CLASS_UNIVERSAL | (16 << 2)) +}; +static const asn_TYPE_tag2member_t asn_MAP_AlgorithmIdentifier_tag2el_1[] = { + { (ASN_TAG_CLASS_UNIVERSAL | (6 << 2)), 0, 0, 0 } /* algorithm */ +}; +static asn_SEQUENCE_specifics_t asn_SPC_AlgorithmIdentifier_specs_1 = { + sizeof(struct AlgorithmIdentifier), + offsetof(struct AlgorithmIdentifier, _asn_ctx), + asn_MAP_AlgorithmIdentifier_tag2el_1, + 1, /* Count of tags in the map */ + 0, 0, 0, /* Optional elements (not needed) */ + -1, /* Start extensions */ + -1 /* Stop extensions */ +}; +asn_TYPE_descriptor_t asn_DEF_AlgorithmIdentifier = { + "AlgorithmIdentifier", + "AlgorithmIdentifier", + SEQUENCE_free, + SEQUENCE_print, + SEQUENCE_constraint, + SEQUENCE_decode_ber, + SEQUENCE_encode_der, + SEQUENCE_decode_xer, + SEQUENCE_encode_xer, + 0, 0, /* No PER support, use "-gen-PER" to enable */ + 0, /* Use generic outmost tag fetcher */ + asn_DEF_AlgorithmIdentifier_tags_1, + sizeof(asn_DEF_AlgorithmIdentifier_tags_1) + /sizeof(asn_DEF_AlgorithmIdentifier_tags_1[0]), /* 1 */ + asn_DEF_AlgorithmIdentifier_tags_1, /* Same as above */ + sizeof(asn_DEF_AlgorithmIdentifier_tags_1) + /sizeof(asn_DEF_AlgorithmIdentifier_tags_1[0]), /* 1 */ + 0, /* No PER visible constraints */ + asn_MBR_AlgorithmIdentifier_1, + 2, /* Elements count */ + &asn_SPC_AlgorithmIdentifier_specs_1 /* Additional specs */ +}; + diff --git a/asn1_test_data/AlgorithmIdentifier.h b/asn1_test_data/AlgorithmIdentifier.h new file mode 100644 index 0000000..aad3566 --- /dev/null +++ b/asn1_test_data/AlgorithmIdentifier.h @@ -0,0 +1,39 @@ +/* + * Generated by asn1c-0.9.28 (http://lionet.info/asn1c) + * From ASN.1 module "COMPOSITESIGALGS" + * found in "pqASN1_compiling.asn" + */ + +#ifndef _AlgorithmIdentifier_H_ +#define _AlgorithmIdentifier_H_ + + +#include + +/* Including external dependencies */ +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* AlgorithmIdentifier */ +typedef struct AlgorithmIdentifier { + OBJECT_IDENTIFIER_t algorithm; + ANY_t *parameters /* OPTIONAL */; + + /* Context for parsing across buffer boundaries */ + asn_struct_ctx_t _asn_ctx; +} AlgorithmIdentifier_t; + +/* Implementation */ +extern asn_TYPE_descriptor_t asn_DEF_AlgorithmIdentifier; + +#ifdef __cplusplus +} +#endif + +#endif /* _AlgorithmIdentifier_H_ */ +#include diff --git a/asn1_test_data/BIT_STRING.c b/asn1_test_data/BIT_STRING.c new file mode 100644 index 0000000..997ff41 --- /dev/null +++ b/asn1_test_data/BIT_STRING.c @@ -0,0 +1,189 @@ +/*- + * Copyright (c) 2003, 2004 Lev Walkin . All rights reserved. + * Redistribution and modifications are permitted subject to BSD license. + */ +#include +#include +#include + +/* + * BIT STRING basic type description. + */ +static const ber_tlv_tag_t asn_DEF_BIT_STRING_tags[] = { + (ASN_TAG_CLASS_UNIVERSAL | (3 << 2)) +}; +static asn_OCTET_STRING_specifics_t asn_DEF_BIT_STRING_specs = { + sizeof(BIT_STRING_t), + offsetof(BIT_STRING_t, _asn_ctx), + ASN_OSUBV_BIT +}; +asn_TYPE_descriptor_t asn_DEF_BIT_STRING = { + "BIT STRING", + "BIT_STRING", + OCTET_STRING_free, /* Implemented in terms of OCTET STRING */ + BIT_STRING_print, + BIT_STRING_constraint, + OCTET_STRING_decode_ber, /* Implemented in terms of OCTET STRING */ + OCTET_STRING_encode_der, /* Implemented in terms of OCTET STRING */ + OCTET_STRING_decode_xer_binary, + BIT_STRING_encode_xer, + OCTET_STRING_decode_uper, /* Unaligned PER decoder */ + OCTET_STRING_encode_uper, /* Unaligned PER encoder */ + 0, /* Use generic outmost tag fetcher */ + asn_DEF_BIT_STRING_tags, + sizeof(asn_DEF_BIT_STRING_tags) + / sizeof(asn_DEF_BIT_STRING_tags[0]), + asn_DEF_BIT_STRING_tags, /* Same as above */ + sizeof(asn_DEF_BIT_STRING_tags) + / sizeof(asn_DEF_BIT_STRING_tags[0]), + 0, /* No PER visible constraints */ + 0, 0, /* No members */ + &asn_DEF_BIT_STRING_specs +}; + +/* + * BIT STRING generic constraint. + */ +int +BIT_STRING_constraint(asn_TYPE_descriptor_t *td, const void *sptr, + asn_app_constraint_failed_f *ctfailcb, void *app_key) { + const BIT_STRING_t *st = (const BIT_STRING_t *)sptr; + + if(st && st->buf) { + if((st->size == 0 && st->bits_unused) + || st->bits_unused < 0 || st->bits_unused > 7) { + ASN__CTFAIL(app_key, td, sptr, + "%s: invalid padding byte (%s:%d)", + td->name, __FILE__, __LINE__); + return -1; + } + } else { + ASN__CTFAIL(app_key, td, sptr, + "%s: value not given (%s:%d)", + td->name, __FILE__, __LINE__); + return -1; + } + + return 0; +} + +static char *_bit_pattern[16] = { + "0000", "0001", "0010", "0011", "0100", "0101", "0110", "0111", + "1000", "1001", "1010", "1011", "1100", "1101", "1110", "1111" +}; + +asn_enc_rval_t +BIT_STRING_encode_xer(asn_TYPE_descriptor_t *td, void *sptr, + int ilevel, enum xer_encoder_flags_e flags, + asn_app_consume_bytes_f *cb, void *app_key) { + asn_enc_rval_t er; + char scratch[128]; + char *p = scratch; + char *scend = scratch + (sizeof(scratch) - 10); + const BIT_STRING_t *st = (const BIT_STRING_t *)sptr; + int xcan = (flags & XER_F_CANONICAL); + uint8_t *buf; + uint8_t *end; + + if(!st || !st->buf) + ASN__ENCODE_FAILED; + + er.encoded = 0; + + buf = st->buf; + end = buf + st->size - 1; /* Last byte is special */ + + /* + * Binary dump + */ + for(; buf < end; buf++) { + int v = *buf; + int nline = xcan?0:(((buf - st->buf) % 8) == 0); + if(p >= scend || nline) { + er.encoded += p - scratch; + ASN__CALLBACK(scratch, p - scratch); + p = scratch; + if(nline) ASN__TEXT_INDENT(1, ilevel); + } + memcpy(p + 0, _bit_pattern[v >> 4], 4); + memcpy(p + 4, _bit_pattern[v & 0x0f], 4); + p += 8; + } + + if(!xcan && ((buf - st->buf) % 8) == 0) + ASN__TEXT_INDENT(1, ilevel); + er.encoded += p - scratch; + ASN__CALLBACK(scratch, p - scratch); + p = scratch; + + if(buf == end) { + int v = *buf; + int ubits = st->bits_unused; + int i; + for(i = 7; i >= ubits; i--) + *p++ = (v & (1 << i)) ? 0x31 : 0x30; + er.encoded += p - scratch; + ASN__CALLBACK(scratch, p - scratch); + } + + if(!xcan) ASN__TEXT_INDENT(1, ilevel - 1); + + ASN__ENCODED_OK(er); +cb_failed: + ASN__ENCODE_FAILED; +} + + +/* + * BIT STRING specific contents printer. + */ +int +BIT_STRING_print(asn_TYPE_descriptor_t *td, const void *sptr, int ilevel, + asn_app_consume_bytes_f *cb, void *app_key) { + const char * const h2c = "0123456789ABCDEF"; + char scratch[64]; + const BIT_STRING_t *st = (const BIT_STRING_t *)sptr; + uint8_t *buf; + uint8_t *end; + char *p = scratch; + + (void)td; /* Unused argument */ + + if(!st || !st->buf) + return (cb("", 8, app_key) < 0) ? -1 : 0; + + ilevel++; + buf = st->buf; + end = buf + st->size; + + /* + * Hexadecimal dump. + */ + for(; buf < end; buf++) { + if((buf - st->buf) % 16 == 0 && (st->size > 16) + && buf != st->buf) { + _i_INDENT(1); + /* Dump the string */ + if(cb(scratch, p - scratch, app_key) < 0) return -1; + p = scratch; + } + *p++ = h2c[*buf >> 4]; + *p++ = h2c[*buf & 0x0F]; + *p++ = 0x20; + } + + if(p > scratch) { + p--; /* Eat the tailing space */ + + if((st->size > 16)) { + _i_INDENT(1); + } + + /* Dump the incomplete 16-bytes row */ + if(cb(scratch, p - scratch, app_key) < 0) + return -1; + } + + return 0; +} + diff --git a/asn1_test_data/BIT_STRING.h b/asn1_test_data/BIT_STRING.h new file mode 100644 index 0000000..732e878 --- /dev/null +++ b/asn1_test_data/BIT_STRING.h @@ -0,0 +1,33 @@ +/*- + * Copyright (c) 2003 Lev Walkin . All rights reserved. + * Redistribution and modifications are permitted subject to BSD license. + */ +#ifndef _BIT_STRING_H_ +#define _BIT_STRING_H_ + +#include /* Some help from OCTET STRING */ + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct BIT_STRING_s { + uint8_t *buf; /* BIT STRING body */ + int size; /* Size of the above buffer */ + + int bits_unused;/* Unused trailing bits in the last octet (0..7) */ + + asn_struct_ctx_t _asn_ctx; /* Parsing across buffer boundaries */ +} BIT_STRING_t; + +extern asn_TYPE_descriptor_t asn_DEF_BIT_STRING; + +asn_struct_print_f BIT_STRING_print; /* Human-readable output */ +asn_constr_check_f BIT_STRING_constraint; +xer_type_encoder_f BIT_STRING_encode_xer; + +#ifdef __cplusplus +} +#endif + +#endif /* _BIT_STRING_H_ */ diff --git a/asn1_test_data/BOOLEAN.c b/asn1_test_data/BOOLEAN.c new file mode 100644 index 0000000..6e55b3e --- /dev/null +++ b/asn1_test_data/BOOLEAN.c @@ -0,0 +1,282 @@ +/*- + * Copyright (c) 2003, 2005 Lev Walkin . All rights reserved. + * Redistribution and modifications are permitted subject to BSD license. + */ +#include +#include +#include + +/* + * BOOLEAN basic type description. + */ +static const ber_tlv_tag_t asn_DEF_BOOLEAN_tags[] = { + (ASN_TAG_CLASS_UNIVERSAL | (1 << 2)) +}; +asn_TYPE_descriptor_t asn_DEF_BOOLEAN = { + "BOOLEAN", + "BOOLEAN", + BOOLEAN_free, + BOOLEAN_print, + asn_generic_no_constraint, + BOOLEAN_decode_ber, + BOOLEAN_encode_der, + BOOLEAN_decode_xer, + BOOLEAN_encode_xer, + BOOLEAN_decode_uper, /* Unaligned PER decoder */ + BOOLEAN_encode_uper, /* Unaligned PER encoder */ + 0, /* Use generic outmost tag fetcher */ + asn_DEF_BOOLEAN_tags, + sizeof(asn_DEF_BOOLEAN_tags) / sizeof(asn_DEF_BOOLEAN_tags[0]), + asn_DEF_BOOLEAN_tags, /* Same as above */ + sizeof(asn_DEF_BOOLEAN_tags) / sizeof(asn_DEF_BOOLEAN_tags[0]), + 0, /* No PER visible constraints */ + 0, 0, /* No members */ + 0 /* No specifics */ +}; + +/* + * Decode BOOLEAN type. + */ +asn_dec_rval_t +BOOLEAN_decode_ber(asn_codec_ctx_t *opt_codec_ctx, + asn_TYPE_descriptor_t *td, + void **bool_value, const void *buf_ptr, size_t size, + int tag_mode) { + BOOLEAN_t *st = (BOOLEAN_t *)*bool_value; + asn_dec_rval_t rval; + ber_tlv_len_t length; + ber_tlv_len_t lidx; + + if(st == NULL) { + st = (BOOLEAN_t *)(*bool_value = 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(opt_codec_ctx, td, 0, buf_ptr, size, + tag_mode, 0, &length, 0); + if(rval.code != RC_OK) + return rval; + + ASN_DEBUG("Boolean length is %d bytes", (int)length); + + buf_ptr = ((const char *)buf_ptr) + rval.consumed; + size -= rval.consumed; + if(length > (ber_tlv_len_t)size) { + rval.code = RC_WMORE; + rval.consumed = 0; + return rval; + } + + /* + * Compute boolean value. + */ + for(*st = 0, lidx = 0; + (lidx < length) && *st == 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 |= ((const uint8_t *)buf_ptr)[lidx]; + } + + rval.code = RC_OK; + rval.consumed += length; + + ASN_DEBUG("Took %ld/%ld bytes to encode %s, value=%d", + (long)rval.consumed, (long)length, + td->name, *st); + + return rval; +} + +asn_enc_rval_t +BOOLEAN_encode_der(asn_TYPE_descriptor_t *td, void *sptr, + int tag_mode, ber_tlv_tag_t tag, + asn_app_consume_bytes_f *cb, void *app_key) { + asn_enc_rval_t erval; + BOOLEAN_t *st = (BOOLEAN_t *)sptr; + + erval.encoded = der_write_tags(td, 1, tag_mode, 0, tag, cb, app_key); + if(erval.encoded == -1) { + erval.failed_type = td; + erval.structure_ptr = sptr; + return erval; + } + + if(cb) { + uint8_t bool_value; + + bool_value = *st ? 0xff : 0; /* 0xff mandated by DER */ + + if(cb(&bool_value, 1, app_key) < 0) { + erval.encoded = -1; + erval.failed_type = td; + erval.structure_ptr = sptr; + return erval; + } + } + + erval.encoded += 1; + + ASN__ENCODED_OK(erval); +} + + +/* + * Decode the chunk of XML text encoding INTEGER. + */ +static enum xer_pbd_rval +BOOLEAN__xer_body_decode(asn_TYPE_descriptor_t *td, void *sptr, const void *chunk_buf, size_t chunk_size) { + BOOLEAN_t *st = (BOOLEAN_t *)sptr; + const char *p = (const char *)chunk_buf; + + (void)td; + + if(chunk_size && p[0] == 0x3c /* '<' */) { + switch(xer_check_tag(chunk_buf, chunk_size, "false")) { + case XCT_BOTH: + /* "" */ + *st = 0; + break; + case XCT_UNKNOWN_BO: + if(xer_check_tag(chunk_buf, chunk_size, "true") + != XCT_BOTH) + return XPBD_BROKEN_ENCODING; + /* "" */ + *st = 1; /* Or 0xff as in DER?.. */ + break; + default: + return XPBD_BROKEN_ENCODING; + } + return XPBD_BODY_CONSUMED; + } else { + return XPBD_BROKEN_ENCODING; + } +} + + +asn_dec_rval_t +BOOLEAN_decode_xer(asn_codec_ctx_t *opt_codec_ctx, + asn_TYPE_descriptor_t *td, void **sptr, const char *opt_mname, + const void *buf_ptr, size_t size) { + + return xer_decode_primitive(opt_codec_ctx, td, + sptr, sizeof(BOOLEAN_t), opt_mname, buf_ptr, size, + BOOLEAN__xer_body_decode); +} + +asn_enc_rval_t +BOOLEAN_encode_xer(asn_TYPE_descriptor_t *td, void *sptr, + int ilevel, enum xer_encoder_flags_e flags, + asn_app_consume_bytes_f *cb, void *app_key) { + const BOOLEAN_t *st = (const BOOLEAN_t *)sptr; + asn_enc_rval_t er; + + (void)ilevel; + (void)flags; + + if(!st) ASN__ENCODE_FAILED; + + if(*st) { + ASN__CALLBACK("", 7); + er.encoded = 7; + } else { + ASN__CALLBACK("", 8); + er.encoded = 8; + } + + ASN__ENCODED_OK(er); +cb_failed: + ASN__ENCODE_FAILED; +} + +int +BOOLEAN_print(asn_TYPE_descriptor_t *td, const void *sptr, int ilevel, + asn_app_consume_bytes_f *cb, void *app_key) { + const BOOLEAN_t *st = (const BOOLEAN_t *)sptr; + const char *buf; + size_t buflen; + + (void)td; /* Unused argument */ + (void)ilevel; /* Unused argument */ + + if(st) { + if(*st) { + buf = "TRUE"; + buflen = 4; + } else { + buf = "FALSE"; + buflen = 5; + } + } else { + buf = ""; + buflen = 8; + } + + return (cb(buf, buflen, app_key) < 0) ? -1 : 0; +} + +void +BOOLEAN_free(asn_TYPE_descriptor_t *td, void *ptr, int contents_only) { + if(td && ptr && !contents_only) { + FREEMEM(ptr); + } +} + +asn_dec_rval_t +BOOLEAN_decode_uper(asn_codec_ctx_t *opt_codec_ctx, asn_TYPE_descriptor_t *td, + asn_per_constraints_t *constraints, void **sptr, asn_per_data_t *pd) { + asn_dec_rval_t rv; + BOOLEAN_t *st = (BOOLEAN_t *)*sptr; + + (void)opt_codec_ctx; + (void)constraints; + + if(!st) { + st = (BOOLEAN_t *)(*sptr = MALLOC(sizeof(*st))); + if(!st) ASN__DECODE_FAILED; + } + + /* + * Extract a single bit + */ + switch(per_get_few_bits(pd, 1)) { + case 1: *st = 1; break; + case 0: *st = 0; break; + case -1: default: ASN__DECODE_STARVED; + } + + ASN_DEBUG("%s decoded as %s", td->name, *st ? "TRUE" : "FALSE"); + + rv.code = RC_OK; + rv.consumed = 1; + return rv; +} + + +asn_enc_rval_t +BOOLEAN_encode_uper(asn_TYPE_descriptor_t *td, + asn_per_constraints_t *constraints, void *sptr, asn_per_outp_t *po) { + const BOOLEAN_t *st = (const BOOLEAN_t *)sptr; + asn_enc_rval_t er = { 0, 0, 0 }; + + (void)constraints; + + if(!st) ASN__ENCODE_FAILED; + + if(per_put_few_bits(po, *st ? 1 : 0, 1)) + ASN__ENCODE_FAILED; + + ASN__ENCODED_OK(er); +} diff --git a/asn1_test_data/BOOLEAN.h b/asn1_test_data/BOOLEAN.h new file mode 100644 index 0000000..217d0f1 --- /dev/null +++ b/asn1_test_data/BOOLEAN.h @@ -0,0 +1,36 @@ +/*- + * Copyright (c) 2003 Lev Walkin . All rights reserved. + * Redistribution and modifications are permitted subject to BSD license. + */ +#ifndef _BOOLEAN_H_ +#define _BOOLEAN_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * The underlying integer may contain various values, but everything + * non-zero is capped to 0xff by the DER encoder. The BER decoder may + * yield non-zero values different from 1, beware. + */ +typedef int BOOLEAN_t; + +extern asn_TYPE_descriptor_t asn_DEF_BOOLEAN; + +asn_struct_free_f BOOLEAN_free; +asn_struct_print_f BOOLEAN_print; +ber_type_decoder_f BOOLEAN_decode_ber; +der_type_encoder_f BOOLEAN_encode_der; +xer_type_decoder_f BOOLEAN_decode_xer; +xer_type_encoder_f BOOLEAN_encode_xer; +per_type_decoder_f BOOLEAN_decode_uper; +per_type_encoder_f BOOLEAN_encode_uper; + +#ifdef __cplusplus +} +#endif + +#endif /* _BOOLEAN_H_ */ diff --git a/asn1_test_data/CustomerInfo.c b/asn1_test_data/CustomerInfo.c new file mode 100644 index 0000000..43068a9 --- /dev/null +++ b/asn1_test_data/CustomerInfo.c @@ -0,0 +1,166 @@ +/* + * Generated by asn1c-0.9.28 (http://lionet.info/asn1c) + * From ASN.1 module "MyShopPurchaseOrders" + * found in "productExample1.asn" + */ + +#include "CustomerInfo.h" + +static int check_permitted_alphabet_2(const void *sptr) { + /* The underlying type is VisibleString */ + const VisibleString_t *st = (const VisibleString_t *)sptr; + const uint8_t *ch = st->buf; + const uint8_t *end = ch + st->size; + + for(; ch < end; ch++) { + uint8_t cv = *ch; + if(!(cv >= 32 && cv <= 126)) return -1; + } + return 0; +} + +static const int permitted_alphabet_table_4[256] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* */ + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* . */ + 2, 3, 4, 5, 6, 7, 8, 9,10,11, 0, 0, 0, 0, 0, 0, /* 0123456789 */ +}; + +static int check_permitted_alphabet_4(const void *sptr) { + const int *table = permitted_alphabet_table_4; + /* The underlying type is NumericString */ + const NumericString_t *st = (const NumericString_t *)sptr; + const uint8_t *ch = st->buf; + const uint8_t *end = ch + st->size; + + for(; ch < end; ch++) { + uint8_t cv = *ch; + if(!table[cv]) return -1; + } + return 0; +} + +static int +memb_companyName_constraint_1(asn_TYPE_descriptor_t *td, const void *sptr, + asn_app_constraint_failed_f *ctfailcb, void *app_key) { + const VisibleString_t *st = (const VisibleString_t *)sptr; + size_t size; + + if(!sptr) { + ASN__CTFAIL(app_key, td, sptr, + "%s: value not given (%s:%d)", + td->name, __FILE__, __LINE__); + return -1; + } + + size = st->size; + + if((size >= 3 && size <= 50) + && !check_permitted_alphabet_2(st)) { + /* Constraint check succeeded */ + return 0; + } else { + ASN__CTFAIL(app_key, td, sptr, + "%s: constraint failed (%s:%d)", + td->name, __FILE__, __LINE__); + return -1; + } +} + +static int +memb_contactPhone_constraint_1(asn_TYPE_descriptor_t *td, const void *sptr, + asn_app_constraint_failed_f *ctfailcb, void *app_key) { + const NumericString_t *st = (const NumericString_t *)sptr; + size_t size; + + if(!sptr) { + ASN__CTFAIL(app_key, td, sptr, + "%s: value not given (%s:%d)", + td->name, __FILE__, __LINE__); + return -1; + } + + size = st->size; + + if((size >= 7 && size <= 12) + && !check_permitted_alphabet_4(st)) { + /* Constraint check succeeded */ + return 0; + } else { + ASN__CTFAIL(app_key, td, sptr, + "%s: constraint failed (%s:%d)", + td->name, __FILE__, __LINE__); + return -1; + } +} + +static asn_TYPE_member_t asn_MBR_CustomerInfo_1[] = { + { ATF_NOFLAGS, 0, offsetof(struct CustomerInfo, companyName), + (ASN_TAG_CLASS_CONTEXT | (0 << 2)), + -1, /* IMPLICIT tag at current level */ + &asn_DEF_VisibleString, + memb_companyName_constraint_1, + 0, /* PER is not compiled, use -gen-PER */ + 0, + "companyName" + }, + { ATF_NOFLAGS, 0, offsetof(struct CustomerInfo, billingAddress), + (ASN_TAG_CLASS_CONTEXT | (1 << 2)), + -1, /* IMPLICIT tag at current level */ + &asn_DEF_Address, + 0, /* Defer constraints checking to the member type */ + 0, /* PER is not compiled, use -gen-PER */ + 0, + "billingAddress" + }, + { ATF_NOFLAGS, 0, offsetof(struct CustomerInfo, contactPhone), + (ASN_TAG_CLASS_CONTEXT | (2 << 2)), + -1, /* IMPLICIT tag at current level */ + &asn_DEF_NumericString, + memb_contactPhone_constraint_1, + 0, /* PER is not compiled, use -gen-PER */ + 0, + "contactPhone" + }, +}; +static const ber_tlv_tag_t asn_DEF_CustomerInfo_tags_1[] = { + (ASN_TAG_CLASS_UNIVERSAL | (16 << 2)) +}; +static const asn_TYPE_tag2member_t asn_MAP_CustomerInfo_tag2el_1[] = { + { (ASN_TAG_CLASS_CONTEXT | (0 << 2)), 0, 0, 0 }, /* companyName */ + { (ASN_TAG_CLASS_CONTEXT | (1 << 2)), 1, 0, 0 }, /* billingAddress */ + { (ASN_TAG_CLASS_CONTEXT | (2 << 2)), 2, 0, 0 } /* contactPhone */ +}; +static asn_SEQUENCE_specifics_t asn_SPC_CustomerInfo_specs_1 = { + sizeof(struct CustomerInfo), + offsetof(struct CustomerInfo, _asn_ctx), + asn_MAP_CustomerInfo_tag2el_1, + 3, /* Count of tags in the map */ + 0, 0, 0, /* Optional elements (not needed) */ + -1, /* Start extensions */ + -1 /* Stop extensions */ +}; +asn_TYPE_descriptor_t asn_DEF_CustomerInfo = { + "CustomerInfo", + "CustomerInfo", + SEQUENCE_free, + SEQUENCE_print, + SEQUENCE_constraint, + SEQUENCE_decode_ber, + SEQUENCE_encode_der, + SEQUENCE_decode_xer, + SEQUENCE_encode_xer, + 0, 0, /* No PER support, use "-gen-PER" to enable */ + 0, /* Use generic outmost tag fetcher */ + asn_DEF_CustomerInfo_tags_1, + sizeof(asn_DEF_CustomerInfo_tags_1) + /sizeof(asn_DEF_CustomerInfo_tags_1[0]), /* 1 */ + asn_DEF_CustomerInfo_tags_1, /* Same as above */ + sizeof(asn_DEF_CustomerInfo_tags_1) + /sizeof(asn_DEF_CustomerInfo_tags_1[0]), /* 1 */ + 0, /* No PER visible constraints */ + asn_MBR_CustomerInfo_1, + 3, /* Elements count */ + &asn_SPC_CustomerInfo_specs_1 /* Additional specs */ +}; + diff --git a/asn1_test_data/CustomerInfo.h b/asn1_test_data/CustomerInfo.h new file mode 100644 index 0000000..6bddb29 --- /dev/null +++ b/asn1_test_data/CustomerInfo.h @@ -0,0 +1,41 @@ +/* + * Generated by asn1c-0.9.28 (http://lionet.info/asn1c) + * From ASN.1 module "MyShopPurchaseOrders" + * found in "productExample1.asn" + */ + +#ifndef _CustomerInfo_H_ +#define _CustomerInfo_H_ + + +#include + +/* Including external dependencies */ +#include +#include "Address.h" +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* CustomerInfo */ +typedef struct CustomerInfo { + VisibleString_t companyName; + Address_t billingAddress; + NumericString_t contactPhone; + + /* Context for parsing across buffer boundaries */ + asn_struct_ctx_t _asn_ctx; +} CustomerInfo_t; + +/* Implementation */ +extern asn_TYPE_descriptor_t asn_DEF_CustomerInfo; + +#ifdef __cplusplus +} +#endif + +#endif /* _CustomerInfo_H_ */ +#include diff --git a/asn1_test_data/DATE.c b/asn1_test_data/DATE.c new file mode 100644 index 0000000..ce7ec2a --- /dev/null +++ b/asn1_test_data/DATE.c @@ -0,0 +1,108 @@ +/* + * Generated by asn1c-0.9.28 (http://lionet.info/asn1c) + * From ASN.1 module "MyShopPurchaseOrders" + * found in "productExample1.asn" + */ + +#include "DATE.h" + +int +DATE_constraint(asn_TYPE_descriptor_t *td, const void *sptr, + asn_app_constraint_failed_f *ctfailcb, void *app_key) { + /* Replace with underlying type checker */ + td->check_constraints = asn_DEF_NULL.check_constraints; + return td->check_constraints(td, sptr, ctfailcb, app_key); +} + +/* + * This type is implemented using NULL, + * so here we adjust the DEF accordingly. + */ +static void +DATE_1_inherit_TYPE_descriptor(asn_TYPE_descriptor_t *td) { + td->free_struct = asn_DEF_NULL.free_struct; + td->print_struct = asn_DEF_NULL.print_struct; + td->check_constraints = asn_DEF_NULL.check_constraints; + td->ber_decoder = asn_DEF_NULL.ber_decoder; + td->der_encoder = asn_DEF_NULL.der_encoder; + td->xer_decoder = asn_DEF_NULL.xer_decoder; + td->xer_encoder = asn_DEF_NULL.xer_encoder; + td->uper_decoder = asn_DEF_NULL.uper_decoder; + td->uper_encoder = asn_DEF_NULL.uper_encoder; + if(!td->per_constraints) + td->per_constraints = asn_DEF_NULL.per_constraints; + td->elements = asn_DEF_NULL.elements; + td->elements_count = asn_DEF_NULL.elements_count; + td->specifics = asn_DEF_NULL.specifics; +} + +void +DATE_free(asn_TYPE_descriptor_t *td, + void *struct_ptr, int contents_only) { + DATE_1_inherit_TYPE_descriptor(td); + td->free_struct(td, struct_ptr, contents_only); +} + +int +DATE_print(asn_TYPE_descriptor_t *td, const void *struct_ptr, + int ilevel, asn_app_consume_bytes_f *cb, void *app_key) { + DATE_1_inherit_TYPE_descriptor(td); + return td->print_struct(td, struct_ptr, ilevel, cb, app_key); +} + +asn_dec_rval_t +DATE_decode_ber(asn_codec_ctx_t *opt_codec_ctx, asn_TYPE_descriptor_t *td, + void **structure, const void *bufptr, size_t size, int tag_mode) { + DATE_1_inherit_TYPE_descriptor(td); + return td->ber_decoder(opt_codec_ctx, td, structure, bufptr, size, tag_mode); +} + +asn_enc_rval_t +DATE_encode_der(asn_TYPE_descriptor_t *td, + void *structure, int tag_mode, ber_tlv_tag_t tag, + asn_app_consume_bytes_f *cb, void *app_key) { + DATE_1_inherit_TYPE_descriptor(td); + return td->der_encoder(td, structure, tag_mode, tag, cb, app_key); +} + +asn_dec_rval_t +DATE_decode_xer(asn_codec_ctx_t *opt_codec_ctx, asn_TYPE_descriptor_t *td, + void **structure, const char *opt_mname, const void *bufptr, size_t size) { + DATE_1_inherit_TYPE_descriptor(td); + return td->xer_decoder(opt_codec_ctx, td, structure, opt_mname, bufptr, size); +} + +asn_enc_rval_t +DATE_encode_xer(asn_TYPE_descriptor_t *td, void *structure, + int ilevel, enum xer_encoder_flags_e flags, + asn_app_consume_bytes_f *cb, void *app_key) { + DATE_1_inherit_TYPE_descriptor(td); + return td->xer_encoder(td, structure, ilevel, flags, cb, app_key); +} + +static const ber_tlv_tag_t asn_DEF_DATE_tags_1[] = { + (ASN_TAG_CLASS_UNIVERSAL | (5 << 2)) +}; +asn_TYPE_descriptor_t asn_DEF_DATE = { + "DATE", + "DATE", + DATE_free, + DATE_print, + DATE_constraint, + DATE_decode_ber, + DATE_encode_der, + DATE_decode_xer, + DATE_encode_xer, + 0, 0, /* No PER support, use "-gen-PER" to enable */ + 0, /* Use generic outmost tag fetcher */ + asn_DEF_DATE_tags_1, + sizeof(asn_DEF_DATE_tags_1) + /sizeof(asn_DEF_DATE_tags_1[0]), /* 1 */ + asn_DEF_DATE_tags_1, /* Same as above */ + sizeof(asn_DEF_DATE_tags_1) + /sizeof(asn_DEF_DATE_tags_1[0]), /* 1 */ + 0, /* No PER visible constraints */ + 0, 0, /* No members */ + 0 /* No specifics */ +}; + diff --git a/asn1_test_data/DATE.h b/asn1_test_data/DATE.h new file mode 100644 index 0000000..40a3553 --- /dev/null +++ b/asn1_test_data/DATE.h @@ -0,0 +1,38 @@ +/* + * Generated by asn1c-0.9.28 (http://lionet.info/asn1c) + * From ASN.1 module "MyShopPurchaseOrders" + * found in "productExample1.asn" + */ + +#ifndef _DATE_H_ +#define _DATE_H_ + + +#include + +/* Including external dependencies */ +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* DATE */ +typedef NULL_t DATE_t; + +/* Implementation */ +extern asn_TYPE_descriptor_t asn_DEF_DATE; +asn_struct_free_f DATE_free; +asn_struct_print_f DATE_print; +asn_constr_check_f DATE_constraint; +ber_type_decoder_f DATE_decode_ber; +der_type_encoder_f DATE_encode_der; +xer_type_decoder_f DATE_decode_xer; +xer_type_encoder_f DATE_encode_xer; + +#ifdef __cplusplus +} +#endif + +#endif /* _DATE_H_ */ +#include diff --git a/asn1_test_data/ECParameters.c b/asn1_test_data/ECParameters.c new file mode 100644 index 0000000..4bcaf1e --- /dev/null +++ b/asn1_test_data/ECParameters.c @@ -0,0 +1,88 @@ +/* + * Generated by asn1c-0.9.28 (http://lionet.info/asn1c) + * From ASN.1 module "COMPOSITESIGALGS" + * found in "pqASN1_compiling.asn" + */ + +#include "ECParameters.h" + +static asn_TYPE_member_t asn_MBR_ECParameters_1[] = { + { ATF_NOFLAGS, 0, offsetof(struct ECParameters, a), + (ASN_TAG_CLASS_UNIVERSAL | (4 << 2)), + 0, + &asn_DEF_OCTET_STRING, + 0, /* Defer constraints checking to the member type */ + 0, /* PER is not compiled, use -gen-PER */ + 0, + "a" + }, + { ATF_NOFLAGS, 0, offsetof(struct ECParameters, b), + (ASN_TAG_CLASS_UNIVERSAL | (4 << 2)), + 0, + &asn_DEF_OCTET_STRING, + 0, /* Defer constraints checking to the member type */ + 0, /* PER is not compiled, use -gen-PER */ + 0, + "b" + }, + { ATF_NOFLAGS, 0, offsetof(struct ECParameters, p), + (ASN_TAG_CLASS_UNIVERSAL | (4 << 2)), + 0, + &asn_DEF_OCTET_STRING, + 0, /* Defer constraints checking to the member type */ + 0, /* PER is not compiled, use -gen-PER */ + 0, + "p" + }, + { ATF_NOFLAGS, 0, offsetof(struct ECParameters, n), + (ASN_TAG_CLASS_UNIVERSAL | (2 << 2)), + 0, + &asn_DEF_NativeInteger, + 0, /* Defer constraints checking to the member type */ + 0, /* PER is not compiled, use -gen-PER */ + 0, + "n" + }, +}; +static const ber_tlv_tag_t asn_DEF_ECParameters_tags_1[] = { + (ASN_TAG_CLASS_UNIVERSAL | (16 << 2)) +}; +static const asn_TYPE_tag2member_t asn_MAP_ECParameters_tag2el_1[] = { + { (ASN_TAG_CLASS_UNIVERSAL | (2 << 2)), 3, 0, 0 }, /* n */ + { (ASN_TAG_CLASS_UNIVERSAL | (4 << 2)), 0, 0, 2 }, /* a */ + { (ASN_TAG_CLASS_UNIVERSAL | (4 << 2)), 1, -1, 1 }, /* b */ + { (ASN_TAG_CLASS_UNIVERSAL | (4 << 2)), 2, -2, 0 } /* p */ +}; +static asn_SEQUENCE_specifics_t asn_SPC_ECParameters_specs_1 = { + sizeof(struct ECParameters), + offsetof(struct ECParameters, _asn_ctx), + asn_MAP_ECParameters_tag2el_1, + 4, /* Count of tags in the map */ + 0, 0, 0, /* Optional elements (not needed) */ + -1, /* Start extensions */ + -1 /* Stop extensions */ +}; +asn_TYPE_descriptor_t asn_DEF_ECParameters = { + "ECParameters", + "ECParameters", + SEQUENCE_free, + SEQUENCE_print, + SEQUENCE_constraint, + SEQUENCE_decode_ber, + SEQUENCE_encode_der, + SEQUENCE_decode_xer, + SEQUENCE_encode_xer, + 0, 0, /* No PER support, use "-gen-PER" to enable */ + 0, /* Use generic outmost tag fetcher */ + asn_DEF_ECParameters_tags_1, + sizeof(asn_DEF_ECParameters_tags_1) + /sizeof(asn_DEF_ECParameters_tags_1[0]), /* 1 */ + asn_DEF_ECParameters_tags_1, /* Same as above */ + sizeof(asn_DEF_ECParameters_tags_1) + /sizeof(asn_DEF_ECParameters_tags_1[0]), /* 1 */ + 0, /* No PER visible constraints */ + asn_MBR_ECParameters_1, + 4, /* Elements count */ + &asn_SPC_ECParameters_specs_1 /* Additional specs */ +}; + diff --git a/asn1_test_data/ECParameters.h b/asn1_test_data/ECParameters.h new file mode 100644 index 0000000..cbded67 --- /dev/null +++ b/asn1_test_data/ECParameters.h @@ -0,0 +1,41 @@ +/* + * Generated by asn1c-0.9.28 (http://lionet.info/asn1c) + * From ASN.1 module "COMPOSITESIGALGS" + * found in "pqASN1_compiling.asn" + */ + +#ifndef _ECParameters_H_ +#define _ECParameters_H_ + + +#include + +/* Including external dependencies */ +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* ECParameters */ +typedef struct ECParameters { + OCTET_STRING_t a; + OCTET_STRING_t b; + OCTET_STRING_t p; + long n; + + /* Context for parsing across buffer boundaries */ + asn_struct_ctx_t _asn_ctx; +} ECParameters_t; + +/* Implementation */ +extern asn_TYPE_descriptor_t asn_DEF_ECParameters; + +#ifdef __cplusplus +} +#endif + +#endif /* _ECParameters_H_ */ +#include diff --git a/asn1_test_data/ExplicitCompositeSignatureParams.c b/asn1_test_data/ExplicitCompositeSignatureParams.c new file mode 100644 index 0000000..869403a --- /dev/null +++ b/asn1_test_data/ExplicitCompositeSignatureParams.c @@ -0,0 +1,102 @@ +/* + * Generated by asn1c-0.9.28 (http://lionet.info/asn1c) + * From ASN.1 module "COMPOSITESIGALGS" + * found in "pqASN1_compiling.asn" + */ + +#include "ExplicitCompositeSignatureParams.h" + +static int +memb_firstAlgorithmParams_constraint_1(asn_TYPE_descriptor_t *td, const void *sptr, + asn_app_constraint_failed_f *ctfailcb, void *app_key) { + + if(!sptr) { + ASN__CTFAIL(app_key, td, sptr, + "%s: value not given (%s:%d)", + td->name, __FILE__, __LINE__); + return -1; + } + + + if(1 /* No applicable constraints whatsoever */) { + /* Nothing is here. See below */ + } + + return td->check_constraints(td, sptr, ctfailcb, app_key); +} + +static int +memb_secondAlgorithmParams_constraint_1(asn_TYPE_descriptor_t *td, const void *sptr, + asn_app_constraint_failed_f *ctfailcb, void *app_key) { + + if(!sptr) { + ASN__CTFAIL(app_key, td, sptr, + "%s: value not given (%s:%d)", + td->name, __FILE__, __LINE__); + return -1; + } + + + if(1 /* No applicable constraints whatsoever */) { + /* Nothing is here. See below */ + } + + return td->check_constraints(td, sptr, ctfailcb, app_key); +} + +static asn_TYPE_member_t asn_MBR_ExplicitCompositeSignatureParams_1[] = { + { ATF_OPEN_TYPE | ATF_NOFLAGS, 0, offsetof(struct ExplicitCompositeSignatureParams, firstAlgorithmParams), + -1 /* Ambiguous tag (ANY?) */, + 0, + &asn_DEF_ANY, + memb_firstAlgorithmParams_constraint_1, + 0, /* PER is not compiled, use -gen-PER */ + 0, + "firstAlgorithmParams" + }, + { ATF_OPEN_TYPE | ATF_NOFLAGS, 0, offsetof(struct ExplicitCompositeSignatureParams, secondAlgorithmParams), + -1 /* Ambiguous tag (ANY?) */, + 0, + &asn_DEF_ANY, + memb_secondAlgorithmParams_constraint_1, + 0, /* PER is not compiled, use -gen-PER */ + 0, + "secondAlgorithmParams" + }, +}; +static const ber_tlv_tag_t asn_DEF_ExplicitCompositeSignatureParams_tags_1[] = { + (ASN_TAG_CLASS_UNIVERSAL | (16 << 2)) +}; +static asn_SEQUENCE_specifics_t asn_SPC_ExplicitCompositeSignatureParams_specs_1 = { + sizeof(struct ExplicitCompositeSignatureParams), + offsetof(struct ExplicitCompositeSignatureParams, _asn_ctx), + 0, /* No top level tags */ + 0, /* No tags in the map */ + 0, 0, 0, /* Optional elements (not needed) */ + -1, /* Start extensions */ + -1 /* Stop extensions */ +}; +asn_TYPE_descriptor_t asn_DEF_ExplicitCompositeSignatureParams = { + "ExplicitCompositeSignatureParams", + "ExplicitCompositeSignatureParams", + SEQUENCE_free, + SEQUENCE_print, + SEQUENCE_constraint, + SEQUENCE_decode_ber, + SEQUENCE_encode_der, + SEQUENCE_decode_xer, + SEQUENCE_encode_xer, + 0, 0, /* No PER support, use "-gen-PER" to enable */ + 0, /* Use generic outmost tag fetcher */ + asn_DEF_ExplicitCompositeSignatureParams_tags_1, + sizeof(asn_DEF_ExplicitCompositeSignatureParams_tags_1) + /sizeof(asn_DEF_ExplicitCompositeSignatureParams_tags_1[0]), /* 1 */ + asn_DEF_ExplicitCompositeSignatureParams_tags_1, /* Same as above */ + sizeof(asn_DEF_ExplicitCompositeSignatureParams_tags_1) + /sizeof(asn_DEF_ExplicitCompositeSignatureParams_tags_1[0]), /* 1 */ + 0, /* No PER visible constraints */ + asn_MBR_ExplicitCompositeSignatureParams_1, + 2, /* Elements count */ + &asn_SPC_ExplicitCompositeSignatureParams_specs_1 /* Additional specs */ +}; + diff --git a/asn1_test_data/ExplicitCompositeSignatureParams.h b/asn1_test_data/ExplicitCompositeSignatureParams.h new file mode 100644 index 0000000..c46d0dd --- /dev/null +++ b/asn1_test_data/ExplicitCompositeSignatureParams.h @@ -0,0 +1,38 @@ +/* + * Generated by asn1c-0.9.28 (http://lionet.info/asn1c) + * From ASN.1 module "COMPOSITESIGALGS" + * found in "pqASN1_compiling.asn" + */ + +#ifndef _ExplicitCompositeSignatureParams_H_ +#define _ExplicitCompositeSignatureParams_H_ + + +#include + +/* Including external dependencies */ +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* ExplicitCompositeSignatureParams */ +typedef struct ExplicitCompositeSignatureParams { + ANY_t firstAlgorithmParams; + ANY_t secondAlgorithmParams; + + /* Context for parsing across buffer boundaries */ + asn_struct_ctx_t _asn_ctx; +} ExplicitCompositeSignatureParams_t; + +/* Implementation */ +extern asn_TYPE_descriptor_t asn_DEF_ExplicitCompositeSignatureParams; + +#ifdef __cplusplus +} +#endif + +#endif /* _ExplicitCompositeSignatureParams_H_ */ +#include diff --git a/asn1_test_data/ExplicitCompositeSignatureValue.c b/asn1_test_data/ExplicitCompositeSignatureValue.c new file mode 100644 index 0000000..07f0a48 --- /dev/null +++ b/asn1_test_data/ExplicitCompositeSignatureValue.c @@ -0,0 +1,68 @@ +/* + * Generated by asn1c-0.9.28 (http://lionet.info/asn1c) + * From ASN.1 module "COMPOSITESIGALGS" + * found in "pqASN1_compiling.asn" + */ + +#include "ExplicitCompositeSignatureValue.h" + +static asn_TYPE_member_t asn_MBR_ExplicitCompositeSignatureValue_1[] = { + { ATF_NOFLAGS, 0, offsetof(struct ExplicitCompositeSignatureValue, firstSignatureValue), + (ASN_TAG_CLASS_UNIVERSAL | (4 << 2)), + 0, + &asn_DEF_OCTET_STRING, + 0, /* Defer constraints checking to the member type */ + 0, /* PER is not compiled, use -gen-PER */ + 0, + "firstSignatureValue" + }, + { ATF_NOFLAGS, 0, offsetof(struct ExplicitCompositeSignatureValue, secondSignatureValue), + (ASN_TAG_CLASS_UNIVERSAL | (4 << 2)), + 0, + &asn_DEF_OCTET_STRING, + 0, /* Defer constraints checking to the member type */ + 0, /* PER is not compiled, use -gen-PER */ + 0, + "secondSignatureValue" + }, +}; +static const ber_tlv_tag_t asn_DEF_ExplicitCompositeSignatureValue_tags_1[] = { + (ASN_TAG_CLASS_UNIVERSAL | (16 << 2)) +}; +static const asn_TYPE_tag2member_t asn_MAP_ExplicitCompositeSignatureValue_tag2el_1[] = { + { (ASN_TAG_CLASS_UNIVERSAL | (4 << 2)), 0, 0, 1 }, /* firstSignatureValue */ + { (ASN_TAG_CLASS_UNIVERSAL | (4 << 2)), 1, -1, 0 } /* secondSignatureValue */ +}; +static asn_SEQUENCE_specifics_t asn_SPC_ExplicitCompositeSignatureValue_specs_1 = { + sizeof(struct ExplicitCompositeSignatureValue), + offsetof(struct ExplicitCompositeSignatureValue, _asn_ctx), + asn_MAP_ExplicitCompositeSignatureValue_tag2el_1, + 2, /* Count of tags in the map */ + 0, 0, 0, /* Optional elements (not needed) */ + -1, /* Start extensions */ + -1 /* Stop extensions */ +}; +asn_TYPE_descriptor_t asn_DEF_ExplicitCompositeSignatureValue = { + "ExplicitCompositeSignatureValue", + "ExplicitCompositeSignatureValue", + SEQUENCE_free, + SEQUENCE_print, + SEQUENCE_constraint, + SEQUENCE_decode_ber, + SEQUENCE_encode_der, + SEQUENCE_decode_xer, + SEQUENCE_encode_xer, + 0, 0, /* No PER support, use "-gen-PER" to enable */ + 0, /* Use generic outmost tag fetcher */ + asn_DEF_ExplicitCompositeSignatureValue_tags_1, + sizeof(asn_DEF_ExplicitCompositeSignatureValue_tags_1) + /sizeof(asn_DEF_ExplicitCompositeSignatureValue_tags_1[0]), /* 1 */ + asn_DEF_ExplicitCompositeSignatureValue_tags_1, /* Same as above */ + sizeof(asn_DEF_ExplicitCompositeSignatureValue_tags_1) + /sizeof(asn_DEF_ExplicitCompositeSignatureValue_tags_1[0]), /* 1 */ + 0, /* No PER visible constraints */ + asn_MBR_ExplicitCompositeSignatureValue_1, + 2, /* Elements count */ + &asn_SPC_ExplicitCompositeSignatureValue_specs_1 /* Additional specs */ +}; + diff --git a/asn1_test_data/ExplicitCompositeSignatureValue.h b/asn1_test_data/ExplicitCompositeSignatureValue.h new file mode 100644 index 0000000..a395ae1 --- /dev/null +++ b/asn1_test_data/ExplicitCompositeSignatureValue.h @@ -0,0 +1,38 @@ +/* + * Generated by asn1c-0.9.28 (http://lionet.info/asn1c) + * From ASN.1 module "COMPOSITESIGALGS" + * found in "pqASN1_compiling.asn" + */ + +#ifndef _ExplicitCompositeSignatureValue_H_ +#define _ExplicitCompositeSignatureValue_H_ + + +#include + +/* Including external dependencies */ +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* ExplicitCompositeSignatureValue */ +typedef struct ExplicitCompositeSignatureValue { + OCTET_STRING_t firstSignatureValue; + OCTET_STRING_t secondSignatureValue; + + /* Context for parsing across buffer boundaries */ + asn_struct_ctx_t _asn_ctx; +} ExplicitCompositeSignatureValue_t; + +/* Implementation */ +extern asn_TYPE_descriptor_t asn_DEF_ExplicitCompositeSignatureValue; + +#ifdef __cplusplus +} +#endif + +#endif /* _ExplicitCompositeSignatureValue_H_ */ +#include diff --git a/asn1_test_data/INTEGER.c b/asn1_test_data/INTEGER.c new file mode 100644 index 0000000..eed8217 --- /dev/null +++ b/asn1_test_data/INTEGER.c @@ -0,0 +1,1025 @@ +/*- + * Copyright (c) 2003-2014 Lev Walkin . + * All rights reserved. + * Redistribution and modifications are permitted subject to BSD license. + */ +#include +#include +#include /* Encoder and decoder of a primitive type */ +#include + +/* + * INTEGER basic type description. + */ +static const ber_tlv_tag_t asn_DEF_INTEGER_tags[] = { + (ASN_TAG_CLASS_UNIVERSAL | (2 << 2)) +}; +asn_TYPE_descriptor_t asn_DEF_INTEGER = { + "INTEGER", + "INTEGER", + ASN__PRIMITIVE_TYPE_free, + INTEGER_print, + asn_generic_no_constraint, + ber_decode_primitive, + INTEGER_encode_der, + INTEGER_decode_xer, + INTEGER_encode_xer, +#ifdef ASN_DISABLE_PER_SUPPORT + 0, + 0, +#else + INTEGER_decode_uper, /* Unaligned PER decoder */ + INTEGER_encode_uper, /* Unaligned PER encoder */ +#endif /* ASN_DISABLE_PER_SUPPORT */ + 0, /* Use generic outmost tag fetcher */ + asn_DEF_INTEGER_tags, + sizeof(asn_DEF_INTEGER_tags) / sizeof(asn_DEF_INTEGER_tags[0]), + asn_DEF_INTEGER_tags, /* Same as above */ + sizeof(asn_DEF_INTEGER_tags) / sizeof(asn_DEF_INTEGER_tags[0]), + 0, /* No PER visible constraints */ + 0, 0, /* No members */ + 0 /* No specifics */ +}; + +/* + * Encode INTEGER type using DER. + */ +asn_enc_rval_t +INTEGER_encode_der(asn_TYPE_descriptor_t *td, void *sptr, + int tag_mode, ber_tlv_tag_t tag, + asn_app_consume_bytes_f *cb, void *app_key) { + INTEGER_t *st = (INTEGER_t *)sptr; + + ASN_DEBUG("%s %s as INTEGER (tm=%d)", + cb?"Encoding":"Estimating", td->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) */ + + return der_encode_primitive(td, sptr, tag_mode, tag, cb, app_key); +} + +static const asn_INTEGER_enum_map_t *INTEGER_map_enum2value(asn_INTEGER_specifics_t *specs, const char *lstart, const char *lstop); + +/* + * INTEGER specific human-readable output. + */ +static ssize_t +INTEGER__dump(const asn_TYPE_descriptor_t *td, const INTEGER_t *st, asn_app_consume_bytes_f *cb, void *app_key, int plainOrXER) { + asn_INTEGER_specifics_t *specs=(asn_INTEGER_specifics_t *)td->specifics; + char scratch[32]; /* Enough for 64-bit integer */ + uint8_t *buf = st->buf; + uint8_t *buf_end = st->buf + st->size; + signed long value; + ssize_t wrote = 0; + char *p; + int ret; + + if(specs && specs->field_unsigned) + ret = asn_INTEGER2ulong(st, (unsigned long *)&value); + else + ret = asn_INTEGER2long(st, &value); + + /* Simple case: the integer size is small */ + if(ret == 0) { + const asn_INTEGER_enum_map_t *el; + size_t scrsize; + char *scr; + + el = (value >= 0 || !specs || !specs->field_unsigned) + ? INTEGER_map_value2enum(specs, value) : 0; + if(el) { + scrsize = el->enum_len + 32; + scr = (char *)alloca(scrsize); + if(plainOrXER == 0) + ret = snprintf(scr, scrsize, + "%ld (%s)", value, el->enum_name); + else + ret = snprintf(scr, scrsize, + "<%s/>", el->enum_name); + } else if(plainOrXER && specs && specs->strict_enumeration) { + ASN_DEBUG("ASN.1 forbids dealing with " + "unknown value of ENUMERATED type"); + errno = EPERM; + return -1; + } else { + scrsize = sizeof(scratch); + scr = scratch; + ret = snprintf(scr, scrsize, + (specs && specs->field_unsigned) + ?"%lu":"%ld", value); + } + assert(ret > 0 && (size_t)ret < scrsize); + return (cb(scr, ret, app_key) < 0) ? -1 : ret; + } else if(plainOrXER && specs && specs->strict_enumeration) { + /* + * Here and earlier, we cannot encode the ENUMERATED values + * if there is no corresponding identifier. + */ + ASN_DEBUG("ASN.1 forbids dealing with " + "unknown value of ENUMERATED type"); + errno = EPERM; + return -1; + } + + /* Output in the long xx:yy:zz... format */ + /* TODO: replace with generic algorithm (Knuth TAOCP Vol 2, 4.3.1) */ + for(p = scratch; buf < buf_end; buf++) { + const char * const h2c = "0123456789ABCDEF"; + if((p - scratch) >= (ssize_t)(sizeof(scratch) - 4)) { + /* Flush buffer */ + if(cb(scratch, p - scratch, app_key) < 0) + return -1; + wrote += p - scratch; + p = scratch; + } + *p++ = h2c[*buf >> 4]; + *p++ = h2c[*buf & 0x0F]; + *p++ = 0x3a; /* ":" */ + } + if(p != scratch) + p--; /* Remove the last ":" */ + + wrote += p - scratch; + return (cb(scratch, p - scratch, app_key) < 0) ? -1 : wrote; +} + +/* + * INTEGER specific human-readable output. + */ +int +INTEGER_print(asn_TYPE_descriptor_t *td, const void *sptr, int ilevel, + asn_app_consume_bytes_f *cb, void *app_key) { + const INTEGER_t *st = (const INTEGER_t *)sptr; + ssize_t ret; + + (void)td; + (void)ilevel; + + if(!st || !st->buf) + ret = cb("", 8, app_key); + else + ret = INTEGER__dump(td, st, cb, app_key, 0); + + return (ret < 0) ? -1 : 0; +} + +struct e2v_key { + const char *start; + const char *stop; + const asn_INTEGER_enum_map_t *vemap; + const unsigned int *evmap; +}; +static int +INTEGER__compar_enum2value(const void *kp, const void *am) { + const struct e2v_key *key = (const struct e2v_key *)kp; + const asn_INTEGER_enum_map_t *el = (const asn_INTEGER_enum_map_t *)am; + const char *ptr, *end, *name; + + /* Remap the element (sort by different criterion) */ + el = key->vemap + key->evmap[el - key->vemap]; + + /* Compare strings */ + for(ptr = key->start, end = key->stop, name = el->enum_name; + ptr < end; ptr++, name++) { + if(*ptr != *name) + return *(const unsigned char *)ptr + - *(const unsigned char *)name; + } + return name[0] ? -1 : 0; +} + +static const asn_INTEGER_enum_map_t * +INTEGER_map_enum2value(asn_INTEGER_specifics_t *specs, const char *lstart, const char *lstop) { + const asn_INTEGER_enum_map_t *el_found; + int count = specs ? specs->map_count : 0; + struct e2v_key key; + const char *lp; + + if(!count) return NULL; + + /* Guaranteed: assert(lstart < lstop); */ + /* Figure out the tag name */ + for(lstart++, lp = lstart; lp < lstop; lp++) { + switch(*lp) { + case 9: case 10: case 11: case 12: case 13: case 32: /* WSP */ + case 0x2f: /* '/' */ case 0x3e: /* '>' */ + break; + default: + continue; + } + break; + } + if(lp == lstop) return NULL; /* No tag found */ + lstop = lp; + + key.start = lstart; + key.stop = lstop; + key.vemap = specs->value2enum; + key.evmap = specs->enum2value; + el_found = (asn_INTEGER_enum_map_t *)bsearch(&key, + specs->value2enum, count, sizeof(specs->value2enum[0]), + INTEGER__compar_enum2value); + if(el_found) { + /* Remap enum2value into value2enum */ + el_found = key.vemap + key.evmap[el_found - key.vemap]; + } + return el_found; +} + +static int +INTEGER__compar_value2enum(const void *kp, const void *am) { + long a = *(const long *)kp; + const asn_INTEGER_enum_map_t *el = (const asn_INTEGER_enum_map_t *)am; + long b = el->nat_value; + if(a < b) return -1; + else if(a == b) return 0; + else return 1; +} + +const asn_INTEGER_enum_map_t * +INTEGER_map_value2enum(asn_INTEGER_specifics_t *specs, long value) { + int count = specs ? specs->map_count : 0; + if(!count) return 0; + return (asn_INTEGER_enum_map_t *)bsearch(&value, specs->value2enum, + count, sizeof(specs->value2enum[0]), + INTEGER__compar_value2enum); +} + +static int +INTEGER_st_prealloc(INTEGER_t *st, int min_size) { + void *p = MALLOC(min_size + 1); + if(p) { + void *b = st->buf; + st->size = 0; + st->buf = p; + FREEMEM(b); + return 0; + } else { + return -1; + } +} + +/* + * Decode the chunk of XML text encoding INTEGER. + */ +static enum xer_pbd_rval +INTEGER__xer_body_decode(asn_TYPE_descriptor_t *td, void *sptr, const void *chunk_buf, size_t chunk_size) { + INTEGER_t *st = (INTEGER_t *)sptr; + long dec_value; + long hex_value = 0; + const char *lp; + const char *lstart = (const char *)chunk_buf; + const char *lstop = lstart + chunk_size; + enum { + ST_LEADSPACE, + ST_SKIPSPHEX, + ST_WAITDIGITS, + ST_DIGITS, + ST_DIGITS_TRAILSPACE, + ST_HEXDIGIT1, + ST_HEXDIGIT2, + ST_HEXDIGITS_TRAILSPACE, + ST_HEXCOLON, + ST_END_ENUM, + ST_UNEXPECTED + } state = ST_LEADSPACE; + const char *dec_value_start = 0; /* INVARIANT: always !0 in ST_DIGITS */ + const char *dec_value_end = 0; + + if(chunk_size) + ASN_DEBUG("INTEGER body %ld 0x%2x..0x%2x", + (long)chunk_size, *lstart, lstop[-1]); + + if(INTEGER_st_prealloc(st, (chunk_size/3) + 1)) + return XPBD_SYSTEM_FAILURE; + + /* + * We may have received a tag here. It will be processed inline. + * Use strtoul()-like code and serialize the result. + */ + for(lp = lstart; lp < lstop; lp++) { + int lv = *lp; + switch(lv) { + case 0x09: case 0x0a: case 0x0d: case 0x20: + switch(state) { + case ST_LEADSPACE: + case ST_DIGITS_TRAILSPACE: + case ST_HEXDIGITS_TRAILSPACE: + case ST_SKIPSPHEX: + continue; + case ST_DIGITS: + dec_value_end = lp; + state = ST_DIGITS_TRAILSPACE; + continue; + case ST_HEXCOLON: + state = ST_HEXDIGITS_TRAILSPACE; + continue; + default: + break; + } + break; + case 0x2d: /* '-' */ + if(state == ST_LEADSPACE) { + dec_value = 0; + dec_value_start = lp; + state = ST_WAITDIGITS; + continue; + } + break; + case 0x2b: /* '+' */ + if(state == ST_LEADSPACE) { + dec_value = 0; + dec_value_start = lp; + state = ST_WAITDIGITS; + continue; + } + break; + case 0x30: case 0x31: case 0x32: case 0x33: case 0x34: + case 0x35: case 0x36: case 0x37: case 0x38: case 0x39: + switch(state) { + case ST_DIGITS: continue; + case ST_SKIPSPHEX: /* Fall through */ + case ST_HEXDIGIT1: + hex_value = (lv - 0x30) << 4; + state = ST_HEXDIGIT2; + continue; + case ST_HEXDIGIT2: + hex_value += (lv - 0x30); + state = ST_HEXCOLON; + st->buf[st->size++] = (uint8_t)hex_value; + continue; + case ST_HEXCOLON: + return XPBD_BROKEN_ENCODING; + case ST_LEADSPACE: + dec_value = 0; + dec_value_start = lp; + /* FALL THROUGH */ + case ST_WAITDIGITS: + state = ST_DIGITS; + continue; + default: + break; + } + break; + case 0x3c: /* '<', start of XML encoded enumeration */ + if(state == ST_LEADSPACE) { + const asn_INTEGER_enum_map_t *el; + el = INTEGER_map_enum2value( + (asn_INTEGER_specifics_t *) + td->specifics, lstart, lstop); + if(el) { + ASN_DEBUG("Found \"%s\" => %ld", + el->enum_name, el->nat_value); + dec_value = el->nat_value; + state = ST_END_ENUM; + lp = lstop - 1; + continue; + } + ASN_DEBUG("Unknown identifier for INTEGER"); + } + return XPBD_BROKEN_ENCODING; + case 0x3a: /* ':' */ + if(state == ST_HEXCOLON) { + /* This colon is expected */ + state = ST_HEXDIGIT1; + continue; + } else if(state == ST_DIGITS) { + /* The colon here means that we have + * decoded the first two hexadecimal + * places as a decimal value. + * Switch decoding mode. */ + ASN_DEBUG("INTEGER re-evaluate as hex form"); + state = ST_SKIPSPHEX; + dec_value_start = 0; + lp = lstart - 1; + continue; + } else { + ASN_DEBUG("state %d at %ld", state, (long)(lp - lstart)); + break; + } + /* [A-Fa-f] */ + case 0x41:case 0x42:case 0x43:case 0x44:case 0x45:case 0x46: + case 0x61:case 0x62:case 0x63:case 0x64:case 0x65:case 0x66: + switch(state) { + case ST_SKIPSPHEX: + case ST_LEADSPACE: /* Fall through */ + case ST_HEXDIGIT1: + hex_value = lv - ((lv < 0x61) ? 0x41 : 0x61); + hex_value += 10; + hex_value <<= 4; + state = ST_HEXDIGIT2; + continue; + case ST_HEXDIGIT2: + hex_value += lv - ((lv < 0x61) ? 0x41 : 0x61); + hex_value += 10; + st->buf[st->size++] = (uint8_t)hex_value; + state = ST_HEXCOLON; + continue; + case ST_DIGITS: + ASN_DEBUG("INTEGER re-evaluate as hex form"); + state = ST_SKIPSPHEX; + dec_value_start = 0; + lp = lstart - 1; + continue; + default: + break; + } + break; + } + + /* Found extra non-numeric stuff */ + ASN_DEBUG("INTEGER :: Found non-numeric 0x%2x at %ld", + lv, (long)(lp - lstart)); + state = ST_UNEXPECTED; + break; + } + + switch(state) { + case ST_END_ENUM: + /* Got a complete and valid enumeration encoded as a tag. */ + break; + case ST_DIGITS: + dec_value_end = lstop; + /* FALL THROUGH */ + case ST_DIGITS_TRAILSPACE: + /* The last symbol encountered was a digit. */ + switch(asn_strtol_lim(dec_value_start, &dec_value_end, &dec_value)) { + case ASN_STRTOL_OK: + break; + case ASN_STRTOL_ERROR_RANGE: + return XPBD_DECODER_LIMIT; + case ASN_STRTOL_ERROR_INVAL: + case ASN_STRTOL_EXPECT_MORE: + case ASN_STRTOL_EXTRA_DATA: + return XPBD_BROKEN_ENCODING; + } + break; + case ST_HEXCOLON: + case ST_HEXDIGITS_TRAILSPACE: + st->buf[st->size] = 0; /* Just in case termination */ + return XPBD_BODY_CONSUMED; + case ST_HEXDIGIT1: + case ST_HEXDIGIT2: + case ST_SKIPSPHEX: + return XPBD_BROKEN_ENCODING; + case ST_LEADSPACE: + /* Content not found */ + return XPBD_NOT_BODY_IGNORE; + case ST_WAITDIGITS: + case ST_UNEXPECTED: + ASN_DEBUG("INTEGER: No useful digits (state %d)", state); + return XPBD_BROKEN_ENCODING; /* No digits */ + } + + /* + * Convert the result of parsing of enumeration or a straight + * decimal value into a BER representation. + */ + if(asn_long2INTEGER(st, dec_value)) + return XPBD_SYSTEM_FAILURE; + + return XPBD_BODY_CONSUMED; +} + +asn_dec_rval_t +INTEGER_decode_xer(asn_codec_ctx_t *opt_codec_ctx, + asn_TYPE_descriptor_t *td, void **sptr, const char *opt_mname, + const void *buf_ptr, size_t size) { + + return xer_decode_primitive(opt_codec_ctx, td, + sptr, sizeof(INTEGER_t), opt_mname, + buf_ptr, size, INTEGER__xer_body_decode); +} + +asn_enc_rval_t +INTEGER_encode_xer(asn_TYPE_descriptor_t *td, void *sptr, + int ilevel, enum xer_encoder_flags_e flags, + asn_app_consume_bytes_f *cb, void *app_key) { + const INTEGER_t *st = (const INTEGER_t *)sptr; + asn_enc_rval_t er; + + (void)ilevel; + (void)flags; + + if(!st || !st->buf) + ASN__ENCODE_FAILED; + + er.encoded = INTEGER__dump(td, st, cb, app_key, 1); + if(er.encoded < 0) ASN__ENCODE_FAILED; + + ASN__ENCODED_OK(er); +} + +#ifndef ASN_DISABLE_PER_SUPPORT + +asn_dec_rval_t +INTEGER_decode_uper(asn_codec_ctx_t *opt_codec_ctx, asn_TYPE_descriptor_t *td, + asn_per_constraints_t *constraints, void **sptr, asn_per_data_t *pd) { + asn_INTEGER_specifics_t *specs=(asn_INTEGER_specifics_t *)td->specifics; + asn_dec_rval_t rval = { RC_OK, 0 }; + INTEGER_t *st = (INTEGER_t *)*sptr; + asn_per_constraint_t *ct; + int repeat; + + (void)opt_codec_ctx; + + if(!st) { + st = (INTEGER_t *)(*sptr = CALLOC(1, sizeof(*st))); + if(!st) ASN__DECODE_FAILED; + } + + if(!constraints) constraints = td->per_constraints; + ct = constraints ? &constraints->value : 0; + + if(ct && ct->flags & APC_EXTENSIBLE) { + int inext = per_get_few_bits(pd, 1); + if(inext < 0) ASN__DECODE_STARVED; + if(inext) ct = 0; + } + + FREEMEM(st->buf); + st->buf = 0; + st->size = 0; + if(ct) { + if(ct->flags & APC_SEMI_CONSTRAINED) { + st->buf = (uint8_t *)CALLOC(1, 2); + if(!st->buf) ASN__DECODE_FAILED; + st->size = 1; + } else if(ct->flags & APC_CONSTRAINED && ct->range_bits >= 0) { + size_t size = (ct->range_bits + 7) >> 3; + st->buf = (uint8_t *)MALLOC(1 + size + 1); + if(!st->buf) ASN__DECODE_FAILED; + st->size = size; + } + } + + /* X.691-2008/11, #13.2.2, constrained whole number */ + if(ct && ct->flags != APC_UNCONSTRAINED) { + /* #11.5.6 */ + ASN_DEBUG("Integer with range %d bits", ct->range_bits); + if(ct->range_bits >= 0) { + if((size_t)ct->range_bits > 8 * sizeof(unsigned long)) + ASN__DECODE_FAILED; + + if(specs && specs->field_unsigned) { + unsigned long uvalue; + if(uper_get_constrained_whole_number(pd, + &uvalue, ct->range_bits)) + ASN__DECODE_STARVED; + ASN_DEBUG("Got value %lu + low %ld", + uvalue, ct->lower_bound); + uvalue += ct->lower_bound; + if(asn_ulong2INTEGER(st, uvalue)) + ASN__DECODE_FAILED; + } else { + unsigned long svalue; + if(uper_get_constrained_whole_number(pd, + &svalue, ct->range_bits)) + ASN__DECODE_STARVED; + ASN_DEBUG("Got value %ld + low %ld", + svalue, ct->lower_bound); + svalue += ct->lower_bound; + if(asn_long2INTEGER(st, svalue)) + ASN__DECODE_FAILED; + } + return rval; + } + } else { + ASN_DEBUG("Decoding unconstrained integer %s", td->name); + } + + /* X.691, #12.2.3, #12.2.4 */ + do { + ssize_t len; + void *p; + int ret; + + /* Get the PER length */ + len = uper_get_length(pd, -1, &repeat); + if(len < 0) ASN__DECODE_STARVED; + + p = REALLOC(st->buf, st->size + len + 1); + if(!p) ASN__DECODE_FAILED; + st->buf = (uint8_t *)p; + + ret = per_get_many_bits(pd, &st->buf[st->size], 0, 8 * len); + if(ret < 0) ASN__DECODE_STARVED; + st->size += len; + } while(repeat); + st->buf[st->size] = 0; /* JIC */ + + /* #12.2.3 */ + if(ct && ct->lower_bound) { + /* + * TODO: replace by in-place arithmetics. + */ + long value; + if(asn_INTEGER2long(st, &value)) + ASN__DECODE_FAILED; + if(asn_long2INTEGER(st, value + ct->lower_bound)) + ASN__DECODE_FAILED; + } + + return rval; +} + +asn_enc_rval_t +INTEGER_encode_uper(asn_TYPE_descriptor_t *td, + asn_per_constraints_t *constraints, void *sptr, asn_per_outp_t *po) { + asn_INTEGER_specifics_t *specs=(asn_INTEGER_specifics_t *)td->specifics; + asn_enc_rval_t er; + INTEGER_t *st = (INTEGER_t *)sptr; + const uint8_t *buf; + const uint8_t *end; + asn_per_constraint_t *ct; + long value = 0; + unsigned long v = 0; + + if(!st || st->size == 0) ASN__ENCODE_FAILED; + + if(!constraints) constraints = td->per_constraints; + ct = constraints ? &constraints->value : 0; + + er.encoded = 0; + + if(ct) { + int inext = 0; + if(specs && specs->field_unsigned) { + unsigned long uval; + if(asn_INTEGER2ulong(st, &uval)) + ASN__ENCODE_FAILED; + /* Check proper range */ + if(ct->flags & APC_SEMI_CONSTRAINED) { + if(uval < (unsigned long)ct->lower_bound) + inext = 1; + } else if(ct->range_bits >= 0) { + if(uval < (unsigned long)ct->lower_bound + || uval > (unsigned long)ct->upper_bound) + inext = 1; + } + ASN_DEBUG("Value %lu (%02x/%d) lb %lu ub %lu %s", + uval, st->buf[0], st->size, + ct->lower_bound, ct->upper_bound, + inext ? "ext" : "fix"); + value = uval; + } else { + if(asn_INTEGER2long(st, &value)) + ASN__ENCODE_FAILED; + /* Check proper range */ + if(ct->flags & APC_SEMI_CONSTRAINED) { + if(value < ct->lower_bound) + inext = 1; + } else if(ct->range_bits >= 0) { + if(value < ct->lower_bound + || value > ct->upper_bound) + inext = 1; + } + ASN_DEBUG("Value %ld (%02x/%d) lb %ld ub %ld %s", + value, st->buf[0], st->size, + ct->lower_bound, ct->upper_bound, + inext ? "ext" : "fix"); + } + if(ct->flags & APC_EXTENSIBLE) { + if(per_put_few_bits(po, inext, 1)) + ASN__ENCODE_FAILED; + if(inext) ct = 0; + } else if(inext) { + ASN__ENCODE_FAILED; + } + } + + + /* X.691-11/2008, #13.2.2, test if constrained whole number */ + if(ct && ct->range_bits >= 0) { + /* #11.5.6 -> #11.3 */ + ASN_DEBUG("Encoding integer %ld (%lu) with range %d bits", + value, value - ct->lower_bound, ct->range_bits); + v = value - ct->lower_bound; + if(uper_put_constrained_whole_number_u(po, v, ct->range_bits)) + ASN__ENCODE_FAILED; + ASN__ENCODED_OK(er); + } + + if(ct && ct->lower_bound) { + ASN_DEBUG("Adjust lower bound to %ld", ct->lower_bound); + /* TODO: adjust lower bound */ + ASN__ENCODE_FAILED; + } + + for(buf = st->buf, end = st->buf + st->size; buf < end;) { + ssize_t mayEncode = uper_put_length(po, end - buf); + if(mayEncode < 0) + ASN__ENCODE_FAILED; + if(per_put_many_bits(po, buf, 8 * mayEncode)) + ASN__ENCODE_FAILED; + buf += mayEncode; + } + + ASN__ENCODED_OK(er); +} + +#endif /* ASN_DISABLE_PER_SUPPORT */ + +int +asn_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; +} + +int +asn_INTEGER2ulong(const INTEGER_t *iptr, unsigned long *lptr) { + uint8_t *b, *end; + unsigned long l; + size_t size; + + if(!iptr || !iptr->buf || !lptr) { + errno = EINVAL; + return -1; + } + + b = iptr->buf; + size = iptr->size; + end = b + size; + + /* If all extra leading bytes are zeroes, ignore them */ + for(; size > sizeof(unsigned long); b++, size--) { + if(*b) { + /* Value won't fit unsigned long */ + errno = ERANGE; + return -1; + } + } + + /* Conversion engine */ + for(l = 0; b < end; b++) + l = (l << 8) | *b; + + *lptr = l; + return 0; +} + +int +asn_ulong2INTEGER(INTEGER_t *st, unsigned long value) { + uint8_t *buf; + uint8_t *end; + uint8_t *b; + int shr; + + if(value <= LONG_MAX) + return asn_long2INTEGER(st, value); + + buf = (uint8_t *)MALLOC(1 + sizeof(value)); + if(!buf) return -1; + + end = buf + (sizeof(value) + 1); + buf[0] = 0; + for(b = buf + 1, shr = (sizeof(long)-1)*8; b < end; shr -= 8, b++) + *b = (uint8_t)(value >> shr); + + if(st->buf) FREEMEM(st->buf); + st->buf = buf; + st->size = 1 + sizeof(value); + + return 0; +} + +int +asn_long2INTEGER(INTEGER_t *st, long value) { + uint8_t *buf, *bp; + uint8_t *p; + uint8_t *pstart; + uint8_t *pend1; + int littleEndian = 1; /* Run-time detection */ + int add; + + if(!st) { + errno = EINVAL; + return -1; + } + + buf = (uint8_t *)MALLOC(sizeof(value)); + if(!buf) return -1; + + if(*(char *)&littleEndian) { + pstart = (uint8_t *)&value + sizeof(value) - 1; + pend1 = (uint8_t *)&value; + add = -1; + } else { + pstart = (uint8_t *)&value; + pend1 = pstart + sizeof(value) - 1; + add = 1; + } + + /* + * If the contents octet consists of more than one octet, + * then 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. + */ + for(p = pstart; p != pend1; p += add) { + switch(*p) { + case 0x00: if((*(p+add) & 0x80) == 0) + continue; + break; + case 0xff: if((*(p+add) & 0x80)) + continue; + break; + } + break; + } + /* Copy the integer body */ + for(pstart = p, bp = buf, pend1 += add; p != pend1; p += add) + *bp++ = *p; + + if(st->buf) FREEMEM(st->buf); + st->buf = buf; + st->size = bp - buf; + + return 0; +} + +/* + * This function is going to be DEPRECATED soon. + */ +enum asn_strtol_result_e +asn_strtol(const char *str, const char *end, long *lp) { + const char *endp = end; + + switch(asn_strtol_lim(str, &endp, lp)) { + case ASN_STRTOL_ERROR_RANGE: + return ASN_STRTOL_ERROR_RANGE; + case ASN_STRTOL_ERROR_INVAL: + return ASN_STRTOL_ERROR_INVAL; + case ASN_STRTOL_EXPECT_MORE: + return ASN_STRTOL_ERROR_INVAL; /* Retain old behavior */ + case ASN_STRTOL_OK: + return ASN_STRTOL_OK; + case ASN_STRTOL_EXTRA_DATA: + return ASN_STRTOL_ERROR_INVAL; /* Retain old behavior */ + } + + return ASN_STRTOL_ERROR_INVAL; /* Retain old behavior */ +} + +/* + * Parse the number in the given string until the given *end position, + * returning the position after the last parsed character back using the + * same (*end) pointer. + * WARNING: This behavior is different from the standard strtol(3). + */ +enum asn_strtol_result_e +asn_strtol_lim(const char *str, const char **end, long *lp) { + int sign = 1; + long l; + + const long upper_boundary = LONG_MAX / 10; + long last_digit_max = LONG_MAX % 10; + + if(str >= *end) return ASN_STRTOL_ERROR_INVAL; + + switch(*str) { + case '-': + last_digit_max++; + sign = -1; + /* FALL THROUGH */ + case '+': + str++; + if(str >= *end) { + *end = str; + return ASN_STRTOL_EXPECT_MORE; + } + } + + for(l = 0; str < (*end); str++) { + switch(*str) { + case 0x30: case 0x31: case 0x32: case 0x33: case 0x34: + case 0x35: case 0x36: case 0x37: case 0x38: case 0x39: { + int d = *str - '0'; + if(l < upper_boundary) { + l = l * 10 + d; + } else if(l == upper_boundary) { + if(d <= last_digit_max) { + if(sign > 0) { + l = l * 10 + d; + } else { + sign = 1; + l = -l * 10 - d; + } + } else { + *end = str; + return ASN_STRTOL_ERROR_RANGE; + } + } else { + *end = str; + return ASN_STRTOL_ERROR_RANGE; + } + } + continue; + default: + *end = str; + *lp = sign * l; + return ASN_STRTOL_EXTRA_DATA; + } + } + + *end = str; + *lp = sign * l; + return ASN_STRTOL_OK; +} + diff --git a/asn1_test_data/INTEGER.h b/asn1_test_data/INTEGER.h new file mode 100644 index 0000000..9a88097 --- /dev/null +++ b/asn1_test_data/INTEGER.h @@ -0,0 +1,82 @@ +/*- + * Copyright (c) 2003, 2005 Lev Walkin . All rights reserved. + * Redistribution and modifications are permitted subject to BSD license. + */ +#ifndef _INTEGER_H_ +#define _INTEGER_H_ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef ASN__PRIMITIVE_TYPE_t INTEGER_t; + +extern asn_TYPE_descriptor_t asn_DEF_INTEGER; + +/* Map with to integer value association */ +typedef struct asn_INTEGER_enum_map_s { + long nat_value; /* associated native integer value */ + size_t enum_len; /* strlen("tag") */ + const char *enum_name; /* "tag" */ +} asn_INTEGER_enum_map_t; + +/* This type describes an enumeration for INTEGER and ENUMERATED types */ +typedef const struct asn_INTEGER_specifics_s { + const asn_INTEGER_enum_map_t *value2enum; /* N -> "tag"; sorted by N */ + const unsigned int *enum2value; /* "tag" => N; sorted by tag */ + int map_count; /* Elements in either map */ + int extension; /* This map is extensible */ + int strict_enumeration; /* Enumeration set is fixed */ + int field_width; /* Size of native integer */ + int field_unsigned; /* Signed=0, unsigned=1 */ +} asn_INTEGER_specifics_t; + +asn_struct_print_f INTEGER_print; +ber_type_decoder_f INTEGER_decode_ber; +der_type_encoder_f INTEGER_encode_der; +xer_type_decoder_f INTEGER_decode_xer; +xer_type_encoder_f INTEGER_encode_xer; +per_type_decoder_f INTEGER_decode_uper; +per_type_encoder_f INTEGER_encode_uper; + +/*********************************** + * 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 + * -1/ENOMEM: Memory allocation failed (in asn_long2INTEGER()). + */ +int asn_INTEGER2long(const INTEGER_t *i, long *l); +int asn_INTEGER2ulong(const INTEGER_t *i, unsigned long *l); +int asn_long2INTEGER(INTEGER_t *i, long l); +int asn_ulong2INTEGER(INTEGER_t *i, unsigned long l); + +/* A a reified version of strtol(3) with nicer error reporting. */ +enum asn_strtol_result_e { + ASN_STRTOL_ERROR_RANGE = -3, /* Input outside of numeric range for long type */ + ASN_STRTOL_ERROR_INVAL = -2, /* Invalid data encountered (e.g., "+-") */ + ASN_STRTOL_EXPECT_MORE = -1, /* More data expected (e.g. "+") */ + ASN_STRTOL_OK = 0, /* Conversion succeded, number ends at (*end) */ + ASN_STRTOL_EXTRA_DATA = 1 /* Conversion succeded, but the string has extra stuff */ +}; +enum asn_strtol_result_e asn_strtol_lim(const char *str, const char **end, long *l); + +/* The asn_strtol is going to be DEPRECATED soon */ +enum asn_strtol_result_e asn_strtol(const char *str, const char *end, long *l); + +/* + * Convert the integer value into the corresponding enumeration map entry. + */ +const asn_INTEGER_enum_map_t *INTEGER_map_value2enum(asn_INTEGER_specifics_t *specs, long value); + +#ifdef __cplusplus +} +#endif + +#endif /* _INTEGER_H_ */ diff --git a/asn1_test_data/Item.c b/asn1_test_data/Item.c new file mode 100644 index 0000000..e224275 --- /dev/null +++ b/asn1_test_data/Item.c @@ -0,0 +1,274 @@ +/* + * Generated by asn1c-0.9.28 (http://lionet.info/asn1c) + * From ASN.1 module "MyShopPurchaseOrders" + * found in "productExample1.asn" + */ + +#include "Item.h" + +static int check_permitted_alphabet_3(const void *sptr) { + /* The underlying type is VisibleString */ + const VisibleString_t *st = (const VisibleString_t *)sptr; + const uint8_t *ch = st->buf; + const uint8_t *end = ch + st->size; + + for(; ch < end; ch++) { + uint8_t cv = *ch; + if(!(cv >= 32 && cv <= 126)) return -1; + } + return 0; +} + +static int +memb_itemCode_constraint_1(asn_TYPE_descriptor_t *td, const void *sptr, + asn_app_constraint_failed_f *ctfailcb, void *app_key) { + long value; + + if(!sptr) { + ASN__CTFAIL(app_key, td, sptr, + "%s: value not given (%s:%d)", + td->name, __FILE__, __LINE__); + return -1; + } + + value = *(const long *)sptr; + + if((value >= 1 && value <= 99999)) { + /* Constraint check succeeded */ + return 0; + } else { + ASN__CTFAIL(app_key, td, sptr, + "%s: constraint failed (%s:%d)", + td->name, __FILE__, __LINE__); + return -1; + } +} + +static int +memb_color_constraint_1(asn_TYPE_descriptor_t *td, const void *sptr, + asn_app_constraint_failed_f *ctfailcb, void *app_key) { + const VisibleString_t *st = (const VisibleString_t *)sptr; + + if(!sptr) { + ASN__CTFAIL(app_key, td, sptr, + "%s: value not given (%s:%d)", + td->name, __FILE__, __LINE__); + return -1; + } + + + if(!check_permitted_alphabet_3(st)) { + /* Constraint check succeeded */ + return 0; + } else { + ASN__CTFAIL(app_key, td, sptr, + "%s: constraint failed (%s:%d)", + td->name, __FILE__, __LINE__); + return -1; + } +} + +static int +memb_power_constraint_1(asn_TYPE_descriptor_t *td, const void *sptr, + asn_app_constraint_failed_f *ctfailcb, void *app_key) { + long value; + + if(!sptr) { + ASN__CTFAIL(app_key, td, sptr, + "%s: value not given (%s:%d)", + td->name, __FILE__, __LINE__); + return -1; + } + + value = *(const long *)sptr; + + if(((value == 110) || (value == 220))) { + /* Constraint check succeeded */ + return 0; + } else { + ASN__CTFAIL(app_key, td, sptr, + "%s: constraint failed (%s:%d)", + td->name, __FILE__, __LINE__); + return -1; + } +} + +static int +memb_deliveryTime_constraint_1(asn_TYPE_descriptor_t *td, const void *sptr, + asn_app_constraint_failed_f *ctfailcb, void *app_key) { + long value; + + if(!sptr) { + ASN__CTFAIL(app_key, td, sptr, + "%s: value not given (%s:%d)", + td->name, __FILE__, __LINE__); + return -1; + } + + value = *(const long *)sptr; + + if(((value >= 8 && value <= 12) || (value >= 14 && value <= 19))) { + /* Constraint check succeeded */ + return 0; + } else { + ASN__CTFAIL(app_key, td, sptr, + "%s: constraint failed (%s:%d)", + td->name, __FILE__, __LINE__); + return -1; + } +} + +static int +memb_quantity_constraint_1(asn_TYPE_descriptor_t *td, const void *sptr, + asn_app_constraint_failed_f *ctfailcb, void *app_key) { + long value; + + if(!sptr) { + ASN__CTFAIL(app_key, td, sptr, + "%s: value not given (%s:%d)", + td->name, __FILE__, __LINE__); + return -1; + } + + value = *(const long *)sptr; + + if((value >= 1 && value <= 1000)) { + /* Constraint check succeeded */ + return 0; + } else { + ASN__CTFAIL(app_key, td, sptr, + "%s: constraint failed (%s:%d)", + td->name, __FILE__, __LINE__); + return -1; + } +} + +static int +memb_unitPrice_constraint_1(asn_TYPE_descriptor_t *td, const void *sptr, + asn_app_constraint_failed_f *ctfailcb, void *app_key) { + + if(!sptr) { + ASN__CTFAIL(app_key, td, sptr, + "%s: value not given (%s:%d)", + td->name, __FILE__, __LINE__); + return -1; + } + + + if(1 /* No applicable constraints whatsoever */) { + /* Nothing is here. See below */ + } + + return td->check_constraints(td, sptr, ctfailcb, app_key); +} + +static asn_TYPE_member_t asn_MBR_Item_1[] = { + { ATF_NOFLAGS, 0, offsetof(struct Item, itemCode), + (ASN_TAG_CLASS_CONTEXT | (0 << 2)), + -1, /* IMPLICIT tag at current level */ + &asn_DEF_NativeInteger, + memb_itemCode_constraint_1, + 0, /* PER is not compiled, use -gen-PER */ + 0, + "itemCode" + }, + { ATF_NOFLAGS, 0, offsetof(struct Item, color), + (ASN_TAG_CLASS_CONTEXT | (1 << 2)), + -1, /* IMPLICIT tag at current level */ + &asn_DEF_VisibleString, + memb_color_constraint_1, + 0, /* PER is not compiled, use -gen-PER */ + 0, + "color" + }, + { ATF_NOFLAGS, 0, offsetof(struct Item, power), + (ASN_TAG_CLASS_CONTEXT | (2 << 2)), + -1, /* IMPLICIT tag at current level */ + &asn_DEF_NativeInteger, + memb_power_constraint_1, + 0, /* PER is not compiled, use -gen-PER */ + 0, + "power" + }, + { ATF_NOFLAGS, 0, offsetof(struct Item, deliveryTime), + (ASN_TAG_CLASS_CONTEXT | (3 << 2)), + -1, /* IMPLICIT tag at current level */ + &asn_DEF_NativeInteger, + memb_deliveryTime_constraint_1, + 0, /* PER is not compiled, use -gen-PER */ + 0, + "deliveryTime" + }, + { ATF_NOFLAGS, 0, offsetof(struct Item, quantity), + (ASN_TAG_CLASS_CONTEXT | (4 << 2)), + -1, /* IMPLICIT tag at current level */ + &asn_DEF_NativeInteger, + memb_quantity_constraint_1, + 0, /* PER is not compiled, use -gen-PER */ + 0, + "quantity" + }, + { ATF_NOFLAGS, 0, offsetof(struct Item, unitPrice), + (ASN_TAG_CLASS_CONTEXT | (5 << 2)), + -1, /* IMPLICIT tag at current level */ + &asn_DEF_NativeReal, + memb_unitPrice_constraint_1, + 0, /* PER is not compiled, use -gen-PER */ + 0, + "unitPrice" + }, + { ATF_NOFLAGS, 0, offsetof(struct Item, isTaxable), + (ASN_TAG_CLASS_CONTEXT | (6 << 2)), + -1, /* IMPLICIT tag at current level */ + &asn_DEF_BOOLEAN, + 0, /* Defer constraints checking to the member type */ + 0, /* PER is not compiled, use -gen-PER */ + 0, + "isTaxable" + }, +}; +static const ber_tlv_tag_t asn_DEF_Item_tags_1[] = { + (ASN_TAG_CLASS_UNIVERSAL | (16 << 2)) +}; +static const asn_TYPE_tag2member_t asn_MAP_Item_tag2el_1[] = { + { (ASN_TAG_CLASS_CONTEXT | (0 << 2)), 0, 0, 0 }, /* itemCode */ + { (ASN_TAG_CLASS_CONTEXT | (1 << 2)), 1, 0, 0 }, /* color */ + { (ASN_TAG_CLASS_CONTEXT | (2 << 2)), 2, 0, 0 }, /* power */ + { (ASN_TAG_CLASS_CONTEXT | (3 << 2)), 3, 0, 0 }, /* deliveryTime */ + { (ASN_TAG_CLASS_CONTEXT | (4 << 2)), 4, 0, 0 }, /* quantity */ + { (ASN_TAG_CLASS_CONTEXT | (5 << 2)), 5, 0, 0 }, /* unitPrice */ + { (ASN_TAG_CLASS_CONTEXT | (6 << 2)), 6, 0, 0 } /* isTaxable */ +}; +static asn_SEQUENCE_specifics_t asn_SPC_Item_specs_1 = { + sizeof(struct Item), + offsetof(struct Item, _asn_ctx), + asn_MAP_Item_tag2el_1, + 7, /* Count of tags in the map */ + 0, 0, 0, /* Optional elements (not needed) */ + -1, /* Start extensions */ + -1 /* Stop extensions */ +}; +asn_TYPE_descriptor_t asn_DEF_Item = { + "Item", + "Item", + SEQUENCE_free, + SEQUENCE_print, + SEQUENCE_constraint, + SEQUENCE_decode_ber, + SEQUENCE_encode_der, + SEQUENCE_decode_xer, + SEQUENCE_encode_xer, + 0, 0, /* No PER support, use "-gen-PER" to enable */ + 0, /* Use generic outmost tag fetcher */ + asn_DEF_Item_tags_1, + sizeof(asn_DEF_Item_tags_1) + /sizeof(asn_DEF_Item_tags_1[0]), /* 1 */ + asn_DEF_Item_tags_1, /* Same as above */ + sizeof(asn_DEF_Item_tags_1) + /sizeof(asn_DEF_Item_tags_1[0]), /* 1 */ + 0, /* No PER visible constraints */ + asn_MBR_Item_1, + 7, /* Elements count */ + &asn_SPC_Item_specs_1 /* Additional specs */ +}; + diff --git a/asn1_test_data/Item.h b/asn1_test_data/Item.h new file mode 100644 index 0000000..e02dc2b --- /dev/null +++ b/asn1_test_data/Item.h @@ -0,0 +1,46 @@ +/* + * Generated by asn1c-0.9.28 (http://lionet.info/asn1c) + * From ASN.1 module "MyShopPurchaseOrders" + * found in "productExample1.asn" + */ + +#ifndef _Item_H_ +#define _Item_H_ + + +#include + +/* Including external dependencies */ +#include +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* Item */ +typedef struct Item { + long itemCode; + VisibleString_t color; + long power; + long deliveryTime; + long quantity; + double unitPrice; + BOOLEAN_t isTaxable; + + /* Context for parsing across buffer boundaries */ + asn_struct_ctx_t _asn_ctx; +} Item_t; + +/* Implementation */ +extern asn_TYPE_descriptor_t asn_DEF_Item; + +#ifdef __cplusplus +} +#endif + +#endif /* _Item_H_ */ +#include diff --git a/asn1_test_data/ListOfItems.c b/asn1_test_data/ListOfItems.c new file mode 100644 index 0000000..70b4a0c --- /dev/null +++ b/asn1_test_data/ListOfItems.c @@ -0,0 +1,51 @@ +/* + * Generated by asn1c-0.9.28 (http://lionet.info/asn1c) + * From ASN.1 module "MyShopPurchaseOrders" + * found in "productExample1.asn" + */ + +#include "ListOfItems.h" + +static asn_TYPE_member_t asn_MBR_ListOfItems_1[] = { + { ATF_POINTER, 0, 0, + (ASN_TAG_CLASS_UNIVERSAL | (16 << 2)), + 0, + &asn_DEF_Item, + 0, /* Defer constraints checking to the member type */ + 0, /* PER is not compiled, use -gen-PER */ + 0, + "" + }, +}; +static const ber_tlv_tag_t asn_DEF_ListOfItems_tags_1[] = { + (ASN_TAG_CLASS_UNIVERSAL | (16 << 2)) +}; +static asn_SET_OF_specifics_t asn_SPC_ListOfItems_specs_1 = { + sizeof(struct ListOfItems), + offsetof(struct ListOfItems, _asn_ctx), + 0, /* XER encoding is XMLDelimitedItemList */ +}; +asn_TYPE_descriptor_t asn_DEF_ListOfItems = { + "ListOfItems", + "ListOfItems", + SEQUENCE_OF_free, + SEQUENCE_OF_print, + SEQUENCE_OF_constraint, + SEQUENCE_OF_decode_ber, + SEQUENCE_OF_encode_der, + SEQUENCE_OF_decode_xer, + SEQUENCE_OF_encode_xer, + 0, 0, /* No PER support, use "-gen-PER" to enable */ + 0, /* Use generic outmost tag fetcher */ + asn_DEF_ListOfItems_tags_1, + sizeof(asn_DEF_ListOfItems_tags_1) + /sizeof(asn_DEF_ListOfItems_tags_1[0]), /* 1 */ + asn_DEF_ListOfItems_tags_1, /* Same as above */ + sizeof(asn_DEF_ListOfItems_tags_1) + /sizeof(asn_DEF_ListOfItems_tags_1[0]), /* 1 */ + 0, /* No PER visible constraints */ + asn_MBR_ListOfItems_1, + 1, /* Single element */ + &asn_SPC_ListOfItems_specs_1 /* Additional specs */ +}; + diff --git a/asn1_test_data/ListOfItems.h b/asn1_test_data/ListOfItems.h new file mode 100644 index 0000000..ed69f2c --- /dev/null +++ b/asn1_test_data/ListOfItems.h @@ -0,0 +1,43 @@ +/* + * Generated by asn1c-0.9.28 (http://lionet.info/asn1c) + * From ASN.1 module "MyShopPurchaseOrders" + * found in "productExample1.asn" + */ + +#ifndef _ListOfItems_H_ +#define _ListOfItems_H_ + + +#include + +/* Including external dependencies */ +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* Forward declarations */ +struct Item; + +/* ListOfItems */ +typedef struct ListOfItems { + A_SEQUENCE_OF(struct Item) list; + + /* Context for parsing across buffer boundaries */ + asn_struct_ctx_t _asn_ctx; +} ListOfItems_t; + +/* Implementation */ +extern asn_TYPE_descriptor_t asn_DEF_ListOfItems; + +#ifdef __cplusplus +} +#endif + +/* Referred external types */ +#include "Item.h" + +#endif /* _ListOfItems_H_ */ +#include diff --git a/asn1_test_data/Makefile.am.sample b/asn1_test_data/Makefile.am.sample new file mode 100644 index 0000000..1b30f9c --- /dev/null +++ b/asn1_test_data/Makefile.am.sample @@ -0,0 +1,112 @@ +ASN_MODULE_SOURCES= \ + DATE.c \ + PurchaseOrder.c \ + CustomerInfo.c \ + Address.c \ + ListOfItems.c \ + Item.c + +ASN_MODULE_HEADERS= \ + DATE.h \ + PurchaseOrder.h \ + CustomerInfo.h \ + Address.h \ + ListOfItems.h \ + Item.h + +ASN_MODULE_HEADERS+=BOOLEAN.h +ASN_MODULE_SOURCES+=BOOLEAN.c +ASN_MODULE_HEADERS+=INTEGER.h +ASN_MODULE_HEADERS+=NativeEnumerated.h +ASN_MODULE_SOURCES+=INTEGER.c +ASN_MODULE_HEADERS+=NULL.h +ASN_MODULE_SOURCES+=NULL.c +ASN_MODULE_SOURCES+=NativeEnumerated.c +ASN_MODULE_HEADERS+=NativeInteger.h +ASN_MODULE_SOURCES+=NativeInteger.c +ASN_MODULE_HEADERS+=NativeReal.h +ASN_MODULE_SOURCES+=NativeReal.c +ASN_MODULE_HEADERS+=REAL.h +ASN_MODULE_HEADERS+=NumericString.h +ASN_MODULE_SOURCES+=NumericString.c +ASN_MODULE_SOURCES+=REAL.c +ASN_MODULE_HEADERS+=VisibleString.h +ASN_MODULE_SOURCES+=VisibleString.c +ASN_MODULE_HEADERS+=asn_SEQUENCE_OF.h +ASN_MODULE_SOURCES+=asn_SEQUENCE_OF.c +ASN_MODULE_HEADERS+=asn_SET_OF.h +ASN_MODULE_SOURCES+=asn_SET_OF.c +ASN_MODULE_HEADERS+=constr_SEQUENCE.h +ASN_MODULE_SOURCES+=constr_SEQUENCE.c +ASN_MODULE_HEADERS+=constr_SEQUENCE_OF.h +ASN_MODULE_SOURCES+=constr_SEQUENCE_OF.c +ASN_MODULE_HEADERS+=constr_SET_OF.h +ASN_MODULE_SOURCES+=constr_SET_OF.c +ASN_MODULE_HEADERS+=asn_application.h +ASN_MODULE_HEADERS+=asn_system.h +ASN_MODULE_HEADERS+=asn_codecs.h +ASN_MODULE_HEADERS+=asn_internal.h +ASN_MODULE_HEADERS+=OCTET_STRING.h +ASN_MODULE_SOURCES+=OCTET_STRING.c +ASN_MODULE_HEADERS+=BIT_STRING.h +ASN_MODULE_SOURCES+=BIT_STRING.c +ASN_MODULE_SOURCES+=asn_codecs_prim.c +ASN_MODULE_HEADERS+=asn_codecs_prim.h +ASN_MODULE_HEADERS+=ber_tlv_length.h +ASN_MODULE_SOURCES+=ber_tlv_length.c +ASN_MODULE_HEADERS+=ber_tlv_tag.h +ASN_MODULE_SOURCES+=ber_tlv_tag.c +ASN_MODULE_HEADERS+=ber_decoder.h +ASN_MODULE_SOURCES+=ber_decoder.c +ASN_MODULE_HEADERS+=der_encoder.h +ASN_MODULE_SOURCES+=der_encoder.c +ASN_MODULE_HEADERS+=constr_TYPE.h +ASN_MODULE_SOURCES+=constr_TYPE.c +ASN_MODULE_HEADERS+=constraints.h +ASN_MODULE_SOURCES+=constraints.c +ASN_MODULE_HEADERS+=xer_support.h +ASN_MODULE_SOURCES+=xer_support.c +ASN_MODULE_HEADERS+=xer_decoder.h +ASN_MODULE_SOURCES+=xer_decoder.c +ASN_MODULE_HEADERS+=xer_encoder.h +ASN_MODULE_SOURCES+=xer_encoder.c +ASN_MODULE_HEADERS+=per_support.h +ASN_MODULE_SOURCES+=per_support.c +ASN_MODULE_HEADERS+=per_decoder.h +ASN_MODULE_SOURCES+=per_decoder.c +ASN_MODULE_HEADERS+=per_encoder.h +ASN_MODULE_SOURCES+=per_encoder.c +ASN_MODULE_HEADERS+=per_opentype.h +ASN_MODULE_SOURCES+=per_opentype.c +ASN_CONVERTER_SOURCES+=converter-sample.c + + +lib_LTLIBRARIES=libsomething.la +libsomething_la_SOURCES=$(ASN_MODULE_SOURCES) $(ASN_MODULE_HEADERS) + +# This file may be used as an input for make(3) +# Remove the lines below to convert it into a pure .am file +TARGET = progname +CFLAGS += -I. +OBJS=${ASN_MODULE_SOURCES:.c=.o} ${ASN_CONVERTER_SOURCES:.c=.o} + +all: $(TARGET) + +$(TARGET): ${OBJS} + $(CC) $(CFLAGS) -o $(TARGET) ${OBJS} $(LDFLAGS) $(LIBS) + +.SUFFIXES: +.SUFFIXES: .c .o + +.c.o: + $(CC) $(CFLAGS) -o $@ -c $< + +clean: + rm -f $(TARGET) + rm -f $(OBJS) + +regen: regenerate-from-asn1-source + +regenerate-from-asn1-source: + asn1c productExample1.asn + diff --git a/asn1_test_data/NULL.c b/asn1_test_data/NULL.c new file mode 100644 index 0000000..62bc91e --- /dev/null +++ b/asn1_test_data/NULL.c @@ -0,0 +1,151 @@ +/*- + * Copyright (c) 2003, 2005 Lev Walkin . All rights reserved. + * Redistribution and modifications are permitted subject to BSD license. + */ +#include +#include +#include +#include /* Implemented in terms of BOOLEAN type */ + +/* + * NULL basic type description. + */ +static const ber_tlv_tag_t asn_DEF_NULL_tags[] = { + (ASN_TAG_CLASS_UNIVERSAL | (5 << 2)) +}; +asn_TYPE_descriptor_t asn_DEF_NULL = { + "NULL", + "NULL", + BOOLEAN_free, + NULL_print, + asn_generic_no_constraint, + BOOLEAN_decode_ber, /* Implemented in terms of BOOLEAN */ + NULL_encode_der, /* Special handling of DER encoding */ + NULL_decode_xer, + NULL_encode_xer, + NULL_decode_uper, /* Unaligned PER decoder */ + NULL_encode_uper, /* Unaligned PER encoder */ + 0, /* Use generic outmost tag fetcher */ + asn_DEF_NULL_tags, + sizeof(asn_DEF_NULL_tags) / sizeof(asn_DEF_NULL_tags[0]), + asn_DEF_NULL_tags, /* Same as above */ + sizeof(asn_DEF_NULL_tags) / sizeof(asn_DEF_NULL_tags[0]), + 0, /* No PER visible constraints */ + 0, 0, /* No members */ + 0 /* No specifics */ +}; + +asn_enc_rval_t +NULL_encode_der(asn_TYPE_descriptor_t *td, void *ptr, + int tag_mode, ber_tlv_tag_t tag, + asn_app_consume_bytes_f *cb, void *app_key) { + asn_enc_rval_t erval; + + erval.encoded = der_write_tags(td, 0, tag_mode, 0, tag, cb, app_key); + if(erval.encoded == -1) { + erval.failed_type = td; + erval.structure_ptr = ptr; + } + + ASN__ENCODED_OK(erval); +} + +asn_enc_rval_t +NULL_encode_xer(asn_TYPE_descriptor_t *td, void *sptr, + int ilevel, enum xer_encoder_flags_e flags, + asn_app_consume_bytes_f *cb, void *app_key) { + asn_enc_rval_t er; + + (void)td; + (void)sptr; + (void)ilevel; + (void)flags; + (void)cb; + (void)app_key; + + /* XMLNullValue is empty */ + er.encoded = 0; + ASN__ENCODED_OK(er); +} + + +static enum xer_pbd_rval +NULL__xer_body_decode(asn_TYPE_descriptor_t *td, void *sptr, const void *chunk_buf, size_t chunk_size) { + (void)td; + (void)sptr; + (void)chunk_buf; /* Going to be empty according to the rules below. */ + + /* + * There must be no content in self-terminating tag. + */ + if(chunk_size) + return XPBD_BROKEN_ENCODING; + else + return XPBD_BODY_CONSUMED; +} + +asn_dec_rval_t +NULL_decode_xer(asn_codec_ctx_t *opt_codec_ctx, + asn_TYPE_descriptor_t *td, void **sptr, const char *opt_mname, + const void *buf_ptr, size_t size) { + + return xer_decode_primitive(opt_codec_ctx, td, + sptr, sizeof(NULL_t), opt_mname, buf_ptr, size, + NULL__xer_body_decode); +} + +int +NULL_print(asn_TYPE_descriptor_t *td, const void *sptr, int ilevel, + asn_app_consume_bytes_f *cb, void *app_key) { + + (void)td; /* Unused argument */ + (void)ilevel; /* Unused argument */ + + if(sptr) { + return (cb("", 9, app_key) < 0) ? -1 : 0; + } else { + return (cb("", 8, app_key) < 0) ? -1 : 0; + } +} + +asn_dec_rval_t +NULL_decode_uper(asn_codec_ctx_t *opt_codec_ctx, asn_TYPE_descriptor_t *td, + asn_per_constraints_t *constraints, void **sptr, asn_per_data_t *pd) { + asn_dec_rval_t rv; + + (void)opt_codec_ctx; + (void)td; + (void)constraints; + (void)pd; + + if(!*sptr) { + *sptr = MALLOC(sizeof(NULL_t)); + if(*sptr) { + *(NULL_t *)*sptr = 0; + } else { + ASN__DECODE_FAILED; + } + } + + /* + * NULL type does not have content octets. + */ + + rv.code = RC_OK; + rv.consumed = 0; + return rv; +} + +asn_enc_rval_t +NULL_encode_uper(asn_TYPE_descriptor_t *td, asn_per_constraints_t *constraints, + void *sptr, asn_per_outp_t *po) { + asn_enc_rval_t er; + + (void)td; + (void)constraints; + (void)sptr; + (void)po; + + er.encoded = 0; + ASN__ENCODED_OK(er); +} diff --git a/asn1_test_data/NULL.h b/asn1_test_data/NULL.h new file mode 100644 index 0000000..131e775 --- /dev/null +++ b/asn1_test_data/NULL.h @@ -0,0 +1,33 @@ +/*- + * Copyright (c) 2003 Lev Walkin . All rights reserved. + * Redistribution and modifications are permitted subject to BSD license. + */ +#ifndef ASN_TYPE_NULL_H +#define ASN_TYPE_NULL_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * The value of the NULL type is meaningless: see BOOLEAN if you want to + * carry true/false semantics. + */ +typedef int NULL_t; + +extern asn_TYPE_descriptor_t asn_DEF_NULL; + +asn_struct_print_f NULL_print; +der_type_encoder_f NULL_encode_der; +xer_type_decoder_f NULL_decode_xer; +xer_type_encoder_f NULL_encode_xer; +per_type_decoder_f NULL_decode_uper; +per_type_encoder_f NULL_encode_uper; + +#ifdef __cplusplus +} +#endif + +#endif /* NULL_H */ diff --git a/asn1_test_data/NativeEnumerated.c b/asn1_test_data/NativeEnumerated.c new file mode 100644 index 0000000..78366af --- /dev/null +++ b/asn1_test_data/NativeEnumerated.c @@ -0,0 +1,207 @@ +/*- + * Copyright (c) 2004, 2007 Lev Walkin . All rights reserved. + * Redistribution and modifications are permitted subject to BSD license. + */ +/* + * 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 +#include + +/* + * NativeEnumerated basic type description. + */ +static const ber_tlv_tag_t asn_DEF_NativeEnumerated_tags[] = { + (ASN_TAG_CLASS_UNIVERSAL | (10 << 2)) +}; +asn_TYPE_descriptor_t asn_DEF_NativeEnumerated = { + "ENUMERATED", /* The ASN.1 type is still ENUMERATED */ + "ENUMERATED", + NativeInteger_free, + NativeInteger_print, + asn_generic_no_constraint, + NativeInteger_decode_ber, + NativeInteger_encode_der, + NativeInteger_decode_xer, + NativeEnumerated_encode_xer, + NativeEnumerated_decode_uper, + NativeEnumerated_encode_uper, + 0, /* Use generic outmost tag fetcher */ + asn_DEF_NativeEnumerated_tags, + sizeof(asn_DEF_NativeEnumerated_tags) / sizeof(asn_DEF_NativeEnumerated_tags[0]), + asn_DEF_NativeEnumerated_tags, /* Same as above */ + sizeof(asn_DEF_NativeEnumerated_tags) / sizeof(asn_DEF_NativeEnumerated_tags[0]), + 0, /* No PER visible constraints */ + 0, 0, /* No members */ + 0 /* No specifics */ +}; + +asn_enc_rval_t +NativeEnumerated_encode_xer(asn_TYPE_descriptor_t *td, void *sptr, + int ilevel, enum xer_encoder_flags_e flags, + asn_app_consume_bytes_f *cb, void *app_key) { + asn_INTEGER_specifics_t *specs=(asn_INTEGER_specifics_t *)td->specifics; + asn_enc_rval_t er; + const long *native = (const long *)sptr; + const asn_INTEGER_enum_map_t *el; + + (void)ilevel; + (void)flags; + + if(!native) ASN__ENCODE_FAILED; + + el = INTEGER_map_value2enum(specs, *native); + if(el) { + size_t srcsize = el->enum_len + 5; + char *src = (char *)alloca(srcsize); + + er.encoded = snprintf(src, srcsize, "<%s/>", el->enum_name); + assert(er.encoded > 0 && (size_t)er.encoded < srcsize); + if(cb(src, er.encoded, app_key) < 0) ASN__ENCODE_FAILED; + ASN__ENCODED_OK(er); + } else { + ASN_DEBUG("ASN.1 forbids dealing with " + "unknown value of ENUMERATED type"); + ASN__ENCODE_FAILED; + } +} + +asn_dec_rval_t +NativeEnumerated_decode_uper(asn_codec_ctx_t *opt_codec_ctx, + asn_TYPE_descriptor_t *td, asn_per_constraints_t *constraints, + void **sptr, asn_per_data_t *pd) { + asn_INTEGER_specifics_t *specs = (asn_INTEGER_specifics_t *)td->specifics; + asn_dec_rval_t rval = { RC_OK, 0 }; + long *native = (long *)*sptr; + asn_per_constraint_t *ct; + long value; + + (void)opt_codec_ctx; + + if(constraints) ct = &constraints->value; + else if(td->per_constraints) ct = &td->per_constraints->value; + else ASN__DECODE_FAILED; /* Mandatory! */ + if(!specs) ASN__DECODE_FAILED; + + if(!native) { + native = (long *)(*sptr = CALLOC(1, sizeof(*native))); + if(!native) ASN__DECODE_FAILED; + } + + ASN_DEBUG("Decoding %s as NativeEnumerated", td->name); + + if(ct->flags & APC_EXTENSIBLE) { + int inext = per_get_few_bits(pd, 1); + if(inext < 0) ASN__DECODE_STARVED; + if(inext) ct = 0; + } + + if(ct && ct->range_bits >= 0) { + value = per_get_few_bits(pd, ct->range_bits); + if(value < 0) ASN__DECODE_STARVED; + if(value >= (specs->extension + ? specs->extension - 1 : specs->map_count)) + ASN__DECODE_FAILED; + } else { + if(!specs->extension) + ASN__DECODE_FAILED; + /* + * X.691, #10.6: normally small non-negative whole number; + */ + value = uper_get_nsnnwn(pd); + if(value < 0) ASN__DECODE_STARVED; + value += specs->extension - 1; + if(value >= specs->map_count) + ASN__DECODE_FAILED; + } + + *native = specs->value2enum[value].nat_value; + ASN_DEBUG("Decoded %s = %ld", td->name, *native); + + return rval; +} + +static int +NativeEnumerated__compar_value2enum(const void *ap, const void *bp) { + const asn_INTEGER_enum_map_t *a = ap; + const asn_INTEGER_enum_map_t *b = bp; + if(a->nat_value == b->nat_value) + return 0; + if(a->nat_value < b->nat_value) + return -1; + return 1; +} + +asn_enc_rval_t +NativeEnumerated_encode_uper(asn_TYPE_descriptor_t *td, + asn_per_constraints_t *constraints, void *sptr, asn_per_outp_t *po) { + asn_INTEGER_specifics_t *specs = (asn_INTEGER_specifics_t *)td->specifics; + asn_enc_rval_t er; + long native, value; + asn_per_constraint_t *ct; + int inext = 0; + asn_INTEGER_enum_map_t key; + const asn_INTEGER_enum_map_t *kf; + + if(!sptr) ASN__ENCODE_FAILED; + if(!specs) ASN__ENCODE_FAILED; + + if(constraints) ct = &constraints->value; + else if(td->per_constraints) ct = &td->per_constraints->value; + else ASN__ENCODE_FAILED; /* Mandatory! */ + + ASN_DEBUG("Encoding %s as NativeEnumerated", td->name); + + er.encoded = 0; + + native = *(long *)sptr; + if(native < 0) ASN__ENCODE_FAILED; + + key.nat_value = native; + kf = bsearch(&key, specs->value2enum, specs->map_count, + sizeof(key), NativeEnumerated__compar_value2enum); + if(!kf) { + ASN_DEBUG("No element corresponds to %ld", native); + ASN__ENCODE_FAILED; + } + value = kf - specs->value2enum; + + if(ct->range_bits >= 0) { + int cmpWith = specs->extension + ? specs->extension - 1 : specs->map_count; + if(value >= cmpWith) + inext = 1; + } + if(ct->flags & APC_EXTENSIBLE) { + if(per_put_few_bits(po, inext, 1)) + ASN__ENCODE_FAILED; + if(inext) ct = 0; + } else if(inext) { + ASN__ENCODE_FAILED; + } + + if(ct && ct->range_bits >= 0) { + if(per_put_few_bits(po, value, ct->range_bits)) + ASN__ENCODE_FAILED; + ASN__ENCODED_OK(er); + } + + if(!specs->extension) + ASN__ENCODE_FAILED; + + /* + * X.691, #10.6: normally small non-negative whole number; + */ + ASN_DEBUG("value = %ld, ext = %d, inext = %d, res = %ld", + value, specs->extension, inext, + value - (inext ? (specs->extension - 1) : 0)); + if(uper_put_nsnnwn(po, value - (inext ? (specs->extension - 1) : 0))) + ASN__ENCODE_FAILED; + + ASN__ENCODED_OK(er); +} + diff --git a/asn1_test_data/NativeEnumerated.h b/asn1_test_data/NativeEnumerated.h new file mode 100644 index 0000000..c59bb1b --- /dev/null +++ b/asn1_test_data/NativeEnumerated.h @@ -0,0 +1,32 @@ +/*- + * Copyright (c) 2004, 2005, 2006 Lev Walkin . + * 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 + +#ifdef __cplusplus +extern "C" { +#endif + +extern asn_TYPE_descriptor_t asn_DEF_NativeEnumerated; + +xer_type_encoder_f NativeEnumerated_encode_xer; +per_type_decoder_f NativeEnumerated_decode_uper; +per_type_encoder_f NativeEnumerated_encode_uper; + +#ifdef __cplusplus +} +#endif + +#endif /* _NativeEnumerated_H_ */ diff --git a/asn1_test_data/NativeInteger.c b/asn1_test_data/NativeInteger.c new file mode 100644 index 0000000..e8ce6d2 --- /dev/null +++ b/asn1_test_data/NativeInteger.c @@ -0,0 +1,332 @@ +/*- + * Copyright (c) 2004, 2005, 2006 Lev Walkin . + * All rights reserved. + * Redistribution and modifications are permitted subject to BSD license. + */ +/* + * 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 +#include + +/* + * NativeInteger basic type description. + */ +static const ber_tlv_tag_t asn_DEF_NativeInteger_tags[] = { + (ASN_TAG_CLASS_UNIVERSAL | (2 << 2)) +}; +asn_TYPE_descriptor_t asn_DEF_NativeInteger = { + "INTEGER", /* The ASN.1 type is still INTEGER */ + "INTEGER", + NativeInteger_free, + NativeInteger_print, + asn_generic_no_constraint, + NativeInteger_decode_ber, + NativeInteger_encode_der, + NativeInteger_decode_xer, + NativeInteger_encode_xer, + NativeInteger_decode_uper, /* Unaligned PER decoder */ + NativeInteger_encode_uper, /* Unaligned PER encoder */ + 0, /* Use generic outmost tag fetcher */ + asn_DEF_NativeInteger_tags, + sizeof(asn_DEF_NativeInteger_tags) / sizeof(asn_DEF_NativeInteger_tags[0]), + asn_DEF_NativeInteger_tags, /* Same as above */ + sizeof(asn_DEF_NativeInteger_tags) / sizeof(asn_DEF_NativeInteger_tags[0]), + 0, /* No PER visible constraints */ + 0, 0, /* No members */ + 0 /* No specifics */ +}; + +/* + * Decode INTEGER type. + */ +asn_dec_rval_t +NativeInteger_decode_ber(asn_codec_ctx_t *opt_codec_ctx, + asn_TYPE_descriptor_t *td, + void **nint_ptr, const void *buf_ptr, size_t size, int tag_mode) { + asn_INTEGER_specifics_t *specs=(asn_INTEGER_specifics_t *)td->specifics; + long *native = (long *)*nint_ptr; + asn_dec_rval_t rval; + ber_tlv_len_t length; + + /* + * If the structure is not there, allocate it. + */ + if(native == NULL) { + native = (long *)(*nint_ptr = CALLOC(1, sizeof(*native))); + if(native == 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(opt_codec_ctx, td, 0, buf_ptr, size, + tag_mode, 0, &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 = ((const char *)buf_ptr) + rval.consumed; + size -= rval.consumed; + if(length > (ber_tlv_len_t)size) { + rval.code = RC_WMORE; + rval.consumed = 0; + return rval; + } + + /* + * ASN.1 encoded INTEGER: buf_ptr, length + * Fill the native, at the same time checking for overflow. + * If overflow occured, return with RC_FAIL. + */ + { + INTEGER_t tmp; + union { + const void *constbuf; + void *nonconstbuf; + } unconst_buf; + long l; + + unconst_buf.constbuf = buf_ptr; + tmp.buf = (uint8_t *)unconst_buf.nonconstbuf; + tmp.size = length; + + if((specs&&specs->field_unsigned) + ? asn_INTEGER2ulong(&tmp, (unsigned long *)&l) /* sic */ + : asn_INTEGER2long(&tmp, &l)) { + rval.code = RC_FAIL; + rval.consumed = 0; + return rval; + } + + *native = l; + } + + rval.code = RC_OK; + rval.consumed += length; + + ASN_DEBUG("Took %ld/%ld bytes to encode %s (%ld)", + (long)rval.consumed, (long)length, td->name, (long)*native); + + return rval; +} + +/* + * Encode the NativeInteger using the standard INTEGER type DER encoder. + */ +asn_enc_rval_t +NativeInteger_encode_der(asn_TYPE_descriptor_t *sd, void *ptr, + int tag_mode, ber_tlv_tag_t tag, + asn_app_consume_bytes_f *cb, void *app_key) { + unsigned long native = *(unsigned long *)ptr; /* Disable sign ext. */ + asn_enc_rval_t erval; + INTEGER_t tmp; + +#ifdef WORDS_BIGENDIAN /* Opportunistic optimization */ + + tmp.buf = (uint8_t *)&native; + tmp.size = sizeof(native); + +#else /* Works even if WORDS_BIGENDIAN is not set where should've been */ + uint8_t buf[sizeof(native)]; + uint8_t *p; + + /* Prepare a fake INTEGER */ + for(p = buf + sizeof(buf) - 1; p >= buf; p--, native >>= 8) + *p = (uint8_t)native; + + 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; +} + +/* + * Decode the chunk of XML text encoding INTEGER. + */ +asn_dec_rval_t +NativeInteger_decode_xer(asn_codec_ctx_t *opt_codec_ctx, + asn_TYPE_descriptor_t *td, void **sptr, const char *opt_mname, + const void *buf_ptr, size_t size) { + asn_INTEGER_specifics_t *specs=(asn_INTEGER_specifics_t *)td->specifics; + asn_dec_rval_t rval; + INTEGER_t st; + void *st_ptr = (void *)&st; + long *native = (long *)*sptr; + + if(!native) { + native = (long *)(*sptr = CALLOC(1, sizeof(*native))); + if(!native) ASN__DECODE_FAILED; + } + + memset(&st, 0, sizeof(st)); + rval = INTEGER_decode_xer(opt_codec_ctx, td, &st_ptr, + opt_mname, buf_ptr, size); + if(rval.code == RC_OK) { + long l; + if((specs&&specs->field_unsigned) + ? asn_INTEGER2ulong(&st, (unsigned long *)&l) /* sic */ + : asn_INTEGER2long(&st, &l)) { + rval.code = RC_FAIL; + rval.consumed = 0; + } else { + *native = l; + } + } else { + /* + * Cannot restart from the middle; + * there is no place to save state in the native type. + * Request a continuation from the very beginning. + */ + rval.consumed = 0; + } + ASN_STRUCT_FREE_CONTENTS_ONLY(asn_DEF_INTEGER, &st); + return rval; +} + + +asn_enc_rval_t +NativeInteger_encode_xer(asn_TYPE_descriptor_t *td, void *sptr, + int ilevel, enum xer_encoder_flags_e flags, + asn_app_consume_bytes_f *cb, void *app_key) { + asn_INTEGER_specifics_t *specs=(asn_INTEGER_specifics_t *)td->specifics; + char scratch[32]; /* Enough for 64-bit int */ + asn_enc_rval_t er; + const long *native = (const long *)sptr; + + (void)ilevel; + (void)flags; + + if(!native) ASN__ENCODE_FAILED; + + er.encoded = snprintf(scratch, sizeof(scratch), + (specs && specs->field_unsigned) + ? "%lu" : "%ld", *native); + if(er.encoded <= 0 || (size_t)er.encoded >= sizeof(scratch) + || cb(scratch, er.encoded, app_key) < 0) + ASN__ENCODE_FAILED; + + ASN__ENCODED_OK(er); +} + +asn_dec_rval_t +NativeInteger_decode_uper(asn_codec_ctx_t *opt_codec_ctx, + asn_TYPE_descriptor_t *td, + asn_per_constraints_t *constraints, void **sptr, asn_per_data_t *pd) { + + asn_INTEGER_specifics_t *specs=(asn_INTEGER_specifics_t *)td->specifics; + asn_dec_rval_t rval; + long *native = (long *)*sptr; + INTEGER_t tmpint; + void *tmpintptr = &tmpint; + + (void)opt_codec_ctx; + ASN_DEBUG("Decoding NativeInteger %s (UPER)", td->name); + + if(!native) { + native = (long *)(*sptr = CALLOC(1, sizeof(*native))); + if(!native) ASN__DECODE_FAILED; + } + + memset(&tmpint, 0, sizeof tmpint); + rval = INTEGER_decode_uper(opt_codec_ctx, td, constraints, + &tmpintptr, pd); + if(rval.code == RC_OK) { + if((specs&&specs->field_unsigned) + ? asn_INTEGER2ulong(&tmpint, (unsigned long *)native) + : asn_INTEGER2long(&tmpint, native)) + rval.code = RC_FAIL; + else + ASN_DEBUG("NativeInteger %s got value %ld", + td->name, *native); + } + ASN_STRUCT_FREE_CONTENTS_ONLY(asn_DEF_INTEGER, &tmpint); + + return rval; +} + +asn_enc_rval_t +NativeInteger_encode_uper(asn_TYPE_descriptor_t *td, + asn_per_constraints_t *constraints, void *sptr, asn_per_outp_t *po) { + asn_INTEGER_specifics_t *specs=(asn_INTEGER_specifics_t *)td->specifics; + asn_enc_rval_t er; + long native; + INTEGER_t tmpint; + + if(!sptr) ASN__ENCODE_FAILED; + + native = *(long *)sptr; + + ASN_DEBUG("Encoding NativeInteger %s %ld (UPER)", td->name, native); + + memset(&tmpint, 0, sizeof(tmpint)); + if((specs&&specs->field_unsigned) + ? asn_ulong2INTEGER(&tmpint, native) + : asn_long2INTEGER(&tmpint, native)) + ASN__ENCODE_FAILED; + er = INTEGER_encode_uper(td, constraints, &tmpint, po); + ASN_STRUCT_FREE_CONTENTS_ONLY(asn_DEF_INTEGER, &tmpint); + return er; +} + +/* + * INTEGER specific human-readable output. + */ +int +NativeInteger_print(asn_TYPE_descriptor_t *td, const void *sptr, int ilevel, + asn_app_consume_bytes_f *cb, void *app_key) { + asn_INTEGER_specifics_t *specs=(asn_INTEGER_specifics_t *)td->specifics; + const long *native = (const long *)sptr; + char scratch[32]; /* Enough for 64-bit int */ + int ret; + + (void)td; /* Unused argument */ + (void)ilevel; /* Unused argument */ + + if(native) { + ret = snprintf(scratch, sizeof(scratch), + (specs && specs->field_unsigned) + ? "%lu" : "%ld", *native); + assert(ret > 0 && (size_t)ret < sizeof(scratch)); + return (cb(scratch, ret, app_key) < 0) ? -1 : 0; + } else { + return (cb("", 8, app_key) < 0) ? -1 : 0; + } +} + +void +NativeInteger_free(asn_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/asn1_test_data/NativeInteger.h b/asn1_test_data/NativeInteger.h new file mode 100644 index 0000000..4e63a83 --- /dev/null +++ b/asn1_test_data/NativeInteger.h @@ -0,0 +1,37 @@ +/*- + * Copyright (c) 2004 Lev Walkin . 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 +#include + +#ifdef __cplusplus +extern "C" { +#endif + +extern asn_TYPE_descriptor_t asn_DEF_NativeInteger; + +asn_struct_free_f NativeInteger_free; +asn_struct_print_f NativeInteger_print; +ber_type_decoder_f NativeInteger_decode_ber; +der_type_encoder_f NativeInteger_encode_der; +xer_type_decoder_f NativeInteger_decode_xer; +xer_type_encoder_f NativeInteger_encode_xer; +per_type_decoder_f NativeInteger_decode_uper; +per_type_encoder_f NativeInteger_encode_uper; + +#ifdef __cplusplus +} +#endif + +#endif /* _NativeInteger_H_ */ diff --git a/asn1_test_data/NativeReal.c b/asn1_test_data/NativeReal.c new file mode 100644 index 0000000..ad6f28c --- /dev/null +++ b/asn1_test_data/NativeReal.c @@ -0,0 +1,342 @@ +/*- + * Copyright (c) 2004, 2006 Lev Walkin . All rights reserved. + * Redistribution and modifications are permitted subject to BSD license. + */ +/* + * Read the NativeReal.h for the explanation wrt. differences between + * REAL and NativeReal. + * Basically, both are decoders and encoders of ASN.1 REAL type, but this + * implementation deals with the standard (machine-specific) representation + * of them instead of using the platform-independent buffer. + */ +#include +#include +#include +#include + +/* + * NativeReal basic type description. + */ +static const ber_tlv_tag_t asn_DEF_NativeReal_tags[] = { + (ASN_TAG_CLASS_UNIVERSAL | (9 << 2)) +}; +asn_TYPE_descriptor_t asn_DEF_NativeReal = { + "REAL", /* The ASN.1 type is still REAL */ + "REAL", + NativeReal_free, + NativeReal_print, + asn_generic_no_constraint, + NativeReal_decode_ber, + NativeReal_encode_der, + NativeReal_decode_xer, + NativeReal_encode_xer, + NativeReal_decode_uper, + NativeReal_encode_uper, + 0, /* Use generic outmost tag fetcher */ + asn_DEF_NativeReal_tags, + sizeof(asn_DEF_NativeReal_tags) / sizeof(asn_DEF_NativeReal_tags[0]), + asn_DEF_NativeReal_tags, /* Same as above */ + sizeof(asn_DEF_NativeReal_tags) / sizeof(asn_DEF_NativeReal_tags[0]), + 0, /* No PER visible constraints */ + 0, 0, /* No members */ + 0 /* No specifics */ +}; + +/* + * Decode REAL type. + */ +asn_dec_rval_t +NativeReal_decode_ber(asn_codec_ctx_t *opt_codec_ctx, + asn_TYPE_descriptor_t *td, + void **dbl_ptr, const void *buf_ptr, size_t size, int tag_mode) { + double *Dbl = (double *)*dbl_ptr; + asn_dec_rval_t rval; + ber_tlv_len_t length; + + /* + * If the structure is not there, allocate it. + */ + if(Dbl == NULL) { + *dbl_ptr = CALLOC(1, sizeof(*Dbl)); + Dbl = (double *)*dbl_ptr; + if(Dbl == NULL) { + rval.code = RC_FAIL; + rval.consumed = 0; + return rval; + } + } + + ASN_DEBUG("Decoding %s as REAL (tm=%d)", + td->name, tag_mode); + + /* + * Check tags. + */ + rval = ber_check_tags(opt_codec_ctx, td, 0, buf_ptr, size, + tag_mode, 0, &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 = ((const char *)buf_ptr) + rval.consumed; + size -= rval.consumed; + if(length > (ber_tlv_len_t)size) { + rval.code = RC_WMORE; + rval.consumed = 0; + return rval; + } + + /* + * ASN.1 encoded REAL: buf_ptr, length + * Fill the Dbl, at the same time checking for overflow. + * If overflow occured, return with RC_FAIL. + */ + { + REAL_t tmp; + union { + const void *constbuf; + void *nonconstbuf; + } unconst_buf; + double d; + + unconst_buf.constbuf = buf_ptr; + tmp.buf = (uint8_t *)unconst_buf.nonconstbuf; + tmp.size = length; + + if(length < (ber_tlv_len_t)size) { + int ret; + uint8_t saved_byte = tmp.buf[tmp.size]; + tmp.buf[tmp.size] = '\0'; + ret = asn_REAL2double(&tmp, &d); + tmp.buf[tmp.size] = saved_byte; + if(ret) { + rval.code = RC_FAIL; + rval.consumed = 0; + return rval; + } + } else if(length < 48 /* Enough for longish %f value. */) { + tmp.buf = alloca(length + 1); + tmp.size = length; + memcpy(tmp.buf, buf_ptr, length); + tmp.buf[tmp.size] = '\0'; + if(asn_REAL2double(&tmp, &d)) { + rval.code = RC_FAIL; + rval.consumed = 0; + return rval; + } + } else { + /* This should probably never happen: impractically long value */ + tmp.buf = CALLOC(1, length + 1); + tmp.size = length; + if(tmp.buf) memcpy(tmp.buf, buf_ptr, length); + if(!tmp.buf || asn_REAL2double(&tmp, &d)) { + FREEMEM(tmp.buf); + rval.code = RC_FAIL; + rval.consumed = 0; + return rval; + } + FREEMEM(tmp.buf); + } + + *Dbl = d; + } + + rval.code = RC_OK; + rval.consumed += length; + + ASN_DEBUG("Took %ld/%ld bytes to encode %s (%f)", + (long)rval.consumed, (long)length, td->name, *Dbl); + + return rval; +} + +/* + * Encode the NativeReal using the standard REAL type DER encoder. + */ +asn_enc_rval_t +NativeReal_encode_der(asn_TYPE_descriptor_t *td, void *ptr, + int tag_mode, ber_tlv_tag_t tag, + asn_app_consume_bytes_f *cb, void *app_key) { + double Dbl = *(const double *)ptr; + asn_enc_rval_t erval; + REAL_t tmp; + + /* Prepare a temporary clean structure */ + memset(&tmp, 0, sizeof(tmp)); + + if(asn_double2REAL(&tmp, Dbl)) { + erval.encoded = -1; + erval.failed_type = td; + erval.structure_ptr = ptr; + return erval; + } + + /* Encode a fake REAL */ + erval = der_encode_primitive(td, &tmp, tag_mode, tag, cb, app_key); + if(erval.encoded == -1) { + assert(erval.structure_ptr == &tmp); + erval.structure_ptr = ptr; + } + + /* Free possibly allocated members of the temporary structure */ + ASN_STRUCT_FREE_CONTENTS_ONLY(asn_DEF_REAL, &tmp); + + return erval; +} + +/* + * Decode REAL type using PER. + */ +asn_dec_rval_t +NativeReal_decode_uper(asn_codec_ctx_t *opt_codec_ctx, + asn_TYPE_descriptor_t *td, asn_per_constraints_t *constraints, + void **dbl_ptr, asn_per_data_t *pd) { + double *Dbl = (double *)*dbl_ptr; + asn_dec_rval_t rval; + REAL_t tmp; + void *ptmp = &tmp; + int ret; + + (void)constraints; + + /* + * If the structure is not there, allocate it. + */ + if(Dbl == NULL) { + *dbl_ptr = CALLOC(1, sizeof(*Dbl)); + Dbl = (double *)*dbl_ptr; + if(Dbl == NULL) + ASN__DECODE_FAILED; + } + + memset(&tmp, 0, sizeof(tmp)); + rval = OCTET_STRING_decode_uper(opt_codec_ctx, td, NULL, + &ptmp, pd); + if(rval.code != RC_OK) { + ASN_STRUCT_FREE_CONTENTS_ONLY(asn_DEF_REAL, &tmp); + return rval; + } + + ret = asn_REAL2double(&tmp, Dbl); + ASN_STRUCT_FREE_CONTENTS_ONLY(asn_DEF_REAL, &tmp); + if(ret) ASN__DECODE_FAILED; + + return rval; +} + +/* + * Encode the NativeReal using the OCTET STRING PER encoder. + */ +asn_enc_rval_t +NativeReal_encode_uper(asn_TYPE_descriptor_t *td, + asn_per_constraints_t *constraints, void *sptr, asn_per_outp_t *po) { + double Dbl = *(const double *)sptr; + asn_enc_rval_t erval; + REAL_t tmp; + + (void)constraints; + + /* Prepare a temporary clean structure */ + memset(&tmp, 0, sizeof(tmp)); + + if(asn_double2REAL(&tmp, Dbl)) + ASN__ENCODE_FAILED; + + /* Encode a DER REAL */ + erval = OCTET_STRING_encode_uper(td, NULL, &tmp, po); + if(erval.encoded == -1) + erval.structure_ptr = sptr; + + /* Free possibly allocated members of the temporary structure */ + ASN_STRUCT_FREE_CONTENTS_ONLY(asn_DEF_REAL, &tmp); + + return erval; +} + +/* + * Decode the chunk of XML text encoding REAL. + */ +asn_dec_rval_t +NativeReal_decode_xer(asn_codec_ctx_t *opt_codec_ctx, + asn_TYPE_descriptor_t *td, void **sptr, const char *opt_mname, + const void *buf_ptr, size_t size) { + asn_dec_rval_t rval; + REAL_t *st = 0; + REAL_t **stp = &st; + double *Dbl = (double *)*sptr; + + if(!Dbl) { + *sptr = CALLOC(1, sizeof(double)); + Dbl = (double *)*sptr; + if(!Dbl) { + rval.code = RC_FAIL; + rval.consumed = 0; + return rval; + } + } + + rval = REAL_decode_xer(opt_codec_ctx, td, (void **)stp, opt_mname, + buf_ptr, size); + if(rval.code == RC_OK) { + if(asn_REAL2double(st, Dbl)) { + rval.code = RC_FAIL; + rval.consumed = 0; + } + } else { + rval.consumed = 0; + } + ASN_STRUCT_FREE(asn_DEF_REAL, st); + return rval; +} + +asn_enc_rval_t +NativeReal_encode_xer(asn_TYPE_descriptor_t *td, void *sptr, + int ilevel, enum xer_encoder_flags_e flags, + asn_app_consume_bytes_f *cb, void *app_key) { + const double *Dbl = (const double *)sptr; + asn_enc_rval_t er; + + (void)ilevel; + + if(!Dbl) ASN__ENCODE_FAILED; + + er.encoded = REAL__dump(*Dbl, flags & XER_F_CANONICAL, cb, app_key); + if(er.encoded < 0) ASN__ENCODE_FAILED; + + ASN__ENCODED_OK(er); +} + +/* + * REAL specific human-readable output. + */ +int +NativeReal_print(asn_TYPE_descriptor_t *td, const void *sptr, int ilevel, + asn_app_consume_bytes_f *cb, void *app_key) { + const double *Dbl = (const double *)sptr; + + (void)td; /* Unused argument */ + (void)ilevel; /* Unused argument */ + + if(!Dbl) return (cb("", 8, app_key) < 0) ? -1 : 0; + + return (REAL__dump(*Dbl, 0, cb, app_key) < 0) ? -1 : 0; +} + +void +NativeReal_free(asn_TYPE_descriptor_t *td, void *ptr, int contents_only) { + + if(!td || !ptr) + return; + + ASN_DEBUG("Freeing %s as REAL (%d, %p, Native)", + td->name, contents_only, ptr); + + if(!contents_only) { + FREEMEM(ptr); + } +} + diff --git a/asn1_test_data/NativeReal.h b/asn1_test_data/NativeReal.h new file mode 100644 index 0000000..68a81d9 --- /dev/null +++ b/asn1_test_data/NativeReal.h @@ -0,0 +1,35 @@ +/*- + * Copyright (c) 2004 Lev Walkin . All rights reserved. + * Redistribution and modifications are permitted subject to BSD license. + */ +/* + * This type differs from the standard REAL in that it is modelled using + * the fixed machine type (double), so it can hold only values of + * limited precision. There is no explicit type (i.e., NativeReal_t). + * Use of this type is normally enabled by -fnative-types. + */ +#ifndef ASN_TYPE_NativeReal_H +#define ASN_TYPE_NativeReal_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +extern asn_TYPE_descriptor_t asn_DEF_NativeReal; + +asn_struct_free_f NativeReal_free; +asn_struct_print_f NativeReal_print; +ber_type_decoder_f NativeReal_decode_ber; +der_type_encoder_f NativeReal_encode_der; +xer_type_decoder_f NativeReal_decode_xer; +xer_type_encoder_f NativeReal_encode_xer; +per_type_decoder_f NativeReal_decode_uper; +per_type_encoder_f NativeReal_encode_uper; + +#ifdef __cplusplus +} +#endif + +#endif /* ASN_TYPE_NativeReal_H */ diff --git a/asn1_test_data/NumericString.c b/asn1_test_data/NumericString.c new file mode 100644 index 0000000..887b3f5 --- /dev/null +++ b/asn1_test_data/NumericString.c @@ -0,0 +1,101 @@ +/*- + * Copyright (c) 2003, 2006 Lev Walkin . All rights reserved. + * Redistribution and modifications are permitted subject to BSD license. + */ +#include +#include + +/* + * NumericString basic type description. + */ +static const ber_tlv_tag_t asn_DEF_NumericString_tags[] = { + (ASN_TAG_CLASS_UNIVERSAL | (18 << 2)), /* [UNIVERSAL 18] IMPLICIT ...*/ + (ASN_TAG_CLASS_UNIVERSAL | (4 << 2)) /* ... OCTET STRING */ +}; +static int asn_DEF_NumericString_v2c(unsigned int value) { + switch(value) { + case 0x20: return 0; + case 0x30: case 0x31: case 0x32: case 0x33: case 0x34: + case 0x35: case 0x36: case 0x37: case 0x38: case 0x39: + return value - (0x30 - 1); + } + return -1; +} +static int asn_DEF_NumericString_c2v(unsigned int code) { + if(code > 0) { + if(code <= 10) + return code + (0x30 - 1); + else + return -1; + } else { + return 0x20; + } +} +static asn_per_constraints_t asn_DEF_NumericString_constraints = { + { APC_CONSTRAINED, 4, 4, 0x20, 0x39 }, /* Value */ + { APC_SEMI_CONSTRAINED, -1, -1, 0, 0 }, /* Size */ + asn_DEF_NumericString_v2c, + asn_DEF_NumericString_c2v +}; +asn_TYPE_descriptor_t asn_DEF_NumericString = { + "NumericString", + "NumericString", + OCTET_STRING_free, + OCTET_STRING_print_utf8, /* ASCII subset */ + NumericString_constraint, + OCTET_STRING_decode_ber, /* Implemented in terms of OCTET STRING */ + OCTET_STRING_encode_der, + OCTET_STRING_decode_xer_utf8, + OCTET_STRING_encode_xer_utf8, + OCTET_STRING_decode_uper, + OCTET_STRING_encode_uper, + 0, /* Use generic outmost tag fetcher */ + asn_DEF_NumericString_tags, + sizeof(asn_DEF_NumericString_tags) + / sizeof(asn_DEF_NumericString_tags[0]) - 1, + asn_DEF_NumericString_tags, + sizeof(asn_DEF_NumericString_tags) + / sizeof(asn_DEF_NumericString_tags[0]), + &asn_DEF_NumericString_constraints, + 0, 0, /* No members */ + 0 /* No specifics */ +}; + +int +NumericString_constraint(asn_TYPE_descriptor_t *td, const void *sptr, + asn_app_constraint_failed_f *ctfailcb, void *app_key) { + const NumericString_t *st = (const NumericString_t *)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__CTFAIL(app_key, td, sptr, + "%s: value byte %ld (%d) " + "not in NumericString alphabet (%s:%d)", + td->name, + (long)((buf - st->buf) + 1), + *buf, + __FILE__, __LINE__); + return -1; + } + } else { + ASN__CTFAIL(app_key, td, sptr, + "%s: value not given (%s:%d)", + td->name, __FILE__, __LINE__); + return -1; + } + + return 0; +} diff --git a/asn1_test_data/NumericString.h b/asn1_test_data/NumericString.h new file mode 100644 index 0000000..51e24e8 --- /dev/null +++ b/asn1_test_data/NumericString.h @@ -0,0 +1,24 @@ +/*- + * Copyright (c) 2003 Lev Walkin . All rights reserved. + * Redistribution and modifications are permitted subject to BSD license. + */ +#ifndef _NumericString_H_ +#define _NumericString_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef OCTET_STRING_t NumericString_t; /* Implemented via OCTET STRING */ + +extern asn_TYPE_descriptor_t asn_DEF_NumericString; + +asn_constr_check_f NumericString_constraint; + +#ifdef __cplusplus +} +#endif + +#endif /* _NumericString_H_ */ diff --git a/asn1_test_data/OBJECT_IDENTIFIER.c b/asn1_test_data/OBJECT_IDENTIFIER.c new file mode 100644 index 0000000..d0f8b31 --- /dev/null +++ b/asn1_test_data/OBJECT_IDENTIFIER.c @@ -0,0 +1,764 @@ +/*- + * Copyright (c) 2003, 2004 Lev Walkin . All rights reserved. + * Redistribution and modifications are permitted subject to BSD license. + */ +#include +#include +#include +#include +#include /* for CHAR_BIT */ +#include + +/* + * OBJECT IDENTIFIER basic type description. + */ +static const ber_tlv_tag_t asn_DEF_OBJECT_IDENTIFIER_tags[] = { + (ASN_TAG_CLASS_UNIVERSAL | (6 << 2)) +}; +asn_TYPE_descriptor_t asn_DEF_OBJECT_IDENTIFIER = { + "OBJECT IDENTIFIER", + "OBJECT_IDENTIFIER", + ASN__PRIMITIVE_TYPE_free, + OBJECT_IDENTIFIER_print, + OBJECT_IDENTIFIER_constraint, + ber_decode_primitive, + der_encode_primitive, + OBJECT_IDENTIFIER_decode_xer, + OBJECT_IDENTIFIER_encode_xer, + OCTET_STRING_decode_uper, + OCTET_STRING_encode_uper, + 0, /* Use generic outmost tag fetcher */ + asn_DEF_OBJECT_IDENTIFIER_tags, + sizeof(asn_DEF_OBJECT_IDENTIFIER_tags) + / sizeof(asn_DEF_OBJECT_IDENTIFIER_tags[0]), + asn_DEF_OBJECT_IDENTIFIER_tags, /* Same as above */ + sizeof(asn_DEF_OBJECT_IDENTIFIER_tags) + / sizeof(asn_DEF_OBJECT_IDENTIFIER_tags[0]), + 0, /* No PER visible constraints */ + 0, 0, /* No members */ + 0 /* No specifics */ +}; + + +int +OBJECT_IDENTIFIER_constraint(asn_TYPE_descriptor_t *td, const void *sptr, + asn_app_constraint_failed_f *ctfailcb, void *app_key) { + const OBJECT_IDENTIFIER_t *st = (const OBJECT_IDENTIFIER_t *)sptr; + + if(st && st->buf) { + if(st->size < 1) { + ASN__CTFAIL(app_key, td, sptr, + "%s: at least one numerical value " + "expected (%s:%d)", + td->name, __FILE__, __LINE__); + return -1; + } + } else { + ASN__CTFAIL(app_key, td, sptr, + "%s: value not given (%s:%d)", + td->name, __FILE__, __LINE__); + return -1; + } + + return 0; +} + + +int +OBJECT_IDENTIFIER_get_single_arc(const uint8_t *arcbuf, unsigned int arclen, signed int add, void *rvbufp, unsigned int rvsize) { + unsigned LE GCC_NOTUSED = 1; /* Little endian (x86) */ + const uint8_t *arcend = arcbuf + arclen; /* End of arc */ + unsigned int cache = 0; /* No more than 14 significant bits */ + unsigned char *rvbuf = (unsigned char *)rvbufp; + unsigned char *rvstart = rvbuf; /* Original start of the value buffer */ + int inc; /* Return value growth direction */ + + rvsize *= CHAR_BIT; /* bytes to bits */ + arclen *= 7; /* bytes to bits */ + + /* + * The arc has the number of bits + * cannot be represented using supplied return value type. + */ + if(arclen > rvsize) { + if(arclen > (rvsize + CHAR_BIT)) { + errno = ERANGE; /* Overflow */ + return -1; + } else { + /* + * Even if the number of bits in the arc representation + * is higher than the width of supplied * return value + * type, there is still possible to fit it when there + * are few unused high bits in the arc value + * representaion. + * + * Moreover, there is a possibility that the + * number could actually fit the arc space, given + * that add is negative, but we don't handle + * such "temporary lack of precision" situation here. + * May be considered as a bug. + */ + uint8_t mask = (0xff << (7-(arclen - rvsize))) & 0x7f; + if((*arcbuf & mask)) { + errno = ERANGE; /* Overflow */ + return -1; + } + /* Fool the routine computing unused bits */ + arclen -= 7; + cache = *arcbuf & 0x7f; + arcbuf++; + } + } + + /* Faster path for common size */ + if(rvsize == (CHAR_BIT * sizeof(unsigned long))) { + unsigned long accum; + /* Gather all bits into the accumulator */ + for(accum = cache; arcbuf < arcend; arcbuf++) + accum = (accum << 7) | (*arcbuf & ~0x80); + if(accum < (unsigned)-add) { + errno = ERANGE; /* Overflow */ + return -1; + } + *(unsigned long *)(void *)rvbuf = accum + add; /* alignment OK! */ + return 0; + } + +#ifndef WORDS_BIGENDIAN + if(*(unsigned char *)&LE) { /* Little endian (x86) */ + /* "Convert" to big endian */ + rvbuf += rvsize / CHAR_BIT - 1; + rvstart--; + inc = -1; /* Descending */ + } else +#endif /* !WORDS_BIGENDIAN */ + inc = +1; /* Big endian is known [at compile time] */ + + { + int bits; /* typically no more than 3-4 bits */ + + /* Clear the high unused bits */ + for(bits = rvsize - arclen; + bits > CHAR_BIT; + rvbuf += inc, bits -= CHAR_BIT) + *rvbuf = 0; + + /* Fill the body of a value */ + for(; arcbuf < arcend; arcbuf++) { + cache = (cache << 7) | (*arcbuf & 0x7f); + bits += 7; + if(bits >= CHAR_BIT) { + bits -= CHAR_BIT; + *rvbuf = (cache >> bits); + rvbuf += inc; + } + } + if(bits) { + *rvbuf = cache; + rvbuf += inc; + } + } + + if(add) { + for(rvbuf -= inc; rvbuf != rvstart; rvbuf -= inc) { + int v = add + *rvbuf; + if(v & ((unsigned)~0 << CHAR_BIT)) { + *rvbuf = (unsigned char)(v + (1 << CHAR_BIT)); + add = -1; + } else { + *rvbuf = v; + break; + } + } + if(rvbuf == rvstart) { + /* No space to carry over */ + errno = ERANGE; /* Overflow */ + return -1; + } + } + + return 0; +} + +ssize_t +OBJECT_IDENTIFIER__dump_arc(const 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_single_arc(arcbuf, arclen, add, + &accum, sizeof(accum))) + return -1; + + if(accum) { + ssize_t len; + + /* Fill the scratch buffer in reverse. */ + p = scratch + sizeof(scratch); + for(; accum; accum /= 10) + *(--p) = (char)(accum % 10) + 0x30; /* Put a digit */ + + len = sizeof(scratch) - (p - scratch); + if(cb(p, len, app_key) < 0) + return -1; + return len; + } else { + *scratch = 0x30; + if(cb(scratch, 1, app_key) < 0) + return -1; + return 1; + } +} + +int +OBJECT_IDENTIFIER_print_arc(const uint8_t *arcbuf, int arclen, int add, + asn_app_consume_bytes_f *cb, void *app_key) { + + if(OBJECT_IDENTIFIER__dump_arc(arcbuf, arclen, add, cb, app_key) < 0) + return -1; + + return 0; +} + +static ssize_t +OBJECT_IDENTIFIER__dump_body(const OBJECT_IDENTIFIER_t *st, asn_app_consume_bytes_f *cb, void *app_key) { + ssize_t wrote_len = 0; + int startn; + int add = 0; + int i; + + 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) < 0) return -1; + } else if(b <= 39) { + add = 0; + if(cb("0", 1, app_key) < 0) return -1; + } else if(b < 79) { + add = -40; + if(cb("1", 1, app_key) < 0) return -1; + } else { + add = -80; + if(cb("2", 1, app_key) < 0) return -1; + } + wrote_len += 1; + } + + if(cb(".", 1, app_key) < 0) /* Separate arcs */ + return -1; + + add = OBJECT_IDENTIFIER__dump_arc(&st->buf[startn], + i - startn + 1, add, cb, app_key); + if(add < 0) return -1; + wrote_len += 1 + add; + startn = i + 1; + add = 0; + } + + return wrote_len; +} + +static enum xer_pbd_rval +OBJECT_IDENTIFIER__xer_body_decode(asn_TYPE_descriptor_t *td, void *sptr, const void *chunk_buf, size_t chunk_size) { + OBJECT_IDENTIFIER_t *st = (OBJECT_IDENTIFIER_t *)sptr; + const char *chunk_end = (const char *)chunk_buf + chunk_size; + const char *endptr; + long s_arcs[10]; + long *arcs = s_arcs; + int arcs_count; + int ret; + + (void)td; + + arcs_count = OBJECT_IDENTIFIER_parse_arcs( + (const char *)chunk_buf, chunk_size, arcs, + sizeof(s_arcs)/sizeof(s_arcs[0]), &endptr); + if(arcs_count < 0) { + /* Expecting more than zero arcs */ + return XPBD_BROKEN_ENCODING; + } else if(arcs_count == 0) { + return XPBD_NOT_BODY_IGNORE; + } + assert(endptr == chunk_end); + + if((size_t)arcs_count > sizeof(s_arcs)/sizeof(s_arcs[0])) { + arcs = (long *)MALLOC(arcs_count * sizeof(long)); + if(!arcs) return XPBD_SYSTEM_FAILURE; + ret = OBJECT_IDENTIFIER_parse_arcs( + (const char *)chunk_buf, chunk_size, + arcs, arcs_count, &endptr); + if(ret != arcs_count) + return XPBD_SYSTEM_FAILURE; /* assert?.. */ + } + + /* + * Convert arcs into BER representation. + */ + ret = OBJECT_IDENTIFIER_set_arcs(st, arcs, sizeof(*arcs), arcs_count); + if(arcs != s_arcs) FREEMEM(arcs); + + return ret ? XPBD_SYSTEM_FAILURE : XPBD_BODY_CONSUMED; +} + +asn_dec_rval_t +OBJECT_IDENTIFIER_decode_xer(asn_codec_ctx_t *opt_codec_ctx, + asn_TYPE_descriptor_t *td, void **sptr, const char *opt_mname, + const void *buf_ptr, size_t size) { + + return xer_decode_primitive(opt_codec_ctx, td, + sptr, sizeof(OBJECT_IDENTIFIER_t), opt_mname, + buf_ptr, size, OBJECT_IDENTIFIER__xer_body_decode); +} + +asn_enc_rval_t +OBJECT_IDENTIFIER_encode_xer(asn_TYPE_descriptor_t *td, void *sptr, + int ilevel, enum xer_encoder_flags_e flags, + asn_app_consume_bytes_f *cb, void *app_key) { + const OBJECT_IDENTIFIER_t *st = (const OBJECT_IDENTIFIER_t *)sptr; + asn_enc_rval_t er; + + (void)ilevel; + (void)flags; + + if(!st || !st->buf) + ASN__ENCODE_FAILED; + + er.encoded = OBJECT_IDENTIFIER__dump_body(st, cb, app_key); + if(er.encoded < 0) ASN__ENCODE_FAILED; + + ASN__ENCODED_OK(er); +} + +int +OBJECT_IDENTIFIER_print(asn_TYPE_descriptor_t *td, const void *sptr, + int ilevel, asn_app_consume_bytes_f *cb, void *app_key) { + const OBJECT_IDENTIFIER_t *st = (const OBJECT_IDENTIFIER_t *)sptr; + + (void)td; /* Unused argument */ + (void)ilevel; /* Unused argument */ + + if(!st || !st->buf) + return (cb("", 8, app_key) < 0) ? -1 : 0; + + /* Dump preamble */ + if(cb("{ ", 2, app_key) < 0) + return -1; + + if(OBJECT_IDENTIFIER__dump_body(st, cb, app_key) < 0) + return -1; + + return (cb(" }", 2, app_key) < 0) ? -1 : 0; +} + +int +OBJECT_IDENTIFIER_get_arcs(const OBJECT_IDENTIFIER_t *oid, void *arcs, + unsigned int arc_type_size, unsigned int arc_slots) { + void *arcs_end = (char *)arcs + (arc_type_size * arc_slots); + int num_arcs = 0; + int startn = 0; + int add = 0; + int i; + + if(!oid || !oid->buf || (arc_slots && arc_type_size <= 1)) { + errno = EINVAL; + return -1; + } + + for(i = 0; i < oid->size; i++) { + uint8_t b = oid->buf[i]; + if((b & 0x80)) /* Continuation expected */ + continue; + + if(num_arcs == 0) { + /* + * First two arcs are encoded through the backdoor. + */ + unsigned LE = 1; /* Little endian */ + int first_arc; + num_arcs++; + if(!arc_slots) { num_arcs++; continue; } + + if(i) first_arc = 2; + else if(b <= 39) first_arc = 0; + else if(b < 79) first_arc = 1; + else first_arc = 2; + + add = -40 * first_arc; + memset(arcs, 0, arc_type_size); + *(unsigned char *)((char *)arcs + + ((*(char *)&LE)?0:(arc_type_size - 1))) + = first_arc; + arcs = ((char *)arcs) + arc_type_size; + } + + /* Decode, if has space */ + if(arcs < arcs_end) { + if(OBJECT_IDENTIFIER_get_single_arc(&oid->buf[startn], + i - startn + 1, add, + arcs, arc_type_size)) + return -1; + startn = i + 1; + arcs = ((char *)arcs) + arc_type_size; + add = 0; + } + num_arcs++; + } + + return num_arcs; +} + + +/* + * Save the single value as an object identifier arc. + */ +int +OBJECT_IDENTIFIER_set_single_arc(uint8_t *arcbuf, const void *arcval, unsigned int arcval_size, int prepared_order) { + /* + * The following conditions must hold: + * assert(arcval); + * assert(arcval_size > 0); + * assert(arcval_size <= 16); + * assert(arcbuf); + */ +#ifdef WORDS_BIGENDIAN + const unsigned isLittleEndian = 0; +#else + unsigned LE = 1; + unsigned isLittleEndian = *(char *)&LE; +#endif + const uint8_t *tend, *tp; + unsigned int cache; + uint8_t *bp = arcbuf; + int bits; + uint8_t buffer[16]; + + if(isLittleEndian && !prepared_order) { + const uint8_t *a = (const unsigned char *)arcval + arcval_size - 1; + const uint8_t *aend = (const uint8_t *)arcval; + uint8_t *msb = buffer + arcval_size - 1; + uint8_t *tb; + for(tb = buffer; a >= aend; tb++, a--) + if((*tb = *a) && (tb < msb)) + msb = tb; + tend = &buffer[arcval_size]; + tp = msb; /* Most significant non-zero byte */ + } else { + /* Look for most significant non-zero byte */ + tend = (const unsigned char *)arcval + arcval_size; + for(tp = (const uint8_t *)arcval; tp < tend - 1; tp++) + if(*tp) break; + } + + /* + * Split the value in 7-bits chunks. + */ + bits = ((tend - tp) * CHAR_BIT) % 7; + if(bits) { + cache = *tp >> (CHAR_BIT - bits); + if(cache) { + *bp++ = cache | 0x80; + cache = *tp++; + bits = CHAR_BIT - bits; + } else { + bits = -bits; + } + } else { + cache = 0; + } + for(; tp < tend; tp++) { + cache = (cache << CHAR_BIT) + *tp; + bits += CHAR_BIT; + while(bits >= 7) { + bits -= 7; + *bp++ = 0x80 | (cache >> bits); + } + } + if(bits) *bp++ = cache; + bp[-1] &= 0x7f; /* Clear the last bit */ + + return bp - arcbuf; +} + +int +OBJECT_IDENTIFIER_set_arcs(OBJECT_IDENTIFIER_t *oid, const void *arcs, unsigned int arc_type_size, unsigned int arc_slots) { + uint8_t *buf; + uint8_t *bp; + unsigned LE = 1; /* Little endian (x86) */ + unsigned isLittleEndian = *((char *)&LE); + unsigned int arc0; + unsigned int arc1; + unsigned size; + unsigned i; + + if(!oid || !arcs || arc_type_size < 1 + || arc_type_size > 16 + || arc_slots < 2) { + errno = EINVAL; + return -1; + } + + switch(arc_type_size) { + case sizeof(char): + arc0 = ((const unsigned char *)arcs)[0]; + arc1 = ((const unsigned char *)arcs)[1]; + break; + case sizeof(short): + arc0 = ((const unsigned short *)arcs)[0]; + arc1 = ((const unsigned short *)arcs)[1]; + break; + case sizeof(int): + arc0 = ((const unsigned int *)arcs)[0]; + arc1 = ((const unsigned int *)arcs)[1]; + break; + default: + arc1 = arc0 = 0; + if(isLittleEndian) { /* Little endian (x86) */ + const unsigned char *ps, *pe; + /* If more significant bytes are present, + * make them > 255 quick */ + for(ps = (const unsigned char *)arcs + 1, pe = ps+arc_type_size; + ps < pe; ps++) + arc0 |= *ps, arc1 |= *(ps + arc_type_size); + arc0 <<= CHAR_BIT, arc1 <<= CHAR_BIT; + arc0 = *((const unsigned char *)arcs + 0); + arc1 = *((const unsigned char *)arcs + arc_type_size); + } else { + const unsigned char *ps, *pe; + /* If more significant bytes are present, + * make them > 255 quick */ + for(ps = (const unsigned char *)arcs, pe = ps+arc_type_size - 1; ps < pe; ps++) + arc0 |= *ps, arc1 |= *(ps + arc_type_size); + arc0 = *((const unsigned char *)arcs + arc_type_size - 1); + arc1 = *((const unsigned char *)arcs +(arc_type_size<< 1)-1); + } + } + + /* + * The previous chapter left us with the first and the second arcs. + * The values are not precise (that is, they are valid only if + * they're less than 255), but OK for the purposes of making + * the sanity test below. + */ + if(arc0 <= 1) { + if(arc1 >= 39) { + /* 8.19.4: At most 39 subsequent values (including 0) */ + errno = ERANGE; + return -1; + } + } else if(arc0 > 2) { + /* 8.19.4: Only three values are allocated from the root node */ + errno = ERANGE; + return -1; + } + /* + * After above tests it is known that the value of arc0 is completely + * trustworthy (0..2). However, the arc1's value is still meaningless. + */ + + /* + * Roughly estimate the maximum size necessary to encode these arcs. + * This estimation implicitly takes in account the following facts, + * that cancel each other: + * * the first two arcs are encoded in a single value. + * * the first value may require more space (+1 byte) + * * the value of the first arc which is in range (0..2) + */ + size = ((arc_type_size * CHAR_BIT + 6) / 7) * arc_slots; + bp = buf = (uint8_t *)MALLOC(size + 1); + if(!buf) { + /* ENOMEM */ + return -1; + } + + /* + * Encode the first two arcs. + * These require special treatment. + */ + { + uint8_t *tp; + uint8_t first_value[1 + 16]; /* of two arcs */ + uint8_t *fv = first_value; + + /* + * Simulate first_value = arc0 * 40 + arc1; + */ + /* Copy the second (1'st) arcs[1] into the first_value */ + *fv++ = 0; + arcs = ((const char *)arcs) + arc_type_size; + if(isLittleEndian) { + const uint8_t *aend = (const unsigned char *)arcs - 1; + const uint8_t *a1 = (const unsigned char *)arcs + arc_type_size - 1; + for(; a1 > aend; fv++, a1--) *fv = *a1; + } else { + const uint8_t *a1 = (const uint8_t *)arcs; + const uint8_t *aend = a1 + arc_type_size; + for(; a1 < aend; fv++, a1++) *fv = *a1; + } + /* Increase the first_value by arc0 */ + arc0 *= 40; /* (0..80) */ + for(tp = first_value + arc_type_size; tp >= first_value; tp--) { + unsigned int v = *tp; + v += arc0; + *tp = v; + if(v >= (1 << CHAR_BIT)) arc0 = v >> CHAR_BIT; + else break; + } + + assert(tp >= first_value); + + bp += OBJECT_IDENTIFIER_set_single_arc(bp, first_value, + fv - first_value, 1); + } + + /* + * Save the rest of arcs. + */ + for(arcs = ((const char *)arcs) + arc_type_size, i = 2; + i < arc_slots; + i++, arcs = ((const char *)arcs) + arc_type_size) { + bp += OBJECT_IDENTIFIER_set_single_arc(bp, + arcs, arc_type_size, 0); + } + + assert((unsigned)(bp - buf) <= size); + + /* + * Replace buffer. + */ + oid->size = bp - buf; + bp = oid->buf; + oid->buf = buf; + if(bp) FREEMEM(bp); + + return 0; +} + + +int +OBJECT_IDENTIFIER_parse_arcs(const char *oid_text, ssize_t oid_txt_length, + long *arcs, unsigned int arcs_slots, const char **opt_oid_text_end) { + unsigned int arcs_count = 0; + const char *oid_end; + enum { + ST_LEADSPACE, + ST_TAILSPACE, + ST_AFTERVALUE, /* Next character ought to be '.' or a space */ + ST_WAITDIGITS /* Next character is expected to be a digit */ + } state = ST_LEADSPACE; + + if(!oid_text || oid_txt_length < -1 || (arcs_slots && !arcs)) { + if(opt_oid_text_end) *opt_oid_text_end = oid_text; + errno = EINVAL; + return -1; + } + + if(oid_txt_length == -1) + oid_txt_length = strlen(oid_text); + +#define _OID_CAPTURE_ARC(oid_text, oid_end) do { \ + const char *endp = oid_end; \ + long value; \ + switch(asn_strtol_lim(oid_text, &endp, &value)) { \ + case ASN_STRTOL_EXTRA_DATA: \ + case ASN_STRTOL_OK: \ + if(arcs_count < arcs_slots) \ + arcs[arcs_count] = value; \ + arcs_count++; \ + oid_text = endp - 1; \ + break; \ + case ASN_STRTOL_ERROR_RANGE: \ + if(opt_oid_text_end) \ + *opt_oid_text_end = oid_text; \ + errno = ERANGE; \ + return -1; \ + case ASN_STRTOL_ERROR_INVAL: \ + case ASN_STRTOL_EXPECT_MORE: \ + if(opt_oid_text_end) \ + *opt_oid_text_end = oid_text; \ + errno = EINVAL; \ + return -1; \ + } \ + } while(0) + + for(oid_end = oid_text + oid_txt_length; oid_text broken OID */ + return -1; + case ST_LEADSPACE: + case ST_WAITDIGITS: + _OID_CAPTURE_ARC(oid_text, oid_end); + state = ST_AFTERVALUE; + continue; + } + break; + default: + /* Unexpected symbols */ + state = ST_WAITDIGITS; + break; + } /* switch() */ + break; + } /* for() */ + + + if(opt_oid_text_end) *opt_oid_text_end = oid_text; + + /* Finalize last arc */ + switch(state) { + case ST_LEADSPACE: + return 0; /* No OID found in input data */ + case ST_WAITDIGITS: + errno = EINVAL; /* Broken OID */ + return -1; + case ST_AFTERVALUE: + case ST_TAILSPACE: + return arcs_count; + } + + errno = EINVAL; /* Broken OID */ + return -1; +} + + diff --git a/asn1_test_data/OBJECT_IDENTIFIER.h b/asn1_test_data/OBJECT_IDENTIFIER.h new file mode 100644 index 0000000..c2c6373 --- /dev/null +++ b/asn1_test_data/OBJECT_IDENTIFIER.h @@ -0,0 +1,139 @@ +/*- + * Copyright (c) 2003, 2004, 2005 Lev Walkin . + * All rights reserved. + * Redistribution and modifications are permitted subject to BSD license. + */ +#ifndef _OBJECT_IDENTIFIER_H_ +#define _OBJECT_IDENTIFIER_H_ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef ASN__PRIMITIVE_TYPE_t OBJECT_IDENTIFIER_t; + +extern asn_TYPE_descriptor_t asn_DEF_OBJECT_IDENTIFIER; + +asn_struct_print_f OBJECT_IDENTIFIER_print; +asn_constr_check_f OBJECT_IDENTIFIER_constraint; +der_type_encoder_f OBJECT_IDENTIFIER_encode_der; +xer_type_decoder_f OBJECT_IDENTIFIER_decode_xer; +xer_type_encoder_f OBJECT_IDENTIFIER_encode_xer; + +/********************************** + * Some handy conversion routines * + **********************************/ + +/* + * This function fills an (_arcs) array with OBJECT IDENTIFIER arcs + * up to specified (_arc_slots) elements. + * + * EXAMPLE: + * void print_arcs(OBJECT_IDENTIFIER_t *oid) { + * unsigned long fixed_arcs[10]; // Try with fixed space first + * unsigned long *arcs = fixed_arcs; + * int arc_type_size = sizeof(fixed_arcs[0]); // sizeof(long) + * int arc_slots = sizeof(fixed_arcs)/sizeof(fixed_arcs[0]); // 10 + * int count; // Real number of arcs. + * int i; + * + * count = OBJECT_IDENTIFIER_get_arcs(oid, arcs, + * arc_type_size, arc_slots); + * // If necessary, reallocate arcs array and try again. + * if(count > arc_slots) { + * arc_slots = count; + * arcs = malloc(arc_type_size * arc_slots); + * if(!arcs) return; + * count = OBJECT_IDENTIFIER_get_arcs(oid, arcs, + * arc_type_size, arc_slots); + * assert(count == arc_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 + * + * WARNING: The function always returns the real number of arcs, + * even if there is no sufficient (_arc_slots) provided. + */ +int OBJECT_IDENTIFIER_get_arcs(const OBJECT_IDENTIFIER_t *_oid, + void *_arcs, /* e.g., unsigned int arcs[N] */ + unsigned int _arc_type_size, /* e.g., sizeof(arcs[0]) */ + unsigned int _arc_slots /* e.g., N */); + +/* + * 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(OBJECT_IDENTIFIER_t *_oid, + const void *_arcs, /* e.g., unsigned int arcs[N] */ + unsigned int _arc_type_size, /* e.g., sizeof(arcs[0]) */ + unsigned int _arc_slots /* e.g., N */); + +/* + * Print the specified OBJECT IDENTIFIER arc. + */ +int OBJECT_IDENTIFIER_print_arc(const 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); + +/* Same as above, but returns the number of written digits, instead of 0 */ +ssize_t OBJECT_IDENTIFIER__dump_arc(const uint8_t *arcbuf, int arclen, int add, + asn_app_consume_bytes_f *cb, void *app_key); + +/* + * Parse the OBJECT IDENTIFIER textual representation ("1.3.6.1.4.1.9363"). + * No arc can exceed the (0..signed_long_max) range (typically, 0..2G if L32). + * This function is not specific to OBJECT IDENTIFIER, it may be used to parse + * the RELATIVE-OID data, or any other data consisting of dot-separated + * series of numeric values. + * + * If (oid_txt_length == -1), the strlen() will be invoked to determine the + * size of the (oid_text) string. + * + * After return, the optional (opt_oid_text_end) is set to the character after + * the last parsed one. (opt_oid_text_end) is never less than (oid_text). + * + * RETURN VALUES: + * -1: Parse error. + * >= 0: Number of arcs contained in the OBJECT IDENTIFIER. + * + * WARNING: The function always returns the real number of arcs, + * even if there is no sufficient (_arc_slots) provided. + * This is useful for (_arc_slots) value estimation. + */ +int OBJECT_IDENTIFIER_parse_arcs(const char *oid_text, ssize_t oid_txt_length, + long arcs[], unsigned int arcs_slots, const char **opt_oid_text_end); + +/* + * Internal functions. + * Used by RELATIVE-OID implementation in particular. + */ +int OBJECT_IDENTIFIER_get_single_arc(const uint8_t *arcbuf, unsigned int arclen, + signed int add, void *value, unsigned int value_size); +int OBJECT_IDENTIFIER_set_single_arc(uint8_t *arcbuf, + const void *arcval, unsigned int arcval_size, int _prepared_order); + +#ifdef __cplusplus +} +#endif + +#endif /* _OBJECT_IDENTIFIER_H_ */ diff --git a/asn1_test_data/OCTET_STRING.c b/asn1_test_data/OCTET_STRING.c new file mode 100644 index 0000000..5420ded --- /dev/null +++ b/asn1_test_data/OCTET_STRING.c @@ -0,0 +1,1807 @@ +/*- + * Copyright (c) 2003, 2004, 2005, 2006 Lev Walkin . + * All rights reserved. + * Redistribution and modifications are permitted subject to BSD license. + */ +#include +#include +#include /* for .bits_unused member */ +#include + +/* + * OCTET STRING basic type description. + */ +static const ber_tlv_tag_t asn_DEF_OCTET_STRING_tags[] = { + (ASN_TAG_CLASS_UNIVERSAL | (4 << 2)) +}; +static const asn_OCTET_STRING_specifics_t asn_DEF_OCTET_STRING_specs = { + sizeof(OCTET_STRING_t), + offsetof(OCTET_STRING_t, _asn_ctx), + ASN_OSUBV_STR +}; +static const asn_per_constraints_t asn_DEF_OCTET_STRING_constraints = { + { APC_CONSTRAINED, 8, 8, 0, 255 }, + { APC_SEMI_CONSTRAINED, -1, -1, 0, 0 }, + 0, 0 +}; +asn_TYPE_descriptor_t asn_DEF_OCTET_STRING = { + "OCTET STRING", /* Canonical name */ + "OCTET_STRING", /* XML tag name */ + OCTET_STRING_free, + OCTET_STRING_print, /* non-ascii stuff, generally */ + asn_generic_no_constraint, + OCTET_STRING_decode_ber, + OCTET_STRING_encode_der, + OCTET_STRING_decode_xer_hex, + OCTET_STRING_encode_xer, + OCTET_STRING_decode_uper, /* Unaligned PER decoder */ + OCTET_STRING_encode_uper, /* Unaligned PER encoder */ + 0, /* Use generic outmost tag fetcher */ + asn_DEF_OCTET_STRING_tags, + sizeof(asn_DEF_OCTET_STRING_tags) + / sizeof(asn_DEF_OCTET_STRING_tags[0]), + asn_DEF_OCTET_STRING_tags, /* Same as above */ + sizeof(asn_DEF_OCTET_STRING_tags) + / sizeof(asn_DEF_OCTET_STRING_tags[0]), + 0, /* No PER visible constraints */ + 0, 0, /* No members */ + &asn_DEF_OCTET_STRING_specs +}; + +#undef _CH_PHASE +#undef NEXT_PHASE +#undef PREV_PHASE +#define _CH_PHASE(ctx, inc) do { \ + if(ctx->phase == 0) \ + ctx->context = 0; \ + ctx->phase += inc; \ + } while(0) +#define NEXT_PHASE(ctx) _CH_PHASE(ctx, +1) +#define PREV_PHASE(ctx) _CH_PHASE(ctx, -1) + +#undef ADVANCE +#define ADVANCE(num_bytes) do { \ + size_t num = (num_bytes); \ + buf_ptr = ((const char *)buf_ptr) + num; \ + size -= num; \ + consumed_myself += num; \ + } while(0) + +#undef RETURN +#define RETURN(_code) do { \ + asn_dec_rval_t tmprval; \ + tmprval.code = _code; \ + tmprval.consumed = consumed_myself; \ + return tmprval; \ + } while(0) + +#undef APPEND +#define APPEND(bufptr, bufsize) do { \ + size_t _bs = (bufsize); /* Append size */ \ + size_t _ns = ctx->context; /* Allocated now */ \ + size_t _es = st->size + _bs; /* Expected size */ \ + /* int is really a typeof(st->size): */ \ + if((int)_es < 0) RETURN(RC_FAIL); \ + if(_ns <= _es) { \ + void *ptr; \ + /* Be nice and round to the memory allocator */ \ + do { _ns = _ns ? _ns << 1 : 16; } \ + while(_ns <= _es); \ + /* int is really a typeof(st->size): */ \ + if((int)_ns < 0) RETURN(RC_FAIL); \ + ptr = REALLOC(st->buf, _ns); \ + if(ptr) { \ + st->buf = (uint8_t *)ptr; \ + ctx->context = _ns; \ + } else { \ + RETURN(RC_FAIL); \ + } \ + ASN_DEBUG("Reallocating into %ld", (long)_ns); \ + } \ + memcpy(st->buf + st->size, bufptr, _bs); \ + /* Convenient nul-termination */ \ + st->buf[_es] = '\0'; \ + st->size = _es; \ + } 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 (or -1) */ + ber_tlv_len_t got; /* What was actually processed */ + int cont_level; /* Depth of subcontainment */ + int want_nulls; /* Want null "end of content" octets? */ + int bits_chopped; /* Flag in BIT STRING mode */ + ber_tlv_tag_t tag; /* For debugging purposes */ + struct _stack_el *prev; + struct _stack_el *next; +}; +struct _stack { + struct _stack_el *tail; + struct _stack_el *cur_ptr; +}; + +static struct _stack_el * +OS__add_stack_el(struct _stack *st) { + struct _stack_el *nel; + + /* + * Reuse the old stack frame or allocate a new one. + */ + if(st->cur_ptr && st->cur_ptr->next) { + nel = st->cur_ptr->next; + nel->bits_chopped = 0; + nel->got = 0; + /* Retain the nel->cont_level, it's correct. */ + } else { + nel = (struct _stack_el *)CALLOC(1, sizeof(struct _stack_el)); + if(nel == NULL) + return NULL; + + if(st->tail) { + /* Increase a subcontainment depth */ + nel->cont_level = st->tail->cont_level + 1; + st->tail->next = nel; + } + nel->prev = st->tail; + st->tail = nel; + } + + st->cur_ptr = nel; + + return nel; +} + +static struct _stack * +_new_stack() { + return (struct _stack *)CALLOC(1, sizeof(struct _stack)); +} + +/* + * Decode OCTET STRING type. + */ +asn_dec_rval_t +OCTET_STRING_decode_ber(asn_codec_ctx_t *opt_codec_ctx, + asn_TYPE_descriptor_t *td, + void **sptr, const void *buf_ptr, size_t size, int tag_mode) { + asn_OCTET_STRING_specifics_t *specs = td->specifics + ? (asn_OCTET_STRING_specifics_t *)td->specifics + : &asn_DEF_OCTET_STRING_specs; + BIT_STRING_t *st = (BIT_STRING_t *)*sptr; + asn_dec_rval_t rval; + asn_struct_ctx_t *ctx; + ssize_t consumed_myself = 0; + struct _stack *stck; /* Expectations stack structure */ + struct _stack_el *sel = 0; /* Stack element */ + int tlv_constr; + enum asn_OS_Subvariant type_variant = specs->subvariant; + + ASN_DEBUG("Decoding %s as %s (frame %ld)", + td->name, + (type_variant == ASN_OSUBV_STR) ? + "OCTET STRING" : "OS-SpecialCase", + (long)size); + + /* + * Create the string if does not exist. + */ + if(st == NULL) { + st = (BIT_STRING_t *)(*sptr = CALLOC(1, specs->struct_size)); + if(st == NULL) RETURN(RC_FAIL); + } + + /* Restore parsing context */ + ctx = (asn_struct_ctx_t *)((char *)st + specs->ctx_offset); + + switch(ctx->phase) { + case 0: + /* + * Check tags. + */ + rval = ber_check_tags(opt_codec_ctx, td, ctx, + buf_ptr, size, tag_mode, -1, + &ctx->left, &tlv_constr); + if(rval.code != RC_OK) + return rval; + + if(tlv_constr) { + /* + * Complex operation, requires stack of expectations. + */ + ctx->ptr = _new_stack(); + if(ctx->ptr) { + stck = (struct _stack *)ctx->ptr; + } else { + RETURN(RC_FAIL); + } + } else { + /* + * Jump into stackless primitive decoding. + */ + _CH_PHASE(ctx, 3); + if(type_variant == ASN_OSUBV_ANY && tag_mode != 1) + APPEND(buf_ptr, rval.consumed); + ADVANCE(rval.consumed); + goto phase3; + } + + NEXT_PHASE(ctx); + /* Fall through */ + case 1: + phase1: + /* + * Fill the stack with expectations. + */ + stck = (struct _stack *)ctx->ptr; + sel = stck->cur_ptr; + do { + ber_tlv_tag_t tlv_tag; + ber_tlv_len_t tlv_len; + ber_tlv_tag_t expected_tag; + ssize_t tl, ll, tlvl; + /* This one works even if (sel->left == -1) */ + ssize_t Left = ((!sel||(size_t)sel->left >= size) + ?(ssize_t)size:sel->left); + + + ASN_DEBUG("%p, s->l=%ld, s->wn=%ld, s->g=%ld\n", sel, + (long)(sel?sel->left:0), + (long)(sel?sel->want_nulls:0), + (long)(sel?sel->got:0) + ); + if(sel && sel->left <= 0 && sel->want_nulls == 0) { + if(sel->prev) { + struct _stack_el *prev = sel->prev; + if(prev->left != -1) { + if(prev->left < sel->got) + RETURN(RC_FAIL); + prev->left -= sel->got; + } + prev->got += sel->got; + sel = stck->cur_ptr = prev; + if(!sel) break; + tlv_constr = 1; + continue; + } else { + sel = stck->cur_ptr = 0; + break; /* Nothing to wait */ + } + } + + tl = ber_fetch_tag(buf_ptr, Left, &tlv_tag); + ASN_DEBUG("fetch tag(size=%ld,L=%ld), %sstack, left=%ld, wn=%ld, tl=%ld", + (long)size, (long)Left, sel?"":"!", + (long)(sel?sel->left:0), + (long)(sel?sel->want_nulls:0), + (long)tl); + 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, + (const char *)buf_ptr + tl,Left - tl,&tlv_len); + ASN_DEBUG("Got tag=%s, tc=%d, left=%ld, tl=%ld, len=%ld, ll=%ld", + ber_tlv_tag_string(tlv_tag), tlv_constr, + (long)Left, (long)tl, (long)tlv_len, (long)ll); + switch(ll) { + case -1: RETURN(RC_FAIL); + case 0: RETURN(RC_WMORE); + } + + if(sel && sel->want_nulls + && ((const uint8_t *)buf_ptr)[0] == 0 + && ((const uint8_t *)buf_ptr)[1] == 0) + { + + ASN_DEBUG("Eat EOC; wn=%d--", sel->want_nulls); + + if(type_variant == ASN_OSUBV_ANY + && (tag_mode != 1 || sel->cont_level)) + APPEND("\0\0", 2); + + ADVANCE(2); + sel->got += 2; + if(sel->left != -1) { + sel->left -= 2; /* assert(sel->left >= 2) */ + } + + sel->want_nulls--; + if(sel->want_nulls == 0) { + /* Move to the next expectation */ + sel->left = 0; + tlv_constr = 1; + } + + continue; + } + + /* + * Set up expected tags, + * depending on ASN.1 type being decoded. + */ + switch(type_variant) { + case ASN_OSUBV_BIT: + /* X.690: 8.6.4.1, NOTE 2 */ + /* Fall through */ + case ASN_OSUBV_STR: + default: + if(sel) { + int level = sel->cont_level; + if(level < td->all_tags_count) { + expected_tag = td->all_tags[level]; + break; + } else if(td->all_tags_count) { + expected_tag = td->all_tags + [td->all_tags_count - 1]; + break; + } + /* else, Fall through */ + } + /* Fall through */ + case ASN_OSUBV_ANY: + expected_tag = tlv_tag; + break; + } + + + if(tlv_tag != expected_tag) { + 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); + } + + tlvl = tl + ll; /* Combined length of T and L encoding */ + if((tlv_len + tlvl) < 0) { + /* tlv_len value is too big */ + ASN_DEBUG("TLV encoding + length (%ld) is too big", + (long)tlv_len); + RETURN(RC_FAIL); + } + + /* + * Append a new expectation. + */ + sel = OS__add_stack_el(stck); + if(!sel) RETURN(RC_FAIL); + + sel->tag = tlv_tag; + + sel->want_nulls = (tlv_len==-1); + if(sel->prev && sel->prev->left != -1) { + /* Check that the parent frame is big enough */ + if(sel->prev->left < tlvl + (tlv_len==-1?0:tlv_len)) + RETURN(RC_FAIL); + if(tlv_len == -1) + sel->left = sel->prev->left - tlvl; + else + sel->left = tlv_len; + } else { + sel->left = tlv_len; + } + if(type_variant == ASN_OSUBV_ANY + && (tag_mode != 1 || sel->cont_level)) + APPEND(buf_ptr, tlvl); + sel->got += tlvl; + ADVANCE(tlvl); + + ASN_DEBUG("+EXPECT2 got=%ld left=%ld, wn=%d, clvl=%d", + (long)sel->got, (long)sel->left, + sel->want_nulls, sel->cont_level); + + } while(tlv_constr); + if(sel == NULL) { + /* Finished operation, "phase out" */ + ASN_DEBUG("Phase out"); + _CH_PHASE(ctx, +3); + break; + } + + NEXT_PHASE(ctx); + /* Fall through */ + case 2: + stck = (struct _stack *)ctx->ptr; + sel = stck->cur_ptr; + ASN_DEBUG("Phase 2: Need %ld bytes, size=%ld, alrg=%ld, wn=%d", + (long)sel->left, (long)size, (long)sel->got, + sel->want_nulls); + { + ber_tlv_len_t len; + + assert(sel->left >= 0); + + len = ((ber_tlv_len_t)size < sel->left) + ? (ber_tlv_len_t)size : sel->left; + if(len > 0) { + if(type_variant == ASN_OSUBV_BIT + && sel->bits_chopped == 0) { + /* Put the unused-bits-octet away */ + st->bits_unused = *(const uint8_t *)buf_ptr; + APPEND(((const char *)buf_ptr+1), (len - 1)); + sel->bits_chopped = 1; + } else { + APPEND(buf_ptr, len); + } + ADVANCE(len); + sel->left -= len; + sel->got += len; + } + + if(sel->left) { + ASN_DEBUG("OS left %ld, size = %ld, wn=%d\n", + (long)sel->left, (long)size, sel->want_nulls); + RETURN(RC_WMORE); + } + + PREV_PHASE(ctx); + goto phase1; + } + break; + case 3: + phase3: + /* + * Primitive form, no stack required. + */ + assert(ctx->left >= 0); + + if(size < (size_t)ctx->left) { + if(!size) RETURN(RC_WMORE); + if(type_variant == ASN_OSUBV_BIT && !ctx->context) { + st->bits_unused = *(const uint8_t *)buf_ptr; + ctx->left--; + ADVANCE(1); + } + APPEND(buf_ptr, size); + assert(ctx->context > 0); + ctx->left -= size; + ADVANCE(size); + RETURN(RC_WMORE); + } else { + if(type_variant == ASN_OSUBV_BIT + && !ctx->context && ctx->left) { + st->bits_unused = *(const uint8_t *)buf_ptr; + ctx->left--; + ADVANCE(1); + } + APPEND(buf_ptr, ctx->left); + ADVANCE(ctx->left); + ctx->left = 0; + + NEXT_PHASE(ctx); + } + break; + } + + if(sel) { + ASN_DEBUG("3sel p=%p, wn=%d, l=%ld, g=%ld, size=%ld", + sel->prev, sel->want_nulls, + (long)sel->left, (long)sel->got, (long)size); + if(sel->prev || sel->want_nulls > 1 || sel->left > 0) { + RETURN(RC_WMORE); + } + } + + /* + * BIT STRING-specific processing. + */ + if(type_variant == ASN_OSUBV_BIT && st->size) { + /* Finalize BIT STRING: zero out unused bits. */ + st->buf[st->size-1] &= 0xff << st->bits_unused; + } + + ASN_DEBUG("Took %ld bytes to encode %s: [%s]:%ld", + (long)consumed_myself, td->name, + (type_variant == ASN_OSUBV_STR) ? (char *)st->buf : "", + (long)st->size); + + + RETURN(RC_OK); +} + +/* + * Encode OCTET STRING type using DER. + */ +asn_enc_rval_t +OCTET_STRING_encode_der(asn_TYPE_descriptor_t *td, void *sptr, + int tag_mode, ber_tlv_tag_t tag, + asn_app_consume_bytes_f *cb, void *app_key) { + asn_enc_rval_t er; + asn_OCTET_STRING_specifics_t *specs = td->specifics + ? (asn_OCTET_STRING_specifics_t *)td->specifics + : &asn_DEF_OCTET_STRING_specs; + BIT_STRING_t *st = (BIT_STRING_t *)sptr; + enum asn_OS_Subvariant type_variant = specs->subvariant; + int fix_last_byte = 0; + + ASN_DEBUG("%s %s as OCTET STRING", + cb?"Estimating":"Encoding", td->name); + + /* + * Write tags. + */ + if(type_variant != ASN_OSUBV_ANY || tag_mode == 1) { + er.encoded = der_write_tags(td, + (type_variant == ASN_OSUBV_BIT) + st->size, + tag_mode, type_variant == ASN_OSUBV_ANY, tag, + cb, app_key); + if(er.encoded == -1) { + er.failed_type = td; + er.structure_ptr = sptr; + return er; + } + } else { + /* Disallow: [] IMPLICIT ANY */ + assert(type_variant != ASN_OSUBV_ANY || tag_mode != -1); + er.encoded = 0; + } + + if(!cb) { + er.encoded += (type_variant == ASN_OSUBV_BIT) + st->size; + ASN__ENCODED_OK(er); + } + + /* + * Prepare to deal with the last octet of BIT STRING. + */ + if(type_variant == ASN_OSUBV_BIT) { + uint8_t b = st->bits_unused & 0x07; + if(b && st->size) fix_last_byte = 1; + ASN__CALLBACK(&b, 1); + er.encoded++; + } + + /* Invoke callback for the main part of the buffer */ + ASN__CALLBACK(st->buf, st->size - fix_last_byte); + + /* The last octet should be stripped off the unused bits */ + if(fix_last_byte) { + uint8_t b = st->buf[st->size-1] & (0xff << st->bits_unused); + ASN__CALLBACK(&b, 1); + } + + er.encoded += st->size; + ASN__ENCODED_OK(er); +cb_failed: + ASN__ENCODE_FAILED; +} + +asn_enc_rval_t +OCTET_STRING_encode_xer(asn_TYPE_descriptor_t *td, void *sptr, + int ilevel, enum xer_encoder_flags_e flags, + asn_app_consume_bytes_f *cb, void *app_key) { + const char * const h2c = "0123456789ABCDEF"; + const OCTET_STRING_t *st = (const OCTET_STRING_t *)sptr; + asn_enc_rval_t er; + char scratch[16 * 3 + 4]; + char *p = scratch; + uint8_t *buf; + uint8_t *end; + size_t i; + + if(!st || (!st->buf && st->size)) + ASN__ENCODE_FAILED; + + er.encoded = 0; + + /* + * Dump the contents of the buffer in hexadecimal. + */ + buf = st->buf; + end = buf + st->size; + if(flags & XER_F_CANONICAL) { + char *scend = scratch + (sizeof(scratch) - 2); + for(; buf < end; buf++) { + if(p >= scend) { + ASN__CALLBACK(scratch, p - scratch); + er.encoded += p - scratch; + p = scratch; + } + *p++ = h2c[(*buf >> 4) & 0x0F]; + *p++ = h2c[*buf & 0x0F]; + } + + ASN__CALLBACK(scratch, p-scratch); /* Dump the rest */ + er.encoded += p - scratch; + } else { + for(i = 0; buf < end; buf++, i++) { + if(!(i % 16) && (i || st->size > 16)) { + ASN__CALLBACK(scratch, p-scratch); + er.encoded += (p-scratch); + p = scratch; + ASN__TEXT_INDENT(1, ilevel); + } + *p++ = h2c[(*buf >> 4) & 0x0F]; + *p++ = h2c[*buf & 0x0F]; + *p++ = 0x20; + } + if(p - scratch) { + p--; /* Remove the tail space */ + ASN__CALLBACK(scratch, p-scratch); /* Dump the rest */ + er.encoded += p - scratch; + if(st->size > 16) + ASN__TEXT_INDENT(1, ilevel-1); + } + } + + ASN__ENCODED_OK(er); +cb_failed: + ASN__ENCODE_FAILED; +} + +static const struct OCTET_STRING__xer_escape_table_s { + const char *string; + int size; +} OCTET_STRING__xer_escape_table[] = { +#define OSXET(s) { s, sizeof(s) - 1 } + OSXET("\074\156\165\154\057\076"), /* */ + OSXET("\074\163\157\150\057\076"), /* */ + OSXET("\074\163\164\170\057\076"), /* */ + OSXET("\074\145\164\170\057\076"), /* */ + OSXET("\074\145\157\164\057\076"), /* */ + OSXET("\074\145\156\161\057\076"), /* */ + OSXET("\074\141\143\153\057\076"), /* */ + OSXET("\074\142\145\154\057\076"), /* */ + OSXET("\074\142\163\057\076"), /* */ + OSXET("\011"), /* \t */ + OSXET("\012"), /* \n */ + OSXET("\074\166\164\057\076"), /* */ + OSXET("\074\146\146\057\076"), /* */ + OSXET("\015"), /* \r */ + OSXET("\074\163\157\057\076"), /* */ + OSXET("\074\163\151\057\076"), /* */ + OSXET("\074\144\154\145\057\076"), /* */ + OSXET("\074\144\143\061\057\076"), /* */ + OSXET("\074\144\143\062\057\076"), /* */ + OSXET("\074\144\143\063\057\076"), /* */ + OSXET("\074\144\143\064\057\076"), /* */ + OSXET("\074\156\141\153\057\076"), /* */ + OSXET("\074\163\171\156\057\076"), /* */ + OSXET("\074\145\164\142\057\076"), /* */ + OSXET("\074\143\141\156\057\076"), /* */ + OSXET("\074\145\155\057\076"), /* */ + OSXET("\074\163\165\142\057\076"), /* */ + OSXET("\074\145\163\143\057\076"), /* */ + OSXET("\074\151\163\064\057\076"), /* */ + OSXET("\074\151\163\063\057\076"), /* */ + OSXET("\074\151\163\062\057\076"), /* */ + OSXET("\074\151\163\061\057\076"), /* */ + { 0, 0 }, /* " " */ + { 0, 0 }, /* ! */ + { 0, 0 }, /* \" */ + { 0, 0 }, /* # */ + { 0, 0 }, /* $ */ + { 0, 0 }, /* % */ + OSXET("\046\141\155\160\073"), /* & */ + { 0, 0 }, /* ' */ + {0,0},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, /* ()*+,-./ */ + {0,0},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, /* 01234567 */ + {0,0},{0,0},{0,0},{0,0}, /* 89:; */ + OSXET("\046\154\164\073"), /* < */ + { 0, 0 }, /* = */ + OSXET("\046\147\164\073"), /* > */ +}; + +static int +OS__check_escaped_control_char(const void *buf, int size) { + size_t i; + /* + * Inefficient algorithm which translates the escape sequences + * defined above into characters. Returns -1 if not found. + * TODO: replace by a faster algorithm (bsearch(), hash or + * nested table lookups). + */ + for(i = 0; i < 32 /* Don't spend time on the bottom half */; i++) { + const struct OCTET_STRING__xer_escape_table_s *el; + el = &OCTET_STRING__xer_escape_table[i]; + if(el->size == size && memcmp(buf, el->string, size) == 0) + return i; + } + return -1; +} + +static int +OCTET_STRING__handle_control_chars(void *struct_ptr, const void *chunk_buf, size_t chunk_size) { + /* + * This might be one of the escape sequences + * for control characters. Check it out. + * #11.15.5 + */ + int control_char = OS__check_escaped_control_char(chunk_buf,chunk_size); + if(control_char >= 0) { + OCTET_STRING_t *st = (OCTET_STRING_t *)struct_ptr; + void *p = REALLOC(st->buf, st->size + 2); + if(p) { + st->buf = (uint8_t *)p; + st->buf[st->size++] = control_char; + st->buf[st->size] = '\0'; /* nul-termination */ + return 0; + } + } + + return -1; /* No, it's not */ +} + +asn_enc_rval_t +OCTET_STRING_encode_xer_utf8(asn_TYPE_descriptor_t *td, void *sptr, + int ilevel, enum xer_encoder_flags_e flags, + asn_app_consume_bytes_f *cb, void *app_key) { + const OCTET_STRING_t *st = (const OCTET_STRING_t *)sptr; + asn_enc_rval_t er; + uint8_t *buf, *end; + uint8_t *ss; /* Sequence start */ + ssize_t encoded_len = 0; + + (void)ilevel; /* Unused argument */ + (void)flags; /* Unused argument */ + + if(!st || (!st->buf && st->size)) + ASN__ENCODE_FAILED; + + buf = st->buf; + end = buf + st->size; + for(ss = buf; buf < end; buf++) { + unsigned int ch = *buf; + int s_len; /* Special encoding sequence length */ + + /* + * Escape certain characters: X.680/11.15 + */ + if(ch < sizeof(OCTET_STRING__xer_escape_table) + /sizeof(OCTET_STRING__xer_escape_table[0]) + && (s_len = OCTET_STRING__xer_escape_table[ch].size)) { + if(((buf - ss) && cb(ss, buf - ss, app_key) < 0) + || cb(OCTET_STRING__xer_escape_table[ch].string, s_len, + app_key) < 0) + ASN__ENCODE_FAILED; + encoded_len += (buf - ss) + s_len; + ss = buf + 1; + } + } + + encoded_len += (buf - ss); + if((buf - ss) && cb(ss, buf - ss, app_key) < 0) + ASN__ENCODE_FAILED; + + er.encoded = encoded_len; + ASN__ENCODED_OK(er); +} + +/* + * Convert from hexadecimal format (cstring): "AB CD EF" + */ +static ssize_t OCTET_STRING__convert_hexadecimal(void *sptr, const void *chunk_buf, size_t chunk_size, int have_more) { + OCTET_STRING_t *st = (OCTET_STRING_t *)sptr; + const char *chunk_stop = (const char *)chunk_buf; + const char *p = chunk_stop; + const char *pend = p + chunk_size; + unsigned int clv = 0; + int half = 0; /* Half bit */ + uint8_t *buf; + + /* Reallocate buffer according to high cap estimation */ + ssize_t _ns = st->size + (chunk_size + 1) / 2; + void *nptr = REALLOC(st->buf, _ns + 1); + if(!nptr) return -1; + st->buf = (uint8_t *)nptr; + buf = st->buf + st->size; + + /* + * If something like " a b c " appears here, the " a b":3 will be + * converted, and the rest skipped. That is, unless buf_size is greater + * than chunk_size, then it'll be equivalent to "ABC0". + */ + for(; p < pend; p++) { + int ch = *(const unsigned char *)p; + switch(ch) { + case 0x09: case 0x0a: case 0x0c: case 0x0d: + case 0x20: + /* Ignore whitespace */ + continue; + case 0x30: case 0x31: case 0x32: case 0x33: case 0x34: /*01234*/ + case 0x35: case 0x36: case 0x37: case 0x38: case 0x39: /*56789*/ + clv = (clv << 4) + (ch - 0x30); + break; + case 0x41: case 0x42: case 0x43: /* ABC */ + case 0x44: case 0x45: case 0x46: /* DEF */ + clv = (clv << 4) + (ch - 0x41 + 10); + break; + case 0x61: case 0x62: case 0x63: /* abc */ + case 0x64: case 0x65: case 0x66: /* def */ + clv = (clv << 4) + (ch - 0x61 + 10); + break; + default: + *buf = 0; /* JIC */ + return -1; + } + if(half++) { + half = 0; + *buf++ = clv; + chunk_stop = p + 1; + } + } + + /* + * Check partial decoding. + */ + if(half) { + if(have_more) { + /* + * Partial specification is fine, + * because no more more PXER_TEXT data is available. + */ + *buf++ = clv << 4; + chunk_stop = p; + } + } else { + chunk_stop = p; + } + + st->size = buf - st->buf; /* Adjust the buffer size */ + assert(st->size <= _ns); + st->buf[st->size] = 0; /* Courtesy termination */ + + return (chunk_stop - (const char *)chunk_buf); /* Converted size */ +} + +/* + * Convert from binary format: "00101011101" + */ +static ssize_t OCTET_STRING__convert_binary(void *sptr, const void *chunk_buf, size_t chunk_size, int have_more) { + BIT_STRING_t *st = (BIT_STRING_t *)sptr; + const char *p = (const char *)chunk_buf; + const char *pend = p + chunk_size; + int bits_unused = st->bits_unused & 0x7; + uint8_t *buf; + + /* Reallocate buffer according to high cap estimation */ + ssize_t _ns = st->size + (chunk_size + 7) / 8; + void *nptr = REALLOC(st->buf, _ns + 1); + if(!nptr) return -1; + st->buf = (uint8_t *)nptr; + buf = st->buf + st->size; + + (void)have_more; + + if(bits_unused == 0) + bits_unused = 8; + else if(st->size) + buf--; + + /* + * Convert series of 0 and 1 into the octet string. + */ + for(; p < pend; p++) { + int ch = *(const unsigned char *)p; + switch(ch) { + case 0x09: case 0x0a: case 0x0c: case 0x0d: + case 0x20: + /* Ignore whitespace */ + break; + case 0x30: + case 0x31: + if(bits_unused-- <= 0) { + *++buf = 0; /* Clean the cell */ + bits_unused = 7; + } + *buf |= (ch&1) << bits_unused; + break; + default: + st->bits_unused = bits_unused; + return -1; + } + } + + if(bits_unused == 8) { + st->size = buf - st->buf; + st->bits_unused = 0; + } else { + st->size = buf - st->buf + 1; + st->bits_unused = bits_unused; + } + + assert(st->size <= _ns); + st->buf[st->size] = 0; /* Courtesy termination */ + + return chunk_size; /* Converted in full */ +} + +/* + * Something like strtod(), but with stricter rules. + */ +static int +OS__strtoent(int base, const char *buf, const char *end, int32_t *ret_value) { + int32_t val = 0; + const char *p; + + for(p = buf; p < end; p++) { + int ch = *p; + + /* Strange huge value */ + if((val * base + base) < 0) + return -1; + + switch(ch) { + case 0x30: case 0x31: case 0x32: case 0x33: case 0x34: /*01234*/ + case 0x35: case 0x36: case 0x37: case 0x38: case 0x39: /*56789*/ + val = val * base + (ch - 0x30); + break; + case 0x41: case 0x42: case 0x43: /* ABC */ + case 0x44: case 0x45: case 0x46: /* DEF */ + val = val * base + (ch - 0x41 + 10); + break; + case 0x61: case 0x62: case 0x63: /* abc */ + case 0x64: case 0x65: case 0x66: /* def */ + val = val * base + (ch - 0x61 + 10); + break; + case 0x3b: /* ';' */ + *ret_value = val; + return (p - buf) + 1; + default: + return -1; /* Character set error */ + } + } + + *ret_value = -1; + return (p - buf); +} + +/* + * Convert from the plain UTF-8 format, expanding entity references: "2 < 3" + */ +static ssize_t OCTET_STRING__convert_entrefs(void *sptr, const void *chunk_buf, size_t chunk_size, int have_more) { + OCTET_STRING_t *st = (OCTET_STRING_t *)sptr; + const char *p = (const char *)chunk_buf; + const char *pend = p + chunk_size; + uint8_t *buf; + + /* Reallocate buffer */ + ssize_t _ns = st->size + chunk_size; + void *nptr = REALLOC(st->buf, _ns + 1); + if(!nptr) return -1; + st->buf = (uint8_t *)nptr; + buf = st->buf + st->size; + + /* + * Convert series of 0 and 1 into the octet string. + */ + for(; p < pend; p++) { + int ch = *(const unsigned char *)p; + int len; /* Length of the rest of the chunk */ + + if(ch != 0x26 /* '&' */) { + *buf++ = ch; + continue; /* That was easy... */ + } + + /* + * Process entity reference. + */ + len = chunk_size - (p - (const char *)chunk_buf); + if(len == 1 /* "&" */) goto want_more; + if(p[1] == 0x23 /* '#' */) { + const char *pval; /* Pointer to start of digits */ + int32_t val = 0; /* Entity reference value */ + int base; + + if(len == 2 /* "&#" */) goto want_more; + if(p[2] == 0x78 /* 'x' */) + pval = p + 3, base = 16; + else + pval = p + 2, base = 10; + len = OS__strtoent(base, pval, p + len, &val); + if(len == -1) { + /* Invalid charset. Just copy verbatim. */ + *buf++ = ch; + continue; + } + if(!len || pval[len-1] != 0x3b) goto want_more; + assert(val > 0); + p += (pval - p) + len - 1; /* Advance past entref */ + + if(val < 0x80) { + *buf++ = (char)val; + } else if(val < 0x800) { + *buf++ = 0xc0 | ((val >> 6)); + *buf++ = 0x80 | ((val & 0x3f)); + } else if(val < 0x10000) { + *buf++ = 0xe0 | ((val >> 12)); + *buf++ = 0x80 | ((val >> 6) & 0x3f); + *buf++ = 0x80 | ((val & 0x3f)); + } else if(val < 0x200000) { + *buf++ = 0xf0 | ((val >> 18)); + *buf++ = 0x80 | ((val >> 12) & 0x3f); + *buf++ = 0x80 | ((val >> 6) & 0x3f); + *buf++ = 0x80 | ((val & 0x3f)); + } else if(val < 0x4000000) { + *buf++ = 0xf8 | ((val >> 24)); + *buf++ = 0x80 | ((val >> 18) & 0x3f); + *buf++ = 0x80 | ((val >> 12) & 0x3f); + *buf++ = 0x80 | ((val >> 6) & 0x3f); + *buf++ = 0x80 | ((val & 0x3f)); + } else { + *buf++ = 0xfc | ((val >> 30) & 0x1); + *buf++ = 0x80 | ((val >> 24) & 0x3f); + *buf++ = 0x80 | ((val >> 18) & 0x3f); + *buf++ = 0x80 | ((val >> 12) & 0x3f); + *buf++ = 0x80 | ((val >> 6) & 0x3f); + *buf++ = 0x80 | ((val & 0x3f)); + } + } else { + /* + * Ugly, limited parsing of & > < + */ + char *sc = (char *)memchr(p, 0x3b, len > 5 ? 5 : len); + if(!sc) goto want_more; + if((sc - p) == 4 + && p[1] == 0x61 /* 'a' */ + && p[2] == 0x6d /* 'm' */ + && p[3] == 0x70 /* 'p' */) { + *buf++ = 0x26; + p = sc; + continue; + } + if((sc - p) == 3) { + if(p[1] == 0x6c) { + *buf = 0x3c; /* '<' */ + } else if(p[1] == 0x67) { + *buf = 0x3e; /* '>' */ + } else { + /* Unsupported entity reference */ + *buf++ = ch; + continue; + } + if(p[2] != 0x74) { + /* Unsupported entity reference */ + *buf++ = ch; + continue; + } + buf++; + p = sc; + continue; + } + /* Unsupported entity reference */ + *buf++ = ch; + } + + continue; + want_more: + if(have_more) { + /* + * We know that no more data (of the same type) + * is coming. Copy the rest verbatim. + */ + *buf++ = ch; + continue; + } + chunk_size = (p - (const char *)chunk_buf); + /* Processing stalled: need more data */ + break; + } + + st->size = buf - st->buf; + assert(st->size <= _ns); + st->buf[st->size] = 0; /* Courtesy termination */ + + return chunk_size; /* Converted in full */ +} + +/* + * Decode OCTET STRING from the XML element's body. + */ +static asn_dec_rval_t +OCTET_STRING__decode_xer(asn_codec_ctx_t *opt_codec_ctx, + asn_TYPE_descriptor_t *td, void **sptr, + const char *opt_mname, const void *buf_ptr, size_t size, + int (*opt_unexpected_tag_decoder) + (void *struct_ptr, const void *chunk_buf, size_t chunk_size), + ssize_t (*body_receiver) + (void *struct_ptr, const void *chunk_buf, size_t chunk_size, + int have_more) +) { + OCTET_STRING_t *st = (OCTET_STRING_t *)*sptr; + asn_OCTET_STRING_specifics_t *specs = td->specifics + ? (asn_OCTET_STRING_specifics_t *)td->specifics + : &asn_DEF_OCTET_STRING_specs; + const char *xml_tag = opt_mname ? opt_mname : td->xml_tag; + asn_struct_ctx_t *ctx; /* Per-structure parser context */ + asn_dec_rval_t rval; /* Return value from the decoder */ + int st_allocated; + + /* + * Create the string if does not exist. + */ + if(!st) { + st = (OCTET_STRING_t *)CALLOC(1, specs->struct_size); + *sptr = (void *)st; + if(!st) goto sta_failed; + st_allocated = 1; + } else { + st_allocated = 0; + } + if(!st->buf) { + /* This is separate from above section */ + st->buf = (uint8_t *)CALLOC(1, 1); + if(!st->buf) { + if(st_allocated) { + *sptr = 0; + goto stb_failed; + } else { + goto sta_failed; + } + } + } + + /* Restore parsing context */ + ctx = (asn_struct_ctx_t *)(((char *)*sptr) + specs->ctx_offset); + + return xer_decode_general(opt_codec_ctx, ctx, *sptr, xml_tag, + buf_ptr, size, opt_unexpected_tag_decoder, body_receiver); + +stb_failed: + FREEMEM(st); +sta_failed: + rval.code = RC_FAIL; + rval.consumed = 0; + return rval; +} + +/* + * Decode OCTET STRING from the hexadecimal data. + */ +asn_dec_rval_t +OCTET_STRING_decode_xer_hex(asn_codec_ctx_t *opt_codec_ctx, + asn_TYPE_descriptor_t *td, void **sptr, + const char *opt_mname, const void *buf_ptr, size_t size) { + return OCTET_STRING__decode_xer(opt_codec_ctx, td, sptr, opt_mname, + buf_ptr, size, 0, OCTET_STRING__convert_hexadecimal); +} + +/* + * Decode OCTET STRING from the binary (0/1) data. + */ +asn_dec_rval_t +OCTET_STRING_decode_xer_binary(asn_codec_ctx_t *opt_codec_ctx, + asn_TYPE_descriptor_t *td, void **sptr, + const char *opt_mname, const void *buf_ptr, size_t size) { + return OCTET_STRING__decode_xer(opt_codec_ctx, td, sptr, opt_mname, + buf_ptr, size, 0, OCTET_STRING__convert_binary); +} + +/* + * Decode OCTET STRING from the string (ASCII/UTF-8) data. + */ +asn_dec_rval_t +OCTET_STRING_decode_xer_utf8(asn_codec_ctx_t *opt_codec_ctx, + asn_TYPE_descriptor_t *td, void **sptr, + const char *opt_mname, const void *buf_ptr, size_t size) { + return OCTET_STRING__decode_xer(opt_codec_ctx, td, sptr, opt_mname, + buf_ptr, size, + OCTET_STRING__handle_control_chars, + OCTET_STRING__convert_entrefs); +} + +static int +OCTET_STRING_per_get_characters(asn_per_data_t *po, uint8_t *buf, + size_t units, unsigned int bpc, unsigned int unit_bits, + long lb, long ub, asn_per_constraints_t *pc) { + uint8_t *end = buf + units * bpc; + + ASN_DEBUG("Expanding %d characters into (%ld..%ld):%d", + (int)units, lb, ub, unit_bits); + + /* X.691: 27.5.4 */ + if((unsigned long)ub <= ((unsigned long)2 << (unit_bits - 1))) { + /* Decode without translation */ + lb = 0; + } else if(pc && pc->code2value) { + if(unit_bits > 16) + return 1; /* FATAL: can't have constrained + * UniversalString with more than + * 16 million code points */ + for(; buf < end; buf += bpc) { + int value; + int code = per_get_few_bits(po, unit_bits); + if(code < 0) return -1; /* WMORE */ + value = pc->code2value(code); + if(value < 0) { + ASN_DEBUG("Code %d (0x%02x) is" + " not in map (%ld..%ld)", + code, code, lb, ub); + return 1; /* FATAL */ + } + switch(bpc) { + case 1: *buf = value; break; + case 2: buf[0] = value >> 8; buf[1] = value; break; + case 4: buf[0] = value >> 24; buf[1] = value >> 16; + buf[2] = value >> 8; buf[3] = value; break; + } + } + return 0; + } + + /* Shortcut the no-op copying to the aligned structure */ + if(lb == 0 && (unit_bits == 8 * bpc)) { + return per_get_many_bits(po, buf, 0, unit_bits * units); + } + + for(; buf < end; buf += bpc) { + int code = per_get_few_bits(po, unit_bits); + int ch = code + lb; + if(code < 0) return -1; /* WMORE */ + if(ch > ub) { + ASN_DEBUG("Code %d is out of range (%ld..%ld)", + ch, lb, ub); + return 1; /* FATAL */ + } + switch(bpc) { + case 1: *buf = ch; break; + case 2: buf[0] = ch >> 8; buf[1] = ch; break; + case 4: buf[0] = ch >> 24; buf[1] = ch >> 16; + buf[2] = ch >> 8; buf[3] = ch; break; + } + } + + return 0; +} + +static int +OCTET_STRING_per_put_characters(asn_per_outp_t *po, const uint8_t *buf, + size_t units, unsigned int bpc, unsigned int unit_bits, + long lb, long ub, asn_per_constraints_t *pc) { + const uint8_t *end = buf + units * bpc; + + ASN_DEBUG("Squeezing %d characters into (%ld..%ld):%d (%d bpc)", + (int)units, lb, ub, unit_bits, bpc); + + /* X.691: 27.5.4 */ + if((unsigned long)ub <= ((unsigned long)2 << (unit_bits - 1))) { + /* Encode as is */ + lb = 0; + } else if(pc && pc->value2code) { + for(; buf < end; buf += bpc) { + int code; + uint32_t value; + switch(bpc) { + case 1: value = *(const uint8_t *)buf; break; + case 2: value = (buf[0] << 8) | buf[1]; break; + case 4: value = (buf[0] << 24) | (buf[1] << 16) + | (buf[2] << 8) | buf[3]; break; + default: return -1; + } + code = pc->value2code(value); + if(code < 0) { + ASN_DEBUG("Character %d (0x%02x) is" + " not in map (%ld..%ld)", + *buf, *buf, lb, ub); + return -1; + } + if(per_put_few_bits(po, code, unit_bits)) + return -1; + } + } + + /* Shortcut the no-op copying to the aligned structure */ + if(lb == 0 && (unit_bits == 8 * bpc)) { + return per_put_many_bits(po, buf, unit_bits * units); + } + + for(ub -= lb; buf < end; buf += bpc) { + int ch; + uint32_t value; + switch(bpc) { + case 1: value = *(const uint8_t *)buf; break; + case 2: value = (buf[0] << 8) | buf[1]; break; + case 4: value = (buf[0] << 24) | (buf[1] << 16) + | (buf[2] << 8) | buf[3]; break; + default: return -1; + } + ch = value - lb; + if(ch < 0 || ch > ub) { + ASN_DEBUG("Character %d (0x%02x)" + " is out of range (%ld..%ld)", + *buf, *buf, lb, ub + lb); + return -1; + } + if(per_put_few_bits(po, ch, unit_bits)) + return -1; + } + + return 0; +} + +asn_dec_rval_t +OCTET_STRING_decode_uper(asn_codec_ctx_t *opt_codec_ctx, + asn_TYPE_descriptor_t *td, asn_per_constraints_t *constraints, + void **sptr, asn_per_data_t *pd) { + + asn_OCTET_STRING_specifics_t *specs = td->specifics + ? (asn_OCTET_STRING_specifics_t *)td->specifics + : &asn_DEF_OCTET_STRING_specs; + asn_per_constraints_t *pc = constraints ? constraints + : td->per_constraints; + asn_per_constraint_t *cval; + asn_per_constraint_t *csiz; + asn_dec_rval_t rval = { RC_OK, 0 }; + BIT_STRING_t *st = (BIT_STRING_t *)*sptr; + ssize_t consumed_myself = 0; + int repeat; + enum { + OS__BPC_BIT = 0, + OS__BPC_CHAR = 1, + OS__BPC_U16 = 2, + OS__BPC_U32 = 4 + } bpc; /* Bytes per character */ + unsigned int unit_bits; + unsigned int canonical_unit_bits; + + (void)opt_codec_ctx; + + if(pc) { + cval = &pc->value; + csiz = &pc->size; + } else { + cval = &asn_DEF_OCTET_STRING_constraints.value; + csiz = &asn_DEF_OCTET_STRING_constraints.size; + } + + switch(specs->subvariant) { + default: + case ASN_OSUBV_ANY: + ASN_DEBUG("Unrecognized subvariant %d", specs->subvariant); + RETURN(RC_FAIL); + case ASN_OSUBV_BIT: + canonical_unit_bits = unit_bits = 1; + bpc = OS__BPC_BIT; + break; + case ASN_OSUBV_STR: + canonical_unit_bits = unit_bits = 8; + if(cval->flags & APC_CONSTRAINED) + unit_bits = cval->range_bits; + bpc = OS__BPC_CHAR; + break; + case ASN_OSUBV_U16: + canonical_unit_bits = unit_bits = 16; + if(cval->flags & APC_CONSTRAINED) + unit_bits = cval->range_bits; + bpc = OS__BPC_U16; + break; + case ASN_OSUBV_U32: + canonical_unit_bits = unit_bits = 32; + if(cval->flags & APC_CONSTRAINED) + unit_bits = cval->range_bits; + bpc = OS__BPC_U32; + break; + } + + /* + * Allocate the string. + */ + if(!st) { + st = (BIT_STRING_t *)(*sptr = CALLOC(1, specs->struct_size)); + if(!st) RETURN(RC_FAIL); + } + + ASN_DEBUG("PER Decoding %s size %ld .. %ld bits %d", + csiz->flags & APC_EXTENSIBLE ? "extensible" : "non-extensible", + csiz->lower_bound, csiz->upper_bound, csiz->effective_bits); + + if(csiz->flags & APC_EXTENSIBLE) { + int inext = per_get_few_bits(pd, 1); + if(inext < 0) RETURN(RC_WMORE); + if(inext) { + csiz = &asn_DEF_OCTET_STRING_constraints.size; + cval = &asn_DEF_OCTET_STRING_constraints.value; + unit_bits = canonical_unit_bits; + } + } + + if(csiz->effective_bits >= 0) { + FREEMEM(st->buf); + if(bpc) { + st->size = csiz->upper_bound * bpc; + } else { + st->size = (csiz->upper_bound + 7) >> 3; + } + st->buf = (uint8_t *)MALLOC(st->size + 1); + if(!st->buf) { st->size = 0; RETURN(RC_FAIL); } + } + + /* X.691, #16.5: zero-length encoding */ + /* X.691, #16.6: short fixed length encoding (up to 2 octets) */ + /* X.691, #16.7: long fixed length encoding (up to 64K octets) */ + if(csiz->effective_bits == 0) { + int ret; + if(bpc) { + ASN_DEBUG("Encoding OCTET STRING size %ld", + csiz->upper_bound); + ret = OCTET_STRING_per_get_characters(pd, st->buf, + csiz->upper_bound, bpc, unit_bits, + cval->lower_bound, cval->upper_bound, pc); + if(ret > 0) RETURN(RC_FAIL); + } else { + ASN_DEBUG("Encoding BIT STRING size %ld", + csiz->upper_bound); + ret = per_get_many_bits(pd, st->buf, 0, + unit_bits * csiz->upper_bound); + } + if(ret < 0) RETURN(RC_WMORE); + consumed_myself += unit_bits * csiz->upper_bound; + st->buf[st->size] = 0; + if(bpc == 0) { + int ubs = (csiz->upper_bound & 0x7); + st->bits_unused = ubs ? 8 - ubs : 0; + } + RETURN(RC_OK); + } + + st->size = 0; + do { + ssize_t raw_len; + ssize_t len_bytes; + ssize_t len_bits; + void *p; + int ret; + + /* Get the PER length */ + raw_len = uper_get_length(pd, csiz->effective_bits, &repeat); + if(raw_len < 0) RETURN(RC_WMORE); + raw_len += csiz->lower_bound; + + ASN_DEBUG("Got PER length eb %ld, len %ld, %s (%s)", + (long)csiz->effective_bits, (long)raw_len, + repeat ? "repeat" : "once", td->name); + if(bpc) { + len_bytes = raw_len * bpc; + len_bits = len_bytes * unit_bits; + } else { + len_bits = raw_len; + len_bytes = (len_bits + 7) >> 3; + if(len_bits & 0x7) + st->bits_unused = 8 - (len_bits & 0x7); + /* len_bits be multiple of 16K if repeat is set */ + } + p = REALLOC(st->buf, st->size + len_bytes + 1); + if(!p) RETURN(RC_FAIL); + st->buf = (uint8_t *)p; + + if(bpc) { + ret = OCTET_STRING_per_get_characters(pd, + &st->buf[st->size], raw_len, bpc, unit_bits, + cval->lower_bound, cval->upper_bound, pc); + if(ret > 0) RETURN(RC_FAIL); + } else { + ret = per_get_many_bits(pd, &st->buf[st->size], + 0, len_bits); + } + if(ret < 0) RETURN(RC_WMORE); + st->size += len_bytes; + } while(repeat); + st->buf[st->size] = 0; /* nul-terminate */ + + return rval; +} + +asn_enc_rval_t +OCTET_STRING_encode_uper(asn_TYPE_descriptor_t *td, + asn_per_constraints_t *constraints, void *sptr, asn_per_outp_t *po) { + + asn_OCTET_STRING_specifics_t *specs = td->specifics + ? (asn_OCTET_STRING_specifics_t *)td->specifics + : &asn_DEF_OCTET_STRING_specs; + asn_per_constraints_t *pc = constraints ? constraints + : td->per_constraints; + asn_per_constraint_t *cval; + asn_per_constraint_t *csiz; + const BIT_STRING_t *st = (const BIT_STRING_t *)sptr; + asn_enc_rval_t er = { 0, 0, 0 }; + int inext = 0; /* Lies not within extension root */ + unsigned int unit_bits; + unsigned int canonical_unit_bits; + unsigned int sizeinunits; + const uint8_t *buf; + int ret; + enum { + OS__BPC_BIT = 0, + OS__BPC_CHAR = 1, + OS__BPC_U16 = 2, + OS__BPC_U32 = 4 + } bpc; /* Bytes per character */ + int ct_extensible; + + if(!st || (!st->buf && st->size)) + ASN__ENCODE_FAILED; + + if(pc) { + cval = &pc->value; + csiz = &pc->size; + } else { + cval = &asn_DEF_OCTET_STRING_constraints.value; + csiz = &asn_DEF_OCTET_STRING_constraints.size; + } + ct_extensible = csiz->flags & APC_EXTENSIBLE; + + switch(specs->subvariant) { + default: + case ASN_OSUBV_ANY: + ASN__ENCODE_FAILED; + case ASN_OSUBV_BIT: + canonical_unit_bits = unit_bits = 1; + bpc = OS__BPC_BIT; + sizeinunits = st->size * 8 - (st->bits_unused & 0x07); + ASN_DEBUG("BIT STRING of %d bytes, %d bits unused", + sizeinunits, st->bits_unused); + break; + case ASN_OSUBV_STR: + canonical_unit_bits = unit_bits = 8; + if(cval->flags & APC_CONSTRAINED) + unit_bits = cval->range_bits; + bpc = OS__BPC_CHAR; + sizeinunits = st->size; + break; + case ASN_OSUBV_U16: + canonical_unit_bits = unit_bits = 16; + if(cval->flags & APC_CONSTRAINED) + unit_bits = cval->range_bits; + bpc = OS__BPC_U16; + sizeinunits = st->size / 2; + break; + case ASN_OSUBV_U32: + canonical_unit_bits = unit_bits = 32; + if(cval->flags & APC_CONSTRAINED) + unit_bits = cval->range_bits; + bpc = OS__BPC_U32; + sizeinunits = st->size / 4; + break; + } + + ASN_DEBUG("Encoding %s into %d units of %d bits" + " (%ld..%ld, effective %d)%s", + td->name, sizeinunits, unit_bits, + csiz->lower_bound, csiz->upper_bound, + csiz->effective_bits, ct_extensible ? " EXT" : ""); + + /* Figure out whether size lies within PER visible constraint */ + + if(csiz->effective_bits >= 0) { + if((int)sizeinunits < csiz->lower_bound + || (int)sizeinunits > csiz->upper_bound) { + if(ct_extensible) { + cval = &asn_DEF_OCTET_STRING_constraints.value; + csiz = &asn_DEF_OCTET_STRING_constraints.size; + unit_bits = canonical_unit_bits; + inext = 1; + } else + ASN__ENCODE_FAILED; + } + } else { + inext = 0; + } + + if(ct_extensible) { + /* Declare whether length is [not] within extension root */ + if(per_put_few_bits(po, inext, 1)) + ASN__ENCODE_FAILED; + } + + /* X.691, #16.5: zero-length encoding */ + /* X.691, #16.6: short fixed length encoding (up to 2 octets) */ + /* X.691, #16.7: long fixed length encoding (up to 64K octets) */ + if(csiz->effective_bits >= 0) { + ASN_DEBUG("Encoding %d bytes (%ld), length in %d bits", + st->size, sizeinunits - csiz->lower_bound, + csiz->effective_bits); + ret = per_put_few_bits(po, sizeinunits - csiz->lower_bound, + csiz->effective_bits); + if(ret) ASN__ENCODE_FAILED; + if(bpc) { + ret = OCTET_STRING_per_put_characters(po, st->buf, + sizeinunits, bpc, unit_bits, + cval->lower_bound, cval->upper_bound, pc); + } else { + ret = per_put_many_bits(po, st->buf, + sizeinunits * unit_bits); + } + if(ret) ASN__ENCODE_FAILED; + ASN__ENCODED_OK(er); + } + + ASN_DEBUG("Encoding %d bytes", st->size); + + if(sizeinunits == 0) { + if(uper_put_length(po, 0)) + ASN__ENCODE_FAILED; + ASN__ENCODED_OK(er); + } + + buf = st->buf; + while(sizeinunits) { + ssize_t maySave = uper_put_length(po, sizeinunits); + if(maySave < 0) ASN__ENCODE_FAILED; + + ASN_DEBUG("Encoding %ld of %ld", + (long)maySave, (long)sizeinunits); + + if(bpc) { + ret = OCTET_STRING_per_put_characters(po, buf, + maySave, bpc, unit_bits, + cval->lower_bound, cval->upper_bound, pc); + } else { + ret = per_put_many_bits(po, buf, maySave * unit_bits); + } + if(ret) ASN__ENCODE_FAILED; + + if(bpc) + buf += maySave * bpc; + else + buf += maySave >> 3; + sizeinunits -= maySave; + assert(!(maySave & 0x07) || !sizeinunits); + } + + ASN__ENCODED_OK(er); +} + +int +OCTET_STRING_print(asn_TYPE_descriptor_t *td, const void *sptr, int ilevel, + asn_app_consume_bytes_f *cb, void *app_key) { + const char * const h2c = "0123456789ABCDEF"; + const OCTET_STRING_t *st = (const OCTET_STRING_t *)sptr; + char scratch[16 * 3 + 4]; + char *p = scratch; + uint8_t *buf; + uint8_t *end; + size_t i; + + (void)td; /* Unused argument */ + + if(!st || (!st->buf && st->size)) + return (cb("", 8, app_key) < 0) ? -1 : 0; + + /* + * 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) < 0) + return -1; + _i_INDENT(1); + p = scratch; + } + *p++ = h2c[(*buf >> 4) & 0x0F]; + *p++ = h2c[*buf & 0x0F]; + *p++ = 0x20; + } + + if(p > scratch) { + p--; /* Remove the tail space */ + if(cb(scratch, p - scratch, app_key) < 0) + return -1; + } + + return 0; +} + +int +OCTET_STRING_print_utf8(asn_TYPE_descriptor_t *td, const void *sptr, + int ilevel, asn_app_consume_bytes_f *cb, void *app_key) { + const OCTET_STRING_t *st = (const OCTET_STRING_t *)sptr; + + (void)td; /* Unused argument */ + (void)ilevel; /* Unused argument */ + + if(st && (st->buf || !st->size)) { + return (cb(st->buf, st->size, app_key) < 0) ? -1 : 0; + } else { + return (cb("", 8, app_key) < 0) ? -1 : 0; + } +} + +void +OCTET_STRING_free(asn_TYPE_descriptor_t *td, void *sptr, int contents_only) { + OCTET_STRING_t *st = (OCTET_STRING_t *)sptr; + asn_OCTET_STRING_specifics_t *specs; + asn_struct_ctx_t *ctx; + struct _stack *stck; + + if(!td || !st) + return; + + specs = td->specifics + ? (asn_OCTET_STRING_specifics_t *)td->specifics + : &asn_DEF_OCTET_STRING_specs; + ctx = (asn_struct_ctx_t *)((char *)st + specs->ctx_offset); + + ASN_DEBUG("Freeing %s as OCTET STRING", td->name); + + if(st->buf) { + FREEMEM(st->buf); + st->buf = 0; + } + + /* + * Remove decode-time stack. + */ + stck = (struct _stack *)ctx->ptr; + 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) { + FREEMEM(st->buf); + st->buf = 0; + 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; + + memcpy(buf, str, len); + ((uint8_t *)buf)[len] = '\0'; /* Couldn't use memcpy(len+1)! */ + FREEMEM(st->buf); + st->buf = (uint8_t *)buf; + st->size = len; + + return 0; +} + +OCTET_STRING_t * +OCTET_STRING_new_fromBuf(asn_TYPE_descriptor_t *td, const char *str, int len) { + asn_OCTET_STRING_specifics_t *specs = td->specifics + ? (asn_OCTET_STRING_specifics_t *)td->specifics + : &asn_DEF_OCTET_STRING_specs; + OCTET_STRING_t *st; + + st = (OCTET_STRING_t *)CALLOC(1, specs->struct_size); + if(st && str && OCTET_STRING_fromBuf(st, str, len)) { + FREEMEM(st); + st = NULL; + } + + return st; +} + diff --git a/asn1_test_data/OCTET_STRING.h b/asn1_test_data/OCTET_STRING.h new file mode 100644 index 0000000..013c7b1 --- /dev/null +++ b/asn1_test_data/OCTET_STRING.h @@ -0,0 +1,86 @@ +/*- + * Copyright (c) 2003 Lev Walkin . All rights reserved. + * Redistribution and modifications are permitted subject to BSD license. + */ +#ifndef _OCTET_STRING_H_ +#define _OCTET_STRING_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct OCTET_STRING { + uint8_t *buf; /* Buffer with consecutive OCTET_STRING bits */ + int size; /* Size of the buffer */ + + asn_struct_ctx_t _asn_ctx; /* Parsing across buffer boundaries */ +} OCTET_STRING_t; + +extern asn_TYPE_descriptor_t asn_DEF_OCTET_STRING; + +asn_struct_free_f OCTET_STRING_free; +asn_struct_print_f OCTET_STRING_print; +asn_struct_print_f OCTET_STRING_print_utf8; +ber_type_decoder_f OCTET_STRING_decode_ber; +der_type_encoder_f OCTET_STRING_encode_der; +xer_type_decoder_f OCTET_STRING_decode_xer_hex; /* Hexadecimal */ +xer_type_decoder_f OCTET_STRING_decode_xer_binary; /* 01010111010 */ +xer_type_decoder_f OCTET_STRING_decode_xer_utf8; /* ASCII/UTF-8 */ +xer_type_encoder_f OCTET_STRING_encode_xer; +xer_type_encoder_f OCTET_STRING_encode_xer_utf8; +per_type_decoder_f OCTET_STRING_decode_uper; +per_type_encoder_f OCTET_STRING_encode_uper; + +/****************************** + * Handy conversion routines. * + ******************************/ + +/* + * This function clears the previous value of the OCTET STRING (if any) + * and then allocates a new memory with the specified content (str/size). + * 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); + +/* Handy conversion from the C string into the OCTET STRING. */ +#define OCTET_STRING_fromString(s, str) OCTET_STRING_fromBuf(s, str, -1) + +/* + * 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(asn_TYPE_descriptor_t *td, + const char *str, int size); + +/**************************** + * Internally useful stuff. * + ****************************/ + +typedef const struct asn_OCTET_STRING_specifics_s { + /* + * Target structure description. + */ + int struct_size; /* Size of the structure */ + int ctx_offset; /* Offset of the asn_struct_ctx_t member */ + + enum asn_OS_Subvariant { + ASN_OSUBV_ANY, /* The open type (ANY) */ + ASN_OSUBV_BIT, /* BIT STRING */ + ASN_OSUBV_STR, /* String types, not {BMP,Universal}String */ + ASN_OSUBV_U16, /* 16-bit character (BMPString) */ + ASN_OSUBV_U32 /* 32-bit character (UniversalString) */ + } subvariant; +} asn_OCTET_STRING_specifics_t; + +#ifdef __cplusplus +} +#endif + +#endif /* _OCTET_STRING_H_ */ diff --git a/asn1_test_data/PurchaseOrder.c b/asn1_test_data/PurchaseOrder.c new file mode 100644 index 0000000..235b2ca --- /dev/null +++ b/asn1_test_data/PurchaseOrder.c @@ -0,0 +1,78 @@ +/* + * Generated by asn1c-0.9.28 (http://lionet.info/asn1c) + * From ASN.1 module "MyShopPurchaseOrders" + * found in "productExample1.asn" + */ + +#include "PurchaseOrder.h" + +static asn_TYPE_member_t asn_MBR_PurchaseOrder_1[] = { + { ATF_NOFLAGS, 0, offsetof(struct PurchaseOrder, dateOfOrder), + (ASN_TAG_CLASS_CONTEXT | (0 << 2)), + -1, /* IMPLICIT tag at current level */ + &asn_DEF_DATE, + 0, /* Defer constraints checking to the member type */ + 0, /* PER is not compiled, use -gen-PER */ + 0, + "dateOfOrder" + }, + { ATF_NOFLAGS, 0, offsetof(struct PurchaseOrder, customer), + (ASN_TAG_CLASS_CONTEXT | (1 << 2)), + -1, /* IMPLICIT tag at current level */ + &asn_DEF_CustomerInfo, + 0, /* Defer constraints checking to the member type */ + 0, /* PER is not compiled, use -gen-PER */ + 0, + "customer" + }, + { ATF_NOFLAGS, 0, offsetof(struct PurchaseOrder, items), + (ASN_TAG_CLASS_CONTEXT | (2 << 2)), + -1, /* IMPLICIT tag at current level */ + &asn_DEF_ListOfItems, + 0, /* Defer constraints checking to the member type */ + 0, /* PER is not compiled, use -gen-PER */ + 0, + "items" + }, +}; +static const ber_tlv_tag_t asn_DEF_PurchaseOrder_tags_1[] = { + (ASN_TAG_CLASS_UNIVERSAL | (16 << 2)) +}; +static const asn_TYPE_tag2member_t asn_MAP_PurchaseOrder_tag2el_1[] = { + { (ASN_TAG_CLASS_CONTEXT | (0 << 2)), 0, 0, 0 }, /* dateOfOrder */ + { (ASN_TAG_CLASS_CONTEXT | (1 << 2)), 1, 0, 0 }, /* customer */ + { (ASN_TAG_CLASS_CONTEXT | (2 << 2)), 2, 0, 0 } /* items */ +}; +static asn_SEQUENCE_specifics_t asn_SPC_PurchaseOrder_specs_1 = { + sizeof(struct PurchaseOrder), + offsetof(struct PurchaseOrder, _asn_ctx), + asn_MAP_PurchaseOrder_tag2el_1, + 3, /* Count of tags in the map */ + 0, 0, 0, /* Optional elements (not needed) */ + -1, /* Start extensions */ + -1 /* Stop extensions */ +}; +asn_TYPE_descriptor_t asn_DEF_PurchaseOrder = { + "PurchaseOrder", + "PurchaseOrder", + SEQUENCE_free, + SEQUENCE_print, + SEQUENCE_constraint, + SEQUENCE_decode_ber, + SEQUENCE_encode_der, + SEQUENCE_decode_xer, + SEQUENCE_encode_xer, + 0, 0, /* No PER support, use "-gen-PER" to enable */ + 0, /* Use generic outmost tag fetcher */ + asn_DEF_PurchaseOrder_tags_1, + sizeof(asn_DEF_PurchaseOrder_tags_1) + /sizeof(asn_DEF_PurchaseOrder_tags_1[0]), /* 1 */ + asn_DEF_PurchaseOrder_tags_1, /* Same as above */ + sizeof(asn_DEF_PurchaseOrder_tags_1) + /sizeof(asn_DEF_PurchaseOrder_tags_1[0]), /* 1 */ + 0, /* No PER visible constraints */ + asn_MBR_PurchaseOrder_1, + 3, /* Elements count */ + &asn_SPC_PurchaseOrder_specs_1 /* Additional specs */ +}; + diff --git a/asn1_test_data/PurchaseOrder.h b/asn1_test_data/PurchaseOrder.h new file mode 100644 index 0000000..a9d9439 --- /dev/null +++ b/asn1_test_data/PurchaseOrder.h @@ -0,0 +1,41 @@ +/* + * Generated by asn1c-0.9.28 (http://lionet.info/asn1c) + * From ASN.1 module "MyShopPurchaseOrders" + * found in "productExample1.asn" + */ + +#ifndef _PurchaseOrder_H_ +#define _PurchaseOrder_H_ + + +#include + +/* Including external dependencies */ +#include "DATE.h" +#include "CustomerInfo.h" +#include "ListOfItems.h" +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* PurchaseOrder */ +typedef struct PurchaseOrder { + DATE_t dateOfOrder; + CustomerInfo_t customer; + ListOfItems_t items; + + /* Context for parsing across buffer boundaries */ + asn_struct_ctx_t _asn_ctx; +} PurchaseOrder_t; + +/* Implementation */ +extern asn_TYPE_descriptor_t asn_DEF_PurchaseOrder; + +#ifdef __cplusplus +} +#endif + +#endif /* _PurchaseOrder_H_ */ +#include diff --git a/asn1_test_data/REAL.c b/asn1_test_data/REAL.c new file mode 100644 index 0000000..0a25062 --- /dev/null +++ b/asn1_test_data/REAL.c @@ -0,0 +1,755 @@ +/*- + * Copyright (c) 2004-2013 Lev Walkin . All rights reserved. + * Redistribution and modifications are permitted subject to BSD license. + */ +#define _ISOC99_SOURCE /* For ilogb() and quiet NAN */ +#define _BSD_SOURCE /* To reintroduce finite(3) */ +#if defined(__alpha) +#include /* For INFINITY */ +#endif +#include +#include /* for strtod(3) */ +#include +#include +#include +#include + +#undef INT_MAX +#define INT_MAX ((int)(((unsigned int)-1) >> 1)) + +#if !(defined(NAN) || defined(INFINITY)) +static volatile double real_zero GCC_NOTUSED = 0.0; +#endif +#ifndef NAN +#define NAN (real_zero/real_zero) +#endif +#ifndef INFINITY +#define INFINITY (1.0/real_zero) +#endif + +#ifdef isfinite +#define _asn_isfinite(d) isfinite(d) /* ISO C99 */ +#else +#define _asn_isfinite(d) finite(d) /* Deprecated on Mac OS X 10.9 */ +#endif + +/* + * REAL basic type description. + */ +static const ber_tlv_tag_t asn_DEF_REAL_tags[] = { + (ASN_TAG_CLASS_UNIVERSAL | (9 << 2)) +}; +asn_TYPE_descriptor_t asn_DEF_REAL = { + "REAL", + "REAL", + ASN__PRIMITIVE_TYPE_free, + REAL_print, + asn_generic_no_constraint, + ber_decode_primitive, + der_encode_primitive, + REAL_decode_xer, + REAL_encode_xer, + REAL_decode_uper, + REAL_encode_uper, + 0, /* Use generic outmost tag fetcher */ + asn_DEF_REAL_tags, + sizeof(asn_DEF_REAL_tags) / sizeof(asn_DEF_REAL_tags[0]), + asn_DEF_REAL_tags, /* Same as above */ + sizeof(asn_DEF_REAL_tags) / sizeof(asn_DEF_REAL_tags[0]), + 0, /* No PER visible constraints */ + 0, 0, /* No members */ + 0 /* No specifics */ +}; + +typedef enum specialRealValue { + SRV__NOT_A_NUMBER, + SRV__MINUS_INFINITY, + SRV__PLUS_INFINITY +} specialRealValue_e; +static struct specialRealValue_s { + char *string; + size_t length; + long dv; +} specialRealValue[] = { +#define SRV_SET(foo, val) { foo, sizeof(foo) - 1, val } + SRV_SET("", 0), + SRV_SET("", -1), + SRV_SET("", 1), +#undef SRV_SET +}; + +ssize_t +REAL__dump(double d, int canonical, asn_app_consume_bytes_f *cb, void *app_key) { + char local_buf[64]; + char *buf = local_buf; + ssize_t buflen = sizeof(local_buf); + const char *fmt = canonical?"%.15E":"%.15f"; + ssize_t ret; + + /* + * Check whether it is a special value. + */ + /* fpclassify(3) is not portable yet */ + if(isnan(d)) { + buf = specialRealValue[SRV__NOT_A_NUMBER].string; + buflen = specialRealValue[SRV__NOT_A_NUMBER].length; + return (cb(buf, buflen, app_key) < 0) ? -1 : buflen; + } else if(!_asn_isfinite(d)) { + if(copysign(1.0, d) < 0.0) { + buf = specialRealValue[SRV__MINUS_INFINITY].string; + buflen = specialRealValue[SRV__MINUS_INFINITY].length; + } else { + buf = specialRealValue[SRV__PLUS_INFINITY].string; + buflen = specialRealValue[SRV__PLUS_INFINITY].length; + } + return (cb(buf, buflen, app_key) < 0) ? -1 : buflen; + } else if(ilogb(d) <= -INT_MAX) { + if(copysign(1.0, d) < 0.0) { + buf = "-0"; + buflen = 2; + } else { + buf = "0"; + buflen = 1; + } + return (cb(buf, buflen, app_key) < 0) ? -1 : buflen; + } + + /* + * Use the libc's double printing, hopefully they got it right. + */ + do { + ret = snprintf(buf, buflen, fmt, d); + if(ret < 0) { + buflen <<= 1; + } else if(ret >= buflen) { + buflen = ret + 1; + } else { + buflen = ret; + break; + } + if(buf != local_buf) FREEMEM(buf); + buf = (char *)MALLOC(buflen); + if(!buf) return -1; + } while(1); + + if(canonical) { + /* + * Transform the "[-]d.dddE+-dd" output into "[-]d.dddE[-]d" + * Check that snprintf() constructed the output correctly. + */ + char *dot; + char *end = buf + buflen; + char *last_zero; + char *prev_zero; + char *s; + + enum { + LZSTATE_NOTHING, + LZSTATE_SEEN_ZERO + } lz_state = LZSTATE_NOTHING; + + dot = (buf[0] == 0x2d /* '-' */) ? (buf + 2) : (buf + 1); + if(*dot >= 0x30) { + if(buf != local_buf) FREEMEM(buf); + errno = EINVAL; + return -1; /* Not a dot, really */ + } + *dot = 0x2e; /* Replace possible comma */ + + for(prev_zero = last_zero = s = dot + 2; s < end; s++) { + switch(*s) { + case 0x45: /* 'E' */ + if(lz_state == LZSTATE_SEEN_ZERO) + last_zero = prev_zero; + break; + case 0x30: /* '0' */ + if(lz_state == LZSTATE_NOTHING) + prev_zero = s; + lz_state = LZSTATE_SEEN_ZERO; + continue; + default: + lz_state = LZSTATE_NOTHING; + continue; + } + break; + } + + if(s == end) { + if(buf != local_buf) FREEMEM(buf); + errno = EINVAL; + return -1; /* No promised E */ + } + + assert(*s == 0x45); + { + char *E = s; + char *expptr = ++E; + char *s = expptr; + int sign; + + if(*expptr == 0x2b /* '+' */) { + /* Skip the "+" */ + buflen -= 1; + sign = 0; + } else { + sign = 1; + s++; + } + expptr++; + if(expptr > end) { + if(buf != local_buf) FREEMEM(buf); + errno = EINVAL; + return -1; + } + if(*expptr == 0x30) { + buflen--; + expptr++; + } + if(*last_zero == 0x30) { + *last_zero = 0x45; /* E */ + buflen -= s - (last_zero + 1); + s = last_zero + 1; + if(sign) { + *s++ = 0x2d /* '-' */; + buflen++; + } + } + for(; expptr <= end; s++, expptr++) + *s = *expptr; + } + } else { + /* + * Remove trailing zeros. + */ + char *end = buf + buflen; + char *last_zero = end; + int stoplooking = 0; + char *z; + for(z = end - 1; z > buf; z--) { + switch(*z) { + case 0x30: + if(!stoplooking) + last_zero = z; + continue; + case 0x31: case 0x32: case 0x33: case 0x34: + case 0x35: case 0x36: case 0x37: case 0x38: case 0x39: + stoplooking = 1; + continue; + default: /* Catch dot and other separators */ + /* + * Replace possible comma (which may even + * be not a comma at all: locale-defined). + */ + *z = 0x2e; + if(last_zero == z + 1) { /* leave x.0 */ + last_zero++; + } + buflen = last_zero - buf; + *last_zero = '\0'; + break; + } + break; + } + } + + ret = cb(buf, buflen, app_key); + if(buf != local_buf) FREEMEM(buf); + return (ret < 0) ? -1 : buflen; +} + +int +REAL_print(asn_TYPE_descriptor_t *td, const void *sptr, int ilevel, + asn_app_consume_bytes_f *cb, void *app_key) { + const REAL_t *st = (const REAL_t *)sptr; + ssize_t ret; + double d; + + (void)td; /* Unused argument */ + (void)ilevel; /* Unused argument */ + + if(!st || !st->buf) + ret = cb("", 8, app_key); + else if(asn_REAL2double(st, &d)) + ret = cb("", 7, app_key); + else + ret = REAL__dump(d, 0, cb, app_key); + + return (ret < 0) ? -1 : 0; +} + +asn_enc_rval_t +REAL_encode_xer(asn_TYPE_descriptor_t *td, void *sptr, + int ilevel, enum xer_encoder_flags_e flags, + asn_app_consume_bytes_f *cb, void *app_key) { + REAL_t *st = (REAL_t *)sptr; + asn_enc_rval_t er; + double d; + + (void)ilevel; + + if(!st || !st->buf || asn_REAL2double(st, &d)) + ASN__ENCODE_FAILED; + + er.encoded = REAL__dump(d, flags & XER_F_CANONICAL, cb, app_key); + if(er.encoded < 0) ASN__ENCODE_FAILED; + + ASN__ENCODED_OK(er); +} + + +/* + * Decode the chunk of XML text encoding REAL. + */ +static enum xer_pbd_rval +REAL__xer_body_decode(asn_TYPE_descriptor_t *td, void *sptr, const void *chunk_buf, size_t chunk_size) { + REAL_t *st = (REAL_t *)sptr; + double value; + const char *xerdata = (const char *)chunk_buf; + char *endptr = 0; + char *b; + + (void)td; + + if(!chunk_size) return XPBD_BROKEN_ENCODING; + + /* + * Decode an XMLSpecialRealValue: , etc. + */ + if(xerdata[0] == 0x3c /* '<' */) { + size_t i; + for(i = 0; i < sizeof(specialRealValue) + / sizeof(specialRealValue[0]); i++) { + struct specialRealValue_s *srv = &specialRealValue[i]; + double dv; + + if(srv->length != chunk_size + || memcmp(srv->string, chunk_buf, chunk_size)) + continue; + + /* + * It could've been done using + * (double)srv->dv / real_zero, + * but it summons fp exception on some platforms. + */ + switch(srv->dv) { + case -1: dv = - INFINITY; break; + case 0: dv = NAN; break; + case 1: dv = INFINITY; break; + default: return XPBD_SYSTEM_FAILURE; + } + + if(asn_double2REAL(st, dv)) + return XPBD_SYSTEM_FAILURE; + + return XPBD_BODY_CONSUMED; + } + ASN_DEBUG("Unknown XMLSpecialRealValue"); + return XPBD_BROKEN_ENCODING; + } + + /* + * Copy chunk into the nul-terminated string, and run strtod. + */ + b = (char *)MALLOC(chunk_size + 1); + if(!b) return XPBD_SYSTEM_FAILURE; + memcpy(b, chunk_buf, chunk_size); + b[chunk_size] = 0; /* nul-terminate */ + + value = strtod(b, &endptr); + FREEMEM(b); + if(endptr == b) return XPBD_BROKEN_ENCODING; + + if(asn_double2REAL(st, value)) + return XPBD_SYSTEM_FAILURE; + + return XPBD_BODY_CONSUMED; +} + +asn_dec_rval_t +REAL_decode_xer(asn_codec_ctx_t *opt_codec_ctx, + asn_TYPE_descriptor_t *td, void **sptr, const char *opt_mname, + const void *buf_ptr, size_t size) { + + return xer_decode_primitive(opt_codec_ctx, td, + sptr, sizeof(REAL_t), opt_mname, + buf_ptr, size, REAL__xer_body_decode); +} + +asn_dec_rval_t +REAL_decode_uper(asn_codec_ctx_t *opt_codec_ctx, + asn_TYPE_descriptor_t *td, asn_per_constraints_t *constraints, + void **sptr, asn_per_data_t *pd) { + (void)constraints; /* No PER visible constraints */ + return OCTET_STRING_decode_uper(opt_codec_ctx, td, 0, sptr, pd); +} + +asn_enc_rval_t +REAL_encode_uper(asn_TYPE_descriptor_t *td, + asn_per_constraints_t *constraints, void *sptr, asn_per_outp_t *po) { + (void)constraints; /* No PER visible constraints */ + return OCTET_STRING_encode_uper(td, 0, sptr, po); +} + +int +asn_REAL2double(const REAL_t *st, double *dbl_value) { + unsigned int octv; + + if(!st || !st->buf) { + errno = EINVAL; + return -1; + } + + if(st->size == 0) { + *dbl_value = 0; + return 0; + } + + octv = st->buf[0]; /* unsigned byte */ + + switch(octv & 0xC0) { + case 0x40: /* X.690: 8.5.6 a) => 8.5.9 */ + /* "SpecialRealValue" */ + + /* Be liberal in what you accept... + * http://en.wikipedia.org/wiki/Robustness_principle + if(st->size != 1) ... + */ + + switch(st->buf[0]) { + case 0x40: /* 01000000: PLUS-INFINITY */ + *dbl_value = INFINITY; + return 0; + case 0x41: /* 01000001: MINUS-INFINITY */ + *dbl_value = - INFINITY; + return 0; + case 0x42: /* 01000010: NOT-A-NUMBER */ + *dbl_value = NAN; + return 0; + case 0x43: /* 01000011: minus zero */ + *dbl_value = -0.0; + return 0; + } + + errno = EINVAL; + return -1; + case 0x00: { /* X.690: 8.5.7 */ + /* + * Decimal. NR{1,2,3} format from ISO 6093. + * NR1: [ ]*[+-]?[0-9]+ + * NR2: [ ]*[+-]?([0-9]+\.[0-9]*|[0-9]*\.[0-9]+) + * NR3: [ ]*[+-]?([0-9]+\.[0-9]*|[0-9]*\.[0-9]+)[Ee][+-]?[0-9]+ + */ + double d; + char *buf; + char *endptr; + int used_malloc = 0; + + if(octv == 0 || (octv & 0x3C)) { + /* Remaining values of bits 6 to 1 are Reserved. */ + errno = EINVAL; + return -1; + } + + + /* 1. By contract, an input buffer should be null-terminated. + * OCTET STRING decoder ensures that, as is asn_double2REAL(). + * 2. ISO 6093 specifies COMMA as a possible decimal separator. + * However, strtod() can't always deal with COMMA. + * So her we fix both by reallocating, copying and fixing. + */ + if(st->buf[st->size] || memchr(st->buf, ',', st->size)) { + uint8_t *p, *end; + char *b; + if(st->size > 100) { + /* Avoid malicious stack overflow in alloca() */ + buf = (char *)MALLOC(st->size); + if(!buf) return -1; + used_malloc = 1; + } else { + buf = alloca(st->size); + } + b = buf; + /* Copy without the first byte and with 0-termination */ + for(p = st->buf + 1, end = st->buf + st->size; + p < end; b++, p++) + *b = (*p == ',') ? '.' : *p; + *b = '\0'; + } else { + buf = (char *)&st->buf[1]; + } + + endptr = buf; + d = strtod(buf, &endptr); + if(*endptr != '\0') { + /* Format is not consistent with ISO 6093 */ + if(used_malloc) FREEMEM(buf); + errno = EINVAL; + return -1; + } + if(used_malloc) FREEMEM(buf); + if(_asn_isfinite(d)) { + *dbl_value = d; + return 0; + } else { + errno = ERANGE; + return -1; + } + } + } + + /* + * Binary representation. + */ + { + double m; + int expval; /* exponent value */ + unsigned int elen; /* exponent value length, in octets */ + unsigned int scaleF; + unsigned int baseF; + uint8_t *ptr; + uint8_t *end; + int sign; + + switch((octv & 0x30) >> 4) { + case 0x00: baseF = 1; break; /* base 2 */ + case 0x01: baseF = 3; break; /* base 8 */ + case 0x02: baseF = 4; break; /* base 16 */ + default: + /* Reserved field, can't parse now. */ + errno = EINVAL; + return -1; + } + + sign = (octv & 0x40); /* bit 7 */ + scaleF = (octv & 0x0C) >> 2; /* bits 4 to 3 */ + + if(st->size <= (int)(1 + (octv & 0x03))) { + errno = EINVAL; + return -1; + } + + elen = (octv & 0x03); /* bits 2 to 1; 8.5.6.4 */ + if(elen == 0x03) { /* bits 2 to 1 = 11; 8.5.6.4, case d) */ + elen = st->buf[1]; /* unsigned binary number */ + if(elen == 0 || st->size <= (int)(2 + elen)) { + errno = EINVAL; + return -1; + } + /* FIXME: verify constraints of case d) */ + ptr = &st->buf[2]; + } else { + ptr = &st->buf[1]; + } + + /* Fetch the multibyte exponent */ + expval = (int)(*(int8_t *)ptr); + end = ptr + elen + 1; + for(ptr++; ptr < end; ptr++) + expval = (expval * 256) + *ptr; + + m = 0.0; /* Initial mantissa value */ + + /* Okay, the exponent is here. Now, what about mantissa? */ + end = st->buf + st->size; + for(; ptr < end; ptr++) + m = ldexp(m, 8) + *ptr; + + if(0) + ASN_DEBUG("m=%.10f, scF=%d, bF=%d, expval=%d, ldexp()=%f, ldexp()=%f\n", + m, scaleF, baseF, expval, + ldexp(m, expval * baseF + scaleF), + ldexp(m, scaleF) * pow(pow(2, baseF), expval) + ); + + /* + * (S * N * 2^F) * B^E + * Essentially: + m = ldexp(m, scaleF) * pow(pow(2, base), expval); + */ + m = ldexp(m, expval * baseF + scaleF); + if(_asn_isfinite(m)) { + *dbl_value = sign ? -m : m; + } else { + errno = ERANGE; + return -1; + } + + } /* if(binary_format) */ + + return 0; +} + +/* + * Assume IEEE 754 floating point: standard 64 bit double. + * [1 bit sign] [11 bits exponent] [52 bits mantissa] + */ +int +asn_double2REAL(REAL_t *st, double dbl_value) { +#ifdef WORDS_BIGENDIAN /* Known to be big-endian */ + int littleEndian = 0; +#else /* need to test: have no explicit information */ + unsigned int LE = 1; + int littleEndian = *(unsigned char *)&LE; +#endif + uint8_t buf[16]; /* More than enough for 8-byte dbl_value */ + uint8_t dscr[sizeof(dbl_value)]; /* double value scratch pad */ + /* Assertion guards: won't even compile, if unexpected double size */ + char assertion_buffer1[9 - sizeof(dbl_value)] GCC_NOTUSED; + char assertion_buffer2[sizeof(dbl_value) - 7] GCC_NOTUSED; + uint8_t *ptr = buf; + uint8_t *mstop; /* Last byte of mantissa */ + unsigned int mval; /* Value of the last byte of mantissa */ + unsigned int bmsign; /* binary mask with sign */ + unsigned int buflen; + unsigned int accum; + int expval; + + if(!st) { + errno = EINVAL; + return -1; + } + + /* + * ilogb(+-0) returns -INT_MAX or INT_MIN (platform-dependent) + * ilogb(+-inf) returns INT_MAX, logb(+-inf) returns +inf + * ilogb(NaN) returns INT_MIN or INT_MAX (platform-dependent) + */ + expval = ilogb(dbl_value); + if(expval <= -INT_MAX /* Also catches +-0 and maybe isnan() */ + || expval == INT_MAX /* catches isfin() and maybe isnan() */ + ) { + if(!st->buf || st->size < 2) { + ptr = (uint8_t *)MALLOC(2); + if(!ptr) return -1; + st->buf = ptr; + } + /* fpclassify(3) is not portable yet */ + if(isnan(dbl_value)) { + st->buf[0] = 0x42; /* NaN */ + st->buf[1] = 0; + st->size = 1; + } else if(!_asn_isfinite(dbl_value)) { + if(copysign(1.0, dbl_value) < 0.0) { + st->buf[0] = 0x41; /* MINUS-INFINITY */ + } else { + st->buf[0] = 0x40; /* PLUS-INFINITY */ + } + st->buf[1] = 0; + st->size = 1; + } else { + if(copysign(1.0, dbl_value) >= 0.0) { + /* no content octets: positive zero */ + st->buf[0] = 0; /* JIC */ + st->size = 0; + } else { + /* Negative zero. #8.5.3, 8.5.9 */ + st->buf[0] = 0x43; + st->size = 1; + } + } + return 0; + } + + if(littleEndian) { + uint8_t *s = ((uint8_t *)&dbl_value) + sizeof(dbl_value) - 2; + uint8_t *start = ((uint8_t *)&dbl_value); + uint8_t *d; + + bmsign = 0x80 | ((s[1] >> 1) & 0x40); /* binary mask & - */ + for(mstop = d = dscr; s >= start; d++, s--) { + *d = *s; + if(*d) mstop = d; + } + } else { + uint8_t *s = ((uint8_t *)&dbl_value) + 1; + uint8_t *end = ((uint8_t *)&dbl_value) + sizeof(double); + uint8_t *d; + + bmsign = 0x80 | ((s[-1] >> 1) & 0x40); /* binary mask & - */ + for(mstop = d = dscr; s < end; d++, s++) { + *d = *s; + if(*d) mstop = d; + } + } + + /* Remove parts of the exponent, leave mantissa and explicit 1. */ + dscr[0] = 0x10 | (dscr[0] & 0x0f); + + /* Adjust exponent in a very unobvious way */ + expval -= 8 * ((mstop - dscr) + 1) - 4; + + /* This loop ensures DER conformance by forcing mantissa odd: 11.3.1 */ + mval = *mstop; + if(mval && !(mval & 1)) { + unsigned int shift_count = 1; + unsigned int ishift; + uint8_t *mptr; + + /* + * Figure out what needs to be done to make mantissa odd. + */ + if(!(mval & 0x0f)) /* Speed-up a little */ + shift_count = 4; + while(((mval >> shift_count) & 1) == 0) + shift_count++; + + ishift = 8 - shift_count; + accum = 0; + + /* Go over the buffer, shifting it shift_count bits right. */ + for(mptr = dscr; mptr <= mstop; mptr++) { + mval = *mptr; + *mptr = accum | (mval >> shift_count); + accum = mval << ishift; + } + + /* Adjust exponent appropriately. */ + expval += shift_count; + } + + if(expval < 0) { + if((expval >> 7) == -1) { + *ptr++ = bmsign | 0x00; + *ptr++ = expval; + } else if((expval >> 15) == -1) { + *ptr++ = bmsign | 0x01; + *ptr++ = expval >> 8; + *ptr++ = expval; + } else { + *ptr++ = bmsign | 0x02; + *ptr++ = expval >> 16; + *ptr++ = expval >> 8; + *ptr++ = expval; + } + } else if(expval <= 0x7f) { + *ptr++ = bmsign | 0x00; + *ptr++ = expval; + } else if(expval <= 0x7fff) { + *ptr++ = bmsign | 0x01; + *ptr++ = expval >> 8; + *ptr++ = expval; + } else { + assert(expval <= 0x7fffff); + *ptr++ = bmsign | 0x02; + *ptr++ = expval >> 16; + *ptr++ = expval >> 8; + *ptr++ = expval; + } + + buflen = (mstop - dscr) + 1; + memcpy(ptr, dscr, buflen); + ptr += buflen; + buflen = ptr - buf; + + ptr = (uint8_t *)MALLOC(buflen + 1); + if(!ptr) return -1; + + memcpy(ptr, buf, buflen); + buf[buflen] = 0; /* JIC */ + + if(st->buf) FREEMEM(st->buf); + st->buf = ptr; + st->size = buflen; + + return 0; +} diff --git a/asn1_test_data/REAL.h b/asn1_test_data/REAL.h new file mode 100644 index 0000000..af3e84c --- /dev/null +++ b/asn1_test_data/REAL.h @@ -0,0 +1,44 @@ +/*- + * Copyright (c) 2004 Lev Walkin . All rights reserved. + * Redistribution and modifications are permitted subject to BSD license. + */ +#ifndef ASN_TYPE_REAL_H +#define ASN_TYPE_REAL_H + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef ASN__PRIMITIVE_TYPE_t REAL_t; + +extern asn_TYPE_descriptor_t asn_DEF_REAL; + +asn_struct_print_f REAL_print; +xer_type_decoder_f REAL_decode_xer; +xer_type_encoder_f REAL_encode_xer; +per_type_decoder_f REAL_decode_uper; +per_type_encoder_f REAL_encode_uper; + +/*********************************** + * Some handy conversion routines. * + ***********************************/ + +ssize_t REAL__dump(double d, int canonical, asn_app_consume_bytes_f *cb, void *app_key); + +/* + * Convert between native double type and REAL representation (DER). + * RETURN VALUES: + * 0: Value converted successfully + * -1: An error occured while converting the value: invalid format. + */ +int asn_REAL2double(const REAL_t *real_ptr, double *d); +int asn_double2REAL(REAL_t *real_ptr, double d); + +#ifdef __cplusplus +} +#endif + +#endif /* ASN_TYPE_REAL_H */ diff --git a/asn1_test_data/RSAParameters.c b/asn1_test_data/RSAParameters.c new file mode 100644 index 0000000..211435c --- /dev/null +++ b/asn1_test_data/RSAParameters.c @@ -0,0 +1,108 @@ +/* + * Generated by asn1c-0.9.28 (http://lionet.info/asn1c) + * From ASN.1 module "COMPOSITESIGALGS" + * found in "pqASN1_compiling.asn" + */ + +#include "RSAParameters.h" + +int +RSAParameters_constraint(asn_TYPE_descriptor_t *td, const void *sptr, + asn_app_constraint_failed_f *ctfailcb, void *app_key) { + /* Replace with underlying type checker */ + td->check_constraints = asn_DEF_NULL.check_constraints; + return td->check_constraints(td, sptr, ctfailcb, app_key); +} + +/* + * This type is implemented using NULL, + * so here we adjust the DEF accordingly. + */ +static void +RSAParameters_1_inherit_TYPE_descriptor(asn_TYPE_descriptor_t *td) { + td->free_struct = asn_DEF_NULL.free_struct; + td->print_struct = asn_DEF_NULL.print_struct; + td->check_constraints = asn_DEF_NULL.check_constraints; + td->ber_decoder = asn_DEF_NULL.ber_decoder; + td->der_encoder = asn_DEF_NULL.der_encoder; + td->xer_decoder = asn_DEF_NULL.xer_decoder; + td->xer_encoder = asn_DEF_NULL.xer_encoder; + td->uper_decoder = asn_DEF_NULL.uper_decoder; + td->uper_encoder = asn_DEF_NULL.uper_encoder; + if(!td->per_constraints) + td->per_constraints = asn_DEF_NULL.per_constraints; + td->elements = asn_DEF_NULL.elements; + td->elements_count = asn_DEF_NULL.elements_count; + td->specifics = asn_DEF_NULL.specifics; +} + +void +RSAParameters_free(asn_TYPE_descriptor_t *td, + void *struct_ptr, int contents_only) { + RSAParameters_1_inherit_TYPE_descriptor(td); + td->free_struct(td, struct_ptr, contents_only); +} + +int +RSAParameters_print(asn_TYPE_descriptor_t *td, const void *struct_ptr, + int ilevel, asn_app_consume_bytes_f *cb, void *app_key) { + RSAParameters_1_inherit_TYPE_descriptor(td); + return td->print_struct(td, struct_ptr, ilevel, cb, app_key); +} + +asn_dec_rval_t +RSAParameters_decode_ber(asn_codec_ctx_t *opt_codec_ctx, asn_TYPE_descriptor_t *td, + void **structure, const void *bufptr, size_t size, int tag_mode) { + RSAParameters_1_inherit_TYPE_descriptor(td); + return td->ber_decoder(opt_codec_ctx, td, structure, bufptr, size, tag_mode); +} + +asn_enc_rval_t +RSAParameters_encode_der(asn_TYPE_descriptor_t *td, + void *structure, int tag_mode, ber_tlv_tag_t tag, + asn_app_consume_bytes_f *cb, void *app_key) { + RSAParameters_1_inherit_TYPE_descriptor(td); + return td->der_encoder(td, structure, tag_mode, tag, cb, app_key); +} + +asn_dec_rval_t +RSAParameters_decode_xer(asn_codec_ctx_t *opt_codec_ctx, asn_TYPE_descriptor_t *td, + void **structure, const char *opt_mname, const void *bufptr, size_t size) { + RSAParameters_1_inherit_TYPE_descriptor(td); + return td->xer_decoder(opt_codec_ctx, td, structure, opt_mname, bufptr, size); +} + +asn_enc_rval_t +RSAParameters_encode_xer(asn_TYPE_descriptor_t *td, void *structure, + int ilevel, enum xer_encoder_flags_e flags, + asn_app_consume_bytes_f *cb, void *app_key) { + RSAParameters_1_inherit_TYPE_descriptor(td); + return td->xer_encoder(td, structure, ilevel, flags, cb, app_key); +} + +static const ber_tlv_tag_t asn_DEF_RSAParameters_tags_1[] = { + (ASN_TAG_CLASS_UNIVERSAL | (5 << 2)) +}; +asn_TYPE_descriptor_t asn_DEF_RSAParameters = { + "RSAParameters", + "RSAParameters", + RSAParameters_free, + RSAParameters_print, + RSAParameters_constraint, + RSAParameters_decode_ber, + RSAParameters_encode_der, + RSAParameters_decode_xer, + RSAParameters_encode_xer, + 0, 0, /* No PER support, use "-gen-PER" to enable */ + 0, /* Use generic outmost tag fetcher */ + asn_DEF_RSAParameters_tags_1, + sizeof(asn_DEF_RSAParameters_tags_1) + /sizeof(asn_DEF_RSAParameters_tags_1[0]), /* 1 */ + asn_DEF_RSAParameters_tags_1, /* Same as above */ + sizeof(asn_DEF_RSAParameters_tags_1) + /sizeof(asn_DEF_RSAParameters_tags_1[0]), /* 1 */ + 0, /* No PER visible constraints */ + 0, 0, /* No members */ + 0 /* No specifics */ +}; + diff --git a/asn1_test_data/RSAParameters.h b/asn1_test_data/RSAParameters.h new file mode 100644 index 0000000..2d82f21 --- /dev/null +++ b/asn1_test_data/RSAParameters.h @@ -0,0 +1,38 @@ +/* + * Generated by asn1c-0.9.28 (http://lionet.info/asn1c) + * From ASN.1 module "COMPOSITESIGALGS" + * found in "pqASN1_compiling.asn" + */ + +#ifndef _RSAParameters_H_ +#define _RSAParameters_H_ + + +#include + +/* Including external dependencies */ +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* RSAParameters */ +typedef NULL_t RSAParameters_t; + +/* Implementation */ +extern asn_TYPE_descriptor_t asn_DEF_RSAParameters; +asn_struct_free_f RSAParameters_free; +asn_struct_print_f RSAParameters_print; +asn_constr_check_f RSAParameters_constraint; +ber_type_decoder_f RSAParameters_decode_ber; +der_type_encoder_f RSAParameters_encode_der; +xer_type_decoder_f RSAParameters_decode_xer; +xer_type_encoder_f RSAParameters_encode_xer; + +#ifdef __cplusplus +} +#endif + +#endif /* _RSAParameters_H_ */ +#include diff --git a/asn1_test_data/Test.c b/asn1_test_data/Test.c new file mode 100644 index 0000000..8743a24 --- /dev/null +++ b/asn1_test_data/Test.c @@ -0,0 +1,58 @@ +/* + * Generated by asn1c-0.9.28 (http://lionet.info/asn1c) + * From ASN.1 module "PKIX1Implicit88" + * found in "test.asn1" + */ + +#include "Test.h" + +static asn_TYPE_member_t asn_MBR_Test_1[] = { + { ATF_NOFLAGS, 0, offsetof(struct Test, myValue), + (ASN_TAG_CLASS_UNIVERSAL | (2 << 2)), + 0, + &asn_DEF_NativeInteger, + 0, /* Defer constraints checking to the member type */ + 0, /* PER is not compiled, use -gen-PER */ + 0, + "myValue" + }, +}; +static const ber_tlv_tag_t asn_DEF_Test_tags_1[] = { + (ASN_TAG_CLASS_UNIVERSAL | (16 << 2)) +}; +static const asn_TYPE_tag2member_t asn_MAP_Test_tag2el_1[] = { + { (ASN_TAG_CLASS_UNIVERSAL | (2 << 2)), 0, 0, 0 } /* myValue */ +}; +static asn_SEQUENCE_specifics_t asn_SPC_Test_specs_1 = { + sizeof(struct Test), + offsetof(struct Test, _asn_ctx), + asn_MAP_Test_tag2el_1, + 1, /* Count of tags in the map */ + 0, 0, 0, /* Optional elements (not needed) */ + -1, /* Start extensions */ + -1 /* Stop extensions */ +}; +asn_TYPE_descriptor_t asn_DEF_Test = { + "Test", + "Test", + SEQUENCE_free, + SEQUENCE_print, + SEQUENCE_constraint, + SEQUENCE_decode_ber, + SEQUENCE_encode_der, + SEQUENCE_decode_xer, + SEQUENCE_encode_xer, + 0, 0, /* No PER support, use "-gen-PER" to enable */ + 0, /* Use generic outmost tag fetcher */ + asn_DEF_Test_tags_1, + sizeof(asn_DEF_Test_tags_1) + /sizeof(asn_DEF_Test_tags_1[0]), /* 1 */ + asn_DEF_Test_tags_1, /* Same as above */ + sizeof(asn_DEF_Test_tags_1) + /sizeof(asn_DEF_Test_tags_1[0]), /* 1 */ + 0, /* No PER visible constraints */ + asn_MBR_Test_1, + 1, /* Elements count */ + &asn_SPC_Test_specs_1 /* Additional specs */ +}; + diff --git a/asn1_test_data/Test.h b/asn1_test_data/Test.h new file mode 100644 index 0000000..3d58b70 --- /dev/null +++ b/asn1_test_data/Test.h @@ -0,0 +1,37 @@ +/* + * Generated by asn1c-0.9.28 (http://lionet.info/asn1c) + * From ASN.1 module "PKIX1Implicit88" + * found in "test.asn1" + */ + +#ifndef _Test_H_ +#define _Test_H_ + + +#include + +/* Including external dependencies */ +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* Test */ +typedef struct Test { + long myValue; + + /* Context for parsing across buffer boundaries */ + asn_struct_ctx_t _asn_ctx; +} Test_t; + +/* Implementation */ +extern asn_TYPE_descriptor_t asn_DEF_Test; + +#ifdef __cplusplus +} +#endif + +#endif /* _Test_H_ */ +#include diff --git a/asn1_test_data/VisibleString.c b/asn1_test_data/VisibleString.c new file mode 100644 index 0000000..0f4dec5 --- /dev/null +++ b/asn1_test_data/VisibleString.c @@ -0,0 +1,79 @@ +/*- + * Copyright (c) 2003, 2006 Lev Walkin . All rights reserved. + * Redistribution and modifications are permitted subject to BSD license. + */ +#include +#include + +/* + * VisibleString basic type description. + */ +static const ber_tlv_tag_t asn_DEF_VisibleString_tags[] = { + (ASN_TAG_CLASS_UNIVERSAL | (26 << 2)), /* [UNIVERSAL 26] IMPLICIT ...*/ + (ASN_TAG_CLASS_UNIVERSAL | (4 << 2)) /* ... OCTET STRING */ +}; +static asn_per_constraints_t asn_DEF_VisibleString_constraints = { + { APC_CONSTRAINED, 7, 7, 0x20, 0x7e }, /* Value */ + { APC_SEMI_CONSTRAINED, -1, -1, 0, 0 }, /* Size */ + 0, 0 +}; +asn_TYPE_descriptor_t asn_DEF_VisibleString = { + "VisibleString", + "VisibleString", + OCTET_STRING_free, + OCTET_STRING_print_utf8, /* ASCII subset */ + VisibleString_constraint, + OCTET_STRING_decode_ber, /* Implemented in terms of OCTET STRING */ + OCTET_STRING_encode_der, + OCTET_STRING_decode_xer_utf8, + OCTET_STRING_encode_xer_utf8, + OCTET_STRING_decode_uper, + OCTET_STRING_encode_uper, + 0, /* Use generic outmost tag fetcher */ + asn_DEF_VisibleString_tags, + sizeof(asn_DEF_VisibleString_tags) + / sizeof(asn_DEF_VisibleString_tags[0]) - 1, + asn_DEF_VisibleString_tags, + sizeof(asn_DEF_VisibleString_tags) + / sizeof(asn_DEF_VisibleString_tags[0]), + &asn_DEF_VisibleString_constraints, + 0, 0, /* No members */ + 0 /* No specifics */ +}; + +int +VisibleString_constraint(asn_TYPE_descriptor_t *td, const void *sptr, + asn_app_constraint_failed_f *ctfailcb, void *app_key) { + const VisibleString_t *st = (const VisibleString_t *)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 + * The alphabet is a subset of ASCII between the space + * and "~" (tilde). + */ + for(; buf < end; buf++) { + if(*buf < 0x20 || *buf > 0x7e) { + ASN__CTFAIL(app_key, td, sptr, + "%s: value byte %ld (%d) " + "not in VisibleString alphabet (%s:%d)", + td->name, + (long)((buf - st->buf) + 1), + *buf, + __FILE__, __LINE__); + return -1; + } + } + } else { + ASN__CTFAIL(app_key, td, sptr, + "%s: value not given (%s:%d)", + td->name, __FILE__, __LINE__); + return -1; + } + + return 0; +} diff --git a/asn1_test_data/VisibleString.h b/asn1_test_data/VisibleString.h new file mode 100644 index 0000000..20ba8cc --- /dev/null +++ b/asn1_test_data/VisibleString.h @@ -0,0 +1,24 @@ +/*- + * Copyright (c) 2003 Lev Walkin . All rights reserved. + * Redistribution and modifications are permitted subject to BSD license. + */ +#ifndef _VisibleString_H_ +#define _VisibleString_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef OCTET_STRING_t VisibleString_t; /* Implemented via OCTET STRING */ + +extern asn_TYPE_descriptor_t asn_DEF_VisibleString; + +asn_constr_check_f VisibleString_constraint; + +#ifdef __cplusplus +} +#endif + +#endif /* _VisibleString_H_ */ diff --git a/asn1_test_data/asn_SEQUENCE_OF.c b/asn1_test_data/asn_SEQUENCE_OF.c new file mode 100644 index 0000000..ec952fc --- /dev/null +++ b/asn1_test_data/asn_SEQUENCE_OF.c @@ -0,0 +1,41 @@ +/*- + * Copyright (c) 2003, 2004 Lev Walkin . All rights reserved. + * Redistribution and modifications are permitted subject to BSD license. + */ +#include +#include + +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 *)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/asn1_test_data/asn_SEQUENCE_OF.h b/asn1_test_data/asn_SEQUENCE_OF.h new file mode 100644 index 0000000..e678f03 --- /dev/null +++ b/asn1_test_data/asn_SEQUENCE_OF.h @@ -0,0 +1,52 @@ +/*- + * Copyright (c) 2003, 2004 Lev Walkin . All rights reserved. + * Redistribution and modifications are permitted subject to BSD license. + */ +#ifndef ASN_SEQUENCE_OF_H +#define ASN_SEQUENCE_OF_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * 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); + +/* + * Cope with different conversions requirements to/from void in C and C++. + * This is mostly useful for support library. + */ +typedef A_SEQUENCE_OF(void) asn_anonymous_sequence_; +#define _A_SEQUENCE_FROM_VOID(ptr) ((asn_anonymous_sequence_ *)(ptr)) +#define _A_CSEQUENCE_FROM_VOID(ptr) ((const asn_anonymous_sequence_ *)(ptr)) + +#ifdef __cplusplus +} +#endif + +#endif /* ASN_SEQUENCE_OF_H */ diff --git a/asn1_test_data/asn_SET_OF.c b/asn1_test_data/asn_SET_OF.c new file mode 100644 index 0000000..944f2cb --- /dev/null +++ b/asn1_test_data/asn_SET_OF.c @@ -0,0 +1,88 @@ +/*- + * Copyright (c) 2003, 2004 Lev Walkin . All rights reserved. + * Redistribution and modifications are permitted subject to BSD license. + */ +#include +#include +#include + +/* + * Add another element into the set. + */ +int +asn_set_add(void *asn_set_of_x, void *ptr) { + asn_anonymous_set_ *as = _A_SET_FROM_VOID(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 = (void **)_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_anonymous_set_ *as = _A_SET_FROM_VOID(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_anonymous_set_ *as = _A_SET_FROM_VOID(asn_set_of_x); + + if(as) { + if(as->array) { + if(as->free) { + while(as->count--) + as->free(as->array[as->count]); + } + FREEMEM(as->array); + as->array = 0; + } + as->count = 0; + as->size = 0; + } + +} + diff --git a/asn1_test_data/asn_SET_OF.h b/asn1_test_data/asn_SET_OF.h new file mode 100644 index 0000000..7edf14b --- /dev/null +++ b/asn1_test_data/asn_SET_OF.h @@ -0,0 +1,62 @@ +/*- + * Copyright (c) 2003, 2004 Lev Walkin . All rights reserved. + * Redistribution and modifications are permitted subject to BSD license. + */ +#ifndef ASN_SET_OF_H +#define ASN_SET_OF_H + +#ifdef __cplusplus +extern "C" { +#endif + +#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); + +/* + * Cope with different conversions requirements to/from void in C and C++. + * This is mostly useful for support library. + */ +typedef A_SET_OF(void) asn_anonymous_set_; +#define _A_SET_FROM_VOID(ptr) ((asn_anonymous_set_ *)(ptr)) +#define _A_CSET_FROM_VOID(ptr) ((const asn_anonymous_set_ *)(ptr)) + +#ifdef __cplusplus +} +#endif + +#endif /* ASN_SET_OF_H */ diff --git a/asn1_test_data/asn_application.h b/asn1_test_data/asn_application.h new file mode 100644 index 0000000..71e9ba6 --- /dev/null +++ b/asn1_test_data/asn_application.h @@ -0,0 +1,47 @@ +/*- + * Copyright (c) 2004, 2006 Lev Walkin . All rights reserved. + * Redistribution and modifications are permitted subject to BSD license. + */ +/* + * Application-level ASN.1 callbacks. + */ +#ifndef ASN_APPLICATION_H +#define ASN_APPLICATION_H + +#include "asn_system.h" /* for platform-dependent types */ +#include "asn_codecs.h" /* for ASN.1 codecs specifics */ + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * 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. + * Non-negative return values indicate success, and ignored. + */ +typedef int (asn_app_consume_bytes_f)(const void *buffer, size_t size, + void *application_specific_key); + +/* + * A callback of this type is called whenever constraint validation fails + * on some ASN.1 type. See "constraints.h" for more details on constraint + * validation. + * This callback specifies a descriptor of the ASN.1 type which failed + * the constraint check, as well as human readable message on what + * particular constraint has failed. + */ +typedef void (asn_app_constraint_failed_f)(void *application_specific_key, + struct asn_TYPE_descriptor_s *type_descriptor_which_failed, + const void *structure_which_failed_ptr, + const char *error_message_format, ...) GCC_PRINTFLIKE(4, 5); + +#ifdef __cplusplus +} +#endif + +#include "constr_TYPE.h" /* for asn_TYPE_descriptor_t */ + +#endif /* ASN_APPLICATION_H */ diff --git a/asn1_test_data/asn_codecs.h b/asn1_test_data/asn_codecs.h new file mode 100644 index 0000000..4b2a294 --- /dev/null +++ b/asn1_test_data/asn_codecs.h @@ -0,0 +1,109 @@ +/*- + * Copyright (c) 2003, 2004, 2005 Lev Walkin . + * All rights reserved. + * Redistribution and modifications are permitted subject to BSD license. + */ +#ifndef ASN_CODECS_H +#define ASN_CODECS_H + +#ifdef __cplusplus +extern "C" { +#endif + +struct asn_TYPE_descriptor_s; /* Forward declaration */ + +/* + * This structure defines a set of parameters that may be passed + * to every ASN.1 encoder or decoder function. + * WARNING: if max_stack_size member is set, and you are calling the + * function pointers of the asn_TYPE_descriptor_t directly, + * this structure must be ALLOCATED ON THE STACK! + * If you can't always satisfy this requirement, use ber_decode(), + * xer_decode() and uper_decode() functions instead. + */ +typedef struct asn_codec_ctx_s { + /* + * Limit the decoder routines to use no (much) more stack than a given + * number of bytes. Most of decoders are stack-based, and this + * would protect against stack overflows if the number of nested + * encodings is high. + * The OCTET STRING, BIT STRING and ANY BER decoders are heap-based, + * and are safe from this kind of overflow. + * A value from getrlimit(RLIMIT_STACK) may be used to initialize + * this variable. Be careful in multithreaded environments, as the + * stack size is rather limited. + */ + size_t max_stack_size; /* 0 disables stack bounds checking */ +} asn_codec_ctx_t; + +/* + * Type of the return value of the encoding functions (der_encode, xer_encode). + */ +typedef struct asn_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 asn_TYPE_descriptor_s *failed_type; + + /* Pointer to the structure of that type */ + void *structure_ptr; +} asn_enc_rval_t; +#define ASN__ENCODE_FAILED do { \ + asn_enc_rval_t tmp_error; \ + tmp_error.encoded = -1; \ + tmp_error.failed_type = td; \ + tmp_error.structure_ptr = sptr; \ + ASN_DEBUG("Failed to encode element %s", td ? td->name : ""); \ + return tmp_error; \ +} while(0) +#define ASN__ENCODED_OK(rval) do { \ + rval.structure_ptr = 0; \ + rval.failed_type = 0; \ + return rval; \ +} while(0) + +/* + * Type of the return value of the decoding functions (ber_decode, xer_decode) + * + * Please note that the number of consumed bytes is ALWAYS meaningful, + * even if code==RC_FAIL. This is to indicate the number of successfully + * decoded bytes, hence providing a possibility to fail with more diagnostics + * (i.e., print the offending remainder of the buffer). + */ +enum asn_dec_rval_code_e { + RC_OK, /* Decoded successfully */ + RC_WMORE, /* More data expected, call again */ + RC_FAIL /* Failure to decode data */ +}; +typedef struct asn_dec_rval_s { + enum asn_dec_rval_code_e code; /* Result code */ + size_t consumed; /* Number of bytes consumed */ +} asn_dec_rval_t; +#define ASN__DECODE_FAILED do { \ + asn_dec_rval_t tmp_error; \ + tmp_error.code = RC_FAIL; \ + tmp_error.consumed = 0; \ + ASN_DEBUG("Failed to decode element %s", td ? td->name : ""); \ + return tmp_error; \ +} while(0) +#define ASN__DECODE_STARVED do { \ + asn_dec_rval_t tmp_error; \ + tmp_error.code = RC_WMORE; \ + tmp_error.consumed = 0; \ + return tmp_error; \ +} while(0) + +#ifdef __cplusplus +} +#endif + +#endif /* ASN_CODECS_H */ diff --git a/asn1_test_data/asn_codecs_prim.c b/asn1_test_data/asn_codecs_prim.c new file mode 100644 index 0000000..426339c --- /dev/null +++ b/asn1_test_data/asn_codecs_prim.c @@ -0,0 +1,312 @@ +/*- + * Copyright (c) 2003, 2004 Lev Walkin . All rights reserved. + * Redistribution and modifications are permitted subject to BSD license. + */ +#include +#include +#include + +/* + * Decode an always-primitive type. + */ +asn_dec_rval_t +ber_decode_primitive(asn_codec_ctx_t *opt_codec_ctx, + asn_TYPE_descriptor_t *td, + void **sptr, const void *buf_ptr, size_t size, int tag_mode) { + ASN__PRIMITIVE_TYPE_t *st = (ASN__PRIMITIVE_TYPE_t *)*sptr; + asn_dec_rval_t rval; + ber_tlv_len_t length = 0; /* =0 to avoid [incorrect] warning. */ + + /* + * If the structure is not there, allocate it. + */ + if(st == NULL) { + st = (ASN__PRIMITIVE_TYPE_t *)CALLOC(1, sizeof(*st)); + if(st == NULL) ASN__DECODE_FAILED; + *sptr = (void *)st; + } + + ASN_DEBUG("Decoding %s as plain primitive (tm=%d)", + td->name, tag_mode); + + /* + * Check tags and extract value length. + */ + rval = ber_check_tags(opt_codec_ctx, td, 0, buf_ptr, size, + tag_mode, 0, &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 = ((const char *)buf_ptr) + rval.consumed; + size -= rval.consumed; + if(length > (ber_tlv_len_t)size) { + rval.code = RC_WMORE; + rval.consumed = 0; + return rval; + } + + st->size = (int)length; + /* The following better be optimized away. */ + if(sizeof(st->size) != sizeof(length) + && (ber_tlv_len_t)st->size != length) { + st->size = 0; + ASN__DECODE_FAILED; + } + + st->buf = (uint8_t *)MALLOC(length + 1); + if(!st->buf) { + st->size = 0; + ASN__DECODE_FAILED; + } + + memcpy(st->buf, buf_ptr, length); + st->buf[length] = '\0'; /* Just in case */ + + 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 an always-primitive type using DER. + */ +asn_enc_rval_t +der_encode_primitive(asn_TYPE_descriptor_t *td, void *sptr, + int tag_mode, ber_tlv_tag_t tag, + asn_app_consume_bytes_f *cb, void *app_key) { + asn_enc_rval_t erval; + ASN__PRIMITIVE_TYPE_t *st = (ASN__PRIMITIVE_TYPE_t *)sptr; + + ASN_DEBUG("%s %s as a primitive type (tm=%d)", + cb?"Encoding":"Estimating", td->name, tag_mode); + + erval.encoded = der_write_tags(td, st->size, tag_mode, 0, tag, + cb, app_key); + ASN_DEBUG("%s wrote tags %d", td->name, (int)erval.encoded); + if(erval.encoded == -1) { + erval.failed_type = td; + erval.structure_ptr = sptr; + return erval; + } + + if(cb && st->buf) { + if(cb(st->buf, st->size, app_key) < 0) { + erval.encoded = -1; + erval.failed_type = td; + erval.structure_ptr = sptr; + return erval; + } + } else { + assert(st->buf || st->size == 0); + } + + erval.encoded += st->size; + ASN__ENCODED_OK(erval); +} + +void +ASN__PRIMITIVE_TYPE_free(asn_TYPE_descriptor_t *td, void *sptr, + int contents_only) { + ASN__PRIMITIVE_TYPE_t *st = (ASN__PRIMITIVE_TYPE_t *)sptr; + + if(!td || !sptr) + return; + + ASN_DEBUG("Freeing %s as a primitive type", td->name); + + if(st->buf) + FREEMEM(st->buf); + + if(!contents_only) + FREEMEM(st); +} + + +/* + * Local internal type passed around as an argument. + */ +struct xdp_arg_s { + asn_TYPE_descriptor_t *type_descriptor; + void *struct_key; + xer_primitive_body_decoder_f *prim_body_decoder; + int decoded_something; + int want_more; +}; + +/* + * Since some kinds of primitive values can be encoded using value-specific + * tags (, , etc), the primitive decoder must + * be supplied with such tags to parse them as needed. + */ +static int +xer_decode__unexpected_tag(void *key, const void *chunk_buf, size_t chunk_size) { + struct xdp_arg_s *arg = (struct xdp_arg_s *)key; + enum xer_pbd_rval bret; + + /* + * The chunk_buf is guaranteed to start at '<'. + */ + assert(chunk_size && ((const char *)chunk_buf)[0] == 0x3c); + + /* + * Decoding was performed once already. Prohibit doing it again. + */ + if(arg->decoded_something) + return -1; + + bret = arg->prim_body_decoder(arg->type_descriptor, + arg->struct_key, chunk_buf, chunk_size); + switch(bret) { + case XPBD_SYSTEM_FAILURE: + case XPBD_DECODER_LIMIT: + case XPBD_BROKEN_ENCODING: + break; + case XPBD_BODY_CONSUMED: + /* Tag decoded successfully */ + arg->decoded_something = 1; + /* Fall through */ + case XPBD_NOT_BODY_IGNORE: /* Safe to proceed further */ + return 0; + } + + return -1; +} + +static ssize_t +xer_decode__primitive_body(void *key, const void *chunk_buf, size_t chunk_size, int have_more) { + struct xdp_arg_s *arg = (struct xdp_arg_s *)key; + enum xer_pbd_rval bret; + size_t lead_wsp_size; + + if(arg->decoded_something) { + if(xer_whitespace_span(chunk_buf, chunk_size) == chunk_size) { + /* + * Example: + * "123 " + * ^- chunk_buf position. + */ + return chunk_size; + } + /* + * Decoding was done once already. Prohibit doing it again. + */ + return -1; + } + + if(!have_more) { + /* + * If we've received something like "1", we can't really + * tell whether it is really `1` or `123`, until we know + * that there is no more data coming. + * The have_more argument will be set to 1 once something + * like this is available to the caller of this callback: + * "1want_more = 1; + return -1; + } + + lead_wsp_size = xer_whitespace_span(chunk_buf, chunk_size); + chunk_buf = (const char *)chunk_buf + lead_wsp_size; + chunk_size -= lead_wsp_size; + + bret = arg->prim_body_decoder(arg->type_descriptor, + arg->struct_key, chunk_buf, chunk_size); + switch(bret) { + case XPBD_SYSTEM_FAILURE: + case XPBD_DECODER_LIMIT: + case XPBD_BROKEN_ENCODING: + break; + case XPBD_BODY_CONSUMED: + /* Tag decoded successfully */ + arg->decoded_something = 1; + /* Fall through */ + case XPBD_NOT_BODY_IGNORE: /* Safe to proceed further */ + return lead_wsp_size + chunk_size; + } + + return -1; +} + + +asn_dec_rval_t +xer_decode_primitive(asn_codec_ctx_t *opt_codec_ctx, + asn_TYPE_descriptor_t *td, + void **sptr, + size_t struct_size, + const char *opt_mname, + const void *buf_ptr, size_t size, + xer_primitive_body_decoder_f *prim_body_decoder +) { + const char *xml_tag = opt_mname ? opt_mname : td->xml_tag; + asn_struct_ctx_t s_ctx; + struct xdp_arg_s s_arg; + asn_dec_rval_t rc; + + /* + * Create the structure if does not exist. + */ + if(!*sptr) { + *sptr = CALLOC(1, struct_size); + if(!*sptr) ASN__DECODE_FAILED; + } + + memset(&s_ctx, 0, sizeof(s_ctx)); + s_arg.type_descriptor = td; + s_arg.struct_key = *sptr; + s_arg.prim_body_decoder = prim_body_decoder; + s_arg.decoded_something = 0; + s_arg.want_more = 0; + + rc = xer_decode_general(opt_codec_ctx, &s_ctx, &s_arg, + xml_tag, buf_ptr, size, + xer_decode__unexpected_tag, xer_decode__primitive_body); + switch(rc.code) { + case RC_OK: + if(!s_arg.decoded_something) { + char ch; + ASN_DEBUG("Primitive body is not recognized, " + "supplying empty one"); + /* + * Decoding opportunity has come and gone. + * Where's the result? + * Try to feed with empty body, see if it eats it. + */ + if(prim_body_decoder(s_arg.type_descriptor, + s_arg.struct_key, &ch, 0) + != XPBD_BODY_CONSUMED) { + /* + * This decoder does not like empty stuff. + */ + ASN__DECODE_FAILED; + } + } + break; + case RC_WMORE: + /* + * Redo the whole thing later. + * We don't have a context to save intermediate parsing state. + */ + rc.consumed = 0; + break; + case RC_FAIL: + rc.consumed = 0; + if(s_arg.want_more) + rc.code = RC_WMORE; + else + ASN__DECODE_FAILED; + break; + } + return rc; +} + diff --git a/asn1_test_data/asn_codecs_prim.h b/asn1_test_data/asn_codecs_prim.h new file mode 100644 index 0000000..0f683fd --- /dev/null +++ b/asn1_test_data/asn_codecs_prim.h @@ -0,0 +1,53 @@ +/*- + * Copyright (c) 2004 Lev Walkin . All rights reserved. + * Redistribution and modifications are permitted subject to BSD license. + */ +#ifndef ASN_CODECS_PRIM_H +#define ASN_CODECS_PRIM_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct ASN__PRIMITIVE_TYPE_s { + uint8_t *buf; /* Buffer with consecutive primitive encoding bytes */ + int size; /* Size of the buffer */ +} ASN__PRIMITIVE_TYPE_t; /* Do not use this type directly! */ + +asn_struct_free_f ASN__PRIMITIVE_TYPE_free; +ber_type_decoder_f ber_decode_primitive; +der_type_encoder_f der_encode_primitive; + +/* + * A callback specification for the xer_decode_primitive() function below. + */ +enum xer_pbd_rval { + XPBD_SYSTEM_FAILURE, /* System failure (memory shortage, etc) */ + XPBD_DECODER_LIMIT, /* Hit some decoder limitation or deficiency */ + XPBD_BROKEN_ENCODING, /* Encoding of a primitive body is broken */ + XPBD_NOT_BODY_IGNORE, /* Not a body format, but safe to ignore */ + XPBD_BODY_CONSUMED /* Body is recognized and consumed */ +}; +typedef enum xer_pbd_rval (xer_primitive_body_decoder_f) + (asn_TYPE_descriptor_t *td, void *struct_ptr, + const void *chunk_buf, size_t chunk_size); + +/* + * Specific function to decode simple primitive types. + * Also see xer_decode_general() in xer_decoder.h + */ +asn_dec_rval_t xer_decode_primitive(asn_codec_ctx_t *opt_codec_ctx, + asn_TYPE_descriptor_t *type_descriptor, + void **struct_ptr, size_t struct_size, + const char *opt_mname, + const void *buf_ptr, size_t size, + xer_primitive_body_decoder_f *prim_body_decoder +); + +#ifdef __cplusplus +} +#endif + +#endif /* ASN_CODECS_PRIM_H */ diff --git a/asn1_test_data/asn_internal.h b/asn1_test_data/asn_internal.h new file mode 100644 index 0000000..9c94ca6 --- /dev/null +++ b/asn1_test_data/asn_internal.h @@ -0,0 +1,128 @@ +/*- + * Copyright (c) 2003, 2004, 2005, 2007 Lev Walkin . + * All rights reserved. + * Redistribution and modifications are permitted subject to BSD license. + */ +/* + * Declarations internally useful for the ASN.1 support code. + */ +#ifndef ASN_INTERNAL_H +#define ASN_INTERNAL_H + +#include "asn_application.h" /* Application-visible API */ + +#ifndef __NO_ASSERT_H__ /* Include assert.h only for internal use. */ +#include /* for assert() macro */ +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/* Environment version might be used to avoid running with the old library */ +#define ASN1C_ENVIRONMENT_VERSION 923 /* Compile-time version */ +int get_asn1c_environment_version(void); /* Run-time version */ + +#define CALLOC(nmemb, size) calloc(nmemb, size) +#define MALLOC(size) malloc(size) +#define REALLOC(oldptr, size) realloc(oldptr, size) +#define FREEMEM(ptr) free(ptr) + +#define asn_debug_indent 0 +#define ASN_DEBUG_INDENT_ADD(i) do{}while(0) + +/* + * 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... */ +#ifdef __GNUC__ +#ifdef ASN_THREAD_SAFE +/* Thread safety requires sacrifice in output indentation: + * Retain empty definition of ASN_DEBUG_INDENT_ADD. */ +#else /* !ASN_THREAD_SAFE */ +#undef ASN_DEBUG_INDENT_ADD +#undef asn_debug_indent +int asn_debug_indent; +#define ASN_DEBUG_INDENT_ADD(i) do { asn_debug_indent += i; } while(0) +#endif /* ASN_THREAD_SAFE */ +#define ASN_DEBUG(fmt, args...) do { \ + int adi = asn_debug_indent; \ + while(adi--) fprintf(stderr, " "); \ + fprintf(stderr, fmt, ##args); \ + fprintf(stderr, " (%s:%d)\n", \ + __FILE__, __LINE__); \ + } while(0) +#else /* !__GNUC__ */ +void ASN_DEBUG_f(const char *fmt, ...); +#define ASN_DEBUG ASN_DEBUG_f +#endif /* __GNUC__ */ +#else /* EMIT_ASN_DEBUG != 1 */ +static void ASN_DEBUG(const char *fmt, ...) { (void)fmt; } +#endif /* EMIT_ASN_DEBUG */ +#endif /* ASN_DEBUG */ + +/* + * Invoke the application-supplied callback and fail, if something is wrong. + */ +#define ASN__E_cbc(buf, size) (cb((buf), (size), app_key) < 0) +#define ASN__E_CALLBACK(foo) do { \ + if(foo) goto cb_failed; \ + } while(0) +#define ASN__CALLBACK(buf, size) \ + ASN__E_CALLBACK(ASN__E_cbc(buf, size)) +#define ASN__CALLBACK2(buf1, size1, buf2, size2) \ + ASN__E_CALLBACK(ASN__E_cbc(buf1, size1) || ASN__E_cbc(buf2, size2)) +#define ASN__CALLBACK3(buf1, size1, buf2, size2, buf3, size3) \ + ASN__E_CALLBACK(ASN__E_cbc(buf1, size1) \ + || ASN__E_cbc(buf2, size2) \ + || ASN__E_cbc(buf3, size3)) + +#define ASN__TEXT_INDENT(nl, level) do { \ + int tmp_level = (level); \ + int tmp_nl = ((nl) != 0); \ + int tmp_i; \ + if(tmp_nl) ASN__CALLBACK("\n", 1); \ + if(tmp_level < 0) tmp_level = 0; \ + for(tmp_i = 0; tmp_i < tmp_level; tmp_i++) \ + ASN__CALLBACK(" ", 4); \ + er.encoded += tmp_nl + 4 * tmp_level; \ + } while(0) + +#define _i_INDENT(nl) do { \ + int tmp_i; \ + if((nl) && cb("\n", 1, app_key) < 0) \ + return -1; \ + for(tmp_i = 0; tmp_i < ilevel; tmp_i++) \ + if(cb(" ", 4, app_key) < 0) \ + return -1; \ + } while(0) + +/* + * Check stack against overflow, if limit is set. + */ +#define ASN__DEFAULT_STACK_MAX (30000) +static int __attribute__((unused)) +ASN__STACK_OVERFLOW_CHECK(asn_codec_ctx_t *ctx) { + if(ctx && ctx->max_stack_size) { + + /* ctx MUST be allocated on the stack */ + ptrdiff_t usedstack = ((char *)ctx - (char *)&ctx); + if(usedstack > 0) usedstack = -usedstack; /* grows up! */ + + /* double negative required to avoid int wrap-around */ + if(usedstack < -(ptrdiff_t)ctx->max_stack_size) { + ASN_DEBUG("Stack limit %ld reached", + (long)ctx->max_stack_size); + return -1; + } + } + return 0; +} + +#ifdef __cplusplus +} +#endif + +#endif /* ASN_INTERNAL_H */ diff --git a/asn1_test_data/asn_system.h b/asn1_test_data/asn_system.h new file mode 100644 index 0000000..d19837e --- /dev/null +++ b/asn1_test_data/asn_system.h @@ -0,0 +1,137 @@ +/*- + * Copyright (c) 2003, 2004, 2007 Lev Walkin . + * All rights reserved. + * Redistribution and modifications are permitted subject to BSD license. + */ +/* + * Miscellaneous system-dependent types. + */ +#ifndef ASN_SYSTEM_H +#define ASN_SYSTEM_H + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifndef _BSD_SOURCE +#define _BSD_SOURCE /* for snprintf() on some linux systems */ +#endif + +#include /* For snprintf(3) */ +#include /* For *alloc(3) */ +#include /* For memcpy(3) */ +#include /* For size_t */ +#include /* For LONG_MAX */ +#include /* For va_start */ +#include /* for offsetof and ptrdiff_t */ + +#ifdef HAVE_ALLOCA_H +#include /* For alloca(3) */ +#endif + +#ifdef _WIN32 + +#include +#define snprintf _snprintf +#define vsnprintf _vsnprintf + +/* To avoid linking with ws2_32.lib, here's the definition of ntohl() */ +#define sys_ntohl(l) ((((l) << 24) & 0xff000000) \ + | (((l) << 8) & 0xff0000) \ + | (((l) >> 8) & 0xff00) \ + | ((l >> 24) & 0xff)) + +#ifdef _MSC_VER /* MSVS.Net */ +#ifndef __cplusplus +#define inline __inline +#endif +#ifndef ASSUMESTDTYPES /* Standard types have been defined elsewhere */ +#define ssize_t SSIZE_T +typedef char int8_t; +typedef short int16_t; +typedef int int32_t; +typedef unsigned char uint8_t; +typedef unsigned short uint16_t; +typedef unsigned int uint32_t; +#endif /* ASSUMESTDTYPES */ +#define WIN32_LEAN_AND_MEAN +#include +#include +#define isnan _isnan +#define finite _finite +#define copysign _copysign +#define ilogb _logb +#else /* !_MSC_VER */ +#include +#endif /* _MSC_VER */ + +#else /* !_WIN32 */ + +#if defined(__vxworks) +#include +#else /* !defined(__vxworks) */ + +#include /* C99 specifies this file */ +/* + * 1. Earlier FreeBSD version didn't have , + * but was present. + * 2. Sun Solaris requires for alloca(3), + * but does not have . + */ +#if (!defined(__FreeBSD__) || !defined(_SYS_INTTYPES_H_)) +#if defined(sun) +#include /* For alloca(3) */ +#include /* for finite(3) */ +#elif defined(__hpux) +#ifdef __GNUC__ +#include /* For alloca(3) */ +#else /* !__GNUC__ */ +#define inline +#endif /* __GNUC__ */ +#else +#include /* SUSv2+ and C99 specify this file, for uintXX_t */ +#endif /* defined(sun) */ +#endif + +#include /* for ntohl() */ +#define sys_ntohl(foo) ntohl(foo) + +#endif /* defined(__vxworks) */ + +#endif /* _WIN32 */ + +#if __GNUC__ >= 3 +#ifndef GCC_PRINTFLIKE +#define GCC_PRINTFLIKE(fmt,var) __attribute__((format(printf,fmt,var))) +#endif +#ifndef GCC_NOTUSED +#define GCC_NOTUSED __attribute__((unused)) +#endif +#else +#ifndef GCC_PRINTFLIKE +#define GCC_PRINTFLIKE(fmt,var) /* nothing */ +#endif +#ifndef GCC_NOTUSED +#define GCC_NOTUSED +#endif +#endif + +/* Figure out if thread safety is requested */ +#if !defined(ASN_THREAD_SAFE) && (defined(THREAD_SAFE) || defined(_REENTRANT)) +#define ASN_THREAD_SAFE +#endif /* Thread safety */ + +#ifndef offsetof /* If not defined by */ +#define offsetof(s, m) ((ptrdiff_t)&(((s *)0)->m) - (ptrdiff_t)((s *)0)) +#endif /* offsetof */ + +#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 */ + +#endif /* ASN_SYSTEM_H */ diff --git a/asn1_test_data/ber_decoder.c b/asn1_test_data/ber_decoder.c new file mode 100644 index 0000000..b3a6329 --- /dev/null +++ b/asn1_test_data/ber_decoder.c @@ -0,0 +1,283 @@ +/*- + * Copyright (c) 2003, 2004 Lev Walkin . All rights reserved. + * Redistribution and modifications are permitted subject to BSD license. + */ +#include + +#undef ADVANCE +#define ADVANCE(num_bytes) do { \ + size_t num = num_bytes; \ + ptr = ((const char *)ptr) + num; \ + size -= num; \ + consumed_myself += num; \ + } while(0) +#undef RETURN +#define RETURN(_code) do { \ + asn_dec_rval_t rval; \ + rval.code = _code; \ + if(opt_ctx) opt_ctx->step = step; /* Save context */ \ + if(_code == RC_OK || opt_ctx) \ + rval.consumed = consumed_myself; \ + else \ + rval.consumed = 0; /* Context-free */ \ + return rval; \ + } while(0) + +/* + * The BER decoder of any type. + */ +asn_dec_rval_t +ber_decode(asn_codec_ctx_t *opt_codec_ctx, + asn_TYPE_descriptor_t *type_descriptor, + void **struct_ptr, const void *ptr, size_t size) { + asn_codec_ctx_t s_codec_ctx; + + /* + * Stack checker requires that the codec context + * must be allocated on the stack. + */ + if(opt_codec_ctx) { + if(opt_codec_ctx->max_stack_size) { + s_codec_ctx = *opt_codec_ctx; + opt_codec_ctx = &s_codec_ctx; + } + } else { + /* If context is not given, be security-conscious anyway */ + memset(&s_codec_ctx, 0, sizeof(s_codec_ctx)); + s_codec_ctx.max_stack_size = ASN__DEFAULT_STACK_MAX; + opt_codec_ctx = &s_codec_ctx; + } + + /* + * Invoke type-specific decoder. + */ + return type_descriptor->ber_decoder(opt_codec_ctx, 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 >> tags matches the definition. + */ +asn_dec_rval_t +ber_check_tags(asn_codec_ctx_t *opt_codec_ctx, + asn_TYPE_descriptor_t *td, asn_struct_ctx_t *opt_ctx, + const void *ptr, size_t size, int tag_mode, int last_tag_form, + 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 step = opt_ctx ? opt_ctx->step : 0; /* Where we left previously */ + int tagno; + + /* + * Make sure we didn't exceed the maximum stack size. + */ + if(ASN__STACK_OVERFLOW_CHECK(opt_codec_ctx)) + RETURN(RC_FAIL); + + /* + * So what does all this implicit 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. + */ + + tagno = step /* Continuing where left previously */ + + (tag_mode==1?-1:0) + ; + ASN_DEBUG("ber_check_tags(%s, size=%ld, tm=%d, step=%d, tagno=%d)", + td->name, (long)size, tag_mode, step, tagno); + /* assert(td->tags_count >= 1) May not be the case for CHOICE or ANY */ + + if(tag_mode == 0 && tagno == td->tags_count) { + /* + * This must be the _untagged_ ANY type, + * which outermost tag isn't known in advance. + * Fetch the tag and length separately. + */ + tag_len = ber_fetch_tag(ptr, size, &tlv_tag); + switch(tag_len) { + case -1: RETURN(RC_FAIL); + case 0: RETURN(RC_WMORE); + } + tlv_constr = BER_TLV_CONSTRUCTED(ptr); + len_len = ber_fetch_length(tlv_constr, + (const char *)ptr + tag_len, size - tag_len, &tlv_len); + switch(len_len) { + case -1: RETURN(RC_FAIL); + case 0: RETURN(RC_WMORE); + } + ASN_DEBUG("Advancing %ld in ANY case", + (long)(tag_len + len_len)); + ADVANCE(tag_len + len_len); + } else { + assert(tagno < td->tags_count); /* At least one loop */ + } + for((void)tagno; tagno < td->tags_count; tagno++, step++) { + + /* + * Fetch and process T from TLV. + */ + tag_len = ber_fetch_tag(ptr, size, &tlv_tag); + ASN_DEBUG("Fetching tag from {%p,%ld}: " + "len %ld, step %d, tagno %d got %s", + ptr, (long)size, + (long)tag_len, step, tagno, + 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 && 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 != td->tags[tagno]) { + /* + * Unexpected tag. Too bad. + */ + ASN_DEBUG("Expected: %s, " + "expectation failed (tn=%d, tm=%d)", + ber_tlv_tag_string(td->tags[tagno]), + tagno, tag_mode + ); + 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 < (td->tags_count - 1)) { + if(tlv_constr == 0) { + ASN_DEBUG("tlv_constr = %d, expfail", + tlv_constr); + RETURN(RC_FAIL); + } + } else { + if(last_tag_form != tlv_constr + && last_tag_form != -1) { + ASN_DEBUG("last_tag_form %d != %d", + last_tag_form, tlv_constr); + RETURN(RC_FAIL); + } + } + + /* + * Fetch and process L from TLV. + */ + len_len = ber_fetch_length(tlv_constr, + (const char *)ptr + tag_len, size - tag_len, &tlv_len); + ASN_DEBUG("Fetching len = %ld", (long)len_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; + if(limit_len < 0) { + /* Too great tlv_len value? */ + RETURN(RC_FAIL); + } + } 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 %ld and inner is %ld", + (long)limit_len, (long)tlv_len); + RETURN(RC_FAIL); + } + + ADVANCE(tag_len + len_len); + + limit_len -= (tag_len + len_len); + if((ssize_t)size > limit_len) { + /* + * Make sure that we won't consume more bytes + * from the parent frame 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/asn1_test_data/ber_decoder.h b/asn1_test_data/ber_decoder.h new file mode 100644 index 0000000..9fe2e89 --- /dev/null +++ b/asn1_test_data/ber_decoder.h @@ -0,0 +1,64 @@ +/*- + * Copyright (c) 2003, 2004 Lev Walkin . All rights reserved. + * Redistribution and modifications are permitted subject to BSD license. + */ +#ifndef _BER_DECODER_H_ +#define _BER_DECODER_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct asn_TYPE_descriptor_s; /* Forward declaration */ +struct asn_codec_ctx_s; /* Forward declaration */ + +/* + * The BER decoder of any type. + * This function may be invoked directly from the application. + * The der_encode() function (der_encoder.h) is an opposite to ber_decode(). + */ +asn_dec_rval_t ber_decode(struct asn_codec_ctx_s *opt_codec_ctx, + struct asn_TYPE_descriptor_s *type_descriptor, + void **struct_ptr, /* Pointer to a target structure's pointer */ + const 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 asn_dec_rval_t (ber_type_decoder_f)( + struct asn_codec_ctx_s *opt_codec_ctx, + struct asn_TYPE_descriptor_s *type_descriptor, + void **struct_ptr, const 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. + */ +asn_dec_rval_t ber_check_tags( + struct asn_codec_ctx_s *opt_codec_ctx, /* codec options */ + struct asn_TYPE_descriptor_s *type_descriptor, + asn_struct_ctx_t *opt_ctx, /* saved decoding context */ + const void *ptr, size_t size, + int tag_mode, /* {-1,0,1}: IMPLICIT, no, EXPLICIT */ + int last_tag_form, /* {-1,0:1}: any, primitive, constr */ + ber_tlv_len_t *last_length, + int *opt_tlv_form /* optional tag form */ + ); + +#ifdef __cplusplus +} +#endif + +#endif /* _BER_DECODER_H_ */ diff --git a/asn1_test_data/ber_tlv_length.c b/asn1_test_data/ber_tlv_length.c new file mode 100644 index 0000000..4c2f1e5 --- /dev/null +++ b/asn1_test_data/ber_tlv_length.c @@ -0,0 +1,178 @@ +/*- + * Copyright (c) 2003, 2004 Lev Walkin . All rights reserved. + * Redistribution and modifications are permitted subject to BSD license. + */ +#include +#include +#include + +ssize_t +ber_fetch_length(int _is_constructed, const void *bufptr, size_t size, + ber_tlv_len_t *len_r) { + const uint8_t *buf = (const uint8_t *)bufptr; + unsigned oct; + + if(size == 0) + return 0; /* Want more */ + + oct = *(const uint8_t *)buf; + if((oct & 0x80) == 0) { + /* + * Short definite length. + */ + *len_r = oct; /* & 0x7F */ + return 1; + } else { + ber_tlv_len_t len; + size_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) { + ber_tlv_len_t lenplusepsilon = (size_t)len + 1024; + /* + * Here length may be very close or equal to 2G. + * However, the arithmetics used in some decoders + * may add some (small) quantities to the length, + * to check the resulting value against some limits. + * This may result in integer wrap-around, which + * we try to avoid by checking it earlier here. + */ + if(lenplusepsilon < 0) { + /* Too large length value */ + return -1; + } + + *len_r = len; + return skipped; + } + + return 0; /* Want more */ + } + +} + +ssize_t +ber_skip_length(asn_codec_ctx_t *opt_codec_ctx, + int _is_constructed, const 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 */ + size_t skip; + + /* + * Make sure we didn't exceed the maximum stack size. + */ + if(ASN__STACK_OVERFLOW_CHECK(opt_codec_ctx)) + return -1; + + /* + * 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 = ((const char *)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(opt_codec_ctx, + BER_TLV_CONSTRUCTED(ptr), + ((const char *)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(((const uint8_t *)ptr)[0] == 0 + && ((const uint8_t *)ptr)[1] == 0) + return skip; + + ptr = ((const char *)ptr) + tl + ll; + size -= tl + ll; + } + + /* UNREACHABLE */ +} + +size_t +der_tlv_length_serialize(ber_tlv_len_t len, void *bufp, size_t size) { + size_t required_size; /* Size of len encoding */ + uint8_t *buf = (uint8_t *)bufp; + uint8_t *end; + size_t i; + + if(len <= 127) { + /* Encoded in 1 octet */ + if(size) *buf = (uint8_t)len; + return 1; + } + + /* + * Compute the size of the subsequent bytes. + */ + for(required_size = 1, i = 8; i < 8 * sizeof(len); i += 8) { + if(len >> i) + required_size++; + else + break; + } + + if(size <= required_size) + return required_size + 1; + + *buf++ = (uint8_t)(0x80 | required_size); /* Length of the encoding */ + + /* + * Produce the len encoding, space permitting. + */ + end = buf + required_size; + for(i -= 8; buf < end; i -= 8, buf++) + *buf = (uint8_t)(len >> i); + + return required_size + 1; +} + diff --git a/asn1_test_data/ber_tlv_length.h b/asn1_test_data/ber_tlv_length.h new file mode 100644 index 0000000..3496802 --- /dev/null +++ b/asn1_test_data/ber_tlv_length.h @@ -0,0 +1,50 @@ +/*- + * Copyright (c) 2003 Lev Walkin . All rights reserved. + * Redistribution and modifications are permitted subject to BSD license. + */ +#ifndef _BER_TLV_LENGTH_H_ +#define _BER_TLV_LENGTH_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +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, const 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( + struct asn_codec_ctx_s *opt_codec_ctx, /* optional context */ + int _is_constructed, const void *bufptr, size_t size); + +/* + * This function serializes the length (L from TLV) in DER format. + * It always returns 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. + */ +size_t der_tlv_length_serialize(ber_tlv_len_t len, void *bufptr, size_t size); + +#ifdef __cplusplus +} +#endif + +#endif /* _BER_TLV_LENGTH_H_ */ diff --git a/asn1_test_data/ber_tlv_tag.c b/asn1_test_data/ber_tlv_tag.c new file mode 100644 index 0000000..4270876 --- /dev/null +++ b/asn1_test_data/ber_tlv_tag.c @@ -0,0 +1,144 @@ +/*- + * Copyright (c) 2003, 2004 Lev Walkin . All rights reserved. + * Redistribution and modifications are permitted subject to BSD license. + */ +#include +#include +#include + +ssize_t +ber_fetch_tag(const void *ptr, size_t size, ber_tlv_tag_t *tag_r) { + ber_tlv_tag_t val; + ber_tlv_tag_t tclass; + size_t skipped; + + if(size == 0) + return 0; + + val = *(const uint8_t *)ptr; + tclass = (val >> 6); + if((val &= 0x1F) != 0x1F) { + /* + * 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 = ((const char *)ptr) + 1, skipped = 2; + skipped <= size; + ptr = ((const char *)ptr) + 1, skipped++) { + unsigned int oct = *(const 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 { + val = (val << 7) | oct; + *tag_r = (val << 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 >= (ssize_t)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; +} + + +size_t +ber_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 = (uint8_t *)bufp; + uint8_t *end; + size_t required_size; + size_t 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. + */ + for(required_size = 1, i = 7; i < 8 * sizeof(tval); i += 7) { + if(tval >> i) + required_size++; + else + break; + } + + if(size < required_size) + return required_size + 1; + + /* + * Fill in the buffer, space permitting. + */ + end = buf + required_size - 1; + for(i -= 7; buf < end; i -= 7, buf++) + *buf = 0x80 | ((tval >> i) & 0x7F); + *buf = (tval & 0x7F); /* Last octet without high bit */ + + return required_size + 1; +} + diff --git a/asn1_test_data/ber_tlv_tag.h b/asn1_test_data/ber_tlv_tag.h new file mode 100644 index 0000000..60e8668 --- /dev/null +++ b/asn1_test_data/ber_tlv_tag.h @@ -0,0 +1,60 @@ +/*- + * Copyright (c) 2003, 2004 Lev Walkin . All rights reserved. + * Redistribution and modifications are permitted subject to BSD license. + */ +#ifndef _BER_TLV_TAG_H_ +#define _BER_TLV_TAG_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +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) (((*(const 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(const void *bufptr, size_t size, ber_tlv_tag_t *tag_r); + +/* + * This function serializes the tag (T from TLV) in BER format. + * It always returns 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. + */ +size_t ber_tlv_tag_serialize(ber_tlv_tag_t tag, void *bufptr, size_t size); + +#ifdef __cplusplus +} +#endif + +#endif /* _BER_TLV_TAG_H_ */ diff --git a/asn1_test_data/constr_SEQUENCE.c b/asn1_test_data/constr_SEQUENCE.c new file mode 100644 index 0000000..5923023 --- /dev/null +++ b/asn1_test_data/constr_SEQUENCE.c @@ -0,0 +1,1425 @@ +/*- + * Copyright (c) 2003, 2004, 2005, 2006, 2007 Lev Walkin . + * All rights reserved. + * Redistribution and modifications are permitted subject to BSD license. + */ +#include +#include +#include + +/* + * 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<(size_t)ctx->left)?size:(size_t)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 's L, even if the buffer size allows + * reading more data. + * For example, consider the buffer containing the following TLVs: + * ... + * 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 && (size_t)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. + */ +#undef ADVANCE +#define ADVANCE(num_bytes) do { \ + size_t num = num_bytes; \ + ptr = ((const char *)ptr) + num; \ + size -= num; \ + if(ctx->left >= 0) \ + ctx->left -= num; \ + consumed_myself += num; \ + } while(0) + +/* + * Switch to the next phase of parsing. + */ +#undef NEXT_PHASE +#undef PHASE_OUT +#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. + */ +#undef RETURN +#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)) + + +/* + * Tags are canonically sorted in the tag2element map. + */ +static int +_t2e_cmp(const void *ap, const void *bp) { + const asn_TYPE_tag2member_t *a = (const asn_TYPE_tag2member_t *)ap; + const asn_TYPE_tag2member_t *b = (const asn_TYPE_tag2member_t *)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) { + if(a->el_no > b->el_no) + return 1; + /* + * Important: we do not check + * for a->el_no <= b->el_no! + */ + 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 SEQUENCE type. + */ +asn_dec_rval_t +SEQUENCE_decode_ber(asn_codec_ctx_t *opt_codec_ctx, asn_TYPE_descriptor_t *td, + void **struct_ptr, const void *ptr, size_t size, int tag_mode) { + /* + * Bring closer parts of structure description. + */ + asn_SEQUENCE_specifics_t *specs = (asn_SEQUENCE_specifics_t *)td->specifics; + asn_TYPE_member_t *elements = td->elements; + + /* + * Parts of the structure being constructed. + */ + void *st = *struct_ptr; /* Target structure. */ + asn_struct_ctx_t *ctx; /* Decoder context */ + + ber_tlv_tag_t tlv_tag; /* T from TLV */ + asn_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", td->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 = (asn_struct_ctx_t *)((char *)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(opt_codec_ctx, td, ctx, ptr, size, + tag_mode, 1, &ctx->left, 0); + if(rval.code != RC_OK) { + ASN_DEBUG("%s tagging check failed: %d", + td->name, rval.code); + return rval; + } + + 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 = ( * 2 + ). + */ + for(edx = (ctx->step >> 1); edx < td->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 use_bsearch; + int n; + + if(ctx->step & 1) + goto microphase2; + + /* + * MICROPHASE 1: Synchronize decoding. + */ + ASN_DEBUG("In %s SEQUENCE left %d, edx=%d flags=%d" + " opt=%d ec=%d", + td->name, (int)ctx->left, edx, + elements[edx].flags, elements[edx].optional, + td->elements_count); + + if(ctx->left == 0 /* No more stuff is expected */ + && ( + /* Explicit OPTIONAL specification reaches the end */ + (edx + elements[edx].optional + == td->elements_count) + || + /* All extensions are optional */ + (IN_EXTENSION_GROUP(specs, edx) + && specs->ext_before > td->elements_count) + ) + ) { + ASN_DEBUG("End of SEQUENCE %s", td->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("Current tag in %s SEQUENCE for element %d " + "(%s) is %s encoded in %d bytes, of frame %ld", + td->name, edx, elements[edx].name, + ber_tlv_tag_string(tlv_tag), (int)tag_len, (long)LEFT); + switch(tag_len) { + case 0: if(!SIZE_VIOLATION) RETURN(RC_WMORE); + /* Fall through */ + case -1: RETURN(RC_FAIL); + } + + if(ctx->left < 0 && ((const uint8_t *)ptr)[0] == 0) { + if(LEFT < 2) { + if(SIZE_VIOLATION) + RETURN(RC_FAIL); + else + RETURN(RC_WMORE); + } else if(((const uint8_t *)ptr)[1] == 0) { + ASN_DEBUG("edx = %d, opt = %d, ec=%d", + edx, elements[edx].optional, + td->elements_count); + if((edx + elements[edx].optional + == td->elements_count) + || (IN_EXTENSION_GROUP(specs, edx) + && specs->ext_before + > td->elements_count)) { + /* + * Yeah, baby! Found the terminator + * of the indefinite length structure. + */ + /* + * Proceed to the canonical + * finalization function. + * No advancing is necessary. + */ + goto phase3; + } + } + } + + /* + * Find the next available type with this tag. + */ + use_bsearch = 0; + opt_edx_end = edx + elements[edx].optional + 1; + if(opt_edx_end > td->elements_count) + opt_edx_end = td->elements_count; /* Cap */ + else if(opt_edx_end - edx > 8) { + /* Limit the scope of linear search... */ + opt_edx_end = edx + 8; + use_bsearch = 1; + /* ... and resort to bsearch() */ + } + 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 = 1 + 2 * edx; /* Remember! */ + goto microphase2; + } else if(elements[n].flags & ATF_OPEN_TYPE) { + /* + * This is the ANY type, which may bear + * any flag whatsoever. + */ + edx = n; + ctx->step = 1 + 2 * edx; /* Remember! */ + goto microphase2; + } else if(elements[n].tag == (ber_tlv_tag_t)-1) { + use_bsearch = 1; + break; + } + } + if(use_bsearch) { + /* + * Resort to a binary search over + * sorted array of tags. + */ + const asn_TYPE_tag2member_t *t2m; + asn_TYPE_tag2member_t key; + key.el_tag = tlv_tag; + key.el_no = edx; + t2m = (const asn_TYPE_tag2member_t *)bsearch(&key, + specs->tag2el, specs->tag2el_count, + sizeof(specs->tag2el[0]), _t2e_cmp); + if(t2m) { + const asn_TYPE_tag2member_t *best = 0; + const asn_TYPE_tag2member_t *t2m_f, *t2m_l; + int edx_max = edx + elements[edx].optional; + /* + * Rewind to the first element with that tag, + * `cause bsearch() does not guarantee order. + */ + t2m_f = t2m + t2m->toff_first; + t2m_l = t2m + t2m->toff_last; + for(t2m = t2m_f; t2m <= t2m_l; t2m++) { + if(t2m->el_no > edx_max) break; + if(t2m->el_no < edx) continue; + best = t2m; + } + if(best) { + edx = best->el_no; + ctx->step = 1 + 2 * edx; + goto microphase2; + } + } + n = opt_edx_end; + } + 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 + elements[edx].optional)) { + ASN_DEBUG("Unexpected tag %s (at %d)", + ber_tlv_tag_string(tlv_tag), edx); + ASN_DEBUG("Expected tag %s (%s)%s", + ber_tlv_tag_string(elements[edx].tag), + elements[edx].name, + elements[edx].optional + ?" or alternatives":""); + RETURN(RC_FAIL); + } else { + /* Skip this tag */ + ssize_t skip; + edx += elements[edx].optional; + + ASN_DEBUG("Skipping unexpected %s (at %d)", + ber_tlv_tag_string(tlv_tag), edx); + skip = ber_skip_length(opt_codec_ctx, + BER_TLV_CONSTRUCTED(ptr), + (const char *)ptr + tag_len, + LEFT - tag_len); + ASN_DEBUG("Skip length %d in %s", + (int)skip, td->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", td->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].flags & ATF_POINTER) { + /* Member is a pointer to another structure */ + memb_ptr2 = (void **)((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(opt_codec_ctx, + elements[edx].type, + memb_ptr2, ptr, LEFT, + elements[edx].tag_mode); + ASN_DEBUG("In %s SEQUENCE decoded %d %s of %d " + "in %d bytes rval.code %d, size=%d", + td->name, edx, elements[edx].type->name, + (int)LEFT, (int)rval.consumed, rval.code, (int)size); + switch(rval.code) { + case RC_OK: + break; + case RC_WMORE: /* More data expected */ + if(!SIZE_VIOLATION) { + ADVANCE(rval.consumed); + RETURN(RC_WMORE); + } + ASN_DEBUG("Size violation (c->l=%ld <= s=%ld)", + (long)ctx->left, (long)size); + /* 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", + td->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 + && ((const uint8_t *)ptr)[0] == 0) { + if(LEFT < 2) { + if(SIZE_VIOLATION) + RETURN(RC_FAIL); + else + RETURN(RC_WMORE); + } else if(((const uint8_t *)ptr)[1] == 0) { + /* + * Correctly finished with <0><0>. + */ + ADVANCE(2); + ctx->left++; + ctx->phase = 4; + continue; + } + } + + if(!IN_EXTENSION_GROUP(specs, td->elements_count) + || ctx->phase == 4) { + ASN_DEBUG("Unexpected continuation " + "of a non-extensible type " + "%s (SEQUENCE): %s", + td->name, + ber_tlv_tag_string(tlv_tag)); + RETURN(RC_FAIL); + } + + ll = ber_skip_length(opt_codec_ctx, + BER_TLV_CONSTRUCTED(ptr), + (const char *)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. + */ +asn_enc_rval_t +SEQUENCE_encode_der(asn_TYPE_descriptor_t *td, + void *sptr, int tag_mode, ber_tlv_tag_t tag, + asn_app_consume_bytes_f *cb, void *app_key) { + size_t computed_size = 0; + asn_enc_rval_t erval; + ssize_t ret; + int edx; + + ASN_DEBUG("%s %s as SEQUENCE", + cb?"Encoding":"Estimating", td->name); + + /* + * Gather the length of the underlying members sequence. + */ + for(edx = 0; edx < td->elements_count; edx++) { + asn_TYPE_member_t *elm = &td->elements[edx]; + void *memb_ptr; + if(elm->flags & ATF_POINTER) { + memb_ptr = *(void **)((char *)sptr + elm->memb_offset); + if(!memb_ptr) { + if(elm->optional) continue; + /* Mandatory element is missing */ + ASN__ENCODE_FAILED; + } + } else { + memb_ptr = (void *)((char *)sptr + 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(td, computed_size, tag_mode, 1, tag, cb, app_key); + ASN_DEBUG("Wrote tags: %ld (+%ld)", (long)ret, (long)computed_size); + if(ret == -1) + ASN__ENCODE_FAILED; + erval.encoded = computed_size + ret; + + if(!cb) ASN__ENCODED_OK(erval); + + /* + * Encode all members. + */ + for(edx = 0; edx < td->elements_count; edx++) { + asn_TYPE_member_t *elm = &td->elements[edx]; + asn_enc_rval_t tmperval; + void *memb_ptr; + + if(elm->flags & ATF_POINTER) { + memb_ptr = *(void **)((char *)sptr + elm->memb_offset); + if(!memb_ptr) continue; + } else { + memb_ptr = (void *)((char *)sptr + 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 %ld bytes", + edx, elm->name, td->name, (long)tmperval.encoded); + } + + if(computed_size != 0) + /* + * Encoded size is not equal to the computed size. + */ + ASN__ENCODE_FAILED; + + ASN__ENCODED_OK(erval); +} + + +#undef XER_ADVANCE +#define XER_ADVANCE(num_bytes) do { \ + size_t num = num_bytes; \ + buf_ptr = ((const char *)buf_ptr) + num;\ + size -= num; \ + consumed_myself += num; \ + } while(0) + +/* + * Decode the XER (XML) data. + */ +asn_dec_rval_t +SEQUENCE_decode_xer(asn_codec_ctx_t *opt_codec_ctx, asn_TYPE_descriptor_t *td, + void **struct_ptr, const char *opt_mname, + const void *buf_ptr, size_t size) { + /* + * Bring closer parts of structure description. + */ + asn_SEQUENCE_specifics_t *specs + = (asn_SEQUENCE_specifics_t *)td->specifics; + asn_TYPE_member_t *elements = td->elements; + const char *xml_tag = opt_mname ? opt_mname : td->xml_tag; + + /* + * ... and parts of the structure being constructed. + */ + void *st = *struct_ptr; /* Target structure. */ + asn_struct_ctx_t *ctx; /* Decoder context */ + + asn_dec_rval_t rval; /* Return value from a decoder */ + ssize_t consumed_myself = 0; /* Consumed bytes from ptr */ + int edx; /* Element index */ + int edx_end; + + /* + * 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 = (asn_struct_ctx_t *)((char *)st + specs->ctx_offset); + + + /* + * Phases of XER/XML processing: + * Phase 0: Check that the opening tag matches our expectations. + * Phase 1: Processing body and reacting on closing tag. + * Phase 2: Processing inner type. + * Phase 3: Skipping unknown extensions. + * Phase 4: PHASED OUT + */ + for(edx = ctx->step; ctx->phase <= 3;) { + pxer_chunk_type_e ch_type; /* XER chunk type */ + ssize_t ch_size; /* Chunk size */ + xer_check_tag_e tcv; /* Tag check value */ + asn_TYPE_member_t *elm; + int n; + + /* + * Go inside the inner member of a sequence. + */ + if(ctx->phase == 2) { + asn_dec_rval_t tmprval; + void *memb_ptr; /* Pointer to the member */ + void **memb_ptr2; /* Pointer to that pointer */ + + elm = &td->elements[edx]; + + if(elm->flags & ATF_POINTER) { + /* Member is a pointer to another structure */ + memb_ptr2 = (void **)((char *)st + elm->memb_offset); + } else { + memb_ptr = (char *)st + elm->memb_offset; + memb_ptr2 = &memb_ptr; + } + + /* Invoke the inner type decoder, m.b. multiple times */ + tmprval = elm->type->xer_decoder(opt_codec_ctx, + elm->type, memb_ptr2, elm->name, + buf_ptr, size); + XER_ADVANCE(tmprval.consumed); + if(tmprval.code != RC_OK) + RETURN(tmprval.code); + ctx->phase = 1; /* Back to body processing */ + ctx->step = ++edx; + ASN_DEBUG("XER/SEQUENCE phase => %d, step => %d", + ctx->phase, ctx->step); + /* Fall through */ + } + + /* + * Get the next part of the XML stream. + */ + ch_size = xer_next_token(&ctx->context, buf_ptr, size, + &ch_type); + if(ch_size == -1) { + RETURN(RC_FAIL); + } else { + switch(ch_type) { + case PXER_WMORE: + RETURN(RC_WMORE); + case PXER_COMMENT: /* Got XML comment */ + case PXER_TEXT: /* Ignore free-standing text */ + XER_ADVANCE(ch_size); /* Skip silently */ + continue; + case PXER_TAG: + break; /* Check the rest down there */ + } + } + + tcv = xer_check_tag(buf_ptr, ch_size, xml_tag); + ASN_DEBUG("XER/SEQUENCE: tcv = %d, ph=%d [%s]", + tcv, ctx->phase, xml_tag); + + /* Skip the extensions section */ + if(ctx->phase == 3) { + switch(xer_skip_unknown(tcv, &ctx->left)) { + case -1: + ctx->phase = 4; + RETURN(RC_FAIL); + case 0: + XER_ADVANCE(ch_size); + continue; + case 1: + XER_ADVANCE(ch_size); + ctx->phase = 1; + continue; + case 2: + ctx->phase = 1; + break; + } + } + + switch(tcv) { + case XCT_CLOSING: + if(ctx->phase == 0) break; + ctx->phase = 0; + /* Fall through */ + case XCT_BOTH: + if(ctx->phase == 0) { + if(edx >= td->elements_count + || + /* Explicit OPTIONAL specs reaches the end */ + (edx + elements[edx].optional + == td->elements_count) + || + /* All extensions are optional */ + (IN_EXTENSION_GROUP(specs, edx) + && specs->ext_before + > td->elements_count) + ) { + XER_ADVANCE(ch_size); + ctx->phase = 4; /* Phase out */ + RETURN(RC_OK); + } else { + ASN_DEBUG("Premature end of XER SEQUENCE"); + RETURN(RC_FAIL); + } + } + /* Fall through */ + case XCT_OPENING: + if(ctx->phase == 0) { + XER_ADVANCE(ch_size); + ctx->phase = 1; /* Processing body phase */ + continue; + } + /* Fall through */ + case XCT_UNKNOWN_OP: + case XCT_UNKNOWN_BO: + + ASN_DEBUG("XER/SEQUENCE: tcv=%d, ph=%d, edx=%d", + tcv, ctx->phase, edx); + if(ctx->phase != 1) { + break; /* Really unexpected */ + } + + if(edx < td->elements_count) { + /* + * Search which member corresponds to this tag. + */ + edx_end = edx + elements[edx].optional + 1; + if(edx_end > td->elements_count) + edx_end = td->elements_count; + for(n = edx; n < edx_end; n++) { + elm = &td->elements[n]; + tcv = xer_check_tag(buf_ptr, + ch_size, elm->name); + switch(tcv) { + case XCT_BOTH: + case XCT_OPENING: + /* + * Process this member. + */ + ctx->step = edx = n; + ctx->phase = 2; + break; + case XCT_UNKNOWN_OP: + case XCT_UNKNOWN_BO: + continue; + default: + n = edx_end; + break; /* Phase out */ + } + break; + } + if(n != edx_end) + continue; + } else { + ASN_DEBUG("Out of defined members: %d/%d", + edx, td->elements_count); + } + + /* It is expected extension */ + if(IN_EXTENSION_GROUP(specs, + edx + (edx < td->elements_count + ? elements[edx].optional : 0))) { + ASN_DEBUG("Got anticipated extension at %d", + edx); + /* + * Check for (XCT_BOTH or XCT_UNKNOWN_BO) + * By using a mask. Only record a pure + * tags. + */ + if(tcv & XCT_CLOSING) { + /* Found without body */ + } else { + ctx->left = 1; + ctx->phase = 3; /* Skip ...'s */ + } + XER_ADVANCE(ch_size); + continue; + } + + /* Fall through */ + default: + break; + } + + ASN_DEBUG("Unexpected XML tag in SEQUENCE [%c%c%c%c%c%c]", + size>0?((const char *)buf_ptr)[0]:'.', + size>1?((const char *)buf_ptr)[1]:'.', + size>2?((const char *)buf_ptr)[2]:'.', + size>3?((const char *)buf_ptr)[3]:'.', + size>4?((const char *)buf_ptr)[4]:'.', + size>5?((const char *)buf_ptr)[5]:'.'); + break; + } + + ctx->phase = 4; /* "Phase out" on hard failure */ + RETURN(RC_FAIL); +} + +asn_enc_rval_t +SEQUENCE_encode_xer(asn_TYPE_descriptor_t *td, void *sptr, + int ilevel, enum xer_encoder_flags_e flags, + asn_app_consume_bytes_f *cb, void *app_key) { + asn_enc_rval_t er; + int xcan = (flags & XER_F_CANONICAL); + int edx; + + if(!sptr) + ASN__ENCODE_FAILED; + + er.encoded = 0; + + for(edx = 0; edx < td->elements_count; edx++) { + asn_enc_rval_t tmper; + asn_TYPE_member_t *elm = &td->elements[edx]; + void *memb_ptr; + const char *mname = elm->name; + unsigned int mlen = strlen(mname); + + if(elm->flags & ATF_POINTER) { + memb_ptr = *(void **)((char *)sptr + elm->memb_offset); + if(!memb_ptr) { + if(elm->optional) + continue; + /* Mandatory element is missing */ + ASN__ENCODE_FAILED; + } + } else { + memb_ptr = (void *)((char *)sptr + elm->memb_offset); + } + + if(!xcan) ASN__TEXT_INDENT(1, ilevel); + ASN__CALLBACK3("<", 1, mname, mlen, ">", 1); + + /* Print the member itself */ + tmper = elm->type->xer_encoder(elm->type, memb_ptr, + ilevel + 1, flags, cb, app_key); + if(tmper.encoded == -1) return tmper; + + ASN__CALLBACK3("", 1); + er.encoded += 5 + (2 * mlen) + tmper.encoded; + } + + if(!xcan) ASN__TEXT_INDENT(1, ilevel - 1); + + ASN__ENCODED_OK(er); +cb_failed: + ASN__ENCODE_FAILED; +} + +int +SEQUENCE_print(asn_TYPE_descriptor_t *td, const void *sptr, int ilevel, + asn_app_consume_bytes_f *cb, void *app_key) { + int edx; + int ret; + + if(!sptr) return (cb("", 8, app_key) < 0) ? -1 : 0; + + /* Dump preamble */ + if(cb(td->name, strlen(td->name), app_key) < 0 + || cb(" ::= {", 6, app_key) < 0) + return -1; + + for(edx = 0; edx < td->elements_count; edx++) { + asn_TYPE_member_t *elm = &td->elements[edx]; + const void *memb_ptr; + + if(elm->flags & ATF_POINTER) { + memb_ptr = *(const void * const *)((const char *)sptr + elm->memb_offset); + if(!memb_ptr) { + if(elm->optional) continue; + /* Print line */ + /* Fall through */ + } + } else { + memb_ptr = (const void *)((const char *)sptr + elm->memb_offset); + } + + /* Indentation */ + _i_INDENT(1); + + /* Print the member's name and stuff */ + if(cb(elm->name, strlen(elm->name), app_key) < 0 + || cb(": ", 2, app_key) < 0) + return -1; + + /* Print the member itself */ + ret = elm->type->print_struct(elm->type, memb_ptr, ilevel + 1, + cb, app_key); + if(ret) return ret; + } + + ilevel--; + _i_INDENT(1); + + return (cb("}", 1, app_key) < 0) ? -1 : 0; +} + +void +SEQUENCE_free(asn_TYPE_descriptor_t *td, void *sptr, int contents_only) { + int edx; + + if(!td || !sptr) + return; + + ASN_DEBUG("Freeing %s as SEQUENCE", td->name); + + for(edx = 0; edx < td->elements_count; edx++) { + asn_TYPE_member_t *elm = &td->elements[edx]; + void *memb_ptr; + if(elm->flags & ATF_POINTER) { + memb_ptr = *(void **)((char *)sptr + elm->memb_offset); + if(memb_ptr) + ASN_STRUCT_FREE(*elm->type, memb_ptr); + } else { + memb_ptr = (void *)((char *)sptr + elm->memb_offset); + ASN_STRUCT_FREE_CONTENTS_ONLY(*elm->type, memb_ptr); + } + } + + if(!contents_only) { + FREEMEM(sptr); + } +} + +int +SEQUENCE_constraint(asn_TYPE_descriptor_t *td, const void *sptr, + asn_app_constraint_failed_f *ctfailcb, void *app_key) { + int edx; + + if(!sptr) { + ASN__CTFAIL(app_key, td, sptr, + "%s: value not given (%s:%d)", + td->name, __FILE__, __LINE__); + return -1; + } + + /* + * Iterate over structure members and check their validity. + */ + for(edx = 0; edx < td->elements_count; edx++) { + asn_TYPE_member_t *elm = &td->elements[edx]; + const void *memb_ptr; + + if(elm->flags & ATF_POINTER) { + memb_ptr = *(const void * const *)((const char *)sptr + elm->memb_offset); + if(!memb_ptr) { + if(elm->optional) + continue; + ASN__CTFAIL(app_key, td, sptr, + "%s: mandatory element %s absent (%s:%d)", + td->name, elm->name, __FILE__, __LINE__); + return -1; + } + } else { + memb_ptr = (const void *)((const char *)sptr + elm->memb_offset); + } + + if(elm->memb_constraints) { + int ret = elm->memb_constraints(elm->type, memb_ptr, + ctfailcb, app_key); + if(ret) return ret; + } else { + int ret = elm->type->check_constraints(elm->type, + memb_ptr, ctfailcb, app_key); + if(ret) return ret; + /* + * Cannot inherit it earlier: + * need to make sure we get the updated version. + */ + elm->memb_constraints = elm->type->check_constraints; + } + } + + return 0; +} + +asn_dec_rval_t +SEQUENCE_decode_uper(asn_codec_ctx_t *opt_codec_ctx, asn_TYPE_descriptor_t *td, + asn_per_constraints_t *constraints, void **sptr, asn_per_data_t *pd) { + asn_SEQUENCE_specifics_t *specs = (asn_SEQUENCE_specifics_t *)td->specifics; + void *st = *sptr; /* Target structure. */ + int extpresent; /* Extension additions are present */ + uint8_t *opres; /* Presence of optional root members */ + asn_per_data_t opmd; + asn_dec_rval_t rv; + int edx; + + (void)constraints; + + if(ASN__STACK_OVERFLOW_CHECK(opt_codec_ctx)) + ASN__DECODE_FAILED; + + if(!st) { + st = *sptr = CALLOC(1, specs->struct_size); + if(!st) ASN__DECODE_FAILED; + } + + ASN_DEBUG("Decoding %s as SEQUENCE (UPER)", td->name); + + /* Handle extensions */ + if(specs->ext_before >= 0) { + extpresent = per_get_few_bits(pd, 1); + if(extpresent < 0) ASN__DECODE_STARVED; + } else { + extpresent = 0; + } + + /* Prepare a place and read-in the presence bitmap */ + memset(&opmd, 0, sizeof(opmd)); + if(specs->roms_count) { + opres = (uint8_t *)MALLOC(((specs->roms_count + 7) >> 3) + 1); + if(!opres) ASN__DECODE_FAILED; + /* Get the presence map */ + if(per_get_many_bits(pd, opres, 0, specs->roms_count)) { + FREEMEM(opres); + ASN__DECODE_STARVED; + } + opmd.buffer = opres; + opmd.nbits = specs->roms_count; + ASN_DEBUG("Read in presence bitmap for %s of %d bits (%x..)", + td->name, specs->roms_count, *opres); + } else { + opres = 0; + } + + /* + * Get the sequence ROOT elements. + */ + for(edx = 0; edx < td->elements_count; edx++) { + asn_TYPE_member_t *elm = &td->elements[edx]; + void *memb_ptr; /* Pointer to the member */ + void **memb_ptr2; /* Pointer to that pointer */ + + if(IN_EXTENSION_GROUP(specs, edx)) + continue; + + /* Fetch the pointer to this member */ + if(elm->flags & ATF_POINTER) { + memb_ptr2 = (void **)((char *)st + elm->memb_offset); + } else { + memb_ptr = (char *)st + elm->memb_offset; + memb_ptr2 = &memb_ptr; + } + + /* Deal with optionality */ + if(elm->optional) { + int present = per_get_few_bits(&opmd, 1); + ASN_DEBUG("Member %s->%s is optional, p=%d (%d->%d)", + td->name, elm->name, present, + (int)opmd.nboff, (int)opmd.nbits); + if(present == 0) { + /* This element is not present */ + if(elm->default_value) { + /* Fill-in DEFAULT */ + if(elm->default_value(1, memb_ptr2)) { + FREEMEM(opres); + ASN__DECODE_FAILED; + } + ASN_DEBUG("Filled-in default"); + } + /* The member is just not present */ + continue; + } + /* Fall through */ + } + + /* Fetch the member from the stream */ + ASN_DEBUG("Decoding member %s in %s", elm->name, td->name); + rv = elm->type->uper_decoder(opt_codec_ctx, elm->type, + elm->per_constraints, memb_ptr2, pd); + if(rv.code != RC_OK) { + ASN_DEBUG("Failed decode %s in %s", + elm->name, td->name); + FREEMEM(opres); + return rv; + } + } + + /* Optionality map is not needed anymore */ + FREEMEM(opres); + + /* + * Deal with extensions. + */ + if(extpresent) { + ssize_t bmlength; + uint8_t *epres; /* Presence of extension members */ + asn_per_data_t epmd; + + bmlength = uper_get_nslength(pd); + if(bmlength < 0) ASN__DECODE_STARVED; + + ASN_DEBUG("Extensions %ld present in %s", (long)bmlength, td->name); + + epres = (uint8_t *)MALLOC((bmlength + 15) >> 3); + if(!epres) ASN__DECODE_STARVED; + + /* Get the extensions map */ + if(per_get_many_bits(pd, epres, 0, bmlength)) { + FREEMEM(epres); + ASN__DECODE_STARVED; + } + + memset(&epmd, 0, sizeof(epmd)); + epmd.buffer = epres; + epmd.nbits = bmlength; + ASN_DEBUG("Read in extensions bitmap for %s of %ld bits (%x..)", + td->name, (long)bmlength, *epres); + + /* Go over extensions and read them in */ + for(edx = specs->ext_after + 1; edx < td->elements_count; edx++) { + asn_TYPE_member_t *elm = &td->elements[edx]; + void *memb_ptr; /* Pointer to the member */ + void **memb_ptr2; /* Pointer to that pointer */ + int present; + + if(!IN_EXTENSION_GROUP(specs, edx)) { + ASN_DEBUG("%d is not extension", edx); + continue; + } + + /* Fetch the pointer to this member */ + if(elm->flags & ATF_POINTER) { + memb_ptr2 = (void **)((char *)st + elm->memb_offset); + } else { + memb_ptr = (void *)((char *)st + elm->memb_offset); + memb_ptr2 = &memb_ptr; + } + + present = per_get_few_bits(&epmd, 1); + if(present <= 0) { + if(present < 0) break; /* No more extensions */ + continue; + } + + ASN_DEBUG("Decoding member %s in %s %p", elm->name, td->name, *memb_ptr2); + rv = uper_open_type_get(opt_codec_ctx, elm->type, + elm->per_constraints, memb_ptr2, pd); + if(rv.code != RC_OK) { + FREEMEM(epres); + return rv; + } + } + + /* Skip over overflow extensions which aren't present + * in this system's version of the protocol */ + for(;;) { + ASN_DEBUG("Getting overflow extensions"); + switch(per_get_few_bits(&epmd, 1)) { + case -1: break; + case 0: continue; + default: + if(uper_open_type_skip(opt_codec_ctx, pd)) { + FREEMEM(epres); + ASN__DECODE_STARVED; + } + } + break; + } + + FREEMEM(epres); + } + + /* Fill DEFAULT members in extensions */ + for(edx = specs->roms_count; edx < specs->roms_count + + specs->aoms_count; edx++) { + asn_TYPE_member_t *elm = &td->elements[edx]; + void **memb_ptr2; /* Pointer to member pointer */ + + if(!elm->default_value) continue; + + /* Fetch the pointer to this member */ + if(elm->flags & ATF_POINTER) { + memb_ptr2 = (void **)((char *)st + + elm->memb_offset); + if(*memb_ptr2) continue; + } else { + continue; /* Extensions are all optionals */ + } + + /* Set default value */ + if(elm->default_value(1, memb_ptr2)) { + ASN__DECODE_FAILED; + } + } + + rv.consumed = 0; + rv.code = RC_OK; + return rv; +} + +static int +SEQUENCE_handle_extensions(asn_TYPE_descriptor_t *td, void *sptr, + asn_per_outp_t *po1, asn_per_outp_t *po2) { + asn_SEQUENCE_specifics_t *specs + = (asn_SEQUENCE_specifics_t *)td->specifics; + int exts_present = 0; + int exts_count = 0; + int edx; + + if(specs->ext_before < 0) + return 0; + + /* Find out which extensions are present */ + for(edx = specs->ext_after + 1; edx < td->elements_count; edx++) { + asn_TYPE_member_t *elm = &td->elements[edx]; + void *memb_ptr; /* Pointer to the member */ + void **memb_ptr2; /* Pointer to that pointer */ + int present; + + if(!IN_EXTENSION_GROUP(specs, edx)) { + ASN_DEBUG("%s (@%d) is not extension", elm->type->name, edx); + continue; + } + + /* Fetch the pointer to this member */ + if(elm->flags & ATF_POINTER) { + memb_ptr2 = (void **)((char *)sptr + elm->memb_offset); + present = (*memb_ptr2 != 0); + } else { + memb_ptr = (void *)((char *)sptr + elm->memb_offset); + memb_ptr2 = &memb_ptr; + present = 1; + } + + ASN_DEBUG("checking %s (@%d) present => %d", + elm->type->name, edx, present); + exts_count++; + exts_present += present; + + /* Encode as presence marker */ + if(po1 && per_put_few_bits(po1, present, 1)) + return -1; + /* Encode as open type field */ + if(po2 && present && uper_open_type_put(elm->type, + elm->per_constraints, *memb_ptr2, po2)) + return -1; + + } + + return exts_present ? exts_count : 0; +} + +asn_enc_rval_t +SEQUENCE_encode_uper(asn_TYPE_descriptor_t *td, + asn_per_constraints_t *constraints, void *sptr, asn_per_outp_t *po) { + asn_SEQUENCE_specifics_t *specs + = (asn_SEQUENCE_specifics_t *)td->specifics; + asn_enc_rval_t er; + int n_extensions; + int edx; + int i; + + (void)constraints; + + if(!sptr) + ASN__ENCODE_FAILED; + + er.encoded = 0; + + ASN_DEBUG("Encoding %s as SEQUENCE (UPER)", td->name); + + + /* + * X.691#18.1 Whether structure is extensible + * and whether to encode extensions + */ + if(specs->ext_before >= 0) { + n_extensions = SEQUENCE_handle_extensions(td, sptr, 0, 0); + per_put_few_bits(po, n_extensions ? 1 : 0, 1); + } else { + n_extensions = 0; /* There are no extensions to encode */ + } + + /* Encode a presence bitmap */ + for(i = 0; i < specs->roms_count; i++) { + asn_TYPE_member_t *elm; + void *memb_ptr; /* Pointer to the member */ + void **memb_ptr2; /* Pointer to that pointer */ + int present; + + edx = specs->oms[i]; + elm = &td->elements[edx]; + + /* Fetch the pointer to this member */ + if(elm->flags & ATF_POINTER) { + memb_ptr2 = (void **)((char *)sptr + elm->memb_offset); + present = (*memb_ptr2 != 0); + } else { + memb_ptr = (void *)((char *)sptr + elm->memb_offset); + memb_ptr2 = &memb_ptr; + present = 1; + } + + /* Eliminate default values */ + if(present && elm->default_value + && elm->default_value(0, memb_ptr2) == 1) + present = 0; + + ASN_DEBUG("Element %s %s %s->%s is %s", + elm->flags & ATF_POINTER ? "ptr" : "inline", + elm->default_value ? "def" : "wtv", + td->name, elm->name, present ? "present" : "absent"); + if(per_put_few_bits(po, present, 1)) + ASN__ENCODE_FAILED; + } + + /* + * Encode the sequence ROOT elements. + */ + ASN_DEBUG("ext_after = %d, ec = %d, eb = %d", specs->ext_after, td->elements_count, specs->ext_before); + for(edx = 0; edx < ((specs->ext_after < 0) + ? td->elements_count : specs->ext_before - 1); edx++) { + + asn_TYPE_member_t *elm = &td->elements[edx]; + void *memb_ptr; /* Pointer to the member */ + void **memb_ptr2; /* Pointer to that pointer */ + + if(IN_EXTENSION_GROUP(specs, edx)) + continue; + + ASN_DEBUG("About to encode %s", elm->type->name); + + /* Fetch the pointer to this member */ + if(elm->flags & ATF_POINTER) { + memb_ptr2 = (void **)((char *)sptr + elm->memb_offset); + if(!*memb_ptr2) { + ASN_DEBUG("Element %s %d not present", + elm->name, edx); + if(elm->optional) + continue; + /* Mandatory element is missing */ + ASN__ENCODE_FAILED; + } + } else { + memb_ptr = (void *)((char *)sptr + elm->memb_offset); + memb_ptr2 = &memb_ptr; + } + + /* Eliminate default values */ + if(elm->default_value && elm->default_value(0, memb_ptr2) == 1) + continue; + + ASN_DEBUG("Encoding %s->%s", td->name, elm->name); + er = elm->type->uper_encoder(elm->type, elm->per_constraints, + *memb_ptr2, po); + if(er.encoded == -1) + return er; + } + + /* No extensions to encode */ + if(!n_extensions) ASN__ENCODED_OK(er); + + ASN_DEBUG("Length of %d bit-map", n_extensions); + /* #18.8. Write down the presence bit-map length. */ + if(uper_put_nslength(po, n_extensions)) + ASN__ENCODE_FAILED; + + ASN_DEBUG("Bit-map of %d elements", n_extensions); + /* #18.7. Encoding the extensions presence bit-map. */ + /* TODO: act upon NOTE in #18.7 for canonical PER */ + if(SEQUENCE_handle_extensions(td, sptr, po, 0) != n_extensions) + ASN__ENCODE_FAILED; + + ASN_DEBUG("Writing %d extensions", n_extensions); + /* #18.9. Encode extensions as open type fields. */ + if(SEQUENCE_handle_extensions(td, sptr, 0, po) != n_extensions) + ASN__ENCODE_FAILED; + + ASN__ENCODED_OK(er); +} + diff --git a/asn1_test_data/constr_SEQUENCE.h b/asn1_test_data/constr_SEQUENCE.h new file mode 100644 index 0000000..c2aeb66 --- /dev/null +++ b/asn1_test_data/constr_SEQUENCE.h @@ -0,0 +1,60 @@ +/*- + * Copyright (c) 2003, 2004 Lev Walkin . All rights reserved. + * Redistribution and modifications are permitted subject to BSD license. + */ +#ifndef _CONSTR_SEQUENCE_H_ +#define _CONSTR_SEQUENCE_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef const struct asn_SEQUENCE_specifics_s { + /* + * Target structure description. + */ + int struct_size; /* Size of the target structure. */ + int ctx_offset; /* Offset of the asn_struct_ctx_t member */ + + /* + * Tags to members mapping table (sorted). + */ + const asn_TYPE_tag2member_t *tag2el; + int tag2el_count; + + /* + * Optional members of the extensions root (roms) or additions (aoms). + * Meaningful for PER. + */ + const int *oms; /* Optional MemberS */ + int roms_count; /* Root optional members count */ + int aoms_count; /* Additions optional members count */ + + /* + * Description of an extensions group. + */ + int ext_after; /* Extensions start after this member */ + int ext_before; /* Extensions stop before this member */ +} asn_SEQUENCE_specifics_t; + + +/* + * A set specialized functions dealing with the SEQUENCE type. + */ +asn_struct_free_f SEQUENCE_free; +asn_struct_print_f SEQUENCE_print; +asn_constr_check_f SEQUENCE_constraint; +ber_type_decoder_f SEQUENCE_decode_ber; +der_type_encoder_f SEQUENCE_encode_der; +xer_type_decoder_f SEQUENCE_decode_xer; +xer_type_encoder_f SEQUENCE_encode_xer; +per_type_decoder_f SEQUENCE_decode_uper; +per_type_encoder_f SEQUENCE_encode_uper; + +#ifdef __cplusplus +} +#endif + +#endif /* _CONSTR_SEQUENCE_H_ */ diff --git a/asn1_test_data/constr_SEQUENCE_OF.c b/asn1_test_data/constr_SEQUENCE_OF.c new file mode 100644 index 0000000..8a08ee8 --- /dev/null +++ b/asn1_test_data/constr_SEQUENCE_OF.c @@ -0,0 +1,208 @@ +/*- + * Copyright (c) 2003, 2004, 2006 Lev Walkin . + * All rights reserved. + * Redistribution and modifications are permitted subject to BSD license. + */ +#include +#include +#include + +/* + * The DER encoder of the SEQUENCE OF type. + */ +asn_enc_rval_t +SEQUENCE_OF_encode_der(asn_TYPE_descriptor_t *td, void *ptr, + int tag_mode, ber_tlv_tag_t tag, + asn_app_consume_bytes_f *cb, void *app_key) { + asn_TYPE_member_t *elm = td->elements; + asn_anonymous_sequence_ *list = _A_SEQUENCE_FROM_VOID(ptr); + size_t computed_size = 0; + ssize_t encoding_size = 0; + asn_enc_rval_t erval; + int edx; + + ASN_DEBUG("Estimating size of SEQUENCE OF %s", td->name); + + /* + * Gather the length of the underlying members sequence. + */ + for(edx = 0; edx < list->count; edx++) { + void *memb_ptr = list->array[edx]; + if(!memb_ptr) continue; + 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(td, computed_size, tag_mode, 1, tag, + cb, app_key); + if(encoding_size == -1) { + erval.encoded = -1; + erval.failed_type = td; + erval.structure_ptr = ptr; + return erval; + } + + computed_size += encoding_size; + if(!cb) { + erval.encoded = computed_size; + ASN__ENCODED_OK(erval); + } + + ASN_DEBUG("Encoding members of SEQUENCE OF %s", td->name); + + /* + * Encode all members. + */ + for(edx = 0; edx < list->count; edx++) { + void *memb_ptr = list->array[edx]; + if(!memb_ptr) continue; + 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 != (size_t)encoding_size) { + /* + * Encoded size is not equal to the computed size. + */ + erval.encoded = -1; + erval.failed_type = td; + erval.structure_ptr = ptr; + } else { + erval.encoded = computed_size; + erval.structure_ptr = 0; + erval.failed_type = 0; + } + + return erval; +} + +asn_enc_rval_t +SEQUENCE_OF_encode_xer(asn_TYPE_descriptor_t *td, void *sptr, + int ilevel, enum xer_encoder_flags_e flags, + asn_app_consume_bytes_f *cb, void *app_key) { + asn_enc_rval_t er; + asn_SET_OF_specifics_t *specs = (asn_SET_OF_specifics_t *)td->specifics; + asn_TYPE_member_t *elm = td->elements; + asn_anonymous_sequence_ *list = _A_SEQUENCE_FROM_VOID(sptr); + const char *mname = specs->as_XMLValueList + ? 0 : ((*elm->name) ? elm->name : elm->type->xml_tag); + unsigned int mlen = mname ? strlen(mname) : 0; + int xcan = (flags & XER_F_CANONICAL); + int i; + + if(!sptr) ASN__ENCODE_FAILED; + + er.encoded = 0; + + for(i = 0; i < list->count; i++) { + asn_enc_rval_t tmper; + void *memb_ptr = list->array[i]; + if(!memb_ptr) continue; + + if(mname) { + if(!xcan) ASN__TEXT_INDENT(1, ilevel); + ASN__CALLBACK3("<", 1, mname, mlen, ">", 1); + } + + tmper = elm->type->xer_encoder(elm->type, memb_ptr, + ilevel + 1, flags, cb, app_key); + if(tmper.encoded == -1) return tmper; + if(tmper.encoded == 0 && specs->as_XMLValueList) { + const char *name = elm->type->xml_tag; + size_t len = strlen(name); + if(!xcan) ASN__TEXT_INDENT(1, ilevel + 1); + ASN__CALLBACK3("<", 1, name, len, "/>", 2); + } + + if(mname) { + ASN__CALLBACK3("", 1); + er.encoded += 5; + } + + er.encoded += (2 * mlen) + tmper.encoded; + } + + if(!xcan) ASN__TEXT_INDENT(1, ilevel - 1); + + ASN__ENCODED_OK(er); +cb_failed: + ASN__ENCODE_FAILED; +} + +asn_enc_rval_t +SEQUENCE_OF_encode_uper(asn_TYPE_descriptor_t *td, + asn_per_constraints_t *constraints, void *sptr, asn_per_outp_t *po) { + asn_anonymous_sequence_ *list; + asn_per_constraint_t *ct; + asn_enc_rval_t er; + asn_TYPE_member_t *elm = td->elements; + int seq; + + if(!sptr) ASN__ENCODE_FAILED; + list = _A_SEQUENCE_FROM_VOID(sptr); + + er.encoded = 0; + + ASN_DEBUG("Encoding %s as SEQUENCE OF (%d)", td->name, list->count); + + if(constraints) ct = &constraints->size; + else if(td->per_constraints) ct = &td->per_constraints->size; + else ct = 0; + + /* If extensible constraint, check if size is in root */ + if(ct) { + int not_in_root = (list->count < ct->lower_bound + || list->count > ct->upper_bound); + ASN_DEBUG("lb %ld ub %ld %s", + ct->lower_bound, ct->upper_bound, + ct->flags & APC_EXTENSIBLE ? "ext" : "fix"); + if(ct->flags & APC_EXTENSIBLE) { + /* Declare whether size is in extension root */ + if(per_put_few_bits(po, not_in_root, 1)) + ASN__ENCODE_FAILED; + if(not_in_root) ct = 0; + } else if(not_in_root && ct->effective_bits >= 0) + ASN__ENCODE_FAILED; + } + + if(ct && ct->effective_bits >= 0) { + /* X.691, #19.5: No length determinant */ + if(per_put_few_bits(po, list->count - ct->lower_bound, + ct->effective_bits)) + ASN__ENCODE_FAILED; + } + + for(seq = -1; seq < list->count;) { + ssize_t mayEncode; + if(seq < 0) seq = 0; + if(ct && ct->effective_bits >= 0) { + mayEncode = list->count; + } else { + mayEncode = uper_put_length(po, list->count - seq); + if(mayEncode < 0) ASN__ENCODE_FAILED; + } + + while(mayEncode--) { + void *memb_ptr = list->array[seq++]; + if(!memb_ptr) ASN__ENCODE_FAILED; + er = elm->type->uper_encoder(elm->type, + elm->per_constraints, memb_ptr, po); + if(er.encoded == -1) + ASN__ENCODE_FAILED; + } + } + + ASN__ENCODED_OK(er); +} + diff --git a/asn1_test_data/constr_SEQUENCE_OF.h b/asn1_test_data/constr_SEQUENCE_OF.h new file mode 100644 index 0000000..e2272f3 --- /dev/null +++ b/asn1_test_data/constr_SEQUENCE_OF.h @@ -0,0 +1,33 @@ +/*- + * Copyright (c) 2003, 2005 Lev Walkin . All rights reserved. + * Redistribution and modifications are permitted subject to BSD license. + */ +#ifndef _CONSTR_SEQUENCE_OF_H_ +#define _CONSTR_SEQUENCE_OF_H_ + +#include +#include /* Implemented using SET OF */ + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * A set specialized functions dealing with the SEQUENCE OF type. + * Generally implemented using SET OF. + */ +#define SEQUENCE_OF_free SET_OF_free +#define SEQUENCE_OF_print SET_OF_print +#define SEQUENCE_OF_constraint SET_OF_constraint +#define SEQUENCE_OF_decode_ber SET_OF_decode_ber +#define SEQUENCE_OF_decode_xer SET_OF_decode_xer +#define SEQUENCE_OF_decode_uper SET_OF_decode_uper +der_type_encoder_f SEQUENCE_OF_encode_der; +xer_type_encoder_f SEQUENCE_OF_encode_xer; +per_type_encoder_f SEQUENCE_OF_encode_uper; + +#ifdef __cplusplus +} +#endif + +#endif /* _CONSTR_SET_OF_H_ */ diff --git a/asn1_test_data/constr_SET_OF.c b/asn1_test_data/constr_SET_OF.c new file mode 100644 index 0000000..2dbc6e5 --- /dev/null +++ b/asn1_test_data/constr_SET_OF.c @@ -0,0 +1,954 @@ +/*- + * Copyright (c) 2003, 2004, 2005 Lev Walkin . + * All rights reserved. + * Redistribution and modifications are permitted subject to BSD license. + */ +#include +#include +#include + +/* + * 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<(size_t)ctx->left)?size:(size_t)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 's L, even if the buffer size allows + * reading more data. + * For example, consider the buffer containing the following TLVs: + * ... + * 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 && (size_t)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. + */ +#undef ADVANCE +#define ADVANCE(num_bytes) do { \ + size_t num = num_bytes; \ + ptr = ((const char *)ptr) + num;\ + size -= num; \ + if(ctx->left >= 0) \ + ctx->left -= num; \ + consumed_myself += num; \ + } while(0) + +/* + * Switch to the next phase of parsing. + */ +#undef NEXT_PHASE +#undef PHASE_OUT +#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. + */ +#undef RETURN +#define RETURN(_code) do { \ + rval.code = _code; \ + rval.consumed = consumed_myself;\ + return rval; \ + } while(0) + +/* + * The decoder of the SET OF type. + */ +asn_dec_rval_t +SET_OF_decode_ber(asn_codec_ctx_t *opt_codec_ctx, asn_TYPE_descriptor_t *td, + void **struct_ptr, const void *ptr, size_t size, int tag_mode) { + /* + * Bring closer parts of structure description. + */ + asn_SET_OF_specifics_t *specs = (asn_SET_OF_specifics_t *)td->specifics; + asn_TYPE_member_t *elm = td->elements; /* Single one */ + + /* + * Parts of the structure being constructed. + */ + void *st = *struct_ptr; /* Target structure. */ + asn_struct_ctx_t *ctx; /* Decoder context */ + + ber_tlv_tag_t tlv_tag; /* T from TLV */ + asn_dec_rval_t rval; /* Return code from subparsers */ + + ssize_t consumed_myself = 0; /* Consumed bytes from ptr */ + + ASN_DEBUG("Decoding %s as SET OF", td->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 = (asn_struct_ctx_t *)((char *)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(opt_codec_ctx, td, ctx, ptr, size, + tag_mode, 1, &ctx->left, 0); + if(rval.code != RC_OK) { + ASN_DEBUG("%s tagging check failed: %d", + td->name, rval.code); + return rval; + } + + 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", td->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 && ((const uint8_t *)ptr)[0] == 0) { + if(LEFT < 2) { + if(SIZE_VIOLATION) + RETURN(RC_FAIL); + else + RETURN(RC_WMORE); + } else if(((const 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(elm->tag != (ber_tlv_tag_t)-1) { + if(BER_TAGS_EQUAL(tlv_tag, elm->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), td->name); + ASN_DEBUG("%s SET OF has tag %s", + td->name, ber_tlv_tag_string(elm->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 = elm->type->ber_decoder(opt_codec_ctx, + elm->type, &ctx->ptr, ptr, LEFT, 0); + ASN_DEBUG("In %s SET OF %s code %d consumed %d", + td->name, elm->type->name, + rval.code, (int)rval.consumed); + switch(rval.code) { + case RC_OK: + { + asn_anonymous_set_ *list = _A_SET_FROM_VOID(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 */ + ASN_STRUCT_FREE(*elm->type, ctx->ptr); + ctx->ptr = 0; + 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 && ((const char *)ptr)[0] != 0) { + /* Unexpected tag */ + RETURN(RC_FAIL); + } else { + RETURN(RC_WMORE); + } + } + if(((const char *)ptr)[0] == 0 + && ((const 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 = (struct _el_buffer *)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 = (const struct _el_buffer *)ap; + const struct _el_buffer *b = (const struct _el_buffer *)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. + */ +asn_enc_rval_t +SET_OF_encode_der(asn_TYPE_descriptor_t *td, void *ptr, + int tag_mode, ber_tlv_tag_t tag, + asn_app_consume_bytes_f *cb, void *app_key) { + asn_TYPE_member_t *elm = td->elements; + asn_TYPE_descriptor_t *elm_type = elm->type; + der_type_encoder_f *der_encoder = elm_type->der_encoder; + asn_anonymous_set_ *list = _A_SET_FROM_VOID(ptr); + size_t computed_size = 0; + ssize_t encoding_size = 0; + struct _el_buffer *encoded_els; + ssize_t eels_count = 0; + size_t max_encoded_len = 1; + asn_enc_rval_t erval; + int ret; + int edx; + + ASN_DEBUG("Estimating size for SET OF %s", td->name); + + /* + * Gather the length of the underlying members sequence. + */ + for(edx = 0; edx < list->count; edx++) { + void *memb_ptr = list->array[edx]; + if(!memb_ptr) continue; + 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 < (size_t)erval.encoded) + max_encoded_len = erval.encoded; + } + + /* + * Encode the TLV for the sequence itself. + */ + encoding_size = der_write_tags(td, computed_size, tag_mode, 1, tag, + cb, app_key); + if(encoding_size == -1) { + erval.encoded = -1; + erval.failed_type = td; + erval.structure_ptr = ptr; + return erval; + } + computed_size += encoding_size; + + if(!cb || list->count == 0) { + erval.encoded = computed_size; + ASN__ENCODED_OK(erval); + } + + /* + * DER mandates dynamic sorting of the SET OF elements + * according to their encodings. Build an array of the + * encoded elements. + */ + encoded_els = (struct _el_buffer *)MALLOC( + list->count * sizeof(encoded_els[0])); + if(encoded_els == NULL) { + erval.encoded = -1; + erval.failed_type = td; + erval.structure_ptr = ptr; + return erval; + } + + ASN_DEBUG("Encoding members of %s SET OF", td->name); + + /* + * Encode all members. + */ + for(edx = 0; edx < list->count; edx++) { + void *memb_ptr = list->array[edx]; + struct _el_buffer *encoded_el = &encoded_els[eels_count]; + + if(!memb_ptr) continue; + + /* + * Prepare space for encoding. + */ + encoded_el->buf = (uint8_t *)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 = td; + 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; + eels_count++; + } + + /* + * Sort the encoded elements according to their encoding. + */ + qsort(encoded_els, eels_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 < eels_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) < 0) + ret = -1; + FREEMEM(encoded_el->buf); + } + FREEMEM(encoded_els); + + if(ret || computed_size != (size_t)encoding_size) { + /* + * Standard callback failed, or + * encoded size is not equal to the computed size. + */ + erval.encoded = -1; + erval.failed_type = td; + erval.structure_ptr = ptr; + } else { + erval.encoded = computed_size; + } + + ASN__ENCODED_OK(erval); +} + +#undef XER_ADVANCE +#define XER_ADVANCE(num_bytes) do { \ + size_t num = num_bytes; \ + buf_ptr = ((const char *)buf_ptr) + num;\ + size -= num; \ + consumed_myself += num; \ + } while(0) + +/* + * Decode the XER (XML) data. + */ +asn_dec_rval_t +SET_OF_decode_xer(asn_codec_ctx_t *opt_codec_ctx, asn_TYPE_descriptor_t *td, + void **struct_ptr, const char *opt_mname, + const void *buf_ptr, size_t size) { + /* + * Bring closer parts of structure description. + */ + asn_SET_OF_specifics_t *specs = (asn_SET_OF_specifics_t *)td->specifics; + asn_TYPE_member_t *element = td->elements; + const char *elm_tag; + const char *xml_tag = opt_mname ? opt_mname : td->xml_tag; + + /* + * ... and parts of the structure being constructed. + */ + void *st = *struct_ptr; /* Target structure. */ + asn_struct_ctx_t *ctx; /* Decoder context */ + + asn_dec_rval_t rval; /* Return value from a decoder */ + ssize_t consumed_myself = 0; /* Consumed bytes from ptr */ + + /* + * 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); + } + + /* Which tag is expected for the downstream */ + if(specs->as_XMLValueList) { + elm_tag = (specs->as_XMLValueList == 1) ? 0 : ""; + } else { + elm_tag = (*element->name) + ? element->name : element->type->xml_tag; + } + + /* + * Restore parsing context. + */ + ctx = (asn_struct_ctx_t *)((char *)st + specs->ctx_offset); + + /* + * Phases of XER/XML processing: + * Phase 0: Check that the opening tag matches our expectations. + * Phase 1: Processing body and reacting on closing tag. + * Phase 2: Processing inner type. + */ + for(; ctx->phase <= 2;) { + pxer_chunk_type_e ch_type; /* XER chunk type */ + ssize_t ch_size; /* Chunk size */ + xer_check_tag_e tcv; /* Tag check value */ + + /* + * Go inside the inner member of a set. + */ + if(ctx->phase == 2) { + asn_dec_rval_t tmprval; + + /* Invoke the inner type decoder, m.b. multiple times */ + ASN_DEBUG("XER/SET OF element [%s]", elm_tag); + tmprval = element->type->xer_decoder(opt_codec_ctx, + element->type, &ctx->ptr, elm_tag, + buf_ptr, size); + if(tmprval.code == RC_OK) { + asn_anonymous_set_ *list = _A_SET_FROM_VOID(st); + if(ASN_SET_ADD(list, ctx->ptr) != 0) + RETURN(RC_FAIL); + ctx->ptr = 0; + XER_ADVANCE(tmprval.consumed); + } else { + XER_ADVANCE(tmprval.consumed); + RETURN(tmprval.code); + } + ctx->phase = 1; /* Back to body processing */ + ASN_DEBUG("XER/SET OF phase => %d", ctx->phase); + /* Fall through */ + } + + /* + * Get the next part of the XML stream. + */ + ch_size = xer_next_token(&ctx->context, + buf_ptr, size, &ch_type); + if(ch_size == -1) { + RETURN(RC_FAIL); + } else { + switch(ch_type) { + case PXER_WMORE: + RETURN(RC_WMORE); + case PXER_COMMENT: /* Got XML comment */ + case PXER_TEXT: /* Ignore free-standing text */ + XER_ADVANCE(ch_size); /* Skip silently */ + continue; + case PXER_TAG: + break; /* Check the rest down there */ + } + } + + tcv = xer_check_tag(buf_ptr, ch_size, xml_tag); + ASN_DEBUG("XER/SET OF: tcv = %d, ph=%d t=%s", + tcv, ctx->phase, xml_tag); + switch(tcv) { + case XCT_CLOSING: + if(ctx->phase == 0) break; + ctx->phase = 0; + /* Fall through */ + case XCT_BOTH: + if(ctx->phase == 0) { + /* No more things to decode */ + XER_ADVANCE(ch_size); + ctx->phase = 3; /* Phase out */ + RETURN(RC_OK); + } + /* Fall through */ + case XCT_OPENING: + if(ctx->phase == 0) { + XER_ADVANCE(ch_size); + ctx->phase = 1; /* Processing body phase */ + continue; + } + /* Fall through */ + case XCT_UNKNOWN_OP: + case XCT_UNKNOWN_BO: + + ASN_DEBUG("XER/SET OF: tcv=%d, ph=%d", tcv, ctx->phase); + if(ctx->phase == 1) { + /* + * Process a single possible member. + */ + ctx->phase = 2; + continue; + } + /* Fall through */ + default: + break; + } + + ASN_DEBUG("Unexpected XML tag in SET OF"); + break; + } + + ctx->phase = 3; /* "Phase out" on hard failure */ + RETURN(RC_FAIL); +} + + + +typedef struct xer_tmp_enc_s { + void *buffer; + size_t offset; + size_t size; +} xer_tmp_enc_t; +static int +SET_OF_encode_xer_callback(const void *buffer, size_t size, void *key) { + xer_tmp_enc_t *t = (xer_tmp_enc_t *)key; + if(t->offset + size >= t->size) { + size_t newsize = (t->size << 2) + size; + void *p = REALLOC(t->buffer, newsize); + if(!p) return -1; + t->buffer = p; + t->size = newsize; + } + memcpy((char *)t->buffer + t->offset, buffer, size); + t->offset += size; + return 0; +} +static int +SET_OF_xer_order(const void *aptr, const void *bptr) { + const xer_tmp_enc_t *a = (const xer_tmp_enc_t *)aptr; + const xer_tmp_enc_t *b = (const xer_tmp_enc_t *)bptr; + size_t minlen = a->offset; + int ret; + if(b->offset < minlen) minlen = b->offset; + /* Well-formed UTF-8 has this nice lexicographical property... */ + ret = memcmp(a->buffer, b->buffer, minlen); + if(ret != 0) return ret; + if(a->offset == b->offset) + return 0; + if(a->offset == minlen) + return -1; + return 1; +} + + +asn_enc_rval_t +SET_OF_encode_xer(asn_TYPE_descriptor_t *td, void *sptr, + int ilevel, enum xer_encoder_flags_e flags, + asn_app_consume_bytes_f *cb, void *app_key) { + asn_enc_rval_t er; + asn_SET_OF_specifics_t *specs = (asn_SET_OF_specifics_t *)td->specifics; + asn_TYPE_member_t *elm = td->elements; + asn_anonymous_set_ *list = _A_SET_FROM_VOID(sptr); + const char *mname = specs->as_XMLValueList + ? 0 : ((*elm->name) ? elm->name : elm->type->xml_tag); + size_t mlen = mname ? strlen(mname) : 0; + int xcan = (flags & XER_F_CANONICAL); + xer_tmp_enc_t *encs = 0; + size_t encs_count = 0; + void *original_app_key = app_key; + asn_app_consume_bytes_f *original_cb = cb; + int i; + + if(!sptr) ASN__ENCODE_FAILED; + + if(xcan) { + encs = (xer_tmp_enc_t *)MALLOC(list->count * sizeof(encs[0])); + if(!encs) ASN__ENCODE_FAILED; + cb = SET_OF_encode_xer_callback; + } + + er.encoded = 0; + + for(i = 0; i < list->count; i++) { + asn_enc_rval_t tmper; + + void *memb_ptr = list->array[i]; + if(!memb_ptr) continue; + + if(encs) { + memset(&encs[encs_count], 0, sizeof(encs[0])); + app_key = &encs[encs_count]; + encs_count++; + } + + if(mname) { + if(!xcan) ASN__TEXT_INDENT(1, ilevel); + ASN__CALLBACK3("<", 1, mname, mlen, ">", 1); + } + + if(!xcan && specs->as_XMLValueList == 1) + ASN__TEXT_INDENT(1, ilevel + 1); + tmper = elm->type->xer_encoder(elm->type, memb_ptr, + ilevel + (specs->as_XMLValueList != 2), + flags, cb, app_key); + if(tmper.encoded == -1) { + td = tmper.failed_type; + sptr = tmper.structure_ptr; + goto cb_failed; + } + if(tmper.encoded == 0 && specs->as_XMLValueList) { + const char *name = elm->type->xml_tag; + size_t len = strlen(name); + ASN__CALLBACK3("<", 1, name, len, "/>", 2); + } + + if(mname) { + ASN__CALLBACK3("", 1); + er.encoded += 5; + } + + er.encoded += (2 * mlen) + tmper.encoded; + } + + if(!xcan) ASN__TEXT_INDENT(1, ilevel - 1); + + if(encs) { + xer_tmp_enc_t *enc = encs; + xer_tmp_enc_t *end = encs + encs_count; + ssize_t control_size = 0; + + cb = original_cb; + app_key = original_app_key; + qsort(encs, encs_count, sizeof(encs[0]), SET_OF_xer_order); + + for(; enc < end; enc++) { + ASN__CALLBACK(enc->buffer, enc->offset); + FREEMEM(enc->buffer); + enc->buffer = 0; + control_size += enc->offset; + } + assert(control_size == er.encoded); + } + + goto cleanup; +cb_failed: + er.encoded = -1; + er.failed_type = td; + er.structure_ptr = sptr; +cleanup: + if(encs) { + while(encs_count-- > 0) { + if(encs[encs_count].buffer) + FREEMEM(encs[encs_count].buffer); + } + FREEMEM(encs); + } + ASN__ENCODED_OK(er); +} + +int +SET_OF_print(asn_TYPE_descriptor_t *td, const void *sptr, int ilevel, + asn_app_consume_bytes_f *cb, void *app_key) { + asn_TYPE_member_t *elm = td->elements; + const asn_anonymous_set_ *list = _A_CSET_FROM_VOID(sptr); + int ret; + int i; + + if(!sptr) return (cb("", 8, app_key) < 0) ? -1 : 0; + + /* Dump preamble */ + if(cb(td->name, strlen(td->name), app_key) < 0 + || cb(" ::= {", 6, app_key) < 0) + return -1; + + for(i = 0; i < list->count; i++) { + const void *memb_ptr = list->array[i]; + if(!memb_ptr) continue; + + _i_INDENT(1); + + ret = elm->type->print_struct(elm->type, memb_ptr, + ilevel + 1, cb, app_key); + if(ret) return ret; + } + + ilevel--; + _i_INDENT(1); + + return (cb("}", 1, app_key) < 0) ? -1 : 0; +} + +void +SET_OF_free(asn_TYPE_descriptor_t *td, void *ptr, int contents_only) { + if(td && ptr) { + asn_SET_OF_specifics_t *specs; + asn_TYPE_member_t *elm = td->elements; + asn_anonymous_set_ *list = _A_SET_FROM_VOID(ptr); + asn_struct_ctx_t *ctx; /* Decoder context */ + 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) + ASN_STRUCT_FREE(*elm->type, memb_ptr); + } + list->count = 0; /* No meaningful elements left */ + + asn_set_empty(list); /* Remove (list->array) */ + + specs = (asn_SET_OF_specifics_t *)td->specifics; + ctx = (asn_struct_ctx_t *)((char *)ptr + specs->ctx_offset); + if(ctx->ptr) { + ASN_STRUCT_FREE(*elm->type, ctx->ptr); + ctx->ptr = 0; + } + + if(!contents_only) { + FREEMEM(ptr); + } + } +} + +int +SET_OF_constraint(asn_TYPE_descriptor_t *td, const void *sptr, + asn_app_constraint_failed_f *ctfailcb, void *app_key) { + asn_TYPE_member_t *elm = td->elements; + asn_constr_check_f *constr; + const asn_anonymous_set_ *list = _A_CSET_FROM_VOID(sptr); + int i; + + if(!sptr) { + ASN__CTFAIL(app_key, td, sptr, + "%s: value not given (%s:%d)", + td->name, __FILE__, __LINE__); + return -1; + } + + constr = elm->memb_constraints; + if(!constr) constr = elm->type->check_constraints; + + /* + * Iterate over the members of an array. + * Validate each in turn, until one fails. + */ + for(i = 0; i < list->count; i++) { + const void *memb_ptr = list->array[i]; + int ret; + + if(!memb_ptr) continue; + + ret = constr(elm->type, memb_ptr, ctfailcb, app_key); + if(ret) return ret; + } + + /* + * Cannot inherit it eralier: + * need to make sure we get the updated version. + */ + if(!elm->memb_constraints) + elm->memb_constraints = elm->type->check_constraints; + + return 0; +} + +asn_dec_rval_t +SET_OF_decode_uper(asn_codec_ctx_t *opt_codec_ctx, asn_TYPE_descriptor_t *td, + asn_per_constraints_t *constraints, void **sptr, asn_per_data_t *pd) { + asn_dec_rval_t rv; + asn_SET_OF_specifics_t *specs = (asn_SET_OF_specifics_t *)td->specifics; + asn_TYPE_member_t *elm = td->elements; /* Single one */ + void *st = *sptr; + asn_anonymous_set_ *list; + asn_per_constraint_t *ct; + int repeat = 0; + ssize_t nelems; + + if(ASN__STACK_OVERFLOW_CHECK(opt_codec_ctx)) + ASN__DECODE_FAILED; + + /* + * Create the target structure if it is not present already. + */ + if(!st) { + st = *sptr = CALLOC(1, specs->struct_size); + if(!st) ASN__DECODE_FAILED; + } + list = _A_SET_FROM_VOID(st); + + /* Figure out which constraints to use */ + if(constraints) ct = &constraints->size; + else if(td->per_constraints) ct = &td->per_constraints->size; + else ct = 0; + + if(ct && ct->flags & APC_EXTENSIBLE) { + int value = per_get_few_bits(pd, 1); + if(value < 0) ASN__DECODE_STARVED; + if(value) ct = 0; /* Not restricted! */ + } + + if(ct && ct->effective_bits >= 0) { + /* X.691, #19.5: No length determinant */ + nelems = per_get_few_bits(pd, ct->effective_bits); + ASN_DEBUG("Preparing to fetch %ld+%ld elements from %s", + (long)nelems, ct->lower_bound, td->name); + if(nelems < 0) ASN__DECODE_STARVED; + nelems += ct->lower_bound; + } else { + nelems = -1; + } + + do { + int i; + if(nelems < 0) { + nelems = uper_get_length(pd, + ct ? ct->effective_bits : -1, &repeat); + ASN_DEBUG("Got to decode %d elements (eff %d)", + (int)nelems, (int)(ct ? ct->effective_bits : -1)); + if(nelems < 0) ASN__DECODE_STARVED; + } + + for(i = 0; i < nelems; i++) { + void *ptr = 0; + ASN_DEBUG("SET OF %s decoding", elm->type->name); + rv = elm->type->uper_decoder(opt_codec_ctx, elm->type, + elm->per_constraints, &ptr, pd); + ASN_DEBUG("%s SET OF %s decoded %d, %p", + td->name, elm->type->name, rv.code, ptr); + if(rv.code == RC_OK) { + if(ASN_SET_ADD(list, ptr) == 0) + continue; + ASN_DEBUG("Failed to add element into %s", + td->name); + /* Fall through */ + rv.code = RC_FAIL; + } else { + ASN_DEBUG("Failed decoding %s of %s (SET OF)", + elm->type->name, td->name); + } + if(ptr) ASN_STRUCT_FREE(*elm->type, ptr); + return rv; + } + + nelems = -1; /* Allow uper_get_length() */ + } while(repeat); + + ASN_DEBUG("Decoded %s as SET OF", td->name); + + rv.code = RC_OK; + rv.consumed = 0; + return rv; +} + diff --git a/asn1_test_data/constr_SET_OF.h b/asn1_test_data/constr_SET_OF.h new file mode 100644 index 0000000..75e18cf --- /dev/null +++ b/asn1_test_data/constr_SET_OF.h @@ -0,0 +1,42 @@ +/*- + * Copyright (c) 2003 Lev Walkin . All rights reserved. + * Redistribution and modifications are permitted subject to BSD license. + */ +#ifndef _CONSTR_SET_OF_H_ +#define _CONSTR_SET_OF_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef const struct asn_SET_OF_specifics_s { + /* + * Target structure description. + */ + int struct_size; /* Size of the target structure. */ + int ctx_offset; /* Offset of the asn_struct_ctx_t member */ + + /* XER-specific stuff */ + int as_XMLValueList; /* The member type must be encoded like this */ +} asn_SET_OF_specifics_t; + +/* + * A set specialized functions dealing with the SET OF type. + */ +asn_struct_free_f SET_OF_free; +asn_struct_print_f SET_OF_print; +asn_constr_check_f SET_OF_constraint; +ber_type_decoder_f SET_OF_decode_ber; +der_type_encoder_f SET_OF_encode_der; +xer_type_decoder_f SET_OF_decode_xer; +xer_type_encoder_f SET_OF_encode_xer; +per_type_decoder_f SET_OF_decode_uper; +per_type_encoder_f SET_OF_encode_uper; + +#ifdef __cplusplus +} +#endif + +#endif /* _CONSTR_SET_OF_H_ */ diff --git a/asn1_test_data/constr_TYPE.c b/asn1_test_data/constr_TYPE.c new file mode 100644 index 0000000..322f68c --- /dev/null +++ b/asn1_test_data/constr_TYPE.c @@ -0,0 +1,77 @@ +/*- + * Copyright (c) 2003, 2004 Lev Walkin . All rights reserved. + * Redistribution and modifications are permitted subject to BSD license. + */ +#include +#include +#include + +/* + * Version of the ASN.1 infrastructure shipped with compiler. + */ +int get_asn1c_environment_version() { return ASN1C_ENVIRONMENT_VERSION; } + +static asn_app_consume_bytes_f _print2fp; + +/* + * Return the outmost tag of the type. + */ +ber_tlv_tag_t +asn_TYPE_outmost_tag(const asn_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, asn_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, 1, _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 = (FILE *)app_key; + + if(fwrite(buffer, 1, size, stream) != size) + return -1; + + return 0; +} + + +/* + * Some compilers do not support variable args macros. + * This function is a replacement of ASN_DEBUG() macro. + */ +void ASN_DEBUG_f(const char *fmt, ...); +void ASN_DEBUG_f(const char *fmt, ...) { + va_list ap; + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + fprintf(stderr, "\n"); + va_end(ap); +} diff --git a/asn1_test_data/constr_TYPE.h b/asn1_test_data/constr_TYPE.h new file mode 100644 index 0000000..a9cd86d --- /dev/null +++ b/asn1_test_data/constr_TYPE.h @@ -0,0 +1,180 @@ +/*- + * Copyright (c) 2003, 2004, 2005, 2006 Lev Walkin . + * All rights reserved. + * Redistribution and modifications are permitted subject to BSD license. + */ +/* + * This file contains the declaration structure called "ASN.1 Type Definition", + * which holds all information necessary for encoding and decoding routines. + * This structure even contains pointer to these encoding and decoding routines + * for each defined ASN.1 type. + */ +#ifndef _CONSTR_TYPE_H_ +#define _CONSTR_TYPE_H_ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct asn_TYPE_descriptor_s; /* Forward declaration */ +struct asn_TYPE_member_s; /* Forward declaration */ + +/* + * This type provides the context information for various ASN.1 routines, + * primarily ones doing decoding. A member _asn_ctx of this type must be + * included into certain target language's structures, such as compound types. + */ +typedef struct asn_struct_ctx_s { + short phase; /* Decoding phase */ + short step; /* Elementary step of a phase */ + int context; /* Other context information */ + void *ptr; /* Decoder-specific stuff (stack elements) */ + ber_tlv_len_t left; /* Number of bytes left, -1 for indefinite */ +} asn_struct_ctx_t; + +#include /* Basic Encoding Rules decoder */ +#include /* Distinguished Encoding Rules encoder */ +#include /* Decoder of XER (XML, text) */ +#include /* Encoder into XER (XML, text) */ +#include /* Packet Encoding Rules decoder */ +#include /* Packet Encoding Rules encoder */ +#include /* Subtype constraints support */ + +/* + * 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 asn_TYPE_descriptor_s *type_descriptor, + void *struct_ptr, int free_contents_only); +#define ASN_STRUCT_FREE(asn_DEF, ptr) (asn_DEF).free_struct(&(asn_DEF),ptr,0) +#define ASN_STRUCT_FREE_CONTENTS_ONLY(asn_DEF, ptr) \ + (asn_DEF).free_struct(&(asn_DEF),ptr,1) + +/* + * Print the structure according to its specification. + */ +typedef int (asn_struct_print_f)( + struct asn_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)( + const struct asn_TYPE_descriptor_s *type_descriptor, + const void *struct_ptr, int tag_mode, ber_tlv_tag_t tag); +/* The instance of the above function type; used internally. */ +asn_outmost_tag_f asn_TYPE_outmost_tag; + + +/* + * The definitive description of the destination language's structure. + */ +typedef struct asn_TYPE_descriptor_s { + const char *name; /* A name of the ASN.1 type. "" in some cases. */ + const char *xml_tag; /* Name used in XML tag */ + + /* + * Generalized functions for dealing with the specific type. + * May be directly invoked by applications. + */ + asn_struct_free_f *free_struct; /* Free the structure */ + asn_struct_print_f *print_struct; /* Human readable output */ + asn_constr_check_f *check_constraints; /* Constraints validator */ + ber_type_decoder_f *ber_decoder; /* Generic BER decoder */ + der_type_encoder_f *der_encoder; /* Canonical DER encoder */ + xer_type_decoder_f *xer_decoder; /* Generic XER decoder */ + xer_type_encoder_f *xer_encoder; /* [Canonical] XER encoder */ + per_type_decoder_f *uper_decoder; /* Unaligned PER decoder */ + per_type_encoder_f *uper_encoder; /* Unaligned PER encoder */ + + /*********************************************************************** + * Internally useful members. Not to be used by applications directly. * + **********************************************************************/ + + /* + * Tags that are expected to occur. + */ + asn_outmost_tag_f *outmost_tag; /* */ + const ber_tlv_tag_t *tags; /* Effective tags sequence for this type */ + int tags_count; /* Number of tags which are expected */ + const ber_tlv_tag_t *all_tags; /* Every tag for BER/containment */ + int all_tags_count; /* Number of tags */ + + asn_per_constraints_t *per_constraints; /* PER compiled constraints */ + + /* + * An ASN.1 production type members (members of SEQUENCE, SET, CHOICE). + */ + struct asn_TYPE_member_s *elements; + int elements_count; + + /* + * Additional information describing the type, used by appropriate + * functions above. + */ + const void *specifics; +} asn_TYPE_descriptor_t; + +/* + * This type describes an element of the constructed type, + * i.e. SEQUENCE, SET, CHOICE, etc. + */ + enum asn_TYPE_flags_e { + ATF_NOFLAGS, + ATF_POINTER = 0x01, /* Represented by the pointer */ + ATF_OPEN_TYPE = 0x02 /* ANY type, without meaningful tag */ + }; +typedef struct asn_TYPE_member_s { + enum asn_TYPE_flags_e flags; /* Element's presentation flags */ + int optional; /* Following optional members, including current */ + int memb_offset; /* Offset of the element */ + ber_tlv_tag_t tag; /* Outmost (most immediate) tag */ + int tag_mode; /* IMPLICIT/no/EXPLICIT tag at current level */ + asn_TYPE_descriptor_t *type; /* Member type descriptor */ + asn_constr_check_f *memb_constraints; /* Constraints validator */ + asn_per_constraints_t *per_constraints; /* PER compiled constraints */ + int (*default_value)(int setval, void **sptr); /* DEFAULT */ + const char *name; /* ASN.1 identifier of the element */ +} asn_TYPE_member_t; + +/* + * BER tag to element number mapping. + */ +typedef struct asn_TYPE_tag2member_s { + ber_tlv_tag_t el_tag; /* Outmost tag of the member */ + int el_no; /* Index of the associated member, base 0 */ + int toff_first; /* First occurence of the el_tag, relative */ + int toff_last; /* Last occurence of the el_tag, relatvie */ +} asn_TYPE_tag2member_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. + * (See also xer_fprint() in xer_encoder.h) + */ +int asn_fprint(FILE *stream, /* Destination stream descriptor */ + asn_TYPE_descriptor_t *td, /* ASN.1 type descriptor */ + const void *struct_ptr); /* Structure to be printed */ + +#ifdef __cplusplus +} +#endif + +#endif /* _CONSTR_TYPE_H_ */ diff --git a/asn1_test_data/constraints.c b/asn1_test_data/constraints.c new file mode 100644 index 0000000..1bdda73 --- /dev/null +++ b/asn1_test_data/constraints.c @@ -0,0 +1,93 @@ +#include "asn_internal.h" +#include "constraints.h" + +int +asn_generic_no_constraint(asn_TYPE_descriptor_t *type_descriptor, + const void *struct_ptr, asn_app_constraint_failed_f *cb, void *key) { + + (void)type_descriptor; /* Unused argument */ + (void)struct_ptr; /* Unused argument */ + (void)cb; /* Unused argument */ + (void)key; /* Unused argument */ + + /* Nothing to check */ + return 0; +} + +int +asn_generic_unknown_constraint(asn_TYPE_descriptor_t *type_descriptor, + const void *struct_ptr, asn_app_constraint_failed_f *cb, void *key) { + + (void)type_descriptor; /* Unused argument */ + (void)struct_ptr; /* Unused argument */ + (void)cb; /* Unused argument */ + (void)key; /* Unused argument */ + + /* Unknown how to check */ + return 0; +} + +struct errbufDesc { + asn_TYPE_descriptor_t *failed_type; + const void *failed_struct_ptr; + char *errbuf; + size_t errlen; +}; + +static void +_asn_i_ctfailcb(void *key, asn_TYPE_descriptor_t *td, const void *sptr, const char *fmt, ...) { + struct errbufDesc *arg = key; + va_list ap; + ssize_t vlen; + ssize_t maxlen; + + arg->failed_type = td; + arg->failed_struct_ptr = sptr; + + maxlen = arg->errlen; + if(maxlen <= 0) + return; + + va_start(ap, fmt); + vlen = vsnprintf(arg->errbuf, maxlen, fmt, ap); + va_end(ap); + if(vlen >= maxlen) { + arg->errbuf[maxlen-1] = '\0'; /* Ensuring libc correctness */ + arg->errlen = maxlen - 1; /* Not counting termination */ + return; + } else if(vlen >= 0) { + arg->errbuf[vlen] = '\0'; /* Ensuring libc correctness */ + arg->errlen = vlen; /* Not counting termination */ + } else { + /* + * The libc on this system is broken. + */ + vlen = sizeof("") - 1; + maxlen--; + arg->errlen = vlen < maxlen ? vlen : maxlen; + memcpy(arg->errbuf, "", arg->errlen); + arg->errbuf[arg->errlen] = 0; + } + + return; +} + +int +asn_check_constraints(asn_TYPE_descriptor_t *type_descriptor, + const void *struct_ptr, char *errbuf, size_t *errlen) { + struct errbufDesc arg; + int ret; + + arg.failed_type = 0; + arg.failed_struct_ptr = 0; + arg.errbuf = errbuf; + arg.errlen = errlen ? *errlen : 0; + + ret = type_descriptor->check_constraints(type_descriptor, + struct_ptr, _asn_i_ctfailcb, &arg); + if(ret == -1 && errlen) + *errlen = arg.errlen; + + return ret; +} + diff --git a/asn1_test_data/constraints.h b/asn1_test_data/constraints.h new file mode 100644 index 0000000..48d49e2 --- /dev/null +++ b/asn1_test_data/constraints.h @@ -0,0 +1,63 @@ +/*- + * Copyright (c) 2004, 2006 Lev Walkin . All rights reserved. + * Redistribution and modifications are permitted subject to BSD license. + */ +#ifndef ASN1_CONSTRAINTS_VALIDATOR_H +#define ASN1_CONSTRAINTS_VALIDATOR_H + +#include /* Platform-dependent types */ + +#ifdef __cplusplus +extern "C" { +#endif + +struct asn_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). + * + * RETURN VALUES: + * This function returns 0 in case all ASN.1 constraints are met + * and -1 if one or more constraints were failed. + */ +int +asn_check_constraints(struct asn_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 asn_TYPE_descriptor_s *type_descriptor, + const void *struct_ptr, + asn_app_constraint_failed_f *optional_callback, /* Log the error */ + void *optional_app_key /* Opaque key passed to a callback */ + ); + +/******************************* + * 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__CTFAIL if(ctfailcb) ctfailcb + +#ifdef __cplusplus +} +#endif + +#endif /* ASN1_CONSTRAINTS_VALIDATOR_H */ diff --git a/asn1_test_data/converter-sample.c b/asn1_test_data/converter-sample.c new file mode 100644 index 0000000..de42326 --- /dev/null +++ b/asn1_test_data/converter-sample.c @@ -0,0 +1,834 @@ +/* + * Generic converter template for a selected ASN.1 type. + * Copyright (c) 2005, 2006, 2007 Lev Walkin . + * All rights reserved. + * + * To compile with your own ASN.1 type, please redefine the PDU as shown: + * + * cc -DPDU=MyCustomType -o myDecoder.o -c converter-sample.c + */ +#ifdef HAVE_CONFIG_H +#include +#endif +#include +#include +#include /* for atoi(3) */ +#include /* for getopt(3) */ +#include /* for strerror(3) */ +#include /* for EX_* exit codes */ +#include /* for errno */ + +#include +#include /* for ASN__DEFAULT_STACK_MAX */ + +/* Convert "Type" defined by -DPDU into "asn_DEF_Type" */ +#define ASN_DEF_PDU(t) asn_DEF_ ## t +#define DEF_PDU_Type(t) ASN_DEF_PDU(t) +#define PDU_Type DEF_PDU_Type(PDU) + +extern asn_TYPE_descriptor_t PDU_Type; /* ASN.1 type to be decoded */ +#ifdef ASN_PDU_COLLECTION /* Generated by asn1c: -pdu=... */ +extern asn_TYPE_descriptor_t *asn_pdu_collection[]; +#endif + +/* + * Open file and parse its contens. + */ +static void *data_decode_from_file(asn_TYPE_descriptor_t *asnTypeOfPDU, + FILE *file, const char *name, ssize_t suggested_bufsize, int first_pdu); +static int write_out(const void *buffer, size_t size, void *key); +static FILE *argument_to_file(char *av[], int idx); +static char *argument_to_name(char *av[], int idx); + + int opt_debug; /* -d (or -dd) */ +static int opt_check; /* -c (constraints checking) */ +static int opt_stack; /* -s (maximum stack size) */ +static int opt_nopad; /* -per-nopad (PER input is not padded) */ +static int opt_onepdu; /* -1 (decode single PDU) */ + +/* Input data format selector */ +static enum input_format { + INP_BER, /* -iber: BER input */ + INP_XER, /* -ixer: XER input */ + INP_PER /* -iper: Unaligned PER input */ +} iform; /* -i */ + +/* Output data format selector */ +static enum output_format { + OUT_XER, /* -oxer: XER (XML) output */ + OUT_DER, /* -oder: DER (BER) output */ + OUT_PER, /* -oper: Unaligned PER output */ + OUT_TEXT, /* -otext: semi-structured text */ + OUT_NULL /* -onull: No pretty-printing */ +} oform; /* -o */ + +#ifdef JUNKTEST /* Enable -J */ +#define JUNKOPT "J:" +static double opt_jprob; /* Junk bit probability */ +static int junk_failures; +static void junk_bytes_with_probability(uint8_t *, size_t, double prob); +#else +#define JUNKOPT +#endif + +/* Debug output function */ +static inline void +DEBUG(const char *fmt, ...) { + va_list ap; + if(!opt_debug) return; + fprintf(stderr, "AD: "); + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); + fprintf(stderr, "\n"); +} + +int +main(int ac, char *av[]) { + static asn_TYPE_descriptor_t *pduType = &PDU_Type; + ssize_t suggested_bufsize = 8192; /* close or equal to stdio buffer */ + int number_of_iterations = 1; + int num; + int ch; + + /* Figure out if Unaligned PER needs to be default */ + if(pduType->uper_decoder) + iform = INP_PER; + + /* + * Pocess the command-line argments. + */ + while((ch = getopt(ac, av, "i:o:1b:cdn:p:hs:" JUNKOPT)) != -1) + switch(ch) { + case 'i': + if(optarg[0] == 'b') { iform = INP_BER; break; } + if(optarg[0] == 'x') { iform = INP_XER; break; } + if(pduType->uper_decoder + && optarg[0] == 'p') { iform = INP_PER; break; } + fprintf(stderr, "-i: '%s': improper format selector\n", + optarg); + exit(EX_UNAVAILABLE); + case 'o': + if(optarg[0] == 'd') { oform = OUT_DER; break; } + if(pduType->uper_encoder + && optarg[0] == 'p') { oform = OUT_PER; break; } + if(optarg[0] == 'x') { oform = OUT_XER; break; } + if(optarg[0] == 't') { oform = OUT_TEXT; break; } + if(optarg[0] == 'n') { oform = OUT_NULL; break; } + fprintf(stderr, "-o: '%s': improper format selector\n", + optarg); + exit(EX_UNAVAILABLE); + case '1': + opt_onepdu = 1; + break; + case 'b': + suggested_bufsize = atoi(optarg); + if(suggested_bufsize < 1 + || suggested_bufsize > 16 * 1024 * 1024) { + fprintf(stderr, + "-b %s: Improper buffer size (1..16M)\n", + optarg); + exit(EX_UNAVAILABLE); + } + break; + case 'c': + opt_check = 1; + break; + case 'd': + opt_debug++; /* Double -dd means ASN.1 debug */ + break; + case 'n': + number_of_iterations = atoi(optarg); + if(number_of_iterations < 1) { + fprintf(stderr, + "-n %s: Improper iterations count\n", optarg); + exit(EX_UNAVAILABLE); + } + break; + case 'p': + if(strcmp(optarg, "er-nopad") == 0) { + opt_nopad = 1; + break; + } +#ifdef ASN_PDU_COLLECTION + if(strcmp(optarg, "list") == 0) { + asn_TYPE_descriptor_t **pdu = asn_pdu_collection; + fprintf(stderr, "Available PDU types:\n"); + for(; *pdu; pdu++) printf("%s\n", (*pdu)->name); + exit(0); + } else if(optarg[0] >= 'A' && optarg[0] <= 'Z') { + asn_TYPE_descriptor_t **pdu = asn_pdu_collection; + while(*pdu && strcmp((*pdu)->name, optarg)) pdu++; + if(*pdu) { pduType = *pdu; break; } + fprintf(stderr, "-p %s: Unrecognized PDU\n", optarg); + } +#endif /* ASN_PDU_COLLECTION */ + fprintf(stderr, "-p %s: Unrecognized option\n", optarg); + exit(EX_UNAVAILABLE); + case 's': + opt_stack = atoi(optarg); + if(opt_stack < 0) { + fprintf(stderr, + "-s %s: Non-negative value expected\n", + optarg); + exit(EX_UNAVAILABLE); + } + break; +#ifdef JUNKTEST + case 'J': + opt_jprob = strtod(optarg, 0); + if(opt_jprob <= 0.0 || opt_jprob > 1.0) { + fprintf(stderr, + "-J %s: Probability range 0..1 expected \n", + optarg); + exit(EX_UNAVAILABLE); + } + break; +#endif /* JUNKTEST */ + case 'h': + default: +#ifdef ASN_CONVERTER_TITLE +#define _AXS(x) #x +#define _ASX(x) _AXS(x) + fprintf(stderr, "%s\n", _ASX(ASN_CONVERTER_TITLE)); +#endif + fprintf(stderr, "Usage: %s [options] ...\n", av[0]); + fprintf(stderr, "Where options are:\n"); + if(pduType->uper_decoder) + fprintf(stderr, + " -iper Input is in Unaligned PER (Packed Encoding Rules) (DEFAULT)\n"); + fprintf(stderr, + " -iber Input is in BER (Basic Encoding Rules)%s\n", + iform == INP_PER ? "" : " (DEFAULT)"); + fprintf(stderr, + " -ixer Input is in XER (XML Encoding Rules)\n"); + if(pduType->uper_encoder) + fprintf(stderr, + " -oper Output in Unaligned PER (Packed Encoding Rules)\n"); + fprintf(stderr, + " -oder Output in DER (Distinguished Encoding Rules)\n" + " -oxer Output in XER (XML Encoding Rules) (DEFAULT)\n" + " -otext Output in plain semi-structured text (dump)\n" + " -onull Verify (decode) input, but do not output\n"); + if(pduType->uper_decoder) + fprintf(stderr, + " -per-nopad Assume PER PDUs are not padded (-iper)\n"); +#ifdef ASN_PDU_COLLECTION + fprintf(stderr, + " -p Specify PDU type to decode\n" + " -p list List available PDUs\n"); +#endif /* ASN_PDU_COLLECTION */ + fprintf(stderr, + " -1 Decode only the first PDU in file\n" + " -b Set the i/o buffer size (default is %ld)\n" + " -c Check ASN.1 constraints after decoding\n" + " -d Enable debugging (-dd is even better)\n" + " -n Process files times\n" + " -s Set the stack usage limit (default is %d)\n" +#ifdef JUNKTEST + " -J Set random junk test bit garbaging probability\n" +#endif + , (long)suggested_bufsize, ASN__DEFAULT_STACK_MAX); + exit(EX_USAGE); + } + + ac -= optind; + av += optind; + + if(ac < 1) { + fprintf(stderr, "%s: No input files specified. " + "Try '-h' for more information\n", + av[-optind]); + exit(EX_USAGE); + } + + setvbuf(stdout, 0, _IOLBF, 0); + + for(num = 0; num < number_of_iterations; num++) { + int ac_i; + /* + * Process all files in turn. + */ + for(ac_i = 0; ac_i < ac; ac_i++) { + asn_enc_rval_t erv; + void *structure; /* Decoded structure */ + FILE *file = argument_to_file(av, ac_i); + char *name = argument_to_name(av, ac_i); + int first_pdu; + + for(first_pdu = 1; first_pdu || !opt_onepdu; first_pdu = 0) { + /* + * Decode the encoded structure from file. + */ + structure = data_decode_from_file(pduType, + file, name, suggested_bufsize, first_pdu); + if(!structure) { + if(errno) { + /* Error message is already printed */ + exit(EX_DATAERR); + } else { + /* EOF */ + break; + } + } + + /* Check ASN.1 constraints */ + if(opt_check) { + char errbuf[128]; + size_t errlen = sizeof(errbuf); + if(asn_check_constraints(pduType, structure, + errbuf, &errlen)) { + fprintf(stderr, "%s: ASN.1 constraint " + "check failed: %s\n", name, errbuf); + exit(EX_DATAERR); + } + } + + switch(oform) { + case OUT_NULL: +#ifdef JUNKTEST + if(opt_jprob == 0.0) +#endif + fprintf(stderr, "%s: decoded successfully\n", name); + break; + case OUT_TEXT: /* -otext */ + asn_fprint(stdout, pduType, structure); + break; + case OUT_XER: /* -oxer */ + if(xer_fprint(stdout, pduType, structure)) { + fprintf(stderr, + "%s: Cannot convert %s into XML\n", + name, pduType->name); + exit(EX_UNAVAILABLE); + } + break; + case OUT_DER: + erv = der_encode(pduType, structure, write_out, stdout); + if(erv.encoded < 0) { + fprintf(stderr, + "%s: Cannot convert %s into DER\n", + name, pduType->name); + exit(EX_UNAVAILABLE); + } + DEBUG("Encoded in %ld bytes of DER", (long)erv.encoded); + break; + case OUT_PER: + erv = uper_encode(pduType, structure, write_out, stdout); + if(erv.encoded < 0) { + fprintf(stderr, + "%s: Cannot convert %s into Unaligned PER\n", + name, pduType->name); + exit(EX_UNAVAILABLE); + } + DEBUG("Encoded in %ld bits of UPER", (long)erv.encoded); + break; + } + + ASN_STRUCT_FREE(*pduType, structure); + } + + if(file && file != stdin) + fclose(file); + } + } + +#ifdef JUNKTEST + if(opt_jprob > 0.0) { + fprintf(stderr, "Junked %f OK (%d/%d)\n", + opt_jprob, junk_failures, number_of_iterations); + } +#endif /* JUNKTEST */ + + return 0; +} + +static struct dynamic_buffer { + uint8_t *data; /* Pointer to the data bytes */ + size_t offset; /* Offset from the start */ + size_t length; /* Length of meaningful contents */ + size_t unbits; /* Unused bits in the last byte */ + size_t allocated; /* Allocated memory for data */ + int nreallocs; /* Number of data reallocations */ + off_t bytes_shifted; /* Number of bytes ever shifted */ +} DynamicBuffer; + +static void +buffer_dump() { + uint8_t *p = DynamicBuffer.data + DynamicBuffer.offset; + uint8_t *e = p + DynamicBuffer.length - (DynamicBuffer.unbits ? 1 : 0); + if(!opt_debug) return; + DEBUG("Buffer: { d=%p, o=%ld, l=%ld, u=%ld, a=%ld, s=%ld }", + DynamicBuffer.data, + (long)DynamicBuffer.offset, + (long)DynamicBuffer.length, + (long)DynamicBuffer.unbits, + (long)DynamicBuffer.allocated, + (long)DynamicBuffer.bytes_shifted); + for(; p < e; p++) { + fprintf(stderr, " %c%c%c%c%c%c%c%c", + ((*p >> 7) & 1) ? '1' : '0', + ((*p >> 6) & 1) ? '1' : '0', + ((*p >> 5) & 1) ? '1' : '0', + ((*p >> 4) & 1) ? '1' : '0', + ((*p >> 3) & 1) ? '1' : '0', + ((*p >> 2) & 1) ? '1' : '0', + ((*p >> 1) & 1) ? '1' : '0', + ((*p >> 0) & 1) ? '1' : '0'); + } + if(DynamicBuffer.unbits) { + unsigned int shift; + fprintf(stderr, " "); + for(shift = 7; shift >= DynamicBuffer.unbits; shift--) + fprintf(stderr, "%c", ((*p >> shift) & 1) ? '1' : '0'); + fprintf(stderr, " %ld:%ld\n", + (long)DynamicBuffer.length - 1, + (long)8 - DynamicBuffer.unbits); + } else { + fprintf(stderr, " %ld\n", (long)DynamicBuffer.length); + } +} + +/* + * Move the buffer content left N bits, possibly joining it with + * preceeding content. + */ +static void +buffer_shift_left(size_t offset, int bits) { + uint8_t *ptr = DynamicBuffer.data + DynamicBuffer.offset + offset; + uint8_t *end = DynamicBuffer.data + DynamicBuffer.offset + + DynamicBuffer.length - 1; + + if(!bits) return; + + DEBUG("Shifting left %d bits off %ld (o=%ld, u=%ld, l=%ld)", + bits, (long)offset, + (long)DynamicBuffer.offset, + (long)DynamicBuffer.unbits, + (long)DynamicBuffer.length); + + if(offset) { + int right; + right = ptr[0] >> (8 - bits); + + DEBUG("oleft: %c%c%c%c%c%c%c%c", + ((ptr[-1] >> 7) & 1) ? '1' : '0', + ((ptr[-1] >> 6) & 1) ? '1' : '0', + ((ptr[-1] >> 5) & 1) ? '1' : '0', + ((ptr[-1] >> 4) & 1) ? '1' : '0', + ((ptr[-1] >> 3) & 1) ? '1' : '0', + ((ptr[-1] >> 2) & 1) ? '1' : '0', + ((ptr[-1] >> 1) & 1) ? '1' : '0', + ((ptr[-1] >> 0) & 1) ? '1' : '0'); + + DEBUG("oriht: %c%c%c%c%c%c%c%c", + ((ptr[0] >> 7) & 1) ? '1' : '0', + ((ptr[0] >> 6) & 1) ? '1' : '0', + ((ptr[0] >> 5) & 1) ? '1' : '0', + ((ptr[0] >> 4) & 1) ? '1' : '0', + ((ptr[0] >> 3) & 1) ? '1' : '0', + ((ptr[0] >> 2) & 1) ? '1' : '0', + ((ptr[0] >> 1) & 1) ? '1' : '0', + ((ptr[0] >> 0) & 1) ? '1' : '0'); + + DEBUG("mriht: %c%c%c%c%c%c%c%c", + ((right >> 7) & 1) ? '1' : '0', + ((right >> 6) & 1) ? '1' : '0', + ((right >> 5) & 1) ? '1' : '0', + ((right >> 4) & 1) ? '1' : '0', + ((right >> 3) & 1) ? '1' : '0', + ((right >> 2) & 1) ? '1' : '0', + ((right >> 1) & 1) ? '1' : '0', + ((right >> 0) & 1) ? '1' : '0'); + + ptr[-1] = (ptr[-1] & (0xff << bits)) | right; + + DEBUG("after: %c%c%c%c%c%c%c%c", + ((ptr[-1] >> 7) & 1) ? '1' : '0', + ((ptr[-1] >> 6) & 1) ? '1' : '0', + ((ptr[-1] >> 5) & 1) ? '1' : '0', + ((ptr[-1] >> 4) & 1) ? '1' : '0', + ((ptr[-1] >> 3) & 1) ? '1' : '0', + ((ptr[-1] >> 2) & 1) ? '1' : '0', + ((ptr[-1] >> 1) & 1) ? '1' : '0', + ((ptr[-1] >> 0) & 1) ? '1' : '0'); + } + + buffer_dump(); + + for(; ptr < end; ptr++) { + int right = ptr[1] >> (8 - bits); + *ptr = (*ptr << bits) | right; + } + *ptr <<= bits; + + DEBUG("Unbits [%d=>", (int)DynamicBuffer.unbits); + if(DynamicBuffer.unbits == 0) { + DynamicBuffer.unbits += bits; + } else { + DynamicBuffer.unbits += bits; + if(DynamicBuffer.unbits > 7) { + DynamicBuffer.unbits -= 8; + DynamicBuffer.length--; + DynamicBuffer.bytes_shifted++; + } + } + DEBUG("Unbits =>%d]", (int)DynamicBuffer.unbits); + + buffer_dump(); + + DEBUG("Shifted. Now (o=%ld, u=%ld l=%ld)", + (long)DynamicBuffer.offset, + (long)DynamicBuffer.unbits, + (long)DynamicBuffer.length); + + +} + +/* + * Ensure that the buffer contains at least this amount of free space. + */ +static void add_bytes_to_buffer(const void *data2add, size_t bytes) { + + if(bytes == 0) return; + + DEBUG("=> add_bytes(%ld) { o=%ld l=%ld u=%ld, s=%ld }", + (long)bytes, + (long)DynamicBuffer.offset, + (long)DynamicBuffer.length, + (long)DynamicBuffer.unbits, + (long)DynamicBuffer.allocated); + + if(DynamicBuffer.allocated + >= (DynamicBuffer.offset + DynamicBuffer.length + bytes)) { + DEBUG("\tNo buffer reallocation is necessary"); + } else if(bytes <= DynamicBuffer.offset) { + DEBUG("\tContents shifted by %ld", DynamicBuffer.offset); + + /* Shift the buffer contents */ + memmove(DynamicBuffer.data, + DynamicBuffer.data + DynamicBuffer.offset, + DynamicBuffer.length); + DynamicBuffer.bytes_shifted += DynamicBuffer.offset; + DynamicBuffer.offset = 0; + } else { + size_t newsize = (DynamicBuffer.allocated << 2) + bytes; + void *p = MALLOC(newsize); + if(!p) { + perror("malloc()"); + exit(EX_OSERR); + } + memcpy(p, + DynamicBuffer.data + DynamicBuffer.offset, + DynamicBuffer.length); + FREEMEM(DynamicBuffer.data); + DynamicBuffer.data = (uint8_t *)p; + DynamicBuffer.offset = 0; + DynamicBuffer.allocated = newsize; + DynamicBuffer.nreallocs++; + DEBUG("\tBuffer reallocated to %ld (%d time)", + newsize, DynamicBuffer.nreallocs); + } + + memcpy(DynamicBuffer.data + + DynamicBuffer.offset + DynamicBuffer.length, + data2add, bytes); + DynamicBuffer.length += bytes; + if(DynamicBuffer.unbits) { + int bits = DynamicBuffer.unbits; + DynamicBuffer.unbits = 0; + buffer_shift_left(DynamicBuffer.length - bytes, bits); + } + + DEBUG("<= add_bytes(%ld) { o=%ld l=%ld u=%ld, s=%ld }", + (long)bytes, + (long)DynamicBuffer.offset, + (long)DynamicBuffer.length, + (long)DynamicBuffer.unbits, + (long)DynamicBuffer.allocated); +} + +static void * +data_decode_from_file(asn_TYPE_descriptor_t *pduType, FILE *file, const char *name, ssize_t suggested_bufsize, int on_first_pdu) { + static uint8_t *fbuf; + static ssize_t fbuf_size; + static asn_codec_ctx_t s_codec_ctx; + asn_codec_ctx_t *opt_codec_ctx = 0; + void *structure = 0; + asn_dec_rval_t rval; + size_t old_offset; + size_t new_offset; + int tolerate_eof; + size_t rd; + + if(!file) { + fprintf(stderr, "%s: %s\n", name, strerror(errno)); + errno = EINVAL; + return 0; + } + + if(opt_stack) { + s_codec_ctx.max_stack_size = opt_stack; + opt_codec_ctx = &s_codec_ctx; + } + + DEBUG("Processing %s", name); + + /* prepare the file buffer */ + if(fbuf_size != suggested_bufsize) { + fbuf = (uint8_t *)REALLOC(fbuf, suggested_bufsize); + if(!fbuf) { + perror("realloc()"); + exit(EX_OSERR); + } + fbuf_size = suggested_bufsize; + } + + if(on_first_pdu) { + DynamicBuffer.offset = 0; + DynamicBuffer.length = 0; + DynamicBuffer.unbits = 0; + DynamicBuffer.allocated = 0; + DynamicBuffer.bytes_shifted = 0; + DynamicBuffer.nreallocs = 0; + } + + old_offset = DynamicBuffer.bytes_shifted + DynamicBuffer.offset; + + /* Pretend immediate EOF */ + rval.code = RC_WMORE; + rval.consumed = 0; + + for(tolerate_eof = 1; /* Allow EOF first time buffer is non-empty */ + (rd = fread(fbuf, 1, fbuf_size, file)) + || feof(file) == 0 + || (tolerate_eof && DynamicBuffer.length) + ;) { + int ecbits = 0; /* Extra consumed bits in case of PER */ + uint8_t *i_bptr; + size_t i_size; + + /* + * Copy the data over, or use the original buffer. + */ + if(DynamicBuffer.allocated) { + /* Append new data into the existing dynamic buffer */ + add_bytes_to_buffer(fbuf, rd); + i_bptr = DynamicBuffer.data + DynamicBuffer.offset; + i_size = DynamicBuffer.length; + } else { + i_bptr = fbuf; + i_size = rd; + } + + DEBUG("Decoding %ld bytes", (long)i_size); + +#ifdef JUNKTEST + junk_bytes_with_probability(i_bptr, i_size, opt_jprob); +#endif + + switch(iform) { + case INP_BER: + rval = ber_decode(opt_codec_ctx, pduType, + (void **)&structure, i_bptr, i_size); + break; + case INP_XER: + rval = xer_decode(opt_codec_ctx, pduType, + (void **)&structure, i_bptr, i_size); + break; + case INP_PER: + if(opt_nopad) + rval = uper_decode(opt_codec_ctx, pduType, + (void **)&structure, i_bptr, i_size, 0, + DynamicBuffer.unbits); + else + rval = uper_decode_complete(opt_codec_ctx, pduType, + (void **)&structure, i_bptr, i_size); + switch(rval.code) { + case RC_OK: + /* Fall through */ + case RC_FAIL: + if(opt_nopad) { + /* uper_decode() returns bits! */ + /* Extra bits */ + ecbits = rval.consumed % 8; + /* Convert into bytes! */ + rval.consumed /= 8; + } + break; + case RC_WMORE: + /* PER does not support restartability */ + ASN_STRUCT_FREE(*pduType, structure); + structure = 0; + rval.consumed = 0; + /* Continue accumulating data */ + break; + } + break; + } + DEBUG("decode(%ld) consumed %ld+%db (%ld), code %d", + (long)DynamicBuffer.length, + (long)rval.consumed, ecbits, (long)i_size, + rval.code); + + if(DynamicBuffer.allocated == 0) { + /* + * Flush remainder into the intermediate buffer. + */ + if(rval.code != RC_FAIL && rval.consumed < rd) { + add_bytes_to_buffer(fbuf + rval.consumed, + rd - rval.consumed); + buffer_shift_left(0, ecbits); + DynamicBuffer.bytes_shifted = rval.consumed; + rval.consumed = 0; + ecbits = 0; + } + } + + /* + * Adjust position inside the source buffer. + */ + if(DynamicBuffer.allocated) { + DynamicBuffer.offset += rval.consumed; + DynamicBuffer.length -= rval.consumed; + } else { + DynamicBuffer.bytes_shifted += rval.consumed; + } + + switch(rval.code) { + case RC_OK: + if(ecbits) buffer_shift_left(0, ecbits); + DEBUG("RC_OK, finishing up with %ld+%d", + (long)rval.consumed, ecbits); + return structure; + case RC_WMORE: + DEBUG("RC_WMORE, continuing read=%ld, cons=%ld " + " with %ld..%ld-%ld..%ld", + (long)rd, + (long)rval.consumed, + (long)DynamicBuffer.offset, + (long)DynamicBuffer.length, + (long)DynamicBuffer.unbits, + (long)DynamicBuffer.allocated); + if(!rd) tolerate_eof--; + continue; + case RC_FAIL: + break; + } + break; + } + + DEBUG("Clean up partially decoded structure"); + ASN_STRUCT_FREE(*pduType, structure); + + new_offset = DynamicBuffer.bytes_shifted + DynamicBuffer.offset; + + /* + * Print a message and return failure only if not EOF, + * unless this is our first PDU (empty file). + */ + if(on_first_pdu + || DynamicBuffer.length + || new_offset - old_offset > ((iform == INP_XER)?sizeof("\r\n")-1:0) + ) { + +#ifdef JUNKTEST + /* + * Nothing's wrong with being unable to decode junk. + * Simulate EOF. + */ + if(opt_jprob != 0.0) { + junk_failures++; + errno = 0; + return 0; + } +#endif + + DEBUG("ofp %d, no=%ld, oo=%ld, dbl=%ld", + on_first_pdu, (long)new_offset, (long)old_offset, + (long)DynamicBuffer.length); + fprintf(stderr, "%s: " + "Decode failed past byte %ld: %s\n", + name, (long)new_offset, + (rval.code == RC_WMORE) + ? "Unexpected end of input" + : "Input processing error"); +#ifndef ENOMSG +#define ENOMSG EINVAL +#endif +#ifndef EBADMSG +#define EBADMSG EINVAL +#endif + errno = (rval.code == RC_WMORE) ? ENOMSG : EBADMSG; + } else { + /* Got EOF after a few successful PDUs */ + errno = 0; + } + + return 0; +} + +/* Dump the buffer out to the specified FILE */ +static int write_out(const void *buffer, size_t size, void *key) { + FILE *fp = (FILE *)key; + return (fwrite(buffer, 1, size, fp) == size) ? 0 : -1; +} + +static int argument_is_stdin(char *av[], int idx) { + if(strcmp(av[idx], "-")) { + return 0; /* Certainly not */ + } else { + /* This might be , unless `./program -- -` */ + if(strcmp(av[-1], "--")) + return 1; + else + return 0; + } +} + +static FILE *argument_to_file(char *av[], int idx) { + return argument_is_stdin(av, idx) + ? stdin + : fopen(av[idx], "rb"); +} + +static char *argument_to_name(char *av[], int idx) { + return argument_is_stdin(av, idx) + ? "standard input" + : av[idx]; +} + +#ifdef JUNKTEST +/* + * Fill bytes with some garbage with specified probability (more or less). + */ +static void +junk_bytes_with_probability(uint8_t *buf, size_t size, double prob) { + static int junkmode; + uint8_t *ptr; + uint8_t *end; + if(opt_jprob <= 0.0) return; + for(ptr = buf, end = ptr + size; ptr < end; ptr++) { + int byte = *ptr; + if(junkmode++ & 1) { + if((((double)random() / RAND_MAX) < prob)) + byte = random() & 0xff; + } else { +#define BPROB(b) ((((double)random() / RAND_MAX) < prob) ? b : 0) + byte ^= BPROB(0x80); + byte ^= BPROB(0x40); + byte ^= BPROB(0x20); + byte ^= BPROB(0x10); + byte ^= BPROB(0x08); + byte ^= BPROB(0x04); + byte ^= BPROB(0x02); + byte ^= BPROB(0x01); + } + if(byte != *ptr) { + DEBUG("Junk buf[%d] %02x -> %02x", + ptr - buf, *ptr, byte); + *ptr = byte; + } + } +} +#endif /* JUNKTEST */ + diff --git a/asn1_test_data/der_encoder.c b/asn1_test_data/der_encoder.c new file mode 100644 index 0000000..1c01480 --- /dev/null +++ b/asn1_test_data/der_encoder.c @@ -0,0 +1,201 @@ +/*- + * Copyright (c) 2003, 2004 Lev Walkin . All rights reserved. + * Redistribution and modifications are permitted subject to BSD license. + */ +#include +#include + +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. + */ +asn_enc_rval_t +der_encode(asn_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); +} + +/* + * Argument type and callback necessary for der_encode_to_buffer(). + */ +typedef struct enc_to_buf_arg { + void *buffer; + size_t left; +} enc_to_buf_arg; +static int encode_to_buffer_cb(const void *buffer, size_t size, void *key) { + enc_to_buf_arg *arg = (enc_to_buf_arg *)key; + + if(arg->left < size) + return -1; /* Data exceeds the available buffer size */ + + memcpy(arg->buffer, buffer, size); + arg->buffer = ((char *)arg->buffer) + size; + arg->left -= size; + + return 0; +} + +/* + * A variant of the der_encode() which encodes the data into the provided buffer + */ +asn_enc_rval_t +der_encode_to_buffer(asn_TYPE_descriptor_t *type_descriptor, void *struct_ptr, + void *buffer, size_t buffer_size) { + enc_to_buf_arg arg; + asn_enc_rval_t ec; + + arg.buffer = buffer; + arg.left = buffer_size; + + ec = type_descriptor->der_encoder(type_descriptor, + struct_ptr, /* Pointer to the destination structure */ + 0, 0, encode_to_buffer_cb, &arg); + if(ec.encoded != -1) { + assert(ec.encoded == (ssize_t)(buffer_size - arg.left)); + /* Return the encoded contents size */ + } + return ec; +} + + +/* + * Write out leading TL[v] sequence according to the type definition. + */ +ssize_t +der_write_tags(asn_TYPE_descriptor_t *sd, + size_t struct_length, + int tag_mode, int last_tag_form, + ber_tlv_tag_t tag, /* EXPLICIT or IMPLICIT tag */ + asn_app_consume_bytes_f *cb, + void *app_key) { + const ber_tlv_tag_t *tags; /* Copy of tags stream */ + int tags_count; /* Number of tags */ + size_t overall_length; + ssize_t *lens; + int i; + + ASN_DEBUG("Writing tags (%s, tm=%d, tc=%d, tag=%s, mtc=%d)", + sd->name, tag_mode, sd->tags_count, + ber_tlv_tag_string(tag), + tag_mode + ?(sd->tags_count+1 + -((tag_mode == -1) && sd->tags_count)) + :sd->tags_count + ); + + 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. + */ + int stag_offset; + ber_tlv_tag_t *tags_buf; + tags_buf = (ber_tlv_tag_t *)alloca((sd->tags_count + 1) * sizeof(ber_tlv_tag_t)); + if(!tags_buf) { /* Can fail on !x86 */ + errno = ENOMEM; + return -1; + } + tags_count = sd->tags_count + + 1 /* EXPLICIT or IMPLICIT tag is given */ + - ((tag_mode == -1) && sd->tags_count); + /* Copy tags over */ + tags_buf[0] = tag; + stag_offset = -1 + ((tag_mode == -1) && sd->tags_count); + for(i = 1; i < tags_count; i++) + tags_buf[i] = sd->tags[i + stag_offset]; + tags = tags_buf; + } else { + tags = sd->tags; + tags_count = sd->tags_count; + } + + /* No tags to write */ + if(tags_count == 0) + return 0; + + lens = (ssize_t *)alloca(tags_count * sizeof(lens[0])); + if(!lens) { + errno = ENOMEM; + 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; + + /* Check if this tag happens to be constructed */ + _constr = (last_tag_form || i < (tags_count - 1)); + + 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 = ber_tlv_tag_serialize(tag, buf, buf_size); + if(tmp == -1 || tmp > (ssize_t)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) < 0) + return -1; + } + + return size; +} diff --git a/asn1_test_data/der_encoder.h b/asn1_test_data/der_encoder.h new file mode 100644 index 0000000..61431c6 --- /dev/null +++ b/asn1_test_data/der_encoder.h @@ -0,0 +1,68 @@ +/*- + * Copyright (c) 2003, 2004 Lev Walkin . All rights reserved. + * Redistribution and modifications are permitted subject to BSD license. + */ +#ifndef _DER_ENCODER_H_ +#define _DER_ENCODER_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct asn_TYPE_descriptor_s; /* Forward declaration */ + +/* + * The DER encoder of any type. May be invoked by the application. + * The ber_decode() function (ber_decoder.h) is an opposite of der_encode(). + */ +asn_enc_rval_t der_encode(struct asn_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 */ + ); + +/* A variant of der_encode() which encodes data into the pre-allocated buffer */ +asn_enc_rval_t der_encode_to_buffer( + struct asn_TYPE_descriptor_s *type_descriptor, + void *struct_ptr, /* Structure to be encoded */ + void *buffer, /* Pre-allocated buffer */ + size_t buffer_size /* Initial buffer size (maximum) */ + ); + +/* + * Type of the generic DER encoder. + */ +typedef asn_enc_rval_t (der_type_encoder_f)( + struct asn_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 asn_TYPE_descriptor_s *type_descriptor, + size_t struct_length, + int tag_mode, /* {-1,0,1}: IMPLICIT, no, EXPLICIT */ + int last_tag_form, /* {0,!0}: prim, constructed */ + ber_tlv_tag_t tag, + asn_app_consume_bytes_f *consume_bytes_cb, + void *app_key + ); + +#ifdef __cplusplus +} +#endif + +#endif /* _DER_ENCODER_H_ */ diff --git a/asn1_test_data/draft-ounsworth-pubkey.asn b/asn1_test_data/draft-ounsworth-pubkey.asn new file mode 100644 index 0000000..d762246 --- /dev/null +++ b/asn1_test_data/draft-ounsworth-pubkey.asn @@ -0,0 +1,5 @@ +COMPOSITESIGALGS { 1 2 3 4 } + +DEFINITIONS IMPLICIT TAGS ::= BEGIN + +-- EXPORTS ALL -- diff --git a/asn1_test_data/draft-ounsworth-sig.asn b/asn1_test_data/draft-ounsworth-sig.asn new file mode 100644 index 0000000..8aaa928 --- /dev/null +++ b/asn1_test_data/draft-ounsworth-sig.asn @@ -0,0 +1,63 @@ +COMPOSITESIGALGS { 1 2 3 4 } + +DEFINITIONS IMPLICIT TAGS ::= BEGIN + +-- EXPORTS ALL -- + +-- faked + +id-entrust-sha256RSAwithsha384ECDSA OBJECT IDENTIFIER ::= { 1 2 3 4 } + +RSAParameters ::= NULL + +ECParameters ::= SEQUENCE { + a OCTET STRING, + b OCTET STRING, + p OCTET STRING, + n INTEGER +} + +-- Borrowed from RFC5180 +AlgorithmIdentifier ::= SEQUENCE { + algorithm OBJECT IDENTIFIER, + parameters ANY DEFINED BY algorithm OPTIONAL } + + +EXPLICIT-COMPOSITE-SIGNATURE-ALGORITHM ::= CLASS { + &id OBJECT IDENTIFIER, + &FirstAlgorithmParams OPTIONAL, + &SecondAlgorithmParams OPTIONAL +} WITH SYNTAX { + ID &id + FIRSTPARAMS &FirstAlgorithmParams + SECONDPARAMS &SecondAlgorithmParams +} + + +-- For testing purposes we define the following pair of RSA and ECDSA, +-- but we intend for this draft to be extended to add additional pairs to this list + +ExplicitCompositeAlgorithms EXPLICIT-COMPOSITE-SIGNATURE-ALGORITHM ::= { + {ID id-entrust-sha256RSAwithsha384ECDSA, FIRSTPARAMS RSAParameters, SECONDPARAMS ECParameters} +} + + +ExplicitCompositeSignatureValue ::= SEQUENCE { + firstSignatureValue OCTET STRING, + secondSignatureValue OCTET STRING +} + +ExplicitCompositeSignatureParams ::= SEQUENCE { + firstAlgorithmParams EXPLICIT-COMPOSITE-SIGNATURE-ALGORITHM.&FirstAlgorithmParams ({ExplicitCompositeAlgorithms}), + secondAlgorithmParams EXPLICIT-COMPOSITE-SIGNATURE-ALGORITHM.&SecondAlgorithmParams ({ExplicitCompositeAlgorithms}{@firstAlgorithmParams}) +} + +sa-CompositeSignature SIGNATURE-ALGORITHM ::= { + IDENTIFIER EXPLICIT-COMPOSITE-SIGNATURE-ALGORITHM.&id + VALUE ExplicitCompositeSignatureValue + PARAMS ARE optional + PUBLIC-KEYS { pk-Composite } + SMIME-CAPS { IDENTIFIED BY id-alg-explicit } +} + +END diff --git a/asn1_test_data/per_decoder.c b/asn1_test_data/per_decoder.c new file mode 100644 index 0000000..461b726 --- /dev/null +++ b/asn1_test_data/per_decoder.c @@ -0,0 +1,93 @@ +#include +#include +#include + +/* + * Decode a "Production of a complete encoding", X.691#10.1. + * The complete encoding contains at least one byte, and is an integral + * multiple of 8 bytes. + */ +asn_dec_rval_t +uper_decode_complete(asn_codec_ctx_t *opt_codec_ctx, asn_TYPE_descriptor_t *td, void **sptr, const void *buffer, size_t size) { + asn_dec_rval_t rval; + + rval = uper_decode(opt_codec_ctx, td, sptr, buffer, size, 0, 0); + if(rval.consumed) { + /* + * We've always given 8-aligned data, + * so convert bits to integral bytes. + */ + rval.consumed += 7; + rval.consumed >>= 3; + } else if(rval.code == RC_OK) { + if(size) { + if(((const uint8_t *)buffer)[0] == 0) { + rval.consumed = 1; /* 1 byte */ + } else { + ASN_DEBUG("Expecting single zeroed byte"); + rval.code = RC_FAIL; + } + } else { + /* Must contain at least 8 bits. */ + rval.code = RC_WMORE; + } + } + + return rval; +} + +asn_dec_rval_t +uper_decode(asn_codec_ctx_t *opt_codec_ctx, asn_TYPE_descriptor_t *td, void **sptr, const void *buffer, size_t size, int skip_bits, int unused_bits) { + asn_codec_ctx_t s_codec_ctx; + asn_dec_rval_t rval; + asn_per_data_t pd; + + if(skip_bits < 0 || skip_bits > 7 + || unused_bits < 0 || unused_bits > 7 + || (unused_bits > 0 && !size)) + ASN__DECODE_FAILED; + + /* + * Stack checker requires that the codec context + * must be allocated on the stack. + */ + if(opt_codec_ctx) { + if(opt_codec_ctx->max_stack_size) { + s_codec_ctx = *opt_codec_ctx; + opt_codec_ctx = &s_codec_ctx; + } + } else { + /* If context is not given, be security-conscious anyway */ + memset(&s_codec_ctx, 0, sizeof(s_codec_ctx)); + s_codec_ctx.max_stack_size = ASN__DEFAULT_STACK_MAX; + opt_codec_ctx = &s_codec_ctx; + } + + /* Fill in the position indicator */ + memset(&pd, 0, sizeof(pd)); + pd.buffer = (const uint8_t *)buffer; + pd.nboff = skip_bits; + pd.nbits = 8 * size - unused_bits; /* 8 is CHAR_BIT from */ + if(pd.nboff > pd.nbits) + ASN__DECODE_FAILED; + + /* + * Invoke type-specific decoder. + */ + if(!td->uper_decoder) + ASN__DECODE_FAILED; /* PER is not compiled in */ + rval = td->uper_decoder(opt_codec_ctx, td, 0, sptr, &pd); + if(rval.code == RC_OK) { + /* Return the number of consumed bits */ + rval.consumed = ((pd.buffer - (const uint8_t *)buffer) << 3) + + pd.nboff - skip_bits; + ASN_DEBUG("PER decoding consumed %ld, counted %ld", + (long)rval.consumed, (long)pd.moved); + assert(rval.consumed == pd.moved); + } else { + /* PER codec is not a restartable */ + rval.consumed = 0; + } + return rval; +} + diff --git a/asn1_test_data/per_decoder.h b/asn1_test_data/per_decoder.h new file mode 100644 index 0000000..8397a54 --- /dev/null +++ b/asn1_test_data/per_decoder.h @@ -0,0 +1,56 @@ +/*- + * Copyright (c) 2005, 2007 Lev Walkin . All rights reserved. + * Redistribution and modifications are permitted subject to BSD license. + */ +#ifndef _PER_DECODER_H_ +#define _PER_DECODER_H_ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct asn_TYPE_descriptor_s; /* Forward declaration */ + +/* + * Unaligned PER decoder of a "complete encoding" as per X.691#10.1. + * On success, this call always returns (.consumed >= 1), as per X.691#10.1.3. + */ +asn_dec_rval_t uper_decode_complete(struct asn_codec_ctx_s *opt_codec_ctx, + struct asn_TYPE_descriptor_s *type_descriptor, /* Type to decode */ + void **struct_ptr, /* Pointer to a target structure's pointer */ + const void *buffer, /* Data to be decoded */ + size_t size /* Size of data buffer */ + ); + +/* + * Unaligned PER decoder of any ASN.1 type. May be invoked by the application. + * WARNING: This call returns the number of BITS read from the stream. Beware. + */ +asn_dec_rval_t uper_decode(struct asn_codec_ctx_s *opt_codec_ctx, + struct asn_TYPE_descriptor_s *type_descriptor, /* Type to decode */ + void **struct_ptr, /* Pointer to a target structure's pointer */ + const void *buffer, /* Data to be decoded */ + size_t size, /* Size of data buffer */ + int skip_bits, /* Number of unused leading bits, 0..7 */ + int unused_bits /* Number of unused tailing bits, 0..7 */ + ); + + +/* + * Type of the type-specific PER decoder function. + */ +typedef asn_dec_rval_t (per_type_decoder_f)(asn_codec_ctx_t *opt_codec_ctx, + struct asn_TYPE_descriptor_s *type_descriptor, + asn_per_constraints_t *constraints, + void **struct_ptr, + asn_per_data_t *per_data + ); + +#ifdef __cplusplus +} +#endif + +#endif /* _PER_DECODER_H_ */ diff --git a/asn1_test_data/per_encoder.c b/asn1_test_data/per_encoder.c new file mode 100644 index 0000000..47f3c91 --- /dev/null +++ b/asn1_test_data/per_encoder.c @@ -0,0 +1,151 @@ +#include +#include +#include + +static asn_enc_rval_t uper_encode_internal(asn_TYPE_descriptor_t *td, asn_per_constraints_t *, void *sptr, asn_app_consume_bytes_f *cb, void *app_key); + +asn_enc_rval_t +uper_encode(asn_TYPE_descriptor_t *td, void *sptr, asn_app_consume_bytes_f *cb, void *app_key) { + return uper_encode_internal(td, 0, sptr, cb, app_key); +} + +/* + * Argument type and callback necessary for uper_encode_to_buffer(). + */ +typedef struct enc_to_buf_arg { + void *buffer; + size_t left; +} enc_to_buf_arg; +static int encode_to_buffer_cb(const void *buffer, size_t size, void *key) { + enc_to_buf_arg *arg = (enc_to_buf_arg *)key; + + if(arg->left < size) + return -1; /* Data exceeds the available buffer size */ + + memcpy(arg->buffer, buffer, size); + arg->buffer = ((char *)arg->buffer) + size; + arg->left -= size; + + return 0; +} + +asn_enc_rval_t +uper_encode_to_buffer(asn_TYPE_descriptor_t *td, void *sptr, void *buffer, size_t buffer_size) { + enc_to_buf_arg key; + + key.buffer = buffer; + key.left = buffer_size; + + if(td) ASN_DEBUG("Encoding \"%s\" using UNALIGNED PER", td->name); + + return uper_encode_internal(td, 0, sptr, encode_to_buffer_cb, &key); +} + +typedef struct enc_dyn_arg { + void *buffer; + size_t length; + size_t allocated; +} enc_dyn_arg; +static int +encode_dyn_cb(const void *buffer, size_t size, void *key) { + enc_dyn_arg *arg = key; + if(arg->length + size >= arg->allocated) { + void *p; + arg->allocated = arg->allocated ? (arg->allocated << 2) : size; + p = REALLOC(arg->buffer, arg->allocated); + if(!p) { + FREEMEM(arg->buffer); + memset(arg, 0, sizeof(*arg)); + return -1; + } + arg->buffer = p; + } + memcpy(((char *)arg->buffer) + arg->length, buffer, size); + arg->length += size; + return 0; +} +ssize_t +uper_encode_to_new_buffer(asn_TYPE_descriptor_t *td, asn_per_constraints_t *constraints, void *sptr, void **buffer_r) { + asn_enc_rval_t er; + enc_dyn_arg key; + + memset(&key, 0, sizeof(key)); + + er = uper_encode_internal(td, constraints, sptr, encode_dyn_cb, &key); + switch(er.encoded) { + case -1: + FREEMEM(key.buffer); + return -1; + case 0: + FREEMEM(key.buffer); + key.buffer = MALLOC(1); + if(key.buffer) { + *(char *)key.buffer = '\0'; + *buffer_r = key.buffer; + return 1; + } else { + return -1; + } + default: + *buffer_r = key.buffer; + ASN_DEBUG("Complete encoded in %ld bits", (long)er.encoded); + return ((er.encoded + 7) >> 3); + } +} + +/* + * Internally useful functions. + */ + +/* Flush partially filled buffer */ +static int +_uper_encode_flush_outp(asn_per_outp_t *po) { + uint8_t *buf; + + if(po->nboff == 0 && po->buffer == po->tmpspace) + return 0; + + buf = po->buffer + (po->nboff >> 3); + /* Make sure we account for the last, partially filled */ + if(po->nboff & 0x07) { + buf[0] &= 0xff << (8 - (po->nboff & 0x07)); + buf++; + } + + return po->outper(po->tmpspace, buf - po->tmpspace, po->op_key); +} + +static asn_enc_rval_t +uper_encode_internal(asn_TYPE_descriptor_t *td, asn_per_constraints_t *constraints, void *sptr, asn_app_consume_bytes_f *cb, void *app_key) { + asn_per_outp_t po; + asn_enc_rval_t er; + + /* + * Invoke type-specific encoder. + */ + if(!td || !td->uper_encoder) + ASN__ENCODE_FAILED; /* PER is not compiled in */ + + po.buffer = po.tmpspace; + po.nboff = 0; + po.nbits = 8 * sizeof(po.tmpspace); + po.outper = cb; + po.op_key = app_key; + po.flushed_bytes = 0; + + er = td->uper_encoder(td, constraints, sptr, &po); + if(er.encoded != -1) { + size_t bits_to_flush; + + bits_to_flush = ((po.buffer - po.tmpspace) << 3) + po.nboff; + + /* Set number of bits encoded to a firm value */ + er.encoded = (po.flushed_bytes << 3) + bits_to_flush; + + if(_uper_encode_flush_outp(&po)) + ASN__ENCODE_FAILED; + } + + return er; +} + diff --git a/asn1_test_data/per_encoder.h b/asn1_test_data/per_encoder.h new file mode 100644 index 0000000..95a6506 --- /dev/null +++ b/asn1_test_data/per_encoder.h @@ -0,0 +1,69 @@ +/*- + * Copyright (c) 2006, 2007 Lev Walkin . All rights reserved. + * Redistribution and modifications are permitted subject to BSD license. + */ +#ifndef _PER_ENCODER_H_ +#define _PER_ENCODER_H_ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct asn_TYPE_descriptor_s; /* Forward declaration */ + +/* + * Unaligned PER encoder of any ASN.1 type. May be invoked by the application. + * WARNING: This function returns the number of encoded bits in the .encoded + * field of the return value. Use the following formula to convert to bytes: + * bytes = ((.encoded + 7) / 8) + */ +asn_enc_rval_t uper_encode(struct asn_TYPE_descriptor_s *type_descriptor, + void *struct_ptr, /* Structure to be encoded */ + asn_app_consume_bytes_f *consume_bytes_cb, /* Data collector */ + void *app_key /* Arbitrary callback argument */ +); + +/* + * A variant of uper_encode() which encodes data into the existing buffer + * WARNING: This function returns the number of encoded bits in the .encoded + * field of the return value. + */ +asn_enc_rval_t uper_encode_to_buffer( + struct asn_TYPE_descriptor_s *type_descriptor, + void *struct_ptr, /* Structure to be encoded */ + void *buffer, /* Pre-allocated buffer */ + size_t buffer_size /* Initial buffer size (max) */ +); + +/* + * A variant of uper_encode_to_buffer() which allocates buffer itself. + * Returns the number of bytes in the buffer or -1 in case of failure. + * WARNING: This function produces a "Production of the complete encoding", + * with length of at least one octet. Contrast this to precise bit-packing + * encoding of uper_encode() and uper_encode_to_buffer(). + */ +ssize_t uper_encode_to_new_buffer( + struct asn_TYPE_descriptor_s *type_descriptor, + asn_per_constraints_t *constraints, + void *struct_ptr, /* Structure to be encoded */ + void **buffer_r /* Buffer allocated and returned */ +); + +/* + * Type of the generic PER encoder function. + */ +typedef asn_enc_rval_t (per_type_encoder_f)( + struct asn_TYPE_descriptor_s *type_descriptor, + asn_per_constraints_t *constraints, + void *struct_ptr, + asn_per_outp_t *per_output +); + +#ifdef __cplusplus +} +#endif + +#endif /* _PER_ENCODER_H_ */ diff --git a/asn1_test_data/per_opentype.c b/asn1_test_data/per_opentype.c new file mode 100644 index 0000000..404aa72 --- /dev/null +++ b/asn1_test_data/per_opentype.c @@ -0,0 +1,378 @@ +/* + * Copyright (c) 2007 Lev Walkin . All rights reserved. + * Redistribution and modifications are permitted subject to BSD license. + */ +#include +#include +#include +#include + +typedef struct uper_ugot_key { + asn_per_data_t oldpd; /* Old per data source */ + size_t unclaimed; + size_t ot_moved; /* Number of bits moved by OT processing */ + int repeat; +} uper_ugot_key; + +static int uper_ugot_refill(asn_per_data_t *pd); +static int per_skip_bits(asn_per_data_t *pd, int skip_nbits); +static asn_dec_rval_t uper_sot_suck(asn_codec_ctx_t *, asn_TYPE_descriptor_t *td, asn_per_constraints_t *constraints, void **sptr, asn_per_data_t *pd); + +/* + * Encode an "open type field". + * #10.1, #10.2 + */ +int +uper_open_type_put(asn_TYPE_descriptor_t *td, asn_per_constraints_t *constraints, void *sptr, asn_per_outp_t *po) { + void *buf; + void *bptr; + ssize_t size; + size_t toGo; + + ASN_DEBUG("Open type put %s ...", td->name); + + size = uper_encode_to_new_buffer(td, constraints, sptr, &buf); + if(size <= 0) return -1; + + for(bptr = buf, toGo = size; toGo;) { + ssize_t maySave = uper_put_length(po, toGo); + ASN_DEBUG("Prepending length %d to %s and allowing to save %d", + (int)size, td->name, (int)maySave); + if(maySave < 0) break; + if(per_put_many_bits(po, bptr, maySave * 8)) break; + bptr = (char *)bptr + maySave; + toGo -= maySave; + } + + FREEMEM(buf); + if(toGo) return -1; + + ASN_DEBUG("Open type put %s of length %ld + overhead (1byte?)", + td->name, (long)size); + + return 0; +} + +static asn_dec_rval_t +uper_open_type_get_simple(asn_codec_ctx_t *ctx, asn_TYPE_descriptor_t *td, + asn_per_constraints_t *constraints, void **sptr, asn_per_data_t *pd) { + asn_dec_rval_t rv; + ssize_t chunk_bytes; + int repeat; + uint8_t *buf = 0; + size_t bufLen = 0; + size_t bufSize = 0; + asn_per_data_t spd; + size_t padding; + + ASN__STACK_OVERFLOW_CHECK(ctx); + + ASN_DEBUG("Getting open type %s...", td->name); + + do { + chunk_bytes = uper_get_length(pd, -1, &repeat); + if(chunk_bytes < 0) { + FREEMEM(buf); + ASN__DECODE_STARVED; + } + if(bufLen + chunk_bytes > bufSize) { + void *ptr; + bufSize = chunk_bytes + (bufSize << 2); + ptr = REALLOC(buf, bufSize); + if(!ptr) { + FREEMEM(buf); + ASN__DECODE_FAILED; + } + buf = ptr; + } + if(per_get_many_bits(pd, buf + bufLen, 0, chunk_bytes << 3)) { + FREEMEM(buf); + ASN__DECODE_STARVED; + } + bufLen += chunk_bytes; + } while(repeat); + + ASN_DEBUG("Getting open type %s encoded in %ld bytes", td->name, + (long)bufLen); + + memset(&spd, 0, sizeof(spd)); + spd.buffer = buf; + spd.nbits = bufLen << 3; + + ASN_DEBUG_INDENT_ADD(+4); + rv = td->uper_decoder(ctx, td, constraints, sptr, &spd); + ASN_DEBUG_INDENT_ADD(-4); + + if(rv.code == RC_OK) { + /* Check padding validity */ + padding = spd.nbits - spd.nboff; + if ((padding < 8 || + /* X.691#10.1.3 */ + (spd.nboff == 0 && spd.nbits == 8 && spd.buffer == buf)) && + per_get_few_bits(&spd, padding) == 0) { + /* Everything is cool */ + FREEMEM(buf); + return rv; + } + FREEMEM(buf); + if(padding >= 8) { + ASN_DEBUG("Too large padding %d in open type", (int)padding); + ASN__DECODE_FAILED; + } else { + ASN_DEBUG("Non-zero padding"); + ASN__DECODE_FAILED; + } + } else { + FREEMEM(buf); + /* rv.code could be RC_WMORE, nonsense in this context */ + rv.code = RC_FAIL; /* Noone would give us more */ + } + + return rv; +} + +static asn_dec_rval_t GCC_NOTUSED +uper_open_type_get_complex(asn_codec_ctx_t *ctx, asn_TYPE_descriptor_t *td, + asn_per_constraints_t *constraints, void **sptr, asn_per_data_t *pd) { + uper_ugot_key arg; + asn_dec_rval_t rv; + ssize_t padding; + + ASN__STACK_OVERFLOW_CHECK(ctx); + + ASN_DEBUG("Getting open type %s from %s", td->name, + per_data_string(pd)); + arg.oldpd = *pd; + arg.unclaimed = 0; + arg.ot_moved = 0; + arg.repeat = 1; + pd->refill = uper_ugot_refill; + pd->refill_key = &arg; + pd->nbits = pd->nboff; /* 0 good bits at this point, will refill */ + pd->moved = 0; /* This now counts the open type size in bits */ + + ASN_DEBUG_INDENT_ADD(+4); + rv = td->uper_decoder(ctx, td, constraints, sptr, pd); + ASN_DEBUG_INDENT_ADD(-4); + +#define UPDRESTOREPD do { \ + /* buffer and nboff are valid, preserve them. */ \ + pd->nbits = arg.oldpd.nbits - (pd->moved - arg.ot_moved); \ + pd->moved = arg.oldpd.moved + (pd->moved - arg.ot_moved); \ + pd->refill = arg.oldpd.refill; \ + pd->refill_key = arg.oldpd.refill_key; \ + } while(0) + + if(rv.code != RC_OK) { + UPDRESTOREPD; + return rv; + } + + ASN_DEBUG("OpenType %s pd%s old%s unclaimed=%d, repeat=%d", td->name, + per_data_string(pd), + per_data_string(&arg.oldpd), + (int)arg.unclaimed, (int)arg.repeat); + + padding = pd->moved % 8; + if(padding) { + int32_t pvalue; + if(padding > 7) { + ASN_DEBUG("Too large padding %d in open type", + (int)padding); + rv.code = RC_FAIL; + UPDRESTOREPD; + return rv; + } + padding = 8 - padding; + ASN_DEBUG("Getting padding of %d bits", (int)padding); + pvalue = per_get_few_bits(pd, padding); + switch(pvalue) { + case -1: + ASN_DEBUG("Padding skip failed"); + UPDRESTOREPD; + ASN__DECODE_STARVED; + case 0: break; + default: + ASN_DEBUG("Non-blank padding (%d bits 0x%02x)", + (int)padding, (int)pvalue); + UPDRESTOREPD; + ASN__DECODE_FAILED; + } + } + if(pd->nboff != pd->nbits) { + ASN_DEBUG("Open type %s overhead pd%s old%s", td->name, + per_data_string(pd), per_data_string(&arg.oldpd)); + if(1) { + UPDRESTOREPD; + ASN__DECODE_FAILED; + } else { + arg.unclaimed += pd->nbits - pd->nboff; + } + } + + /* Adjust pd back so it points to original data */ + UPDRESTOREPD; + + /* Skip data not consumed by the decoder */ + if(arg.unclaimed) { + ASN_DEBUG("Getting unclaimed %d", (int)arg.unclaimed); + switch(per_skip_bits(pd, arg.unclaimed)) { + case -1: + ASN_DEBUG("Claim of %d failed", (int)arg.unclaimed); + ASN__DECODE_STARVED; + case 0: + ASN_DEBUG("Got claim of %d", (int)arg.unclaimed); + break; + default: + /* Padding must be blank */ + ASN_DEBUG("Non-blank unconsumed padding"); + ASN__DECODE_FAILED; + } + arg.unclaimed = 0; + } + + if(arg.repeat) { + ASN_DEBUG("Not consumed the whole thing"); + rv.code = RC_FAIL; + return rv; + } + + return rv; +} + + +asn_dec_rval_t +uper_open_type_get(asn_codec_ctx_t *ctx, asn_TYPE_descriptor_t *td, + asn_per_constraints_t *constraints, void **sptr, asn_per_data_t *pd) { + + return uper_open_type_get_simple(ctx, td, constraints, sptr, pd); +} + +int +uper_open_type_skip(asn_codec_ctx_t *ctx, asn_per_data_t *pd) { + asn_TYPE_descriptor_t s_td; + asn_dec_rval_t rv; + + s_td.name = ""; + s_td.uper_decoder = uper_sot_suck; + + rv = uper_open_type_get(ctx, &s_td, 0, 0, pd); + if(rv.code != RC_OK) + return -1; + else + return 0; +} + +/* + * Internal functions. + */ + +static asn_dec_rval_t +uper_sot_suck(asn_codec_ctx_t *ctx, asn_TYPE_descriptor_t *td, + asn_per_constraints_t *constraints, void **sptr, asn_per_data_t *pd) { + asn_dec_rval_t rv; + + (void)ctx; + (void)td; + (void)constraints; + (void)sptr; + + while(per_get_few_bits(pd, 24) >= 0); + + rv.code = RC_OK; + rv.consumed = pd->moved; + + return rv; +} + +static int +uper_ugot_refill(asn_per_data_t *pd) { + uper_ugot_key *arg = pd->refill_key; + ssize_t next_chunk_bytes, next_chunk_bits; + ssize_t avail; + + asn_per_data_t *oldpd = &arg->oldpd; + + ASN_DEBUG("REFILLING pd->moved=%ld, oldpd->moved=%ld", + (long)pd->moved, (long)oldpd->moved); + + /* Advance our position to where pd is */ + oldpd->buffer = pd->buffer; + oldpd->nboff = pd->nboff; + oldpd->nbits -= pd->moved - arg->ot_moved; + oldpd->moved += pd->moved - arg->ot_moved; + arg->ot_moved = pd->moved; + + if(arg->unclaimed) { + /* Refill the container */ + if(per_get_few_bits(oldpd, 1)) + return -1; + if(oldpd->nboff == 0) { + assert(0); + return -1; + } + pd->buffer = oldpd->buffer; + pd->nboff = oldpd->nboff - 1; + pd->nbits = oldpd->nbits; + ASN_DEBUG("UNCLAIMED <- return from (pd->moved=%ld)", + (long)pd->moved); + return 0; + } + + if(!arg->repeat) { + ASN_DEBUG("Want more but refill doesn't have it"); + return -1; + } + + next_chunk_bytes = uper_get_length(oldpd, -1, &arg->repeat); + ASN_DEBUG("Open type LENGTH %ld bytes at off %ld, repeat %ld", + (long)next_chunk_bytes, (long)oldpd->moved, (long)arg->repeat); + if(next_chunk_bytes < 0) return -1; + if(next_chunk_bytes == 0) { + pd->refill = 0; /* No more refills, naturally */ + assert(!arg->repeat); /* Implementation guarantee */ + } + next_chunk_bits = next_chunk_bytes << 3; + avail = oldpd->nbits - oldpd->nboff; + if(avail >= next_chunk_bits) { + pd->nbits = oldpd->nboff + next_chunk_bits; + arg->unclaimed = 0; + ASN_DEBUG("!+Parent frame %ld bits, alloting %ld [%ld..%ld] (%ld)", + (long)next_chunk_bits, (long)oldpd->moved, + (long)oldpd->nboff, (long)oldpd->nbits, + (long)(oldpd->nbits - oldpd->nboff)); + } else { + pd->nbits = oldpd->nbits; + arg->unclaimed = next_chunk_bits - avail; + ASN_DEBUG("!-Parent frame %ld, require %ld, will claim %ld", + (long)avail, (long)next_chunk_bits, + (long)arg->unclaimed); + } + pd->buffer = oldpd->buffer; + pd->nboff = oldpd->nboff; + ASN_DEBUG("Refilled pd%s old%s", + per_data_string(pd), per_data_string(oldpd)); + return 0; +} + +static int +per_skip_bits(asn_per_data_t *pd, int skip_nbits) { + int hasNonZeroBits = 0; + while(skip_nbits > 0) { + int skip; + + /* per_get_few_bits() is more efficient when nbits <= 24 */ + if(skip_nbits < 24) + skip = skip_nbits; + else + skip = 24; + skip_nbits -= skip; + + switch(per_get_few_bits(pd, skip)) { + case -1: return -1; /* Starving */ + case 0: continue; /* Skipped empty space */ + default: hasNonZeroBits = 1; continue; + } + } + return hasNonZeroBits; +} diff --git a/asn1_test_data/per_opentype.h b/asn1_test_data/per_opentype.h new file mode 100644 index 0000000..facfaa6 --- /dev/null +++ b/asn1_test_data/per_opentype.h @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2007 Lev Walkin . All rights reserved. + * Redistribution and modifications are permitted subject to BSD license. + */ +#ifndef _PER_OPENTYPE_H_ +#define _PER_OPENTYPE_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +asn_dec_rval_t uper_open_type_get(asn_codec_ctx_t *opt_codec_ctx, asn_TYPE_descriptor_t *td, asn_per_constraints_t *constraints, void **sptr, asn_per_data_t *pd); + +int uper_open_type_skip(asn_codec_ctx_t *opt_codec_ctx, asn_per_data_t *pd); + +int uper_open_type_put(asn_TYPE_descriptor_t *td, asn_per_constraints_t *constraints, void *sptr, asn_per_outp_t *po); + +#ifdef __cplusplus +} +#endif + +#endif /* _PER_OPENTYPE_H_ */ diff --git a/asn1_test_data/per_support.c b/asn1_test_data/per_support.c new file mode 100644 index 0000000..14b4c4c --- /dev/null +++ b/asn1_test_data/per_support.c @@ -0,0 +1,483 @@ +/* + * Copyright (c) 2005-2014 Lev Walkin . + * All rights reserved. + * Redistribution and modifications are permitted subject to BSD license. + */ +#include +#include +#include + +char * +per_data_string(asn_per_data_t *pd) { + static char buf[2][32]; + static int n; + n = (n+1) % 2; + snprintf(buf[n], sizeof(buf[n]), + "{m=%ld span %+ld[%d..%d] (%d)}", + (long)pd->moved, + (((long)pd->buffer) & 0xf), + (int)pd->nboff, (int)pd->nbits, + (int)(pd->nbits - pd->nboff)); + return buf[n]; +} + +void +per_get_undo(asn_per_data_t *pd, int nbits) { + if((ssize_t)pd->nboff < nbits) { + assert((ssize_t)pd->nboff < nbits); + } else { + pd->nboff -= nbits; + pd->moved -= nbits; + } +} + +/* + * Extract a small number of bits (<= 31) from the specified PER data pointer. + */ +int32_t +per_get_few_bits(asn_per_data_t *pd, int nbits) { + size_t off; /* Next after last bit offset */ + ssize_t nleft; /* Number of bits left in this stream */ + uint32_t accum; + const uint8_t *buf; + + if(nbits < 0) + return -1; + + nleft = pd->nbits - pd->nboff; + if(nbits > nleft) { + int32_t tailv, vhead; + if(!pd->refill || nbits > 31) return -1; + /* Accumulate unused bytes before refill */ + ASN_DEBUG("Obtain the rest %d bits (want %d)", + (int)nleft, (int)nbits); + tailv = per_get_few_bits(pd, nleft); + if(tailv < 0) return -1; + /* Refill (replace pd contents with new data) */ + if(pd->refill(pd)) + return -1; + nbits -= nleft; + vhead = per_get_few_bits(pd, nbits); + /* Combine the rest of previous pd with the head of new one */ + tailv = (tailv << nbits) | vhead; /* Could == -1 */ + return tailv; + } + + /* + * Normalize position indicator. + */ + if(pd->nboff >= 8) { + pd->buffer += (pd->nboff >> 3); + pd->nbits -= (pd->nboff & ~0x07); + pd->nboff &= 0x07; + } + pd->moved += nbits; + pd->nboff += nbits; + off = pd->nboff; + buf = pd->buffer; + + /* + * Extract specified number of bits. + */ + if(off <= 8) + accum = nbits ? (buf[0]) >> (8 - off) : 0; + else if(off <= 16) + accum = ((buf[0] << 8) + buf[1]) >> (16 - off); + else if(off <= 24) + accum = ((buf[0] << 16) + (buf[1] << 8) + buf[2]) >> (24 - off); + else if(off <= 31) + accum = ((buf[0] << 24) + (buf[1] << 16) + + (buf[2] << 8) + (buf[3])) >> (32 - off); + else if(nbits <= 31) { + asn_per_data_t tpd = *pd; + /* Here are we with our 31-bits limit plus 1..7 bits offset. */ + per_get_undo(&tpd, nbits); + /* The number of available bits in the stream allow + * for the following operations to take place without + * invoking the ->refill() function */ + accum = per_get_few_bits(&tpd, nbits - 24) << 24; + accum |= per_get_few_bits(&tpd, 24); + } else { + per_get_undo(pd, nbits); + return -1; + } + + accum &= (((uint32_t)1 << nbits) - 1); + + ASN_DEBUG(" [PER got %2d<=%2d bits => span %d %+ld[%d..%d]:%02x (%d) => 0x%x]", + (int)nbits, (int)nleft, + (int)pd->moved, + (((long)pd->buffer) & 0xf), + (int)pd->nboff, (int)pd->nbits, + ((pd->buffer != NULL)?pd->buffer[0]:0), + (int)(pd->nbits - pd->nboff), + (int)accum); + + return accum; +} + +/* + * Extract a large number of bits from the specified PER data pointer. + */ +int +per_get_many_bits(asn_per_data_t *pd, uint8_t *dst, int alright, int nbits) { + int32_t value; + + if(alright && (nbits & 7)) { + /* Perform right alignment of a first few bits */ + value = per_get_few_bits(pd, nbits & 0x07); + if(value < 0) return -1; + *dst++ = value; /* value is already right-aligned */ + nbits &= ~7; + } + + while(nbits) { + if(nbits >= 24) { + value = per_get_few_bits(pd, 24); + if(value < 0) return -1; + *(dst++) = value >> 16; + *(dst++) = value >> 8; + *(dst++) = value; + nbits -= 24; + } else { + value = per_get_few_bits(pd, nbits); + if(value < 0) return -1; + if(nbits & 7) { /* implies left alignment */ + value <<= 8 - (nbits & 7), + nbits += 8 - (nbits & 7); + if(nbits > 24) + *dst++ = value >> 24; + } + if(nbits > 16) + *dst++ = value >> 16; + if(nbits > 8) + *dst++ = value >> 8; + *dst++ = value; + break; + } + } + + return 0; +} + +/* + * Get the length "n" from the stream. + */ +ssize_t +uper_get_length(asn_per_data_t *pd, int ebits, int *repeat) { + ssize_t value; + + *repeat = 0; + + if(ebits >= 0) return per_get_few_bits(pd, ebits); + + value = per_get_few_bits(pd, 8); + if(value < 0) return -1; + if((value & 128) == 0) /* #10.9.3.6 */ + return (value & 0x7F); + if((value & 64) == 0) { /* #10.9.3.7 */ + value = ((value & 63) << 8) | per_get_few_bits(pd, 8); + if(value < 0) return -1; + return value; + } + value &= 63; /* this is "m" from X.691, #10.9.3.8 */ + if(value < 1 || value > 4) + return -1; + *repeat = 1; + return (16384 * value); +} + +/* + * Get the normally small length "n". + * This procedure used to decode length of extensions bit-maps + * for SET and SEQUENCE types. + */ +ssize_t +uper_get_nslength(asn_per_data_t *pd) { + ssize_t length; + + ASN_DEBUG("Getting normally small length"); + + if(per_get_few_bits(pd, 1) == 0) { + length = per_get_few_bits(pd, 6) + 1; + if(length <= 0) return -1; + ASN_DEBUG("l=%d", (int)length); + return length; + } else { + int repeat; + length = uper_get_length(pd, -1, &repeat); + if(length >= 0 && !repeat) return length; + return -1; /* Error, or do not support >16K extensions */ + } +} + +/* + * Get the normally small non-negative whole number. + * X.691, #10.6 + */ +ssize_t +uper_get_nsnnwn(asn_per_data_t *pd) { + ssize_t value; + + value = per_get_few_bits(pd, 7); + if(value & 64) { /* implicit (value < 0) */ + value &= 63; + value <<= 2; + value |= per_get_few_bits(pd, 2); + if(value & 128) /* implicit (value < 0) */ + return -1; + if(value == 0) + return 0; + if(value >= 3) + return -1; + value = per_get_few_bits(pd, 8 * value); + return value; + } + + return value; +} + +/* + * X.691-11/2008, #11.6 + * Encoding of a normally small non-negative whole number + */ +int +uper_put_nsnnwn(asn_per_outp_t *po, int n) { + int bytes; + + if(n <= 63) { + if(n < 0) return -1; + return per_put_few_bits(po, n, 7); + } + if(n < 256) + bytes = 1; + else if(n < 65536) + bytes = 2; + else if(n < 256 * 65536) + bytes = 3; + else + return -1; /* This is not a "normally small" value */ + if(per_put_few_bits(po, bytes, 8)) + return -1; + + return per_put_few_bits(po, n, 8 * bytes); +} + + +/* X.691-2008/11, #11.5.6 -> #11.3 */ +int uper_get_constrained_whole_number(asn_per_data_t *pd, unsigned long *out_value, int nbits) { + unsigned long lhalf; /* Lower half of the number*/ + long half; + + if(nbits <= 31) { + half = per_get_few_bits(pd, nbits); + if(half < 0) return -1; + *out_value = half; + return 0; + } + + if((size_t)nbits > 8 * sizeof(*out_value)) + return -1; /* RANGE */ + + half = per_get_few_bits(pd, 31); + if(half < 0) return -1; + + if(uper_get_constrained_whole_number(pd, &lhalf, nbits - 31)) + return -1; + + *out_value = ((unsigned long)half << (nbits - 31)) | lhalf; + return 0; +} + + +/* X.691-2008/11, #11.5.6 -> #11.3 */ +int uper_put_constrained_whole_number_s(asn_per_outp_t *po, long v, int nbits) { + /* + * Assume signed number can be safely coerced into + * unsigned of the same range. + * The following testing code will likely be optimized out + * by compiler if it is true. + */ + unsigned long uvalue1 = ULONG_MAX; + long svalue = uvalue1; + unsigned long uvalue2 = svalue; + assert(uvalue1 == uvalue2); + return uper_put_constrained_whole_number_u(po, v, nbits); +} + +int uper_put_constrained_whole_number_u(asn_per_outp_t *po, unsigned long v, int nbits) { + if(nbits <= 31) { + return per_put_few_bits(po, v, nbits); + } else { + /* Put higher portion first, followed by lower 31-bit */ + if(uper_put_constrained_whole_number_u(po, v >> 31, nbits - 31)) + return -1; + return per_put_few_bits(po, v, 31); + } +} + +/* + * Put a small number of bits (<= 31). + */ +int +per_put_few_bits(asn_per_outp_t *po, uint32_t bits, int obits) { + size_t off; /* Next after last bit offset */ + size_t omsk; /* Existing last byte meaningful bits mask */ + uint8_t *buf; + + if(obits <= 0 || obits >= 32) return obits ? -1 : 0; + + ASN_DEBUG("[PER put %d bits %x to %p+%d bits]", + obits, (int)bits, po->buffer, (int)po->nboff); + + /* + * Normalize position indicator. + */ + if(po->nboff >= 8) { + po->buffer += (po->nboff >> 3); + po->nbits -= (po->nboff & ~0x07); + po->nboff &= 0x07; + } + + /* + * Flush whole-bytes output, if necessary. + */ + if(po->nboff + obits > po->nbits) { + int complete_bytes = (po->buffer - po->tmpspace); + ASN_DEBUG("[PER output %ld complete + %ld]", + (long)complete_bytes, (long)po->flushed_bytes); + if(po->outper(po->tmpspace, complete_bytes, po->op_key) < 0) + return -1; + if(po->nboff) + po->tmpspace[0] = po->buffer[0]; + po->buffer = po->tmpspace; + po->nbits = 8 * sizeof(po->tmpspace); + po->flushed_bytes += complete_bytes; + } + + /* + * Now, due to sizeof(tmpspace), we are guaranteed large enough space. + */ + buf = po->buffer; + omsk = ~((1 << (8 - po->nboff)) - 1); + off = (po->nboff + obits); + + /* Clear data of debris before meaningful bits */ + bits &= (((uint32_t)1 << obits) - 1); + + ASN_DEBUG("[PER out %d %u/%x (t=%d,o=%d) %x&%x=%x]", obits, + (int)bits, (int)bits, + (int)po->nboff, (int)off, + buf[0], (int)(omsk&0xff), + (int)(buf[0] & omsk)); + + if(off <= 8) /* Completely within 1 byte */ + po->nboff = off, + bits <<= (8 - off), + buf[0] = (buf[0] & omsk) | bits; + else if(off <= 16) + po->nboff = off, + bits <<= (16 - off), + buf[0] = (buf[0] & omsk) | (bits >> 8), + buf[1] = bits; + else if(off <= 24) + po->nboff = off, + bits <<= (24 - off), + buf[0] = (buf[0] & omsk) | (bits >> 16), + buf[1] = bits >> 8, + buf[2] = bits; + else if(off <= 31) + po->nboff = off, + bits <<= (32 - off), + buf[0] = (buf[0] & omsk) | (bits >> 24), + buf[1] = bits >> 16, + buf[2] = bits >> 8, + buf[3] = bits; + else { + per_put_few_bits(po, bits >> (obits - 24), 24); + per_put_few_bits(po, bits, obits - 24); + } + + ASN_DEBUG("[PER out %u/%x => %02x buf+%ld]", + (int)bits, (int)bits, buf[0], + (long)(po->buffer - po->tmpspace)); + + return 0; +} + + +/* + * Output a large number of bits. + */ +int +per_put_many_bits(asn_per_outp_t *po, const uint8_t *src, int nbits) { + + while(nbits) { + uint32_t value; + + if(nbits >= 24) { + value = (src[0] << 16) | (src[1] << 8) | src[2]; + src += 3; + nbits -= 24; + if(per_put_few_bits(po, value, 24)) + return -1; + } else { + value = src[0]; + if(nbits > 8) + value = (value << 8) | src[1]; + if(nbits > 16) + value = (value << 8) | src[2]; + if(nbits & 0x07) + value >>= (8 - (nbits & 0x07)); + if(per_put_few_bits(po, value, nbits)) + return -1; + break; + } + } + + return 0; +} + +/* + * Put the length "n" (or part of it) into the stream. + */ +ssize_t +uper_put_length(asn_per_outp_t *po, size_t length) { + + if(length <= 127) /* #10.9.3.6 */ + return per_put_few_bits(po, length, 8) + ? -1 : (ssize_t)length; + else if(length < 16384) /* #10.9.3.7 */ + return per_put_few_bits(po, length|0x8000, 16) + ? -1 : (ssize_t)length; + + length >>= 14; + if(length > 4) length = 4; + + return per_put_few_bits(po, 0xC0 | length, 8) + ? -1 : (ssize_t)(length << 14); +} + + +/* + * Put the normally small length "n" into the stream. + * This procedure used to encode length of extensions bit-maps + * for SET and SEQUENCE types. + */ +int +uper_put_nslength(asn_per_outp_t *po, size_t length) { + + if(length <= 64) { + /* #10.9.3.4 */ + if(length == 0) return -1; + return per_put_few_bits(po, length-1, 7) ? -1 : 0; + } else { + if(uper_put_length(po, length) != (ssize_t)length) { + /* This might happen in case of >16K extensions */ + return -1; + } + } + + return 0; +} + diff --git a/asn1_test_data/per_support.h b/asn1_test_data/per_support.h new file mode 100644 index 0000000..a75ac94 --- /dev/null +++ b/asn1_test_data/per_support.h @@ -0,0 +1,135 @@ +/* + * Copyright (c) 2005-2014 Lev Walkin . + * All rights reserved. + * Redistribution and modifications are permitted subject to BSD license. + */ +#ifndef _PER_SUPPORT_H_ +#define _PER_SUPPORT_H_ + +#include /* Platform-specific types */ + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Pre-computed PER constraints. + */ +typedef const struct asn_per_constraint_s { + enum asn_per_constraint_flags { + APC_UNCONSTRAINED = 0x0, /* No PER visible constraints */ + APC_SEMI_CONSTRAINED = 0x1, /* Constrained at "lb" */ + APC_CONSTRAINED = 0x2, /* Fully constrained */ + APC_EXTENSIBLE = 0x4 /* May have extension */ + } flags; + int range_bits; /* Full number of bits in the range */ + int effective_bits; /* Effective bits */ + long lower_bound; /* "lb" value */ + long upper_bound; /* "ub" value */ +} asn_per_constraint_t; +typedef const struct asn_per_constraints_s { + struct asn_per_constraint_s value; + struct asn_per_constraint_s size; + int (*value2code)(unsigned int value); + int (*code2value)(unsigned int code); +} asn_per_constraints_t; + +/* + * This structure describes a position inside an incoming PER bit stream. + */ +typedef struct asn_per_data_s { + const uint8_t *buffer; /* Pointer to the octet stream */ + size_t nboff; /* Bit offset to the meaningful bit */ + size_t nbits; /* Number of bits in the stream */ + size_t moved; /* Number of bits moved through this bit stream */ + int (*refill)(struct asn_per_data_s *); + void *refill_key; +} asn_per_data_t; + +/* + * Extract a small number of bits (<= 31) from the specified PER data pointer. + * This function returns -1 if the specified number of bits could not be + * extracted due to EOD or other conditions. + */ +int32_t per_get_few_bits(asn_per_data_t *per_data, int get_nbits); + +/* Undo the immediately preceeding "get_few_bits" operation */ +void per_get_undo(asn_per_data_t *per_data, int get_nbits); + +/* + * Extract a large number of bits from the specified PER data pointer. + * This function returns -1 if the specified number of bits could not be + * extracted due to EOD or other conditions. + */ +int per_get_many_bits(asn_per_data_t *pd, uint8_t *dst, int right_align, + int get_nbits); + +/* + * Get the length "n" from the Unaligned PER stream. + */ +ssize_t uper_get_length(asn_per_data_t *pd, + int effective_bound_bits, + int *repeat); + +/* + * Get the normally small length "n". + */ +ssize_t uper_get_nslength(asn_per_data_t *pd); + +/* + * Get the normally small non-negative whole number. + */ +ssize_t uper_get_nsnnwn(asn_per_data_t *pd); + +/* X.691-2008/11, #11.5.6 */ +int uper_get_constrained_whole_number(asn_per_data_t *pd, unsigned long *v, int nbits); + +/* Non-thread-safe debugging function, don't use it */ +char *per_data_string(asn_per_data_t *pd); + +/* + * This structure supports forming PER output. + */ +typedef struct asn_per_outp_s { + uint8_t *buffer; /* Pointer into the (tmpspace) */ + size_t nboff; /* Bit offset to the meaningful bit */ + size_t nbits; /* Number of bits left in (tmpspace) */ + uint8_t tmpspace[32]; /* Preliminary storage to hold data */ + int (*outper)(const void *data, size_t size, void *op_key); + void *op_key; /* Key for (outper) data callback */ + size_t flushed_bytes; /* Bytes already flushed through (outper) */ +} asn_per_outp_t; + +/* Output a small number of bits (<= 31) */ +int per_put_few_bits(asn_per_outp_t *per_data, uint32_t bits, int obits); + +/* Output a large number of bits */ +int per_put_many_bits(asn_per_outp_t *po, const uint8_t *src, int put_nbits); + +/* X.691-2008/11, #11.5 */ +int uper_put_constrained_whole_number_s(asn_per_outp_t *po, long v, int nbits); +int uper_put_constrained_whole_number_u(asn_per_outp_t *po, unsigned long v, int nbits); + +/* + * Put the length "n" to the Unaligned PER stream. + * This function returns the number of units which may be flushed + * in the next units saving iteration. + */ +ssize_t uper_put_length(asn_per_outp_t *po, size_t whole_length); + +/* + * Put the normally small length "n" to the Unaligned PER stream. + * Returns 0 or -1. + */ +int uper_put_nslength(asn_per_outp_t *po, size_t length); + +/* + * Put the normally small non-negative whole number. + */ +int uper_put_nsnnwn(asn_per_outp_t *po, int n); + +#ifdef __cplusplus +} +#endif + +#endif /* _PER_SUPPORT_H_ */ diff --git a/asn1_test_data/pqASN1.asn b/asn1_test_data/pqASN1.asn new file mode 100644 index 0000000..2313754 --- /dev/null +++ b/asn1_test_data/pqASN1.asn @@ -0,0 +1,48 @@ +PKIX1Implicit88 { iso(1) identified-organization(3) dod(6) internet(1) security(5) mechanisms(5) pkix(7) id-mod(0) id-pkix1-implicit(19) } +--PKIX1Explicit-2009 { iso(1) identified-organization(3) dod(6) internet(1) security(5) mechanisms(5) pkix(7) id-mod(0) id-mod-pkix1-explicit-02(51) } + +DEFINITIONS IMPLICIT TAGS ::= + +BEGIN + +-- EXPORTS ALL -- + +-- Borrowed from RFC5180 +AlgorithmIdentifier ::= SEQUENCE { + algorithm OBJECT IDENTIFIER, + parameters ANY DEFINED BY algorithm OPTIONAL } + + +EXPLICIT-COMPOSITE-SIGNATURE-ALGORITHM ::= CLASS { + &id OBJECT IDENTIFIER, + &firstAlgorithmParams ANY, + &secondAlgorithmParams ANY +} WITH SYNTAX { ID &id, FIRSTPARAMS &firstAlgorithmParams, SECONDPARAMS &secondAlgorithmParams } + + +-- For testing purposes we define the following pair of RSA and ECDSA, +-- but we intend for this draft to be extended to add additional pairs to this list +ExplicitCompositeAlgorithms EXPLICIT-COMPOSITE-SIGNATURE-ALGORITHM ::= { + {ID entrust.pkix.pq.sha256RSAwithsha384EDSA, null, ECParameters} +} + + +ExplicitCompositeSignatureValue ::= SEQUENCE { + firstSignatureValue OCTET STRING, + secondSignatureValue OCTET STRING +} + +ExplicitCompositeSignatureParams ::= SEQUENCE { + firstAlgorithmParams EXPLICIT-COMPOSITE-SIGNATURE-ALGORITHM.&firstAlgorithmParams ({ExplicitCompositeAlgorithms}) + secondAlgorithmParams EXPLICIT-COMPOSITE-SIGNATURE-ALGORITHM.&secondAlgorithmParams ({ExplicitCompositeAlgorithms}{@firstAlgorithmParams}) +} + +sa-CompositeSignature SIGNATURE-ALGORITHM ::= { + IDENTIFIER EXPLICIT-COMPOSITE-SIGNATURE-ALGORITHM.&id, + VALUE ExplicitCompositeSignatureValue + PARAMS ARE optional + PUBLIC-KEYS { pk-Composite } + SMIME-CAPS { IDENTIFIED BY id-alg-explicit } +} + +END diff --git a/asn1_test_data/pqASN1_compiling.asn b/asn1_test_data/pqASN1_compiling.asn new file mode 100644 index 0000000..76b4c37 --- /dev/null +++ b/asn1_test_data/pqASN1_compiling.asn @@ -0,0 +1,65 @@ +COMPOSITESIGALGS { 1 2 3 4 } + +DEFINITIONS IMPLICIT TAGS ::= + +BEGIN + +-- EXPORTS ALL -- + +-- faked + +id-entrust-sha256RSAwithsha384ECDSA OBJECT IDENTIFIER ::= { 1 2 3 4 } + +RSAParameters ::= NULL + +ECParameters ::= SEQUENCE { + a OCTET STRING, + b OCTET STRING, + p OCTET STRING, + n INTEGER +} + +-- Borrowed from RFC5180 +AlgorithmIdentifier ::= SEQUENCE { + algorithm OBJECT IDENTIFIER, + parameters ANY DEFINED BY algorithm OPTIONAL } + + +EXPLICIT-COMPOSITE-SIGNATURE-ALGORITHM ::= CLASS { + &id OBJECT IDENTIFIER, + &FirstAlgorithmParams OPTIONAL, + &SecondAlgorithmParams OPTIONAL +} WITH SYNTAX { + ID &id + FIRSTPARAMS &FirstAlgorithmParams + SECONDPARAMS &SecondAlgorithmParams +} + + +-- For testing purposes we define the following pair of RSA and ECDSA, +-- but we intend for this draft to be extended to add additional pairs to this list + +ExplicitCompositeAlgorithms EXPLICIT-COMPOSITE-SIGNATURE-ALGORITHM ::= { + {ID id-entrust-sha256RSAwithsha384ECDSA, FIRSTPARAMS RSAParameters, SECONDPARAMS ECParameters} +} + + +ExplicitCompositeSignatureValue ::= SEQUENCE { + firstSignatureValue OCTET STRING, + secondSignatureValue OCTET STRING +} + +ExplicitCompositeSignatureParams ::= SEQUENCE { + firstAlgorithmParams EXPLICIT-COMPOSITE-SIGNATURE-ALGORITHM.&FirstAlgorithmParams ({ExplicitCompositeAlgorithms}), + secondAlgorithmParams EXPLICIT-COMPOSITE-SIGNATURE-ALGORITHM.&SecondAlgorithmParams ({ExplicitCompositeAlgorithms}{@firstAlgorithmParams}) +} + +sa-CompositeSignature SIGNATURE-ALGORITHM ::= { + IDENTIFIER EXPLICIT-COMPOSITE-SIGNATURE-ALGORITHM.&id + VALUE ExplicitCompositeSignatureValue + PARAMS ARE optional + PUBLIC-KEYS { pk-Composite } + SMIME-CAPS { IDENTIFIED BY id-alg-explicit } +} + +END diff --git a/asn1_test_data/productExample.asn b/asn1_test_data/productExample.asn new file mode 100644 index 0000000..446dde0 --- /dev/null +++ b/asn1_test_data/productExample.asn @@ -0,0 +1,27 @@ +PKIX1Explicit-2009 { iso(1) identified-organization(3) dod(6) internet(1) security(5) mechanisms(5) pkix(7) id-mod(0) id-mod-pkix1-explicit-02(51) } + +DEFINITIONS AUTOMATIC TAGS ::= BEGIN + +PRODUCT ::= CLASS { + &code INTEGER (1..99999) UNIQUE, + &description VisibleString (SIZE (1..100)), + &price REAL +} WITH SYNTAX { CODE &code , DESCRIPTION &description , PRICE &price } + +ProductCatalog PRODUCT ::= { + {CODE 101, DESCRIPTION "iPhone v4", PRICE 250.00} | + {CODE 102, DESCRIPTION "Android Galaxy", PRICE 250.00} | + {CODE 103, DESCRIPTION "Win7 Nokia", PRICE 150.00} +} + +Item ::= SEQUENCE { + itemCode PRODUCT.&code ({ProductCatalog}), + itemDescription PRODUCT.&description ({ProductCatalog}{@itemCode}), + quantity INTEGER (1..1000), + unitPrice PRODUCT.&price ({ProductCatalog}{@itemCode}), + itemTotal REAL, + isTaxable BOOLEAN +} + + +END diff --git a/asn1_test_data/productExample1.asn b/asn1_test_data/productExample1.asn new file mode 100644 index 0000000..734d182 --- /dev/null +++ b/asn1_test_data/productExample1.asn @@ -0,0 +1,35 @@ +MyShopPurchaseOrders DEFINITIONS AUTOMATIC TAGS ::= BEGIN + +DATE ::= NULL + +PurchaseOrder ::= SEQUENCE { + dateOfOrder DATE, + customer CustomerInfo, + items ListOfItems +} + +CustomerInfo ::= SEQUENCE { + companyName VisibleString (SIZE (3..50)), + billingAddress Address, + contactPhone NumericString (SIZE (7..12)) +} + +Address::= SEQUENCE { + street VisibleString (SIZE (5 .. 50)) OPTIONAL, + city VisibleString (SIZE (2..30)), + state VisibleString (SIZE(2) ^ FROM ("A".."Z")), + zipCode NumericString (SIZE(5 | 9)) +} + +ListOfItems ::= SEQUENCE (SIZE (1..100)) OF Item + +Item ::= SEQUENCE { + itemCode INTEGER (1..99999), + color VisibleString ("Black" | "Blue" | "Brown"), + power INTEGER (110 | 220), + deliveryTime INTEGER (8..12 | 14..19), + quantity INTEGER (1..1000), + unitPrice REAL (1.00 .. 9999.00), + isTaxable BOOLEAN +} +END diff --git a/asn1_test_data/test.asn1 b/asn1_test_data/test.asn1 new file mode 100644 index 0000000..7214f97 --- /dev/null +++ b/asn1_test_data/test.asn1 @@ -0,0 +1,21 @@ +PKIX1Implicit88 { iso(1) identified-organization(3) dod(6) internet(1) security(5) mechanisms(5) pkix(7) id-mod(0) id-pkix1-implicit(19) } + +DEFINITIONS IMPLICIT TAGS ::= + +BEGIN + +-- EXPORTS ALL -- + +Test ::= SEQUENCE { + myValue INTEGER +} + +EXPLICIT-COMPOSITE-PUBLIC-KEY ::= CLASS { + &a INTEGER +} + +EXPLICIT-COMPOSITE-SIGNATURE-ALGORITHM ::= CLASS { + &a INTEGER +} + +END diff --git a/asn1_test_data/xer_decoder.c b/asn1_test_data/xer_decoder.c new file mode 100644 index 0000000..299a7c1 --- /dev/null +++ b/asn1_test_data/xer_decoder.c @@ -0,0 +1,368 @@ +/* + * Copyright (c) 2004, 2005 Lev Walkin . All rights reserved. + * Redistribution and modifications are permitted subject to BSD license. + */ +#include +#include +#include /* XER/XML parsing support */ + + +/* + * Decode the XER encoding of a given type. + */ +asn_dec_rval_t +xer_decode(asn_codec_ctx_t *opt_codec_ctx, asn_TYPE_descriptor_t *td, + void **struct_ptr, const void *buffer, size_t size) { + asn_codec_ctx_t s_codec_ctx; + + /* + * Stack checker requires that the codec context + * must be allocated on the stack. + */ + if(opt_codec_ctx) { + if(opt_codec_ctx->max_stack_size) { + s_codec_ctx = *opt_codec_ctx; + opt_codec_ctx = &s_codec_ctx; + } + } else { + /* If context is not given, be security-conscious anyway */ + memset(&s_codec_ctx, 0, sizeof(s_codec_ctx)); + s_codec_ctx.max_stack_size = ASN__DEFAULT_STACK_MAX; + opt_codec_ctx = &s_codec_ctx; + } + + /* + * Invoke type-specific decoder. + */ + return td->xer_decoder(opt_codec_ctx, td, struct_ptr, 0, buffer, size); +} + + + +struct xer__cb_arg { + pxml_chunk_type_e chunk_type; + size_t chunk_size; + const void *chunk_buf; + int callback_not_invoked; +}; + +static int +xer__token_cb(pxml_chunk_type_e type, const void *_chunk_data, size_t _chunk_size, void *key) { + struct xer__cb_arg *arg = (struct xer__cb_arg *)key; + arg->chunk_type = type; + arg->chunk_size = _chunk_size; + arg->chunk_buf = _chunk_data; + arg->callback_not_invoked = 0; + return -1; /* Terminate the XML parsing */ +} + +/* + * Fetch the next token from the XER/XML stream. + */ +ssize_t +xer_next_token(int *stateContext, const void *buffer, size_t size, pxer_chunk_type_e *ch_type) { + struct xer__cb_arg arg; + int new_stateContext = *stateContext; + ssize_t ret; + + arg.callback_not_invoked = 1; + ret = pxml_parse(&new_stateContext, buffer, size, xer__token_cb, &arg); + if(ret < 0) return -1; + if(arg.callback_not_invoked) { + assert(ret == 0); /* No data was consumed */ + *ch_type = PXER_WMORE; + return 0; /* Try again with more data */ + } else { + assert(arg.chunk_size); + assert(arg.chunk_buf == buffer); + } + + /* + * Translate the XML chunk types into more convenient ones. + */ + switch(arg.chunk_type) { + case PXML_TEXT: + *ch_type = PXER_TEXT; + break; + case PXML_TAG: + *ch_type = PXER_WMORE; + return 0; /* Want more */ + case PXML_TAG_END: + *ch_type = PXER_TAG; + break; + case PXML_COMMENT: + case PXML_COMMENT_END: + *ch_type = PXER_COMMENT; + break; + } + + *stateContext = new_stateContext; + return arg.chunk_size; +} + +#define CSLASH 0x2f /* '/' */ +#define LANGLE 0x3c /* '<' */ +#define RANGLE 0x3e /* '>' */ + +xer_check_tag_e +xer_check_tag(const void *buf_ptr, int size, const char *need_tag) { + const char *buf = (const char *)buf_ptr; + const char *end; + xer_check_tag_e ct = XCT_OPENING; + + if(size < 2 || buf[0] != LANGLE || buf[size-1] != RANGLE) { + if(size >= 2) + ASN_DEBUG("Broken XML tag: \"%c...%c\"", + buf[0], buf[size - 1]); + return XCT_BROKEN; + } + + /* + * Determine the tag class. + */ + if(buf[1] == CSLASH) { + buf += 2; /* advance past "" */ + ct = XCT_CLOSING; + if(size > 0 && buf[size-1] == CSLASH) + return XCT_BROKEN; /* */ + } else { + buf++; /* advance past "<" */ + size -= 2; /* strip "<" and ">" */ + if(size > 0 && buf[size-1] == CSLASH) { + ct = XCT_BOTH; + size--; /* One more, for "/" */ + } + } + + /* Sometimes we don't care about the tag */ + if(!need_tag || !*need_tag) + return (xer_check_tag_e)(XCT__UNK__MASK | ct); + + /* + * Determine the tag name. + */ + for(end = buf + size; buf < end; buf++, need_tag++) { + int b = *buf, n = *need_tag; + if(b != n) { + if(n == 0) { + switch(b) { + case 0x09: case 0x0a: case 0x0c: case 0x0d: + case 0x20: + /* "": whitespace is normal */ + return ct; + } + } + return (xer_check_tag_e)(XCT__UNK__MASK | ct); + } + if(b == 0) + return XCT_BROKEN; /* Embedded 0 in buf?! */ + } + if(*need_tag) + return (xer_check_tag_e)(XCT__UNK__MASK | ct); + + return ct; +} + + +#undef ADVANCE +#define ADVANCE(num_bytes) do { \ + size_t num = (num_bytes); \ + buf_ptr = ((const char *)buf_ptr) + num; \ + size -= num; \ + consumed_myself += num; \ + } while(0) + +#undef RETURN +#define RETURN(_code) do { \ + rval.code = _code; \ + rval.consumed = consumed_myself; \ + if(rval.code != RC_OK) \ + ASN_DEBUG("Failed with %d", rval.code); \ + return rval; \ + } while(0) + +#define XER_GOT_BODY(chunk_buf, chunk_size, size) do { \ + ssize_t converted_size = body_receiver \ + (struct_key, chunk_buf, chunk_size, \ + (size_t)chunk_size < size); \ + if(converted_size == -1) RETURN(RC_FAIL); \ + if(converted_size == 0 \ + && size == (size_t)chunk_size) \ + RETURN(RC_WMORE); \ + chunk_size = converted_size; \ + } while(0) +#define XER_GOT_EMPTY() do { \ + if(body_receiver(struct_key, 0, 0, size > 0) == -1) \ + RETURN(RC_FAIL); \ + } while(0) + +/* + * Generalized function for decoding the primitive values. + */ +asn_dec_rval_t +xer_decode_general(asn_codec_ctx_t *opt_codec_ctx, + asn_struct_ctx_t *ctx, /* Type decoder context */ + void *struct_key, + const char *xml_tag, /* Expected XML tag */ + const void *buf_ptr, size_t size, + int (*opt_unexpected_tag_decoder) + (void *struct_key, const void *chunk_buf, size_t chunk_size), + ssize_t (*body_receiver) + (void *struct_key, const void *chunk_buf, size_t chunk_size, + int have_more) + ) { + + asn_dec_rval_t rval; + ssize_t consumed_myself = 0; + + (void)opt_codec_ctx; + + /* + * Phases of XER/XML processing: + * Phase 0: Check that the opening tag matches our expectations. + * Phase 1: Processing body and reacting on closing tag. + */ + if(ctx->phase > 1) RETURN(RC_FAIL); + for(;;) { + pxer_chunk_type_e ch_type; /* XER chunk type */ + ssize_t ch_size; /* Chunk size */ + xer_check_tag_e tcv; /* Tag check value */ + + /* + * Get the next part of the XML stream. + */ + ch_size = xer_next_token(&ctx->context, buf_ptr, size, + &ch_type); + if(ch_size == -1) { + RETURN(RC_FAIL); + } else { + switch(ch_type) { + case PXER_WMORE: + RETURN(RC_WMORE); + case PXER_COMMENT: /* Got XML comment */ + ADVANCE(ch_size); /* Skip silently */ + continue; + case PXER_TEXT: + if(ctx->phase == 0) { + /* + * We have to ignore whitespace here, + * but in order to be forward compatible + * with EXTENDED-XER (EMBED-VALUES, #25) + * any text is just ignored here. + */ + } else { + XER_GOT_BODY(buf_ptr, ch_size, size); + } + ADVANCE(ch_size); + continue; + case PXER_TAG: + break; /* Check the rest down there */ + } + } + + assert(ch_type == PXER_TAG && size); + + tcv = xer_check_tag(buf_ptr, ch_size, xml_tag); + /* + * Phase 0: + * Expecting the opening tag + * for the type being processed. + * Phase 1: + * Waiting for the closing XML tag. + */ + switch(tcv) { + case XCT_BOTH: + if(ctx->phase) break; + /* Finished decoding of an empty element */ + XER_GOT_EMPTY(); + ADVANCE(ch_size); + ctx->phase = 2; /* Phase out */ + RETURN(RC_OK); + case XCT_OPENING: + if(ctx->phase) break; + ADVANCE(ch_size); + ctx->phase = 1; /* Processing body phase */ + continue; + case XCT_CLOSING: + if(!ctx->phase) break; + ADVANCE(ch_size); + ctx->phase = 2; /* Phase out */ + RETURN(RC_OK); + case XCT_UNKNOWN_BO: + /* + * Certain tags in the body may be expected. + */ + if(opt_unexpected_tag_decoder + && opt_unexpected_tag_decoder(struct_key, + buf_ptr, ch_size) >= 0) { + /* Tag's processed fine */ + ADVANCE(ch_size); + if(!ctx->phase) { + /* We are not expecting + * the closing tag anymore. */ + ctx->phase = 2; /* Phase out */ + RETURN(RC_OK); + } + continue; + } + /* Fall through */ + default: + break; /* Unexpected tag */ + } + + ASN_DEBUG("Unexpected XML tag (expected \"%s\")", xml_tag); + break; /* Dark and mysterious things have just happened */ + } + + RETURN(RC_FAIL); +} + + +size_t +xer_whitespace_span(const void *chunk_buf, size_t chunk_size) { + const char *p = (const char *)chunk_buf; + const char *pend = p + chunk_size; + + for(; p < pend; p++) { + switch(*p) { + /* X.693, #8.1.4 + * HORISONTAL TAB (9) + * LINE FEED (10) + * CARRIAGE RETURN (13) + * SPACE (32) + */ + case 0x09: case 0x0a: case 0x0d: case 0x20: + continue; + default: + break; + } + break; + } + return (p - (const char *)chunk_buf); +} + +/* + * This is a vastly simplified, non-validating XML tree skipper. + */ +int +xer_skip_unknown(xer_check_tag_e tcv, ber_tlv_len_t *depth) { + assert(*depth > 0); + switch(tcv) { + case XCT_BOTH: + case XCT_UNKNOWN_BO: + /* These negate each other. */ + return 0; + case XCT_OPENING: + case XCT_UNKNOWN_OP: + ++(*depth); + return 0; + case XCT_CLOSING: + case XCT_UNKNOWN_CL: + if(--(*depth) == 0) + return (tcv == XCT_CLOSING) ? 2 : 1; + return 0; + default: + return -1; + } +} diff --git a/asn1_test_data/xer_decoder.h b/asn1_test_data/xer_decoder.h new file mode 100644 index 0000000..301b613 --- /dev/null +++ b/asn1_test_data/xer_decoder.h @@ -0,0 +1,106 @@ +/*- + * Copyright (c) 2004 Lev Walkin . All rights reserved. + * Redistribution and modifications are permitted subject to BSD license. + */ +#ifndef _XER_DECODER_H_ +#define _XER_DECODER_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct asn_TYPE_descriptor_s; /* Forward declaration */ + +/* + * The XER decoder of any ASN.1 type. May be invoked by the application. + */ +asn_dec_rval_t xer_decode(struct asn_codec_ctx_s *opt_codec_ctx, + struct asn_TYPE_descriptor_s *type_descriptor, + void **struct_ptr, /* Pointer to a target structure's pointer */ + const void *buffer, /* Data to be decoded */ + size_t size /* Size of data buffer */ + ); + +/* + * Type of the type-specific XER decoder function. + */ +typedef asn_dec_rval_t (xer_type_decoder_f)(asn_codec_ctx_t *opt_codec_ctx, + struct asn_TYPE_descriptor_s *type_descriptor, + void **struct_ptr, + const char *opt_mname, /* Member name */ + const void *buf_ptr, size_t size + ); + +/******************************* + * INTERNALLY USEFUL FUNCTIONS * + *******************************/ + +/* + * Generalized function for decoding the primitive values. + * Used by more specialized functions, such as OCTET_STRING_decode_xer_utf8 + * and others. This function should not be used by applications, as its API + * is subject to changes. + */ +asn_dec_rval_t xer_decode_general(asn_codec_ctx_t *opt_codec_ctx, + asn_struct_ctx_t *ctx, /* Type decoder context */ + void *struct_key, /* Treated as opaque pointer */ + const char *xml_tag, /* Expected XML tag name */ + const void *buf_ptr, size_t size, + int (*opt_unexpected_tag_decoder) + (void *struct_key, const void *chunk_buf, size_t chunk_size), + ssize_t (*body_receiver) + (void *struct_key, const void *chunk_buf, size_t chunk_size, + int have_more) + ); + + +/* + * Fetch the next XER (XML) token from the stream. + * The function returns the number of bytes occupied by the chunk type, + * returned in the _ch_type. The _ch_type is only set (and valid) when + * the return value is >= 0. + */ + typedef enum pxer_chunk_type { + PXER_WMORE, /* Chunk type is not clear, more data expected. */ + PXER_TAG, /* Complete XER tag */ + PXER_TEXT, /* Plain text between XER tags */ + PXER_COMMENT /* A comment, may be part of */ + } pxer_chunk_type_e; +ssize_t xer_next_token(int *stateContext, + const void *buffer, size_t size, pxer_chunk_type_e *_ch_type); + +/* + * This function checks the buffer against the tag name is expected to occur. + */ + typedef enum xer_check_tag { + XCT_BROKEN = 0, /* The tag is broken */ + XCT_OPENING = 1, /* This is the tag */ + XCT_CLOSING = 2, /* This is the tag */ + XCT_BOTH = 3, /* This is the tag */ + XCT__UNK__MASK = 4, /* Mask of everything unexpected */ + XCT_UNKNOWN_OP = 5, /* Unexpected tag */ + XCT_UNKNOWN_CL = 6, /* Unexpected tag */ + XCT_UNKNOWN_BO = 7 /* Unexpected tag */ + } xer_check_tag_e; +xer_check_tag_e xer_check_tag(const void *buf_ptr, int size, + const char *need_tag); + +/* + * Get the number of bytes consisting entirely of XER whitespace characters. + * RETURN VALUES: + * >=0: Number of whitespace characters in the string. + */ +size_t xer_whitespace_span(const void *chunk_buf, size_t chunk_size); + +/* + * Skip the series of anticipated extensions. + */ +int xer_skip_unknown(xer_check_tag_e tcv, ber_tlv_len_t *depth); + +#ifdef __cplusplus +} +#endif + +#endif /* _XER_DECODER_H_ */ diff --git a/asn1_test_data/xer_encoder.c b/asn1_test_data/xer_encoder.c new file mode 100644 index 0000000..4606575 --- /dev/null +++ b/asn1_test_data/xer_encoder.c @@ -0,0 +1,67 @@ +/*- + * Copyright (c) 2003, 2004 Lev Walkin . All rights reserved. + * Redistribution and modifications are permitted subject to BSD license. + */ +#include +#include +#include + +/* + * The XER encoder of any type. May be invoked by the application. + */ +asn_enc_rval_t +xer_encode(asn_TYPE_descriptor_t *td, void *sptr, + enum xer_encoder_flags_e xer_flags, + asn_app_consume_bytes_f *cb, void *app_key) { + asn_enc_rval_t er, tmper; + const char *mname; + size_t mlen; + int xcan = (xer_flags & XER_F_CANONICAL) ? 1 : 2; + + if(!td || !sptr) goto cb_failed; + + mname = td->xml_tag; + mlen = strlen(mname); + + ASN__CALLBACK3("<", 1, mname, mlen, ">", 1); + + tmper = td->xer_encoder(td, sptr, 1, xer_flags, cb, app_key); + if(tmper.encoded == -1) return tmper; + + ASN__CALLBACK3("\n", xcan); + + er.encoded = 4 + xcan + (2 * mlen) + tmper.encoded; + + ASN__ENCODED_OK(er); +cb_failed: + ASN__ENCODE_FAILED; +} + +/* + * This is a helper function for xer_fprint, which directs all incoming data + * into the provided file descriptor. + */ +static int +xer__print2fp(const void *buffer, size_t size, void *app_key) { + FILE *stream = (FILE *)app_key; + + if(fwrite(buffer, 1, size, stream) != size) + return -1; + + return 0; +} + +int +xer_fprint(FILE *stream, asn_TYPE_descriptor_t *td, void *sptr) { + asn_enc_rval_t er; + + if(!stream) stream = stdout; + if(!td || !sptr) + return -1; + + er = xer_encode(td, sptr, XER_F_BASIC, xer__print2fp, stream); + if(er.encoded == -1) + return -1; + + return fflush(stream); +} diff --git a/asn1_test_data/xer_encoder.h b/asn1_test_data/xer_encoder.h new file mode 100644 index 0000000..055e73c --- /dev/null +++ b/asn1_test_data/xer_encoder.h @@ -0,0 +1,59 @@ +/*- + * Copyright (c) 2004 Lev Walkin . All rights reserved. + * Redistribution and modifications are permitted subject to BSD license. + */ +#ifndef _XER_ENCODER_H_ +#define _XER_ENCODER_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct asn_TYPE_descriptor_s; /* Forward declaration */ + +/* Flags used by the xer_encode() and (*xer_type_encoder_f), defined below */ +enum xer_encoder_flags_e { + /* Mode of encoding */ + XER_F_BASIC = 0x01, /* BASIC-XER (pretty-printing) */ + XER_F_CANONICAL = 0x02 /* Canonical XER (strict rules) */ +}; + +/* + * The XER encoder of any type. May be invoked by the application. + */ +asn_enc_rval_t xer_encode(struct asn_TYPE_descriptor_s *type_descriptor, + void *struct_ptr, /* Structure to be encoded */ + enum xer_encoder_flags_e xer_flags, + asn_app_consume_bytes_f *consume_bytes_cb, + void *app_key /* Arbitrary callback argument */ + ); + +/* + * The variant of the above function which dumps the BASIC-XER (XER_F_BASIC) + * output into the chosen file pointer. + * RETURN VALUES: + * 0: The structure is printed. + * -1: Problem printing the structure. + * WARNING: No sensible errno value is returned. + */ +int xer_fprint(FILE *stream, struct asn_TYPE_descriptor_s *td, void *sptr); + +/* + * Type of the generic XER encoder. + */ +typedef asn_enc_rval_t (xer_type_encoder_f)( + struct asn_TYPE_descriptor_s *type_descriptor, + void *struct_ptr, /* Structure to be encoded */ + int ilevel, /* Level of indentation */ + enum xer_encoder_flags_e xer_flags, + asn_app_consume_bytes_f *consume_bytes_cb, /* Callback */ + void *app_key /* Arbitrary callback argument */ + ); + +#ifdef __cplusplus +} +#endif + +#endif /* _XER_ENCODER_H_ */ diff --git a/asn1_test_data/xer_support.c b/asn1_test_data/xer_support.c new file mode 100644 index 0000000..36b4bfb --- /dev/null +++ b/asn1_test_data/xer_support.c @@ -0,0 +1,227 @@ +/* + * Copyright (c) 2003, 2004 X/IO Labs, xiolabs.com. + * Copyright (c) 2003, 2004, 2005 Lev Walkin . + * All rights reserved. + * Redistribution and modifications are permitted subject to BSD license. + */ +#include +#include + +/* Parser states */ +typedef enum { + ST_TEXT, + ST_TAG_START, + ST_TAG_BODY, + ST_TAG_QUOTE_WAIT, + ST_TAG_QUOTED_STRING, + ST_TAG_UNQUOTED_STRING, + ST_COMMENT_WAIT_DASH1, /* ""[0] */ + ST_COMMENT_CLO_RT /* "-->"[1] */ +} pstate_e; + +static const int +_charclass[256] = { + 0,0,0,0,0,0,0,0, 0,1,1,0,1,1,0,0, + 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, + 1,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, + 2,2,2,2,2,2,2,2, 2,2,0,0,0,0,0,0, /* 01234567 89 */ + 0,3,3,3,3,3,3,3, 3,3,3,3,3,3,3,3, /* ABCDEFG HIJKLMNO */ + 3,3,3,3,3,3,3,3, 3,3,3,0,0,0,0,0, /* PQRSTUVW XYZ */ + 0,3,3,3,3,3,3,3, 3,3,3,3,3,3,3,3, /* abcdefg hijklmno */ + 3,3,3,3,3,3,3,3, 3,3,3,0,0,0,0,0 /* pqrstuvw xyz */ +}; +#define WHITESPACE(c) (_charclass[(unsigned char)(c)] == 1) +#define ALNUM(c) (_charclass[(unsigned char)(c)] >= 2) +#define ALPHA(c) (_charclass[(unsigned char)(c)] == 3) + +/* Aliases for characters, ASCII/UTF-8 */ +#define EXCLAM 0x21 /* '!' */ +#define CQUOTE 0x22 /* '"' */ +#define CDASH 0x2d /* '-' */ +#define CSLASH 0x2f /* '/' */ +#define LANGLE 0x3c /* '<' */ +#define CEQUAL 0x3d /* '=' */ +#define RANGLE 0x3e /* '>' */ +#define CQUEST 0x3f /* '?' */ + +/* Invoke token callback */ +#define TOKEN_CB_CALL(type, _ns, _current_too, _final) do { \ + int _ret; \ + pstate_e ns = _ns; \ + ssize_t _sz = (p - chunk_start) + _current_too; \ + if (!_sz) { \ + /* Shortcut */ \ + state = _ns; \ + break; \ + } \ + _ret = cb(type, chunk_start, _sz, key); \ + if(_ret < _sz) { \ + if(_current_too && _ret == -1) \ + state = ns; \ + goto finish; \ + } \ + chunk_start = p + _current_too; \ + state = ns; \ + } while(0) + +#define TOKEN_CB(_type, _ns, _current_too) \ + TOKEN_CB_CALL(_type, _ns, _current_too, 0) + +#define PXML_TAG_FINAL_CHUNK_TYPE PXML_TAG_END +#define PXML_COMMENT_FINAL_CHUNK_TYPE PXML_COMMENT_END + +#define TOKEN_CB_FINAL(_type, _ns, _current_too) \ + TOKEN_CB_CALL( _type ## _FINAL_CHUNK_TYPE , _ns, _current_too, 1) + +/* + * Parser itself + */ +ssize_t pxml_parse(int *stateContext, const void *xmlbuf, size_t size, pxml_callback_f *cb, void *key) { + pstate_e state = (pstate_e)*stateContext; + const char *chunk_start = (const char *)xmlbuf; + const char *p = chunk_start; + const char *end = p + size; + + for(; p < end; p++) { + int C = *(const unsigned char *)p; + switch(state) { + case ST_TEXT: + /* + * Initial state: we're in the middle of some text, + * or just have started. + */ + if (C == LANGLE) + /* We're now in the tag, probably */ + TOKEN_CB(PXML_TEXT, ST_TAG_START, 0); + break; + case ST_TAG_START: + if (ALPHA(C) || (C == CSLASH)) + state = ST_TAG_BODY; + else if (C == EXCLAM) + state = ST_COMMENT_WAIT_DASH1; + else + /* + * Not characters and not whitespace. + * Must be something like "3 < 4". + */ + TOKEN_CB(PXML_TEXT, ST_TEXT, 1);/* Flush as data */ + break; + case ST_TAG_BODY: + switch(C) { + case RANGLE: + /* End of the tag */ + TOKEN_CB_FINAL(PXML_TAG, ST_TEXT, 1); + break; + case LANGLE: + /* + * The previous tag wasn't completed, but still + * recognized as valid. (Mozilla-compatible) + */ + TOKEN_CB_FINAL(PXML_TAG, ST_TAG_START, 0); + break; + case CEQUAL: + state = ST_TAG_QUOTE_WAIT; + break; + } + break; + case ST_TAG_QUOTE_WAIT: + /* + * State after the equal sign ("=") in the tag. + */ + switch(C) { + case CQUOTE: + state = ST_TAG_QUOTED_STRING; + break; + case RANGLE: + /* End of the tag */ + TOKEN_CB_FINAL(PXML_TAG, ST_TEXT, 1); + break; + default: + if(!WHITESPACE(C)) + /* Unquoted string value */ + state = ST_TAG_UNQUOTED_STRING; + } + break; + case ST_TAG_QUOTED_STRING: + /* + * Tag attribute's string value in quotes. + */ + if(C == CQUOTE) { + /* Return back to the tag state */ + state = ST_TAG_BODY; + } + break; + case ST_TAG_UNQUOTED_STRING: + if(C == RANGLE) { + /* End of the tag */ + TOKEN_CB_FINAL(PXML_TAG, ST_TEXT, 1); + } else if(WHITESPACE(C)) { + /* Return back to the tag state */ + state = ST_TAG_BODY; + } + break; + case ST_COMMENT_WAIT_DASH1: + if(C == CDASH) { + state = ST_COMMENT_WAIT_DASH2; + } else { + /* Some ordinary tag. */ + state = ST_TAG_BODY; + } + break; + case ST_COMMENT_WAIT_DASH2: + if(C == CDASH) { + /* Seen "<--" */ + state = ST_COMMENT; + } else { + /* Some ordinary tag */ + state = ST_TAG_BODY; + } + break; + case ST_COMMENT: + if(C == CDASH) { + state = ST_COMMENT_CLO_DASH2; + } + break; + case ST_COMMENT_CLO_DASH2: + if(C == CDASH) { + state = ST_COMMENT_CLO_RT; + } else { + /* This is not an end of a comment */ + state = ST_COMMENT; + } + break; + case ST_COMMENT_CLO_RT: + if(C == RANGLE) { + TOKEN_CB_FINAL(PXML_COMMENT, ST_TEXT, 1); + } else if(C == CDASH) { + /* Maintain current state, still waiting for '>' */ + } else { + state = ST_COMMENT; + } + break; + } /* switch(*ptr) */ + } /* for() */ + + /* + * Flush the partially processed chunk, state permitting. + */ + if(p - chunk_start) { + switch (state) { + case ST_COMMENT: + TOKEN_CB(PXML_COMMENT, state, 0); + break; + case ST_TEXT: + TOKEN_CB(PXML_TEXT, state, 0); + break; + default: break; /* a no-op */ + } + } + +finish: + *stateContext = (int)state; + return chunk_start - (const char *)xmlbuf; +} + diff --git a/asn1_test_data/xer_support.h b/asn1_test_data/xer_support.h new file mode 100644 index 0000000..8b01944 --- /dev/null +++ b/asn1_test_data/xer_support.h @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2003, 2004 X/IO Labs, xiolabs.com. + * Copyright (c) 2003, 2004 Lev Walkin . All rights reserved. + * Redistribution and modifications are permitted subject to BSD license. + */ +#ifndef _XER_SUPPORT_H_ +#define _XER_SUPPORT_H_ + +#include /* Platform-specific types */ + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Types of data transferred to the application. + */ +typedef enum { + PXML_TEXT, /* Plain text between XML tags. */ + PXML_TAG, /* A tag, starting with '<'. */ + PXML_COMMENT, /* An XML comment, including "". */ + /* + * The following chunk types are reported if the chunk + * terminates the specified XML element. + */ + PXML_TAG_END, /* Tag ended */ + PXML_COMMENT_END /* Comment ended */ +} pxml_chunk_type_e; + +/* + * Callback function that is called by the parser when parsed data is + * available. The _opaque is the pointer to a field containing opaque user + * data specified in pxml_create() call. The chunk type is _type and the text + * data is the piece of buffer identified by _bufid (as supplied to + * pxml_feed() call) starting at offset _offset and of _size bytes size. + * The chunk is NOT '\0'-terminated. + */ +typedef int (pxml_callback_f)(pxml_chunk_type_e _type, + const void *_chunk_data, size_t _chunk_size, void *_key); + +/* + * Parse the given buffer as it were a chunk of XML data. + * Invoke the specified callback each time the meaninful data is found. + * This function returns number of bytes consumed from the bufer. + * It will always be lesser than or equal to the specified _size. + * The next invocation of this function must account the difference. + */ +ssize_t pxml_parse(int *_stateContext, const void *_buf, size_t _size, + pxml_callback_f *cb, void *_key); + +#ifdef __cplusplus +} +#endif + +#endif /* _XER_SUPPORT_H_ */ diff --git a/draft-ounsworth-pq-composite-sigs.html b/draft-ounsworth-pq-composite-sigs.html new file mode 100644 index 0000000..51d86de --- /dev/null +++ b/draft-ounsworth-pq-composite-sigs.html @@ -0,0 +1,974 @@ + + + + + + + Composite Signatures For Use In Internet PKI + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
LAMPSM. Ounsworth
Internet-DraftEntrust
Intended status: Standards TrackM. Pala
Expires: January 13, 2022CableLabs
July 12, 2021
+ +

Composite Signatures For Use In Internet PKI
+ draft-ounsworth-pq-composite-sigs-05

+ +

Abstract

+

With the widespread adoption of post-quantum cryptography will come the need for an entity to possess multiple public keys on different cryptographic algorithms. Since the trustworthiness of individual post-quantum algorithms is at question, a multi-key cryptographic operation will need to be performed in such a way that breaking it requires breaking each of the component algorithms individually. This requires defining new structures for holding composite signature data.

+

This document defines the structures CompositeSignatureValue, and CompositeParams, which are sequences of the respective structure for each component algorithm. This document also defines processes for generating and verifying composite signatures. This document makes no assumptions about what the component algorithms are, provided that their algorithm identifiers and signature generation and verification processes are defined.

+

Status of This Memo

+

This Internet-Draft is submitted in full conformance with the provisions of BCP 78 and BCP 79.

+

Internet-Drafts are working documents of the Internet Engineering Task Force (IETF). Note that other groups may also distribute working documents as Internet-Drafts. The list of current Internet-Drafts is at https://datatracker.ietf.org/drafts/current/.

+

Internet-Drafts are draft documents valid for a maximum of six months and may be updated, replaced, or obsoleted by other documents at any time. It is inappropriate to use Internet-Drafts as reference material or to cite them other than as "work in progress."

+

This Internet-Draft will expire on January 13, 2022.

+

Copyright Notice

+

Copyright (c) 2021 IETF Trust and the persons identified as the document authors. All rights reserved.

+

This document is subject to BCP 78 and the IETF Trust's Legal Provisions Relating to IETF Documents (https://trustee.ietf.org/license-info) in effect on the date of publication of this document. Please review these documents carefully, as they describe your rights and restrictions with respect to this document. Code Components extracted from this document must include Simplified BSD License text as described in Section 4.e of the Trust Legal Provisions and are provided without warranty as described in the Simplified BSD License.

+ + +
+

Table of Contents

+ + +

+1. Introduction +

+

During the transition to post-quantum cryptography, there will be uncertainty as to the strength of cryptographic algorithms; we will no longer fully trust traditional cryptography such as RSA, Diffie-Hellman, DSA and their elliptic curve variants, but we will also not fully trust their post-quantum replacements until they have had sufficient scrutiny. Unlike previous cryptographic algorithm migrations, the choice of when to migrate and which algorithms to migrate to, is not so clear. Even after the migration period, it may be advantageous for an entity's cryptographic identity to be composed of multiple public-key algorithms.

+

The deployment of composite signatures using post-quantum algorithms will face two challenges

+

+ +
    +
  • Algorithm strength uncertainty: During the transition period, some post-quantum signature and encryption algorithms will not be fully trusted, while also the trust in legacy public key algorithms will start to erode. A relying party may learn some time after deployment that a public key algorithm has become untrustworthy, but in the interim, they may not know which algorithm an adversary has compromised.
  • +
  • Backwards compatibility: During the transition period, post-quantum algorithms will not be supported by all clients.
  • +
+

This document provides a mechanism to address algorithm strength uncertainty by building on ~~ reference draft-ounsworth-pq-composite-pubkeys ~~ by providing formats for encoding multiple signature values into existing public signature fields, as well as the process for validating a composite signature. Backwards compatibility is addressed via the Composite-OR mechanism described herein.

+

This document is intended for general applicability anywhere that digital signatures are used within PKIX and CMS structures.

+

+1.1. Terminology +

+

The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in BCP 14 [RFC2119] [RFC8174] when, and only when, they appear in all capitals, as shown here.

+

The following terms are used in this document:

+

ALGORITHM: An information object class for identifying the type of cryptographic operation to be performed. This document is primarily concerned with algorithms for producing digital signatures.

+

BER: Basic Encoding Rules (BER) as defined in [X.690].

+

COMPONENT ALGORITHM: A single basic algorithm which is contained within a composite algorithm.

+

COMPOSITE ALGORITHM: An algorithm which is a sequence of two or more component algorithms, as defined in Section 2.

+

DER: Distinguished Encoding Rules as defined in [X.690].

+

LEGACY: For the purposes of this document, a legacy key or signature is a non-composite key or signature.

+

PUBLIC / PRIVATE KEY: The public and private portion of an asymmetric cryptographic key, making no assumptions about which algorithm.

+

SIGNATURE: A digital cryptographic signature, making no assumptions about which algorithm.

+

+2. Composite Identifiers and Structures +

+

In order for signatures to be composed of multiple algorithms, we define encodings consisting of a sequence of signature primitives (aka "component algorithms") such that these structures can be used as a drop-in replacement for existing signature fields such as those found in PKCS#10 [RFC2986], CMP [RFC4210], X.509 [RFC5280], CMS [RFC5652].

+

This section defines the following structures:

+

+ +
    +
  • The id-alg-composite is an AlgorithmIdentifier identifying a composite signature object.

    The sa-CompositeSignature AlgorithmIdentifier and the corresponding CompositeParams identify the algorithm(s) used in a composite signature.
  • +
  • The CompositeSignatureValue, carries a sequence of signatures that are generated by a CompositePrivateKey, and can be verified with the corresponding CompositePublicKey.
  • +
+

EDNOTE 2: the choice to define composite algorithm parameters as a sequence inside the existing fields avoids the exponential proliferation of OIDs that are needed for each combination of signature algorithms in other schemes for achieving multi-key certificates. This scheme also naturally extends from 2-keypair to n-keypair keys and certificates.

+

EDNOTE 2a: We have heard community feedback that the ASN.1 structures presented here are too flexible in that allow arbitrary combinations of an arbitrary number of signature algorithms. The feedback is that this is too much of a "footgun" for implementors and sysadmins. We are working on an alternative formulation using ASN.1 information object classes that allow for compiling explicit pairs of algorithmIDs. We would love community feedback on which approach is preferred. See slide 30 of this presentation: https://datatracker.ietf.org/meeting/interim-2021-lamps-01/materials/slides-interim-2021-lamps-01-sessa-position-presentation-by-mike-ounsworth-00.pdf

+

+2.1. Algorithm Identifier +

+

The following object identifier is used for identifying a composite signature. Additional encoding information is provided below for each of these objects.

+
+id-alg-composite OBJECT IDENTIFIER ::= {
+    iso(1)  identified-organization(3) dod(6) internet(1) private(4)
+    enterprise(1) OpenCA(18227) Algorithms(2) id-alg-composite(1) }
+
+

EDNOTE 3: this is a temporary OID for the purposes of prototyping. We are requesting IANA to assign a permanent OID, see Section 5.

+

+2.2. Composite Keys +

+

A Composite signature MUST be associated with a Composite public key as defined in ~~ reference draft-ounsworth-pq-composite-pubkey ~~.

+

+2.2.1. Key Usage Bits +

+

For protocols such as X.509 [RFC5280] that specify key usage along with the public key, then the composite public key associated with a composite signature MUST have a signing-type key usage.

+

If the keyUsage extension is present in a Certification Authority (CA) certificate that indicates id-composite-key, then any combination of the following values MAY be present:

+
+digitalSignature;
+nonRepudiation;
+keyCertSign; and
+cRLSign.
+
+

If the keyUsage extension is present in an End Entity (EE) certificate that indicates id-composite-key, then any combination of the following values MAY be present:

+
+digitalSignature; and
+nonRepudiation;
+
+

+2.3. Composite Signature +

+

The ASN.1 algorithm object for a composite signature is:

+
+sa-CompositeSignature SIGNATURE-ALGORITHM ::= {
+    IDENTIFIER id-alg-composite
+    VALUE CompositeSignatureValue
+    PARAMS TYPE CompositeParams ARE required
+    PUBLIC-KEYS { pk-Composite }
+    SMIME-CAPS { IDENTIFIED BY id-alg-composite } }
+}
+
+

The following algorithm parameters MUST be included:

+
+CompositeParams ::= SEQUENCE SIZE (2..MAX) OF AlgorithmIdentifier
+
+

The signature's CompositeParams sequence MUST contain the same component algorithms listed in the same order as in the associated CompositePrivateKey and CompositePublicKey.

+

The output of the composite signature algorithm is the DER encoding of the following structure:

+
+CompositeSignatureValue ::= SEQUENCE SIZE (2..MAX) OF BIT STRING
+
+

Where each BIT STRING within the SEQUENCE is a signature value produced by one of the component keys. It MUST contain one signature value produced by each component algorithm, and in the same order as in the associated CompositeParams object.

+

The choice of SEQUENCE OF BIT STRING, rather than for example a single BIT STRING containing the concatenated signature values, is to gracefully handle variable-length signature values by taking advantage of ASN.1's built-in length fields.

+

+2.4. Encoding Rules +

+

Many protocol specifications will require that composite signature data structures be represented by an octet string or bit string.

+

When an octet string is required, the DER encoding of the composite data structure SHALL be used directly.

+

When a bit string is required, the octets of the DER encoded composite data structure SHALL be used as the bits of the bit string, with the most significant bit of the first octet becoming the first bit, and so on, ending with the least significant bit of the last octet becoming the last bit of the bit string.

+

In the interests of simplicity and avoiding compatibility issues, implementations that parse these structures MAY accept both BER and DER.

+

+3. Composite Signature Processes +

+

This section specifies the processes for generating and verifying composite signatures.

+

This process addresses algorithm strength uncertainty by providing the verifier with parallel signatures from all the component signature algorithms; thus breaking the composite signature would require breaking all of the component signatures.

+

+3.1. Composite Signature Generation Process +

+

Generation of a composite signature involves applying each component algorithm's signature process to the input message according to its specification, and then placing each component signature value into the CompositeSignatureValue structure defined in Section 2.3.

+

The following process is used to generate composite signature values.

+
+Input:
+     K1, K2, .., Kn     Private keys for the n component signature
+                        algorithms, a CompositePrivateKey
+     M                  Message to be signed, an octet string
+
+Output:
+     S                  The signatures, a CompositeSignatureValue
+
+Signature Generation Process:
+   1. Generate the n component signatures independently,
+      according to their algorithm specifications.
+
+        for i := 1 to n
+            Si := Sign( Ki, M )
+
+   2. Encode each component signature S1, S2, .., Sn into a BIT STRING
+      according to its algorithm specification.
+
+        S ::= Sequence { S1, S2, .., Sn }
+
+   3. Output S
+
+

Since recursive composite public keys are disallowed in ~~ Reference draft-ounsworth-pq-composite-pubkeys sec-composite-pub-keys ~~, no component signature may itself be a composite; ie the signature generation process MUST fail if one of the private keys K1, K2, .., Kn is a composite with the OID id-alg-composite.

+

A composite signature MUST produce and include in the output a signature value for every component key in the corresponding CompositePrivateKey. For this mode, please see Composite-OR in section Section 3.2.

+

+3.2. Composite-OR Signature Generation Process +

+

EDNOTE: This section was written with the intention of keeping the primary Composite OID reserved for the simple and strict mode; if you want to do either a simple OR, or a custom policy then we have given a different OID. We are still debating whether this is useful to specify at issuing time, or whether this is adding needless complexity to the draft.

+

If the algorithm ID of the public key associated with this signature is id-composite-or-key then the signer MAY use only a subset of the component keys and therefore produce fewer signatures than the number of component keys.

+

Composite-OR signature generation uses the same structures and algorithms as Composite, with the difference that the signature generation process may emit a null instead of a signature value in step 1 for one or more component algorithms. A Composite-OR signature MUST NOT be entirely null; it must contain at least one valid signature.

+

The design intent of this mode is to support migration scenarios where an end entity has been issued keys on algorithms that either itself or the peer with which it is communicating do not (yet) support. This design allows for both the mode where the signer omits signatures that it knows its peer cannot process in order to save bandwidth and performance, and the mode where it includes all component signatures and allows the verifier to choose how many to verify. The latter is RECOMMENDED for signatures that need both sort-term backwards compatibility as well as long-term security.

+

EDNOTE: Do we want to allow a Composite-OR with only a single signature to produce non-composite signatureAlgorithm and signatureValua as per [RFC5280]? Advantages: bandwidth savings of an extra OID and some sequences with one element. Disadvantages: ambiguous whether a signature is traditional or composite until you look at the corresponding public key.

+

+3.3. Composite Signature Verification Process +

+

Verification of a composite signature involves applying each component algorithm's verification process according to its specification.

+

In the absence of an application profile specifying otherwise, compliant applications MUST output "Valid signature" (true) if and only if all component signatures were successfully validated, and "Invalid signature" (false) otherwise.

+

The following process is used to perform this verification.

+
+Input:
+     P    Signer's composite public key
+     M    Message whose signature is to be verified, an octet string
+     S    Composite Signature to be verified
+     A    Composite Algorithm identifier
+
+Output:
+    Validity      "Valid signature" (true) if the composite signature
+                  is valid, "Invalid signature" (false) otherwise.
+
+Signature Verification Procedure::
+   1. Parse P, S, A into the component public keys, signatures,
+      and algorithm identifiers
+
+      P1, P2, .., Pn := Desequence( P )
+      S1, S2, .., Sn := Desequence( S )
+      A1, A2, .., An := Desequence( A )
+
+    If Error during Desequencing, or the three sequences have
+    different numbers of elements, or any of the public keys P1, P2, .., Pn or
+    algorithm identifiers A1, A2, .., An are composite with the OID
+    id-alg-composite then output "Invalid signature" and stop.
+
+   2. Check each component signature individually, according to its
+       algorithm specification.
+       If any fail, then the entire signature validation fails.
+
+     for i := 1 to n
+          if not verify( Pi, M, Si ), then
+            output "Invalid signature"
+
+      if all succeeded, then
+        output "Valid signature"
+
+

Since recursive composite public keys are disallowed in ~~ Reference draft-ounsworth-pq-composite-keys sec-composite-pub-keys ~~, no component signature may be composite; ie the signature verification procedure MUST fail if any of the public keys P1, P2, .., Pn or algorithm identifiers A1, A2, .., An are composite with the OID id-alg-composite.

+

+3.4. Composite-OR Signature Verification +

+

EDNOTE: This section was written with the intention of keeping the primary Composite OID reserved for the simple and strict mode; if you want to do either a simple OR, or a custom policy then we have given a different OID. We are still debating whether this is useful to specify at issuing time, or whether this is adding needless complexity to the draft.

+

When the public key associated with the signature being verified has algorithm id-composite-or-key, then an alternate verification processes MAY be used, at the discretion of the implementor. In this section we provide some examples of alternate verification processes.

+

If the signature is a traditional (non-composite) algorithm and value or a composite signature with a single component, then it MAY be considered valid if it verifies under one of the component keys.

+

If the signature is composite, then the implementor MAY implement policy for which combinations are acceptable.

+

EDNOTE: Does this mean Composite-OR end entity certificates need to be issued by a PKI that is marked as Composite-OR all the way to the top so that verifiers that do not support all the algorithms don't fail? Need to think more about the security implications of allowing a Composite-or in an end entity cert implicitely turning all Composite algIDs into Composite-or algIDs in its cert chain.

+

EDNOTE: Do we need to specify the semantics of verifying an "n of m" subset signature? I suspect that specifying this in general will be a rat's nest of edge cases, so I propose to "leave this to the implementor".

+

+3.4.1. Composite-OR Legacy Mode +

+

The Composite-OR Legacy Mode is provided to facilitate migration by allowing existing PKI entities (including root CAs, intermediate CAs, and end entities) to have their existing keys re-certified inside a Composite-OR structure along with Post-Quantum keys, and for signatures made by that key prior to the migration to remain valid. Note that Composite-OR Legacy Mode is only provided for signature verification, and not for signature generation; legacy signatures SHOULD NOT be produced from a Composite key.

+

EDNOTE: to further solidify this, we could add a clause that Legacy Mode signatures are to fail if the signature was produced after notBefore date of the Composite-OR certificate?

+

In Composite-OR Legacy Mode, a legacy signature algorithm and legacy signature value MAY be validated against a Composite-OR public key. The legacy signature algorithm is to be interpreted by the verifier as a sa-CompositeSignature with CompositeParams in the following way:

+
+CompositeParams {legacyAlgorithmIdentifier, null, .., null}
+
+

with the correct number of nulls to match the Composite-OR public key that the signature is being verified against. For the purposes of a signature validation under Composite-OR Legacy Mode, a null AlgorithmIdentifier is considered to be a match for the corresponding algorithm in the Composite-OR public key.

+

The legacy signature value is to be interpreted by the verifier as a sa-CompositeSignature with CompositeParams in the following way:

+
+CompositeSignatureValue  {legacySignatureValue, null, .., null}
+
+

with the correct number of nulls to match the Composite-OR public key that the signature is being verified against. The verification algorithm in section Section 3.4 applies.

+

Security consideration: when implementing Composite-OR Legacy Mode, it is important to catch the edge case of {null, null, .., null} for both AlgorithmIdentifier and SignatureValue and return Invalid Signature.

+

It is RECOMMENDED that Composite-OR Legacy Mode be implemented as an optional mode in the verifier that can be enabled or disabled by runtime configuration or policy.

+

EDNOTE: the signing public key is often identified in the signed document by issuer+serialNumber or by an SKI containing a hash of the public key value. Might need X.509 extensions identifying the SKI of the legacy cert it's replacing?

+

+4. In Practice +

+

This section addresses practical issues of how this draft affects other protocols and standards.

+

~~~ BEGIN EDNOTE 10~~~

+

EDNOTE 10: Possible topics to address:

+

+ +
    +
  • The size of these certs and cert chains.
  • +
  • In particular, implications for (large) composite keys / signatures / certs on the handshake stages of TLS and IKEv2.
  • +
  • If a cert in the chain is a composite cert then does the whole chain need to be of composite Certs?
  • +
  • We could also explain that the root CA cert does not have to be of the same algorithms. The root cert SHOULD NOT be transferred in the authentication exchange to save transport overhead and thus it can be different than the intermediate and leaf certs.
  • +
  • We could talk about overhead (size and processing).
  • +
  • We could also discuss backwards compatibility.
  • +
  • We could include a subsection about implementation considerations.
  • +
+

~~~ END EDNOTE 10~~~

+

+4.1. Cryptographic protocols +

+

This section talks about how protocols like (D)TLS and IKEv2 are affected by this specifications. It will not attempt to solve all these problems, but it will explain the rationale, how things will work and what open problems need to be solved. Obvious issues that need to be discussed.

+

+ +
    +
  • How does the protocol declare support for composite signatures? TLS has hooks for declaring support for specific signature algorithms, however it would need to be extended, because the client would need to declare support for both the composite infrastructure, as well as for the various component signature algorithms.
  • +
  • How does the protocol use the multiple keys. The obvious way would be to have the server sign using its composite public key; is this sufficient.
  • +
  • Overhead; including certificate size, signature processing time, and size of the signature.
  • +
  • How to deal with crypto protocols that use public key encryption algorithms; this document only lists how to work with signature algorithms. Encoding composite public keys is straightforward; encoding composite ciphertexts is less so - we decided to put that off to another draft.
  • +
+

+5. IANA Considerations +

+

The ASN.1 module OID is TBD. The id-alg-composite OID is to be assigned by IANA. The authors suggest that IANA assign an OID on the id-pkix arc:

+
+id-alg-composite OBJECT IDENTIFIER ::= {
+    iso(1) identified-organization(3) dod(6) internet(1) security(5)
+    mechanisms(5) pkix(7) algorithms(6) composite(??) }
+
+

+6. Security Considerations +

+

+6.1. Policy for Deprecated and Acceptable Algorithms +

+

Traditionally, a public key, certificate, or signature contains a single cryptographic algorithm. If and when an algorithm becomes deprecated (for example, RSA-512, or SHA1), it is obvious that structures using that algorithm are implicitly revoked.

+

In the composite model this is less obvious since a single public key, certificate, or signature may contain a mixture of deprecated and non-deprecated algorithms. Moreover, implementers may decide that certain cryptographic algorithms have complementary security properties and are acceptable in combination even though neither algorithm is acceptable by itself.

+

Specifying a modified verification algorithm to handle these situations is beyond the scope of this draft, but could be desirable as the subject of an application profile document, or to be up to the discretion of implementers.

+
+2. Check policy to see whether A1, A2, ..., An constitutes a valid
+   combination of algorithms.
+
+   if not checkPolicy(A1, A2, ..., An), then
+     output "Invalid signature"
+
+

While intentionally not specified in this document, implementors should put careful thought into implementing a meaningfull policy mechinism within the context of their signature verification engines, for example only algorithms that provide similar security levels should be combined together.

+

+7. Appendices +

+

+7.1. ASN.1 Module +

+
+<CODE STARTS>
+
+Composite-Signatures-2019
+  { TBD }
+
+DEFINITIONS IMPLICIT TAGS ::= BEGIN
+
+EXPORTS ALL;
+
+IMPORTS
+  PUBLIC-KEY, SIGNATURE-ALGORITHM
+    FROM AlgorithmInformation-2009  -- RFC 5912 [X509ASN1]
+      { iso(1) identified-organization(3) dod(6) internet(1)
+        security(5) mechanisms(5) pkix(7) id-mod(0)
+        id-mod-algorithmInformation-02(58) }
+
+  SubjectPublicKeyInfo
+    FROM PKIX1Explicit-2009
+      { iso(1) identified-organization(3) dod(6) internet(1)
+        security(5) mechanisms(5) pkix(7) id-mod(0)
+        id-mod-pkix1-explicit-02(51) }
+
+  OneAsymmetricKey
+    FROM AsymmetricKeyPackageModuleV1
+      { iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1)
+        pkcs-9(9) smime(16) modules(0)
+        id-mod-asymmetricKeyPkgV1(50) } ;
+
+--
+-- Object Identifiers
+--
+
+id-alg-composite OBJECT IDENTIFIER ::= { TBD }
+
+--
+-- Public Key
+--
+
+pk-Composite PUBLIC-KEY ::= {
+    IDENTIFIER id-alg-composite
+    KEY CompositePublicKey
+    PARAMS ARE absent
+    PRIVATE-KEY CompositePrivateKey
+}
+
+CompositePublicKey ::= SEQUENCE SIZE (2..MAX) OF SubjectPublicKeyInfo
+
+CompositePrivateKey ::= SEQUENCE SIZE (2..MAX) OF OneAsymmetricKey
+
+--
+-- Signature Algorithm
+--
+
+sa-CompositeSignature SIGNATURE-ALGORITHM ::= {
+    IDENTIFIER id-alg-composite
+    VALUE CompositeSignatureValue
+    PARAMS TYPE CompositeParams ARE required
+    PUBLIC-KEYS { pk-Composite }
+    SMIME-CAPS { IDENTIFIED BY id-alg-composite } }
+
+CompositeParams ::= SEQUENCE SIZE (2..MAX) OF AlgorithmIdentifier
+
+CompositeSignatureValue ::= SEQUENCE SIZE (2..MAX) OF BIT STRING
+
+END
+
+<CODE ENDS>
+
+
+

+7.2. Intellectual Property Considerations +

+

The following IPR Disclosure relates to this draft:

+

https://datatracker.ietf.org/ipr/3588/

+

+8. Contributors and Acknowledgements +

+

This document incorporates contributions and comments from a large group of experts. The Editors would especially like to acknowledge the expertise and tireless dedication of the following people, who attended many long meetings and generated millions of bytes of electronic mail and VOIP traffic over the past year in pursuit of this document:

+

John Gray (Entrust), Serge Mister (Entrust), Scott Fluhrer (Cisco Systems), Panos Kampanakis (Cisco Systems), Daniel Van Geest (ISARA), Tim Hollebeek (Digicert), and Francois Rousseau.

+

We are grateful to all, including any contributors who may have been inadvertently omitted from this list.

+

This document borrows text from similar documents, including those referenced below. Thanks go to the authors of those documents. "Copying always makes things easier and less error prone" - [RFC8411].

+

+8.1. Making contributions +

+

Additional contributions to this draft are weclome. Please see the working copy of this draft at, as well as open issues at:

+

https://github.com/EntrustCorporation/draft-ounsworth-composite-sigs

+

+9. Normative References

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
[RFC2119] +Bradner, S., "Key words for use in RFCs to Indicate Requirement Levels", BCP 14, RFC 2119, DOI 10.17487/RFC2119, March 1997.
[RFC2986] +Nystrom, M. and B. Kaliski, "PKCS #10: Certification Request Syntax Specification Version 1.7", RFC 2986, DOI 10.17487/RFC2986, November 2000.
[RFC4210] +Adams, C., Farrell, S., Kause, T. and T. Mononen, "Internet X.509 Public Key Infrastructure Certificate Management Protocol (CMP)", RFC 4210, DOI 10.17487/RFC4210, September 2005.
[RFC5280] +Cooper, D., Santesson, S., Farrell, S., Boeyen, S., Housley, R. and W. Polk, "Internet X.509 Public Key Infrastructure Certificate and Certificate Revocation List (CRL) Profile", RFC 5280, DOI 10.17487/RFC5280, May 2008.
[RFC5652] +Housley, R., "Cryptographic Message Syntax (CMS)", STD 70, RFC 5652, DOI 10.17487/RFC5652, September 2009.
[RFC8174] +Leiba, B., "Ambiguity of Uppercase vs Lowercase in RFC 2119 Key Words", BCP 14, RFC 8174, DOI 10.17487/RFC8174, May 2017.
[RFC8411] +Schaad, J. and R. Andrews, "IANA Registration for the Cryptographic Algorithm Object Identifier Range", RFC 8411, DOI 10.17487/RFC8411, August 2018.
[X.690] +ITU-T, "Information technology - ASN.1 encoding Rules: Specification of Basic Encoding Rules (BER), Canonical Encoding Rules (CER) and Distinguished Encoding Rules (DER)", ISO/IEC 8825-1:2015, November 2015.
+

Authors' Addresses

+
+
+ + Mike Ounsworth + + + Entrust Limited + + 2500 Solandt Road -- Suite 100 + + + Ottawa, Ontario, + + K2K 3G5 + + Canada + + EMail: mike.ounsworth@entrust.com + +
+
+
+ + Massimiliano Pala + + + CableLabs + + + + + + + + + + EMail: director@openca.org + +
+
+ + + diff --git a/draft-ounsworth-pq-composite-sigs.mkd b/draft-ounsworth-pq-composite-sigs.mkd index 2344adf..2bdf1e6 100644 --- a/draft-ounsworth-pq-composite-sigs.mkd +++ b/draft-ounsworth-pq-composite-sigs.mkd @@ -1,14 +1,14 @@ --- -title: Composite Keys and Signatures For Use In Internet PKI -abbrev: PQ Composite Certs +title: Composite Signatures For Use In Internet PKI +abbrev: PQ Composite Sigs # -docname: draft-ounsworth-pq-composite-sigs-03 +docname: draft-ounsworth-pq-composite-sigs-05 # # # # - + area: Security wg: LAMPS kw: Internet-Draft @@ -22,15 +22,15 @@ pi: # can use array (if all yes) or hash here author: - - ins: M. Ounsworth (Editor) + ins: M. Ounsworth name: Mike Ounsworth - org: Entrust Datacard Limited - abbrev: Entrust Datacard - street: 1000 Innovation Drive + org: Entrust Limited + abbrev: Entrust + street: 2500 Solandt Road – Suite 100 city: Ottawa, Ontario country: Canada - code: K2K 1E3 - email: mike.ounsworth@entrustdatacard.com + code: K2K 3G5 + email: mike.ounsworth@entrust.com - ins: M. Pala @@ -39,15 +39,11 @@ author: email: director@openca.org normative: - RFC1421: RFC2119: RFC2986: RFC4210: - RFC4648: RFC5280: RFC5652: - RFC5958: - RFC7468: RFC8174: RFC8411: X.690: @@ -62,8 +58,6 @@ normative: # informative: - I-D.draft-truskovsky-lamps-pq-hybrid-x509-01: - I-D.draft-pala-composite-crypto-00: --- abstract -With the widespread adoption of post-quantum cryptography will come the need for an entity to possess multiple public keys on different cryptographic algorithms. Since the trustworthiness of individual post-quantum algorithms is at question, a multi-key cryptographic operation will need to be performed in such a way that breaking it requires breaking each of the component algorithms individually. This requires defining new structures for holding composite public keys and composite signature data. +With the widespread adoption of post-quantum cryptography will come the need for an entity to possess multiple public keys on different cryptographic algorithms. Since the trustworthiness of individual post-quantum algorithms is at question, a multi-key cryptographic operation will need to be performed in such a way that breaking it requires breaking each of the component algorithms individually. This requires defining new structures for holding composite signature data. -This document defines the structures CompositePublicKey, CompositeSignatureValue, and CompositeParams, which are sequences of the respective structure for each component algorithm. This document also defines algorithms for generating and verifying composite signatures. This document makes no assumptions about what the component algorithms are, provided that their algorithm identifiers and signature generation and verification algorithms are defined. +This document defines the structures CompositeSignatureValue, and CompositeParams, which are sequences of the respective structure for each component algorithm. This document also defines processes for generating and verifying composite signatures. This document makes no assumptions about what the component algorithms are, provided that their algorithm identifiers and signature generation and verification processes are defined. @@ -88,14 +82,14 @@ This document defines the structures CompositePublicKey, CompositeSignatureValue During the transition to post-quantum cryptography, there will be uncertainty as to the strength of cryptographic algorithms; we will no longer fully trust traditional cryptography such as RSA, Diffie-Hellman, DSA and their elliptic curve variants, but we will also not fully trust their post-quantum replacements until they have had sufficient scrutiny. Unlike previous cryptographic algorithm migrations, the choice of when to migrate and which algorithms to migrate to, is not so clear. Even after the migration period, it may be advantageous for an entity's cryptographic identity to be composed of multiple public-key algorithms. -The deployment of composite public keys and composite signatures using post-quantum algorithms will face two challenges +The deployment of composite signatures using post-quantum algorithms will face two challenges - Algorithm strength uncertainty: During the transition period, some post-quantum signature and encryption algorithms will not be fully trusted, while also the trust in legacy public key algorithms will start to erode. A relying party may learn some time after deployment that a public key algorithm has become untrustworthy, but in the interim, they may not know which algorithm an adversary has compromised. - Backwards compatibility: During the transition period, post-quantum algorithms will not be supported by all clients. -This document provides a mechanism to address algorithm strength uncertainty by providing formats for encoding multiple public keys, private keys and signature values into existing public key and signature fields, as well as an algorithm for validating a composite signature. The issue of backwards compatibility is left open to be addressed in separate draft(s). +This document provides a mechanism to address algorithm strength uncertainty by building on ~~ reference draft-ounsworth-pq-composite-pubkeys ~~ by providing formats for encoding multiple signature values into existing public signature fields, as well as the process for validating a composite signature. Backwards compatibility is addressed via the Composite-OR mechanism described herein. -This document is intended for general applicability anywhere that public key structures or digital signatures are used within PKIX structures. While the CompositePublicKey structure defined herein is equally applicable to asymmetric encryption keys, this document is intentionally restricted to signatures. +This document is intended for general applicability anywhere that digital signatures are used within PKIX and CMS structures. @@ -110,8 +104,7 @@ ALGORITHM: An information object class for identifying the type of cryptographic operation to be performed. This document is primarily concerned with algorithms for producing digital - signatures, though the public key structure could just as - easily hold encryption keys. + signatures. BER: Basic Encoding Rules (BER) as defined in [X.690]. @@ -121,19 +114,19 @@ COMPONENT ALGORITHM: composite algorithm. COMPOSITE ALGORITHM: - An algorithm which is a sequence of one or more component + An algorithm which is a sequence of two or more component algorithms, as defined in {{sec-composite-structs}}. DER: Distinguished Encoding Rules as defined in [X.690]. +LEGACY: For the purposes of this document, a legacy key or signature is a + non-composite key or signature. + PUBLIC / PRIVATE KEY: The public and private portion of an asymmetric cryptographic key, making no assumptions about which algorithm. -PRIMITIVE PUBLIC KEY / SIGNATURE: - A public key or signature object of a non-composite algorithm - type. SIGNATURE: A digital cryptographic signature, making no assumptions @@ -141,24 +134,25 @@ SIGNATURE: -# Composite Structures {#sec-composite-structs} -In order for public keys and signatures to be composed of multiple algorithms, we define encodings consisting of a sequence of public key and signature primitives (aka "component algorithms") such that these structures can be used as a drop-in replacement for existing public key or signature fields such as those found in PKCS#10 [RFC2986], CMP [RFC4210], X.509 [RFC5280], CMS [RFC5652]. - -This section defines the following structures: +# Composite Identifiers and Structures {#sec-composite-structs} - - The id-alg-composite is an OID identifying a composite public key or signature object. +In order for signatures to be composed of multiple algorithms, we define encodings consisting of a sequence of signature primitives (aka "component algorithms") such that these structures can be used as a drop-in replacement for existing signature fields such as those found in PKCS#10 [RFC2986], CMP [RFC4210], X.509 [RFC5280], CMS [RFC5652]. - - The CompositePublicKey carries all the public keys associated with an identity within a single public key structure. +This section defines the following structures: - - The CompositePrivateKey carries all the private keys associated with an identity within a single private key structure. + - The id-alg-composite is an AlgorithmIdentifier identifying a composite signature object. + + The sa-CompositeSignature AlgorithmIdentifier and the corresponding CompositeParams identify the algorithm(s) used in a composite signature. - The CompositeSignatureValue, carries a sequence of signatures that are generated by a CompositePrivateKey, and can be verified with the corresponding CompositePublicKey. EDNOTE 2: the choice to define composite algorithm parameters as a sequence inside the existing fields avoids the exponential proliferation of OIDs that are needed for each combination of signature algorithms in other schemes for achieving multi-key certificates. This scheme also naturally extends from 2-keypair to n-keypair keys and certificates. +EDNOTE 2a: We have heard community feedback that the ASN.1 structures presented here are too flexible in that allow arbitrary combinations of an arbitrary number of signature algorithms. The feedback is that this is too much of a "footgun" for implementors and sysadmins. We are working on an alternative formulation using ASN.1 information object classes that allow for compiling explicit pairs of algorithmIDs. We would love community feedback on which approach is preferred. See slide 30 of this presentation: https://datatracker.ietf.org/meeting/interim-2021-lamps-01/materials/slides-interim-2021-lamps-01-sessa-position-presentation-by-mike-ounsworth-00.pdf + ## Algorithm Identifier {#sec-alg-identifier} -The same algorithm identifier is used for identifying a public key, a private key, and a signature. Additional encoding information is provided below for each of these objects. +The following object identifier is used for identifying a composite signature. Additional encoding information is provided below for each of these objects. ~~~ asn.1 id-alg-composite OBJECT IDENTIFIER ::= { @@ -170,82 +164,30 @@ EDNOTE 3: this is a temporary OID for the purposes of prototyping. We are reques ## Composite Keys -A composite key is a single key object that performs an atomic signature or verification operation, using its encapsulated sequence of component keys. +A Composite signature MUST be associated with a Composite public key as defined in ~~ reference draft-ounsworth-pq-composite-pubkey ~~. -The ASN.1 algorithm object for composite public and private keys is: - -~~~ asn.1 -pk-Composite PUBLIC-KEY ::= { - IDENTIFIER id-alg-composite - KEY CompositePublicKey - PARAMS ARE absent - CERT-KEY-USAGE - { digitalSignature, nonRepudiation, keyCertSign, cRLSign } - PRIVATE-KEY CompositePrivateKey -} -~~~ -{: artwork-name="CompositeAlgorithmObject-asn.1-structures"} - - -EDNOTE 4: the authors are currently unsure whether the params should be absent (ie this structure simply says "I am a composite algorithm"), or used to duplicate some amount of information about what the component algoritms are. See {{sec-composite-pub-keys}} for a longer ENDOTE on this. ### Key Usage Bits -The intended application for the key is indicated in the keyUsage certificate extension and defined in the CERT-KEY-USAGE field of pk-Composite. +For protocols such as X.509 [RFC5280] that specify key usage along with the public key, then the composite public key associated with a composite signature MUST have a signing-type key usage. -If the keyUsage extension is present in an end-entity certificate that indicates id-alg-composite, then the keyUsage extension MUST contain one or both of the following values: -~~~ - nonRepudiation; and - digitalSignature. -~~~ -{: artwork-name="Cert-Key-Usage-1"} - -If the keyUsage extension is present in a certification authority certificate that indicates id-alg-composite, then the keyUsage extension MUST contain one or more of the following values: +If the keyUsage extension is present in a Certification Authority (CA) certificate that indicates id-composite-key, then any combination of the following values MAY be present: ~~~ - nonRepudiation; - digitalSignature; - keyCertSign; and - cRLSign. +digitalSignature; +nonRepudiation; +keyCertSign; and +cRLSign. ~~~ -{: artwork-name="Cert-Key-Usage-2"} - -As this draft only covers composite signatures, the key usage bits specified here apply to all component keys within a composite key. - -## Composite Public Key {#sec-composite-pub-keys} -Composite public key data is represented by the following structure: +If the keyUsage extension is present in an End Entity (EE) certificate that indicates id-composite-key, then any combination of the following values MAY be present: -~~~ asn.1 -CompositePublicKey ::= SEQUENCE SIZE (1..MAX) OF SubjectPublicKeyInfo ~~~ -{: artwork-name="CompositePublicKey-asn.1-structures"} - - -The corresponding AlgorithmIdentifier for a composite public key MUST use the id-alg-composite object identifier, defined in {{sec-alg-identifier}}, and the parameters field MUST be absent. - -A composite public key MUST contain at least one component public key. - -A CompositePublicKey MUST NOT contain a component public key which itself describes a composite key; ie recursive CompositePublicKeys are not allowed - -Each element of a CompositePublicKey is a SubjectPublicKeyInfo object one of the component public keys. When the CompositePublicKey must be provided in octet string or bit string format, the data structure is encoded as specified in {{sec-encoding-rules}}. - - -## Composite Private Key - -The composite private key data is represented by the following structure: - -~~~ asn.1 -CompositePrivateKey ::= SEQUENCE SIZE (1..MAX) OF OneAsymmetricKey +digitalSignature; and +nonRepudiation; ~~~ -{: artwork-name="CompositePrivateKey-asn.1-structures"} -Each element is a OneAsymmetricKey [RFC5958] object for a component private key. - -The corresponding AlgorithmIdentifier for a composite private key MUST use the id-alg-composite object identifier, and the parameters field MUST be absent. - -A CompositePrivateKey MUST contain at least one component private key, and they MUST be in the same order as in the corresponding CompositePublicKey. ## Composite Signature {#sec-composite-sig-structs} @@ -262,10 +204,10 @@ sa-CompositeSignature SIGNATURE-ALGORITHM ::= { } ~~~ -The id-alg-composite object identifier MUST be used to identify when a signature has been created by a CompositePrivateKey and following algorithm parameters MUST be included: +The following algorithm parameters MUST be included: ~~~ asn.1 -CompositeParams ::= SEQUENCE SIZE (1..MAX) OF AlgorithmIdentifier +CompositeParams ::= SEQUENCE SIZE (2..MAX) OF AlgorithmIdentifier ~~~ {: artwork-name="CompositeSignatureParams-asn.1-structures"} @@ -274,18 +216,18 @@ The signature's CompositeParams sequence MUST contain the same component algorit The output of the composite signature algorithm is the DER encoding of the following structure: ~~~ asn.1 -CompositeSignatureValue ::= SEQUENCE SIZE (1..MAX) OF BIT STRING +CompositeSignatureValue ::= SEQUENCE SIZE (2..MAX) OF BIT STRING ~~~ {: artwork-name="composite-sig-asn.1"} Where each BIT STRING within the SEQUENCE is a signature value produced by one of the component keys. It MUST contain one signature value produced by each component algorithm, and in the same order as in the associated CompositeParams object. -The choice of `SEQUENCE OF BIT STRING`, rather than for example a single BIT STRING containing the concatenated signature values, is to gracefully handle variable-length signature values by taking advantage of ASN.1's build-in length fields. +The choice of `SEQUENCE OF BIT STRING`, rather than for example a single BIT STRING containing the concatenated signature values, is to gracefully handle variable-length signature values by taking advantage of ASN.1's built-in length fields. ## Encoding Rules {#sec-encoding-rules} -Many protocol specifications will require that the composite public key, composite private key, and composite signature data structures be represented by an octet string or bit string. +Many protocol specifications will require that composite signature data structures be represented by an octet string or bit string. When an octet string is required, the DER encoding of the composite data structure SHALL be used directly. @@ -293,17 +235,17 @@ When a bit string is required, the octets of the DER encoded composite data stru In the interests of simplicity and avoiding compatibility issues, implementations that parse these structures MAY accept both BER and DER. -# Composite Signature Algorithm {#sec-composite-signature-algorithm} +# Composite Signature Processes {#sec-composite-signature-algorithm} -This section specifies the algorithms for generating and verifying composite signatures. +This section specifies the processes for generating and verifying composite signatures. -This algorithm addresses algorithm strength uncertainty by providing the verifier with parallel signatures from all the component signature algorithms; thus breaking the composite signature would require breaking all of the component signatures. +This process addresses algorithm strength uncertainty by providing the verifier with parallel signatures from all the component signature algorithms; thus breaking the composite signature would require breaking all of the component signatures. -## Composite Signature Generation {#sec-comp-sig-gen} +## Composite Signature Generation Process {#sec-comp-sig-gen} -Generation of a composite signature involves applying each component algorithm's signature routine to the input message according to its specification, and then placing each component signature value into the CompositeSignatureValue structure defined in {{sec-composite-sig-structs}}. +Generation of a composite signature involves applying each component algorithm's signature process to the input message according to its specification, and then placing each component signature value into the CompositeSignatureValue structure defined in {{sec-composite-sig-structs}}. -The following algorithm is used to generate composite signature values. +The following process is used to generate composite signature values. ~~~ Input: @@ -314,7 +256,7 @@ Input: Output: S The signatures, a CompositeSignatureValue -Signature Generation Procedure: +Signature Generation Process: 1. Generate the n component signatures independently, according to their algorithm specifications. @@ -322,7 +264,7 @@ Signature Generation Procedure: Si := Sign( Ki, M ) 2. Encode each component signature S1, S2, .., Sn into a BIT STRING - according to its algorithm specification.2020-01-21g + according to its algorithm specification. S ::= Sequence { S1, S2, .., Sn } @@ -331,18 +273,29 @@ Signature Generation Procedure: {: artwork-name="alg-composite-sig-gen"} -Since recursive composite public keys are disallowed in {{sec-composite-pub-keys}}, no component signature may itself be a composite; ie the signature generation routine MUST fail if one of the private keys K1, K2, .., Kn is a composite with the OID id-alg-composite. +Since recursive composite public keys are disallowed in ~~ Reference draft-ounsworth-pq-composite-pubkeys sec-composite-pub-keys ~~, no component signature may itself be a composite; ie the signature generation process MUST fail if one of the private keys K1, K2, .., Kn is a composite with the OID id-alg-composite. + +A composite signature MUST produce and include in the output a signature value for every component key in the corresponding CompositePrivateKey. For this mode, please see Composite-OR in section {{sec-comp-or-sig-gen}}. + +## Composite-OR Signature Generation Process {#sec-comp-or-sig-gen} + +EDNOTE: This section was written with the intention of keeping the primary Composite OID reserved for the simple and strict mode; if you want to do either a simple OR, or a custom policy then we have given a different OID. We are still debating whether this is useful to specify at issuing time, or whether this is adding needless complexity to the draft. -A composite signature MUST produce and include in the output a signature value for every component key in the corresponding CompositePrivateKey. While it may be tempting to allow a signer to produce a signature with a subset of their keys, the complexity of securely verifying such a "subset signature" is high and out of scope for this document. +If the algorithm ID of the public key associated with this signature is id-composite-or-key then the signer MAY use only a subset of the component keys and therefore produce fewer signatures than the number of component keys. +Composite-OR signature generation uses the same structures and algorithms as Composite, with the difference that the signature generation process may emit a null instead of a signature value in step 1 for one or more component algorithms. A Composite-OR signature MUST NOT be entirely null; it must contain at least one valid signature. -## Composite Signature Verification {#sec-comp-sig-verify} +The design intent of this mode is to support migration scenarios where an end entity has been issued keys on algorithms that either itself or the peer with which it is communicating do not (yet) support. This design allows for both the mode where the signer omits signatures that it knows its peer cannot process in order to save bandwidth and performance, and the mode where it includes all component signatures and allows the verifier to choose how many to verify. The latter is RECOMMENDED for signatures that need both sort-term backwards compatibility as well as long-term security. -Verification of a composite signature involves applying each component algorithm's verification routine according to its specification. +EDNOTE: Do we want to allow a Composite-OR with only a single signature to produce non-composite signatureAlgorithm and signatureValua as per [RFC5280]? Advantages: bandwidth savings of an extra OID and some sequences with one element. Disadvantages: ambiguous whether a signature is traditional or composite until you look at the corresponding public key. + +## Composite Signature Verification Process {#sec-comp-sig-verify} + +Verification of a composite signature involves applying each component algorithm's verification process according to its specification. In the absence of an application profile specifying otherwise, compliant applications MUST output "Valid signature" (true) if and only if all component signatures were successfully validated, and "Invalid signature" (false) otherwise. -The following algorithm is used to perform this verification. +The following process is used to perform this verification. @@ -383,10 +336,53 @@ Signature Verification Procedure:: ~~~ {: artwork-name="alg-sig-verif"} -Since recursive composite public keys are disallowed in {{sec-composite-pub-keys}}, no component signature may be composite; ie the signature verification procedure MUST fail if any of the public keys P1, P2, .., Pn or algorithm identifiers A1, A2, .., An are composite with the OID id-alg-composite. +Since recursive composite public keys are disallowed in ~~ Reference draft-ounsworth-pq-composite-keys sec-composite-pub-keys ~~, no component signature may be composite; ie the signature verification procedure MUST fail if any of the public keys P1, P2, .., Pn or algorithm identifiers A1, A2, .., An are composite with the OID id-alg-composite. + + +## Composite-OR Signature Verification {#sec-comp-or-sig-verify} + +EDNOTE: This section was written with the intention of keeping the primary Composite OID reserved for the simple and strict mode; if you want to do either a simple OR, or a custom policy then we have given a different OID. We are still debating whether this is useful to specify at issuing time, or whether this is adding needless complexity to the draft. -It is expected that some use-cases for algorithm migration or high performance will require verifiers to succeed when only a subset of the component algorithms have been verified. Defining this verification behaviour is out of scope for this document, and falls to an application profile. +When the public key associated with the signature being verified has algorithm id-composite-or-key, then an alternate verification processes MAY be used, at the discretion of the implementor. In this section we provide some examples of alternate verification processes. +If the signature is a traditional (non-composite) algorithm and value or a composite signature with a single component, then it MAY be considered valid if it verifies under one of the component keys. + +If the signature is composite, then the implementor MAY implement policy for which combinations are acceptable. + + +EDNOTE: Does this mean Composite-OR end entity certificates need to be issued by a PKI that is marked as Composite-OR all the way to the top so that verifiers that do not support all the algorithms don't fail? Need to think more about the security implications of allowing a Composite-or in an end entity cert implicitely turning all Composite algIDs into Composite-or algIDs in its cert chain. + +EDNOTE: Do we need to specify the semantics of verifying an "n of m" subset signature? I suspect that specifying this in general will be a rat's nest of edge cases, so I propose to "leave this to the implementor". + + +### Composite-OR Legacy Mode + +The Composite-OR Legacy Mode is provided to facilitate migration by allowing existing PKI entities (including root CAs, intermediate CAs, and end entities) to have their existing keys re-certified inside a Composite-OR structure along with Post-Quantum keys, and for signatures made by that key prior to the migration to remain valid. Note that Composite-OR Legacy Mode is only provided for signature verification, and not for signature generation; legacy signatures SHOULD NOT be produced from a Composite key. + +EDNOTE: to further solidify this, we could add a clause that Legacy Mode signatures are to fail if the signature was produced after notBefore date of the Composite-OR certificate? + +In Composite-OR Legacy Mode, a legacy signature algorithm and legacy signature value MAY be validated against a Composite-OR public key. The legacy signature algorithm is to be interpreted by the verifier as a sa-CompositeSignature with CompositeParams in the following way: + +~~~ asn.1 +CompositeParams {legacyAlgorithmIdentifier, null, .., null} +~~~ + +with the correct number of nulls to match the Composite-OR public key that the signature is being verified against. For the purposes of a signature validation under Composite-OR Legacy Mode, a null AlgorithmIdentifier is considered to be a match for the corresponding algorithm in the Composite-OR public key. + +The legacy signature value is to be interpreted by the verifier as a sa-CompositeSignature with CompositeParams in the following way: + +~~~ asn.1 +CompositeSignatureValue {legacySignatureValue, null, .., null} +~~~ + +with the correct number of nulls to match the Composite-OR public key that the signature is being verified against. The verification algorithm in section {{sec-comp-or-sig-verify}} applies. + + +Security consideration: when implementing Composite-OR Legacy Mode, it is important to catch the edge case of {null, null, .., null} for both AlgorithmIdentifier and SignatureValue and return Invalid Signature. + +It is RECOMMENDED that Composite-OR Legacy Mode be implemented as an optional mode in the verifier that can be enabled or disabled by runtime configuration or policy. + +EDNOTE: the signing public key is often identified in the signed document by issuer+serialNumber or by an SKI containing a hash of the public key value. Might need X.509 extensions identifying the SKI of the legacy cert it's replacing? @@ -409,26 +405,10 @@ EDNOTE 10: Possible topics to address: ~~~ END EDNOTE 10~~~ -## PEM Storage of Composite Private Keys - -CompositePrivateKeys can be encoded to the PEM format by placing a CompositePrivateKey into the privateKey field of a PrivateKeyInfo or OneAsymmetricKey object, and then applying the PEM encoding rules as defined in [RFC7468] section 10 and 11 for plaintext and encrypted private keys, respectively. - - -## Asymmetric Key Packages (CMS) - -The Cryptographic Message Syntax (CMS), as defined in [RFC5652], can be used to digitally sign, digest, authenticate, or encrypt the asymmetric key format content type. - -When encoding composite private keys, the privateKeyAlgorithm in the OneAsymmetricKey SHALL be set to id-alg-composite. -The parameters of the privateKeyAlgorithm SHALL be a sequence of AlgorithmIdentifier objects, each of which are encoded according to the rules defined for each of the different keys in the composite private key. - -The value of the privateKey field in the OneAsymmetricKey SHALL be set to the DER encoding of the SEQUENCE of private key values that make up the composite key. The number and order of elements in the sequence SHALL be the same as identified in the sequence of parameters in the privateKeyAlgorithm. - -The value of the publicKey (if present) SHALL be set to the DER encoding of the corresponding CompositePublicKey. If this field is present, the number and order of component keys MUST be the same as identified in the sequence of parameters in the privateKeyAlgorithm. - -The value of the attributes is encoded as usual. ## Cryptographic protocols + This section talks about how protocols like (D)TLS and IKEv2 are affected by this specifications. It will not attempt to solve all these problems, but it will explain the rationale, how things will work and what open problems need to be solved. Obvious issues that need to be discussed. - How does the protocol declare support for composite signatures? TLS has hooks for declaring support for specific signature algorithms, however it would need to be extended, because the client would need to declare support for both the composite infrastructure, as well as for the various component signature algorithms. @@ -474,21 +454,6 @@ Specifying a modified verification algorithm to handle these situations is beyon While intentionally not specified in this document, implementors should put careful thought into implementing a meaningfull policy mechinism within the context of their signature verification engines, for example only algorithms that provide similar security levels should be combined together. -## Protection of Private Keys - -Structures described in this document do not protect private keys in any way unless combined with a security protocol or encryption properties of the objects (if any) where the CompositePrivateKey is used (see next Section). - -Protection of the private keys is vital to public key cryptography. The consequences of disclosure depend on the purpose of the private key. If a private key is used for signature, then the disclosure allows unauthorized signing. If a private key is used for key management, then disclosure allows unauthorized parties to access the managed keying material. The encryption algorithm used in the encryption process must be at least as 'strong' as the key it is protecting. - -## Checking for Compromised Key Reuse - -CA implementations need to be careful when checking for compromised key reuse, for example as required by WebTrust regulations; when checking for compromised keys, you MUST unpack the CompositePublicKey structure and compare individual component keys. In other words, when marking a key as revoked for key compromise, the individual component keys should be marked, not the composite key as a whole. - - -## Composite Encryption and KEMs - -This document deals only with signature keys. While the CompositePublicKey and CompositePrivateKey structures could equally be used to hold encryption or KEM keys, the authors warn that there are non-trivial design decisions to be made when constructing a multi-key public key encryption or KEM algorithm. Some of these design and implementation decisions, if done incorrectly will result in a catastrophic loss of security. We leave it to the community to standardize analogous composite encryption and KEM schemes. - @@ -541,14 +506,12 @@ pk-Composite PUBLIC-KEY ::= { IDENTIFIER id-alg-composite KEY CompositePublicKey PARAMS ARE absent - CERT-KEY-USAGE - { digitalSignature, nonRepudiation, keyCertSign, cRLSign } PRIVATE-KEY CompositePrivateKey } -CompositePublicKey ::= SEQUENCE SIZE (1..MAX) OF SubjectPublicKeyInfo +CompositePublicKey ::= SEQUENCE SIZE (2..MAX) OF SubjectPublicKeyInfo -CompositePrivateKey ::= SEQUENCE SIZE (1..MAX) OF OneAsymmetricKey +CompositePrivateKey ::= SEQUENCE SIZE (2..MAX) OF OneAsymmetricKey -- -- Signature Algorithm @@ -561,9 +524,9 @@ sa-CompositeSignature SIGNATURE-ALGORITHM ::= { PUBLIC-KEYS { pk-Composite } SMIME-CAPS { IDENTIFIED BY id-alg-composite } } -CompositeParams ::= SEQUENCE SIZE (1..MAX) OF AlgorithmIdentifier +CompositeParams ::= SEQUENCE SIZE (2..MAX) OF AlgorithmIdentifier -CompositeSignatureValue ::= SEQUENCE SIZE (1..MAX) OF BIT STRING +CompositeSignatureValue ::= SEQUENCE SIZE (2..MAX) OF BIT STRING END @@ -582,12 +545,13 @@ https://datatracker.ietf.org/ipr/3588/ # Contributors and Acknowledgements This document incorporates contributions and comments from a large group of experts. The Editors would especially like to acknowledge the expertise and tireless dedication of the following people, who attended many long meetings and generated millions of bytes of electronic mail and VOIP traffic over the past year in pursuit of this document: -John Gray (Entrust Datacard), -Serge Mister (Entrust Datacard), +John Gray (Entrust), +Serge Mister (Entrust), Scott Fluhrer (Cisco Systems), Panos Kampanakis (Cisco Systems), -Daniel Van Geest (ISARA), and -Tim Hollebeek (Digicert). +Daniel Van Geest (ISARA), +Tim Hollebeek (Digicert), and +François Rousseau. We are grateful to all, including any contributors who may have been inadvertently omitted from this list. @@ -595,4 +559,10 @@ been inadvertently omitted from this list. This document borrows text from similar documents, including those referenced below. Thanks go to the authors of those documents. "Copying always makes things easier and less error prone" - [RFC8411]. +## Making contributions + +Additional contributions to this draft are weclome. Please see the working copy of this draft at, as well as open issues at: + +https://github.com/EntrustCorporation/draft-ounsworth-composite-sigs + diff --git a/draft-ounsworth-pq-composite-sigs.txt b/draft-ounsworth-pq-composite-sigs.txt index 43351dc..ec7e160 100644 --- a/draft-ounsworth-pq-composite-sigs.txt +++ b/draft-ounsworth-pq-composite-sigs.txt @@ -2,15 +2,15 @@ -LAMPS M. Ounsworth (Editor) -Internet-Draft Entrust Datacard +LAMPS M. Ounsworth +Internet-Draft Entrust Intended status: Standards Track M. Pala -Expires: September 27, 2020 CableLabs - March 26, 2020 +Expires: January 13, 2022 CableLabs + July 12, 2021 - Composite Keys and Signatures For Use In Internet PKI - draft-ounsworth-pq-composite-sigs-03 + Composite Signatures For Use In Internet PKI + draft-ounsworth-pq-composite-sigs-05 Abstract @@ -20,16 +20,16 @@ Abstract post-quantum algorithms is at question, a multi-key cryptographic operation will need to be performed in such a way that breaking it requires breaking each of the component algorithms individually. - This requires defining new structures for holding composite public - keys and composite signature data. + This requires defining new structures for holding composite signature + data. - This document defines the structures CompositePublicKey, - CompositeSignatureValue, and CompositeParams, which are sequences of - the respective structure for each component algorithm. This document - also defines algorithms for generating and verifying composite - signatures. This document makes no assumptions about what the - component algorithms are, provided that their algorithm identifiers - and signature generation and verification algorithms are defined. + This document defines the structures CompositeSignatureValue, and + CompositeParams, which are sequences of the respective structure for + each component algorithm. This document also defines processes for + generating and verifying composite signatures. This document makes + no assumptions about what the component algorithms are, provided that + their algorithm identifiers and signature generation and verification + processes are defined. Status of This Memo @@ -46,21 +46,21 @@ Status of This Memo time. It is inappropriate to use Internet-Drafts as reference material or to cite them other than as "work in progress." - This Internet-Draft will expire on September 27, 2020. + This Internet-Draft will expire on January 13, 2022. -Ounsworth (Editor) & PaExpires September 27, 2020 [Page 1] +Ounsworth & Pala Expires January 13, 2022 [Page 1] -Internet-Draft PQ Composite Certs March 2020 +Internet-Draft PQ Composite Sigs July 2021 Copyright Notice - Copyright (c) 2020 IETF Trust and the persons identified as the + Copyright (c) 2021 IETF Trust and the persons identified as the document authors. All rights reserved. This document is subject to BCP 78 and the IETF Trust's Legal @@ -77,41 +77,41 @@ Table of Contents 1. Introduction . . . . . . . . . . . . . . . . . . . . . . . . 3 1.1. Terminology . . . . . . . . . . . . . . . . . . . . . . . 3 - 2. Composite Structures . . . . . . . . . . . . . . . . . . . . 4 + 2. Composite Identifiers and Structures . . . . . . . . . . . . 4 2.1. Algorithm Identifier . . . . . . . . . . . . . . . . . . 5 2.2. Composite Keys . . . . . . . . . . . . . . . . . . . . . 5 - 2.2.1. Key Usage Bits . . . . . . . . . . . . . . . . . . . 6 - 2.3. Composite Public Key . . . . . . . . . . . . . . . . . . 6 - 2.4. Composite Private Key . . . . . . . . . . . . . . . . . . 7 - 2.5. Composite Signature . . . . . . . . . . . . . . . . . . . 7 - 2.6. Encoding Rules . . . . . . . . . . . . . . . . . . . . . 8 - 3. Composite Signature Algorithm . . . . . . . . . . . . . . . . 8 - 3.1. Composite Signature Generation . . . . . . . . . . . . . 8 - 3.2. Composite Signature Verification . . . . . . . . . . . . 9 - 4. In Practice . . . . . . . . . . . . . . . . . . . . . . . . . 11 - 4.1. PEM Storage of Composite Private Keys . . . . . . . . . . 11 - 4.2. Asymmetric Key Packages (CMS) . . . . . . . . . . . . . . 11 - 4.3. Cryptographic protocols . . . . . . . . . . . . . . . . . 12 + 2.2.1. Key Usage Bits . . . . . . . . . . . . . . . . . . . 5 + 2.3. Composite Signature . . . . . . . . . . . . . . . . . . . 6 + 2.4. Encoding Rules . . . . . . . . . . . . . . . . . . . . . 6 + 3. Composite Signature Processes . . . . . . . . . . . . . . . . 7 + 3.1. Composite Signature Generation Process . . . . . . . . . 7 + 3.2. Composite-OR Signature Generation Process . . . . . . . . 8 + 3.3. Composite Signature Verification Process . . . . . . . . 9 + 3.4. Composite-OR Signature Verification . . . . . . . . . . . 10 + 3.4.1. Composite-OR Legacy Mode . . . . . . . . . . . . . . 11 + 4. In Practice . . . . . . . . . . . . . . . . . . . . . . . . . 12 + 4.1. Cryptographic protocols . . . . . . . . . . . . . . . . . 13 5. IANA Considerations . . . . . . . . . . . . . . . . . . . . . 13 - 6. Security Considerations . . . . . . . . . . . . . . . . . . . 13 - 6.1. Policy for Deprecated and Acceptable Algorithms . . . . . 13 - 6.2. Protection of Private Keys . . . . . . . . . . . . . . . 13 - 6.3. Checking for Compromised Key Reuse . . . . . . . . . . . 14 - 6.4. Composite Encryption and KEMs . . . . . . . . . . . . . . 14 + 6. Security Considerations . . . . . . . . . . . . . . . . . . . 14 + 6.1. Policy for Deprecated and Acceptable Algorithms . . . . . 14 7. Appendices . . . . . . . . . . . . . . . . . . . . . . . . . 14 7.1. ASN.1 Module . . . . . . . . . . . . . . . . . . . . . . 14 7.2. Intellectual Property Considerations . . . . . . . . . . 16 8. Contributors and Acknowledgements . . . . . . . . . . . . . . 16 - 9. References . . . . . . . . . . . . . . . . . . . . . . . . . 16 - 9.1. Normative References . . . . . . . . . . . . . . . . . . 16 - 9.2. Informative References . . . . . . . . . . . . . . . . . 18 - Authors' Addresses . . . . . . . . . . . . . . . . . . . . . . . 18 + 8.1. Making contributions . . . . . . . . . . . . . . . . . . 16 + 9. Normative References . . . . . . . . . . . . . . . . . . . . 16 + Authors' Addresses . . . . . . . . . . . . . . . . . . . . . . . 17 -Ounsworth (Editor) & PaExpires September 27, 2020 [Page 2] + + + + + +Ounsworth & Pala Expires January 13, 2022 [Page 2] -Internet-Draft PQ Composite Certs March 2020 +Internet-Draft PQ Composite Sigs July 2021 1. Introduction @@ -127,8 +127,8 @@ Internet-Draft PQ Composite Certs March 2020 be advantageous for an entity's cryptographic identity to be composed of multiple public-key algorithms. - The deployment of composite public keys and composite signatures - using post-quantum algorithms will face two challenges + The deployment of composite signatures using post-quantum algorithms + will face two challenges o Algorithm strength uncertainty: During the transition period, some post-quantum signature and encryption algorithms will not be fully @@ -142,17 +142,14 @@ Internet-Draft PQ Composite Certs March 2020 quantum algorithms will not be supported by all clients. This document provides a mechanism to address algorithm strength - uncertainty by providing formats for encoding multiple public keys, - private keys and signature values into existing public key and - signature fields, as well as an algorithm for validating a composite - signature. The issue of backwards compatibility is left open to be - addressed in separate draft(s). + uncertainty by building on ~~ reference draft-ounsworth-pq-composite- + pubkeys ~~ by providing formats for encoding multiple signature + values into existing public signature fields, as well as the process + for validating a composite signature. Backwards compatibility is + addressed via the Composite-OR mechanism described herein. This document is intended for general applicability anywhere that - public key structures or digital signatures are used within PKIX - structures. While the CompositePublicKey structure defined herein is - equally applicable to asymmetric encryption keys, this document is - intentionally restricted to signatures. + digital signatures are used within PKIX and CMS structures. 1.1. Terminology @@ -162,69 +159,57 @@ Internet-Draft PQ Composite Certs March 2020 14 [RFC2119] [RFC8174] when, and only when, they appear in all capitals, as shown here. + The following terms are used in this document: -Ounsworth (Editor) & PaExpires September 27, 2020 [Page 3] - -Internet-Draft PQ Composite Certs March 2020 - The following terms are used in this document: +Ounsworth & Pala Expires January 13, 2022 [Page 3] + +Internet-Draft PQ Composite Sigs July 2021 + ALGORITHM: An information object class for identifying the type of cryptographic operation to be performed. This document is primarily - concerned with algorithms for producing digital signatures, though - the public key structure could just as easily hold encryption keys. + concerned with algorithms for producing digital signatures. BER: Basic Encoding Rules (BER) as defined in [X.690]. COMPONENT ALGORITHM: A single basic algorithm which is contained within a composite algorithm. - COMPOSITE ALGORITHM: An algorithm which is a sequence of one or more + COMPOSITE ALGORITHM: An algorithm which is a sequence of two or more component algorithms, as defined in Section 2. DER: Distinguished Encoding Rules as defined in [X.690]. + LEGACY: For the purposes of this document, a legacy key or signature + is a non-composite key or signature. + PUBLIC / PRIVATE KEY: The public and private portion of an asymmetric cryptographic key, making no assumptions about which algorithm. - PRIMITIVE PUBLIC KEY / SIGNATURE: A public key or signature object of - a non-composite algorithm type. - SIGNATURE: A digital cryptographic signature, making no assumptions about which algorithm. -2. Composite Structures +2. Composite Identifiers and Structures - In order for public keys and signatures to be composed of multiple - algorithms, we define encodings consisting of a sequence of public - key and signature primitives (aka "component algorithms") such that - these structures can be used as a drop-in replacement for existing - public key or signature fields such as those found in PKCS#10 - [RFC2986], CMP [RFC4210], X.509 [RFC5280], CMS [RFC5652]. + In order for signatures to be composed of multiple algorithms, we + define encodings consisting of a sequence of signature primitives + (aka "component algorithms") such that these structures can be used + as a drop-in replacement for existing signature fields such as those + found in PKCS#10 [RFC2986], CMP [RFC4210], X.509 [RFC5280], CMS + [RFC5652]. This section defines the following structures: - o The id-alg-composite is an OID identifying a composite public key - or signature object. - - o The CompositePublicKey carries all the public keys associated with - an identity within a single public key structure. - - o The CompositePrivateKey carries all the private keys associated - with an identity within a single private key structure. - - - - - - -Ounsworth (Editor) & PaExpires September 27, 2020 [Page 4] - -Internet-Draft PQ Composite Certs March 2020 + o The id-alg-composite is an AlgorithmIdentifier identifying a + composite signature object. + The sa-CompositeSignature AlgorithmIdentifier and the + corresponding CompositeParams identify the algorithm(s) used in a + composite signature. o The CompositeSignatureValue, carries a sequence of signatures that are generated by a CompositePrivateKey, and can be verified with @@ -233,15 +218,35 @@ Internet-Draft PQ Composite Certs March 2020 EDNOTE 2: the choice to define composite algorithm parameters as a sequence inside the existing fields avoids the exponential proliferation of OIDs that are needed for each combination of + + + +Ounsworth & Pala Expires January 13, 2022 [Page 4] + +Internet-Draft PQ Composite Sigs July 2021 + + signature algorithms in other schemes for achieving multi-key certificates. This scheme also naturally extends from 2-keypair to n-keypair keys and certificates. + EDNOTE 2a: We have heard community feedback that the ASN.1 structures + presented here are too flexible in that allow arbitrary combinations + of an arbitrary number of signature algorithms. The feedback is that + this is too much of a "footgun" for implementors and sysadmins. We + are working on an alternative formulation using ASN.1 information + object classes that allow for compiling explicit pairs of + algorithmIDs. We would love community feedback on which approach is + preferred. See slide 30 of this presentation: + https://datatracker.ietf.org/meeting/interim-2021-lamps-01/materials/ + slides-interim-2021-lamps-01-sessa-position-presentation-by-mike- + ounsworth-00.pdf + 2.1. Algorithm Identifier - The same algorithm identifier is used for identifying a public key, a - private key, and a signature. Additional encoding information is - provided below for each of these objects. + The following object identifier is used for identifying a composite + signature. Additional encoding information is provided below for + each of these objects. id-alg-composite OBJECT IDENTIFIER ::= { iso(1) identified-organization(3) dod(6) internet(1) private(4) @@ -252,111 +257,39 @@ Internet-Draft PQ Composite Certs March 2020 2.2. Composite Keys - A composite key is a single key object that performs an atomic - signature or verification operation, using its encapsulated sequence - of component keys. - - The ASN.1 algorithm object for composite public and private keys is: - - pk-Composite PUBLIC-KEY ::= { - IDENTIFIER id-alg-composite - KEY CompositePublicKey - PARAMS ARE absent - CERT-KEY-USAGE - { digitalSignature, nonRepudiation, keyCertSign, cRLSign } - PRIVATE-KEY CompositePrivateKey - } - - EDNOTE 4: the authors are currently unsure whether the params should - be absent (ie this structure simply says "I am a composite - algorithm"), or used to duplicate some amount of information about - what the component algoritms are. See Section 2.3 for a longer - ENDOTE on this. - - - - - -Ounsworth (Editor) & PaExpires September 27, 2020 [Page 5] - -Internet-Draft PQ Composite Certs March 2020 - + A Composite signature MUST be associated with a Composite public key + as defined in ~~ reference draft-ounsworth-pq-composite-pubkey ~~. 2.2.1. Key Usage Bits - The intended application for the key is indicated in the keyUsage - certificate extension and defined in the CERT-KEY-USAGE field of pk- - Composite. - - If the keyUsage extension is present in an end-entity certificate - that indicates id-alg-composite, then the keyUsage extension MUST - contain one or both of the following values: - - nonRepudiation; and - digitalSignature. - - If the keyUsage extension is present in a certification authority - certificate that indicates id-alg-composite, then the keyUsage - extension MUST contain one or more of the following values: - - nonRepudiation; - digitalSignature; - keyCertSign; and - cRLSign. - - As this draft only covers composite signatures, the key usage bits - specified here apply to all component keys within a composite key. - -2.3. Composite Public Key - - Composite public key data is represented by the following structure: - - CompositePublicKey ::= SEQUENCE SIZE (1..MAX) OF SubjectPublicKeyInfo - - The corresponding AlgorithmIdentifier for a composite public key MUST - use the id-alg-composite object identifier, defined in Section 2.1, - and the parameters field MUST be absent. - - A composite public key MUST contain at least one component public - key. - - A CompositePublicKey MUST NOT contain a component public key which - itself describes a composite key; ie recursive CompositePublicKeys - are not allowed + For protocols such as X.509 [RFC5280] that specify key usage along + with the public key, then the composite public key associated with a + composite signature MUST have a signing-type key usage. - Each element of a CompositePublicKey is a SubjectPublicKeyInfo object - one of the component public keys. When the CompositePublicKey must - be provided in octet string or bit string format, the data structure - is encoded as specified in Section 2.6. + If the keyUsage extension is present in a Certification Authority + (CA) certificate that indicates id-composite-key, then any + combination of the following values MAY be present: + digitalSignature; + nonRepudiation; + keyCertSign; and + cRLSign. - -Ounsworth (Editor) & PaExpires September 27, 2020 [Page 6] +Ounsworth & Pala Expires January 13, 2022 [Page 5] -Internet-Draft PQ Composite Certs March 2020 - - -2.4. Composite Private Key - - The composite private key data is represented by the following - structure: +Internet-Draft PQ Composite Sigs July 2021 - CompositePrivateKey ::= SEQUENCE SIZE (1..MAX) OF OneAsymmetricKey - Each element is a OneAsymmetricKey [RFC5958] object for a component - private key. + If the keyUsage extension is present in an End Entity (EE) + certificate that indicates id-composite-key, then any combination of + the following values MAY be present: - The corresponding AlgorithmIdentifier for a composite private key - MUST use the id-alg-composite object identifier, and the parameters - field MUST be absent. + digitalSignature; and + nonRepudiation; - A CompositePrivateKey MUST contain at least one component private - key, and they MUST be in the same order as in the corresponding - CompositePublicKey. - -2.5. Composite Signature +2.3. Composite Signature The ASN.1 algorithm object for a composite signature is: @@ -368,11 +301,9 @@ Internet-Draft PQ Composite Certs March 2020 SMIME-CAPS { IDENTIFIED BY id-alg-composite } } } - The id-alg-composite object identifier MUST be used to identify when - a signature has been created by a CompositePrivateKey and following - algorithm parameters MUST be included: + The following algorithm parameters MUST be included: - CompositeParams ::= SEQUENCE SIZE (1..MAX) OF AlgorithmIdentifier + CompositeParams ::= SEQUENCE SIZE (2..MAX) OF AlgorithmIdentifier The signature's CompositeParams sequence MUST contain the same component algorithms listed in the same order as in the associated @@ -381,32 +312,31 @@ Internet-Draft PQ Composite Certs March 2020 The output of the composite signature algorithm is the DER encoding of the following structure: - CompositeSignatureValue ::= SEQUENCE SIZE (1..MAX) OF BIT STRING + CompositeSignatureValue ::= SEQUENCE SIZE (2..MAX) OF BIT STRING Where each BIT STRING within the SEQUENCE is a signature value produced by one of the component keys. It MUST contain one signature + value produced by each component algorithm, and in the same order as + in the associated CompositeParams object. + The choice of "SEQUENCE OF BIT STRING", rather than for example a + single BIT STRING containing the concatenated signature values, is to + gracefully handle variable-length signature values by taking + advantage of ASN.1's built-in length fields. +2.4. Encoding Rules + Many protocol specifications will require that composite signature + data structures be represented by an octet string or bit string. -Ounsworth (Editor) & PaExpires September 27, 2020 [Page 7] - -Internet-Draft PQ Composite Certs March 2020 - value produced by each component algorithm, and in the same order as - in the associated CompositeParams object. - The choice of "SEQUENCE OF BIT STRING", rather than for example a - single BIT STRING containing the concatenated signature values, is to - gracefully handle variable-length signature values by taking - advantage of ASN.1's build-in length fields. -2.6. Encoding Rules +Ounsworth & Pala Expires January 13, 2022 [Page 6] + +Internet-Draft PQ Composite Sigs July 2021 - Many protocol specifications will require that the composite public - key, composite private key, and composite signature data structures - be represented by an octet string or bit string. When an octet string is required, the DER encoding of the composite data structure SHALL be used directly. @@ -421,33 +351,47 @@ Internet-Draft PQ Composite Certs March 2020 implementations that parse these structures MAY accept both BER and DER. -3. Composite Signature Algorithm +3. Composite Signature Processes - This section specifies the algorithms for generating and verifying + This section specifies the processes for generating and verifying composite signatures. - This algorithm addresses algorithm strength uncertainty by providing + This process addresses algorithm strength uncertainty by providing the verifier with parallel signatures from all the component signature algorithms; thus breaking the composite signature would require breaking all of the component signatures. -3.1. Composite Signature Generation +3.1. Composite Signature Generation Process Generation of a composite signature involves applying each component - algorithm's signature routine to the input message according to its + algorithm's signature process to the input message according to its specification, and then placing each component signature value into - the CompositeSignatureValue structure defined in Section 2.5. + the CompositeSignatureValue structure defined in Section 2.3. + + The following process is used to generate composite signature values. + + + + + + + + + + - The following algorithm is used to generate composite signature - values. -Ounsworth (Editor) & PaExpires September 27, 2020 [Page 8] + + + + +Ounsworth & Pala Expires January 13, 2022 [Page 7] -Internet-Draft PQ Composite Certs March 2020 +Internet-Draft PQ Composite Sigs July 2021 Input: @@ -458,7 +402,7 @@ Internet-Draft PQ Composite Certs March 2020 Output: S The signatures, a CompositeSignatureValue - Signature Generation Procedure: + Signature Generation Process: 1. Generate the n component signatures independently, according to their algorithm specifications. @@ -466,28 +410,74 @@ Internet-Draft PQ Composite Certs March 2020 Si := Sign( Ki, M ) 2. Encode each component signature S1, S2, .., Sn into a BIT STRING - according to its algorithm specification.2020-01-21g + according to its algorithm specification. S ::= Sequence { S1, S2, .., Sn } 3. Output S - Since recursive composite public keys are disallowed in Section 2.3, - no component signature may itself be a composite; ie the signature - generation routine MUST fail if one of the private keys K1, K2, .., + Since recursive composite public keys are disallowed in ~~ Reference + draft-ounsworth-pq-composite-pubkeys sec-composite-pub-keys ~~, no + component signature may itself be a composite; ie the signature + generation process MUST fail if one of the private keys K1, K2, .., Kn is a composite with the OID id-alg-composite. A composite signature MUST produce and include in the output a signature value for every component key in the corresponding - CompositePrivateKey. While it may be tempting to allow a signer to - produce a signature with a subset of their keys, the complexity of - securely verifying such a "subset signature" is high and out of scope - for this document. + CompositePrivateKey. For this mode, please see Composite-OR in + section Section 3.2. + +3.2. Composite-OR Signature Generation Process + + EDNOTE: This section was written with the intention of keeping the + primary Composite OID reserved for the simple and strict mode; if you + want to do either a simple OR, or a custom policy then we have given + a different OID. We are still debating whether this is useful to + specify at issuing time, or whether this is adding needless + complexity to the draft. + + If the algorithm ID of the public key associated with this signature + is id-composite-or-key then the signer MAY use only a subset of the + component keys and therefore produce fewer signatures than the number + of component keys. + + + -3.2. Composite Signature Verification + +Ounsworth & Pala Expires January 13, 2022 [Page 8] + +Internet-Draft PQ Composite Sigs July 2021 + + + Composite-OR signature generation uses the same structures and + algorithms as Composite, with the difference that the signature + generation process may emit a null instead of a signature value in + step 1 for one or more component algorithms. A Composite-OR + signature MUST NOT be entirely null; it must contain at least one + valid signature. + + The design intent of this mode is to support migration scenarios + where an end entity has been issued keys on algorithms that either + itself or the peer with which it is communicating do not (yet) + support. This design allows for both the mode where the signer omits + signatures that it knows its peer cannot process in order to save + bandwidth and performance, and the mode where it includes all + component signatures and allows the verifier to choose how many to + verify. The latter is RECOMMENDED for signatures that need both + sort-term backwards compatibility as well as long-term security. + + EDNOTE: Do we want to allow a Composite-OR with only a single + signature to produce non-composite signatureAlgorithm and + signatureValua as per [RFC5280]? Advantages: bandwidth savings of an + extra OID and some sequences with one element. Disadvantages: + ambiguous whether a signature is traditional or composite until you + look at the corresponding public key. + +3.3. Composite Signature Verification Process Verification of a composite signature involves applying each - component algorithm's verification routine according to its + component algorithm's verification process according to its specification. In the absence of an application profile specifying otherwise, @@ -495,15 +485,25 @@ Internet-Draft PQ Composite Certs March 2020 only if all component signatures were successfully validated, and "Invalid signature" (false) otherwise. - The following algorithm is used to perform this verification. + The following process is used to perform this verification. + -Ounsworth (Editor) & PaExpires September 27, 2020 [Page 9] + + + + + + + + + +Ounsworth & Pala Expires January 13, 2022 [Page 9] -Internet-Draft PQ Composite Certs March 2020 +Internet-Draft PQ Composite Sigs July 2021 Input: @@ -540,27 +540,112 @@ Signature Verification Procedure:: if all succeeded, then output "Valid signature" - Since recursive composite public keys are disallowed in Section 2.3, - no component signature may be composite; ie the signature - verification procedure MUST fail if any of the public keys P1, P2, - .., Pn or algorithm identifiers A1, A2, .., An are composite with the - OID id-alg-composite. + Since recursive composite public keys are disallowed in ~~ Reference + draft-ounsworth-pq-composite-keys sec-composite-pub-keys ~~, no + component signature may be composite; ie the signature verification + procedure MUST fail if any of the public keys P1, P2, .., Pn or + algorithm identifiers A1, A2, .., An are composite with the OID id- + alg-composite. + +3.4. Composite-OR Signature Verification + + EDNOTE: This section was written with the intention of keeping the + primary Composite OID reserved for the simple and strict mode; if you + want to do either a simple OR, or a custom policy then we have given + a different OID. We are still debating whether this is useful to - It is expected that some use-cases for algorithm migration or high - performance will require verifiers to succeed when only a subset of - the component algorithms have been verified. Defining this - verification behaviour is out of scope for this document, and falls - to an application profile. +Ounsworth & Pala Expires January 13, 2022 [Page 10] + +Internet-Draft PQ Composite Sigs July 2021 + + + specify at issuing time, or whether this is adding needless + complexity to the draft. + When the public key associated with the signature being verified has + algorithm id-composite-or-key, then an alternate verification + processes MAY be used, at the discretion of the implementor. In this + section we provide some examples of alternate verification processes. + If the signature is a traditional (non-composite) algorithm and value + or a composite signature with a single component, then it MAY be + considered valid if it verifies under one of the component keys. -Ounsworth (Editor) & PaExpires September 27, 2020 [Page 10] + If the signature is composite, then the implementor MAY implement + policy for which combinations are acceptable. + + EDNOTE: Does this mean Composite-OR end entity certificates need to + be issued by a PKI that is marked as Composite-OR all the way to the + top so that verifiers that do not support all the algorithms don't + fail? Need to think more about the security implications of allowing + a Composite-or in an end entity cert implicitely turning all + Composite algIDs into Composite-or algIDs in its cert chain. + + EDNOTE: Do we need to specify the semantics of verifying an "n of m" + subset signature? I suspect that specifying this in general will be + a rat's nest of edge cases, so I propose to "leave this to the + implementor". + +3.4.1. Composite-OR Legacy Mode + + The Composite-OR Legacy Mode is provided to facilitate migration by + allowing existing PKI entities (including root CAs, intermediate CAs, + and end entities) to have their existing keys re-certified inside a + Composite-OR structure along with Post-Quantum keys, and for + signatures made by that key prior to the migration to remain valid. + Note that Composite-OR Legacy Mode is only provided for signature + verification, and not for signature generation; legacy signatures + SHOULD NOT be produced from a Composite key. + + EDNOTE: to further solidify this, we could add a clause that Legacy + Mode signatures are to fail if the signature was produced after + notBefore date of the Composite-OR certificate? + + In Composite-OR Legacy Mode, a legacy signature algorithm and legacy + signature value MAY be validated against a Composite-OR public key. + The legacy signature algorithm is to be interpreted by the verifier + as a sa-CompositeSignature with CompositeParams in the following way: + + CompositeParams {legacyAlgorithmIdentifier, null, .., null} + + + +Ounsworth & Pala Expires January 13, 2022 [Page 11] -Internet-Draft PQ Composite Certs March 2020 +Internet-Draft PQ Composite Sigs July 2021 + + + with the correct number of nulls to match the Composite-OR public key + that the signature is being verified against. For the purposes of a + signature validation under Composite-OR Legacy Mode, a null + AlgorithmIdentifier is considered to be a match for the corresponding + algorithm in the Composite-OR public key. + + The legacy signature value is to be interpreted by the verifier as a + sa-CompositeSignature with CompositeParams in the following way: + + CompositeSignatureValue {legacySignatureValue, null, .., null} + with the correct number of nulls to match the Composite-OR public key + that the signature is being verified against. The verification + algorithm in section Section 3.4 applies. + + Security consideration: when implementing Composite-OR Legacy Mode, + it is important to catch the edge case of {null, null, .., null} for + both AlgorithmIdentifier and SignatureValue and return Invalid + Signature. + + It is RECOMMENDED that Composite-OR Legacy Mode be implemented as an + optional mode in the verifier that can be enabled or disabled by + runtime configuration or policy. + + EDNOTE: the signing public key is often identified in the signed + document by issuer+serialNumber or by an SKI containing a hash of the + public key value. Might need X.509 extensions identifying the SKI of + the legacy cert it's replacing? 4. In Practice @@ -581,6 +666,14 @@ Internet-Draft PQ Composite Certs March 2020 o We could also explain that the root CA cert does not have to be of the same algorithms. The root cert SHOULD NOT be transferred in + + + +Ounsworth & Pala Expires January 13, 2022 [Page 12] + +Internet-Draft PQ Composite Sigs July 2021 + + the authentication exchange to save transport overhead and thus it can be different than the intermediate and leaf certs. @@ -592,51 +685,7 @@ Internet-Draft PQ Composite Certs March 2020 ~~~ END EDNOTE 10~~~ -4.1. PEM Storage of Composite Private Keys - - CompositePrivateKeys can be encoded to the PEM format by placing a - CompositePrivateKey into the privateKey field of a PrivateKeyInfo or - OneAsymmetricKey object, and then applying the PEM encoding rules as - defined in [RFC7468] section 10 and 11 for plaintext and encrypted - private keys, respectively. - -4.2. Asymmetric Key Packages (CMS) - - The Cryptographic Message Syntax (CMS), as defined in [RFC5652], can - be used to digitally sign, digest, authenticate, or encrypt the - asymmetric key format content type. - - When encoding composite private keys, the privateKeyAlgorithm in the - OneAsymmetricKey SHALL be set to id-alg-composite. - - - - - -Ounsworth (Editor) & PaExpires September 27, 2020 [Page 11] - -Internet-Draft PQ Composite Certs March 2020 - - - The parameters of the privateKeyAlgorithm SHALL be a sequence of - AlgorithmIdentifier objects, each of which are encoded according to - the rules defined for each of the different keys in the composite - private key. - - The value of the privateKey field in the OneAsymmetricKey SHALL be - set to the DER encoding of the SEQUENCE of private key values that - make up the composite key. The number and order of elements in the - sequence SHALL be the same as identified in the sequence of - parameters in the privateKeyAlgorithm. - - The value of the publicKey (if present) SHALL be set to the DER - encoding of the corresponding CompositePublicKey. If this field is - present, the number and order of component keys MUST be the same as - identified in the sequence of parameters in the privateKeyAlgorithm. - - The value of the attributes is encoded as usual. - -4.3. Cryptographic protocols +4.1. Cryptographic protocols This section talks about how protocols like (D)TLS and IKEv2 are affected by this specifications. It will not attempt to solve all @@ -664,16 +713,6 @@ Internet-Draft PQ Composite Certs March 2020 encoding composite ciphertexts is less so - we decided to put that off to another draft. - - - - - -Ounsworth (Editor) & PaExpires September 27, 2020 [Page 12] - -Internet-Draft PQ Composite Certs March 2020 - - 5. IANA Considerations The ASN.1 module OID is TBD. The id-alg-composite OID is to be @@ -684,6 +723,13 @@ Internet-Draft PQ Composite Certs March 2020 iso(1) identified-organization(3) dod(6) internet(1) security(5) mechanisms(5) pkix(7) algorithms(6) composite(??) } + + +Ounsworth & Pala Expires January 13, 2022 [Page 13] + +Internet-Draft PQ Composite Sigs July 2021 + + 6. Security Considerations 6.1. Policy for Deprecated and Acceptable Algorithms @@ -717,51 +763,6 @@ Internet-Draft PQ Composite Certs March 2020 for example only algorithms that provide similar security levels should be combined together. -6.2. Protection of Private Keys - - Structures described in this document do not protect private keys in - any way unless combined with a security protocol or encryption - - - - -Ounsworth (Editor) & PaExpires September 27, 2020 [Page 13] - -Internet-Draft PQ Composite Certs March 2020 - - - properties of the objects (if any) where the CompositePrivateKey is - used (see next Section). - - Protection of the private keys is vital to public key cryptography. - The consequences of disclosure depend on the purpose of the private - key. If a private key is used for signature, then the disclosure - allows unauthorized signing. If a private key is used for key - management, then disclosure allows unauthorized parties to access the - managed keying material. The encryption algorithm used in the - encryption process must be at least as 'strong' as the key it is - protecting. - -6.3. Checking for Compromised Key Reuse - - CA implementations need to be careful when checking for compromised - key reuse, for example as required by WebTrust regulations; when - checking for compromised keys, you MUST unpack the CompositePublicKey - structure and compare individual component keys. In other words, - when marking a key as revoked for key compromise, the individual - component keys should be marked, not the composite key as a whole. - -6.4. Composite Encryption and KEMs - - This document deals only with signature keys. While the - CompositePublicKey and CompositePrivateKey structures could equally - be used to hold encryption or KEM keys, the authors warn that there - are non-trivial design decisions to be made when constructing a - multi-key public key encryption or KEM algorithm. Some of these - design and implementation decisions, if done incorrectly will result - in a catastrophic loss of security. We leave it to the community to - standardize analogous composite encryption and KEM schemes. - 7. Appendices 7.1. ASN.1 Module @@ -777,15 +778,15 @@ Internet-Draft PQ Composite Certs March 2020 IMPORTS PUBLIC-KEY, SIGNATURE-ALGORITHM - FROM AlgorithmInformation-2009 -- RFC 5912 [X509ASN1] -Ounsworth (Editor) & PaExpires September 27, 2020 [Page 14] +Ounsworth & Pala Expires January 13, 2022 [Page 14] -Internet-Draft PQ Composite Certs March 2020 +Internet-Draft PQ Composite Sigs July 2021 + FROM AlgorithmInformation-2009 -- RFC 5912 [X509ASN1] { iso(1) identified-organization(3) dod(6) internet(1) security(5) mechanisms(5) pkix(7) id-mod(0) id-mod-algorithmInformation-02(58) } @@ -816,14 +817,12 @@ Internet-Draft PQ Composite Certs March 2020 IDENTIFIER id-alg-composite KEY CompositePublicKey PARAMS ARE absent - CERT-KEY-USAGE - { digitalSignature, nonRepudiation, keyCertSign, cRLSign } PRIVATE-KEY CompositePrivateKey } - CompositePublicKey ::= SEQUENCE SIZE (1..MAX) OF SubjectPublicKeyInfo + CompositePublicKey ::= SEQUENCE SIZE (2..MAX) OF SubjectPublicKeyInfo - CompositePrivateKey ::= SEQUENCE SIZE (1..MAX) OF OneAsymmetricKey + CompositePrivateKey ::= SEQUENCE SIZE (2..MAX) OF OneAsymmetricKey -- -- Signature Algorithm @@ -834,19 +833,18 @@ Internet-Draft PQ Composite Certs March 2020 VALUE CompositeSignatureValue PARAMS TYPE CompositeParams ARE required PUBLIC-KEYS { pk-Composite } + SMIME-CAPS { IDENTIFIED BY id-alg-composite } } -Ounsworth (Editor) & PaExpires September 27, 2020 [Page 15] +Ounsworth & Pala Expires January 13, 2022 [Page 15] -Internet-Draft PQ Composite Certs March 2020 - +Internet-Draft PQ Composite Sigs July 2021 - SMIME-CAPS { IDENTIFIED BY id-alg-composite } } - CompositeParams ::= SEQUENCE SIZE (1..MAX) OF AlgorithmIdentifier + CompositeParams ::= SEQUENCE SIZE (2..MAX) OF AlgorithmIdentifier - CompositeSignatureValue ::= SEQUENCE SIZE (1..MAX) OF BIT STRING + CompositeSignatureValue ::= SEQUENCE SIZE (2..MAX) OF BIT STRING END @@ -868,9 +866,9 @@ Internet-Draft PQ Composite Certs March 2020 electronic mail and VOIP traffic over the past year in pursuit of this document: - John Gray (Entrust Datacard), Serge Mister (Entrust Datacard), Scott - Fluhrer (Cisco Systems), Panos Kampanakis (Cisco Systems), Daniel Van - Geest (ISARA), and Tim Hollebeek (Digicert). + John Gray (Entrust), Serge Mister (Entrust), Scott Fluhrer (Cisco + Systems), Panos Kampanakis (Cisco Systems), Daniel Van Geest (ISARA), + Tim Hollebeek (Digicert), and Francois Rousseau. We are grateful to all, including any contributors who may have been inadvertently omitted from this list. @@ -880,22 +878,24 @@ Internet-Draft PQ Composite Certs March 2020 "Copying always makes things easier and less error prone" - [RFC8411]. -9. References +8.1. Making contributions + + Additional contributions to this draft are weclome. Please see the + working copy of this draft at, as well as open issues at: + + https://github.com/EntrustCorporation/draft-ounsworth-composite-sigs + +9. Normative References -9.1. Normative References - [RFC1421] Linn, J., "Privacy Enhancement for Internet Electronic - Mail: Part I: Message Encryption and Authentication - Procedures", RFC 1421, DOI 10.17487/RFC1421, February - 1993, . -Ounsworth (Editor) & PaExpires September 27, 2020 [Page 16] +Ounsworth & Pala Expires January 13, 2022 [Page 16] -Internet-Draft PQ Composite Certs March 2020 +Internet-Draft PQ Composite Sigs July 2021 [RFC2119] Bradner, S., "Key words for use in RFCs to Indicate @@ -914,10 +914,6 @@ Internet-Draft PQ Composite Certs March 2020 DOI 10.17487/RFC4210, September 2005, . - [RFC4648] Josefsson, S., "The Base16, Base32, and Base64 Data - Encodings", RFC 4648, DOI 10.17487/RFC4648, October 2006, - . - [RFC5280] Cooper, D., Santesson, S., Farrell, S., Boeyen, S., Housley, R., and W. Polk, "Internet X.509 Public Key Infrastructure Certificate and Certificate Revocation List @@ -928,14 +924,6 @@ Internet-Draft PQ Composite Certs March 2020 RFC 5652, DOI 10.17487/RFC5652, September 2009, . - [RFC5958] Turner, S., "Asymmetric Key Packages", RFC 5958, - DOI 10.17487/RFC5958, August 2010, - . - - [RFC7468] Josefsson, S. and S. Leonard, "Textual Encodings of PKIX, - PKCS, and CMS Structures", RFC 7468, DOI 10.17487/RFC7468, - April 2015, . - [RFC8174] Leiba, B., "Ambiguity of Uppercase vs Lowercase in RFC 2119 Key Words", BCP 14, RFC 8174, DOI 10.17487/RFC8174, May 2017, . @@ -945,42 +933,34 @@ Internet-Draft PQ Composite Certs March 2020 RFC 8411, DOI 10.17487/RFC8411, August 2018, . + [X.690] ITU-T, "Information technology - ASN.1 encoding Rules: + Specification of Basic Encoding Rules (BER), Canonical + Encoding Rules (CER) and Distinguished Encoding Rules + (DER)", ISO/IEC 8825-1:2015, November 2015. +Authors' Addresses -Ounsworth (Editor) & PaExpires September 27, 2020 [Page 17] - -Internet-Draft PQ Composite Certs March 2020 - [X.690] ITU-T, "Information technology - ASN.1 encoding Rules: - Specification of Basic Encoding Rules (BER), Canonical - Encoding Rules (CER) and Distinguished Encoding Rules - (DER)", ISO/IEC 8825-1:2015, November 2015. -9.2. Informative References - [I-D.pala-composite-crypto] - Pala, M., "Composite Public Keys and Signatures", draft- - pala-composite-crypto-00 (work in progress), February - 2019. - [I-D.truskovsky-lamps-pq-hybrid-x509] - Truskovsky, A., Geest, D., Fluhrer, S., Kampanakis, P., - Ounsworth, M., and S. Mister, "Multiple Public-Key - Algorithm X.509 Certificates", draft-truskovsky-lamps-pq- - hybrid-x509-01 (work in progress), August 2018. -Authors' Addresses + +Ounsworth & Pala Expires January 13, 2022 [Page 17] + +Internet-Draft PQ Composite Sigs July 2021 + Mike Ounsworth - Entrust Datacard Limited - 1000 Innovation Drive - Ottawa, Ontario K2K 1E3 + Entrust Limited + 2500 Solandt Road -- Suite 100 + Ottawa, Ontario K2K 3G5 Canada - Email: mike.ounsworth@entrustdatacard.com + Email: mike.ounsworth@entrust.com Massimiliano Pala @@ -1005,4 +985,24 @@ Authors' Addresses -Ounsworth (Editor) & PaExpires September 27, 2020 [Page 18] + + + + + + + + + + + + + + + + + + + + +Ounsworth & Pala Expires January 13, 2022 [Page 18] diff --git a/makefile b/makefile index 7ec2128..767e5b1 100755 --- a/makefile +++ b/makefile @@ -27,5 +27,5 @@ all: xml clean: - rm -f $(docName).xml $(docName).html # $(docName).txt - # Explicitely not deleting the .txt because that should be committed to git for other people's ease of editing. + rm -f $(docName).xml #$(docName).html # $(docName).txt + # Explicitely not deleting the .html or .txt because that should be committed to git for other people's ease of editing.