Browse Source

trying to use asn1c to decode DG1

Eric Betts 11 tháng trước cách đây
mục cha
commit
88e7f838eb
76 tập tin đã thay đổi với 15993 bổ sung0 xóa
  1. 6 0
      README.md
  2. 6 0
      application.fam
  3. 101 0
      bsearch.c
  4. 15 0
      eMRTD.asn1
  5. 57 0
      lib/asn1/Application1.c
  6. 51 0
      lib/asn1/Application1.h
  7. 652 0
      lib/asn1/BIT_STRING.c
  8. 46 0
      lib/asn1/BIT_STRING.h
  9. 174 0
      lib/asn1/BIT_STRING_oer.c
  10. 51 0
      lib/asn1/DG1.c
  11. 38 0
      lib/asn1/DG1.h
  12. 32 0
      lib/asn1/MRZ.c
  13. 43 0
      lib/asn1/MRZ.h
  14. 80 0
      lib/asn1/Makefile.am.libasncodec
  15. 2043 0
      lib/asn1/OCTET_STRING.c
  16. 100 0
      lib/asn1/OCTET_STRING.h
  17. 171 0
      lib/asn1/OCTET_STRING_oer.c
  18. 402 0
      lib/asn1/OPEN_TYPE.c
  19. 63 0
      lib/asn1/OPEN_TYPE.h
  20. 92 0
      lib/asn1/OPEN_TYPE_oer.c
  21. 440 0
      lib/asn1/asn_application.c
  22. 169 0
      lib/asn1/asn_application.h
  23. 333 0
      lib/asn1/asn_bit_data.c
  24. 83 0
      lib/asn1/asn_bit_data.h
  25. 108 0
      lib/asn1/asn_codecs.h
  26. 317 0
      lib/asn1/asn_codecs_prim.c
  27. 51 0
      lib/asn1/asn_codecs_prim.h
  28. 47 0
      lib/asn1/asn_internal.c
  29. 152 0
      lib/asn1/asn_internal.h
  30. 50 0
      lib/asn1/asn_ioc.h
  31. 56 0
      lib/asn1/asn_random_fill.c
  32. 51 0
      lib/asn1/asn_random_fill.h
  33. 150 0
      lib/asn1/asn_system.h
  34. 283 0
      lib/asn1/ber_decoder.c
  35. 66 0
      lib/asn1/ber_decoder.h
  36. 168 0
      lib/asn1/ber_tlv_length.c
  37. 50 0
      lib/asn1/ber_tlv_length.h
  38. 144 0
      lib/asn1/ber_tlv_tag.c
  39. 60 0
      lib/asn1/ber_tlv_tag.h
  40. 1340 0
      lib/asn1/constr_CHOICE.c
  41. 78 0
      lib/asn1/constr_CHOICE.h
  42. 380 0
      lib/asn1/constr_CHOICE_oer.c
  43. 1626 0
      lib/asn1/constr_SEQUENCE.c
  44. 66 0
      lib/asn1/constr_SEQUENCE.h
  45. 561 0
      lib/asn1/constr_SEQUENCE_oer.c
  46. 1147 0
      lib/asn1/constr_SET.c
  47. 87 0
      lib/asn1/constr_SET.h
  48. 80 0
      lib/asn1/constr_TYPE.c
  49. 260 0
      lib/asn1/constr_TYPE.h
  50. 93 0
      lib/asn1/constraints.c
  51. 62 0
      lib/asn1/constraints.h
  52. 194 0
      lib/asn1/der_encoder.c
  53. 68 0
      lib/asn1/der_encoder.h
  54. 0 0
      lib/asn1/netinet/in.h
  55. 152 0
      lib/asn1/oer_decoder.c
  56. 72 0
      lib/asn1/oer_decoder.h
  57. 141 0
      lib/asn1/oer_encoder.c
  58. 70 0
      lib/asn1/oer_encoder.h
  59. 122 0
      lib/asn1/oer_support.c
  60. 47 0
      lib/asn1/oer_support.h
  61. 97 0
      lib/asn1/per_decoder.c
  62. 57 0
      lib/asn1/per_decoder.h
  63. 165 0
      lib/asn1/per_encoder.c
  64. 70 0
      lib/asn1/per_encoder.h
  65. 396 0
      lib/asn1/per_opentype.c
  66. 32 0
      lib/asn1/per_opentype.h
  67. 295 0
      lib/asn1/per_support.c
  68. 112 0
      lib/asn1/per_support.h
  69. 369 0
      lib/asn1/xer_decoder.c
  70. 106 0
      lib/asn1/xer_decoder.h
  71. 237 0
      lib/asn1/xer_encoder.c
  72. 83 0
      lib/asn1/xer_encoder.h
  73. 227 0
      lib/asn1/xer_support.c
  74. 55 0
      lib/asn1/xer_support.h
  75. 42 0
      passy_reader.c
  76. 3 0
      passy_reader.h

+ 6 - 0
README.md

@@ -15,3 +15,9 @@
 ## To do
  - Support other country passports
  - Support DG2, the photo
+
+## Generate asn:
+
+```bash
+asn1c -D ./lib/asn1 -no-gen-example -pdu=all eMRTD.asn1
+```

+ 6 - 0
application.fam

@@ -15,4 +15,10 @@ App(
     fap_weburl="https://github.com/bettse/passy",
     fap_icon_assets="images",  # Image assets to compile for this application
     fap_libs=["mbedtls"],
+    fap_private_libs=[
+        Lib(
+            name="asn1",
+            cflags=["-Wno-error", "-DASN_EMIT_DEBUG=0", "-Os", "-DASN_DISABLE_PER_SUPPORT=1"],
+        ),
+    ],
 )

+ 101 - 0
bsearch.c

@@ -0,0 +1,101 @@
+/*
+ * Copyright (c) 1990, 1993
+ *	The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char sccsid[] = "@(#)bsearch.c	8.1 (Berkeley) 6/4/93";
+#endif /* LIBC_SCCS and not lint */
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD: src/lib/libc/stdlib/bsearch.c,v 1.4 2007/01/09 00:28:09 imp Exp $");
+
+#include <stddef.h>
+#include <stdlib.h>
+
+/*
+ * Perform a binary search.
+ *
+ * The code below is a bit sneaky.  After a comparison fails, we
+ * divide the work in half by moving either left or right. If lim
+ * is odd, moving left simply involves halving lim: e.g., when lim
+ * is 5 we look at item 2, so we change lim to 2 so that we will
+ * look at items 0 & 1.  If lim is even, the same applies.  If lim
+ * is odd, moving right again involes halving lim, this time moving
+ * the base up one item past p: e.g., when lim is 5 we change base
+ * to item 3 and make lim 2 so that we will look at items 3 and 4.
+ * If lim is even, however, we have to shrink it by one before
+ * halving: e.g., when lim is 4, we still looked at item 2, so we
+ * have to make lim 3, then halve, obtaining 1, so that we will only
+ * look at item 3.
+ */
+void* bsearch(key, base0, nmemb, size, compar) const void* key;
+const void* base0;
+size_t nmemb;
+size_t size;
+int (*compar)(const void*, const void*);
+{
+    const char* base = base0;
+    size_t lim;
+    int cmp;
+    const void* p;
+
+    for(lim = nmemb; lim != 0; lim >>= 1) {
+        p = base + (lim >> 1) * size;
+        cmp = (*compar)(key, p);
+        if(cmp == 0) return ((void*)p);
+        if(cmp > 0) { /* key > p: move right */
+            base = (char*)p + size;
+            lim--;
+        } /* else move left */
+    }
+    return (NULL);
+}
+
+#ifdef __BLOCKS__
+void* bsearch_b(key, base0, nmemb, size, compar) const void* key;
+const void* base0;
+size_t nmemb;
+size_t size;
+int (^compar)(const void*, const void*);
+{
+    const char* base = base0;
+    size_t lim;
+    int cmp;
+    const void* p;
+
+    for(lim = nmemb; lim != 0; lim >>= 1) {
+        p = base + (lim >> 1) * size;
+        cmp = compar(key, p);
+        if(cmp == 0) return ((void*)p);
+        if(cmp > 0) { /* key > p: move right */
+            base = (char*)p + size;
+            lim--;
+        } /* else move left */
+    }
+    return (NULL);
+}
+#endif /* __BLOCKS__ */

+ 15 - 0
eMRTD.asn1

@@ -0,0 +1,15 @@
+Passport DEFINITIONS ::=
+BEGIN
+
+MRZ ::= [APPLICATION 31] OCTET STRING
+
+Application1 ::= SET {
+  mrz MRZ
+}
+
+-- ASN.1 definition for the MRZ
+DG1 ::= [APPLICATION 1] IMPLICIT SEQUENCE {
+  mrz MRZ
+}
+
+END

+ 57 - 0
lib/asn1/Application1.c

@@ -0,0 +1,57 @@
+/*
+ * Generated by asn1c-0.9.29 (http://lionet.info/asn1c)
+ * From ASN.1 module "Passport"
+ * 	found in "eMRTD.asn1"
+ * 	`asn1c -D ./lib/asn1 -no-gen-example -pdu=all`
+ */
+
+#include "Application1.h"
+
+static asn_TYPE_member_t asn_MBR_Application1_1[] = {
+	{ ATF_NOFLAGS, 0, offsetof(struct Application1, mrz),
+		(ASN_TAG_CLASS_APPLICATION | (31 << 2)),
+		0,
+		&asn_DEF_MRZ,
+		0,
+		{ 0, 0, 0 },
+		0, 0, /* No default value */
+		"mrz"
+		},
+};
+static const ber_tlv_tag_t asn_DEF_Application1_tags_1[] = {
+	(ASN_TAG_CLASS_UNIVERSAL | (17 << 2))
+};
+static const asn_TYPE_tag2member_t asn_MAP_Application1_tag2el_1[] = {
+    { (ASN_TAG_CLASS_APPLICATION | (31 << 2)), 0, 0, 0 } /* mrz */
+};
+static const uint8_t asn_MAP_Application1_mmap_1[(1 + (8 * sizeof(unsigned int)) - 1) / 8] = {
+	(1 << 7)
+};
+static 
+asn_SET_specifics_t asn_SPC_Application1_specs_1 = {
+	sizeof(struct Application1),
+	offsetof(struct Application1, _asn_ctx),
+	offsetof(struct Application1, _presence_map),
+	asn_MAP_Application1_tag2el_1,
+	1,	/* Count of tags in the map */
+	asn_MAP_Application1_tag2el_1,	/* Same as above */
+	1,	/* Count of tags in the CXER map */
+	0,	/* Whether extensible */
+	(const unsigned int *)asn_MAP_Application1_mmap_1	/* Mandatory elements map */
+};
+asn_TYPE_descriptor_t asn_DEF_Application1 = {
+	"Application1",
+	"Application1",
+	&asn_OP_SET,
+	asn_DEF_Application1_tags_1,
+	sizeof(asn_DEF_Application1_tags_1)
+		/sizeof(asn_DEF_Application1_tags_1[0]), /* 1 */
+	asn_DEF_Application1_tags_1,	/* Same as above */
+	sizeof(asn_DEF_Application1_tags_1)
+		/sizeof(asn_DEF_Application1_tags_1[0]), /* 1 */
+	{ 0, 0, SET_constraint },
+	asn_MBR_Application1_1,
+	1,	/* Elements count */
+	&asn_SPC_Application1_specs_1	/* Additional specs */
+};
+

+ 51 - 0
lib/asn1/Application1.h

@@ -0,0 +1,51 @@
+/*
+ * Generated by asn1c-0.9.29 (http://lionet.info/asn1c)
+ * From ASN.1 module "Passport"
+ * 	found in "eMRTD.asn1"
+ * 	`asn1c -D ./lib/asn1 -no-gen-example -pdu=all`
+ */
+
+#ifndef	_Application1_H_
+#define	_Application1_H_
+
+
+#include <asn_application.h>
+
+/* Including external dependencies */
+#include "MRZ.h"
+#include <constr_SET.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Dependencies */
+
+/*
+ * Method of determining the components presence
+ */
+typedef enum Application1_PR {
+	Application1_PR_mrz,	/* Member mrz is present */
+} Application1_PR;
+
+/* Application1 */
+typedef struct Application1 {
+	MRZ_t	 mrz;
+	
+	/* Presence bitmask: ASN_SET_ISPRESENT(pApplication1, Application1_PR_x) */
+	unsigned int _presence_map
+		[((1+(8*sizeof(unsigned int))-1)/(8*sizeof(unsigned int)))];
+	
+	/* Context for parsing across buffer boundaries */
+	asn_struct_ctx_t _asn_ctx;
+} Application1_t;
+
+/* Implementation */
+extern asn_TYPE_descriptor_t asn_DEF_Application1;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif	/* _Application1_H_ */
+#include <asn_internal.h>

+ 652 - 0
lib/asn1/BIT_STRING.c

@@ -0,0 +1,652 @@
+/*-
+ * Copyright (c) 2003, 2004 Lev Walkin <vlm@lionet.info>. All rights reserved.
+ * Redistribution and modifications are permitted subject to BSD license.
+ */
+#include <asn_internal.h>
+#include <BIT_STRING.h>
+#include <asn_internal.h>
+
+/*
+ * BIT STRING basic type description.
+ */
+static const ber_tlv_tag_t asn_DEF_BIT_STRING_tags[] = {
+	(ASN_TAG_CLASS_UNIVERSAL | (3 << 2))
+};
+asn_OCTET_STRING_specifics_t asn_SPC_BIT_STRING_specs = {
+	sizeof(BIT_STRING_t),
+	offsetof(BIT_STRING_t, _asn_ctx),
+	ASN_OSUBV_BIT
+};
+asn_TYPE_operation_t asn_OP_BIT_STRING = {
+	OCTET_STRING_free,         /* Implemented in terms of OCTET STRING */
+	BIT_STRING_print,
+	BIT_STRING_compare,
+	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,
+#ifdef	ASN_DISABLE_OER_SUPPORT
+	0,
+	0,
+#else
+	BIT_STRING_decode_oer,
+	BIT_STRING_encode_oer,
+#endif  /* ASN_DISABLE_OER_SUPPORT */
+#ifdef	ASN_DISABLE_PER_SUPPORT
+	0,
+	0,
+#else
+	BIT_STRING_decode_uper,	/* Unaligned PER decoder */
+	BIT_STRING_encode_uper,	/* Unaligned PER encoder */
+#endif  /* ASN_DISABLE_PER_SUPPORT */
+	BIT_STRING_random_fill,
+	0	/* Use generic outmost tag fetcher */
+};
+asn_TYPE_descriptor_t asn_DEF_BIT_STRING = {
+	"BIT STRING",
+	"BIT_STRING",
+	&asn_OP_BIT_STRING,
+	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, 0, BIT_STRING_constraint },
+	0, 0,	/* No members */
+	&asn_SPC_BIT_STRING_specs
+};
+
+/*
+ * BIT STRING generic constraint.
+ */
+int
+BIT_STRING_constraint(const 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 const 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(const asn_TYPE_descriptor_t *td, const 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) {
+			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);
+	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;
+		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(const 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("<absent>", 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;
+	}
+
+    if(st->bits_unused) {
+        int ret = snprintf(scratch, sizeof(scratch), " (%d bit%s unused)",
+                           st->bits_unused, st->bits_unused == 1 ? "" : "s");
+        assert(ret > 0 && ret < (ssize_t)sizeof(scratch));
+        if(ret > 0 && ret < (ssize_t)sizeof(scratch)
+           && cb(scratch, ret, app_key) < 0)
+            return -1;
+    }
+
+	return 0;
+}
+
+/*
+ * Non-destructively remove the trailing 0-bits from the given bit string.
+ */
+static const BIT_STRING_t *
+BIT_STRING__compactify(const BIT_STRING_t *st, BIT_STRING_t *tmp) {
+    const uint8_t *b;
+    union {
+        const uint8_t *c_buf;
+        uint8_t *nc_buf;
+    } unconst;
+
+    if(st->size == 0) {
+        assert(st->bits_unused == 0);
+        return st;
+    } else {
+        for(b = &st->buf[st->size - 1]; b > st->buf && *b == 0; b--) {
+            ;
+        }
+        /* b points to the last byte which may contain data */
+        if(*b) {
+            int unused = 7;
+            uint8_t v = *b;
+            v &= -(int8_t)v;
+            if(v & 0x0F) unused -= 4;
+            if(v & 0x33) unused -= 2;
+            if(v & 0x55) unused -= 1;
+            tmp->size = b-st->buf + 1;
+            tmp->bits_unused = unused;
+        } else {
+            tmp->size = b-st->buf;
+            tmp->bits_unused = 0;
+        }
+
+        assert(b >= st->buf);
+    }
+
+    unconst.c_buf = st->buf;
+    tmp->buf = unconst.nc_buf;
+    return tmp;
+}
+
+/*
+ * Lexicographically compare the common prefix of both strings,
+ * and if it is the same return -1 for the smallest string.
+ */
+int
+BIT_STRING_compare(const asn_TYPE_descriptor_t *td, const void *aptr,
+                   const void *bptr) {
+    /*
+     * Remove information about trailing bits, since
+     * X.680 (08/2015) #22.7 "ensure that different semantics are not"
+     * "associated with [values that differ only in] the trailing 0 bits."
+     */
+    BIT_STRING_t compact_a, compact_b;
+    const BIT_STRING_t *a = BIT_STRING__compactify(aptr, &compact_a);
+    const BIT_STRING_t *b = BIT_STRING__compactify(bptr, &compact_b);
+    const asn_OCTET_STRING_specifics_t *specs = td->specifics;
+
+    assert(specs && specs->subvariant == ASN_OSUBV_BIT);
+
+    if(a && b) {
+        size_t common_prefix_size = a->size <= b->size ? a->size : b->size;
+        int ret = memcmp(a->buf, b->buf, common_prefix_size);
+        if(ret == 0) {
+            /* Figure out which string with equal prefixes is longer. */
+            if(a->size < b->size) {
+                return -1;
+            } else if(a->size > b->size) {
+                return 1;
+            } else {
+                /* Figure out how many unused bits */
+                if(a->bits_unused > b->bits_unused) {
+                    return -1;
+                } else if(a->bits_unused < b->bits_unused) {
+                    return 1;
+                } else {
+                    return 0;
+                }
+            }
+        } else {
+            return ret;
+        }
+    } else if(!a && !b) {
+        return 0;
+    } else if(!a) {
+        return -1;
+    } else {
+        return 1;
+    }
+}
+
+#ifndef  ASN_DISABLE_PER_SUPPORT
+
+#undef  RETURN
+#define RETURN(_code)                       \
+    do {                                    \
+        asn_dec_rval_t tmprval;             \
+        tmprval.code = _code;               \
+        tmprval.consumed = consumed_myself; \
+        return tmprval;                     \
+    } while(0)
+
+static asn_per_constraint_t asn_DEF_BIT_STRING_constraint_size = {
+    APC_SEMI_CONSTRAINED, -1, -1, 0, 0};
+
+asn_dec_rval_t
+BIT_STRING_decode_uper(const asn_codec_ctx_t *opt_codec_ctx,
+                       const asn_TYPE_descriptor_t *td,
+                       const asn_per_constraints_t *constraints, void **sptr,
+                       asn_per_data_t *pd) {
+    const asn_OCTET_STRING_specifics_t *specs = td->specifics
+		? (const asn_OCTET_STRING_specifics_t *)td->specifics
+		: &asn_SPC_BIT_STRING_specs;
+    const asn_per_constraints_t *pc =
+        constraints ? constraints : td->encoding_constraints.per_constraints;
+	const 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;
+
+	(void)opt_codec_ctx;
+
+	if(pc) {
+		csiz = &pc->size;
+	} else {
+		csiz = &asn_DEF_BIT_STRING_constraint_size;
+	}
+
+	if(specs->subvariant != ASN_OSUBV_BIT) {
+		ASN_DEBUG("Subvariant %d is not BIT OSUBV_BIT", specs->subvariant);
+		RETURN(RC_FAIL);
+    }
+
+	/*
+	 * 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_BIT_STRING_constraint_size;
+		}
+	}
+
+	if(csiz->effective_bits >= 0) {
+		FREEMEM(st->buf);
+        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;
+        ASN_DEBUG("Encoding BIT STRING size %ld", csiz->upper_bound);
+        ret = per_get_many_bits(pd, st->buf, 0, csiz->upper_bound);
+		if(ret < 0) RETURN(RC_WMORE);
+		consumed_myself += csiz->upper_bound;
+		st->buf[st->size] = 0;
+        st->bits_unused = (8 - (csiz->upper_bound & 0x7)) & 0x7;
+        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, csiz->lower_bound,
+		                          &repeat);
+		if(raw_len < 0) RETURN(RC_WMORE);
+        if(raw_len == 0 && st->buf) break;
+
+		ASN_DEBUG("Got PER length eb %ld, len %ld, %s (%s)",
+			(long)csiz->effective_bits, (long)raw_len,
+			repeat ? "repeat" : "once", td->name);
+        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;
+
+        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
+BIT_STRING_encode_uper(const asn_TYPE_descriptor_t *td,
+                       const asn_per_constraints_t *constraints,
+                       const void *sptr, asn_per_outp_t *po) {
+    const asn_OCTET_STRING_specifics_t *specs =
+        td->specifics ? (const asn_OCTET_STRING_specifics_t *)td->specifics
+                      : &asn_SPC_BIT_STRING_specs;
+    const asn_per_constraints_t *pc =
+        constraints ? constraints : td->encoding_constraints.per_constraints;
+	const asn_per_constraint_t *csiz;
+	const BIT_STRING_t *st = (const BIT_STRING_t *)sptr;
+	BIT_STRING_t compact_bstr;  /* Do not modify this directly! */
+	asn_enc_rval_t er = { 0, 0, 0 };
+	int inext = 0;		/* Lies not within extension root */
+	size_t size_in_bits;
+	const uint8_t *buf;
+	int ret;
+	int ct_extensible;
+
+	if(!st || (!st->buf && st->size))
+		ASN__ENCODE_FAILED;
+
+	if(specs->subvariant == ASN_OSUBV_BIT) {
+        if((st->size == 0 && st->bits_unused) || (st->bits_unused & ~7))
+            ASN__ENCODE_FAILED;
+    } else {
+		ASN__ENCODE_FAILED;
+    }
+
+	if(pc) {
+        csiz = &pc->size;
+    } else {
+		csiz = &asn_DEF_BIT_STRING_constraint_size;
+	}
+	ct_extensible = csiz->flags & APC_EXTENSIBLE;
+
+    /* Figure out the size without the trailing bits */
+    st = BIT_STRING__compactify(st, &compact_bstr);
+    size_in_bits = 8 * st->size - st->bits_unused;
+
+    ASN_DEBUG(
+        "Encoding %s into %" ASN_PRI_SIZE " bits"
+        " (%ld..%ld, effective %d)%s",
+        td->name, size_in_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((ssize_t)size_in_bits > csiz->upper_bound) {
+            if(ct_extensible) {
+                csiz = &asn_DEF_BIT_STRING_constraint_size;
+                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;
+	}
+
+    if(csiz->effective_bits >= 0 && !inext) {
+        int add_trailer = (ssize_t)size_in_bits < csiz->lower_bound;
+        ASN_DEBUG(
+            "Encoding %" ASN_PRI_SIZE " bytes (%ld), length (in %d bits) trailer %d; actual "
+            "value %" ASN_PRI_SSIZE "",
+            st->size, size_in_bits - csiz->lower_bound, csiz->effective_bits,
+            add_trailer,
+            add_trailer ? 0 : (ssize_t)size_in_bits - csiz->lower_bound);
+        ret = per_put_few_bits(
+            po, add_trailer ? 0 : (ssize_t)size_in_bits - csiz->lower_bound,
+            csiz->effective_bits);
+        if(ret) ASN__ENCODE_FAILED;
+        ret = per_put_many_bits(po, st->buf, size_in_bits);
+        if(ret) ASN__ENCODE_FAILED;
+        if(add_trailer) {
+            static const uint8_t zeros[16];
+            size_t trailing_zero_bits = csiz->lower_bound - size_in_bits;
+            while(trailing_zero_bits > 0) {
+                if(trailing_zero_bits > 8 * sizeof(zeros)) {
+                    ret = per_put_many_bits(po, zeros, 8 * sizeof(zeros));
+                    trailing_zero_bits -= 8 * sizeof(zeros);
+                } else {
+                    ret = per_put_many_bits(po, zeros, trailing_zero_bits);
+                    trailing_zero_bits = 0;
+                }
+                if(ret) ASN__ENCODE_FAILED;
+            }
+        }
+        ASN__ENCODED_OK(er);
+    }
+
+    ASN_DEBUG("Encoding %" ASN_PRI_SIZE " bytes", st->size);
+
+    buf = st->buf;
+    do {
+        int need_eom = 0;
+        ssize_t maySave = uper_put_length(po, size_in_bits, &need_eom);
+        if(maySave < 0) ASN__ENCODE_FAILED;
+
+        ASN_DEBUG("Encoding %" ASN_PRI_SSIZE " of %" ASN_PRI_SIZE "", maySave, size_in_bits);
+
+        ret = per_put_many_bits(po, buf, maySave);
+        if(ret) ASN__ENCODE_FAILED;
+
+        buf += maySave >> 3;
+        size_in_bits -= maySave;
+        assert(!(maySave & 0x07) || !size_in_bits);
+        if(need_eom && uper_put_length(po, 0, 0))
+            ASN__ENCODE_FAILED; /* End of Message length */
+    } while(size_in_bits);
+
+    ASN__ENCODED_OK(er);
+}
+
+#endif  /* ASN_DISABLE_PER_SUPPORT */
+
+asn_random_fill_result_t
+BIT_STRING_random_fill(const asn_TYPE_descriptor_t *td, void **sptr,
+                       const asn_encoding_constraints_t *constraints,
+                       size_t max_length) {
+    const asn_OCTET_STRING_specifics_t *specs =
+        td->specifics ? (const asn_OCTET_STRING_specifics_t *)td->specifics
+                      : &asn_SPC_BIT_STRING_specs;
+    asn_random_fill_result_t result_ok = {ARFILL_OK, 1};
+    asn_random_fill_result_t result_failed = {ARFILL_FAILED, 0};
+    asn_random_fill_result_t result_skipped = {ARFILL_SKIPPED, 0};
+    static unsigned lengths[] = {0,     1,     2,     3,     4,     8,
+                                 126,   127,   128,   16383, 16384, 16385,
+                                 65534, 65535, 65536, 65537};
+    uint8_t *buf;
+    uint8_t *bend;
+    uint8_t *b;
+    size_t rnd_bits, rnd_len;
+    BIT_STRING_t *st;
+
+    if(max_length == 0) return result_skipped;
+
+    switch(specs->subvariant) {
+    case ASN_OSUBV_ANY:
+        return result_failed;
+    case ASN_OSUBV_BIT:
+        break;
+    default:
+        break;
+    }
+
+    /* Figure out how far we should go */
+    rnd_bits = lengths[asn_random_between(
+        0, sizeof(lengths) / sizeof(lengths[0]) - 1)];
+    if(!constraints || !constraints->per_constraints)
+        constraints = &td->encoding_constraints;
+    if(constraints->per_constraints) {
+        const asn_per_constraint_t *pc = &constraints->per_constraints->size;
+        if(pc->flags & APC_CONSTRAINED) {
+            long suggested_upper_bound = pc->upper_bound < (ssize_t)max_length
+                                             ? pc->upper_bound
+                                             : (ssize_t)max_length;
+            if(max_length < (size_t)pc->lower_bound) {
+                return result_skipped;
+            }
+            if(pc->flags & APC_EXTENSIBLE) {
+                switch(asn_random_between(0, 5)) {
+                case 0:
+                    if(pc->lower_bound > 0) {
+                        rnd_bits = pc->lower_bound - 1;
+                        break;
+                    }
+                    /* Fall through */
+                case 1:
+                    rnd_bits = pc->upper_bound + 1;
+                    break;
+                case 2:
+                    /* Keep rnd_bits from the table */
+                    if(rnd_bits < max_length) {
+                        break;
+                    }
+                    /* Fall through */
+                default:
+                    rnd_bits = asn_random_between(pc->lower_bound,
+                                                  suggested_upper_bound);
+                }
+            } else {
+                rnd_bits =
+                    asn_random_between(pc->lower_bound, suggested_upper_bound);
+            }
+        } else {
+            rnd_bits = asn_random_between(0, max_length - 1);
+        }
+    } else if(rnd_bits >= max_length) {
+        rnd_bits = asn_random_between(0, max_length - 1);
+    }
+
+    rnd_len = (rnd_bits + 7) / 8;
+    buf = CALLOC(1, rnd_len + 1);
+    if(!buf) return result_failed;
+
+    bend = &buf[rnd_len];
+
+    for(b = buf; b < bend; b++) {
+        *(uint8_t *)b = asn_random_between(0, 255);
+    }
+    *b = 0; /* Zero-terminate just in case. */
+
+    if(*sptr) {
+        st = *sptr;
+        FREEMEM(st->buf);
+    } else {
+        st = (BIT_STRING_t *)(*sptr = CALLOC(1, specs->struct_size));
+        if(!st) {
+            FREEMEM(buf);
+            return result_failed;
+        }
+    }
+
+    st->buf = buf;
+    st->size = rnd_len;
+    st->bits_unused = (8 - (rnd_bits & 0x7)) & 0x7;
+    if(st->bits_unused) {
+        assert(st->size > 0);
+        st->buf[st->size-1] &= 0xff << st->bits_unused;
+    }
+
+    result_ok.length = st->size;
+    return result_ok;
+}

+ 46 - 0
lib/asn1/BIT_STRING.h

@@ -0,0 +1,46 @@
+/*-
+ * Copyright (c) 2003-2017 Lev Walkin <vlm@lionet.info>. All rights reserved.
+ * Redistribution and modifications are permitted subject to BSD license.
+ */
+#ifndef	_BIT_STRING_H_
+#define	_BIT_STRING_H_
+
+#include <OCTET_STRING.h>	/* Some help from OCTET STRING */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct BIT_STRING_s {
+	uint8_t *buf;	/* BIT STRING body */
+	size_t 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;
+extern asn_TYPE_operation_t asn_OP_BIT_STRING;
+extern asn_OCTET_STRING_specifics_t asn_SPC_BIT_STRING_specs;
+
+asn_struct_print_f BIT_STRING_print;	/* Human-readable output */
+asn_struct_compare_f BIT_STRING_compare;
+asn_constr_check_f BIT_STRING_constraint;
+xer_type_encoder_f BIT_STRING_encode_xer;
+oer_type_decoder_f BIT_STRING_decode_oer;
+oer_type_encoder_f BIT_STRING_encode_oer;
+per_type_decoder_f BIT_STRING_decode_uper;
+per_type_encoder_f BIT_STRING_encode_uper;
+asn_random_fill_f  BIT_STRING_random_fill;
+
+#define BIT_STRING_free              OCTET_STRING_free
+#define BIT_STRING_decode_ber        OCTET_STRING_decode_ber
+#define BIT_STRING_encode_der        OCTET_STRING_encode_der
+#define BIT_STRING_decode_xer        OCTET_STRING_decode_xer_binary
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif	/* _BIT_STRING_H_ */

+ 174 - 0
lib/asn1/BIT_STRING_oer.c

@@ -0,0 +1,174 @@
+/*
+ * Copyright (c) 2017 Lev Walkin <vlm@lionet.info>.
+ * All rights reserved.
+ * Redistribution and modifications are permitted subject to BSD license.
+ */
+#ifndef ASN_DISABLE_OER_SUPPORT
+
+#include <asn_internal.h>
+#include <BIT_STRING.h>
+#include <errno.h>
+
+asn_dec_rval_t
+BIT_STRING_decode_oer(const asn_codec_ctx_t *opt_codec_ctx,
+                      const asn_TYPE_descriptor_t *td,
+                      const asn_oer_constraints_t *constraints, void **sptr,
+                      const void *ptr, size_t size) {
+    BIT_STRING_t *st = (BIT_STRING_t *)*sptr;
+    const asn_oer_constraints_t *cts =
+        constraints ? constraints : td->encoding_constraints.oer_constraints;
+    ssize_t ct_size = cts ? cts->size : -1;
+    asn_dec_rval_t rval = {RC_OK, 0};
+    size_t expected_length = 0;
+
+    (void)opt_codec_ctx;
+
+    if(!st) {
+        st = (BIT_STRING_t *)(*sptr = CALLOC(1, sizeof(*st)));
+        if(!st) ASN__DECODE_FAILED;
+    }
+
+    if(ct_size >= 0) {
+        expected_length = (ct_size + 7) >> 3;
+        st->bits_unused = (8 - (ct_size & 7)) & 7;
+    } else {
+        /*
+         * X.696 (08/2015) #13.3.1
+         * Encode length determinant as _number of octets_, but only
+         * if upper bound is not equal to lower bound.
+         */
+        ssize_t len_len = oer_fetch_length(ptr, size, &expected_length);
+        if(len_len > 0) {
+            ptr = (const char *)ptr + len_len;
+            size -= len_len;
+        } else if(len_len == 0) {
+            ASN__DECODE_STARVED;
+        } else if(len_len < 0) {
+            ASN__DECODE_FAILED;
+        }
+
+        if(expected_length < 1) {
+            ASN__DECODE_FAILED;
+        } else if(expected_length > size) {
+            ASN__DECODE_STARVED;
+        }
+
+        st->bits_unused = ((const uint8_t *)ptr)[0];
+        if(st->bits_unused & ~7) {
+            ASN_DEBUG("%s: unused bits outside of 0..7 range", td->name);
+            ASN__DECODE_FAILED;
+        }
+        ptr = (const char *)ptr + 1;
+        size--;
+        expected_length--;
+        rval.consumed = len_len + 1;
+    }
+
+    if(size < expected_length) {
+        ASN__DECODE_STARVED;
+    } else {
+        uint8_t *buf = MALLOC(expected_length + 1);
+        if(buf == NULL) {
+            ASN__DECODE_FAILED;
+        } else {
+            memcpy(buf, ptr, expected_length);
+            buf[expected_length] = '\0';
+        }
+        FREEMEM(st->buf);
+        st->buf = buf;
+        st->size = expected_length;
+        if(expected_length > 0) {
+            buf[expected_length - 1] &= (0xff << st->bits_unused);
+        }
+
+        rval.consumed += expected_length;
+        return rval;
+    }
+}
+
+/*
+ * Encode as Canonical OER.
+ */
+asn_enc_rval_t
+BIT_STRING_encode_oer(const asn_TYPE_descriptor_t *td,
+                      const asn_oer_constraints_t *constraints,
+                      const void *sptr, asn_app_consume_bytes_f *cb,
+                      void *app_key) {
+    const BIT_STRING_t *st = (const BIT_STRING_t *)sptr;
+    asn_enc_rval_t erval = {0, 0, 0};
+    const asn_oer_constraints_t *cts =
+        constraints ? constraints : td->encoding_constraints.oer_constraints;
+    ssize_t ct_size = cts ? cts->size : -1;
+    size_t trailing_zeros = 0;
+    int fix_last_byte = 0;
+
+    if(!st) ASN__ENCODE_FAILED;
+
+    if(st->bits_unused & ~7) {
+        ASN_DEBUG("BIT STRING unused bits %d out of 0..7 range",
+                  st->bits_unused);
+        ASN__ENCODE_FAILED;
+    }
+    if(st->bits_unused && !(st->size && st->buf)) {
+        ASN_DEBUG("BIT STRING %s size 0 can't support unused bits %d", td->name,
+                  st->bits_unused);
+        ASN__ENCODE_FAILED;
+    }
+
+    if(ct_size >= 0) {
+        size_t ct_bytes = (ct_size + 7) >> 3;
+        if(st->size > ct_bytes) {
+            ASN_DEBUG("More bits in BIT STRING %s (%" ASN_PRI_SSIZE ") than constrained %" ASN_PRI_SSIZE "",
+                      td->name, 8 * st->size - st->bits_unused, ct_size);
+            ASN__ENCODE_FAILED;
+        }
+        trailing_zeros = ct_bytes - st->size;   /* Allow larger constraint */
+    } else {
+        uint8_t ub = st->bits_unused & 7;
+        ssize_t len_len = oer_serialize_length(1 + st->size, cb, app_key);
+        if(len_len < 0) ASN__ENCODE_FAILED;
+        if(cb(&ub, 1, app_key) < 0) {
+            ASN__ENCODE_FAILED;
+        }
+        erval.encoded += len_len + 1;
+    }
+
+    if(st->bits_unused) {
+        if(st->buf[st->size - 1] & (0xff << st->bits_unused)) {
+            fix_last_byte = 1;
+        }
+    }
+
+    if(cb(st->buf, st->size - fix_last_byte, app_key) < 0) {
+        ASN__ENCODE_FAILED;
+    }
+
+    if(fix_last_byte) {
+        uint8_t b = st->buf[st->size - 1] & (0xff << st->bits_unused);
+        if(cb(&b, 1, app_key) < 0) {
+            ASN__ENCODE_FAILED;
+        }
+    }
+
+    erval.encoded += st->size;
+
+    if(trailing_zeros) {
+        static uint8_t zeros[16];
+        while(trailing_zeros > 0) {
+            int ret;
+            if(trailing_zeros < sizeof(zeros)) {
+                ret = cb(zeros, trailing_zeros, app_key);
+                erval.encoded += trailing_zeros;
+            } else {
+                ret = cb(zeros, sizeof(zeros), app_key);
+                erval.encoded += sizeof(zeros);
+            }
+            if(ret < 0) ASN__ENCODE_FAILED;
+        }
+    }
+
+    return erval;
+}
+
+
+#endif  /* ASN_DISABLE_OER_SUPPORT */

+ 51 - 0
lib/asn1/DG1.c

@@ -0,0 +1,51 @@
+/*
+ * Generated by asn1c-0.9.29 (http://lionet.info/asn1c)
+ * From ASN.1 module "Passport"
+ * 	found in "eMRTD.asn1"
+ * 	`asn1c -D ./lib/asn1 -no-gen-example -pdu=all`
+ */
+
+#include "DG1.h"
+
+static asn_TYPE_member_t asn_MBR_DG1_1[] = {
+	{ ATF_NOFLAGS, 0, offsetof(struct DG1, mrz),
+		(ASN_TAG_CLASS_APPLICATION | (31 << 2)),
+		0,
+		&asn_DEF_MRZ,
+		0,
+		{ 0, 0, 0 },
+		0, 0, /* No default value */
+		"mrz"
+		},
+};
+static const ber_tlv_tag_t asn_DEF_DG1_tags_1[] = {
+	(ASN_TAG_CLASS_APPLICATION | (1 << 2)),
+	(ASN_TAG_CLASS_UNIVERSAL | (16 << 2))
+};
+static const asn_TYPE_tag2member_t asn_MAP_DG1_tag2el_1[] = {
+    { (ASN_TAG_CLASS_APPLICATION | (31 << 2)), 0, 0, 0 } /* mrz */
+};
+static asn_SEQUENCE_specifics_t asn_SPC_DG1_specs_1 = {
+	sizeof(struct DG1),
+	offsetof(struct DG1, _asn_ctx),
+	asn_MAP_DG1_tag2el_1,
+	1,	/* Count of tags in the map */
+	0, 0, 0,	/* Optional elements (not needed) */
+	-1,	/* First extension addition */
+};
+asn_TYPE_descriptor_t asn_DEF_DG1 = {
+	"DG1",
+	"DG1",
+	&asn_OP_SEQUENCE,
+	asn_DEF_DG1_tags_1,
+	sizeof(asn_DEF_DG1_tags_1)
+		/sizeof(asn_DEF_DG1_tags_1[0]) - 1, /* 1 */
+	asn_DEF_DG1_tags_1,	/* Same as above */
+	sizeof(asn_DEF_DG1_tags_1)
+		/sizeof(asn_DEF_DG1_tags_1[0]), /* 2 */
+	{ 0, 0, SEQUENCE_constraint },
+	asn_MBR_DG1_1,
+	1,	/* Elements count */
+	&asn_SPC_DG1_specs_1	/* Additional specs */
+};
+

+ 38 - 0
lib/asn1/DG1.h

@@ -0,0 +1,38 @@
+/*
+ * Generated by asn1c-0.9.29 (http://lionet.info/asn1c)
+ * From ASN.1 module "Passport"
+ * 	found in "eMRTD.asn1"
+ * 	`asn1c -D ./lib/asn1 -no-gen-example -pdu=all`
+ */
+
+#ifndef	_DG1_H_
+#define	_DG1_H_
+
+
+#include <asn_application.h>
+
+/* Including external dependencies */
+#include "MRZ.h"
+#include <constr_SEQUENCE.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* DG1 */
+typedef struct DG1 {
+	MRZ_t	 mrz;
+	
+	/* Context for parsing across buffer boundaries */
+	asn_struct_ctx_t _asn_ctx;
+} DG1_t;
+
+/* Implementation */
+extern asn_TYPE_descriptor_t asn_DEF_DG1;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif	/* _DG1_H_ */
+#include <asn_internal.h>

+ 32 - 0
lib/asn1/MRZ.c

@@ -0,0 +1,32 @@
+/*
+ * Generated by asn1c-0.9.29 (http://lionet.info/asn1c)
+ * From ASN.1 module "Passport"
+ * 	found in "eMRTD.asn1"
+ * 	`asn1c -D ./lib/asn1 -no-gen-example -pdu=all`
+ */
+
+#include "MRZ.h"
+
+/*
+ * This type is implemented using OCTET_STRING,
+ * so here we adjust the DEF accordingly.
+ */
+static const ber_tlv_tag_t asn_DEF_MRZ_tags_1[] = {
+	(ASN_TAG_CLASS_APPLICATION | (31 << 2)),
+	(ASN_TAG_CLASS_UNIVERSAL | (4 << 2))
+};
+asn_TYPE_descriptor_t asn_DEF_MRZ = {
+	"MRZ",
+	"MRZ",
+	&asn_OP_OCTET_STRING,
+	asn_DEF_MRZ_tags_1,
+	sizeof(asn_DEF_MRZ_tags_1)
+		/sizeof(asn_DEF_MRZ_tags_1[0]), /* 2 */
+	asn_DEF_MRZ_tags_1,	/* Same as above */
+	sizeof(asn_DEF_MRZ_tags_1)
+		/sizeof(asn_DEF_MRZ_tags_1[0]), /* 2 */
+	{ 0, 0, OCTET_STRING_constraint },
+	0, 0,	/* No members */
+	&asn_SPC_OCTET_STRING_specs	/* Additional specs */
+};
+

+ 43 - 0
lib/asn1/MRZ.h

@@ -0,0 +1,43 @@
+/*
+ * Generated by asn1c-0.9.29 (http://lionet.info/asn1c)
+ * From ASN.1 module "Passport"
+ * 	found in "eMRTD.asn1"
+ * 	`asn1c -D ./lib/asn1 -no-gen-example -pdu=all`
+ */
+
+#ifndef	_MRZ_H_
+#define	_MRZ_H_
+
+
+#include <asn_application.h>
+
+/* Including external dependencies */
+#include <OCTET_STRING.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* MRZ */
+typedef OCTET_STRING_t	 MRZ_t;
+
+/* Implementation */
+extern asn_TYPE_descriptor_t asn_DEF_MRZ;
+asn_struct_free_f MRZ_free;
+asn_struct_print_f MRZ_print;
+asn_constr_check_f MRZ_constraint;
+ber_type_decoder_f MRZ_decode_ber;
+der_type_encoder_f MRZ_encode_der;
+xer_type_decoder_f MRZ_decode_xer;
+xer_type_encoder_f MRZ_encode_xer;
+oer_type_decoder_f MRZ_decode_oer;
+oer_type_encoder_f MRZ_encode_oer;
+per_type_decoder_f MRZ_decode_uper;
+per_type_encoder_f MRZ_encode_uper;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif	/* _MRZ_H_ */
+#include <asn_internal.h>

+ 80 - 0
lib/asn1/Makefile.am.libasncodec

@@ -0,0 +1,80 @@
+ASN_MODULE_SRCS=	\
+	./lib/asn1/MRZ.c	\
+	./lib/asn1/Application1.c	\
+	./lib/asn1/DG1.c
+
+ASN_MODULE_HDRS=	\
+	./lib/asn1/MRZ.h	\
+	./lib/asn1/Application1.h	\
+	./lib/asn1/DG1.h
+
+ASN_MODULE_HDRS+=./lib/asn1/OPEN_TYPE.h
+ASN_MODULE_SRCS+=./lib/asn1/OPEN_TYPE.c
+ASN_MODULE_HDRS+=./lib/asn1/constr_CHOICE.h
+ASN_MODULE_SRCS+=./lib/asn1/constr_CHOICE.c
+ASN_MODULE_HDRS+=./lib/asn1/constr_SEQUENCE.h
+ASN_MODULE_SRCS+=./lib/asn1/constr_SEQUENCE.c
+ASN_MODULE_HDRS+=./lib/asn1/constr_SET.h
+ASN_MODULE_SRCS+=./lib/asn1/constr_SET.c
+ASN_MODULE_HDRS+=./lib/asn1/asn_application.h
+ASN_MODULE_SRCS+=./lib/asn1/asn_application.c
+ASN_MODULE_HDRS+=./lib/asn1/asn_ioc.h
+ASN_MODULE_HDRS+=./lib/asn1/asn_system.h
+ASN_MODULE_HDRS+=./lib/asn1/asn_codecs.h
+ASN_MODULE_HDRS+=./lib/asn1/asn_internal.h
+ASN_MODULE_SRCS+=./lib/asn1/asn_internal.c
+ASN_MODULE_HDRS+=./lib/asn1/asn_random_fill.h
+ASN_MODULE_SRCS+=./lib/asn1/asn_random_fill.c
+ASN_MODULE_HDRS+=./lib/asn1/asn_bit_data.h
+ASN_MODULE_SRCS+=./lib/asn1/asn_bit_data.c
+ASN_MODULE_HDRS+=./lib/asn1/OCTET_STRING.h
+ASN_MODULE_SRCS+=./lib/asn1/OCTET_STRING.c
+ASN_MODULE_HDRS+=./lib/asn1/BIT_STRING.h
+ASN_MODULE_SRCS+=./lib/asn1/BIT_STRING.c
+ASN_MODULE_SRCS+=./lib/asn1/asn_codecs_prim.c
+ASN_MODULE_HDRS+=./lib/asn1/asn_codecs_prim.h
+ASN_MODULE_HDRS+=./lib/asn1/ber_tlv_length.h
+ASN_MODULE_SRCS+=./lib/asn1/ber_tlv_length.c
+ASN_MODULE_HDRS+=./lib/asn1/ber_tlv_tag.h
+ASN_MODULE_SRCS+=./lib/asn1/ber_tlv_tag.c
+ASN_MODULE_HDRS+=./lib/asn1/ber_decoder.h
+ASN_MODULE_SRCS+=./lib/asn1/ber_decoder.c
+ASN_MODULE_HDRS+=./lib/asn1/der_encoder.h
+ASN_MODULE_SRCS+=./lib/asn1/der_encoder.c
+ASN_MODULE_HDRS+=./lib/asn1/constr_TYPE.h
+ASN_MODULE_SRCS+=./lib/asn1/constr_TYPE.c
+ASN_MODULE_HDRS+=./lib/asn1/constraints.h
+ASN_MODULE_SRCS+=./lib/asn1/constraints.c
+ASN_MODULE_HDRS+=./lib/asn1/xer_support.h
+ASN_MODULE_SRCS+=./lib/asn1/xer_support.c
+ASN_MODULE_HDRS+=./lib/asn1/xer_decoder.h
+ASN_MODULE_SRCS+=./lib/asn1/xer_decoder.c
+ASN_MODULE_HDRS+=./lib/asn1/xer_encoder.h
+ASN_MODULE_SRCS+=./lib/asn1/xer_encoder.c
+ASN_MODULE_HDRS+=./lib/asn1/per_support.h
+ASN_MODULE_SRCS+=./lib/asn1/per_support.c
+ASN_MODULE_HDRS+=./lib/asn1/per_decoder.h
+ASN_MODULE_SRCS+=./lib/asn1/per_decoder.c
+ASN_MODULE_HDRS+=./lib/asn1/per_encoder.h
+ASN_MODULE_SRCS+=./lib/asn1/per_encoder.c
+ASN_MODULE_HDRS+=./lib/asn1/per_opentype.h
+ASN_MODULE_SRCS+=./lib/asn1/per_opentype.c
+ASN_MODULE_HDRS+=./lib/asn1/oer_decoder.h
+ASN_MODULE_HDRS+=./lib/asn1/oer_encoder.h
+ASN_MODULE_HDRS+=./lib/asn1/oer_support.h
+ASN_MODULE_SRCS+=./lib/asn1/oer_decoder.c
+ASN_MODULE_SRCS+=./lib/asn1/oer_encoder.c
+ASN_MODULE_SRCS+=./lib/asn1/oer_support.c
+ASN_MODULE_SRCS+=./lib/asn1/OPEN_TYPE_oer.c
+ASN_MODULE_SRCS+=./lib/asn1/BIT_STRING_oer.c
+ASN_MODULE_SRCS+=./lib/asn1/OCTET_STRING_oer.c
+ASN_MODULE_SRCS+=./lib/asn1/constr_CHOICE_oer.c
+ASN_MODULE_SRCS+=./lib/asn1/constr_SEQUENCE_oer.c
+
+ASN_MODULE_CFLAGS=
+
+lib_LTLIBRARIES+=libasncodec.la
+libasncodec_la_SOURCES=$(ASN_MODULE_SRCS) $(ASN_MODULE_HDRS)
+libasncodec_la_CPPFLAGS=-I$(top_srcdir)/./lib/asn1/
+libasncodec_la_CFLAGS=$(ASN_MODULE_CFLAGS)
+libasncodec_la_LDFLAGS=-lm

+ 2043 - 0
lib/asn1/OCTET_STRING.c

@@ -0,0 +1,2043 @@
+/*-
+ * Copyright (c) 2003-2017 Lev Walkin <vlm@lionet.info>.
+ * All rights reserved.
+ * Redistribution and modifications are permitted subject to BSD license.
+ */
+#include <asn_internal.h>
+#include <OCTET_STRING.h>
+#include <BIT_STRING.h>	/* for .bits_unused member */
+#include <errno.h>
+
+/*
+ * OCTET STRING basic type description.
+ */
+static const ber_tlv_tag_t asn_DEF_OCTET_STRING_tags[] = {
+	(ASN_TAG_CLASS_UNIVERSAL | (4 << 2))
+};
+asn_OCTET_STRING_specifics_t asn_SPC_OCTET_STRING_specs = {
+	sizeof(OCTET_STRING_t),
+	offsetof(OCTET_STRING_t, _asn_ctx),
+	ASN_OSUBV_STR
+};
+
+asn_TYPE_operation_t asn_OP_OCTET_STRING = {
+	OCTET_STRING_free,
+	OCTET_STRING_print,	/* OCTET STRING generally means a non-ascii sequence */
+	OCTET_STRING_compare,
+	OCTET_STRING_decode_ber,
+	OCTET_STRING_encode_der,
+	OCTET_STRING_decode_xer_hex,
+	OCTET_STRING_encode_xer,
+#ifdef	ASN_DISABLE_OER_SUPPORT
+	0,
+	0,
+#else
+	OCTET_STRING_decode_oer,
+	OCTET_STRING_encode_oer,
+#endif  /* ASN_DISABLE_OER_SUPPORT */
+#ifdef	ASN_DISABLE_PER_SUPPORT
+	0,
+	0,
+#else
+	OCTET_STRING_decode_uper,	/* Unaligned PER decoder */
+	OCTET_STRING_encode_uper,	/* Unaligned PER encoder */
+#endif	/* ASN_DISABLE_PER_SUPPORT */
+	OCTET_STRING_random_fill,
+	0	/* Use generic outmost tag fetcher */
+};
+asn_TYPE_descriptor_t asn_DEF_OCTET_STRING = {
+	"OCTET STRING",		/* Canonical name */
+	"OCTET_STRING",		/* XML tag name */
+	&asn_OP_OCTET_STRING,
+	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, 0, asn_generic_no_constraint },
+	0, 0,	/* No members */
+	&asn_SPC_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 */
+    unsigned 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(void) {
+	return (struct _stack *)CALLOC(1, sizeof(struct _stack));
+}
+
+/*
+ * Decode OCTET STRING type.
+ */
+asn_dec_rval_t
+OCTET_STRING_decode_ber(const asn_codec_ctx_t *opt_codec_ctx,
+                        const asn_TYPE_descriptor_t *td, void **sptr,
+                        const void *buf_ptr, size_t size, int tag_mode) {
+    const asn_OCTET_STRING_specifics_t *specs = td->specifics
+				? (const asn_OCTET_STRING_specifics_t *)td->specifics
+				: &asn_SPC_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) {
+				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) */
+		size_t Left = ((!sel||(size_t)sel->left >= size)
+					?size:(size_t)sel->left);
+
+
+		ASN_DEBUG("%p, s->l=%ld, s->wn=%ld, s->g=%ld\n", (void *)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) {
+				unsigned 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=%u",
+			(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",
+			(void *)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) {
+        if(st->size) {
+			if(st->bits_unused < 0 || st->bits_unused > 7) {
+				RETURN(RC_FAIL);
+			}
+			/* Finalize BIT STRING: zero out unused bits. */
+			st->buf[st->size-1] &= 0xff << st->bits_unused;
+		} else {
+			if(st->bits_unused) {
+				RETURN(RC_FAIL);
+			}
+		}
+	}
+
+	ASN_DEBUG("Took %ld bytes to encode %s: [%s]:%ld",
+		(long)consumed_myself, td->name,
+		(type_variant == ASN_OSUBV_STR) ? (char *)st->buf : "<data>",
+		(long)st->size);
+
+
+	RETURN(RC_OK);
+}
+
+/*
+ * Encode OCTET STRING type using DER.
+ */
+asn_enc_rval_t
+OCTET_STRING_encode_der(const asn_TYPE_descriptor_t *td, const void *sptr,
+                        int tag_mode, ber_tlv_tag_t tag,
+                        asn_app_consume_bytes_f *cb, void *app_key) {
+    asn_enc_rval_t er;
+	const asn_OCTET_STRING_specifics_t *specs = td->specifics
+				? (const asn_OCTET_STRING_specifics_t *)td->specifics
+				: &asn_SPC_OCTET_STRING_specs;
+	const BIT_STRING_t *st = (const 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: [<tag>] 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);
+	}
+
+	/* 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);
+	}
+
+	ASN__ENCODED_OK(er);
+cb_failed:
+	ASN__ENCODE_FAILED;
+}
+
+asn_enc_rval_t
+OCTET_STRING_encode_xer(const asn_TYPE_descriptor_t *td, const 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);
+				p = scratch;
+			}
+			*p++ = h2c[(*buf >> 4) & 0x0F];
+			*p++ = h2c[*buf & 0x0F];
+		}
+
+		ASN__CALLBACK(scratch, p-scratch);	/* Dump the rest */
+	} else {
+		for(i = 0; buf < end; buf++, i++) {
+			if(!(i % 16) && (i || st->size > 16)) {
+				ASN__CALLBACK(scratch, 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 */
+			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"),	/* <nul/> */
+	OSXET("\074\163\157\150\057\076"),	/* <soh/> */
+	OSXET("\074\163\164\170\057\076"),	/* <stx/> */
+	OSXET("\074\145\164\170\057\076"),	/* <etx/> */
+	OSXET("\074\145\157\164\057\076"),	/* <eot/> */
+	OSXET("\074\145\156\161\057\076"),	/* <enq/> */
+	OSXET("\074\141\143\153\057\076"),	/* <ack/> */
+	OSXET("\074\142\145\154\057\076"),	/* <bel/> */
+	OSXET("\074\142\163\057\076"),		/* <bs/> */
+	OSXET("\011"),				/* \t */
+	OSXET("\012"),				/* \n */
+	OSXET("\074\166\164\057\076"),		/* <vt/> */
+	OSXET("\074\146\146\057\076"),		/* <ff/> */
+	OSXET("\015"),				/* \r */
+	OSXET("\074\163\157\057\076"),		/* <so/> */
+	OSXET("\074\163\151\057\076"),		/* <si/> */
+	OSXET("\074\144\154\145\057\076"),	/* <dle/> */
+	OSXET("\074\144\143\061\057\076"),	/* <de1/> */
+	OSXET("\074\144\143\062\057\076"),	/* <de2/> */
+	OSXET("\074\144\143\063\057\076"),	/* <de3/> */
+	OSXET("\074\144\143\064\057\076"),	/* <de4/> */
+	OSXET("\074\156\141\153\057\076"),	/* <nak/> */
+	OSXET("\074\163\171\156\057\076"),	/* <syn/> */
+	OSXET("\074\145\164\142\057\076"),	/* <etb/> */
+	OSXET("\074\143\141\156\057\076"),	/* <can/> */
+	OSXET("\074\145\155\057\076"),		/* <em/> */
+	OSXET("\074\163\165\142\057\076"),	/* <sub/> */
+	OSXET("\074\145\163\143\057\076"),	/* <esc/> */
+	OSXET("\074\151\163\064\057\076"),	/* <is4/> */
+	OSXET("\074\151\163\063\057\076"),	/* <is3/> */
+	OSXET("\074\151\163\062\057\076"),	/* <is2/> */
+	OSXET("\074\151\163\061\057\076"),	/* <is1/> */
+	{ 0, 0 },	/* " " */
+	{ 0, 0 },	/* ! */
+	{ 0, 0 },	/* \" */
+	{ 0, 0 },	/* # */
+	{ 0, 0 },	/* $ */
+	{ 0, 0 },	/* % */
+	OSXET("\046\141\155\160\073"),	/* &amp; */
+	{ 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"),	/* &lt; */
+	{ 0, 0 },	/* = */
+	OSXET("\046\147\164\073"),	/* &gt; */
+};
+
+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(const asn_TYPE_descriptor_t *td, const 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 */
+	size_t new_size = st->size + (chunk_size + 1) / 2;
+	void *nptr = REALLOC(st->buf, new_size + 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 <= new_size);
+	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 */
+	size_t new_size = st->size + (chunk_size + 7) / 8;
+	void *nptr = REALLOC(st->buf, new_size + 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 <= new_size);
+	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) {
+	const int32_t last_unicode_codepoint = 0x10ffff;
+	int32_t val = 0;
+	const char *p;
+
+	for(p = buf; p < end; p++) {
+		int ch = *p;
+
+		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 */
+		}
+
+		/* Value exceeds the Unicode range. */
+		if(val > last_unicode_codepoint) {
+			return -1;
+		}
+	}
+
+	*ret_value = -1;
+	return (p - buf);
+}
+
+/*
+ * Convert from the plain UTF-8 format, expanding entity references: "2 &lt; 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 */
+	size_t new_size = st->size + chunk_size;
+	void *nptr = REALLOC(st->buf, new_size + 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 &amp; &gt; &lt;
+			 */
+			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 <= new_size);
+	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(
+    const asn_codec_ctx_t *opt_codec_ctx, const 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;
+	const asn_OCTET_STRING_specifics_t *specs = td->specifics
+				? (const asn_OCTET_STRING_specifics_t *)td->specifics
+				: &asn_SPC_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(const asn_codec_ctx_t *opt_codec_ctx,
+                            const 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(const asn_codec_ctx_t *opt_codec_ctx,
+                               const 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(const asn_codec_ctx_t *opt_codec_ctx,
+                             const 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);
+}
+
+#ifndef  ASN_DISABLE_PER_SUPPORT
+
+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, const 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) {
+		int32_t code = per_get_few_bits(po, unit_bits);
+		int32_t 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, const 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,
+                      value, lb, ub + lb);
+            return -1;
+        }
+        if(per_put_few_bits(po, ch, unit_bits)) return -1;
+    }
+
+    return 0;
+}
+
+static 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_dec_rval_t
+OCTET_STRING_decode_uper(const asn_codec_ctx_t *opt_codec_ctx,
+                         const asn_TYPE_descriptor_t *td,
+                         const asn_per_constraints_t *constraints, void **sptr,
+                         asn_per_data_t *pd) {
+    const asn_OCTET_STRING_specifics_t *specs = td->specifics
+		? (const asn_OCTET_STRING_specifics_t *)td->specifics
+		: &asn_SPC_OCTET_STRING_specs;
+    const asn_per_constraints_t *pc =
+        constraints ? constraints : td->encoding_constraints.per_constraints;
+    const asn_per_constraint_t *cval;
+	const asn_per_constraint_t *csiz;
+	asn_dec_rval_t rval = { RC_OK, 0 };
+	OCTET_STRING_t *st = (OCTET_STRING_t *)*sptr;
+	ssize_t consumed_myself = 0;
+	int repeat;
+	enum {
+		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:
+	case ASN_OSUBV_BIT:
+		ASN_DEBUG("Unrecognized subvariant %d", specs->subvariant);
+		RETURN(RC_FAIL);
+		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 = (OCTET_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;
+			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;
+		RETURN(RC_OK);
+	}
+
+	st->size = 0;
+	do {
+		ssize_t raw_len;
+		ssize_t len_bytes;
+		void *p;
+		int ret;
+
+		/* Get the PER length */
+		raw_len = uper_get_length(pd, csiz->effective_bits, csiz->lower_bound,
+		                          &repeat);
+		if(raw_len < 0) RETURN(RC_WMORE);
+		if(raw_len == 0 && st->buf) break;
+
+		ASN_DEBUG("Got PER length eb %ld, len %ld, %s (%s)",
+			(long)csiz->effective_bits, (long)raw_len,
+			repeat ? "repeat" : "once", td->name);
+        len_bytes = raw_len * bpc;
+		p = REALLOC(st->buf, st->size + len_bytes + 1);
+		if(!p) RETURN(RC_FAIL);
+		st->buf = (uint8_t *)p;
+
+        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);
+		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(const asn_TYPE_descriptor_t *td,
+                         const asn_per_constraints_t *constraints,
+                         const void *sptr, asn_per_outp_t *po) {
+    const asn_OCTET_STRING_specifics_t *specs = td->specifics
+		? (const asn_OCTET_STRING_specifics_t *)td->specifics
+		: &asn_SPC_OCTET_STRING_specs;
+	const asn_per_constraints_t *pc = constraints ? constraints
+				: td->encoding_constraints.per_constraints;
+	const asn_per_constraint_t *cval;
+	const asn_per_constraint_t *csiz;
+	const OCTET_STRING_t *st = (const OCTET_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;
+	size_t size_in_units;
+	const uint8_t *buf;
+	int ret;
+	enum {
+		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:
+	case ASN_OSUBV_BIT:
+		ASN__ENCODE_FAILED;
+	case ASN_OSUBV_STR:
+		canonical_unit_bits = unit_bits = 8;
+		if(cval->flags & APC_CONSTRAINED)
+			unit_bits = cval->range_bits;
+		bpc = OS__BPC_CHAR;
+		size_in_units = 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;
+		size_in_units = st->size >> 1;
+		if(st->size & 1) {
+			ASN_DEBUG("%s string size is not modulo 2", td->name);
+			ASN__ENCODE_FAILED;
+		}
+		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;
+		size_in_units = st->size >> 2;
+		if(st->size & 3) {
+			ASN_DEBUG("%s string size is not modulo 4", td->name);
+			ASN__ENCODE_FAILED;
+		}
+		break;
+	}
+
+	ASN_DEBUG("Encoding %s into %" ASN_PRI_SIZE " units of %d bits"
+		" (%ld..%ld, effective %d)%s",
+		td->name, size_in_units, 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((ssize_t)size_in_units < csiz->lower_bound
+           || (ssize_t)size_in_units > csiz->upper_bound) {
+            if(ct_extensible) {
+                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;
+	}
+
+    if(csiz->effective_bits >= 0 && !inext) {
+        ASN_DEBUG("Encoding %" ASN_PRI_SIZE " bytes (%ld), length in %d bits", st->size,
+                  size_in_units - csiz->lower_bound, csiz->effective_bits);
+        ret = per_put_few_bits(po, size_in_units - csiz->lower_bound,
+                               csiz->effective_bits);
+        if(ret) ASN__ENCODE_FAILED;
+        ret = OCTET_STRING_per_put_characters(po, st->buf, size_in_units, bpc,
+                                              unit_bits, cval->lower_bound,
+                                              cval->upper_bound, pc);
+        if(ret) ASN__ENCODE_FAILED;
+        ASN__ENCODED_OK(er);
+    }
+
+    ASN_DEBUG("Encoding %" ASN_PRI_SIZE " bytes", st->size);
+
+    buf = st->buf;
+    ASN_DEBUG("Encoding %" ASN_PRI_SIZE " in units", size_in_units);
+    do {
+        int need_eom = 0;
+        ssize_t may_save = uper_put_length(po, size_in_units, &need_eom);
+        if(may_save < 0) ASN__ENCODE_FAILED;
+
+        ASN_DEBUG("Encoding %" ASN_PRI_SSIZE " of %" ASN_PRI_SIZE "%s", may_save, size_in_units,
+                  need_eom ? ",+EOM" : "");
+
+        ret = OCTET_STRING_per_put_characters(po, buf, may_save, bpc, unit_bits,
+                                              cval->lower_bound,
+                                              cval->upper_bound, pc);
+        if(ret) ASN__ENCODE_FAILED;
+
+        buf += may_save * bpc;
+        size_in_units -= may_save;
+        assert(!(may_save & 0x07) || !size_in_units);
+        if(need_eom && uper_put_length(po, 0, 0))
+            ASN__ENCODE_FAILED; /* End of Message length */
+    } while(size_in_units);
+
+    ASN__ENCODED_OK(er);
+}
+
+#endif  /* ASN_DISABLE_PER_SUPPORT */
+
+int
+OCTET_STRING_print(const 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("<absent>", 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(const 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("<absent>", 8, app_key) < 0) ? -1 : 0;
+	}
+}
+
+void
+OCTET_STRING_free(const asn_TYPE_descriptor_t *td, void *sptr,
+                  enum asn_struct_free_method method) {
+	OCTET_STRING_t *st = (OCTET_STRING_t *)sptr;
+	const asn_OCTET_STRING_specifics_t *specs;
+	asn_struct_ctx_t *ctx;
+	struct _stack *stck;
+
+	if(!td || !st)
+		return;
+
+	specs = td->specifics
+		    ? (const asn_OCTET_STRING_specifics_t *)td->specifics
+		    : &asn_SPC_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);
+	}
+
+    switch(method) {
+    case ASFM_FREE_EVERYTHING:
+        FREEMEM(sptr);
+        break;
+    case ASFM_FREE_UNDERLYING:
+        break;
+    case ASFM_FREE_UNDERLYING_AND_RESET:
+        memset(sptr, 0,
+               td->specifics
+                   ? ((const asn_OCTET_STRING_specifics_t *)(td->specifics))
+                         ->struct_size
+                   : sizeof(OCTET_STRING_t));
+        break;
+    }
+}
+
+/*
+ * 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(const asn_TYPE_descriptor_t *td, const char *str,
+                         int len) {
+    const asn_OCTET_STRING_specifics_t *specs =
+        td->specifics ? (const asn_OCTET_STRING_specifics_t *)td->specifics
+                      : &asn_SPC_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;
+}
+
+/*
+ * Lexicographically compare the common prefix of both strings,
+ * and if it is the same return -1 for the smallest string.
+ */
+int
+OCTET_STRING_compare(const asn_TYPE_descriptor_t *td, const void *aptr,
+                     const void *bptr) {
+    const asn_OCTET_STRING_specifics_t *specs = td->specifics;
+    const OCTET_STRING_t *a = aptr;
+    const OCTET_STRING_t *b = bptr;
+
+    assert(!specs || specs->subvariant != ASN_OSUBV_BIT);
+
+    if(a && b) {
+        size_t common_prefix_size = a->size <= b->size ? a->size : b->size;
+        int ret = memcmp(a->buf, b->buf, common_prefix_size);
+        if(ret == 0) {
+            /* Figure out which string with equal prefixes is longer. */
+            if(a->size < b->size) {
+                return -1;
+            } else if(a->size > b->size) {
+                return 1;
+            } else {
+                return 0;
+            }
+        } else {
+            return ret < 0 ? -1 : 1;
+        }
+    } else if(!a && !b) {
+        return 0;
+    } else if(!a) {
+        return -1;
+    } else {
+        return 1;
+    }
+
+}
+
+/*
+ * Biased function for randomizing character values around their limits.
+ */
+static uint32_t
+OCTET_STRING__random_char(unsigned long lb, unsigned long ub) {
+    assert(lb <= ub);
+    switch(asn_random_between(0, 16)) {
+    case 0:
+        if(lb < ub) return lb + 1;
+        /* Fall through */
+    case 1:
+        return lb;
+    case 2:
+        if(lb < ub) return ub - 1;
+        /* Fall through */
+    case 3:
+        return ub;
+    default:
+        return asn_random_between(lb, ub);
+    }
+}
+
+
+size_t
+OCTET_STRING_random_length_constrained(
+    const asn_TYPE_descriptor_t *td,
+    const asn_encoding_constraints_t *constraints, size_t max_length) {
+    const unsigned lengths[] = {0,     1,     2,     3,     4,     8,
+                                126,   127,   128,   16383, 16384, 16385,
+                                65534, 65535, 65536, 65537};
+    size_t rnd_len;
+
+    /* Figure out how far we should go */
+    rnd_len = lengths[asn_random_between(
+        0, sizeof(lengths) / sizeof(lengths[0]) - 1)];
+
+    if(!constraints || !constraints->per_constraints)
+        constraints = &td->encoding_constraints;
+    if(constraints->per_constraints) {
+        const asn_per_constraint_t *pc = &constraints->per_constraints->size;
+        if(pc->flags & APC_CONSTRAINED) {
+            long suggested_upper_bound = pc->upper_bound < (ssize_t)max_length
+                                             ? pc->upper_bound
+                                             : (ssize_t)max_length;
+            if(max_length <= (size_t)pc->lower_bound) {
+                return pc->lower_bound;
+            }
+            if(pc->flags & APC_EXTENSIBLE) {
+                switch(asn_random_between(0, 5)) {
+                case 0:
+                    if(pc->lower_bound > 0) {
+                        rnd_len = pc->lower_bound - 1;
+                        break;
+                    }
+                    /* Fall through */
+                case 1:
+                    rnd_len = pc->upper_bound + 1;
+                    break;
+                case 2:
+                    /* Keep rnd_len from the table */
+                    if(rnd_len <= max_length) {
+                        break;
+                    }
+                    /* Fall through */
+                default:
+                    rnd_len = asn_random_between(pc->lower_bound,
+                                                 suggested_upper_bound);
+                }
+            } else {
+                rnd_len =
+                    asn_random_between(pc->lower_bound, suggested_upper_bound);
+            }
+        } else {
+            rnd_len = asn_random_between(0, max_length);
+        }
+    } else if(rnd_len > max_length) {
+        rnd_len = asn_random_between(0, max_length);
+    }
+
+    return rnd_len;
+}
+
+asn_random_fill_result_t
+OCTET_STRING_random_fill(const asn_TYPE_descriptor_t *td, void **sptr,
+                         const asn_encoding_constraints_t *constraints,
+                         size_t max_length) {
+	const asn_OCTET_STRING_specifics_t *specs = td->specifics
+				? (const asn_OCTET_STRING_specifics_t *)td->specifics
+				: &asn_SPC_OCTET_STRING_specs;
+    asn_random_fill_result_t result_ok = {ARFILL_OK, 1};
+    asn_random_fill_result_t result_failed = {ARFILL_FAILED, 0};
+    asn_random_fill_result_t result_skipped = {ARFILL_SKIPPED, 0};
+    unsigned int unit_bytes = 1;
+    unsigned long clb = 0;  /* Lower bound on char */
+    unsigned long cub = 255;  /* Higher bound on char value */
+    uint8_t *buf;
+    uint8_t *bend;
+    uint8_t *b;
+    size_t rnd_len;
+    OCTET_STRING_t *st;
+
+    if(max_length == 0 && !*sptr) return result_skipped;
+
+    switch(specs->subvariant) {
+    default:
+    case ASN_OSUBV_ANY:
+        return result_failed;
+    case ASN_OSUBV_BIT:
+        /* Handled by BIT_STRING itself. */
+        return result_failed;
+    case ASN_OSUBV_STR:
+        unit_bytes = 1;
+        clb = 0;
+        cub = 255;
+        break;
+    case ASN_OSUBV_U16:
+        unit_bytes = 2;
+        clb = 0;
+        cub = 65535;
+        break;
+    case ASN_OSUBV_U32:
+        unit_bytes = 4;
+        clb = 0;
+        cub = 0x10FFFF;
+        break;
+    }
+
+    if(!constraints || !constraints->per_constraints)
+        constraints = &td->encoding_constraints;
+    if(constraints->per_constraints) {
+        const asn_per_constraint_t *pc = &constraints->per_constraints->value;
+        if(pc->flags & APC_SEMI_CONSTRAINED) {
+            clb = pc->lower_bound;
+        } else if(pc->flags & APC_CONSTRAINED) {
+            clb = pc->lower_bound;
+            cub = pc->upper_bound;
+        }
+    }
+
+    rnd_len =
+        OCTET_STRING_random_length_constrained(td, constraints, max_length);
+
+    buf = CALLOC(unit_bytes, rnd_len + 1);
+    if(!buf) return result_failed;
+
+    bend = &buf[unit_bytes * rnd_len];
+
+    switch(unit_bytes) {
+    case 1:
+        for(b = buf; b < bend; b += unit_bytes) {
+            *(uint8_t *)b = OCTET_STRING__random_char(clb, cub);
+        }
+        *(uint8_t *)b = 0;
+        break;
+    case 2:
+        for(b = buf; b < bend; b += unit_bytes) {
+            uint32_t code = OCTET_STRING__random_char(clb, cub);
+            b[0] = code >> 8;
+            b[1] = code;
+        }
+        *(uint16_t *)b = 0;
+        break;
+    case 4:
+        for(b = buf; b < bend; b += unit_bytes) {
+            uint32_t code = OCTET_STRING__random_char(clb, cub);
+            b[0] = code >> 24;
+            b[1] = code >> 16;
+            b[2] = code >> 8;
+            b[3] = code;
+        }
+        *(uint32_t *)b = 0;
+        break;
+    }
+
+    if(*sptr) {
+        st = *sptr;
+        FREEMEM(st->buf);
+    } else {
+        st = (OCTET_STRING_t *)(*sptr = CALLOC(1, specs->struct_size));
+        if(!st) {
+            FREEMEM(buf);
+            return result_failed;
+        }
+    }
+
+    st->buf = buf;
+    st->size = unit_bytes * rnd_len;
+
+    result_ok.length = st->size;
+    return result_ok;
+}

+ 100 - 0
lib/asn1/OCTET_STRING.h

@@ -0,0 +1,100 @@
+/*-
+ * Copyright (c) 2003-2017 Lev Walkin <vlm@lionet.info>. All rights reserved.
+ * Redistribution and modifications are permitted subject to BSD license.
+ */
+#ifndef	_OCTET_STRING_H_
+#define	_OCTET_STRING_H_
+
+#include <asn_application.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct OCTET_STRING {
+	uint8_t *buf;	/* Buffer with consecutive OCTET_STRING bits */
+	size_t 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;
+extern asn_TYPE_operation_t asn_OP_OCTET_STRING;
+
+asn_struct_free_f OCTET_STRING_free;
+asn_struct_print_f OCTET_STRING_print;
+asn_struct_print_f OCTET_STRING_print_utf8;
+asn_struct_compare_f OCTET_STRING_compare;
+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;
+oer_type_decoder_f OCTET_STRING_decode_oer;
+oer_type_encoder_f OCTET_STRING_encode_oer;
+per_type_decoder_f OCTET_STRING_decode_uper;
+per_type_encoder_f OCTET_STRING_encode_uper;
+asn_random_fill_f  OCTET_STRING_random_fill;
+
+#define OCTET_STRING_constraint  asn_generic_no_constraint
+#define OCTET_STRING_decode_xer  OCTET_STRING_decode_xer_hex
+
+/******************************
+ * 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(const asn_TYPE_descriptor_t *td,
+                                         const char *str, int size);
+
+/****************************
+ * Internally useful stuff. *
+ ****************************/
+
+typedef struct asn_OCTET_STRING_specifics_s {
+    /*
+     * Target structure description.
+     */
+    unsigned struct_size;   /* Size of the structure */
+    unsigned 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;
+
+extern asn_OCTET_STRING_specifics_t asn_SPC_OCTET_STRING_specs;
+
+size_t OCTET_STRING_random_length_constrained(
+    const asn_TYPE_descriptor_t *, const asn_encoding_constraints_t *,
+    size_t max_length);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif	/* _OCTET_STRING_H_ */

+ 171 - 0
lib/asn1/OCTET_STRING_oer.c

@@ -0,0 +1,171 @@
+/*
+ * Copyright (c) 2017 Lev Walkin <vlm@lionet.info>.
+ * All rights reserved.
+ * Redistribution and modifications are permitted subject to BSD license.
+ */
+#ifndef ASN_DISABLE_OER_SUPPORT
+
+#include <asn_internal.h>
+#include <OCTET_STRING.h>
+#include <errno.h>
+
+asn_dec_rval_t
+OCTET_STRING_decode_oer(const asn_codec_ctx_t *opt_codec_ctx,
+                        const asn_TYPE_descriptor_t *td,
+                        const asn_oer_constraints_t *constraints, void **sptr,
+                        const void *ptr, size_t size) {
+    const asn_OCTET_STRING_specifics_t *specs =
+        td->specifics ? (const asn_OCTET_STRING_specifics_t *)td->specifics
+                      : &asn_SPC_OCTET_STRING_specs;
+    OCTET_STRING_t *st = (OCTET_STRING_t *)*sptr;
+    const asn_oer_constraints_t *cts =
+        constraints ? constraints : td->encoding_constraints.oer_constraints;
+    ssize_t ct_size = cts ? cts->size : -1;
+    asn_dec_rval_t rval = {RC_OK, 0};
+    size_t expected_length = 0;
+
+    size_t unit_bytes;
+    switch(specs->subvariant) {
+    default:
+    case ASN_OSUBV_BIT:
+        ASN_DEBUG("Invalid use of OCTET STRING to decode BIT STRING");
+        ASN__DECODE_FAILED;
+    case ASN_OSUBV_ANY:
+        /* Fall through */
+    case ASN_OSUBV_STR:
+        unit_bytes = 1;
+        break;
+    case ASN_OSUBV_U16:
+        unit_bytes = 2;
+        break;
+    case ASN_OSUBV_U32:
+        unit_bytes = 4;
+        break;
+    }
+
+    (void)opt_codec_ctx;
+
+    if(!st) {
+        st = (OCTET_STRING_t *)(*sptr = CALLOC(1, specs->struct_size));
+        if(!st) ASN__DECODE_FAILED;
+    }
+
+    if(ct_size >= 0) {
+        expected_length = unit_bytes * ct_size;
+    } else {
+        /*
+         * X.696 (08/2015) #27.2
+         * Encode length determinant as _number of octets_, but only
+         * if upper bound is not equal to lower bound.
+         */
+        ssize_t len_len = oer_fetch_length(ptr, size, &expected_length);
+        if(len_len > 0) {
+            rval.consumed = len_len;
+            ptr = (const char *)ptr + len_len;
+            size -= len_len;
+        } else if(len_len == 0) {
+            ASN__DECODE_STARVED;
+        } else if(len_len < 0) {
+            ASN__DECODE_FAILED;
+        }
+
+        if(expected_length % unit_bytes != 0) {
+            ASN_DEBUG(
+                "Data size %" ASN_PRI_SIZE " bytes is not consistent with multiplier %" ASN_PRI_SIZE "",
+                expected_length, unit_bytes);
+            ASN__DECODE_FAILED;
+        }
+    }
+
+    if(size < expected_length) {
+        ASN__DECODE_STARVED;
+    } else {
+        uint8_t *buf = MALLOC(expected_length + 1);
+        if(buf == NULL) {
+            ASN__DECODE_FAILED;
+        } else {
+            memcpy(buf, ptr, expected_length);
+            buf[expected_length] = '\0';
+        }
+        FREEMEM(st->buf);
+        st->buf = buf;
+        st->size = expected_length;
+
+        rval.consumed += expected_length;
+        return rval;
+    }
+}
+
+/*
+ * Encode as Canonical OER.
+ */
+asn_enc_rval_t
+OCTET_STRING_encode_oer(const asn_TYPE_descriptor_t *td,
+                        const asn_oer_constraints_t *constraints,
+                        const void *sptr, asn_app_consume_bytes_f *cb,
+                        void *app_key) {
+    const asn_OCTET_STRING_specifics_t *specs =
+        td->specifics ? (const asn_OCTET_STRING_specifics_t *)td->specifics
+                      : &asn_SPC_OCTET_STRING_specs;
+    const OCTET_STRING_t *st = (const OCTET_STRING_t *)sptr;
+    const asn_oer_constraints_t *cts =
+        constraints ? constraints : td->encoding_constraints.oer_constraints;
+    ssize_t ct_size = cts ? cts->size : -1;
+    asn_enc_rval_t er = {0, 0, 0};
+
+    if(!st) ASN__ENCODE_FAILED;
+
+    ASN_DEBUG("Encoding %s %" ASN_PRI_SIZE " as OCTET STRING", td ? td->name : "", st->size);
+
+    if(ct_size >= 0) {
+        /*
+         * Check that available data matches the constraint
+         */
+        size_t unit_bytes;
+        switch(specs->subvariant) {
+        default:
+        case ASN_OSUBV_BIT:
+            ASN_DEBUG("Invalid use of OCTET STRING to encode BIT STRING");
+            ASN__ENCODE_FAILED;
+        case ASN_OSUBV_ANY:
+            /* Fall through */
+        case ASN_OSUBV_STR:
+            unit_bytes = 1;
+            break;
+        case ASN_OSUBV_U16:
+            unit_bytes = 2;
+            break;
+        case ASN_OSUBV_U32:
+            unit_bytes = 4;
+            break;
+        }
+
+        if(st->size != unit_bytes * (size_t)ct_size) {
+            ASN_DEBUG(
+                "Trying to encode %s (%" ASN_PRI_SIZE " bytes) which doesn't fit SIZE "
+                "constraint (%" ASN_PRI_SIZE ")",
+                td->name, st->size, ct_size);
+            ASN__ENCODE_FAILED;
+        }
+    } else {
+        /*
+         * X.696 (08/2015) #27.2
+         * Encode length determinant as _number of octets_, but only
+         * if upper bound is not equal to lower bound.
+         */
+        ssize_t ret = oer_serialize_length(st->size, cb, app_key);
+        if(ret < 0) {
+            ASN__ENCODE_FAILED;
+        }
+        er.encoded += ret;
+    }
+
+    er.encoded += st->size;
+    if(cb(st->buf, st->size, app_key) < 0) {
+        ASN__ENCODE_FAILED;
+    } else {
+        ASN__ENCODED_OK(er);
+    }
+}
+
+#endif  /* ASN_DISABLE_OER_SUPPORT */

+ 402 - 0
lib/asn1/OPEN_TYPE.c

@@ -0,0 +1,402 @@
+/*
+ * Copyright (c) 2017 Lev Walkin <vlm@lionet.info>. All rights reserved.
+ * Redistribution and modifications are permitted subject to BSD license.
+ */
+#include <asn_internal.h>
+#include <OPEN_TYPE.h>
+#include <constr_CHOICE.h>
+#include <per_opentype.h>
+#include <errno.h>
+
+asn_TYPE_operation_t asn_OP_OPEN_TYPE = {
+	OPEN_TYPE_free,
+	OPEN_TYPE_print,
+	OPEN_TYPE_compare,
+	OPEN_TYPE_decode_ber,
+	OPEN_TYPE_encode_der,
+	OPEN_TYPE_decode_xer,
+	OPEN_TYPE_encode_xer,
+	0, 0,	/* No OER support, use "-gen-OER" to enable */
+#ifdef ASN_DISABLE_PER_SUPPORT
+	0, 0,
+#else
+	OPEN_TYPE_decode_uper,
+	OPEN_TYPE_encode_uper,
+#endif
+	0,  /* Random fill is not supported for open type */
+	0,	/* Use generic outmost tag fetcher */
+};
+
+#undef  ADVANCE
+#define ADVANCE(num_bytes)               \
+    do {                                 \
+        size_t num = num_bytes;          \
+        ptr = ((const char *)ptr) + num; \
+        size -= num;                     \
+        consumed_myself += num;          \
+    } while(0)
+
+asn_dec_rval_t
+OPEN_TYPE_ber_get(const asn_codec_ctx_t *opt_codec_ctx,
+                  const asn_TYPE_descriptor_t *td, void *sptr,
+                  const asn_TYPE_member_t *elm, const void *ptr, size_t size) {
+    size_t consumed_myself = 0;
+    asn_type_selector_result_t selected;
+    void *memb_ptr;   /* Pointer to the member */
+    void **memb_ptr2; /* Pointer to that pointer */
+    void *inner_value;
+    asn_dec_rval_t rv;
+
+    if(!(elm->flags & ATF_OPEN_TYPE)) {
+        ASN__DECODE_FAILED;
+    }
+
+    if(!elm->type_selector) {
+        ASN_DEBUG("Type selector is not defined for Open Type %s->%s->%s",
+                  td->name, elm->name, elm->type->name);
+        ASN__DECODE_FAILED;
+    }
+
+    selected = elm->type_selector(td, sptr);
+    if(!selected.presence_index) {
+        ASN__DECODE_FAILED;
+    }
+
+    /* Fetch the pointer to this member */
+    if(elm->flags & ATF_POINTER) {
+        memb_ptr2 = (void **)((char *)sptr + elm->memb_offset);
+    } else {
+        memb_ptr = (char *)sptr + elm->memb_offset;
+        memb_ptr2 = &memb_ptr;
+    }
+    if(*memb_ptr2 != NULL) {
+        /* Make sure we reset the structure first before encoding */
+        if(CHOICE_variant_set_presence(elm->type, *memb_ptr2, 0) != 0) {
+            ASN__DECODE_FAILED;
+        }
+    }
+
+    inner_value =
+        (char *)*memb_ptr2
+        + elm->type->elements[selected.presence_index - 1].memb_offset;
+
+    ASN_DEBUG("presence %d\n", selected.presence_index);
+
+    rv = selected.type_descriptor->op->ber_decoder(
+        opt_codec_ctx, selected.type_descriptor, &inner_value, ptr, size,
+        elm->tag_mode);
+    ADVANCE(rv.consumed);
+    rv.consumed = 0;
+    switch(rv.code) {
+    case RC_OK:
+        if(CHOICE_variant_set_presence(elm->type, *memb_ptr2,
+                                       selected.presence_index)
+           == 0) {
+            rv.code = RC_OK;
+            rv.consumed = consumed_myself;
+            return rv;
+        } else {
+            /* Oh, now a full-blown failure failure */
+        }
+        /* Fall through */
+    case RC_FAIL:
+        rv.consumed = consumed_myself;
+        /* Fall through */
+    case RC_WMORE:
+        break;
+    }
+
+    if(*memb_ptr2) {
+        const asn_CHOICE_specifics_t *specs =
+            selected.type_descriptor->specifics;
+        if(elm->flags & ATF_POINTER) {
+            ASN_STRUCT_FREE(*selected.type_descriptor, inner_value);
+            *memb_ptr2 = NULL;
+        } else {
+            ASN_STRUCT_FREE_CONTENTS_ONLY(*selected.type_descriptor,
+                                          inner_value);
+            memset(*memb_ptr2, 0, specs->struct_size);
+        }
+    }
+    return rv;
+}
+
+asn_dec_rval_t
+OPEN_TYPE_xer_get(const asn_codec_ctx_t *opt_codec_ctx,
+                  const asn_TYPE_descriptor_t *td, void *sptr,
+                  const asn_TYPE_member_t *elm, const void *ptr, size_t size) {
+    size_t consumed_myself = 0;
+    asn_type_selector_result_t selected;
+    void *memb_ptr;   /* Pointer to the member */
+    void **memb_ptr2; /* Pointer to that pointer */
+    void *inner_value;
+    asn_dec_rval_t rv;
+
+    int xer_context = 0;
+    ssize_t ch_size;
+    pxer_chunk_type_e ch_type;
+
+    if(!(elm->flags & ATF_OPEN_TYPE)) {
+        ASN__DECODE_FAILED;
+    }
+
+    if(!elm->type_selector) {
+        ASN_DEBUG("Type selector is not defined for Open Type %s->%s->%s",
+                  td->name, elm->name, elm->type->name);
+        ASN__DECODE_FAILED;
+    }
+
+    selected = elm->type_selector(td, sptr);
+    if(!selected.presence_index) {
+        ASN__DECODE_FAILED;
+    }
+
+    /* Fetch the pointer to this member */
+    assert(elm->flags == ATF_OPEN_TYPE);
+    if(elm->flags & ATF_POINTER) {
+        memb_ptr2 = (void **)((char *)sptr + elm->memb_offset);
+    } else {
+        memb_ptr = (char *)sptr + elm->memb_offset;
+        memb_ptr2 = &memb_ptr;
+    }
+    if(*memb_ptr2 != NULL) {
+        /* Make sure we reset the structure first before encoding */
+        if(CHOICE_variant_set_presence(elm->type, *memb_ptr2, 0)
+           != 0) {
+            ASN__DECODE_FAILED;
+        }
+    }
+
+    /*
+     * Confirm wrapper.
+     */
+    for(;;) {
+        ch_size = xer_next_token(&xer_context, ptr, size, &ch_type);
+        if(ch_size < 0) {
+            ASN__DECODE_FAILED;
+        } else {
+            switch(ch_type) {
+            case PXER_WMORE:
+                ASN__DECODE_STARVED;
+            case PXER_COMMENT:
+            case PXER_TEXT:
+                ADVANCE(ch_size);
+                continue;
+            case PXER_TAG:
+                break;
+            }
+            break;
+        }
+    }
+
+    /*
+     * Wrapper value confirmed.
+     */
+    switch(xer_check_tag(ptr, ch_size, elm->name)) {
+    case XCT_OPENING:
+        ADVANCE(ch_size);
+        break;
+    case XCT_BROKEN:
+    default:
+        ASN__DECODE_FAILED;
+    }
+
+    inner_value =
+        (char *)*memb_ptr2
+        + elm->type->elements[selected.presence_index - 1].memb_offset;
+
+    rv = selected.type_descriptor->op->xer_decoder(
+        opt_codec_ctx, selected.type_descriptor, &inner_value, NULL, ptr, size);
+    ADVANCE(rv.consumed);
+    rv.consumed = 0;
+    switch(rv.code) {
+    case RC_OK:
+        if(CHOICE_variant_set_presence(elm->type, *memb_ptr2,
+                                       selected.presence_index)
+           == 0) {
+            break;
+        } else {
+            rv.code = RC_FAIL;
+        }
+        /* Fall through */
+    case RC_FAIL:
+        /* Point to a best position where failure occurred */
+        rv.consumed = consumed_myself;
+        /* Fall through */
+    case RC_WMORE:
+        /* Wrt. rv.consumed==0:
+         * In case a genuine RC_WMORE, the whole Open Type decoding
+         * will have to be restarted.
+         */
+        if(*memb_ptr2) {
+            const asn_CHOICE_specifics_t *specs =
+                selected.type_descriptor->specifics;
+            if(elm->flags & ATF_POINTER) {
+                ASN_STRUCT_FREE(*selected.type_descriptor, inner_value);
+                *memb_ptr2 = NULL;
+            } else {
+                ASN_STRUCT_FREE_CONTENTS_ONLY(*selected.type_descriptor,
+                                              inner_value);
+                memset(*memb_ptr2, 0, specs->struct_size);
+            }
+        }
+        return rv;
+    }
+
+    /*
+     * Finalize wrapper.
+     */
+    for(;;) {
+        ch_size = xer_next_token(&xer_context, ptr, size, &ch_type);
+        if(ch_size < 0) {
+            ASN__DECODE_FAILED;
+        } else {
+            switch(ch_type) {
+            case PXER_WMORE:
+                ASN__DECODE_STARVED;
+            case PXER_COMMENT:
+            case PXER_TEXT:
+                ADVANCE(ch_size);
+                continue;
+            case PXER_TAG:
+                break;
+            }
+            break;
+        }
+    }
+
+    /*
+     * Wrapper value confirmed.
+     */
+    switch(xer_check_tag(ptr, ch_size, elm->name)) {
+    case XCT_CLOSING:
+        ADVANCE(ch_size);
+        break;
+    case XCT_BROKEN:
+    default:
+        ASN__DECODE_FAILED;
+    }
+
+    rv.consumed += consumed_myself;
+
+    return rv;
+}
+
+
+#ifndef  ASN_DISABLE_PER_SUPPORT
+
+asn_dec_rval_t
+OPEN_TYPE_uper_get(const asn_codec_ctx_t *opt_codec_ctx,
+                   const asn_TYPE_descriptor_t *td, void *sptr,
+                   const asn_TYPE_member_t *elm, asn_per_data_t *pd) {
+    asn_type_selector_result_t selected;
+    void *memb_ptr;   /* Pointer to the member */
+    void **memb_ptr2; /* Pointer to that pointer */
+    void *inner_value;
+    asn_dec_rval_t rv;
+
+    if(!(elm->flags & ATF_OPEN_TYPE)) {
+        ASN__DECODE_FAILED;
+    }
+
+    if(!elm->type_selector) {
+        ASN_DEBUG("Type selector is not defined for Open Type %s->%s->%s",
+                  td->name, elm->name, elm->type->name);
+        ASN__DECODE_FAILED;
+    }
+
+    selected = elm->type_selector(td, sptr);
+    if(!selected.presence_index) {
+        ASN__DECODE_FAILED;
+    }
+
+    /* Fetch the pointer to this member */
+    assert(elm->flags == ATF_OPEN_TYPE);
+    if(elm->flags & ATF_POINTER) {
+        memb_ptr2 = (void **)((char *)sptr + elm->memb_offset);
+    } else {
+        memb_ptr = (char *)sptr + elm->memb_offset;
+        memb_ptr2 = &memb_ptr;
+    }
+    if(*memb_ptr2 != NULL) {
+        /* Make sure we reset the structure first before encoding */
+        if(CHOICE_variant_set_presence(elm->type, *memb_ptr2, 0)
+           != 0) {
+            ASN__DECODE_FAILED;
+        }
+    }
+
+    inner_value =
+        (char *)*memb_ptr2
+        + elm->type->elements[selected.presence_index - 1].memb_offset;
+
+    rv = uper_open_type_get(opt_codec_ctx, selected.type_descriptor, NULL,
+                            &inner_value, pd);
+    switch(rv.code) {
+    case RC_OK:
+        if(CHOICE_variant_set_presence(elm->type, *memb_ptr2,
+                                       selected.presence_index)
+           == 0) {
+            break;
+        } else {
+            rv.code = RC_FAIL;
+        }
+        /* Fall through */
+    case RC_WMORE:
+    case RC_FAIL:
+        if(*memb_ptr2) {
+            const asn_CHOICE_specifics_t *specs =
+                selected.type_descriptor->specifics;
+            if(elm->flags & ATF_POINTER) {
+                ASN_STRUCT_FREE(*selected.type_descriptor, inner_value);
+                *memb_ptr2 = NULL;
+            } else {
+                ASN_STRUCT_FREE_CONTENTS_ONLY(*selected.type_descriptor,
+                                              inner_value);
+                memset(*memb_ptr2, 0, specs->struct_size);
+            }
+        }
+    }
+    return rv;
+}
+
+asn_enc_rval_t
+OPEN_TYPE_encode_uper(const asn_TYPE_descriptor_t *td,
+                      const asn_per_constraints_t *constraints,
+                      const void *sptr, asn_per_outp_t *po) {
+    const void *memb_ptr;   /* Pointer to the member */
+    asn_TYPE_member_t *elm; /* CHOICE's element */
+    asn_enc_rval_t er;
+    unsigned present;
+
+    (void)constraints;
+
+    present = CHOICE_variant_get_presence(td, sptr);
+    if(present == 0 || present > td->elements_count) {
+        ASN__ENCODE_FAILED;
+    } else {
+        present--;
+    }
+
+    ASN_DEBUG("Encoding %s OPEN TYPE element %d", td->name, present);
+
+    elm = &td->elements[present];
+    if(elm->flags & ATF_POINTER) {
+        /* Member is a pointer to another structure */
+        memb_ptr =
+            *(const void *const *)((const char *)sptr + elm->memb_offset);
+        if(!memb_ptr) ASN__ENCODE_FAILED;
+    } else {
+        memb_ptr = (const char *)sptr + elm->memb_offset;
+    }
+
+    if(uper_open_type_put(elm->type, NULL, memb_ptr, po) < 0) {
+        ASN__ENCODE_FAILED;
+    }
+
+    er.encoded = 0;
+    ASN__ENCODED_OK(er);
+}
+
+
+#endif  /* ASN_DISABLE_PER_SUPPORT */

+ 63 - 0
lib/asn1/OPEN_TYPE.h

@@ -0,0 +1,63 @@
+/*-
+ * Copyright (c) 2017-2017 Lev Walkin <vlm@lionet.info>. All rights reserved.
+ * Redistribution and modifications are permitted subject to BSD license.
+ */
+#ifndef ASN_OPEN_TYPE_H
+#define ASN_OPEN_TYPE_H
+
+#include <asn_application.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define OPEN_TYPE_free CHOICE_free
+#define OPEN_TYPE_print CHOICE_print
+#define OPEN_TYPE_compare CHOICE_compare
+#define OPEN_TYPE_constraint CHOICE_constraint
+#define OPEN_TYPE_decode_ber NULL
+#define OPEN_TYPE_encode_der CHOICE_encode_der
+#define OPEN_TYPE_decode_xer NULL
+#define OPEN_TYPE_encode_xer CHOICE_encode_xer
+#define OPEN_TYPE_decode_uper NULL
+
+extern asn_TYPE_operation_t asn_OP_OPEN_TYPE;
+
+/*
+ * Decode an Open Type which is potentially constraiend
+ * by the other members of the parent structure.
+ */
+asn_dec_rval_t OPEN_TYPE_ber_get(const asn_codec_ctx_t *opt_codec_ctx,
+                                 const asn_TYPE_descriptor_t *parent_type,
+                                 void *parent_structure,
+                                 const asn_TYPE_member_t *element,
+                                 const void *ptr, size_t size);
+
+asn_dec_rval_t OPEN_TYPE_xer_get(const asn_codec_ctx_t *opt_codec_ctx,
+                                 const asn_TYPE_descriptor_t *parent_type,
+                                 void *parent_structure,
+                                 const asn_TYPE_member_t *element,
+                                 const void *ptr, size_t size);
+
+asn_dec_rval_t OPEN_TYPE_oer_get(const asn_codec_ctx_t *opt_codec_ctx,
+                                 const asn_TYPE_descriptor_t *parent_type,
+                                 void *parent_structure,
+                                 asn_TYPE_member_t *element, const void *ptr,
+                                 size_t size);
+
+asn_dec_rval_t OPEN_TYPE_uper_get(const asn_codec_ctx_t *opt_codec_ctx,
+                                  const asn_TYPE_descriptor_t *parent_type,
+                                  void *parent_structure,
+                                  const asn_TYPE_member_t *element,
+                                  asn_per_data_t *pd);
+
+asn_enc_rval_t OPEN_TYPE_encode_uper(
+    const asn_TYPE_descriptor_t *type_descriptor,
+    const asn_per_constraints_t *constraints, const void *struct_ptr,
+    asn_per_outp_t *per_output);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif	/* ASN_OPEN_TYPE_H */

+ 92 - 0
lib/asn1/OPEN_TYPE_oer.c

@@ -0,0 +1,92 @@
+/*
+ * Copyright (c) 2017 Lev Walkin <vlm@lionet.info>. All rights reserved.
+ * Redistribution and modifications are permitted subject to BSD license.
+ */
+#include <asn_internal.h>
+#include <OPEN_TYPE.h>
+#include <constr_CHOICE.h>
+#include <errno.h>
+
+asn_dec_rval_t
+OPEN_TYPE_oer_get(const asn_codec_ctx_t *opt_codec_ctx,
+                  const asn_TYPE_descriptor_t *td, void *sptr,
+                  asn_TYPE_member_t *elm, const void *ptr, size_t size) {
+    asn_type_selector_result_t selected;
+    void *memb_ptr;   /* Pointer to the member */
+    void **memb_ptr2; /* Pointer to that pointer */
+    void *inner_value;
+    asn_dec_rval_t rv;
+    size_t ot_ret;
+
+
+    if(!(elm->flags & ATF_OPEN_TYPE)) {
+        ASN__DECODE_FAILED;
+    }
+
+    if(!elm->type_selector) {
+        ASN_DEBUG("Type selector is not defined for Open Type %s->%s->%s",
+                  td->name, elm->name, elm->type->name);
+        ASN__DECODE_FAILED;
+    }
+
+    selected = elm->type_selector(td, sptr);
+    if(!selected.presence_index) {
+        ASN__DECODE_FAILED;
+    }
+
+    /* Fetch the pointer to this member */
+    if(elm->flags & ATF_POINTER) {
+        memb_ptr2 = (void **)((char *)sptr + elm->memb_offset);
+    } else {
+        memb_ptr = (char *)sptr + elm->memb_offset;
+        memb_ptr2 = &memb_ptr;
+    }
+    if(*memb_ptr2 != NULL) {
+        /* Make sure we reset the structure first before encoding */
+        if(CHOICE_variant_set_presence(elm->type, *memb_ptr2, 0) != 0) {
+            ASN__DECODE_FAILED;
+        }
+    }
+
+    inner_value =
+        (char *)*memb_ptr2
+        + elm->type->elements[selected.presence_index - 1].memb_offset;
+
+    ot_ret = oer_open_type_get(opt_codec_ctx, selected.type_descriptor, NULL,
+                               &inner_value, ptr, size);
+    switch(ot_ret) {
+    default:
+        if(CHOICE_variant_set_presence(elm->type, *memb_ptr2,
+                                       selected.presence_index)
+           == 0) {
+            rv.code = RC_OK;
+            rv.consumed = ot_ret;
+            return rv;
+        } else {
+            /* Oh, now a full-blown failure failure */
+        }
+        /* Fall through */
+    case -1:
+        rv.code = RC_FAIL;
+        rv.consumed = ot_ret;
+        break;
+    case 0:
+        rv.code = RC_WMORE;
+        rv.consumed = 0;
+        break;
+    }
+
+    if(*memb_ptr2) {
+        const asn_CHOICE_specifics_t *specs =
+            selected.type_descriptor->specifics;
+        if(elm->flags & ATF_POINTER) {
+            ASN_STRUCT_FREE(*selected.type_descriptor, inner_value);
+            *memb_ptr2 = NULL;
+        } else {
+            ASN_STRUCT_FREE_CONTENTS_ONLY(*selected.type_descriptor,
+                                          inner_value);
+            memset(*memb_ptr2, 0, specs->struct_size);
+        }
+    }
+    return rv;
+}

+ 440 - 0
lib/asn1/asn_application.c

@@ -0,0 +1,440 @@
+/*
+ * Copyright (c) 2017 Lev Walkin <vlm@lionet.info>. All rights reserved.
+ * Redistribution and modifications are permitted subject to BSD license.
+ */
+#include <asn_internal.h>
+#include <asn_application.h>
+#include <errno.h>
+
+static asn_enc_rval_t asn_encode_internal(const asn_codec_ctx_t *opt_codec_ctx,
+                                          enum asn_transfer_syntax syntax,
+                                          const asn_TYPE_descriptor_t *td,
+                                          const void *sptr,
+                                          asn_app_consume_bytes_f *callback,
+                                          void *callback_key);
+
+
+struct callback_count_bytes_key {
+    asn_app_consume_bytes_f *callback;
+    void *callback_key;
+    size_t computed_size;
+};
+
+/*
+ * Encoder which just counts bytes that come through it.
+ */
+static int
+callback_count_bytes_cb(const void *data, size_t size, void *keyp) {
+    struct callback_count_bytes_key *key = keyp;
+    int ret;
+
+    ret = key->callback(data, size, key->callback_key);
+    if(ret >= 0) {
+        key->computed_size += size;
+    }
+
+    return ret;
+}
+
+struct overrun_encoder_key {
+    void *buffer;
+    size_t buffer_size;
+    size_t computed_size;
+};
+
+struct dynamic_encoder_key {
+    void *buffer;
+    size_t buffer_size;
+    size_t computed_size;
+};
+
+struct callback_failure_catch_key {
+    asn_app_consume_bytes_f *callback;
+    void *callback_key;
+    int callback_failed;
+};
+
+/*
+ * Encoder which doesn't stop counting bytes
+ * even if it reaches the end of the buffer.
+ */
+static int
+overrun_encoder_cb(const void *data, size_t size, void *keyp) {
+    struct overrun_encoder_key *key = keyp;
+
+    if(key->computed_size + size > key->buffer_size) {
+        /*
+         * Avoid accident on the next call:
+         * stop adding bytes to the buffer.
+         */
+        key->buffer_size = 0;
+    } else {
+        memcpy((char *)key->buffer + key->computed_size, data, size);
+    }
+    key->computed_size += size;
+
+    return 0;
+}
+
+/*
+ * Encoder which dynamically allocates output, and continues
+ * to count even if allocation failed.
+ */
+static int
+dynamic_encoder_cb(const void *data, size_t size, void *keyp) {
+    struct dynamic_encoder_key *key = keyp;
+
+    if(key->buffer) {
+        if(key->computed_size + size >= key->buffer_size) {
+            void *p;
+            size_t new_size = key->buffer_size;
+
+            do {
+                new_size *= 2;
+            } while(new_size <= key->computed_size + size);
+
+            p = REALLOC(key->buffer, new_size);
+            if(p) {
+                key->buffer = p;
+                key->buffer_size = new_size;
+            } else {
+                FREEMEM(key->buffer);
+                key->buffer = 0;
+                key->buffer_size = 0;
+                key->computed_size += size;
+                return 0;
+            }
+        }
+        memcpy((char *)key->buffer + key->computed_size, data, size);
+    }
+
+    key->computed_size += size;
+
+    return 0;
+}
+
+/*
+ * Encoder which help convert the application level encoder failure into EIO.
+ */
+static int
+callback_failure_catch_cb(const void *data, size_t size, void *keyp) {
+    struct callback_failure_catch_key *key = keyp;
+    int ret;
+
+    ret = key->callback(data, size, key->callback_key);
+    if(ret < 0) {
+        key->callback_failed = 1;
+    }
+
+    return ret;
+}
+
+asn_enc_rval_t
+asn_encode(const asn_codec_ctx_t *opt_codec_ctx,
+           enum asn_transfer_syntax syntax, const asn_TYPE_descriptor_t *td,
+           const void *sptr, asn_app_consume_bytes_f *callback, void *callback_key) {
+    struct callback_failure_catch_key cb_key;
+    asn_enc_rval_t er;
+
+    if(!callback) {
+        errno = EINVAL;
+        ASN__ENCODE_FAILED;
+    }
+
+    cb_key.callback = callback;
+    cb_key.callback_key = callback_key;
+    cb_key.callback_failed = 0;
+
+    er = asn_encode_internal(opt_codec_ctx, syntax, td, sptr,
+                             callback_failure_catch_cb, &cb_key);
+    if(cb_key.callback_failed) {
+        assert(er.encoded == -1);
+        assert(errno == EBADF);
+        errno = EIO;
+    }
+
+    return er;
+}
+
+asn_enc_rval_t
+asn_encode_to_buffer(const asn_codec_ctx_t *opt_codec_ctx,
+                     enum asn_transfer_syntax syntax,
+                     const asn_TYPE_descriptor_t *td, const void *sptr,
+                     void *buffer, size_t buffer_size) {
+    struct overrun_encoder_key buf_key;
+    asn_enc_rval_t er;
+
+    if(buffer_size > 0 && !buffer) {
+        errno = EINVAL;
+        ASN__ENCODE_FAILED;
+    }
+
+    buf_key.buffer = buffer;
+    buf_key.buffer_size = buffer_size;
+    buf_key.computed_size = 0;
+
+    er = asn_encode_internal(opt_codec_ctx, syntax, td, sptr,
+                             overrun_encoder_cb, &buf_key);
+
+    if(er.encoded >= 0 && (size_t)er.encoded != buf_key.computed_size) {
+        ASN_DEBUG("asn_encode() returned %" ASN_PRI_SSIZE
+                  " yet produced %" ASN_PRI_SIZE " bytes",
+                  er.encoded, buf_key.computed_size);
+        assert(er.encoded < 0 || (size_t)er.encoded == buf_key.computed_size);
+    }
+
+    return er;
+}
+
+asn_encode_to_new_buffer_result_t
+asn_encode_to_new_buffer(const asn_codec_ctx_t *opt_codec_ctx,
+                         enum asn_transfer_syntax syntax,
+                         const asn_TYPE_descriptor_t *td, const void *sptr) {
+    struct dynamic_encoder_key buf_key;
+    asn_encode_to_new_buffer_result_t res;
+
+    buf_key.buffer_size = 16;
+    buf_key.buffer = MALLOC(buf_key.buffer_size);
+    buf_key.computed_size = 0;
+
+    res.result = asn_encode_internal(opt_codec_ctx, syntax, td, sptr,
+                                     dynamic_encoder_cb, &buf_key);
+
+    if(res.result.encoded >= 0
+       && (size_t)res.result.encoded != buf_key.computed_size) {
+        ASN_DEBUG("asn_encode() returned %" ASN_PRI_SSIZE
+                  " yet produced %" ASN_PRI_SIZE " bytes",
+                  res.result.encoded, buf_key.computed_size);
+        assert(res.result.encoded < 0
+               || (size_t)res.result.encoded == buf_key.computed_size);
+    }
+
+    res.buffer = buf_key.buffer;
+
+    /* 0-terminate just in case. */
+    if(res.buffer) {
+        assert(buf_key.computed_size < buf_key.buffer_size);
+        ((char *)res.buffer)[buf_key.computed_size] = '\0';
+    }
+
+    return res;
+}
+
+static asn_enc_rval_t
+asn_encode_internal(const asn_codec_ctx_t *opt_codec_ctx,
+                    enum asn_transfer_syntax syntax,
+                    const asn_TYPE_descriptor_t *td, const void *sptr,
+                    asn_app_consume_bytes_f *callback, void *callback_key) {
+    asn_enc_rval_t er;
+    enum xer_encoder_flags_e xer_flags = XER_F_CANONICAL;
+
+    (void)opt_codec_ctx; /* Parameters are not checked on encode yet. */
+
+    if(!td || !sptr) {
+        errno = EINVAL;
+        ASN__ENCODE_FAILED;
+    }
+
+    switch(syntax) {
+    case ATS_NONSTANDARD_PLAINTEXT:
+        if(td->op->print_struct) {
+            struct callback_count_bytes_key cb_key;
+            cb_key.callback = callback;
+            cb_key.callback_key = callback_key;
+            cb_key.computed_size = 0;
+            if(td->op->print_struct(td, sptr, 1, callback_count_bytes_cb,
+                                    &cb_key)
+                   < 0
+               || callback_count_bytes_cb("\n", 1, &cb_key) < 0) {
+                errno = EBADF; /* Structure has incorrect form. */
+                er.encoded = -1;
+                er.failed_type = td;
+                er.structure_ptr = sptr;
+            } else {
+                er.encoded = cb_key.computed_size;
+                er.failed_type = 0;
+                er.structure_ptr = 0;
+            }
+        } else {
+            errno = ENOENT; /* Transfer syntax is not defined for this type. */
+            ASN__ENCODE_FAILED;
+        }
+        break;
+
+    case ATS_RANDOM:
+        errno = ENOENT; /* Randomization doesn't make sense on output. */
+        ASN__ENCODE_FAILED;
+
+    case ATS_BER:
+        /* BER is a superset of DER. */
+        /* Fall through. */
+    case ATS_DER:
+        if(td->op->der_encoder) {
+            er = der_encode(td, sptr, callback, callback_key);
+            if(er.encoded == -1) {
+                if(er.failed_type && er.failed_type->op->der_encoder) {
+                    errno = EBADF;  /* Structure has incorrect form. */
+                } else {
+                    errno = ENOENT; /* DER is not defined for this type. */
+                }
+            }
+        } else {
+            errno = ENOENT; /* Transfer syntax is not defined for this type. */
+            ASN__ENCODE_FAILED;
+        }
+        break;
+    case ATS_CER:
+        errno = ENOENT; /* Transfer syntax is not defined for any type. */
+        ASN__ENCODE_FAILED;
+
+#ifdef  ASN_DISABLE_OER_SUPPORT
+    case ATS_BASIC_OER:
+    case ATS_CANONICAL_OER:
+        errno = ENOENT; /* PER is not defined. */
+        ASN__ENCODE_FAILED;
+        break;
+#else /* ASN_DISABLE_OER_SUPPORT */
+    case ATS_BASIC_OER:
+        /* CANONICAL-OER is a superset of BASIC-OER. */
+        /* Fall through. */
+    case ATS_CANONICAL_OER:
+        if(td->op->oer_encoder) {
+            er = oer_encode(td, sptr, callback, callback_key);
+            if(er.encoded == -1) {
+                if(er.failed_type && er.failed_type->op->oer_encoder) {
+                    errno = EBADF;  /* Structure has incorrect form. */
+                } else {
+                    errno = ENOENT; /* OER is not defined for this type. */
+                }
+            }
+        } else {
+            errno = ENOENT; /* Transfer syntax is not defined for this type. */
+            ASN__ENCODE_FAILED;
+        }
+        break;
+#endif /* ASN_DISABLE_OER_SUPPORT */
+
+#ifdef  ASN_DISABLE_PER_SUPPORT
+    case ATS_UNALIGNED_BASIC_PER:
+    case ATS_UNALIGNED_CANONICAL_PER:
+        errno = ENOENT; /* PER is not defined. */
+        ASN__ENCODE_FAILED;
+        break;
+#else /* ASN_DISABLE_PER_SUPPORT */
+    case ATS_UNALIGNED_BASIC_PER:
+        /* CANONICAL-UPER is a superset of BASIC-UPER. */
+        /* Fall through. */
+    case ATS_UNALIGNED_CANONICAL_PER:
+        if(td->op->uper_encoder) {
+            er = uper_encode(td, 0, sptr, callback, callback_key);
+            if(er.encoded == -1) {
+                if(er.failed_type && er.failed_type->op->uper_encoder) {
+                    errno = EBADF;  /* Structure has incorrect form. */
+                } else {
+                    errno = ENOENT; /* UPER is not defined for this type. */
+                }
+            } else {
+                ASN_DEBUG("Complete encoded in %ld bits", (long)er.encoded);
+                if(er.encoded == 0) {
+                    /* Enforce "Complete Encoding" of X.691 #11.1 */
+                    if(callback("\0", 1, callback_key) < 0) {
+                        errno = EBADF;
+                        ASN__ENCODE_FAILED;
+                    }
+                    er.encoded = 8; /* Exactly 8 zero bits is added. */
+                }
+                /* Convert bits into bytes */
+                er.encoded = (er.encoded + 7) >> 3;
+            }
+        } else {
+            errno = ENOENT; /* Transfer syntax is not defined for this type. */
+            ASN__ENCODE_FAILED;
+        }
+        break;
+#endif  /* ASN_DISABLE_PER_SUPPORT */
+
+    case ATS_BASIC_XER:
+        /* CANONICAL-XER is a superset of BASIC-XER. */
+        xer_flags &= ~XER_F_CANONICAL;
+        xer_flags |= XER_F_BASIC;
+        /* Fall through. */
+    case ATS_CANONICAL_XER:
+        if(td->op->xer_encoder) {
+            er = xer_encode(td, sptr, xer_flags, callback, callback_key);
+            if(er.encoded == -1) {
+                if(er.failed_type && er.failed_type->op->xer_encoder) {
+                    errno = EBADF;  /* Structure has incorrect form. */
+                } else {
+                    errno = ENOENT; /* XER is not defined for this type. */
+                }
+            }
+        } else {
+            errno = ENOENT; /* Transfer syntax is not defined for this type. */
+            ASN__ENCODE_FAILED;
+        }
+        break;
+
+    default:
+        errno = ENOENT;
+        ASN__ENCODE_FAILED;
+    }
+
+    return er;
+}
+
+asn_dec_rval_t
+asn_decode(const asn_codec_ctx_t *opt_codec_ctx,
+           enum asn_transfer_syntax syntax, const asn_TYPE_descriptor_t *td,
+           void **sptr, const void *buffer, size_t size) {
+    if(!td || !td->op || !sptr || (size && !buffer)) {
+        ASN__DECODE_FAILED;
+    }
+
+    switch(syntax) {
+    case ATS_CER:
+    case ATS_NONSTANDARD_PLAINTEXT:
+    default:
+        errno = ENOENT;
+        ASN__DECODE_FAILED;
+
+    case ATS_RANDOM:
+        if(!td->op->random_fill) {
+            ASN__DECODE_FAILED;
+        } else {
+            if(asn_random_fill(td, sptr, 16000) == 0) {
+                asn_dec_rval_t ret = {RC_OK, 0};
+                return ret;
+            } else {
+                ASN__DECODE_FAILED;
+            }
+        }
+        break;
+
+    case ATS_DER:
+    case ATS_BER:
+        return ber_decode(opt_codec_ctx, td, sptr, buffer, size);
+
+    case ATS_BASIC_OER:
+    case ATS_CANONICAL_OER:
+#ifdef  ASN_DISABLE_OER_SUPPORT
+        errno = ENOENT;
+        ASN__DECODE_FAILED;
+#else
+        return oer_decode(opt_codec_ctx, td, sptr, buffer, size);
+#endif
+
+    case ATS_UNALIGNED_BASIC_PER:
+    case ATS_UNALIGNED_CANONICAL_PER:
+#ifdef  ASN_DISABLE_PER_SUPPORT
+        errno = ENOENT;
+        ASN__DECODE_FAILED;
+#else
+        return uper_decode_complete(opt_codec_ctx, td, sptr, buffer, size);
+#endif
+
+    case ATS_BASIC_XER:
+    case ATS_CANONICAL_XER:
+        return xer_decode(opt_codec_ctx, td, sptr, buffer, size);
+    }
+}
+

+ 169 - 0
lib/asn1/asn_application.h

@@ -0,0 +1,169 @@
+/*-
+ * Copyright (c) 2004-2017 Lev Walkin <vlm@lionet.info>. 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
+
+/*
+ * A selection of ASN.1 Transfer Syntaxes to use with generalized
+ * encoders and decoders declared further in this .h file.
+ */
+enum asn_transfer_syntax {
+    /* Avoid appearance of a default transfer syntax. */
+    ATS_INVALID = 0,
+    /* Plaintext output (not conforming to any standard), for debugging. */
+    ATS_NONSTANDARD_PLAINTEXT,
+    /* Returns a randomly generatede structure. */
+    ATS_RANDOM,
+    /*
+     * X.690:
+     * BER: Basic Encoding Rules.
+     * DER: Distinguished Encoding Rules.
+     * CER: Canonical Encoding Rules.
+     * DER and CER are more strict variants of BER.
+     */
+    ATS_BER,
+    ATS_DER,
+    ATS_CER, /* Only decoding is supported */
+    /*
+     * X.696:
+     * OER: Octet Encoding Rules.
+     * CANONICAL-OER is a more strict variant of BASIC-OER.
+     */
+    ATS_BASIC_OER,
+    ATS_CANONICAL_OER,
+    /*
+     * X.691:
+     * PER: Packed Encoding Rules.
+     * CANONICAL-PER is a more strict variant of BASIC-PER.
+     * NOTE: Produces or consumes a complete encoding (X.691 (08/2015) #11.1).
+     */
+    ATS_UNALIGNED_BASIC_PER,
+    ATS_UNALIGNED_CANONICAL_PER,
+    /*
+     * X.693:
+     * XER: XML Encoding Rules.
+     * CANONICAL-XER is a more strict variant of BASIC-XER.
+     */
+    ATS_BASIC_XER,
+    ATS_CANONICAL_XER
+};
+
+/*
+ * A generic encoder for any supported transfer syntax.
+ * RETURN VALUES:
+ * The (.encoded) field of the return value is REDEFINED to mean the following:
+ * >=0: The computed size of the encoded data. Can exceed the (buffer_size).
+ *  -1: Error encoding the structure. See the error code in (errno):
+ *      EINVAL: Incorrect parameters to the function, such as NULLs.
+ *      ENOENT: Encoding transfer syntax is not defined (for this type).
+ *      EBADF:  The structure has invalid form or content constraint failed.
+ *      The (.failed_type) and (.structure_ptr) MIGHT be set to the appropriate
+ *      values at the place of failure, if at all possible.
+ * WARNING: The (.encoded) field of the return value can exceed the buffer_size.
+ * This is similar to snprintf(3) contract which might return values
+ * greater than the buffer size.
+ */
+asn_enc_rval_t asn_encode_to_buffer(
+    const asn_codec_ctx_t *opt_codec_parameters, /* See asn_codecs.h */
+    enum asn_transfer_syntax,
+    const struct asn_TYPE_descriptor_s *type_to_encode,
+    const void *structure_to_encode, void *buffer, size_t buffer_size);
+
+/*
+ * A variant of asn_encode_to_buffer() with automatically allocated buffer.
+ * RETURN VALUES:
+ * On success, returns a newly allocated (.buffer) containing the whole message.
+ * The message size is returned in (.result.encoded).
+ * On failure:
+ *  (.buffer) is NULL,
+ *  (.result.encoded) as in asn_encode_to_buffer(),
+ *  The errno codes as in asn_encode_to_buffer(), plus the following:
+ *      ENOMEM: Memory allocation failed due to system or internal limits.
+ * The user is responsible for freeing the (.buffer).
+ */
+typedef struct asn_encode_to_new_buffer_result_s {
+    void *buffer;   /* NULL if failed to encode. */
+    asn_enc_rval_t result;
+} asn_encode_to_new_buffer_result_t;
+asn_encode_to_new_buffer_result_t asn_encode_to_new_buffer(
+    const asn_codec_ctx_t *opt_codec_parameters, /* See asn_codecs.h */
+    enum asn_transfer_syntax,
+    const struct asn_TYPE_descriptor_s *type_to_encode,
+    const void *structure_to_encode);
+
+
+/*
+ * 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 generic encoder for any supported transfer syntax.
+ * Returns the comprehensive encoding result descriptor (see asn_codecs.h).
+ * RETURN VALUES:
+ * The negative (.encoded) field of the return values is accompanied with the
+ * following error codes (errno):
+ *      EINVAL: Incorrect parameters to the function, such as NULLs.
+ *      ENOENT: Encoding transfer syntax is not defined (for this type).
+ *      EBADF:  The structure has invalid form or content constraint failed.
+ *      EIO:    The (callback) has returned negative value during encoding.
+ */
+asn_enc_rval_t asn_encode(
+    const asn_codec_ctx_t *opt_codec_parameters, /* See asn_codecs.h */
+    enum asn_transfer_syntax,
+    const struct asn_TYPE_descriptor_s *type_to_encode,
+    const void *structure_to_encode,
+    asn_app_consume_bytes_f *callback, void *callback_key);
+
+
+/*
+ * A generic decoder for any supported transfer syntax.
+ */
+asn_dec_rval_t asn_decode(
+    const asn_codec_ctx_t *opt_codec_parameters, enum asn_transfer_syntax,
+    const struct asn_TYPE_descriptor_s *type_to_decode,
+    void **structure_ptr, /* Pointer to a target structure's pointer */
+    const void *buffer,   /* Data to be decoded */
+    size_t size           /* Size of that buffer */
+);
+
+
+/*
+ * 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,
+	const struct asn_TYPE_descriptor_s *type_descriptor_which_failed,
+	const void *structure_which_failed_ptr,
+	const char *error_message_format, ...) CC_PRINTFLIKE(4, 5);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#include "constr_TYPE.h"	/* for asn_TYPE_descriptor_t */
+
+#endif	/* ASN_APPLICATION_H */

+ 333 - 0
lib/asn1/asn_bit_data.c

@@ -0,0 +1,333 @@
+/*
+ * Copyright (c) 2005-2017 Lev Walkin <vlm@lionet.info>.
+ * All rights reserved.
+ * Redistribution and modifications are permitted subject to BSD license.
+ */
+#include <asn_system.h>
+#include <asn_internal.h>
+#include <asn_bit_data.h>
+
+/*
+ * Create a contiguous non-refillable bit data structure.
+ * Can be freed by FREEMEM().
+ */
+asn_bit_data_t *
+asn_bit_data_new_contiguous(const void *data, size_t size_bits) {
+    size_t size_bytes = (size_bits + 7) / 8;
+    asn_bit_data_t *pd;
+    uint8_t *bytes;
+
+    /* Get the extensions map */
+    pd = CALLOC(1, sizeof(*pd) + size_bytes + 1);
+    if(!pd) {
+        return NULL;
+    }
+    bytes = (void *)(((char *)pd) + sizeof(*pd));
+    memcpy(bytes, data, size_bytes);
+    bytes[size_bytes] = 0;
+    pd->buffer = bytes;
+    pd->nboff = 0;
+    pd->nbits = size_bits;
+
+    return pd;
+}
+
+
+char *
+asn_bit_data_string(asn_bit_data_t *pd) {
+	static char buf[2][32];
+	static int n;
+	n = (n+1) % 2;
+    snprintf(buf[n], sizeof(buf[n]),
+             "{m=%" ASN_PRI_SIZE " span %" ASN_PRI_SIZE "[%" ASN_PRI_SIZE
+             "..%" ASN_PRI_SIZE "] (%" ASN_PRI_SIZE ")}",
+             pd->moved, ((uintptr_t)(pd->buffer) & 0xf), pd->nboff, pd->nbits,
+             pd->nbits - pd->nboff);
+    return buf[n];
+}
+
+void
+asn_get_undo(asn_bit_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
+asn_get_few_bits(asn_bit_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 = asn_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 = asn_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 = (((uint32_t)buf[0] << 24) + (buf[1] << 16)
+			+ (buf[2] << 8) + (buf[3])) >> (32 - off);
+	else if(nbits <= 31) {
+		asn_bit_data_t tpd = *pd;
+		/* Here are we with our 31-bits limit plus 1..7 bits offset. */
+		asn_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  = asn_get_few_bits(&tpd, nbits - 24) << 24;
+		accum |= asn_get_few_bits(&tpd, 24);
+	} else {
+		asn_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
+asn_get_many_bits(asn_bit_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 = asn_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 = asn_get_few_bits(pd, 24);
+			if(value < 0) return -1;
+			*(dst++) = value >> 16;
+			*(dst++) = value >> 8;
+			*(dst++) = value;
+			nbits -= 24;
+		} else {
+			value = asn_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;
+}
+
+/*
+ * Put a small number of bits (<= 31).
+ */
+int
+asn_put_few_bits(asn_bit_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, (void *)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) {
+		size_t complete_bytes;
+		if(!po->buffer) po->buffer = po->tmpspace;
+		complete_bytes = (po->buffer - po->tmpspace);
+		ASN_DEBUG("[PER output %ld complete + %ld]",
+			(long)complete_bytes, (long)po->flushed_bytes);
+		if(po->output(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 {
+		if(asn_put_few_bits(po, bits >> (obits - 24), 24)) return -1;
+		if(asn_put_few_bits(po, bits, obits - 24)) return -1;
+	}
+
+	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
+asn_put_many_bits(asn_bit_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(asn_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(asn_put_few_bits(po, value, nbits))
+				return -1;
+			break;
+		}
+	}
+
+	return 0;
+}
+
+
+int
+asn_put_aligned_flush(asn_bit_outp_t *po) {
+    uint32_t unused_bits = (0x7 & (8 - (po->nboff & 0x07)));
+    size_t complete_bytes =
+        (po->buffer ? po->buffer - po->tmpspace : 0) + ((po->nboff + 7) >> 3);
+
+    if(unused_bits) {
+        po->buffer[po->nboff >> 3] &= ~0u << unused_bits;
+    }
+
+    if(po->output(po->tmpspace, complete_bytes, po->op_key) < 0) {
+        return -1;
+    } else {
+        po->buffer = po->tmpspace;
+        po->nboff = 0;
+        po->nbits = 8 * sizeof(po->tmpspace);
+        po->flushed_bytes += complete_bytes;
+        return 0;
+    }
+}
+

+ 83 - 0
lib/asn1/asn_bit_data.h

@@ -0,0 +1,83 @@
+/*
+ * Copyright (c) 2005-2017 Lev Walkin <vlm@lionet.info>. All rights reserved.
+ * Redistribution and modifications are permitted subject to BSD license.
+ */
+#ifndef	ASN_BIT_DATA
+#define	ASN_BIT_DATA
+
+#include <asn_system.h>		/* Platform-specific types */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * This structure describes a position inside an incoming PER bit stream.
+ */
+typedef struct asn_bit_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_bit_data_s *);
+  void *refill_key;
+} asn_bit_data_t;
+
+/*
+ * Create a contiguous non-refillable bit data structure.
+ * Can be freed by FREEMEM().
+ */
+asn_bit_data_t *asn_bit_data_new_contiguous(const void *data, size_t size_bits);
+
+/*
+ * 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 asn_get_few_bits(asn_bit_data_t *, int get_nbits);
+
+/* Undo the immediately preceeding "get_few_bits" operation */
+void asn_get_undo(asn_bit_data_t *, 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 asn_get_many_bits(asn_bit_data_t *, uint8_t *dst, int right_align,
+			int get_nbits);
+
+/* Non-thread-safe debugging function, don't use it */
+char *asn_bit_data_string(asn_bit_data_t *);
+
+/*
+ * This structure supports forming bit output.
+ */
+typedef struct asn_bit_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 (*output)(const void *data, size_t size, void *op_key);
+	void *op_key;		/* Key for (output) data callback */
+	size_t flushed_bytes;	/* Bytes already flushed through (output) */
+} asn_bit_outp_t;
+
+/* Output a small number of bits (<= 31) */
+int asn_put_few_bits(asn_bit_outp_t *, uint32_t bits, int obits);
+
+/* Output a large number of bits */
+int asn_put_many_bits(asn_bit_outp_t *, const uint8_t *src, int put_nbits);
+
+/*
+ * Flush whole bytes (0 or more) through (outper) member.
+ * The least significant bits which are not used are guaranteed to be set to 0.
+ * Returns -1 if callback returns -1. Otherwise, 0.
+ */
+int asn_put_aligned_flush(asn_bit_outp_t *);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif	/* ASN_BIT_DATA */

+ 108 - 0
lib/asn1/asn_codecs.h

@@ -0,0 +1,108 @@
+/*
+ * Copyright (c) 2003-2017 Lev Walkin <vlm@lionet.info>. 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 */
+	const struct asn_TYPE_descriptor_s *failed_type;
+
+	/* Pointer to the structure of that type */
+	const 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 */

+ 317 - 0
lib/asn1/asn_codecs_prim.c

@@ -0,0 +1,317 @@
+/*-
+ * Copyright (c) 2003, 2004 Lev Walkin <vlm@lionet.info>. All rights reserved.
+ * Redistribution and modifications are permitted subject to BSD license.
+ */
+#include <asn_internal.h>
+#include <asn_codecs_prim.h>
+#include <errno.h>
+
+/*
+ * Decode an always-primitive type.
+ */
+asn_dec_rval_t
+ber_decode_primitive(const asn_codec_ctx_t *opt_codec_ctx,
+                     const 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(const asn_TYPE_descriptor_t *td, const void *sptr,
+                     int tag_mode, ber_tlv_tag_t tag,
+                     asn_app_consume_bytes_f *cb, void *app_key) {
+    asn_enc_rval_t erval;
+	const ASN__PRIMITIVE_TYPE_t *st = (const 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(const asn_TYPE_descriptor_t *td, void *sptr,
+                         enum asn_struct_free_method method) {
+    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);
+
+    switch(method) {
+    case ASFM_FREE_EVERYTHING:
+        FREEMEM(sptr);
+        break;
+    case ASFM_FREE_UNDERLYING:
+        break;
+    case ASFM_FREE_UNDERLYING_AND_RESET:
+        memset(sptr, 0, sizeof(ASN__PRIMITIVE_TYPE_t));
+        break;
+    }
+}
+
+
+/*
+ * Local internal type passed around as an argument.
+ */
+struct xdp_arg_s {
+    const 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 (<MINUS-INFINITY>, <enum-element>, 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:
+			 * "<INTEGER>123<!--/--> </INTEGER>"
+			 *                      ^- 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:
+		 * "1<tag_start..."
+		 */
+		arg->want_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(const asn_codec_ctx_t *opt_codec_ctx,
+                     const 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;
+}
+

+ 51 - 0
lib/asn1/asn_codecs_prim.h

@@ -0,0 +1,51 @@
+/*-
+ * Copyright (c) 2004-2017 Lev Walkin <vlm@lionet.info>. All rights reserved.
+ * Redistribution and modifications are permitted subject to BSD license.
+ */
+#ifndef	ASN_CODECS_PRIM_H
+#define	ASN_CODECS_PRIM_H
+
+#include <asn_application.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct ASN__PRIMITIVE_TYPE_s {
+    uint8_t *buf;   /* Buffer with consecutive primitive encoding bytes */
+    size_t 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)(
+    const 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(
+    const asn_codec_ctx_t *opt_codec_ctx,
+    const 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 */

+ 47 - 0
lib/asn1/asn_internal.c

@@ -0,0 +1,47 @@
+#include <asn_internal.h>
+
+ssize_t
+asn__format_to_callback(int (*cb)(const void *, size_t, void *key), void *key,
+                        const char *fmt, ...) {
+    char scratch[64];
+    char *buf = scratch;
+    size_t buf_size = sizeof(scratch);
+    int wrote;
+    int cb_ret;
+
+    do {
+        va_list args;
+        va_start(args, fmt);
+
+        wrote = vsnprintf(buf, buf_size, fmt, args);
+        if(wrote < (ssize_t)buf_size) {
+            if(wrote < 0) {
+                if(buf != scratch) FREEMEM(buf);
+                return -1;
+            }
+            break;
+        }
+
+        buf_size <<= 1;
+        if(buf == scratch) {
+            buf = MALLOC(buf_size);
+            if(!buf) return -1;
+        } else {
+            void *p = REALLOC(buf, buf_size);
+            if(!p) {
+                FREEMEM(buf);
+                return -1;
+            }
+            buf = p;
+        }
+    } while(1);
+
+    cb_ret = cb(buf, wrote, key);
+    if(buf != scratch) FREEMEM(buf);
+    if(cb_ret < 0) {
+        return -1;
+    }
+
+    return wrote;
+}
+

+ 152 - 0
lib/asn1/asn_internal.h

@@ -0,0 +1,152 @@
+/*
+ * Copyright (c) 2003-2017 Lev Walkin <vlm@lionet.info>. 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
+#ifndef __EXTENSIONS__
+#define __EXTENSIONS__          /* for Sun */
+#endif
+#include "asn_application.h"	/* Application-visible API */
+
+#ifndef	__NO_ASSERT_H__		/* Include assert.h only for internal use. */
+#include <assert.h>		/* 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)
+
+#ifdef  EMIT_ASN_DEBUG
+#warning "Use ASN_EMIT_DEBUG instead of EMIT_ASN_DEBUG"
+#define ASN_EMIT_DEBUG  EMIT_ASN_DEBUG
+#endif
+
+/*
+ * 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	ASN_EMIT_DEBUG == 1	/* And it was asked to emit this code... */
+#if __STDC_VERSION__ >= 199901L
+#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	/* !C99 */
+void CC_PRINTFLIKE(1, 2) ASN_DEBUG_f(const char *fmt, ...);
+#define	ASN_DEBUG	ASN_DEBUG_f
+#endif	/* C99 */
+#else	/* ASN_EMIT_DEBUG != 1 */
+#if __STDC_VERSION__ >= 199901L
+#define ASN_DEBUG(...) do{}while(0)
+#else   /* not C99 */
+static void CC_PRINTFLIKE(1, 2) ASN_DEBUG(const char *fmt, ...) { (void)fmt; }
+#endif  /* C99 or better */
+#endif	/* ASN_EMIT_DEBUG */
+#endif	/* ASN_DEBUG */
+
+/*
+ * Print to a callback.
+ * The callback is expected to return negative values on error.
+ * 0 and positive values are treated as success.
+ * RETURN VALUES:
+ *  -1: Failed to format or invoke the callback.
+ *  >0: Size of the data that got delivered to the callback.
+ */
+ssize_t CC_PRINTFLIKE(3, 4)
+asn__format_to_callback(
+    int (*callback)(const void *, size_t, void *key), void *key,
+    const char *fmt, ...);
+
+/*
+ * 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(size, foo) \
+    do {                           \
+        if(foo) goto cb_failed;    \
+        er.encoded += (size);      \
+    } while(0)
+#define ASN__CALLBACK(buf, size) ASN__E_CALLBACK(size, ASN__E_cbc(buf, size))
+#define ASN__CALLBACK2(buf1, size1, buf2, size2) \
+    ASN__E_CALLBACK((size1) + (size2),           \
+                    ASN__E_cbc(buf1, size1) || ASN__E_cbc(buf2, size2))
+#define ASN__CALLBACK3(buf1, size1, buf2, size2, buf3, size3)          \
+    ASN__E_CALLBACK((size1) + (size2) + (size3),                       \
+                    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); \
+    } 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 CC_NOTUSED
+ASN__STACK_OVERFLOW_CHECK(const asn_codec_ctx_t *ctx) {
+	if(ctx && ctx->max_stack_size) {
+
+		/* ctx MUST be allocated on the stack */
+		ptrdiff_t usedstack = ((const char *)ctx - (const 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 */

+ 50 - 0
lib/asn1/asn_ioc.h

@@ -0,0 +1,50 @@
+/*
+ * Run-time support for Information Object Classes.
+ * Copyright (c) 2017 Lev Walkin <vlm@lionet.info>. All rights reserved.
+ * Redistribution and modifications are permitted subject to BSD license.
+ */
+#ifndef	ASN_IOC_H
+#define	ASN_IOC_H
+
+#include <asn_system.h>		/* Platform-specific types */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct asn_TYPE_descriptor_s;
+struct asn_ioc_cell_s;
+
+/*
+ * X.681, #13
+ */
+typedef struct asn_ioc_set_s {
+    size_t rows_count;
+    size_t columns_count;
+    const struct asn_ioc_cell_s *rows;
+} asn_ioc_set_t;
+
+
+typedef struct asn_ioc_cell_s {
+    const char *field_name; /* Is equal to corresponding column_name */
+    enum {
+        aioc__value,
+        aioc__type,
+        aioc__open_type,
+    } cell_kind;
+    struct asn_TYPE_descriptor_s *type_descriptor;
+    const void *value_sptr;
+    struct {
+        size_t types_count;
+        struct {
+            unsigned choice_position;
+        } *types;
+    } open_type;
+} asn_ioc_cell_t;
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif	/* ASN_IOC_H */

+ 56 - 0
lib/asn1/asn_random_fill.c

@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2017 Lev Walkin <vlm@lionet.info>.
+ * All rights reserved.
+ * Redistribution and modifications are permitted subject to BSD license.
+ */
+#include <asn_internal.h>
+#include <asn_random_fill.h>
+#include <constr_TYPE.h>
+
+int
+asn_random_fill(const struct asn_TYPE_descriptor_s *td, void **struct_ptr,
+                size_t length) {
+
+    if(td && td->op->random_fill) {
+        asn_random_fill_result_t res =
+            td->op->random_fill(td, struct_ptr, 0, length);
+        return (res.code == ARFILL_OK) ? 0 : -1;
+    } else {
+        return -1;
+    }
+}
+
+static uintmax_t
+asn__intmax_range(intmax_t lb, intmax_t ub) {
+    assert(lb <= ub);
+    if((ub < 0) == (lb < 0)) {
+        return ub - lb;
+    } else if(lb < 0) {
+        return 1 + ((uintmax_t)ub + (uintmax_t)-(lb + 1));
+    } else {
+        assert(!"Unreachable");
+        return 0;
+    }
+}
+
+intmax_t
+asn_random_between(intmax_t lb, intmax_t rb) {
+    if(lb == rb) {
+        return lb;
+    } else {
+        const uintmax_t intmax_max = ((~(uintmax_t)0) >> 1);
+        uintmax_t range = asn__intmax_range(lb, rb);
+        uintmax_t value = 0;
+        uintmax_t got_entropy = 0;
+
+        assert(RAND_MAX > 0xffffff);    /* Seen 7ffffffd! */
+        assert(range < intmax_max);
+
+        for(; got_entropy < range;) {
+            got_entropy = (got_entropy << 24) | 0xffffff;
+            value = (value << 24) | (random() % 0xffffff);
+        }
+
+        return lb + (intmax_t)(value % (range + 1));
+    }
+}

+ 51 - 0
lib/asn1/asn_random_fill.h

@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2017 Lev Walkin <vlm@lionet.info>. All rights reserved.
+ * Redistribution and modifications are permitted subject to BSD license.
+ */
+#ifndef	ASN_RANDOM_FILL
+#define	ASN_RANDOM_FILL
+
+/* Forward declarations */
+struct asn_TYPE_descriptor_s;
+struct asn_encoding_constraints_s;
+
+/*
+ * Initialize a structure with random data according to the type specification
+ * and optional member constraints.
+ * ARGUMENTS:
+ *  (max_length)        - See (approx_max_length_limit).
+ *  (memb_constraints)  - Member constraints, if exist.
+ *                        The type can be constrained differently according
+ *                        to PER and OER specifications, so we find a value
+ *                        at the intersection of these constraints.
+ * In case the return differs from ARFILL_OK, the (struct_ptr) contents
+ * and (current_length) value remain in their original state.
+ */
+typedef struct asn_random_fill_result_s {
+    enum {
+        ARFILL_FAILED = -1, /* System error (memory?) */
+        ARFILL_OK = 0,      /* Initialization succeeded */
+        ARFILL_SKIPPED = 1  /* Not done due to (length?) constraint */
+    } code;
+    size_t length; /* Approximate number of bytes created. */
+} asn_random_fill_result_t;
+typedef asn_random_fill_result_t(asn_random_fill_f)(
+    const struct asn_TYPE_descriptor_s *td, void **struct_ptr,
+    const struct asn_encoding_constraints_s *memb_constraints,
+    size_t max_length);
+
+/*
+ * Returns 0 if the structure was properly initialized, -1 otherwise.
+ * The (approx_max_length_limit) specifies the approximate limit of the
+ * resulting structure in units closely resembling bytes. The actual result
+ * might be several times larger or smaller than the length limit.
+ */
+int asn_random_fill(const struct asn_TYPE_descriptor_s *td, void **struct_ptr,
+                    size_t approx_max_length_limit);
+
+/*
+ * Returns a random number between min and max.
+ */
+intmax_t asn_random_between(intmax_t min, intmax_t max);
+
+#endif	/* ASN_RANDOM_FILL */

+ 150 - 0
lib/asn1/asn_system.h

@@ -0,0 +1,150 @@
+/*
+ * Copyright (c) 2003-2017 Lev Walkin <vlm@lionet.info>. 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 _DEFAULT_SOURCE
+#define _DEFAULT_SOURCE 1
+#endif
+
+#ifndef _BSD_SOURCE
+#define _BSD_SOURCE /* for snprintf() on some linux systems  */
+#endif
+
+#include <stdio.h>	/* For snprintf(3) */
+#include <stdlib.h>	/* For *alloc(3) */
+#include <string.h>	/* For memcpy(3) */
+#include <sys/types.h>	/* For size_t */
+#include <limits.h>	/* For LONG_MAX */
+#include <stdarg.h>	/* For va_start */
+#include <stddef.h>	/* for offsetof and ptrdiff_t */
+
+#ifdef	_WIN32
+
+#include <malloc.h>
+#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
+#if _MSC_VER < 1600
+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;
+#else /* _MSC_VER >= 1600 */
+#include <stdint.h>
+#endif /* _MSC_VER < 1600 */
+#endif	/* ASSUMESTDTYPES */
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#include <float.h>
+#define isnan _isnan
+#define finite _finite
+#define copysign _copysign
+#define	ilogb	_logb
+#else	/* !_MSC_VER */
+#include <stdint.h>
+#endif	/* _MSC_VER */
+
+#else	/* !_WIN32 */
+
+#if defined(__vxworks)
+#include <types/vxTypes.h>
+#else	/* !defined(__vxworks) */
+
+#include <inttypes.h>	/* C99 specifies this file */
+#include <netinet/in.h> /* for ntohl() */
+#define	sys_ntohl(foo)	ntohl(foo)
+#endif	/* defined(__vxworks) */
+
+#endif	/* _WIN32 */
+
+#if	__GNUC__ >= 3 || defined(__clang__)
+#define CC_ATTRIBUTE(attr)    __attribute__((attr))
+#else
+#define CC_ATTRIBUTE(attr)
+#endif
+#define CC_PRINTFLIKE(fmt, var)     CC_ATTRIBUTE(format(printf, fmt, var))
+#define	CC_NOTUSED                  CC_ATTRIBUTE(unused)
+#ifndef CC_ATTR_NO_SANITIZE
+#define CC_ATTR_NO_SANITIZE(what)   CC_ATTRIBUTE(no_sanitize(what))
+#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 <stddef.h> */
+#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 */
+
+#if __STDC_VERSION__ >= 199901L
+#ifndef SIZE_MAX
+#define SIZE_MAX   ((~((size_t)0)) >> 1)
+#endif
+
+#ifndef RSIZE_MAX   /* C11, Annex K */
+#define RSIZE_MAX   (SIZE_MAX >> 1)
+#endif
+#ifndef RSSIZE_MAX   /* Halve signed size even further than unsigned */
+#define RSSIZE_MAX   ((ssize_t)(RSIZE_MAX >> 1))
+#endif
+#else   /* Old compiler */
+#undef  SIZE_MAX
+#undef  RSIZE_MAX
+#undef  RSSIZE_MAX
+#define SIZE_MAX   ((~((size_t)0)) >> 1)
+#define RSIZE_MAX   (SIZE_MAX >> 1)
+#define RSSIZE_MAX   ((ssize_t)(RSIZE_MAX >> 1))
+#endif
+
+#if __STDC_VERSION__ >= 199901L
+#define ASN_PRI_SIZE "zu"
+#define ASN_PRI_SSIZE "zd"
+#define ASN_PRIuMAX PRIuMAX
+#define ASN_PRIdMAX PRIdMAX
+#else
+#define ASN_PRI_SIZE "lu"
+#define ASN_PRI_SSIZE "ld"
+#if LLONG_MAX > LONG_MAX
+#define ASN_PRIuMAX "llu"
+#define ASN_PRIdMAX "lld"
+#else
+#define ASN_PRIuMAX "lu"
+#define ASN_PRIdMAX "ld"
+#endif
+#endif
+
+#endif	/* ASN_SYSTEM_H */

+ 283 - 0
lib/asn1/ber_decoder.c

@@ -0,0 +1,283 @@
+/*-
+ * Copyright (c) 2003, 2004 Lev Walkin <vlm@lionet.info>. All rights reserved.
+ * Redistribution and modifications are permitted subject to BSD license.
+ */
+#include <asn_internal.h>
+
+#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(const asn_codec_ctx_t *opt_codec_ctx,
+           const 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->op->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 <TL<TL<TL...>>> tags matches the definition.
+ */
+asn_dec_rval_t
+ber_check_tags(const asn_codec_ctx_t *opt_codec_ctx,
+               const 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 == (int)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 < (int)td->tags_count);	/* At least one loop */
+	}
+	for((void)tagno; tagno < (int)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 < ((int)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);
+}

+ 66 - 0
lib/asn1/ber_decoder.h

@@ -0,0 +1,66 @@
+/*-
+ * Copyright (c) 2003-2017 Lev Walkin <vlm@lionet.info>. All rights reserved.
+ * Redistribution and modifications are permitted subject to BSD license.
+ */
+#ifndef	_BER_DECODER_H_
+#define	_BER_DECODER_H_
+
+#include <asn_application.h>
+
+#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.
+ * Decodes BER, DER and CER data (DER and CER are different subsets of BER).
+ *
+ * NOTE: Use the der_encode() function (der_encoder.h) to produce encoding
+ * which is compliant with ber_decode().
+ */
+asn_dec_rval_t ber_decode(
+    const struct asn_codec_ctx_s *opt_codec_ctx,
+    const 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)(
+    const struct asn_codec_ctx_s *opt_codec_ctx,
+    const 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(
+    const struct asn_codec_ctx_s *opt_codec_ctx, /* codec options */
+    const 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_ */

+ 168 - 0
lib/asn1/ber_tlv_length.c

@@ -0,0 +1,168 @@
+/*-
+ * Copyright (c) 2003, 2004 Lev Walkin <vlm@lionet.info>. All rights reserved.
+ * Redistribution and modifications are permitted subject to BSD license.
+ */
+#include <asn_internal.h>
+#include <ber_tlv_length.h>
+#include <ber_tlv_tag.h>
+
+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--) {
+
+			/* Verify that we won't overflow. */
+			if(!(len >> ((8 * sizeof(len)) - (8+1)))) {
+				len = (len << 8) | *buf;
+			} else {
+				/* Too large length value. */
+				return -1;
+			}
+		}
+
+		if(oct == 0) {
+			if(len < 0 || len > RSSIZE_MAX) {
+				/* Length value out of sane range. */
+				return -1;
+			}
+
+			*len_r = len;
+			return skipped;
+		}
+
+		return 0;	/* Want more */
+	}
+
+}
+
+ssize_t
+ber_skip_length(const 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;
+	int 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 * (int)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;
+}
+

+ 50 - 0
lib/asn1/ber_tlv_length.h

@@ -0,0 +1,50 @@
+/*-
+ * Copyright (c) 2003-2017 Lev Walkin <vlm@lionet.info>. All rights reserved.
+ * Redistribution and modifications are permitted subject to BSD license.
+ */
+#ifndef	_BER_TLV_LENGTH_H_
+#define	_BER_TLV_LENGTH_H_
+
+#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(
+	const 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_ */

+ 144 - 0
lib/asn1/ber_tlv_tag.c

@@ -0,0 +1,144 @@
+/*-
+ * Copyright (c) 2003, 2004 Lev Walkin <vlm@lionet.info>. All rights reserved.
+ * Redistribution and modifications are permitted subject to BSD license.
+ */
+#include <asn_internal.h>
+#include <ber_tlv_tag.h>
+#include <errno.h>
+
+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) {
+	const 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;
+}
+

+ 60 - 0
lib/asn1/ber_tlv_tag.h

@@ -0,0 +1,60 @@
+/*-
+ * Copyright (c) 2003-2017 Lev Walkin <vlm@lionet.info>. All rights reserved.
+ * Redistribution and modifications are permitted subject to BSD license.
+ */
+#ifndef	_BER_TLV_TAG_H_
+#define	_BER_TLV_TAG_H_
+
+#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_ */

+ 1340 - 0
lib/asn1/constr_CHOICE.c

@@ -0,0 +1,1340 @@
+/*
+ * Copyright (c) 2003-2017 Lev Walkin <vlm@lionet.info>. All rights reserved.
+ * Redistribution and modifications are permitted subject to BSD license.
+ */
+#include <asn_internal.h>
+#include <constr_CHOICE.h>
+#include <per_opentype.h>
+
+/*
+ * Number of bytes left for this structure.
+ * (ctx->left) indicates the number of bytes _transferred_ for the structure.
+ * (size) contains the number of bytes in the buffer passed.
+ */
+#define	LEFT	((size<(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 <TLV>'s L, even if the buffer size allows
+ * reading more data.
+ * For example, consider the buffer containing the following TLVs:
+ * <T:5><L:1><V> <T:6>...
+ * The TLV length clearly indicates that one byte is expected in V, but
+ * if the V processor returns with "want more data" even if the buffer
+ * contains way more data than the V processor have seen.
+ */
+#define	SIZE_VIOLATION	(ctx->left >= 0 && (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
+#define	NEXT_PHASE(ctx)	do {			\
+		ctx->phase++;			\
+		ctx->step = 0;			\
+	} while(0)
+
+/*
+ * Return a standardized complex structure.
+ */
+#undef	RETURN
+#define	RETURN(_code)	do {			\
+		rval.code = _code;		\
+		rval.consumed = consumed_myself;\
+		return rval;			\
+	} while(0)
+
+/*
+ * See the definitions.
+ */
+static unsigned _fetch_present_idx(const void *struct_ptr, unsigned off,
+                                   unsigned size);
+static void _set_present_idx(void *sptr, unsigned offset, unsigned size,
+                             unsigned pres);
+static const void *_get_member_ptr(const asn_TYPE_descriptor_t *,
+                                   const void *sptr, asn_TYPE_member_t **elm,
+                                   unsigned *present);
+
+/*
+ * Tags are canonically sorted in the tag to member table.
+ */
+static int
+_search4tag(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)
+			return 0;
+		else if(a_value < b_value)
+			return -1;
+		else
+			return 1;
+	} else if(a_class < b_class) {
+		return -1;
+	} else {
+		return 1;
+	}
+}
+
+/*
+ * The decoder of the CHOICE type.
+ */
+asn_dec_rval_t
+CHOICE_decode_ber(const asn_codec_ctx_t *opt_codec_ctx,
+                  const asn_TYPE_descriptor_t *td, void **struct_ptr,
+                  const void *ptr, size_t size, int tag_mode) {
+    /*
+	 * Bring closer parts of structure description.
+	 */
+	const asn_CHOICE_specifics_t *specs =
+		(const asn_CHOICE_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 */
+	ssize_t tag_len;	/* Length of TLV's T */
+	asn_dec_rval_t rval;	/* Return code from subparsers */
+
+	ssize_t consumed_myself = 0;	/* Consumed bytes from ptr */
+
+	ASN_DEBUG("Decoding %s as CHOICE", 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.
+		 */
+
+		if(tag_mode || td->tags_count) {
+			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) {
+				/* ?Substracted below! */
+				ctx->left += rval.consumed;
+			}
+			ADVANCE(rval.consumed);
+		} else {
+			ctx->left = -1;
+		}
+
+		NEXT_PHASE(ctx);
+
+		ASN_DEBUG("Structure consumes %ld bytes, buffer %ld",
+			(long)ctx->left, (long)size);
+
+		/* Fall through */
+	case 1:
+		/*
+		 * Fetch the T from TLV.
+		 */
+		tag_len = ber_fetch_tag(ptr, LEFT, &tlv_tag);
+		ASN_DEBUG("In %s CHOICE tag length %d", td->name, (int)tag_len);
+		switch(tag_len) {
+		case 0: if(!SIZE_VIOLATION) RETURN(RC_WMORE);
+			/* Fall through */
+		case -1: RETURN(RC_FAIL);
+		}
+
+		do {
+			const asn_TYPE_tag2member_t *t2m;
+			asn_TYPE_tag2member_t key;
+
+			key.el_tag = tlv_tag;
+			t2m = (const asn_TYPE_tag2member_t *)bsearch(&key,
+					specs->tag2el, specs->tag2el_count,
+					sizeof(specs->tag2el[0]), _search4tag);
+			if(t2m) {
+				/*
+				 * Found the element corresponding to the tag.
+				 */
+				NEXT_PHASE(ctx);
+				ctx->step = t2m->el_no;
+				break;
+			} else if(specs->ext_start == -1) {
+				ASN_DEBUG("Unexpected tag %s "
+					"in non-extensible CHOICE %s",
+					ber_tlv_tag_string(tlv_tag), td->name);
+				RETURN(RC_FAIL);
+			} else {
+				/* Skip this tag */
+				ssize_t skip;
+
+				ASN_DEBUG("Skipping unknown tag %s",
+					ber_tlv_tag_string(tlv_tag));
+
+				skip = ber_skip_length(opt_codec_ctx,
+					BER_TLV_CONSTRUCTED(ptr),
+					(const char *)ptr + tag_len,
+					LEFT - tag_len);
+
+				switch(skip) {
+				case 0: if(!SIZE_VIOLATION) RETURN(RC_WMORE);
+					/* Fall through */
+				case -1: RETURN(RC_FAIL);
+				}
+
+				ADVANCE(skip + tag_len);
+				RETURN(RC_OK);
+			}
+		} while(0);
+
+	case 2:
+		/*
+		 * PHASE 2.
+		 * Read in the element.
+		 */
+	    do {
+		asn_TYPE_member_t *elm;/* CHOICE's element */
+		void *memb_ptr;		/* Pointer to the member */
+		void **memb_ptr2;	/* Pointer to that pointer */
+
+		elm = &elements[ctx->step];
+
+		/*
+		 * Compute the position of the member inside a structure,
+		 * and also a type of containment (it may be contained
+		 * as pointer or using inline inclusion).
+		 */
+		if(elm->flags & ATF_POINTER) {
+			/* Member is a pointer to another structure */
+			memb_ptr2 = (void **)((char *)st + elm->memb_offset);
+		} else {
+			/*
+			 * A pointer to a pointer
+			 * holding the start of the structure
+			 */
+			memb_ptr = (char *)st + elm->memb_offset;
+			memb_ptr2 = &memb_ptr;
+		}
+		/* Set presence to be able to free it properly at any time */
+		_set_present_idx(st, specs->pres_offset,
+				specs->pres_size, ctx->step + 1);
+		/*
+		 * Invoke the member fetch routine according to member's type
+		 */
+		rval = elm->type->op->ber_decoder(opt_codec_ctx, elm->type,
+				memb_ptr2, ptr, LEFT, elm->tag_mode);
+		switch(rval.code) {
+		case RC_OK:
+			break;
+		case RC_WMORE: /* More data expected */
+			if(!SIZE_VIOLATION) {
+				ADVANCE(rval.consumed);
+				RETURN(RC_WMORE);
+			}
+			RETURN(RC_FAIL);
+		case RC_FAIL: /* Fatal error */
+			RETURN(rval.code);
+		} /* switch(rval) */
+		
+		ADVANCE(rval.consumed);
+	  } while(0);
+
+		NEXT_PHASE(ctx);
+
+		/* Fall through */
+	case 3:
+		ASN_DEBUG("CHOICE %s Leftover: %ld, size = %ld, tm=%d, tc=%d",
+			td->name, (long)ctx->left, (long)size,
+			tag_mode, td->tags_count);
+
+		if(ctx->left > 0) {
+			/*
+			 * The type must be fully decoded
+			 * by the CHOICE member-specific decoder.
+			 */
+			RETURN(RC_FAIL);
+		}
+
+		if(ctx->left == -1
+		&& !(tag_mode || td->tags_count)) {
+			/*
+			 * This is an untagged CHOICE.
+			 * It doesn't contain nothing
+			 * except for the member itself, including all its tags.
+			 * The decoding is completed.
+			 */
+			NEXT_PHASE(ctx);
+			break;
+		}
+
+		/*
+		 * Read in the "end of data chunks"'s.
+		 */
+		while(ctx->left < 0) {
+			ssize_t tl;
+
+			tl = ber_fetch_tag(ptr, LEFT, &tlv_tag);
+			switch(tl) {
+			case 0: if(!SIZE_VIOLATION) RETURN(RC_WMORE);
+				/* Fall through */
+			case -1: RETURN(RC_FAIL);
+			}
+
+			/*
+			 * Expected <0><0>...
+			 */
+			if(((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++;
+					continue;
+				}
+			} else {
+				ASN_DEBUG("Unexpected continuation in %s",
+					td->name);
+				RETURN(RC_FAIL);
+			}
+
+			/* UNREACHABLE */
+		}
+
+		NEXT_PHASE(ctx);
+	case 4:
+		/* No meaningful work here */
+		break;
+	}
+	
+	RETURN(RC_OK);
+}
+
+asn_enc_rval_t
+CHOICE_encode_der(const asn_TYPE_descriptor_t *td, const void *sptr,
+                  int tag_mode, ber_tlv_tag_t tag, asn_app_consume_bytes_f *cb,
+                  void *app_key) {
+    const asn_CHOICE_specifics_t *specs = (const asn_CHOICE_specifics_t *)td->specifics;
+	asn_TYPE_member_t *elm;	/* CHOICE element */
+	asn_enc_rval_t erval;
+	const void *memb_ptr;
+	size_t computed_size = 0;
+	unsigned present;
+
+	if(!sptr) ASN__ENCODE_FAILED;
+
+	ASN_DEBUG("%s %s as CHOICE",
+		cb?"Encoding":"Estimating", td->name);
+
+	present = _fetch_present_idx(sptr,
+		specs->pres_offset, specs->pres_size);
+
+	/*
+	 * If the structure was not initialized, it cannot be encoded:
+	 * can't deduce what to encode in the choice type.
+	 */
+	if(present == 0 || present > td->elements_count) {
+		if(present == 0 && td->elements_count == 0) {
+			/* The CHOICE is empty?! */
+			erval.encoded = 0;
+			ASN__ENCODED_OK(erval);
+		}
+		ASN__ENCODE_FAILED;
+	}
+
+	/*
+	 * Seek over the present member of the structure.
+	 */
+	elm = &td->elements[present-1];
+	if(elm->flags & ATF_POINTER) {
+        memb_ptr =
+            *(const void *const *)((const char *)sptr + elm->memb_offset);
+        if(memb_ptr == 0) {
+			if(elm->optional) {
+				erval.encoded = 0;
+				ASN__ENCODED_OK(erval);
+			}
+			/* Mandatory element absent */
+			ASN__ENCODE_FAILED;
+		}
+	} else {
+        memb_ptr = (const void *)((const char *)sptr + elm->memb_offset);
+    }
+
+	/*
+	 * If the CHOICE itself is tagged EXPLICIT:
+	 * T ::= [2] EXPLICIT CHOICE { ... }
+	 * Then emit the appropriate tags.
+	 */
+	if(tag_mode == 1 || td->tags_count) {
+		/*
+		 * For this, we need to pre-compute the member.
+		 */
+		ssize_t ret;
+
+		/* Encode member with its tag */
+		erval = elm->type->op->der_encoder(elm->type, memb_ptr,
+			elm->tag_mode, elm->tag, 0, 0);
+		if(erval.encoded == -1)
+			return erval;
+
+		/* Encode CHOICE with parent or my own tag */
+		ret = der_write_tags(td, erval.encoded, tag_mode, 1, tag,
+			cb, app_key);
+		if(ret == -1)
+			ASN__ENCODE_FAILED;
+		computed_size += ret;
+	}
+
+	/*
+	 * Encode the single underlying member.
+	 */
+	erval = elm->type->op->der_encoder(elm->type, memb_ptr,
+		elm->tag_mode, elm->tag, cb, app_key);
+	if(erval.encoded == -1)
+		return erval;
+
+	ASN_DEBUG("Encoded CHOICE member in %ld bytes (+%ld)",
+		(long)erval.encoded, (long)computed_size);
+
+	erval.encoded += computed_size;
+
+	return erval;
+}
+
+ber_tlv_tag_t
+CHOICE_outmost_tag(const asn_TYPE_descriptor_t *td, const void *ptr, int tag_mode, ber_tlv_tag_t tag) {
+	const asn_CHOICE_specifics_t *specs = (const asn_CHOICE_specifics_t *)td->specifics;
+	unsigned present;
+
+	assert(tag_mode == 0); (void)tag_mode;
+	assert(tag == 0); (void)tag;
+
+	/*
+	 * Figure out which CHOICE element is encoded.
+	 */
+	present = _fetch_present_idx(ptr, specs->pres_offset, specs->pres_size);
+
+	if(present > 0 && present <= td->elements_count) {
+		const asn_TYPE_member_t *elm = &td->elements[present-1];
+		const void *memb_ptr;
+
+		if(elm->flags & ATF_POINTER) {
+			memb_ptr = *(const void * const *)
+					((const char *)ptr + elm->memb_offset);
+		} else {
+			memb_ptr = (const void *)
+					((const char *)ptr + elm->memb_offset);
+		}
+
+		return asn_TYPE_outmost_tag(elm->type, memb_ptr,
+			elm->tag_mode, elm->tag);
+	} else {
+		return (ber_tlv_tag_t)-1;
+	}
+}
+
+int
+CHOICE_constraint(const asn_TYPE_descriptor_t *td, const void *sptr,
+                  asn_app_constraint_failed_f *ctfailcb, void *app_key) {
+    const asn_CHOICE_specifics_t *specs =
+        (const asn_CHOICE_specifics_t *)td->specifics;
+    unsigned present;
+
+	if(!sptr) {
+		ASN__CTFAIL(app_key, td, sptr,
+			"%s: value not given (%s:%d)",
+			td->name, __FILE__, __LINE__);
+		return -1;
+	}
+
+	/*
+	 * Figure out which CHOICE element is encoded.
+	 */
+	present = _fetch_present_idx(sptr, specs->pres_offset,specs->pres_size);
+	if(present > 0 && present <= td->elements_count) {
+		asn_TYPE_member_t *elm = &td->elements[present-1];
+		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)
+					return 0;
+				ASN__CTFAIL(app_key, td, sptr,
+					"%s: mandatory CHOICE 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->encoding_constraints.general_constraints) {
+			return elm->encoding_constraints.general_constraints(elm->type, memb_ptr,
+				ctfailcb, app_key);
+		} else {
+			return elm->type->encoding_constraints.general_constraints(elm->type,
+					memb_ptr, ctfailcb, app_key);
+		}
+	} else {
+		ASN__CTFAIL(app_key, td, sptr,
+			"%s: no CHOICE element given (%s:%d)",
+			td->name, __FILE__, __LINE__);
+		return -1;
+	}
+}
+
+#undef	XER_ADVANCE
+#define	XER_ADVANCE(num_bytes)	do {			\
+		size_t num = num_bytes;			\
+		buf_ptr = (const void *)(((const char *)buf_ptr) + num); \
+		size -= num;				\
+		consumed_myself += num;			\
+	} while(0)
+
+/*
+ * Decode the XER (XML) data.
+ */
+asn_dec_rval_t
+CHOICE_decode_xer(const asn_codec_ctx_t *opt_codec_ctx,
+                  const 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.
+	 */
+	const asn_CHOICE_specifics_t *specs = (const asn_CHOICE_specifics_t *)td->specifics;
+	const char *xml_tag = opt_mname ? opt_mname : td->xml_tag;
+
+	/*
+	 * 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 of a decoder */
+	ssize_t consumed_myself = 0;	/* Consumed bytes from ptr */
+	size_t edx;			/* Element index */
+
+	/*
+	 * 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);
+	if(ctx->phase == 0 && !*xml_tag)
+		ctx->phase = 1;	/* Skip the outer tag checking phase */
+
+	/*
+	 * 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: Only waiting for closing tag.
+	 * Phase 4: Skipping unknown extensions.
+	 * Phase 5: PHASED OUT
+	 */
+	for(edx = ctx->step; ctx->phase <= 4;) {
+		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;
+
+		/*
+		 * Go inside the member.
+		 */
+		if(ctx->phase == 2) {
+			asn_dec_rval_t tmprval;
+			void *memb_ptr;		/* Pointer to the member */
+			void **memb_ptr2;	/* Pointer to that pointer */
+			unsigned old_present;
+
+			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;
+			}
+
+			/* Start/Continue decoding the inner member */
+			tmprval = elm->type->op->xer_decoder(opt_codec_ctx,
+					elm->type, memb_ptr2, elm->name,
+					buf_ptr, size);
+			XER_ADVANCE(tmprval.consumed);
+			ASN_DEBUG("XER/CHOICE: itdf: [%s] code=%d",
+				elm->type->name, tmprval.code);
+			old_present = _fetch_present_idx(st,
+				specs->pres_offset, specs->pres_size);
+			assert(old_present == 0 || old_present == edx + 1);
+			/* Record what we've got */
+			_set_present_idx(st,
+				specs->pres_offset, specs->pres_size, edx + 1);
+			if(tmprval.code != RC_OK)
+				RETURN(tmprval.code);
+			ctx->phase = 3;
+			/* Fall through */
+		}
+
+		/* No need to wait for closing tag; special mode. */
+		if(ctx->phase == 3 && !*xml_tag) {
+			ctx->phase = 5;	/* Phase out */
+			RETURN(RC_OK);
+		}
+
+		/*
+		 * 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/CHOICE checked [%c%c%c%c] vs [%s], tcv=%d",
+			ch_size>0?((const uint8_t *)buf_ptr)[0]:'?',
+			ch_size>1?((const uint8_t *)buf_ptr)[1]:'?',
+			ch_size>2?((const uint8_t *)buf_ptr)[2]:'?',
+			ch_size>3?((const uint8_t *)buf_ptr)[3]:'?',
+		xml_tag, tcv);
+
+		/* Skip the extensions section */
+		if(ctx->phase == 4) {
+			ASN_DEBUG("skip_unknown(%d, %ld)",
+				tcv, (long)ctx->left);
+			switch(xer_skip_unknown(tcv, &ctx->left)) {
+			case -1:
+				ctx->phase = 5;
+				RETURN(RC_FAIL);
+			case 1:
+				ctx->phase = 3;
+				/* Fall through */
+			case 0:
+				XER_ADVANCE(ch_size);
+				continue;
+			case 2:
+				ctx->phase = 3;
+				break;
+			}
+		}
+
+		switch(tcv) {
+		case XCT_BOTH:
+			break;	/* No CHOICE? */
+		case XCT_CLOSING:
+			if(ctx->phase != 3)
+				break;
+			XER_ADVANCE(ch_size);
+			ctx->phase = 5;	/* Phase out */
+			RETURN(RC_OK);
+		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:
+
+			if(ctx->phase != 1)
+				break;	/* Really unexpected */
+
+			/*
+			 * Search which inner member corresponds to this tag.
+			 */
+			for(edx = 0; edx < td->elements_count; edx++) {
+				elm = &td->elements[edx];
+				tcv = xer_check_tag(buf_ptr,ch_size,elm->name);
+				switch(tcv) {
+				case XCT_BOTH:
+				case XCT_OPENING:
+					/*
+					 * Process this member.
+					 */
+					ctx->step = edx;
+					ctx->phase = 2;
+					break;
+				case XCT_UNKNOWN_OP:
+				case XCT_UNKNOWN_BO:
+					continue;
+				default:
+					edx = td->elements_count;
+					break;	/* Phase out */
+				}
+				break;
+			}
+			if(edx != td->elements_count)
+				continue;
+
+			/* It is expected extension */
+			if(specs->ext_start != -1) {
+				ASN_DEBUG("Got anticipated extension");
+				/*
+				 * Check for (XCT_BOTH or XCT_UNKNOWN_BO)
+				 * By using a mask. Only record a pure
+				 * <opening> tags.
+				 */
+				if(tcv & XCT_CLOSING) {
+					/* Found </extension> without body */
+					ctx->phase = 3; /* Terminating */
+				} else {
+					ctx->left = 1;
+					ctx->phase = 4; /* Skip ...'s */
+				}
+				XER_ADVANCE(ch_size);
+				continue;
+			}
+
+			/* Fall through */
+		default:
+			break;
+		}
+
+		ASN_DEBUG("Unexpected XML tag [%c%c%c%c] in CHOICE [%s]"
+			" (ph=%d, tag=%s)",
+			ch_size>0?((const uint8_t *)buf_ptr)[0]:'?',
+			ch_size>1?((const uint8_t *)buf_ptr)[1]:'?',
+			ch_size>2?((const uint8_t *)buf_ptr)[2]:'?',
+			ch_size>3?((const uint8_t *)buf_ptr)[3]:'?',
+			td->name, ctx->phase, xml_tag);
+		break;
+	}
+
+	ctx->phase = 5;	/* Phase out, just in case */
+	RETURN(RC_FAIL);
+}
+
+
+asn_enc_rval_t
+CHOICE_encode_xer(const asn_TYPE_descriptor_t *td, const void *sptr, int ilevel,
+                  enum xer_encoder_flags_e flags, asn_app_consume_bytes_f *cb,
+                  void *app_key) {
+    const asn_CHOICE_specifics_t *specs =
+        (const asn_CHOICE_specifics_t *)td->specifics;
+    asn_enc_rval_t er;
+	unsigned present;
+
+	if(!sptr)
+		ASN__ENCODE_FAILED;
+
+	/*
+	 * Figure out which CHOICE element is encoded.
+	 */
+	present = _fetch_present_idx(sptr, specs->pres_offset,specs->pres_size);
+
+	if(present == 0 || present > td->elements_count) {
+		ASN__ENCODE_FAILED;
+	}  else {
+		asn_enc_rval_t tmper;
+		asn_TYPE_member_t *elm = &td->elements[present-1];
+		const void *memb_ptr;
+		const char *mname = elm->name;
+		unsigned int mlen = strlen(mname);
+
+		if(elm->flags & ATF_POINTER) {
+            memb_ptr =
+                *(const void *const *)((const char *)sptr + elm->memb_offset);
+            if(!memb_ptr) ASN__ENCODE_FAILED;
+		} else {
+            memb_ptr = (const void *)((const char *)sptr + elm->memb_offset);
+        }
+
+        er.encoded = 0;
+
+		if(!(flags & XER_F_CANONICAL)) ASN__TEXT_INDENT(1, ilevel);
+		ASN__CALLBACK3("<", 1, mname, mlen, ">", 1);
+
+		tmper = elm->type->op->xer_encoder(elm->type, memb_ptr,
+				ilevel + 1, flags, cb, app_key);
+		if(tmper.encoded == -1) return tmper;
+		er.encoded += tmper.encoded;
+
+		ASN__CALLBACK3("</", 2, mname, mlen, ">", 1);
+	}
+
+	if(!(flags & XER_F_CANONICAL)) ASN__TEXT_INDENT(1, ilevel - 1);
+
+	ASN__ENCODED_OK(er);
+cb_failed:
+	ASN__ENCODE_FAILED;
+}
+
+asn_dec_rval_t
+CHOICE_decode_uper(const asn_codec_ctx_t *opt_codec_ctx,
+                   const asn_TYPE_descriptor_t *td,
+                   const asn_per_constraints_t *constraints, void **sptr,
+                   asn_per_data_t *pd) {
+    const asn_CHOICE_specifics_t *specs =
+        (const asn_CHOICE_specifics_t *)td->specifics;
+    asn_dec_rval_t rv;
+	const asn_per_constraint_t *ct;
+	asn_TYPE_member_t *elm;	/* CHOICE's element */
+	void *memb_ptr;
+	void **memb_ptr2;
+	void *st = *sptr;
+	int value;
+
+	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;
+	}
+
+	if(constraints) ct = &constraints->value;
+	else if(td->encoding_constraints.per_constraints) ct = &td->encoding_constraints.per_constraints->value;
+	else ct = 0;
+
+	if(ct && ct->flags & APC_EXTENSIBLE) {
+		value = per_get_few_bits(pd, 1);
+		if(value < 0) ASN__DECODE_STARVED;
+		if(value) ct = 0;	/* Not restricted */
+	}
+
+	if(ct && ct->range_bits >= 0) {
+		value = per_get_few_bits(pd, ct->range_bits);
+		if(value < 0) ASN__DECODE_STARVED;
+		ASN_DEBUG("CHOICE %s got index %d in range %d",
+			td->name, value, ct->range_bits);
+		if(value > ct->upper_bound)
+			ASN__DECODE_FAILED;
+	} else {
+		if(specs->ext_start == -1)
+			ASN__DECODE_FAILED;
+		value = uper_get_nsnnwn(pd);
+		if(value < 0) ASN__DECODE_STARVED;
+		value += specs->ext_start;
+		if((unsigned)value >= td->elements_count)
+			ASN__DECODE_FAILED;
+	}
+
+	/* Adjust if canonical order is different from natural order */
+	if(specs->from_canonical_order) {
+        ASN_DEBUG("CHOICE presence from wire %d", value);
+		value = specs->from_canonical_order[value];
+        ASN_DEBUG("CHOICE presence index effective %d", value);
+    }
+
+	/* Set presence to be able to free it later */
+	_set_present_idx(st, specs->pres_offset, specs->pres_size, value + 1);
+
+	elm = &td->elements[value];
+	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;
+	}
+	ASN_DEBUG("Discovered CHOICE %s encodes %s", td->name, elm->name);
+
+	if(ct && ct->range_bits >= 0) {
+		rv = elm->type->op->uper_decoder(opt_codec_ctx, elm->type,
+			elm->encoding_constraints.per_constraints, memb_ptr2, pd);
+	} else {
+		rv = uper_open_type_get(opt_codec_ctx, elm->type,
+			elm->encoding_constraints.per_constraints, memb_ptr2, pd);
+	}
+
+	if(rv.code != RC_OK)
+		ASN_DEBUG("Failed to decode %s in %s (CHOICE) %d",
+			elm->name, td->name, rv.code);
+	return rv;
+}
+
+asn_enc_rval_t
+CHOICE_encode_uper(const asn_TYPE_descriptor_t *td,
+                   const asn_per_constraints_t *constraints, const void *sptr,
+                   asn_per_outp_t *po) {
+    const asn_CHOICE_specifics_t *specs = (const asn_CHOICE_specifics_t *)td->specifics;
+	asn_TYPE_member_t *elm;	/* CHOICE's element */
+	const asn_per_constraint_t *ct;
+	const void *memb_ptr;
+	unsigned present;
+	int present_enc;
+
+	if(!sptr) ASN__ENCODE_FAILED;
+
+	ASN_DEBUG("Encoding %s as CHOICE", td->name);
+
+	if(constraints) ct = &constraints->value;
+	else if(td->encoding_constraints.per_constraints)
+		ct = &td->encoding_constraints.per_constraints->value;
+	else ct = 0;
+
+	present = _fetch_present_idx(sptr, specs->pres_offset, specs->pres_size);
+
+	/*
+	 * If the structure was not initialized properly, it cannot be encoded:
+	 * can't deduce what to encode in the choice type.
+	 */
+	if(present == 0 || present > td->elements_count)
+		ASN__ENCODE_FAILED;
+	else
+		present--;
+
+	ASN_DEBUG("Encoding %s CHOICE element %d", td->name, present);
+
+    /* Adjust if canonical order is different from natural order */
+    if(specs->to_canonical_order)
+        present_enc = specs->to_canonical_order[present];
+    else
+        present_enc = present;
+
+    if(ct && ct->range_bits >= 0) {
+		if(present_enc < ct->lower_bound
+		|| present_enc > ct->upper_bound) {
+			if(ct->flags & APC_EXTENSIBLE) {
+                ASN_DEBUG(
+                    "CHOICE member %d (enc %d) is an extension (%ld..%ld)",
+                    present, present_enc, ct->lower_bound, ct->upper_bound);
+                if(per_put_few_bits(po, 1, 1))
+					ASN__ENCODE_FAILED;
+			} else {
+				ASN__ENCODE_FAILED;
+			}
+			ct = 0;
+		}
+	}
+	if(ct && ct->flags & APC_EXTENSIBLE) {
+        ASN_DEBUG("CHOICE member %d (enc %d) is not an extension (%ld..%ld)",
+                  present, present_enc, ct->lower_bound, ct->upper_bound);
+        if(per_put_few_bits(po, 0, 1))
+			ASN__ENCODE_FAILED;
+    }
+
+
+	elm = &td->elements[present];
+    ASN_DEBUG("CHOICE member \"%s\" %d (as %d)", elm->name, present,
+              present_enc);
+    if(elm->flags & ATF_POINTER) {
+		/* Member is a pointer to another structure */
+        memb_ptr =
+            *(const void *const *)((const char *)sptr + elm->memb_offset);
+        if(!memb_ptr) ASN__ENCODE_FAILED;
+	} else {
+        memb_ptr = (const char *)sptr + elm->memb_offset;
+    }
+
+    if(ct && ct->range_bits >= 0) {
+        if(per_put_few_bits(po, present_enc, ct->range_bits))
+            ASN__ENCODE_FAILED;
+
+        return elm->type->op->uper_encoder(
+            elm->type, elm->encoding_constraints.per_constraints, memb_ptr, po);
+    } else {
+        asn_enc_rval_t rval;
+        if(specs->ext_start == -1) ASN__ENCODE_FAILED;
+        if(uper_put_nsnnwn(po, present_enc - specs->ext_start))
+            ASN__ENCODE_FAILED;
+        if(uper_open_type_put(elm->type,
+                              elm->encoding_constraints.per_constraints,
+                              memb_ptr, po))
+            ASN__ENCODE_FAILED;
+        rval.encoded = 0;
+        ASN__ENCODED_OK(rval);
+    }
+}
+
+
+int
+CHOICE_print(const asn_TYPE_descriptor_t *td, const void *sptr, int ilevel,
+             asn_app_consume_bytes_f *cb, void *app_key) {
+    const asn_CHOICE_specifics_t *specs = (const asn_CHOICE_specifics_t *)td->specifics;
+	unsigned present;
+
+	if(!sptr) return (cb("<absent>", 8, app_key) < 0) ? -1 : 0;
+
+	/*
+	 * Figure out which CHOICE element is encoded.
+	 */
+	present = _fetch_present_idx(sptr, specs->pres_offset,specs->pres_size);
+
+	/*
+	 * Print that element.
+	 */
+	if(present > 0 && present <= td->elements_count) {
+		asn_TYPE_member_t *elm = &td->elements[present-1];
+		const void *memb_ptr;
+
+		if(elm->flags & ATF_POINTER) {
+			memb_ptr = *(const void * const *)((const char *)sptr + elm->memb_offset);
+			if(!memb_ptr) return (cb("<absent>", 8, app_key) < 0) ? -1 : 0;
+		} else {
+			memb_ptr = (const void *)((const char *)sptr + elm->memb_offset);
+		}
+
+		/* Print member's name and stuff */
+		if(0) {
+			if(cb(elm->name, strlen(elm->name), app_key) < 0
+			|| cb(": ", 2, app_key) < 0)
+				return -1;
+		}
+
+		return elm->type->op->print_struct(elm->type, memb_ptr, ilevel,
+			cb, app_key);
+	} else {
+		return (cb("<absent>", 8, app_key) < 0) ? -1 : 0;
+	}
+}
+
+void
+CHOICE_free(const asn_TYPE_descriptor_t *td, void *ptr,
+            enum asn_struct_free_method method) {
+    const asn_CHOICE_specifics_t *specs =
+        (const asn_CHOICE_specifics_t *)td->specifics;
+    unsigned present;
+
+	if(!td || !ptr)
+		return;
+
+	ASN_DEBUG("Freeing %s as CHOICE", td->name);
+
+	/*
+	 * Figure out which CHOICE element is encoded.
+	 */
+	present = _fetch_present_idx(ptr, specs->pres_offset, specs->pres_size);
+
+	/*
+	 * Free that element.
+	 */
+	if(present > 0 && present <= td->elements_count) {
+		asn_TYPE_member_t *elm = &td->elements[present-1];
+		void *memb_ptr;
+
+		if(elm->flags & ATF_POINTER) {
+			memb_ptr = *(void **)((char *)ptr + elm->memb_offset);
+			if(memb_ptr)
+				ASN_STRUCT_FREE(*elm->type, memb_ptr);
+		} else {
+			memb_ptr = (void *)((char *)ptr + elm->memb_offset);
+			ASN_STRUCT_FREE_CONTENTS_ONLY(*elm->type, memb_ptr);
+		}
+	}
+
+    switch(method) {
+    case ASFM_FREE_EVERYTHING:
+        FREEMEM(ptr);
+        break;
+    case ASFM_FREE_UNDERLYING:
+        break;
+    case ASFM_FREE_UNDERLYING_AND_RESET:
+        memset(ptr, 0, specs->struct_size);
+        break;
+    }
+}
+
+
+/*
+ * The following functions functions offer protection against -fshort-enums,
+ * compatible with little- and big-endian machines.
+ * If assertion is triggered, either disable -fshort-enums, or add an entry
+ * here with the ->pres_size of your target stracture.
+ * Unless the target structure is packed, the ".present" member
+ * is guaranteed to be aligned properly. ASN.1 compiler itself does not
+ * produce packed code.
+ */
+static unsigned
+_fetch_present_idx(const void *struct_ptr, unsigned pres_offset,
+                   unsigned pres_size) {
+    const void *present_ptr;
+	unsigned present;
+
+	present_ptr = ((const char *)struct_ptr) + pres_offset;
+
+	switch(pres_size) {
+	case sizeof(int):	present = *(const unsigned int *)present_ptr; break;
+	case sizeof(short):	present = *(const unsigned short *)present_ptr; break;
+	case sizeof(char):	present = *(const unsigned char *)present_ptr; break;
+	default:
+		/* ANSI C mandates enum to be equivalent to integer */
+		assert(pres_size != sizeof(int));
+		return 0;	/* If not aborted, pass back safe value */
+	}
+
+	return present;
+}
+
+static void
+_set_present_idx(void *struct_ptr, unsigned pres_offset, unsigned pres_size,
+                 unsigned present) {
+    void *present_ptr;
+	present_ptr = ((char *)struct_ptr) + pres_offset;
+
+	switch(pres_size) {
+	case sizeof(int):	*(unsigned int *)present_ptr   = present; break;
+	case sizeof(short):	*(unsigned short *)present_ptr = present; break;
+	case sizeof(char):	*(unsigned char *)present_ptr  = present; break;
+	default:
+		/* ANSI C mandates enum to be equivalent to integer */
+		assert(pres_size != sizeof(int));
+	}
+}
+
+static const void *
+_get_member_ptr(const asn_TYPE_descriptor_t *td, const void *sptr,
+                asn_TYPE_member_t **elm_ptr, unsigned *present_out) {
+    const asn_CHOICE_specifics_t *specs =
+        (const asn_CHOICE_specifics_t *)td->specifics;
+    unsigned present;
+
+    if(!sptr) {
+        *elm_ptr = NULL;
+        *present_out = 0;
+        return NULL;
+    }
+
+    /*
+	 * Figure out which CHOICE element is encoded.
+	 */
+	present = _fetch_present_idx(sptr, specs->pres_offset, specs->pres_size);
+    *present_out = present;
+
+    /*
+     * The presence index is intentionally 1-based to avoid
+     * treating zeroed structure as a valid one.
+     */
+	if(present > 0 && present <= td->elements_count) {
+        asn_TYPE_member_t *const elm = &td->elements[present - 1];
+        const void *memb_ptr;
+
+		if(elm->flags & ATF_POINTER) {
+            memb_ptr =
+                *(const void *const *)((const char *)sptr + elm->memb_offset);
+        } else {
+            memb_ptr = (const void *)((const char *)sptr + elm->memb_offset);
+        }
+        *elm_ptr = elm;
+        return memb_ptr;
+    } else {
+        *elm_ptr = NULL;
+        return NULL;
+    }
+
+}
+
+int
+CHOICE_compare(const asn_TYPE_descriptor_t *td, const void *aptr, const void *bptr) {
+    asn_TYPE_member_t *aelm;
+    asn_TYPE_member_t *belm;
+    unsigned apresent = 0;
+    unsigned bpresent = 0;
+    const void *amember = _get_member_ptr(td, aptr, &aelm, &apresent);
+    const void *bmember = _get_member_ptr(td, bptr, &belm, &bpresent);
+
+    if(amember && bmember) {
+        if(apresent == bpresent) {
+            assert(aelm == belm);
+            return aelm->type->op->compare_struct(aelm->type, amember, bmember);
+        } else if(apresent < bpresent) {
+            return -1;
+        } else {
+            return 1;
+        }
+    } else if(!amember) {
+        return -1;
+    } else {
+        return 1;
+    }
+}
+
+/*
+ * Return the 1-based choice variant presence index.
+ * Returns 0 in case of error.
+ */
+unsigned
+CHOICE_variant_get_presence(const asn_TYPE_descriptor_t *td, const void *sptr) {
+    const asn_CHOICE_specifics_t *specs =
+        (const asn_CHOICE_specifics_t *)td->specifics;
+    return _fetch_present_idx(sptr, specs->pres_offset, specs->pres_size);
+}
+
+/*
+ * Sets or resets the 1-based choice variant presence index.
+ * In case a previous index is not zero, the currently selected structure
+ * member is freed and zeroed-out first.
+ * Returns 0 on success and -1 on error.
+ */
+int
+CHOICE_variant_set_presence(const asn_TYPE_descriptor_t *td, void *sptr,
+                            unsigned present) {
+    const asn_CHOICE_specifics_t *specs =
+        (const asn_CHOICE_specifics_t *)td->specifics;
+    unsigned old_present;
+
+    if(!sptr) {
+        return -1;
+    }
+
+    if(present > td->elements_count)
+        return -1;
+
+    old_present =
+        _fetch_present_idx(sptr, specs->pres_offset, specs->pres_size);
+    if(present == old_present)
+        return 0;
+
+    if(old_present != 0) {
+        assert(old_present <= td->elements_count);
+        ASN_STRUCT_RESET(*td, sptr);
+    }
+
+    _set_present_idx(sptr, specs->pres_offset, specs->pres_size, present);
+
+    return 0;
+}
+
+
+asn_random_fill_result_t
+CHOICE_random_fill(const asn_TYPE_descriptor_t *td, void **sptr,
+                   const asn_encoding_constraints_t *constr,
+                   size_t max_length) {
+    const asn_CHOICE_specifics_t *specs =
+        (const asn_CHOICE_specifics_t *)td->specifics;
+    asn_random_fill_result_t res;
+    asn_random_fill_result_t result_failed = {ARFILL_FAILED, 0};
+    asn_random_fill_result_t result_skipped = {ARFILL_SKIPPED, 0};
+    const asn_TYPE_member_t *elm;
+    unsigned present;
+    void *memb_ptr;   /* Pointer to the member */
+    void **memb_ptr2; /* Pointer to that pointer */
+    void *st = *sptr;
+
+    if(max_length == 0) return result_skipped;
+
+    (void)constr;
+
+    if(st == NULL) {
+        st = CALLOC(1, specs->struct_size);
+        if(st == NULL) {
+            return result_failed;
+        }
+    }
+
+    present = asn_random_between(1, td->elements_count);
+    elm = &td->elements[present - 1];
+
+	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;
+	}
+
+    res = elm->type->op->random_fill(elm->type, memb_ptr2,
+                                    &elm->encoding_constraints, max_length);
+    _set_present_idx(st, specs->pres_offset, specs->pres_size, present);
+    if(res.code == ARFILL_OK) {
+        *sptr = st;
+    } else {
+        if(st == *sptr) {
+            ASN_STRUCT_RESET(*td, st);
+        } else {
+            ASN_STRUCT_FREE(*td, st);
+        }
+    }
+
+    return res;
+}
+
+
+asn_TYPE_operation_t asn_OP_CHOICE = {
+	CHOICE_free,
+	CHOICE_print,
+	CHOICE_compare,
+	CHOICE_decode_ber,
+	CHOICE_encode_der,
+	CHOICE_decode_xer,
+	CHOICE_encode_xer,
+#ifdef	ASN_DISABLE_OER_SUPPORT
+	0,
+	0,
+#else
+	CHOICE_decode_oer,
+	CHOICE_encode_oer,
+#endif  /* ASN_DISABLE_OER_SUPPORT */
+#ifdef ASN_DISABLE_PER_SUPPORT
+	0,
+	0,
+#else
+	CHOICE_decode_uper,
+	CHOICE_encode_uper,
+#endif	/* ASN_DISABLE_PER_SUPPORT */
+	CHOICE_random_fill,
+	CHOICE_outmost_tag
+};

+ 78 - 0
lib/asn1/constr_CHOICE.h

@@ -0,0 +1,78 @@
+/*
+ * Copyright (c) 2003-2017 Lev Walkin <vlm@lionet.info>. All rights reserved.
+ * Redistribution and modifications are permitted subject to BSD license.
+ */
+#ifndef	_CONSTR_CHOICE_H_
+#define	_CONSTR_CHOICE_H_
+
+#include <asn_application.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct asn_CHOICE_specifics_s {
+	/*
+	 * Target structure description.
+	 */
+	unsigned struct_size;       /* Size of the target structure. */
+	unsigned ctx_offset;        /* Offset of the asn_codec_ctx_t member */
+	unsigned pres_offset;       /* Identifier of the present member */
+	unsigned pres_size;         /* Size of the identifier (enum) */
+
+	/*
+	 * Tags to members mapping table.
+	 */
+	const asn_TYPE_tag2member_t *tag2el;
+	unsigned tag2el_count;
+
+	/* Canonical ordering of CHOICE elements, for PER */
+	const unsigned *to_canonical_order;
+	const unsigned *from_canonical_order;
+
+	/*
+	 * Extensions-related stuff.
+	 */
+	signed ext_start; /* First member of extensions, or -1 */
+} asn_CHOICE_specifics_t;
+
+/*
+ * A set specialized functions dealing with the CHOICE type.
+ */
+asn_struct_free_f CHOICE_free;
+asn_struct_print_f CHOICE_print;
+asn_struct_compare_f CHOICE_compare;
+asn_constr_check_f CHOICE_constraint;
+ber_type_decoder_f CHOICE_decode_ber;
+der_type_encoder_f CHOICE_encode_der;
+xer_type_decoder_f CHOICE_decode_xer;
+xer_type_encoder_f CHOICE_encode_xer;
+oer_type_decoder_f CHOICE_decode_oer;
+oer_type_encoder_f CHOICE_encode_oer;
+per_type_decoder_f CHOICE_decode_uper;
+per_type_encoder_f CHOICE_encode_uper;
+asn_outmost_tag_f CHOICE_outmost_tag;
+asn_random_fill_f CHOICE_random_fill;
+extern asn_TYPE_operation_t asn_OP_CHOICE;
+
+/*
+ * Return the 1-based choice variant presence index.
+ * Returns 0 in case of error.
+ */
+unsigned CHOICE_variant_get_presence(const asn_TYPE_descriptor_t *td,
+                                     const void *structure_ptr);
+
+/*
+ * Sets or resets the 1-based choice variant presence index.
+ * In case a previous index is not zero, the currently selected structure
+ * member is freed and zeroed-out first.
+ * Returns 0 on success and -1 on error.
+ */
+int CHOICE_variant_set_presence(const asn_TYPE_descriptor_t *td,
+                                void *structure_ptr, unsigned present);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif	/* _CONSTR_CHOICE_H_ */

+ 380 - 0
lib/asn1/constr_CHOICE_oer.c

@@ -0,0 +1,380 @@
+/*
+ * Copyright (c) 2017 Lev Walkin <vlm@lionet.info>.
+ * All rights reserved.
+ * Redistribution and modifications are permitted subject to BSD license.
+ */
+#ifndef ASN_DISABLE_OER_SUPPORT
+
+#include <asn_internal.h>
+#include <constr_CHOICE.h>
+#include <errno.h>
+
+/*
+ * Return a standardized complex structure.
+ */
+#undef  RETURN
+#define RETURN(_code)                    \
+    do {                                 \
+        asn_dec_rval_t rval;             \
+        rval.code = _code;               \
+        rval.consumed = consumed_myself; \
+        return rval;                     \
+    } while(0)
+
+#undef  ADVANCE
+#define ADVANCE(num_bytes)               \
+    do {                                 \
+        size_t num = num_bytes;          \
+        ptr = ((const char *)ptr) + num; \
+        size -= num;                     \
+        consumed_myself += num;          \
+    } while(0)
+
+/*
+ * Switch to the next phase of parsing.
+ */
+#undef  NEXT_PHASE
+#define NEXT_PHASE(ctx) \
+    do {                \
+        ctx->phase++;   \
+        ctx->step = 0;  \
+    } while(0)
+#undef  SET_PHASE
+#define SET_PHASE(ctx, value) \
+    do {                      \
+        ctx->phase = value;   \
+        ctx->step = 0;        \
+    } while(0)
+
+/*
+ * Tags are canonically sorted in the tag to member table.
+ */
+static int
+_search4tag(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)
+            return 0;
+        else if(a_value < b_value)
+            return -1;
+        else
+            return 1;
+    } else if(a_class < b_class) {
+        return -1;
+    } else {
+        return 1;
+    }
+}
+
+/*
+ * X.696 (08/2015) #8.7 Encoding of tags
+ */
+static ssize_t
+oer_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 & 0x3F) != 0x3F) {
+        /* #8.7.1 */
+        *tag_r = ((val & 0x3F) << 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 */
+}
+
+asn_dec_rval_t
+CHOICE_decode_oer(const asn_codec_ctx_t *opt_codec_ctx,
+                  const asn_TYPE_descriptor_t *td,
+                  const asn_oer_constraints_t *constraints, void **struct_ptr,
+                  const void *ptr, size_t size) {
+    /*
+     * Bring closer parts of structure description.
+     */
+    const asn_CHOICE_specifics_t *specs =
+        (const asn_CHOICE_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 */
+
+    ssize_t consumed_myself = 0; /* Consumed bytes from ptr */
+
+    (void)constraints;
+
+    ASN_DEBUG("Decoding %s as CHOICE", 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);
+    switch(ctx->phase) {
+    case 0: {
+        /*
+         * Discover the tag.
+         */
+        ber_tlv_tag_t tlv_tag; /* T from TLV */
+        ssize_t tag_len;       /* Length of TLV's T */
+
+        tag_len = oer_fetch_tag(ptr, size, &tlv_tag);
+        switch(tag_len) {
+        case 0:
+            ASN__DECODE_STARVED;
+        case -1:
+            ASN__DECODE_FAILED;
+        }
+
+        do {
+            const asn_TYPE_tag2member_t *t2m;
+            asn_TYPE_tag2member_t key = {0, 0, 0, 0};
+            key.el_tag = tlv_tag;
+
+            t2m = (const asn_TYPE_tag2member_t *)bsearch(
+                &key, specs->tag2el, specs->tag2el_count,
+                sizeof(specs->tag2el[0]), _search4tag);
+            if(t2m) {
+                /*
+                 * Found the element corresponding to the tag.
+                 */
+                NEXT_PHASE(ctx);
+                ctx->step = t2m->el_no;
+                break;
+            } else if(specs->ext_start == -1) {
+                ASN_DEBUG(
+                    "Unexpected tag %s "
+                    "in non-extensible CHOICE %s",
+                    ber_tlv_tag_string(tlv_tag), td->name);
+                RETURN(RC_FAIL);
+            } else {
+                /* Skip open type extension */
+                ASN_DEBUG(
+                    "Not implemented skipping open type extension for tag %s",
+                    ber_tlv_tag_string(tlv_tag));
+                RETURN(RC_FAIL);
+            }
+        } while(0);
+
+
+        ADVANCE(tag_len);
+    }
+        /* Fall through */
+    case 1: {
+        asn_TYPE_member_t *elm = &elements[ctx->step]; /* CHOICE's element */
+        void *memb_ptr;         /* Pointer to the member */
+        void **memb_ptr2;       /* Pointer to that pointer */
+        asn_dec_rval_t rval;
+
+        /*
+         * Compute the position of the member inside a structure,
+         * and also a type of containment (it may be contained
+         * as pointer or using inline inclusion).
+         */
+        if(elm->flags & ATF_POINTER) {
+            /* Member is a pointer to another structure */
+            memb_ptr2 = (void **)((char *)st + elm->memb_offset);
+        } else {
+            /*
+             * A pointer to a pointer
+             * holding the start of the structure
+             */
+            memb_ptr = (char *)st + elm->memb_offset;
+            memb_ptr2 = &memb_ptr;
+        }
+
+        /* Set presence to be able to free it properly at any time */
+        (void)CHOICE_variant_set_presence(td, st, ctx->step + 1);
+
+        if(specs->ext_start >= 0 && specs->ext_start <= ctx->step) {
+            ssize_t got =
+                oer_open_type_get(opt_codec_ctx, elm->type,
+                                  elm->encoding_constraints.oer_constraints,
+                                  memb_ptr2, ptr, size);
+            if(got < 0) ASN__DECODE_FAILED;
+            if(got == 0) ASN__DECODE_STARVED;
+            rval.code = RC_OK;
+            rval.consumed = got;
+        } else {
+            rval = elm->type->op->oer_decoder(
+                opt_codec_ctx, elm->type,
+                elm->encoding_constraints.oer_constraints, memb_ptr2, ptr,
+                size);
+        }
+        rval.consumed += consumed_myself;
+        switch(rval.code) {
+        case RC_OK:
+            NEXT_PHASE(ctx);
+        case RC_WMORE:
+            break;
+        case RC_FAIL:
+            SET_PHASE(ctx, 3);  /* => 3 */
+        }
+        return rval;
+    }
+    case 2:
+        /* Already decoded everything */
+        RETURN(RC_OK);
+    case 3:
+        /* Failed to decode, after all */
+        RETURN(RC_FAIL);
+    }
+
+    RETURN(RC_FAIL);
+}
+
+/*
+ * X.696 (08/2015) #8.7 Encoding of tags
+ */
+static ssize_t
+oer_put_tag(ber_tlv_tag_t tag, asn_app_consume_bytes_f *cb, void *app_key) {
+    uint8_t tclass = BER_TAG_CLASS(tag);
+    ber_tlv_tag_t tval = BER_TAG_VALUE(tag);
+
+    if(tval < 0x3F) {
+        uint8_t b = (uint8_t)((tclass << 6) | tval);
+        if(cb(&b, 1, app_key) < 0) {
+            return -1;
+        }
+        return 1;
+    } else {
+        uint8_t buf[1 + 2 * sizeof(tval)];
+        uint8_t *b = &buf[sizeof(buf)-1]; /* Last addressable */
+        size_t encoded;
+        for(; ; tval >>= 7) {
+            if(tval >> 7) {
+                *b-- = 0x80 | (tval & 0x7f);
+            } else {
+                *b-- = tval & 0x7f;
+                break;
+            }
+        }
+        *b = (uint8_t)((tclass << 6) | 0x3F);
+        encoded = sizeof(buf) - (b - buf);
+        if(cb(b, encoded, app_key) < 0) {
+            return -1;
+        }
+        return encoded;
+    }
+
+}
+
+/*
+ * Encode as Canonical OER.
+ */
+asn_enc_rval_t
+CHOICE_encode_oer(const asn_TYPE_descriptor_t *td,
+                  const asn_oer_constraints_t *constraints, const void *sptr,
+                  asn_app_consume_bytes_f *cb, void *app_key) {
+    const asn_CHOICE_specifics_t *specs =
+        (const asn_CHOICE_specifics_t *)td->specifics;
+    asn_TYPE_member_t *elm; /* CHOICE element */
+    unsigned present;
+    const void *memb_ptr;
+    ber_tlv_tag_t tag;
+    ssize_t tag_len;
+    asn_enc_rval_t er = {0, 0, 0};
+
+    (void)constraints;
+
+    if(!sptr) ASN__ENCODE_FAILED;
+
+    ASN_DEBUG("OER %s encoding as CHOICE", td->name);
+
+    present = CHOICE_variant_get_presence(td, sptr);
+    if(present == 0 || present > td->elements_count) {
+        ASN_DEBUG("CHOICE %s member is not selected", td->name);
+        ASN__ENCODE_FAILED;
+    }
+
+    elm = &td->elements[present-1];
+    if(elm->flags & ATF_POINTER) {
+        memb_ptr =
+            *(const void *const *)((const char *)sptr + elm->memb_offset);
+        if(memb_ptr == 0) {
+            /* Mandatory element absent */
+            ASN__ENCODE_FAILED;
+        }
+    } else {
+        memb_ptr = (const void *)((const char *)sptr + elm->memb_offset);
+    }
+
+    tag = asn_TYPE_outmost_tag(elm->type, memb_ptr, elm->tag_mode, elm->tag);
+    if(tag == 0) {
+        ASN__ENCODE_FAILED;
+    }
+
+    tag_len = oer_put_tag(tag, cb, app_key);
+    if(tag_len < 0) {
+        ASN__ENCODE_FAILED;
+    }
+
+    if(specs->ext_start >= 0 && (unsigned)specs->ext_start <= (present-1)) {
+        ssize_t encoded = oer_open_type_put(elm->type,
+                               elm->encoding_constraints.oer_constraints,
+                               memb_ptr, cb, app_key);
+        if(encoded < 0) ASN__ENCODE_FAILED;
+        er.encoded = tag_len + encoded;
+    } else {
+        er = elm->type->op->oer_encoder(
+            elm->type, elm->encoding_constraints.oer_constraints, memb_ptr, cb,
+            app_key);
+        if(er.encoded >= 0) er.encoded += tag_len;
+    }
+
+    return er;
+}
+
+#endif  /* ASN_DISABLE_OER_SUPPORT */

+ 1626 - 0
lib/asn1/constr_SEQUENCE.c

@@ -0,0 +1,1626 @@
+/*
+ * Copyright (c) 2003-2017 Lev Walkin <vlm@lionet.info>.
+ * All rights reserved.
+ * Redistribution and modifications are permitted subject to BSD license.
+ */
+#include <asn_internal.h>
+#include <constr_SEQUENCE.h>
+#include <OPEN_TYPE.h>
+#include <per_opentype.h>
+
+/*
+ * Number of bytes left for this structure.
+ * (ctx->left) indicates the number of bytes _transferred_ for the structure.
+ * (size) contains the number of bytes in the buffer passed.
+ */
+#define	LEFT	((size<(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 <TLV>'s L, even if the buffer size allows
+ * reading more data.
+ * For example, consider the buffer containing the following TLVs:
+ * <T:5><L:1><V> <T:6>...
+ * The TLV length clearly indicates that one byte is expected in V, but
+ * if the V processor returns with "want more data" even if the buffer
+ * contains way more data than the V processor have seen.
+ */
+#define	SIZE_VIOLATION	(ctx->left >= 0 && (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) \
+    ((specs)->first_extension >= 0          \
+     && (unsigned)(specs)->first_extension <= (memb_idx))
+
+/*
+ * 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(const asn_codec_ctx_t *opt_codec_ctx,
+                    const asn_TYPE_descriptor_t *td, void **struct_ptr,
+                    const void *ptr, size_t size, int tag_mode) {
+    /*
+	 * Bring closer parts of structure description.
+	 */
+	const asn_SEQUENCE_specifics_t *specs = (const asn_SEQUENCE_specifics_t *)td->specifics;
+    const 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 */
+	size_t 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 = (<member_number> * 2 + <microphase>).
+		 */
+	  for(edx = ((size_t)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 */
+		size_t opt_edx_end;	/* Next non-optional element */
+		size_t n;
+		int use_bsearch;
+
+		if(ctx->step & 1)
+			goto microphase2;
+
+		/*
+		 * MICROPHASE 1: Synchronize decoding.
+		 */
+		ASN_DEBUG("In %s SEQUENCE left %d, edx=%" ASN_PRI_SIZE " 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))) {
+            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 %" ASN_PRI_SIZE " "
+			"(%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 = %" ASN_PRI_SIZE ", opt = %d, ec=%d", edx,
+                          elements[edx].optional, td->elements_count);
+                if((edx + elements[edx].optional == td->elements_count)
+                   || IN_EXTENSION_GROUP(specs, edx)) {
+                    /*
+                     * 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_ANY_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 = {0, 0, 0, 0};
+			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;
+				size_t 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 %" ASN_PRI_SIZE ")",
+					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 %" ASN_PRI_SIZE ")",
+					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
+		 */
+		if(elements[edx].flags & ATF_OPEN_TYPE) {
+			rval = OPEN_TYPE_ber_get(opt_codec_ctx, td, st, &elements[edx], ptr, LEFT);
+        } else {
+			rval = elements[edx].type->op->ber_decoder(opt_codec_ctx,
+					elements[edx].type,
+					memb_ptr2, ptr, LEFT,
+					elements[edx].tag_mode);
+		}
+		ASN_DEBUG("In %s SEQUENCE decoded %" ASN_PRI_SIZE " %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;
+        /* Fall through */
+	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(const asn_TYPE_descriptor_t *td, const 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;
+	size_t 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];
+
+		const void *memb_ptr;		/* Pointer to the member */
+        const void *const *memb_ptr2; /* Pointer to that pointer */
+
+        if(elm->flags & ATF_POINTER) {
+            memb_ptr2 =
+                (const void *const *)((const char *)sptr + elm->memb_offset);
+            if(!*memb_ptr2) {
+				ASN_DEBUG("Element %s %" ASN_PRI_SIZE " not present",
+					elm->name, edx);
+				if(elm->optional)
+					continue;
+				/* Mandatory element is missing */
+				ASN__ENCODE_FAILED;
+			}
+		} else {
+            memb_ptr = (const void *)((const char *)sptr + elm->memb_offset);
+            memb_ptr2 = &memb_ptr;
+		}
+
+		/* Eliminate default values */
+		if(elm->default_value_cmp && elm->default_value_cmp(*memb_ptr2) == 0)
+			continue;
+
+		erval = elm->type->op->der_encoder(elm->type, *memb_ptr2,
+			elm->tag_mode, elm->tag,
+			0, 0);
+		if(erval.encoded == -1)
+			return erval;
+		computed_size += erval.encoded;
+		ASN_DEBUG("Member %" ASN_PRI_SIZE " %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;
+        const void *memb_ptr;           /* Pointer to the member */
+        const void *const *memb_ptr2;   /* Pointer to that pointer */
+
+        if(elm->flags & ATF_POINTER) {
+            memb_ptr2 =
+                (const void *const *)((const char *)sptr + elm->memb_offset);
+            if(!*memb_ptr2) continue;
+		} else {
+            memb_ptr = (const void *)((const char *)sptr + elm->memb_offset);
+            memb_ptr2 = &memb_ptr;
+		}
+
+		/* Eliminate default values */
+        if(elm->default_value_cmp && elm->default_value_cmp(*memb_ptr2) == 0)
+            continue;
+
+		tmperval = elm->type->op->der_encoder(elm->type, *memb_ptr2,
+			elm->tag_mode, elm->tag, cb, app_key);
+		if(tmperval.encoded == -1)
+			return tmperval;
+		computed_size -= tmperval.encoded;
+		ASN_DEBUG("Member %" ASN_PRI_SIZE " %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);        \
+        ptr = ((const char *)ptr) + num; \
+        size -= num;                     \
+        consumed_myself += num;          \
+    } while(0)
+
+/*
+ * Decode the XER (XML) data.
+ */
+asn_dec_rval_t
+SEQUENCE_decode_xer(const asn_codec_ctx_t *opt_codec_ctx,
+                    const asn_TYPE_descriptor_t *td, void **struct_ptr,
+                    const char *opt_mname, const void *ptr, size_t size) {
+    /*
+	 * Bring closer parts of structure description.
+	 */
+	const asn_SEQUENCE_specifics_t *specs
+		= (const 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 */
+	size_t edx;			/* Element index */
+
+	/*
+	 * 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;
+
+		/*
+		 * Go inside the inner member of a sequence.
+		 */
+		if(ctx->phase == 2) {
+			asn_dec_rval_t tmprval;
+			void *memb_ptr_dontuse;		/* 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_dontuse = (char *)st + elm->memb_offset;
+				memb_ptr2 = &memb_ptr_dontuse;  /* Only use of memb_ptr_dontuse */
+			}
+
+			if(elm->flags & ATF_OPEN_TYPE) {
+				tmprval = OPEN_TYPE_xer_get(opt_codec_ctx, td, st, elm, ptr, size);
+			} else {
+				/* Invoke the inner type decoder, m.b. multiple times */
+				tmprval = elm->type->op->xer_decoder(opt_codec_ctx,
+						elm->type, memb_ptr2, elm->name,
+						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, 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(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)) {
+                    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=%" ASN_PRI_SIZE "",
+				tcv, ctx->phase, edx);
+			if(ctx->phase != 1) {
+				break;	/* Really unexpected */
+			}
+
+			if(edx < td->elements_count) {
+				/*
+				 * Search which member corresponds to this tag.
+				 */
+				size_t n;
+				size_t 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(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: %" ASN_PRI_SIZE "/%u",
+					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 %" ASN_PRI_SIZE "",
+					edx);
+				/*
+				 * Check for (XCT_BOTH or XCT_UNKNOWN_BO)
+				 * By using a mask. Only record a pure
+				 * <opening> tags.
+				 */
+				if(tcv & XCT_CLOSING) {
+					/* Found </extension> 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 *)ptr)[0]:'.',
+			size>1?((const char *)ptr)[1]:'.',
+			size>2?((const char *)ptr)[2]:'.',
+			size>3?((const char *)ptr)[3]:'.',
+			size>4?((const char *)ptr)[4]:'.',
+			size>5?((const char *)ptr)[5]:'.');
+		break;
+	}
+
+	ctx->phase = 4;	/* "Phase out" on hard failure */
+	RETURN(RC_FAIL);
+}
+
+asn_enc_rval_t
+SEQUENCE_encode_xer(const asn_TYPE_descriptor_t *td, const 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);
+    asn_TYPE_descriptor_t *tmp_def_val_td = 0;
+    void *tmp_def_val = 0;
+	size_t 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];
+        const void *memb_ptr;
+        const char *mname = elm->name;
+        unsigned int mlen = strlen(mname);
+
+        if(elm->flags & ATF_POINTER) {
+            memb_ptr =
+                *(const void *const *)((const char *)sptr + elm->memb_offset);
+            if(!memb_ptr) {
+                assert(tmp_def_val == 0);
+                if(elm->default_value_set) {
+                    if(elm->default_value_set(&tmp_def_val)) {
+                        ASN__ENCODE_FAILED;
+                    } else {
+                        memb_ptr = tmp_def_val;
+                        tmp_def_val_td = elm->type;
+                    }
+                } else if(elm->optional) {
+                    continue;
+                } else {
+                    /* Mandatory element is missing */
+                    ASN__ENCODE_FAILED;
+                }
+            }
+        } else {
+            memb_ptr = (const void *)((const 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->op->xer_encoder(elm->type, memb_ptr, ilevel + 1,
+                                           flags, cb, app_key);
+        if(tmp_def_val) {
+            ASN_STRUCT_FREE(*tmp_def_val_td, tmp_def_val);
+            tmp_def_val = 0;
+        }
+        if(tmper.encoded == -1) return tmper;
+        er.encoded += tmper.encoded;
+
+        ASN__CALLBACK3("</", 2, mname, mlen, ">", 1);
+    }
+
+    if(!xcan) ASN__TEXT_INDENT(1, ilevel - 1);
+
+    ASN__ENCODED_OK(er);
+cb_failed:
+    if(tmp_def_val) ASN_STRUCT_FREE(*tmp_def_val_td, tmp_def_val);
+    ASN__ENCODE_FAILED;
+}
+
+int
+SEQUENCE_print(const asn_TYPE_descriptor_t *td, const void *sptr, int ilevel,
+               asn_app_consume_bytes_f *cb, void *app_key) {
+    size_t edx;
+	int ret;
+
+	if(!sptr) return (cb("<absent>", 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 <absent> 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->op->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(const asn_TYPE_descriptor_t *td, void *sptr,
+              enum asn_struct_free_method method) {
+    size_t edx;
+    const asn_SEQUENCE_specifics_t *specs =
+        (const asn_SEQUENCE_specifics_t *)td->specifics;
+    asn_struct_ctx_t *ctx; /* Decoder context */
+
+	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);
+		}
+	}
+
+	/* Clean parsing context */
+	ctx = (asn_struct_ctx_t *)((char *)sptr + specs->ctx_offset);
+	FREEMEM(ctx->ptr);
+
+    switch(method) {
+    case ASFM_FREE_EVERYTHING:
+        FREEMEM(sptr);
+        break;
+    case ASFM_FREE_UNDERLYING:
+        break;
+    case ASFM_FREE_UNDERLYING_AND_RESET:
+        memset(
+            sptr, 0,
+            ((const asn_SEQUENCE_specifics_t *)(td->specifics))->struct_size);
+        break;
+    }
+}
+
+int
+SEQUENCE_constraint(const asn_TYPE_descriptor_t *td, const void *sptr,
+                    asn_app_constraint_failed_f *ctfailcb, void *app_key) {
+    size_t 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->encoding_constraints.general_constraints) {
+			int ret = elm->encoding_constraints.general_constraints(elm->type, memb_ptr,
+				ctfailcb, app_key);
+			if(ret) return ret;
+		} else {
+			return elm->type->encoding_constraints.general_constraints(elm->type,
+				memb_ptr, ctfailcb, app_key);
+		}
+	}
+
+	return 0;
+}
+
+#ifndef ASN_DISABLE_PER_SUPPORT
+
+asn_dec_rval_t
+SEQUENCE_decode_uper(const asn_codec_ctx_t *opt_codec_ctx,
+                     const asn_TYPE_descriptor_t *td,
+                     const asn_per_constraints_t *constraints, void **sptr,
+                     asn_per_data_t *pd) {
+    const asn_SEQUENCE_specifics_t *specs = (const 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;
+	size_t 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->first_extension < 0) {
+		extpresent = 0;
+	} else {
+		extpresent = per_get_few_bits(pd, 1);
+		if(extpresent < 0) ASN__DECODE_STARVED;
+	}
+
+	/* 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 < (specs->first_extension < 0 ? td->elements_count
+                                          : (size_t)specs->first_extension);
+        edx++) {
+        asn_TYPE_member_t *elm = &td->elements[edx];
+		void *memb_ptr;		/* Pointer to the member */
+		void **memb_ptr2;	/* Pointer to that pointer */
+
+		assert(!IN_EXTENSION_GROUP(specs, edx));
+
+		/* 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_set) {
+					/* Fill-in DEFAULT */
+					if(elm->default_value_set(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);
+
+		if(elm->flags & ATF_OPEN_TYPE) {
+			rv = OPEN_TYPE_uper_get(opt_codec_ctx, td, st, elm, pd);
+		} else {
+			rv = elm->type->op->uper_decoder(opt_codec_ctx, elm->type,
+					elm->encoding_constraints.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 %" ASN_PRI_SSIZE " present in %s", 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->first_extension; 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;
+
+            /* 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->encoding_constraints.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;
+				}
+                ASN_DEBUG("Skipped overflow extension");
+                continue;
+			}
+			break;
+		}
+
+		FREEMEM(epres);
+	}
+
+    if(specs->first_extension >= 0) {
+        unsigned i;
+        /* Fill DEFAULT members in extensions */
+        for(i = specs->roms_count; i < specs->roms_count + specs->aoms_count;
+            i++) {
+            asn_TYPE_member_t *elm;
+            void **memb_ptr2; /* Pointer to member pointer */
+
+            edx = specs->oms[i];
+            elm = &td->elements[edx];
+
+            if(!elm->default_value_set) 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_set(memb_ptr2)) {
+                ASN__DECODE_FAILED;
+            }
+        }
+    }
+
+	rv.consumed = 0;
+	rv.code = RC_OK;
+	return rv;
+}
+
+static int
+SEQUENCE__handle_extensions(const asn_TYPE_descriptor_t *td, const void *sptr,
+                            asn_per_outp_t *po1, asn_per_outp_t *po2) {
+    const asn_SEQUENCE_specifics_t *specs =
+        (const asn_SEQUENCE_specifics_t *)td->specifics;
+    int exts_present = 0;
+    int exts_count = 0;
+    size_t edx;
+
+    if(specs->first_extension < 0) {
+        return 0;
+    }
+
+    /* Find out which extensions are present */
+    for(edx = specs->first_extension; edx < td->elements_count; edx++) {
+        asn_TYPE_member_t *elm = &td->elements[edx];
+        const void *memb_ptr;         /* Pointer to the member */
+        const void *const *memb_ptr2; /* Pointer to that pointer */
+        int present;
+
+        /* Fetch the pointer to this member */
+        if(elm->flags & ATF_POINTER) {
+            memb_ptr2 =
+                (const void *const *)((const char *)sptr + elm->memb_offset);
+            present = (*memb_ptr2 != 0);
+        } else {
+            memb_ptr = (const void *)((const char *)sptr + elm->memb_offset);
+            memb_ptr2 = &memb_ptr;
+			present = 1;
+		}
+
+        ASN_DEBUG("checking %s:%s (@%" ASN_PRI_SIZE ") present => %d", elm->name,
+                  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->encoding_constraints.per_constraints,
+                                 *memb_ptr2, po2))
+            return -1;
+    }
+
+    return exts_present ? exts_count : 0;
+}
+
+asn_enc_rval_t
+SEQUENCE_encode_uper(const asn_TYPE_descriptor_t *td,
+                     const asn_per_constraints_t *constraints, const void *sptr,
+                     asn_per_outp_t *po) {
+    const asn_SEQUENCE_specifics_t *specs
+		= (const asn_SEQUENCE_specifics_t *)td->specifics;
+	asn_enc_rval_t er;
+	int n_extensions;
+	size_t edx;
+	size_t 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->first_extension < 0) {
+        n_extensions = 0; /* There are no extensions to encode */
+    } else {
+        n_extensions = SEQUENCE__handle_extensions(td, sptr, 0, 0);
+        if(n_extensions < 0) ASN__ENCODE_FAILED;
+        if(per_put_few_bits(po, n_extensions ? 1 : 0, 1)) {
+            ASN__ENCODE_FAILED;
+        }
+    }
+
+	/* Encode a presence bitmap */
+	for(i = 0; i < specs->roms_count; i++) {
+		asn_TYPE_member_t *elm;
+		const void *memb_ptr;		/* Pointer to the member */
+        const void *const *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 =
+                (const void *const *)((const char *)sptr + elm->memb_offset);
+            present = (*memb_ptr2 != 0);
+		} else {
+            memb_ptr = (const void *)((const char *)sptr + elm->memb_offset);
+            memb_ptr2 = &memb_ptr;
+			present = 1;
+		}
+
+		/* Eliminate default values */
+        if(present && elm->default_value_cmp
+           && elm->default_value_cmp(*memb_ptr2) == 0)
+            present = 0;
+
+		ASN_DEBUG("Element %s %s %s->%s is %s",
+			elm->flags & ATF_POINTER ? "ptr" : "inline",
+			elm->default_value_cmp ? "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("first_extension = %d, elements = %d", specs->first_extension,
+              td->elements_count);
+	for(edx = 0;
+		edx < ((specs->first_extension < 0) ? td->elements_count
+                                            : (size_t)specs->first_extension);
+		edx++) {
+		asn_TYPE_member_t *elm = &td->elements[edx];
+		const void *memb_ptr;         /* Pointer to the member */
+		const void *const *memb_ptr2; /* Pointer to that pointer */
+
+		ASN_DEBUG("About to encode %s", elm->type->name);
+
+		/* Fetch the pointer to this member */
+		if(elm->flags & ATF_POINTER) {
+            memb_ptr2 =
+                (const void *const *)((const char *)sptr + elm->memb_offset);
+            if(!*memb_ptr2) {
+				ASN_DEBUG("Element %s %" ASN_PRI_SIZE " not present",
+					elm->name, edx);
+				if(elm->optional)
+					continue;
+				/* Mandatory element is missing */
+				ASN__ENCODE_FAILED;
+			}
+		} else {
+            memb_ptr = (const void *)((const char *)sptr + elm->memb_offset);
+            memb_ptr2 = &memb_ptr;
+		}
+
+		/* Eliminate default values */
+		if(elm->default_value_cmp && elm->default_value_cmp(*memb_ptr2) == 0)
+			continue;
+
+        ASN_DEBUG("Encoding %s->%s:%s", td->name, elm->name, elm->type->name);
+        er = elm->type->op->uper_encoder(
+            elm->type, elm->encoding_constraints.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 extensions %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);
+}
+
+#endif  /* ASN_DISABLE_PER_SUPPORT */
+
+int
+SEQUENCE_compare(const asn_TYPE_descriptor_t *td, const void *aptr,
+                 const void *bptr) {
+    size_t edx;
+
+	for(edx = 0; edx < td->elements_count; edx++) {
+		asn_TYPE_member_t *elm = &td->elements[edx];
+		const void *amemb;
+		const void *bmemb;
+        int ret;
+
+		if(elm->flags & ATF_POINTER) {
+            amemb =
+                *(const void *const *)((const char *)aptr + elm->memb_offset);
+            bmemb =
+                *(const void *const *)((const char *)bptr + elm->memb_offset);
+            if(!amemb) {
+                if(!bmemb) continue;
+                if(elm->default_value_cmp
+                   && elm->default_value_cmp(bmemb) == 0) {
+                    /* A is absent, but B is present and equal to DEFAULT */
+                    continue;
+                }
+                return -1;
+            } else if(!bmemb) {
+                if(elm->default_value_cmp
+                   && elm->default_value_cmp(amemb) == 0) {
+                    /* B is absent, but A is present and equal to DEFAULT */
+                    continue;
+                }
+                return 1;
+            }
+		} else {
+            amemb = (const void *)((const char *)aptr + elm->memb_offset);
+            bmemb = (const void *)((const char *)bptr + elm->memb_offset);
+		}
+
+        ret = elm->type->op->compare_struct(elm->type, amemb, bmemb);
+        if(ret != 0) return ret;
+    }
+
+    return 0;
+}
+
+asn_TYPE_operation_t asn_OP_SEQUENCE = {
+	SEQUENCE_free,
+	SEQUENCE_print,
+	SEQUENCE_compare,
+	SEQUENCE_decode_ber,
+	SEQUENCE_encode_der,
+	SEQUENCE_decode_xer,
+	SEQUENCE_encode_xer,
+#ifdef	ASN_DISABLE_OER_SUPPORT
+	0,
+	0,
+#else
+	SEQUENCE_decode_oer,
+	SEQUENCE_encode_oer,
+#endif  /* ASN_DISABLE_OER_SUPPORT */
+#ifdef ASN_DISABLE_PER_SUPPORT
+	0,
+	0,
+#else
+	SEQUENCE_decode_uper,
+	SEQUENCE_encode_uper,
+#endif /* ASN_DISABLE_PER_SUPPORT */
+	SEQUENCE_random_fill,
+	0	/* Use generic outmost tag fetcher */
+};
+
+
+asn_random_fill_result_t
+SEQUENCE_random_fill(const asn_TYPE_descriptor_t *td, void **sptr,
+                   const asn_encoding_constraints_t *constr,
+                   size_t max_length) {
+    const asn_SEQUENCE_specifics_t *specs =
+        (const asn_SEQUENCE_specifics_t *)td->specifics;
+    asn_random_fill_result_t result_ok = {ARFILL_OK, 0};
+    asn_random_fill_result_t result_failed = {ARFILL_FAILED, 0};
+    asn_random_fill_result_t result_skipped = {ARFILL_SKIPPED, 0};
+    void *st = *sptr;
+    size_t edx;
+
+    if(max_length == 0) return result_skipped;
+
+    (void)constr;
+
+    if(st == NULL) {
+        st = CALLOC(1, specs->struct_size);
+        if(st == NULL) {
+            return result_failed;
+        }
+    }
+
+    for(edx = 0; edx < td->elements_count; edx++) {
+        const asn_TYPE_member_t *elm = &td->elements[edx];
+        void *memb_ptr;   /* Pointer to the member */
+        void **memb_ptr2; /* Pointer to that pointer */
+        asn_random_fill_result_t tmpres;
+
+        if(elm->optional && asn_random_between(0, 4) == 2) {
+            /* Sometimes decide not to fill the optional value */
+            continue;
+        }
+
+        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;
+        }
+
+        tmpres = elm->type->op->random_fill(
+            elm->type, memb_ptr2, &elm->encoding_constraints,
+            max_length > result_ok.length ? max_length - result_ok.length : 0);
+        switch(tmpres.code) {
+        case ARFILL_OK:
+            result_ok.length += tmpres.length;
+            continue;
+        case ARFILL_SKIPPED:
+            assert(!(elm->flags & ATF_POINTER) || *memb_ptr2 == NULL);
+            continue;
+        case ARFILL_FAILED:
+            if(st == *sptr) {
+                ASN_STRUCT_RESET(*td, st);
+            } else {
+                ASN_STRUCT_FREE(*td, st);
+            }
+            return tmpres;
+        }
+    }
+
+    *sptr = st;
+
+    return result_ok;
+}
+

+ 66 - 0
lib/asn1/constr_SEQUENCE.h

@@ -0,0 +1,66 @@
+/*-
+ * Copyright (c) 2003-2017 Lev Walkin <vlm@lionet.info>. All rights reserved.
+ * Redistribution and modifications are permitted subject to BSD license.
+ */
+#ifndef	_CONSTR_SEQUENCE_H_
+#define	_CONSTR_SEQUENCE_H_
+
+#include <asn_application.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct asn_SEQUENCE_specifics_s {
+	/*
+	 * Target structure description.
+	 */
+	unsigned struct_size;	/* Size of the target structure. */
+	unsigned ctx_offset;	/* Offset of the asn_struct_ctx_t member */
+
+	/*
+	 * Tags to members mapping table (sorted).
+	 */
+	const asn_TYPE_tag2member_t *tag2el;
+	unsigned tag2el_count;
+
+	/*
+	 * Optional members of the extensions root (roms) or additions (aoms).
+	 * Meaningful for PER.
+	 */
+	const int *oms;         /* Optional MemberS */
+	unsigned roms_count;    /* Root optional members count */
+	unsigned aoms_count;    /* Additions optional members count */
+
+	/*
+	 * Description of an extensions group.
+	 * Root components are clustered at the beginning of the structure,
+	 * whereas extensions are clustered at the end. -1 means not extensible.
+	 */
+	signed first_extension;       /* First extension addition */
+} 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_struct_compare_f SEQUENCE_compare;
+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;
+oer_type_decoder_f SEQUENCE_decode_oer;
+oer_type_encoder_f SEQUENCE_encode_oer;
+per_type_decoder_f SEQUENCE_decode_uper;
+per_type_encoder_f SEQUENCE_encode_uper;
+asn_random_fill_f  SEQUENCE_random_fill;
+extern asn_TYPE_operation_t asn_OP_SEQUENCE;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif	/* _CONSTR_SEQUENCE_H_ */

+ 561 - 0
lib/asn1/constr_SEQUENCE_oer.c

@@ -0,0 +1,561 @@
+/*
+ * Copyright (c) 2017 Lev Walkin <vlm@lionet.info>.
+ * All rights reserved.
+ * Redistribution and modifications are permitted subject to BSD license.
+ */
+#ifndef ASN_DISABLE_OER_SUPPORT
+
+#include <asn_internal.h>
+#include <constr_SEQUENCE.h>
+#include <OPEN_TYPE.h>
+#include <errno.h>
+
+/*
+ * 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;                         \
+        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)
+
+/*
+ * Check whether we are inside the extensions group.
+ */
+#define IN_EXTENSION_GROUP(specs, memb_idx) \
+    ((specs)->first_extension >= 0          \
+     && (unsigned)(specs)->first_extension <= (memb_idx))
+
+#define IN_ROOT_GROUP_PRED(edx)                            \
+    edx < (specs->first_extension < 0 ? td->elements_count \
+                                      : (size_t)specs->first_extension)
+
+#define FOR_IN_ROOT_GROUP(edx) for(edx = 0; IN_ROOT_GROUP_PRED(edx); edx++)
+
+/*
+ * Return a standardized complex structure.
+ */
+#undef  RETURN
+#define RETURN(_code)   do {                    \
+                rval.code = _code;              \
+                rval.consumed = consumed_myself;\
+                return rval;                    \
+        } while(0)
+
+/*
+ * Return pointer to a member.
+ */
+static void **
+element_ptrptr(void *struct_ptr, asn_TYPE_member_t *elm, void **tmp_save_ptr) {
+    if(elm->flags & ATF_POINTER) {
+        /* Member is a pointer to another structure */
+        return (void **)((char *)struct_ptr + elm->memb_offset);
+    } else {
+        assert(tmp_save_ptr);
+        *tmp_save_ptr = (void *)((char *)struct_ptr + elm->memb_offset);
+        return tmp_save_ptr;
+    }
+}
+
+static const void *
+element_ptr(const void *struct_ptr, const asn_TYPE_member_t *elm) {
+    if(elm->flags & ATF_POINTER) {
+        /* Member is a pointer to another structure */
+        return *(const void *const *)((const char *)struct_ptr
+                                      + elm->memb_offset);
+    } else {
+        return (const void *)((const char *)struct_ptr + elm->memb_offset);
+    }
+}
+
+asn_dec_rval_t
+SEQUENCE_decode_oer(const asn_codec_ctx_t *opt_codec_ctx,
+                    const asn_TYPE_descriptor_t *td,
+                    const asn_oer_constraints_t *constraints, void **struct_ptr,
+                    const void *ptr, size_t size) {
+    const asn_SEQUENCE_specifics_t *specs =
+        (const asn_SEQUENCE_specifics_t *)td->specifics;
+    asn_dec_rval_t rval = {RC_OK, 0};
+    void *st = *struct_ptr; /* Target structure */
+    asn_struct_ctx_t *ctx; /* Decoder context */
+    size_t consumed_myself = 0; /* Consumed bytes from ptr. */
+
+    (void)constraints;
+
+    if(ASN__STACK_OVERFLOW_CHECK(opt_codec_ctx))
+        ASN__DECODE_FAILED;
+
+    /*
+     * 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: {
+        /*
+         * Fetch preamble.
+         */
+        asn_bit_data_t *preamble;
+        int has_extensions_bit = (specs->first_extension >= 0);
+        size_t preamble_bits = (has_extensions_bit + specs->roms_count);
+        size_t preamble_bytes = ((7 + preamble_bits) >> 3);
+
+        ASN_DEBUG("OER SEQUENCE %s Decoding PHASE 0", td->name);
+
+        ASN_DEBUG(
+            "Expecting preamble bits %" ASN_PRI_SIZE " for %s (including %d extension bits)",
+            preamble_bits, td->name, has_extensions_bit);
+
+        if(preamble_bytes > size) {
+            ASN__DECODE_STARVED;
+        }
+
+        preamble = asn_bit_data_new_contiguous(ptr, preamble_bits);
+        if(!preamble) {
+            RETURN(RC_FAIL);
+        }
+        preamble->nboff = has_extensions_bit;
+        ctx->ptr = preamble;
+        ADVANCE(preamble_bytes);
+    }
+        NEXT_PHASE(ctx);
+        /* FALL THROUGH */
+    case 1: {
+        /* Decode components of the extension root */
+        asn_bit_data_t *preamble = ctx->ptr;
+        size_t edx;
+
+        ASN_DEBUG("OER SEQUENCE %s Decoding PHASE 1 (Root)", td->name);
+
+        assert(preamble);
+
+        for(edx = (ctx->step >> 1); IN_ROOT_GROUP_PRED(edx);
+            edx++, ctx->step = (ctx->step & ~1) + 2) {
+            asn_TYPE_member_t *elm = &td->elements[edx];
+
+            ASN_DEBUG("Decoding %s->%s", td->name, elm->name);
+
+            assert(!IN_EXTENSION_GROUP(specs, edx));
+
+            if(ctx->step & 1) {
+                goto microphase2_decode_continues;
+            }
+
+
+            if(elm->optional) {
+                int32_t present = asn_get_few_bits(preamble, 1);
+                if(present < 0) {
+                    ASN_DEBUG("Presence map ended prematurely: %d", present);
+                    RETURN(RC_FAIL);
+                } else if(present == 0) {
+                    if(elm->default_value_set) {
+                        /* Fill-in DEFAULT */
+                        void *tmp;
+                        if(elm->default_value_set(
+                               element_ptrptr(st, elm, &tmp))) {
+                            RETURN(RC_FAIL);
+                        }
+                    }
+                    /* The member is not present. */
+                    continue;
+                }
+                /* Present OPTIONAL or DEFAULT component. */
+            }
+
+            /*
+             * MICROPHASE 2: Invoke the member-specific decoder.
+             */
+            ctx->step |= 1; /* Confirm entering next microphase */
+        microphase2_decode_continues:
+            if(elm->flags & ATF_OPEN_TYPE) {
+                rval = OPEN_TYPE_oer_get(opt_codec_ctx, td, st, elm, ptr, size);
+            } else {
+                void *save_memb_ptr; /* Temporary reference. */
+                void **memb_ptr2;  /* Pointer to a pointer to a memmber */
+
+                memb_ptr2 = element_ptrptr(st, elm, &save_memb_ptr);
+
+                rval = elm->type->op->oer_decoder(
+                    opt_codec_ctx, elm->type,
+                    elm->encoding_constraints.oer_constraints, memb_ptr2, ptr,
+                    size);
+            }
+            switch(rval.code) {
+            case RC_OK:
+                ADVANCE(rval.consumed);
+                break;
+            case RC_WMORE:
+                ASN_DEBUG("More bytes needed at element %s \"%s\"", td->name,
+                          elm->name);
+                ADVANCE(rval.consumed);
+                RETURN(RC_WMORE);
+            case RC_FAIL:
+                ASN_DEBUG("Decoding failed at element %s \"%s\"", td->name,
+                          elm->name);
+                RETURN(RC_FAIL);
+            }
+        } /* for(all root members) */
+
+    }
+        NEXT_PHASE(ctx);
+        /* FALL THROUGH */
+    case 2:
+        assert(ctx->ptr);
+        {
+        /* Cleanup preamble. */
+        asn_bit_data_t *preamble = ctx->ptr;
+        asn_bit_data_t *extadds;
+        int has_extensions_bit = (specs->first_extension >= 0);
+        int extensions_present =
+            has_extensions_bit
+            && (preamble->buffer == NULL
+                || (((const uint8_t *)preamble->buffer)[0] & 0x80));
+        uint8_t unused_bits;
+        size_t len = 0;
+        ssize_t len_len;
+
+        ASN_DEBUG("OER SEQUENCE %s Decoding PHASE 2", td->name);
+
+        preamble->buffer = 0; /* Will do extensions_present==1 next time. */
+
+        if(!extensions_present) {
+            ctx->phase = 10;
+            RETURN(RC_OK);
+        }
+
+        /*
+         * X.696 (08/2015) #16.1 (c), #16.4
+         * Read in the extension addition presence bitmap.
+         */
+
+        len_len = oer_fetch_length(ptr, size, &len);
+        if(len_len > 0) {
+            ADVANCE(len_len);
+        } else if(len_len < 0) {
+            RETURN(RC_FAIL);
+        } else {
+            RETURN(RC_WMORE);
+        }
+
+        if(len == 0) {
+            /* 16.4.1-2 */
+            RETURN(RC_FAIL);
+        } else if(len > size) {
+            RETURN(RC_WMORE);
+        }
+
+        /* Account for unused bits */
+        unused_bits = 0x7 & *(const uint8_t *)ptr;
+        ADVANCE(1);
+        len--;
+        if(unused_bits && len == 0) {
+            RETURN(RC_FAIL);
+        }
+
+        /* Get the extensions map */
+        extadds = asn_bit_data_new_contiguous(ptr, len * 8 - unused_bits);
+        if(!extadds) {
+            RETURN(RC_FAIL);
+        }
+        FREEMEM(preamble);
+        ctx->ptr = extadds;
+        ADVANCE(len);
+    }
+        NEXT_PHASE(ctx);
+        ctx->step =
+            (specs->first_extension < 0 ? td->elements_count
+                                        : (size_t)specs->first_extension);
+        /* Fall through */
+    case 3:
+        ASN_DEBUG("OER SEQUENCE %s Decoding PHASE 3 (Extensions)", td->name);
+        for(; ctx->step < (signed)td->elements_count; ctx->step++) {
+            asn_bit_data_t *extadds = ctx->ptr;
+            size_t edx = ctx->step;
+            asn_TYPE_member_t *elm = &td->elements[edx];
+            void *tmp_memb_ptr;
+            void **memb_ptr2 = element_ptrptr(st, elm, &tmp_memb_ptr);
+
+            switch(asn_get_few_bits(extadds, 1)) {
+            case -1:
+                /*
+                 * Not every one of our extensions is known to the remote side.
+                 * Continue filling in their defaults though.
+                 */
+                /* Fall through */
+            case 0:
+                /* Fill-in DEFAULT */
+                if(elm->default_value_set
+                   && elm->default_value_set(memb_ptr2)) {
+                    RETURN(RC_FAIL);
+                }
+                continue;
+            case 1: {
+                /* Read OER open type */
+                ssize_t ot_size =
+                    oer_open_type_get(opt_codec_ctx, elm->type,
+                                      elm->encoding_constraints.oer_constraints,
+                                      memb_ptr2, ptr, size);
+                assert(ot_size <= (ssize_t)size);
+                if(ot_size > 0) {
+                    ADVANCE(ot_size);
+                } else if(ot_size < 0) {
+                    RETURN(RC_FAIL);
+                } else {
+                    /* Roll back open type parsing */
+                    asn_get_undo(extadds, 1);
+                    RETURN(RC_WMORE);
+                }
+                break;
+            }
+            default:
+                RETURN(RC_FAIL);
+            }
+        }
+
+        NEXT_PHASE(ctx);
+        /* Fall through */
+    case 4:
+        ASN_DEBUG("OER SEQUENCE %s Decoding PHASE 4", td->name);
+        /* Read in the rest of Open Types while ignoring them */
+        for(;;) {
+            asn_bit_data_t *extadds = ctx->ptr;
+            switch(asn_get_few_bits(extadds, 1)) {
+            case 0:
+                continue;
+            case 1: {
+                ssize_t skipped = oer_open_type_skip(ptr, size);
+                if(skipped > 0) {
+                    ADVANCE(skipped);
+                } else if(skipped < 0) {
+                    RETURN(RC_FAIL);
+                } else {
+                    asn_get_undo(extadds, 1);
+                    RETURN(RC_WMORE);
+                }
+                continue;
+            }
+            case -1:
+                /* No more Open Type encoded components */
+                break;
+            default:
+                RETURN(RC_FAIL);
+            }
+            break;
+        }
+    }
+
+    RETURN(RC_OK);
+}
+
+/*
+ * Encode as Canonical OER.
+ */
+asn_enc_rval_t
+SEQUENCE_encode_oer(const asn_TYPE_descriptor_t *td,
+                    const asn_oer_constraints_t *constraints, const void *sptr,
+                    asn_app_consume_bytes_f *cb, void *app_key) {
+    const asn_SEQUENCE_specifics_t *specs = (const asn_SEQUENCE_specifics_t *)td->specifics;
+    size_t computed_size = 0;
+    int has_extensions_bit = (specs->first_extension >= 0);
+    size_t preamble_bits = (has_extensions_bit + specs->roms_count);
+    uint32_t has_extensions = 0;
+    size_t edx;
+    int ret;
+
+    (void)constraints;
+
+    if(preamble_bits) {
+        asn_bit_outp_t preamble;
+
+        memset(&preamble, 0, sizeof(preamble));
+        preamble.output = cb;
+        preamble.op_key = app_key;
+
+        if(has_extensions_bit) {
+            for(edx = specs->first_extension; edx < td->elements_count; edx++) {
+                asn_TYPE_member_t *elm = &td->elements[edx];
+                const void *memb_ptr = element_ptr(sptr, elm);
+                if(memb_ptr) {
+                    if(elm->default_value_cmp
+                       && elm->default_value_cmp(memb_ptr) == 0) {
+                        /* Do not encode default values in extensions */
+                    } else {
+                        has_extensions = 1;
+                        break;
+                    }
+                }
+            }
+            ret = asn_put_few_bits(&preamble, has_extensions, 1);
+            assert(ret == 0);
+            if(ret < 0) {
+                ASN__ENCODE_FAILED;
+            }
+        }
+
+        /*
+         * Encode optional components bitmap.
+         */
+        if(specs->roms_count) {
+            FOR_IN_ROOT_GROUP(edx) {
+                asn_TYPE_member_t *elm = &td->elements[edx];
+
+                if(IN_EXTENSION_GROUP(specs, edx)) break;
+
+                if(elm->optional) {
+                    const void *memb_ptr = element_ptr(sptr, elm);
+                    uint32_t has_component = memb_ptr != NULL;
+                    if(has_component && elm->default_value_cmp
+                       && elm->default_value_cmp(memb_ptr) == 0) {
+                        has_component = 0;
+                    }
+                    ret = asn_put_few_bits(&preamble, has_component, 1);
+                    if(ret < 0) {
+                        ASN__ENCODE_FAILED;
+                    }
+                }
+            }
+        }
+
+        asn_put_aligned_flush(&preamble);
+        computed_size += preamble.flushed_bytes;
+    }   /* if(preamble_bits) */
+
+    /*
+     * Put root components and extensions root.
+     */
+    for(edx = 0; edx < td->elements_count; edx++) {
+        asn_TYPE_member_t *elm = &td->elements[edx];
+        asn_enc_rval_t er;
+        const void *memb_ptr;
+
+        if(IN_EXTENSION_GROUP(specs, edx)) break;
+
+        memb_ptr = element_ptr(sptr, elm);
+        if(memb_ptr) {
+            if(elm->default_value_cmp
+               && elm->default_value_cmp(memb_ptr) == 0) {
+                /* Skip default values in encoding */
+                continue;
+            }
+        } else {
+            if(elm->optional) continue;
+            /* Mandatory element is missing */
+            ASN__ENCODE_FAILED;
+        }
+        if(!elm->type->op->oer_encoder) {
+            ASN_DEBUG("OER encoder is not defined for type %s", elm->type->name);
+            ASN__ENCODE_FAILED;
+        }
+        er = elm->type->op->oer_encoder(
+            elm->type, elm->encoding_constraints.oer_constraints, memb_ptr, cb,
+            app_key);
+        if(er.encoded == -1) {
+            ASN_DEBUG("... while encoding %s member \"%s\"\n", td->name,
+                      elm->name);
+            return er;
+        }
+        computed_size += er.encoded;
+    }
+
+    /*
+     * Before encode extensions, encode extensions additions presense bitmap
+     # X.696 (08/2015) #16.4.
+     */
+    if(has_extensions) {
+        asn_bit_outp_t extadds;
+
+        /* Special case allowing us to use exactly one byte for #8.6 */
+        size_t aoms_length_bits = specs->aoms_count;
+        size_t aoms_length_bytes = (7 + aoms_length_bits) >> 3;
+        uint8_t unused_bits = 0x07 & (8 - (aoms_length_bits & 0x07));
+
+        assert(1 + aoms_length_bytes <= 127);
+
+        memset(&extadds, 0, sizeof(extadds));
+        extadds.output = cb;
+        extadds.op_key = app_key;
+
+        /* #8.6 length determinant */
+        ret = asn_put_few_bits(&extadds, (1 + aoms_length_bytes), 8);
+        if(ret < 0) ASN__ENCODE_FAILED;
+
+        /* Number of unused bytes, #16.4.2 */
+        ret = asn_put_few_bits(&extadds, unused_bits, 8);
+        if(ret < 0) ASN__ENCODE_FAILED;
+
+        /* Encode presence bitmap #16.4.3 */
+        for(edx = specs->first_extension; edx < td->elements_count; edx++) {
+            asn_TYPE_member_t *elm = &td->elements[edx];
+            const void *memb_ptr = element_ptr(sptr, elm);
+            if(memb_ptr && elm->default_value_cmp
+               && elm->default_value_cmp(memb_ptr) == 0) {
+                memb_ptr = 0;   /* Do not encode default value. */
+            }
+            ret |= asn_put_few_bits(&extadds, memb_ptr ? 1 : 0, 1);
+        }
+        if(ret < 0) ASN__ENCODE_FAILED;
+
+        asn_put_aligned_flush(&extadds);
+        computed_size += extadds.flushed_bytes;
+
+        /* Now, encode extensions */
+        for(edx = specs->first_extension; edx < td->elements_count; edx++) {
+            asn_TYPE_member_t *elm = &td->elements[edx];
+            const void *memb_ptr = element_ptr(sptr, elm);
+
+            if(memb_ptr) {
+                if(elm->default_value_cmp
+                   && elm->default_value_cmp(memb_ptr) == 0) {
+                    /* Do not encode default value. */
+                } else {
+                    ssize_t wrote = oer_open_type_put(
+                        elm->type, elm->encoding_constraints.oer_constraints,
+                        memb_ptr, cb, app_key);
+                    if(wrote == -1) {
+                        ASN__ENCODE_FAILED;
+                    }
+                    computed_size += wrote;
+                }
+            } else if(!elm->optional) {
+                ASN__ENCODE_FAILED;
+            }
+        }
+    }   /* if(has_extensions) */
+
+
+    {
+        asn_enc_rval_t er = {0, 0, 0};
+        er.encoded = computed_size;
+        ASN__ENCODED_OK(er);
+    }
+}
+
+#endif  /* ASN_DISABLE_OER_SUPPORT */

+ 1147 - 0
lib/asn1/constr_SET.c

@@ -0,0 +1,1147 @@
+/*
+ * Copyright (c) 2003-2017 Lev Walkin <vlm@lionet.info>.
+ * All rights reserved.
+ * Redistribution and modifications are permitted subject to BSD license.
+ */
+#include <asn_internal.h>
+#include <constr_SET.h>
+
+/* Check that all the mandatory members are present */
+static int _SET_is_populated(const asn_TYPE_descriptor_t *td, const void *st);
+
+/*
+ * 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 <TLV>'s L, even if the buffer size allows
+ * reading more data.
+ * For example, consider the buffer containing the following TLVs:
+ * <T:5><L:1><V> <T:6>...
+ * The TLV length clearly indicates that one byte is expected in V, but
+ * if the V processor returns with "want more data" even if the buffer
+ * contains way more data than the V processor have seen.
+ */
+#define	SIZE_VIOLATION	(ctx->left >= 0 && (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
+#define	NEXT_PHASE(ctx)	do {			\
+		ctx->phase++;			\
+		ctx->step = 0;			\
+	} while(0)
+
+/*
+ * Return a standardized complex structure.
+ */
+#undef	RETURN
+#define	RETURN(_code)	do {			\
+		rval.code = _code;		\
+		rval.consumed = consumed_myself;\
+		return rval;			\
+	} while(0)
+
+/*
+ * Tags are canonically sorted in the tag2element map.
+ */
+static int
+_t2e_cmp(const void *ap, const void *bp) {
+	const 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)
+			return 0;
+		else if(a_value < b_value)
+			return -1;
+		else
+			return 1;
+	} else if(a_class < b_class) {
+		return -1;
+	} else {
+		return 1;
+	}
+}
+
+/*
+ * The decoder of the SET type.
+ */
+asn_dec_rval_t
+SET_decode_ber(const asn_codec_ctx_t *opt_codec_ctx,
+               const asn_TYPE_descriptor_t *td, void **struct_ptr,
+               const void *ptr, size_t size, int tag_mode) {
+    /*
+	 * Bring closer parts of structure description.
+	 */
+	const asn_SET_specifics_t *specs = (const asn_SET_specifics_t *)td->specifics;
+	const 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 */
+	size_t edx;			/* SET element's index */
+
+	ASN_DEBUG("Decoding %s as SET", td->name);
+
+	if(ASN__STACK_OVERFLOW_CHECK(opt_codec_ctx))
+		ASN__DECODE_FAILED;
+
+	/*
+	 * 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 advertised %ld bytes, "
+			"buffer contains %ld", (long)ctx->left, (long)size);
+
+		/* Fall through */
+	case 1:
+		/*
+		 * PHASE 1.
+		 * From the place where we've left it previously,
+		 * try to decode the next member from the list of
+		 * this structure's elements.
+		 * Note that elements in BER may arrive out of
+		 * order, yet DER mandates that they shall arive in the
+		 * canonical order of their tags. So, there is a room
+		 * for optimization.
+		 */
+	  for(;; ctx->step = 0) {
+		const asn_TYPE_tag2member_t *t2m;
+		asn_TYPE_tag2member_t key;
+		void *memb_ptr;		/* Pointer to the member */
+		void **memb_ptr2;	/* Pointer to that pointer */
+		ssize_t tag_len;	/* Length of TLV's T */
+
+		if(ctx->step & 1) {
+			edx = ctx->step >> 1;
+			goto microphase2;
+		}
+
+		/*
+		 * MICROPHASE 1: Synchronize decoding.
+		 */
+
+		if(ctx->left == 0)
+			/*
+			 * No more things to decode.
+			 * Exit out of here and check whether all mandatory
+			 * elements have been received (in the next phase).
+			 */
+			break;
+
+		/*
+		 * Fetch the T from TLV.
+		 */
+		tag_len = ber_fetch_tag(ptr, LEFT, &tlv_tag);
+		switch(tag_len) {
+		case 0: if(!SIZE_VIOLATION) RETURN(RC_WMORE);
+			/* Fall through */
+		case -1: RETURN(RC_FAIL);
+		}
+
+		if(ctx->left < 0 && ((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.
+				 * Invoke the generic finalization function.
+				 */
+				goto phase3;
+			}
+		}
+
+		key.el_tag = tlv_tag;
+		t2m = (const asn_TYPE_tag2member_t *)bsearch(&key,
+				specs->tag2el, specs->tag2el_count,
+				sizeof(specs->tag2el[0]), _t2e_cmp);
+		if(t2m) {
+			/*
+			 * Found the element corresponding to the tag.
+			 */
+			edx = t2m->el_no;
+			ctx->step = (edx << 1) + 1;
+			ASN_DEBUG("Got tag %s (%s), edx %" ASN_PRI_SSIZE "",
+				ber_tlv_tag_string(tlv_tag), td->name, edx);
+		} else if(specs->extensible == 0) {
+			ASN_DEBUG("Unexpected tag %s "
+				"in non-extensible SET %s",
+				ber_tlv_tag_string(tlv_tag), td->name);
+			RETURN(RC_FAIL);
+		} else {
+			/* Skip this tag */
+			ssize_t skip;
+
+			ASN_DEBUG("Skipping unknown tag %s",
+				ber_tlv_tag_string(tlv_tag));
+
+			skip = ber_skip_length(opt_codec_ctx,
+				BER_TLV_CONSTRUCTED(ptr),
+				(const char *)ptr + tag_len, LEFT - tag_len);
+
+			switch(skip) {
+			case 0: if(!SIZE_VIOLATION) RETURN(RC_WMORE);
+				/* Fall through */
+			case -1: RETURN(RC_FAIL);
+			}
+
+			ADVANCE(skip + tag_len);
+			continue;  /* Try again with the next tag */
+		}
+
+		/*
+		 * MICROPHASE 2: Invoke the member-specific decoder.
+		 */
+	microphase2:
+
+		/*
+		 * Check for duplications: must not overwrite
+		 * already decoded elements.
+		 */
+		if(ASN_SET_ISPRESENT2((char *)st + specs->pres_offset, edx)) {
+			ASN_DEBUG("SET %s: Duplicate element %s (%" ASN_PRI_SSIZE ")",
+				td->name, elements[edx].name, edx);
+			RETURN(RC_FAIL);
+		}
+		
+		/*
+		 * Compute the position of the member inside a structure,
+		 * and also a type of containment (it may be contained
+		 * as pointer or using inline inclusion).
+		 */
+		if(elements[edx].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->op->ber_decoder(opt_codec_ctx,
+				elements[edx].type,
+				memb_ptr2, ptr, LEFT,
+				elements[edx].tag_mode);
+		switch(rval.code) {
+		case RC_OK:
+			ASN_SET_MKPRESENT((char *)st + specs->pres_offset, edx);
+			break;
+		case RC_WMORE: /* More data expected */
+			if(!SIZE_VIOLATION) {
+				ADVANCE(rval.consumed);
+				RETURN(RC_WMORE);
+			}
+			/* Fall through */
+		case RC_FAIL: /* Fatal error */
+			RETURN(RC_FAIL);
+		} /* switch(rval) */
+		
+		ADVANCE(rval.consumed);
+	  }	/* for(all structure members) */
+
+	phase3:
+		ctx->phase = 3;
+		/* Fall through */
+	case 3:
+	case 4:	/* Only 00 is expected */
+		ASN_DEBUG("SET %s Leftover: %ld, size = %ld",
+			td->name, (long)ctx->left, (long)size);
+
+		/*
+		 * Skip everything until the end of the SET.
+		 */
+		while(ctx->left) {
+			ssize_t tl, ll;
+
+			tl = ber_fetch_tag(ptr, LEFT, &tlv_tag);
+			switch(tl) {
+			case 0: if(!SIZE_VIOLATION) RETURN(RC_WMORE);
+				/* Fall through */
+			case -1: RETURN(RC_FAIL);
+			}
+
+			/*
+			 * If expected <0><0>...
+			 */
+			if(ctx->left < 0
+				&& ((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(specs->extensible == 0 || ctx->phase == 4) {
+				ASN_DEBUG("Unexpected continuation "
+					"of a non-extensible type %s "
+					"(ptr=%02x)",
+					td->name, *(const uint8_t *)ptr);
+				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);
+		}
+
+		ctx->phase = 5;
+        /* Fall through */
+	case 5:
+		/* Check that all mandatory elements are present. */
+		if(!_SET_is_populated(td, st))
+			RETURN(RC_FAIL);
+
+		NEXT_PHASE(ctx);
+	}
+	
+	RETURN(RC_OK);
+}
+
+static int
+_SET_is_populated(const asn_TYPE_descriptor_t *td, const void *st) {
+    const asn_SET_specifics_t *specs = (const asn_SET_specifics_t *)td->specifics;
+	size_t edx;
+
+	/*
+	 * Check that all mandatory elements are present.
+	 */
+	for(edx = 0; edx < td->elements_count;
+		edx += (8 * sizeof(specs->_mandatory_elements[0]))) {
+		unsigned int midx, pres, must;
+
+		midx = edx/(8 * sizeof(specs->_mandatory_elements[0]));
+        pres = ((const unsigned int *)((const char *)st
+                                       + specs->pres_offset))[midx];
+        must = sys_ntohl(specs->_mandatory_elements[midx]);
+
+		if((pres & must) == must) {
+			/*
+			 * Yes, everything seems to be in place.
+			 */
+		} else {
+			ASN_DEBUG("One or more mandatory elements "
+				"of a SET %s %d (%08x.%08x)=%08x "
+				"are not present",
+				td->name,
+				midx,
+				pres,
+				must,
+				(~(pres & must) & must)
+			);
+			return 0;
+		}
+	}
+
+	return 1;
+}
+
+/*
+ * The DER encoder of the SET type.
+ */
+asn_enc_rval_t
+SET_encode_der(const asn_TYPE_descriptor_t *td, const void *sptr, int tag_mode,
+               ber_tlv_tag_t tag, asn_app_consume_bytes_f *cb, void *app_key) {
+    const asn_SET_specifics_t *specs = (const asn_SET_specifics_t *)td->specifics;
+	size_t computed_size = 0;
+	asn_enc_rval_t er;
+	int t2m_build_own = (specs->tag2el_count != td->elements_count);
+	const asn_TYPE_tag2member_t *t2m;
+	asn_TYPE_tag2member_t *t2m_build;
+	size_t t2m_count;
+	ssize_t ret;
+	size_t edx;
+
+	/*
+	 * Use existing, or build our own tags map.
+	 */
+	if(t2m_build_own) {
+        t2m_build = (asn_TYPE_tag2member_t *)CALLOC(td->elements_count,
+                                                    sizeof(t2m_build[0]));
+        if(!t2m_build) ASN__ENCODE_FAILED;
+		t2m_count = 0;
+	} else {
+		t2m_build = NULL;
+		/*
+		 * There is no untagged CHOICE in this SET.
+		 * Employ existing table.
+		 */
+	}
+
+	/*
+	 * Gather the length of the underlying members sequence.
+	 */
+	for(edx = 0; edx < td->elements_count; edx++) {
+		asn_TYPE_member_t *elm = &td->elements[edx];
+		asn_enc_rval_t tmper;
+		const void *memb_ptr_dontuse; /* Pointer to the member */
+        const void *const *memb_ptr2; /* Pointer to that pointer */
+
+        /*
+		 * Compute the length of the encoding of this member.
+		 */
+		if(elm->flags & ATF_POINTER) {
+            memb_ptr2 =
+                (const void *const *)((const char *)sptr + elm->memb_offset);
+            if(!*memb_ptr2) {
+				if(!elm->optional) {
+					/* Mandatory elements missing */
+					FREEMEM(t2m_build);
+					ASN__ENCODE_FAILED;
+				}
+				if(t2m_build) {
+					t2m_build[t2m_count].el_no = edx;
+					t2m_build[t2m_count].el_tag = 0;
+					t2m_count++;
+				}
+				continue;
+			}
+		} else {
+            memb_ptr_dontuse =
+                (const void *)((const char *)sptr + elm->memb_offset);
+            memb_ptr2 = &memb_ptr_dontuse; /* Only use of memb_ptr_dontuse */
+		}
+
+		/* Eliminate default values */
+        if(elm->default_value_cmp && elm->default_value_cmp(*memb_ptr2) == 0) {
+            if(t2m_build) {
+				t2m_build[t2m_count].el_no = edx;
+				t2m_build[t2m_count].el_tag = 0;
+				t2m_count++;
+			}
+			continue;
+		}
+
+		tmper = elm->type->op->der_encoder(elm->type, *memb_ptr2,
+			elm->tag_mode, elm->tag,
+			0, 0);
+		if(tmper.encoded == -1)
+			return tmper;
+		computed_size += tmper.encoded;
+
+		/*
+		 * Remember the outmost tag of this member.
+		 */
+		if(t2m_build) {
+			t2m_build[t2m_count].el_no = edx;
+			t2m_build[t2m_count].el_tag = asn_TYPE_outmost_tag(
+				elm->type, *memb_ptr2, elm->tag_mode, elm->tag);
+			t2m_count++;
+		} else {
+			/*
+			 * No dynamic sorting is necessary.
+			 */
+		}
+	}
+
+	/*
+	 * Finalize order of the components.
+	 */
+	if(t2m_build) {
+		/*
+		 * Sort the underlying members according to their
+		 * canonical tags order. DER encoding mandates it.
+		 */
+		qsort(t2m_build, t2m_count, sizeof(specs->tag2el[0]), _t2e_cmp);
+		t2m = t2m_build;
+	} else {
+		/*
+		 * Tags are already sorted by the compiler.
+		 */
+		t2m = specs->tag2el;
+		t2m_count = specs->tag2el_count;
+	}
+	assert(t2m_count == td->elements_count);
+
+	/*
+	 * Encode the TLV for the sequence itself.
+	 */
+	ret = der_write_tags(td, computed_size, tag_mode, 1, tag, cb, app_key);
+	if(ret == -1) {
+		FREEMEM(t2m_build);
+		ASN__ENCODE_FAILED;
+	}
+	er.encoded = computed_size + ret;
+
+	if(!cb) {
+		FREEMEM(t2m_build);
+		ASN__ENCODED_OK(er);
+    }
+
+	/*
+	 * Encode all members.
+	 */
+	for(edx = 0; edx < td->elements_count; edx++) {
+		asn_TYPE_member_t *elm;
+		asn_enc_rval_t tmper;
+
+		const void *memb_ptr_dontuse;		/* Pointer to the member */
+        const void *const *memb_ptr2;       /* Pointer to that pointer */
+
+        /* Encode according to the tag order */
+		elm = &td->elements[t2m[edx].el_no];
+
+		if(elm->flags & ATF_POINTER) {
+            memb_ptr2 =
+                (const void *const *)((const char *)sptr + elm->memb_offset);
+            if(!*memb_ptr2) continue;
+		} else {
+            memb_ptr_dontuse =
+                (const void *)((const char *)sptr + elm->memb_offset);
+            memb_ptr2 = &memb_ptr_dontuse; /* Only use of memb_ptr_dontuse */
+		}
+
+		/* Eliminate default values */
+        if(elm->default_value_cmp && elm->default_value_cmp(*memb_ptr2) == 0)
+            continue;
+
+		tmper = elm->type->op->der_encoder(elm->type, *memb_ptr2,
+			elm->tag_mode, elm->tag, cb, app_key);
+		if(tmper.encoded == -1)
+			return tmper;
+		computed_size -= tmper.encoded;
+	}
+
+	if(computed_size != 0) {
+		/*
+		 * Encoded size is not equal to the computed size.
+		 */
+		FREEMEM(t2m_build);
+		ASN__ENCODE_FAILED;
+	}
+
+    FREEMEM(t2m_build);
+	ASN__ENCODED_OK(er);
+}
+
+#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_decode_xer(const asn_codec_ctx_t *opt_codec_ctx,
+               const 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.
+	 */
+	const asn_SET_specifics_t *specs = (const asn_SET_specifics_t *)td->specifics;
+	const 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 */
+	size_t edx;			/* Element index */
+
+	/*
+	 * 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 */
+		const asn_TYPE_member_t *elm;
+
+		/*
+		 * Go inside the inner member of a set.
+		 */
+		if(ctx->phase == 2) {
+			asn_dec_rval_t tmprval;
+			void *memb_ptr_dontuse; /* Pointer to the member */
+			void **memb_ptr2;	/* Pointer to that pointer */
+
+			if(ASN_SET_ISPRESENT2((char *)st + specs->pres_offset,
+					edx)) {
+				ASN_DEBUG("SET %s: Duplicate element %s (%" ASN_PRI_SSIZE ")",
+				td->name, elements[edx].name, edx);
+				RETURN(RC_FAIL);
+			}
+
+			elm = &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_dontuse = (char *)st + elm->memb_offset;
+				memb_ptr2 = &memb_ptr_dontuse; /* Only use of memb_ptr_dontuse */
+			}
+
+			/* Invoke the inner type decoder, m.b. multiple times */
+			tmprval = elm->type->op->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 */
+			ASN_SET_MKPRESENT((char *)st + specs->pres_offset, edx);
+			ASN_DEBUG("XER/SET 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: tcv = %d, ph=%d", tcv, ctx->phase);
+
+		/* Skip the extensions section */
+		if(ctx->phase == 3) {
+			switch(xer_skip_unknown(tcv, &ctx->left)) {
+			case -1:
+				ctx->phase = 4;
+				RETURN(RC_FAIL);
+			case 1:
+				ctx->phase = 1;
+				/* Fall through */
+			case 0:
+				XER_ADVANCE(ch_size);
+				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(_SET_is_populated(td, st)) {
+					XER_ADVANCE(ch_size);
+					ctx->phase = 4;	/* Phase out */
+					RETURN(RC_OK);
+				} else {
+					ASN_DEBUG("Premature end of XER SET");
+					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/SET: tcv=%d, ph=%d", tcv, ctx->phase);
+			if(ctx->phase != 1)
+				break;	/* Really unexpected */
+
+			/*
+			 * Search which member corresponds to this tag.
+			 */
+			for(edx = 0; edx < td->elements_count; edx++) {
+				switch(xer_check_tag(buf_ptr, ch_size,
+					elements[edx].name)) {
+				case XCT_BOTH:
+				case XCT_OPENING:
+					/*
+					 * Process this member.
+					 */
+					ctx->step = edx;
+					ctx->phase = 2;
+					break;
+				case XCT_UNKNOWN_OP:
+				case XCT_UNKNOWN_BO:
+					continue;
+				default:
+					edx = td->elements_count;
+					break;	/* Phase out */
+				}
+				break;
+			}
+			if(edx != td->elements_count)
+				continue;
+
+			/* It is expected extension */
+			if(specs->extensible) {
+				ASN_DEBUG("Got anticipated extension");
+				/*
+				 * Check for (XCT_BOTH or XCT_UNKNOWN_BO)
+				 * By using a mask. Only record a pure
+				 * <opening> tags.
+				 */
+				if(tcv & XCT_CLOSING) {
+					/* Found </extension> 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 SET, expected \"%s\"",
+			xml_tag);
+		break;
+	}
+
+	ctx->phase = 4;	/* "Phase out" on hard failure */
+	RETURN(RC_FAIL);
+}
+
+asn_enc_rval_t
+SET_encode_xer(const asn_TYPE_descriptor_t *td, const void *sptr, int ilevel,
+               enum xer_encoder_flags_e flags, asn_app_consume_bytes_f *cb,
+               void *app_key) {
+    const asn_SET_specifics_t *specs = (const asn_SET_specifics_t *)td->specifics;
+	asn_enc_rval_t er;
+	int xcan = (flags & XER_F_CANONICAL);
+	const asn_TYPE_tag2member_t *t2m = specs->tag2el_cxer;
+	size_t t2m_count = specs->tag2el_cxer_count;
+	size_t edx;
+
+	if(!sptr)
+		ASN__ENCODE_FAILED;
+
+	assert(t2m_count == td->elements_count);
+
+	er.encoded = 0;
+
+	for(edx = 0; edx < t2m_count; edx++) {
+		asn_enc_rval_t tmper;
+		asn_TYPE_member_t *elm;
+        const void *memb_ptr;
+        const char *mname;
+		size_t mlen;
+
+		elm = &td->elements[t2m[edx].el_no];
+		mname = elm->name;
+		mlen = strlen(elm->name);
+
+		if(elm->flags & ATF_POINTER) {
+            memb_ptr =
+                *(const void *const *)((const char *)sptr + elm->memb_offset);
+            if(!memb_ptr) {
+				if(elm->optional)
+					continue;
+				/* Mandatory element missing */
+				ASN__ENCODE_FAILED;
+			}
+		} else {
+            memb_ptr = (const void *)((const 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->op->xer_encoder(elm->type, memb_ptr,
+				ilevel + 1, flags, cb, app_key);
+		if(tmper.encoded == -1) return tmper;
+		er.encoded += tmper.encoded;
+
+		ASN__CALLBACK3("</", 2, mname, mlen, ">", 1);
+	}
+
+	if(!xcan) ASN__TEXT_INDENT(1, ilevel - 1);
+
+	ASN__ENCODED_OK(er);
+cb_failed:
+	ASN__ENCODE_FAILED;
+}
+
+int
+SET_print(const asn_TYPE_descriptor_t *td, const void *sptr, int ilevel,
+          asn_app_consume_bytes_f *cb, void *app_key) {
+    size_t edx;
+	int ret;
+
+	if(!sptr) return (cb("<absent>", 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 <absent> line */
+				/* Fall through */
+			}
+		} else {
+			memb_ptr = (const void *)((const char *)sptr + elm->memb_offset);
+		}
+
+		_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->op->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_free(const asn_TYPE_descriptor_t *td, void *ptr,
+         enum asn_struct_free_method method) {
+    size_t edx;
+
+	if(!td || !ptr)
+		return;
+
+	ASN_DEBUG("Freeing %s as SET", 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 *)ptr + elm->memb_offset);
+			if(memb_ptr)
+				ASN_STRUCT_FREE(*elm->type, memb_ptr);
+		} else {
+			memb_ptr = (void *)((char *)ptr + elm->memb_offset);
+			ASN_STRUCT_FREE_CONTENTS_ONLY(*elm->type, memb_ptr);
+		}
+	}
+
+    switch(method) {
+    case ASFM_FREE_EVERYTHING:
+        FREEMEM(ptr);
+        break;
+    case ASFM_FREE_UNDERLYING:
+        break;
+    case ASFM_FREE_UNDERLYING_AND_RESET:
+        memset(ptr, 0,
+               ((const asn_SET_specifics_t *)(td->specifics))->struct_size);
+        break;
+    }
+}
+
+int
+SET_constraint(const asn_TYPE_descriptor_t *td, const void *sptr,
+               asn_app_constraint_failed_f *ctfailcb, void *app_key) {
+    size_t 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->encoding_constraints.general_constraints) {
+			return elm->encoding_constraints.general_constraints(
+					elm->type, memb_ptr, ctfailcb, app_key);
+		} else {
+			return elm->type->encoding_constraints.general_constraints(
+					elm->type, memb_ptr, ctfailcb, app_key);
+		}
+	}
+
+	return 0;
+}
+
+int
+SET_compare(const asn_TYPE_descriptor_t *td, const void *aptr,
+            const void *bptr) {
+    size_t edx;
+
+	for(edx = 0; edx < td->elements_count; edx++) {
+		asn_TYPE_member_t *elm = &td->elements[edx];
+		const void *amemb;
+		const void *bmemb;
+		int ret;
+
+		if(elm->flags & ATF_POINTER) {
+            amemb =
+                *(const void *const *)((const char *)aptr + elm->memb_offset);
+            bmemb =
+                *(const void *const *)((const char *)bptr + elm->memb_offset);
+            if(!amemb) {
+                if(!bmemb) continue;
+                return -1;
+            } else if(!bmemb) {
+                return 1;
+            }
+		} else {
+            amemb = (const void *)((const char *)aptr + elm->memb_offset);
+            bmemb = (const void *)((const char *)bptr + elm->memb_offset);
+		}
+
+        ret = elm->type->op->compare_struct(elm->type, amemb, bmemb);
+        if(ret != 0) return ret;
+    }
+
+    return 0;
+}
+
+
+asn_TYPE_operation_t asn_OP_SET = {
+	SET_free,
+	SET_print,
+	SET_compare,
+	SET_decode_ber,
+	SET_encode_der,
+	SET_decode_xer,
+	SET_encode_xer,
+	0,	/* SET_decode_oer */
+	0,	/* SET_encode_oer */
+	0,	/* SET_decode_uper */
+	0,	/* SET_encode_uper */
+	SET_random_fill,
+	0	/* Use generic outmost tag fetcher */
+};
+
+
+asn_random_fill_result_t
+SET_random_fill(const asn_TYPE_descriptor_t *td, void **sptr,
+                   const asn_encoding_constraints_t *constr,
+                   size_t max_length) {
+    const asn_SET_specifics_t *specs =
+        (const asn_SET_specifics_t *)td->specifics;
+    asn_random_fill_result_t result_ok = {ARFILL_OK, 0};
+    asn_random_fill_result_t result_failed = {ARFILL_FAILED, 0};
+    asn_random_fill_result_t result_skipped = {ARFILL_SKIPPED, 0};
+    void *st = *sptr;
+    size_t edx;
+
+    if(max_length == 0) return result_skipped;
+
+    (void)constr;
+
+    if(st == NULL) {
+        st = CALLOC(1, specs->struct_size);
+        if(st == NULL) {
+            return result_failed;
+        }
+    }
+
+    for(edx = 0; edx < td->elements_count; edx++) {
+        const asn_TYPE_member_t *elm = &td->elements[edx];
+        void *memb_ptr;   /* Pointer to the member */
+        void **memb_ptr2; /* Pointer to that pointer */
+        asn_random_fill_result_t tmpres;
+
+        if(elm->optional && asn_random_between(0, 4) == 2) {
+            /* Sometimes decide not to fill the optional value */
+            continue;
+        }
+
+        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;
+        }
+
+        tmpres = elm->type->op->random_fill(
+            elm->type, memb_ptr2, &elm->encoding_constraints,
+            max_length > result_ok.length ? max_length - result_ok.length : 0);
+        switch(tmpres.code) {
+        case ARFILL_OK:
+            result_ok.length += tmpres.length;
+            continue;
+        case ARFILL_SKIPPED:
+            assert(!(elm->flags & ATF_POINTER) || *memb_ptr2 == NULL);
+            continue;
+        case ARFILL_FAILED:
+            if(st == *sptr) {
+                ASN_STRUCT_RESET(*td, st);
+            } else {
+                ASN_STRUCT_FREE(*td, st);
+            }
+            return tmpres;
+        }
+    }
+
+    *sptr = st;
+
+    return result_ok;
+}
+

+ 87 - 0
lib/asn1/constr_SET.h

@@ -0,0 +1,87 @@
+/*-
+ * Copyright (c) 2003-2017 Lev Walkin <vlm@lionet.info>. All rights reserved.
+ * Redistribution and modifications are permitted subject to BSD license.
+ */
+#ifndef	_CONSTR_SET_H_
+#define	_CONSTR_SET_H_
+
+#include <asn_application.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+typedef struct asn_SET_specifics_s {
+	/*
+	 * Target structure description.
+	 */
+	unsigned struct_size;       /* Size of the target structure. */
+	unsigned ctx_offset;        /* Offset of the asn_struct_ctx_t member */
+	unsigned pres_offset;       /* Offset of _presence_map member */
+
+	/*
+	 * Tags to members mapping table (sorted).
+	 * Sometimes suitable for DER encoding (untagged CHOICE is present);
+	 * if so, tag2el_count will be greater than td->elements_count.
+	 */
+	const asn_TYPE_tag2member_t *tag2el;
+	unsigned tag2el_count;
+
+	/*
+	 * Tags to members mapping table, second edition.
+	 * Suitable for CANONICAL-XER encoding.
+	 */
+	const asn_TYPE_tag2member_t *tag2el_cxer;
+	unsigned tag2el_cxer_count;
+
+	/*
+	 * Extensions-related stuff.
+	 */
+	int extensible;                             /* Whether SET is extensible */
+	const unsigned int *_mandatory_elements;    /* Bitmask of mandatory ones */
+} asn_SET_specifics_t;
+
+/*
+ * A set specialized functions dealing with the SET type.
+ */
+asn_struct_free_f SET_free;
+asn_struct_print_f SET_print;
+asn_struct_compare_f SET_compare;
+asn_constr_check_f SET_constraint;
+ber_type_decoder_f SET_decode_ber;
+der_type_encoder_f SET_encode_der;
+xer_type_decoder_f SET_decode_xer;
+xer_type_encoder_f SET_encode_xer;
+per_type_decoder_f SET_decode_uper;
+per_type_encoder_f SET_encode_uper;
+asn_random_fill_f  SET_random_fill;
+extern asn_TYPE_operation_t asn_OP_SET;
+
+/***********************
+ * Some handy helpers. *
+ ***********************/
+
+/*
+ * Figure out whether the SET member indicated by PR_x has already been decoded.
+ * It is very simple bitfield test, despite its visual complexity.
+ */
+#define	ASN_SET_ISPRESENT(set_ptr, PR_x)				\
+	ASN_SET_ISPRESENT2(&((set_ptr)->_presence_map), PR_x)
+#define	ASN_SET_ISPRESENT2(map_ptr, PR_x)				\
+	(((unsigned int *)(map_ptr))					\
+		[(PR_x)	/ (8 * sizeof(unsigned int))]			\
+		& (1u << ((8 * sizeof(unsigned int)) - 1			\
+		- ((PR_x) % (8 * sizeof(unsigned int))))))
+
+#define	ASN_SET_MKPRESENT(map_ptr, PR_x)				\
+	(((unsigned int *)(map_ptr))					\
+		[(PR_x)	/ (8 * sizeof(unsigned int))]			\
+		|= (1u << ((8 * sizeof(unsigned int)) - 1		\
+		- ((PR_x) % (8 * sizeof(unsigned int))))))
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif	/* _CONSTR_SET_H_ */

+ 80 - 0
lib/asn1/constr_TYPE.c

@@ -0,0 +1,80 @@
+/*-
+ * Copyright (c) 2003, 2004 Lev Walkin <vlm@lionet.info>. All rights reserved.
+ * Redistribution and modifications are permitted subject to BSD license.
+ */
+#include <asn_internal.h>
+#include <constr_TYPE.h>
+#include <errno.h>
+
+/*
+ * 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->op->outmost_tag(type_descriptor, struct_ptr, 0, 0);
+}
+
+/*
+ * Print the target language's structure in human readable form.
+ */
+int
+asn_fprint(FILE *stream, const 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->op->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);
+}

+ 260 - 0
lib/asn1/constr_TYPE.h

@@ -0,0 +1,260 @@
+/*
+ * Copyright (c) 2003-2017 Lev Walkin <vlm@lionet.info>. 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 <ber_tlv_length.h>
+#include <ber_tlv_tag.h>
+
+#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 <ber_decoder.h>	/* Basic Encoding Rules decoder */
+#include <der_encoder.h>	/* Distinguished Encoding Rules encoder */
+#include <xer_decoder.h>	/* Decoder of XER (XML, text) */
+#include <xer_encoder.h>	/* Encoder into XER (XML, text) */
+#include <per_decoder.h>	/* Packet Encoding Rules decoder */
+#include <per_encoder.h>	/* Packet Encoding Rules encoder */
+#include <constraints.h>	/* Subtype constraints support */
+#include <asn_random_fill.h>	/* Random structures support */
+
+#ifdef  ASN_DISABLE_OER_SUPPORT
+typedef void (oer_type_decoder_f)(void);
+typedef void (oer_type_encoder_f)(void);
+typedef void asn_oer_constraints_t;
+#else
+#include <oer_decoder.h>	/* Octet Encoding Rules encoder */
+#include <oer_encoder.h>	/* Octet Encoding Rules encoder */
+#endif
+
+/*
+ * Free the structure according to its specification.
+ * Use one of ASN_STRUCT_{FREE,RESET,CONTENTS_ONLY} macros instead.
+ * Do not use directly.
+ */
+enum asn_struct_free_method {
+    ASFM_FREE_EVERYTHING,   /* free(struct_ptr) and underlying members */
+    ASFM_FREE_UNDERLYING,   /* free underlying members */
+    ASFM_FREE_UNDERLYING_AND_RESET   /* FREE_UNDERLYING + memset(0) */
+};
+typedef void (asn_struct_free_f)(
+		const struct asn_TYPE_descriptor_s *type_descriptor,
+		void *struct_ptr, enum asn_struct_free_method);
+
+/*
+ * Free the structure including freeing the memory pointed to by ptr itself.
+ */
+#define ASN_STRUCT_FREE(asn_DEF, ptr) \
+    (asn_DEF).op->free_struct(&(asn_DEF), (ptr), ASFM_FREE_EVERYTHING)
+
+/*
+ * Free the memory used by the members of the structure without freeing the
+ * the structure pointer itself.
+ * ZERO-OUT the structure to the safe clean state.
+ * (Retaining the pointer may be useful in case the structure is allocated
+ *  statically or arranged on the stack, yet its elements are dynamic.)
+ */
+#define ASN_STRUCT_RESET(asn_DEF, ptr) \
+    (asn_DEF).op->free_struct(&(asn_DEF), (ptr), ASFM_FREE_UNDERLYING_AND_RESET)
+
+/*
+ * Free memory used by the members of the structure without freeing
+ * the structure pointer itself.
+ * (Retaining the pointer may be useful in case the structure is allocated
+ *  statically or arranged on the stack, yet its elements are dynamic.)
+ * AVOID using it in the application code;
+ * Use a safer ASN_STRUCT_RESET() instead.
+ */
+#define ASN_STRUCT_FREE_CONTENTS_ONLY(asn_DEF, ptr) \
+    (asn_DEF).op->free_struct(&(asn_DEF), (ptr), ASFM_FREE_UNDERLYING)
+
+/*
+ * Print the structure according to its specification.
+ */
+typedef int(asn_struct_print_f)(
+    const struct asn_TYPE_descriptor_s *type_descriptor,
+    const void *struct_ptr,
+    int level, /* Indentation level */
+    asn_app_consume_bytes_f *callback, void *app_key);
+
+/*
+ * Compare two structs between each other.
+ * Returns <0 if struct_A is "smaller" than struct_B, >0 if "greater",
+ * and =0 if "equal to", for some type-specific, stable definition of
+ * "smaller", "greater" and "equal to".
+ */
+typedef int (asn_struct_compare_f)(
+		const struct asn_TYPE_descriptor_s *type_descriptor,
+		const void *struct_A,
+		const void *struct_B);
+
+/*
+ * 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;
+
+/*
+ * Fetch the desired type of the Open Type based on the
+ * Information Object Set driven constraints.
+ */
+typedef struct asn_type_selector_result_s {
+    const struct asn_TYPE_descriptor_s *type_descriptor; /* Type encoded. */
+    unsigned presence_index; /* Associated choice variant. */
+} asn_type_selector_result_t;
+typedef asn_type_selector_result_t(asn_type_selector_f)(
+    const struct asn_TYPE_descriptor_s *parent_type_descriptor,
+    const void *parent_structure_ptr);
+
+/*
+ * Generalized functions for dealing with the speciic type.
+ * May be directly invoked by applications.
+ */
+typedef struct asn_TYPE_operation_s {
+    asn_struct_free_f *free_struct;     /* Free the structure */
+    asn_struct_print_f *print_struct;   /* Human readable output */
+    asn_struct_compare_f *compare_struct; /* Compare two structures */
+    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 */
+    oer_type_decoder_f *oer_decoder;      /* Generic OER decoder */
+    oer_type_encoder_f *oer_encoder;      /* Canonical OER encoder */
+    per_type_decoder_f *uper_decoder;     /* Unaligned PER decoder */
+    per_type_encoder_f *uper_encoder;     /* Unaligned PER encoder */
+    asn_random_fill_f *random_fill;       /* Initialize with a random value */
+    asn_outmost_tag_f *outmost_tag;       /* <optional, internal> */
+} asn_TYPE_operation_t;
+
+/*
+ * A constraints tuple specifying both the OER and PER constraints.
+ */
+typedef struct asn_encoding_constraints_s {
+    const struct asn_oer_constraints_s *oer_constraints;
+    const struct asn_per_constraints_s *per_constraints;
+    asn_constr_check_f *general_constraints;
+} asn_encoding_constraints_t;
+
+/*
+ * 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_TYPE_operation_t *op;
+
+    /***********************************************************************
+     * Internally useful members. Not to be used by applications directly. *
+     **********************************************************************/
+
+    /*
+     * Tags that are expected to occur.
+     */
+    const ber_tlv_tag_t *tags;      /* Effective tags sequence for this type */
+    unsigned tags_count;            /* Number of tags which are expected */
+    const ber_tlv_tag_t *all_tags;  /* Every tag for BER/containment */
+    unsigned all_tags_count;        /* Number of tags */
+
+    /* OER, PER, and general constraints */
+    asn_encoding_constraints_t encoding_constraints;
+
+    /*
+     * An ASN.1 production type members (members of SEQUENCE, SET, CHOICE).
+     */
+    struct asn_TYPE_member_s *elements;
+    unsigned 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, /* Open Type */
+    ATF_ANY_TYPE = 0x04   /* ANY type (deprecated!) */
+  };
+typedef struct asn_TYPE_member_s {
+    enum asn_TYPE_flags_e flags; /* Element's presentation flags */
+    unsigned optional;      /* Following optional members, including current */
+    unsigned 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_type_selector_f *type_selector;     /* IoS runtime type selector */
+    asn_encoding_constraints_t encoding_constraints;
+    int (*default_value_cmp)(const void *sptr); /* Compare DEFAULT <value> */
+    int (*default_value_set)(void **sptr);      /* Set DEFAULT <value> */
+    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 */
+    unsigned 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, relative */
+} asn_TYPE_tag2member_t;
+
+/*
+ * This function 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 */
+               const asn_TYPE_descriptor_t *td, /* ASN.1 type descriptor */
+               const void *struct_ptr);         /* Structure to be printed */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif	/* _CONSTR_TYPE_H_ */

+ 93 - 0
lib/asn1/constraints.c

@@ -0,0 +1,93 @@
+#include <asn_internal.h>
+#include <constraints.h>
+
+int
+asn_generic_no_constraint(const 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(const 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 {
+    const asn_TYPE_descriptor_t *failed_type;
+    const void *failed_struct_ptr;
+	char *errbuf;
+	size_t errlen;
+};
+
+static void
+_asn_i_ctfailcb(void *key, const 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("<broken vsnprintf>") - 1;
+		maxlen--;
+		arg->errlen = vlen < maxlen ? vlen : maxlen;
+		memcpy(arg->errbuf, "<broken vsnprintf>", arg->errlen);
+		arg->errbuf[arg->errlen] = 0;
+	}
+
+	return;
+}
+
+int
+asn_check_constraints(const 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->encoding_constraints.general_constraints(
+        type_descriptor, struct_ptr, _asn_i_ctfailcb, &arg);
+    if(ret == -1 && errlen) *errlen = arg.errlen;
+
+    return ret;
+}
+

+ 62 - 0
lib/asn1/constraints.h

@@ -0,0 +1,62 @@
+/*-
+ * Copyright (c) 2004-2017 Lev Walkin <vlm@lionet.info>. All rights reserved.
+ * Redistribution and modifications are permitted subject to BSD license.
+ */
+#ifndef	ASN1_CONSTRAINTS_VALIDATOR_H
+#define	ASN1_CONSTRAINTS_VALIDATOR_H
+
+#include <asn_system.h>		/* 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(
+    const 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)(
+    const 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 */

+ 194 - 0
lib/asn1/der_encoder.c

@@ -0,0 +1,194 @@
+/*-
+ * Copyright (c) 2003, 2004 Lev Walkin <vlm@lionet.info>. All rights reserved.
+ * Redistribution and modifications are permitted subject to BSD license.
+ */
+#include <asn_internal.h>
+#include <errno.h>
+
+static ssize_t der_write_TL(ber_tlv_tag_t tag, ber_tlv_len_t len,
+	asn_app_consume_bytes_f *cb, void *app_key, int constructed);
+
+/*
+ * The DER encoder of any type.
+ */
+asn_enc_rval_t
+der_encode(const asn_TYPE_descriptor_t *type_descriptor, const 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->op->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(const asn_TYPE_descriptor_t *type_descriptor,
+                     const 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->op->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(const 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) {
+#define ASN1_DER_MAX_TAGS_COUNT 4
+    ber_tlv_tag_t
+        tags_buf_scratch[ASN1_DER_MAX_TAGS_COUNT * sizeof(ber_tlv_tag_t)];
+    ssize_t lens[ASN1_DER_MAX_TAGS_COUNT * sizeof(ssize_t)];
+    const ber_tlv_tag_t *tags; /* Copy of tags stream */
+    int tags_count;            /* Number of tags */
+    size_t overall_length;
+    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(sd->tags_count + 1 > ASN1_DER_MAX_TAGS_COUNT) {
+        ASN_DEBUG("System limit %d on tags count", ASN1_DER_MAX_TAGS_COUNT);
+        return -1;
+    }
+
+	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_scratch;
+		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;
+
+	/*
+	 * 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("Encoding %s TL sequence (%d elements)", 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;
+}

+ 68 - 0
lib/asn1/der_encoder.h

@@ -0,0 +1,68 @@
+/*-
+ * Copyright (c) 2003-2017 Lev Walkin <vlm@lionet.info>. All rights reserved.
+ * Redistribution and modifications are permitted subject to BSD license.
+ */
+#ifndef	_DER_ENCODER_H_
+#define	_DER_ENCODER_H_
+
+#include <asn_application.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct asn_TYPE_descriptor_s;	/* Forward declaration */
+
+/*
+ * The DER encoder of any type. May be invoked by the application.
+ * Produces DER- and BER-compliant encoding. (DER is a subset of BER).
+ *
+ * NOTE: Use the ber_decode() function (ber_decoder.h) to decode data
+ * produced by der_encode().
+ */
+asn_enc_rval_t der_encode(const struct asn_TYPE_descriptor_s *type_descriptor,
+                          const 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(
+    const struct asn_TYPE_descriptor_s *type_descriptor,
+    const 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)(
+    const struct asn_TYPE_descriptor_s *type_descriptor,
+    const 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(const 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_ */

+ 0 - 0
lib/asn1/netinet/in.h


+ 152 - 0
lib/asn1/oer_decoder.c

@@ -0,0 +1,152 @@
+/*
+ * Copyright (c) 2017 Lev Walkin <vlm@lionet.info>. All rights reserved.
+ * Redistribution and modifications are permitted subject to BSD license.
+ */
+#include <asn_internal.h>
+#include <asn_codecs_prim.h>
+
+/*
+ * The OER decoder of any type.
+ */
+asn_dec_rval_t
+oer_decode(const asn_codec_ctx_t *opt_codec_ctx,
+           const 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->op->oer_decoder(opt_codec_ctx, type_descriptor, 0,
+		struct_ptr,	/* Pointer to the destination structure */
+		ptr, size	/* Buffer and its size */
+		);
+}
+
+/*
+ * Open Type is encoded as a length (#8.6) followed by that number of bytes.
+ * Since we're just skipping, reading the length would be enough.
+ */
+ssize_t
+oer_open_type_skip(const void *bufptr, size_t size) {
+    size_t len = 0;
+    return oer_fetch_length(bufptr, size, &len);
+}
+
+/*
+ * Read the Open Type (X.696 (08/2015), #30).
+ * RETURN VALUES:
+ *       0:     More data expected than bufptr contains.
+ *      -1:     Fatal error deciphering length.
+ *      >0:     Number of bytes used from bufptr.
+ */
+ssize_t
+oer_open_type_get(const asn_codec_ctx_t *opt_codec_ctx,
+                  const struct asn_TYPE_descriptor_s *td,
+                  const asn_oer_constraints_t *constraints, void **struct_ptr,
+                  const void *bufptr, size_t size) {
+    asn_dec_rval_t dr;
+    size_t container_len = 0;
+    ssize_t len_len;
+    enum asn_struct_free_method dispose_method =
+        (*struct_ptr) ? ASFM_FREE_UNDERLYING_AND_RESET : ASFM_FREE_EVERYTHING;
+
+    /* Get the size of a length determinant */
+    len_len = oer_fetch_length(bufptr, size, &container_len);
+    if(len_len <= 0) {
+        return len_len; /* Error or more data expected */
+    }
+
+    /*
+     * len_len can't be bigger than size, but size without len_len
+     * should be bigger or equal to container length
+     */
+    if(size - len_len < container_len) {
+        /* More data is expected */
+        return 0;
+    }
+
+    dr = td->op->oer_decoder(opt_codec_ctx, td, constraints, struct_ptr,
+                         (const uint8_t *)bufptr + len_len, container_len);
+    if(dr.code == RC_OK) {
+        return len_len + container_len;
+    } else {
+        /* Even if RC_WMORE, we can't get more data into a closed container. */
+        td->op->free_struct(td, *struct_ptr, dispose_method);
+        *struct_ptr = NULL;
+        return -1;
+    }
+}
+
+
+asn_dec_rval_t
+oer_decode_primitive(const asn_codec_ctx_t *opt_codec_ctx,
+                     const asn_TYPE_descriptor_t *td,
+                     const asn_oer_constraints_t *constraints, void **sptr,
+                     const void *ptr, size_t size) {
+    ASN__PRIMITIVE_TYPE_t *st = (ASN__PRIMITIVE_TYPE_t *)*sptr;
+    asn_dec_rval_t rval = {RC_OK, 0};
+    size_t expected_length = 0;
+    ssize_t len_len;
+
+    (void)td;
+    (void)opt_codec_ctx;
+    (void)constraints;
+
+    if(!st) {
+        st = (ASN__PRIMITIVE_TYPE_t *)(*sptr = CALLOC(
+                                           1, sizeof(ASN__PRIMITIVE_TYPE_t)));
+        if(!st) ASN__DECODE_FAILED;
+    }
+
+
+    /*
+     * X.696 (08/2015) #27.2
+     * Encode length determinant as _number of octets_, but only
+     * if upper bound is not equal to lower bound.
+     */
+    len_len = oer_fetch_length(ptr, size, &expected_length);
+    if(len_len > 0) {
+        rval.consumed = len_len;
+        ptr = (const char *)ptr + len_len;
+        size -= len_len;
+    } else if(len_len == 0) {
+        ASN__DECODE_STARVED;
+    } else if(len_len < 0) {
+        ASN__DECODE_FAILED;
+    }
+
+    if(size < expected_length) {
+        ASN__DECODE_STARVED;
+    } else {
+        uint8_t *buf = MALLOC(expected_length + 1);
+        if(buf == NULL) {
+            ASN__DECODE_FAILED;
+        } else {
+            memcpy(buf, ptr, expected_length);
+            buf[expected_length] = '\0';
+        }
+        FREEMEM(st->buf);
+        st->buf = buf;
+        st->size = expected_length;
+
+        rval.consumed += expected_length;
+        return rval;
+    }
+}

+ 72 - 0
lib/asn1/oer_decoder.h

@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) 2017 Lev Walkin <vlm@lionet.info>. All rights reserved.
+ * Redistribution and modifications are permitted subject to BSD license.
+ */
+#ifndef	OER_DECODER_H
+#define	OER_DECODER_H
+
+#include <asn_application.h>
+#include <oer_support.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct asn_TYPE_descriptor_s;	/* Forward declaration */
+struct asn_codec_ctx_s;		/* Forward declaration */
+
+/*
+ * The Octet Encoding Rules (OER, X.696 08/2015) decoder for any given type.
+ * This function may be invoked directly by the application.
+ * Parses CANONICAL-OER and BASIC-OER.
+ */
+asn_dec_rval_t oer_decode(const struct asn_codec_ctx_s *opt_codec_ctx,
+	const 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(oer_type_decoder_f)(
+    const struct asn_codec_ctx_s *opt_codec_ctx,
+    const struct asn_TYPE_descriptor_s *type_descriptor,
+    const asn_oer_constraints_t *constraints,
+    void **struct_ptr,
+    const void *buf_ptr,
+    size_t size);
+
+/*
+ * Swallow the Open Type (X.696 (08/2015), #30) into /dev/null.
+ * RETURN VALUES:
+ *      -1:     Fatal error deciphering length.
+ *       0:     More data expected than bufptr contains.
+ *      >0:     Number of bytes used from bufptr.
+ */
+ssize_t oer_open_type_skip(const void *bufptr, size_t size);
+
+/*
+ * Read the Open Type (X.696 (08/2015), #30).
+ * RETURN VALUES:
+ *       0:     More data expected than bufptr contains.
+ *      -1:     Fatal error deciphering length.
+ *      >0:     Number of bytes used from bufptr.
+ */
+ssize_t oer_open_type_get(const asn_codec_ctx_t *opt_codec_ctx,
+                          const struct asn_TYPE_descriptor_s *td,
+                          const asn_oer_constraints_t *constraints,
+                          void **struct_ptr, const void *bufptr, size_t size);
+
+/*
+ * Length-prefixed buffer decoding for primitive types.
+ */
+oer_type_decoder_f oer_decode_primitive;
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif	/* OER_DECODER_H */

+ 141 - 0
lib/asn1/oer_encoder.c

@@ -0,0 +1,141 @@
+/*
+ * Copyright (c) 2017 Lev Walkin <vlm@lionet.info>. All rights reserved.
+ * Redistribution and modifications are permitted subject to BSD license.
+ */
+#include <asn_internal.h>
+#include <asn_codecs_prim.h>
+
+/*
+ * The OER encoder of any type.
+ */
+asn_enc_rval_t
+oer_encode(const asn_TYPE_descriptor_t *type_descriptor, const void *struct_ptr,
+           asn_app_consume_bytes_f *consume_bytes, void *app_key) {
+    ASN_DEBUG("OER encoder invoked for %s", type_descriptor->name);
+
+    /*
+     * Invoke type-specific encoder.
+     */
+    return type_descriptor->op->oer_encoder(
+        type_descriptor, 0,
+        struct_ptr, /* Pointer to the destination structure */
+        consume_bytes, app_key);
+}
+
+/*
+ * Argument type and callback necessary for oer_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 oer_encode() which encodes the data into the provided buffer
+ */
+asn_enc_rval_t
+oer_encode_to_buffer(const asn_TYPE_descriptor_t *type_descriptor,
+                     const asn_oer_constraints_t *constraints,
+                     const void *struct_ptr, /* Structure to be encoded */
+                     void *buffer,           /* Pre-allocated buffer */
+                     size_t buffer_size      /* Initial buffer size (maximum) */
+) {
+    enc_to_buf_arg arg;
+    asn_enc_rval_t ec;
+
+    arg.buffer = buffer;
+    arg.left = buffer_size;
+
+    if(type_descriptor->op->oer_encoder == NULL) {
+        ec.encoded = -1;
+        ec.failed_type = type_descriptor;
+        ec.structure_ptr = struct_ptr;
+        ASN_DEBUG("OER encoder is not defined for %s",
+                type_descriptor->name);
+    } else {
+        ec = type_descriptor->op->oer_encoder(
+            type_descriptor, constraints,
+            struct_ptr, /* Pointer to the destination structure */
+            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;
+}
+
+asn_enc_rval_t
+oer_encode_primitive(const asn_TYPE_descriptor_t *td,
+                     const asn_oer_constraints_t *constraints, const void *sptr,
+                     asn_app_consume_bytes_f *cb, void *app_key) {
+    const ASN__PRIMITIVE_TYPE_t *st = (const ASN__PRIMITIVE_TYPE_t *)sptr;
+    asn_enc_rval_t er = {0, 0, 0};
+    ssize_t ret;
+
+    (void)constraints;
+
+    if(!st) ASN__ENCODE_FAILED;
+
+    ASN_DEBUG("Encoding %s (%" ASN_PRI_SIZE " bytes)", td ? td->name : "", st->size);
+
+    /*
+     * X.696 (08/2015) #27.2
+     */
+    ret = oer_serialize_length(st->size, cb, app_key);
+    if(ret < 0) {
+        ASN__ENCODE_FAILED;
+    }
+    er.encoded += ret;
+
+    er.encoded += st->size;
+    if(cb(st->buf, st->size, app_key) < 0) {
+        ASN__ENCODE_FAILED;
+    } else {
+        ASN__ENCODED_OK(er);
+    }
+}
+
+static int
+oer__count_bytes(const void *buffer, size_t size, void *bytes_ptr) {
+    size_t *bytes = bytes_ptr;
+    (void)buffer;
+    *bytes += size;
+    return 0;
+}
+
+ssize_t
+oer_open_type_put(const asn_TYPE_descriptor_t *td,
+                  const asn_oer_constraints_t *constraints, const void *sptr,
+                  asn_app_consume_bytes_f *cb, void *app_key) {
+    size_t serialized_byte_count = 0;
+    asn_enc_rval_t er;
+    ssize_t len_len;
+
+    er = td->op->oer_encoder(td, constraints, sptr, oer__count_bytes,
+                             &serialized_byte_count);
+    if(er.encoded < 0) return -1;
+    assert(serialized_byte_count == (size_t)er.encoded);
+
+    len_len = oer_serialize_length(serialized_byte_count, cb, app_key);
+    if(len_len == -1) return -1;
+
+    er = td->op->oer_encoder(td, constraints, sptr, cb, app_key);
+    if(er.encoded < 0) return -1;
+    assert(serialized_byte_count == (size_t)er.encoded);
+
+    return len_len + er.encoded;
+}
+

+ 70 - 0
lib/asn1/oer_encoder.h

@@ -0,0 +1,70 @@
+/*
+ * Copyright (c) 2017 Lev Walkin <vlm@lionet.info>. All rights reserved.
+ * Redistribution and modifications are permitted subject to BSD license.
+ */
+#ifndef	OER_ENCODER_H
+#define	OER_ENCODER_H
+
+#include <asn_application.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct asn_TYPE_descriptor_s;	/* Forward declaration */
+
+/*
+ * The Octet Encoding Rules (OER, X.696 08/2015) encoder for any type.
+ * This function may be invoked directly by the application.
+ * Produces CANONICAL-OER output compatible with CANONICAL-OER
+ * and BASIC-OER decoders.
+ */
+asn_enc_rval_t oer_encode(const struct asn_TYPE_descriptor_s *type_descriptor,
+                          const void *struct_ptr, /* Structure to be encoded */
+                          asn_app_consume_bytes_f *consume_bytes_cb,
+                          void *app_key /* Arbitrary callback argument */
+);
+
+/* A variant of oer_encode() which encodes data into the pre-allocated buffer */
+asn_enc_rval_t oer_encode_to_buffer(
+    const struct asn_TYPE_descriptor_s *type_descriptor,
+    const asn_oer_constraints_t *constraints,
+    const void *struct_ptr, /* Structure to be encoded */
+    void *buffer,           /* Pre-allocated buffer */
+    size_t buffer_size      /* Initial buffer size (maximum) */
+);
+
+/*
+ * Type of the generic OER encoder.
+ */
+typedef asn_enc_rval_t(oer_type_encoder_f)(
+    const struct asn_TYPE_descriptor_s *type_descriptor,
+    const asn_oer_constraints_t *constraints,
+    const void *struct_ptr,                    /* Structure to be encoded */
+    asn_app_consume_bytes_f *consume_bytes_cb, /* Callback */
+    void *app_key                              /* Arbitrary callback argument */
+);
+
+/*
+ * Write out the Open Type (X.696 (08/2015), #30).
+ * RETURN VALUES:
+ *  -1: Fatal error encoding the type.
+ *  >0: Number of bytes serialized.
+ */
+ssize_t oer_open_type_put(const struct asn_TYPE_descriptor_s *td,
+                          const asn_oer_constraints_t *constraints,
+                          const void *struct_ptr,
+                          asn_app_consume_bytes_f *consume_bytes_cb,
+                          void *app_key);
+
+
+/*
+ * Length-prefixed buffer encoding for primitive types.
+ */
+oer_type_encoder_f oer_encode_primitive;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif	/* OER_ENCODER_H */

+ 122 - 0
lib/asn1/oer_support.c

@@ -0,0 +1,122 @@
+/*
+ * Copyright (c) 2017 Lev Walkin <vlm@lionet.info>.
+ * All rights reserved.
+ * Redistribution and modifications are permitted subject to BSD license.
+ */
+#include <asn_system.h>
+#include <asn_internal.h>
+
+#include <oer_support.h>
+
+/*
+ * Fetch the length determinant (X.696 08/2015, #8.6) into *len_r.
+ * RETURN VALUES:
+ *       0:     More data expected than bufptr contains.
+ *      -1:     Fatal error deciphering length.
+ *      >0:     Number of bytes used from bufptr.
+ */
+ssize_t
+oer_fetch_length(const void *bufptr, size_t size, size_t *len_r) {
+    uint8_t first_byte;
+    size_t len_len;    /* Length of the length determinant */
+    const uint8_t *b;
+    const uint8_t *bend;
+    size_t len;
+
+    if(size == 0) {
+        *len_r = 0;
+        return 0;
+    }
+
+    first_byte = *(const uint8_t *)bufptr;
+    if((first_byte & 0x80) == 0) {   /* Short form */
+        *len_r = first_byte; /* 0..127 */
+        return 1;
+    }
+
+    len_len = (first_byte & 0x7f);
+    if((1 + len_len) > size) {
+        *len_r = 0;
+        return 0;
+    }
+
+    b = (const uint8_t *)bufptr + 1;
+    bend = b + len_len;
+
+    for(; b < bend && *b == 0; b++) {
+        /* Skip the leading 0-bytes */
+    }
+
+    if((bend - b) > (ssize_t)sizeof(size_t)) {
+        /* Length is not representable by the native size_t type */
+        *len_r = 0;
+        return -1;
+    }
+
+    for(len = 0; b < bend; b++) {
+        len = (len << 8) + *b;
+    }
+
+    if(len > RSIZE_MAX) { /* A bit of C11 validation */
+        *len_r = 0;
+        return -1;
+    }
+
+    *len_r = len;
+    assert(len_len + 1 == (size_t)(bend - (const uint8_t *)bufptr));
+    return len_len + 1;
+}
+
+
+/*
+ * Serialize OER length. Returns the number of bytes serialized
+ * or -1 if a given callback returned with negative result.
+ */
+ssize_t
+oer_serialize_length(size_t length, asn_app_consume_bytes_f *cb,
+                     void *app_key) {
+    uint8_t scratch[1 + sizeof(length)];
+    uint8_t *sp = scratch;
+    int littleEndian = 1;   /* Run-time detection */
+    const uint8_t *pstart;
+    const uint8_t *pend;
+    const uint8_t *p;
+    int add;
+
+    if(length <= 127) {
+        uint8_t b = length;
+        if(cb(&b, 1, app_key) < 0) {
+            return -1;
+        }
+        return 1;
+    }
+
+    if(*(char *)&littleEndian) {
+        pstart = (const uint8_t *)&length + sizeof(length) - 1;
+        pend = (const uint8_t *)&length;
+        add = -1;
+    } else {
+        pstart = (const uint8_t *)&length;
+        pend = pstart + sizeof(length);
+        add = 1;
+    }
+
+    for(p = pstart; p != pend; p += add) {
+        /* Skip leading zeros. */
+        if(*p) break;
+    }
+
+    for(sp = scratch + 1; ; p += add) {
+        *sp++ = *p;
+        if(p == pend) break;
+    }
+    assert((sp - scratch) - 1 <= 0x7f);
+    scratch[0] = 0x80 + ((sp - scratch) - 1);
+
+    if(cb(scratch, sp - scratch, app_key) < 0) {
+        return -1;
+    }
+
+    return sp - scratch;
+}
+

+ 47 - 0
lib/asn1/oer_support.h

@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2017 Lev Walkin <vlm@lionet.info>. All rights reserved.
+ * Redistribution and modifications are permitted subject to BSD license.
+ */
+#ifndef	OER_SUPPORT_H
+#define	OER_SUPPORT_H
+
+#include <asn_system.h>		/* Platform-specific types */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Pre-computed OER constraints.
+ */
+typedef struct asn_oer_constraint_number_s {
+    unsigned width;    /* ±8,4,2,1 fixed bytes */
+    unsigned positive; /* 1 for unsigned number, 0 for signed */
+} asn_oer_constraint_number_t;
+typedef struct asn_oer_constraints_s {
+    asn_oer_constraint_number_t value;
+    ssize_t size;    /* -1 (no constraint) or >= 0 */
+} asn_oer_constraints_t;
+
+
+/*
+ * Fetch the length determinant (X.696 (08/2015), #8.6) into *len_r.
+ * RETURN VALUES:
+ *       0:     More data expected than bufptr contains.
+ *      -1:     Fatal error deciphering length.
+ *      >0:     Number of bytes used from bufptr.
+ */
+ssize_t oer_fetch_length(const void *bufptr, size_t size, size_t *len_r);
+
+/*
+ * Serialize OER length. Returns the number of bytes serialized
+ * or -1 if a given callback returned with negative result.
+ */
+ssize_t oer_serialize_length(size_t length, asn_app_consume_bytes_f *cb, void *app_key);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif	/* OER_SUPPORT_H */

+ 97 - 0
lib/asn1/per_decoder.c

@@ -0,0 +1,97 @@
+#include <asn_application.h>
+#include <asn_internal.h>
+#include <per_decoder.h>
+
+/*
+ * 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(const asn_codec_ctx_t *opt_codec_ctx,
+                     const 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(const asn_codec_ctx_t *opt_codec_ctx,
+            const 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 <limits.h> */
+	if(pd.nboff > pd.nbits)
+		ASN__DECODE_FAILED;
+
+	/*
+	 * Invoke type-specific decoder.
+	 */
+	if(!td->op->uper_decoder)
+		ASN__DECODE_FAILED;	/* PER is not compiled in */
+	rval = td->op->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;
+}
+

+ 57 - 0
lib/asn1/per_decoder.h

@@ -0,0 +1,57 @@
+/*-
+ * Copyright (c) 2005-2017 Lev Walkin <vlm@lionet.info>. All rights reserved.
+ * Redistribution and modifications are permitted subject to BSD license.
+ */
+#ifndef	_PER_DECODER_H_
+#define	_PER_DECODER_H_
+
+#include <asn_application.h>
+#include <per_support.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct asn_TYPE_descriptor_s;	/* Forward declaration */
+
+/*
+ * Unaligned PER decoder of a "complete encoding" as per X.691 (08/2015) #11.1.
+ * On success, this call always returns (.consumed >= 1), as per #11.1.3.
+ */
+asn_dec_rval_t uper_decode_complete(
+    const struct asn_codec_ctx_s *opt_codec_ctx,
+    const 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(
+    const struct asn_codec_ctx_s *opt_codec_ctx,
+    const 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 the input data buffer, in bytes */
+    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)(
+    const asn_codec_ctx_t *opt_codec_ctx,
+    const struct asn_TYPE_descriptor_s *type_descriptor,
+    const asn_per_constraints_t *constraints, void **struct_ptr,
+    asn_per_data_t *per_data);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif	/* _PER_DECODER_H_ */

+ 165 - 0
lib/asn1/per_encoder.c

@@ -0,0 +1,165 @@
+#include <asn_application.h>
+#include <asn_internal.h>
+#include <per_encoder.h>
+
+static int _uper_encode_flush_outp(asn_per_outp_t *po);
+
+static int
+ignore_output(const void *data, size_t size, void *app_key) {
+    (void)data;
+    (void)size;
+    (void)app_key;
+    return 0;
+}
+
+asn_enc_rval_t
+uper_encode(const asn_TYPE_descriptor_t *td,
+            const asn_per_constraints_t *constraints, const 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->op->uper_encoder)
+        ASN__ENCODE_FAILED;	/* PER is not compiled in */
+
+    po.buffer = po.tmpspace;
+    po.nboff = 0;
+    po.nbits = 8 * sizeof(po.tmpspace);
+    po.output = cb ? cb : ignore_output;
+    po.op_key = app_key;
+    po.flushed_bytes = 0;
+
+    er = td->op->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;
+}
+
+/*
+ * 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(const asn_TYPE_descriptor_t *td,
+                      const asn_per_constraints_t *constraints,
+                      const 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(td, constraints, 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) {
+        size_t new_size = arg->allocated ? arg->allocated : 8;
+        void *p;
+
+        do {
+            new_size <<= 2;
+        } while(arg->length + size >= new_size);
+
+        p = REALLOC(arg->buffer, new_size);
+        if(!p) {
+            FREEMEM(arg->buffer);
+            memset(arg, 0, sizeof(*arg));
+            return -1;
+        }
+        arg->buffer = p;
+        arg->allocated = new_size;
+    }
+    memcpy(((char *)arg->buffer) + arg->length, buffer, size);
+    arg->length += size;
+    return 0;
+}
+ssize_t
+uper_encode_to_new_buffer(const asn_TYPE_descriptor_t *td,
+                          const asn_per_constraints_t *constraints,
+                          const void *sptr, void **buffer_r) {
+    asn_enc_rval_t er;
+	enc_dyn_arg key;
+
+	memset(&key, 0, sizeof(key));
+
+	er = uper_encode(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->output(po->tmpspace, buf - po->tmpspace, po->op_key);
+}
+

+ 70 - 0
lib/asn1/per_encoder.h

@@ -0,0 +1,70 @@
+/*-
+ * Copyright (c) 2006-2017 Lev Walkin <vlm@lionet.info>. All rights reserved.
+ * Redistribution and modifications are permitted subject to BSD license.
+ */
+#ifndef	_PER_ENCODER_H_
+#define	_PER_ENCODER_H_
+
+#include <asn_application.h>
+#include <per_support.h>
+
+#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(
+    const struct asn_TYPE_descriptor_s *type_descriptor,
+    const asn_per_constraints_t *constraints,
+    const 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(
+    const struct asn_TYPE_descriptor_s *type_descriptor,
+    const asn_per_constraints_t *constraints,
+    const 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(
+    const struct asn_TYPE_descriptor_s *type_descriptor,
+    const asn_per_constraints_t *constraints,
+    const 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)(
+    const struct asn_TYPE_descriptor_s *type_descriptor,
+    const asn_per_constraints_t *constraints, const void *struct_ptr,
+    asn_per_outp_t *per_output);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif	/* _PER_ENCODER_H_ */

+ 396 - 0
lib/asn1/per_opentype.c

@@ -0,0 +1,396 @@
+/*
+ * Copyright (c) 2007 Lev Walkin <vlm@lionet.info>. All rights reserved.
+ * Redistribution and modifications are permitted subject to BSD license.
+ */
+#include <asn_internal.h>
+#include <per_support.h>
+#include <constr_TYPE.h>
+#include <per_opentype.h>
+
+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(const asn_codec_ctx_t *,
+                                    const asn_TYPE_descriptor_t *td,
+                                    const 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(const asn_TYPE_descriptor_t *td,
+                   const asn_per_constraints_t *constraints, const void *sptr,
+                   asn_per_outp_t *po) {
+    void *buf;
+    void *bptr;
+    ssize_t size;
+
+    ASN_DEBUG("Open type put %s ...", td->name);
+
+    size = uper_encode_to_new_buffer(td, constraints, sptr, &buf);
+    if(size <= 0) return -1;
+
+    ASN_DEBUG("Open type put %s of length %" ASN_PRI_SSIZE " + overhead (1byte?)", td->name,
+              size);
+
+    bptr = buf;
+    do {
+        int need_eom = 0;
+        ssize_t may_save = uper_put_length(po, size, &need_eom);
+        ASN_DEBUG("Prepending length %" ASN_PRI_SSIZE
+                  " to %s and allowing to save %" ASN_PRI_SSIZE,
+                  size, td->name, may_save);
+        if(may_save < 0) break;
+        if(per_put_many_bits(po, bptr, may_save * 8)) break;
+        bptr = (char *)bptr + may_save;
+        size -= may_save;
+        if(need_eom && uper_put_length(po, 0, 0)) {
+            FREEMEM(buf);
+            return -1;
+        }
+    } while(size);
+
+    FREEMEM(buf);
+    if(size) return -1;
+
+    return 0;
+}
+
+static asn_dec_rval_t
+uper_open_type_get_simple(const asn_codec_ctx_t *ctx,
+                          const asn_TYPE_descriptor_t *td,
+                          const 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, 0, &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->op->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 CC_NOTUSED
+uper_open_type_get_complex(const asn_codec_ctx_t *ctx,
+                           const 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,
+		asn_bit_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->op->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,
+		asn_bit_data_string(pd),
+		asn_bit_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,
+			asn_bit_data_string(pd), asn_bit_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(const asn_codec_ctx_t *ctx, const asn_TYPE_descriptor_t *td,
+                   const 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(const asn_codec_ctx_t *ctx, asn_per_data_t *pd) {
+	asn_TYPE_descriptor_t s_td;
+    asn_TYPE_operation_t s_op;
+	asn_dec_rval_t rv;
+
+	s_td.name = "<unknown extension>";
+	s_td.op = &s_op;
+    s_op.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(const asn_codec_ctx_t *ctx, const asn_TYPE_descriptor_t *td,
+              const 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, 0, &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",
+		asn_bit_data_string(pd), asn_bit_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;
+}

+ 32 - 0
lib/asn1/per_opentype.h

@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2007-2017 Lev Walkin <vlm@lionet.info>. 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(const asn_codec_ctx_t *opt_codec_ctx,
+                                  const asn_TYPE_descriptor_t *td,
+                                  const asn_per_constraints_t *constraints,
+                                  void **sptr, asn_per_data_t *pd);
+
+int uper_open_type_skip(const asn_codec_ctx_t *opt_codec_ctx,
+                        asn_per_data_t *pd);
+
+/*
+ * X.691 (2015/08), #11.2
+ * Returns -1 if error is encountered. 0 if all OK.
+ */
+int uper_open_type_put(const asn_TYPE_descriptor_t *td,
+                       const asn_per_constraints_t *constraints,
+                       const void *sptr, asn_per_outp_t *po);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif	/* _PER_OPENTYPE_H_ */

+ 295 - 0
lib/asn1/per_support.c

@@ -0,0 +1,295 @@
+/*
+ * Copyright (c) 2005-2017 Lev Walkin <vlm@lionet.info>. All rights reserved.
+ * Redistribution and modifications are permitted subject to BSD license.
+ */
+#include <asn_system.h>
+#include <asn_internal.h>
+#include <per_support.h>
+
+/*
+ * X.691-201508 #10.9 General rules for encoding a length determinant.
+ * Get the optionally constrained length "n" from the stream.
+ */
+ssize_t
+uper_get_length(asn_per_data_t *pd, int ebits, size_t lower_bound,
+                int *repeat) {
+    ssize_t value;
+
+    *repeat = 0;
+
+    /* #11.9.4.1 Encoding if constrained (according to effective bits) */
+    if(ebits >= 0 && ebits <= 16) {
+        value = per_get_few_bits(pd, ebits);
+        if(value >= 0) value += lower_bound;
+        return value;
+    }
+
+	value = per_get_few_bits(pd, 8);
+    if((value & 0x80) == 0) { /* #11.9.3.6 */
+        return (value & 0x7F);
+    } else if((value & 0x40) == 0) { /* #11.9.3.7 */
+        /* bit 8 ... set to 1 and bit 7 ... set to zero */
+        value = ((value & 0x3f) << 8) | per_get_few_bits(pd, 8);
+        return value; /* potential -1 from per_get_few_bits passes through. */
+    } else if(value < 0) {
+        ASN_DEBUG("END of stream reached for PER");
+        return -1;
+    }
+    value &= 0x3f; /* this is "m" from X.691, #11.9.3.8 */
+    if(value < 1 || value > 4) {
+        return -1; /* Prohibited by #11.9.3.8 */
+    }
+    *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, 0, &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_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);
+    }
+}
+
+/*
+ * X.691 (08/2015) #11.9 "General rules for encoding a length determinant"
+ * Put the length "n" (or part of it) into the stream.
+ */
+ssize_t
+uper_put_length(asn_per_outp_t *po, size_t length, int *need_eom) {
+    int dummy = 0;
+    if(!need_eom) need_eom = &dummy;
+
+    if(length <= 127) {	/* #11.9.3.6 */
+        *need_eom = 0;
+        return per_put_few_bits(po, length, 8)
+            ? -1 : (ssize_t)length;
+    } else if(length < 16384) { /* #10.9.3.7 */
+        *need_eom = 0;
+        return per_put_few_bits(po, length|0x8000, 16)
+            ? -1 : (ssize_t)length;
+    }
+
+    *need_eom = 0 == (length & 16383);
+    length >>= 14;
+    if(length > 4) {
+        *need_eom = 0;
+        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) {
+        /* #11.9.3.4 */
+        if(length == 0) return -1;
+        return per_put_few_bits(po, length - 1, 7) ? -1 : 0;
+    } else {
+        int need_eom = 0;
+        if(uper_put_length(po, length, &need_eom) != (ssize_t)length
+           || need_eom) {
+            /* This might happen in case of >16K extensions */
+            return -1;
+        }
+    }
+
+    return 0;
+}
+
+static int
+per__long_range(long lb, long ub, unsigned long *range_r) {
+    unsigned long bounds_range;
+    if((ub < 0) == (lb < 0)) {
+        bounds_range = ub - lb;
+    } else if(lb < 0) {
+        assert(ub >= 0);
+        bounds_range = 1 + ((unsigned long)ub + (unsigned long)-(lb + 1));
+    } else {
+        assert(!"Unreachable");
+        return -1;
+    }
+    *range_r = bounds_range;
+    return 0;
+}
+
+int
+per_long_range_rebase(long v, long lb, long ub, unsigned long *output) {
+    unsigned long range;
+
+    assert(lb <= ub);
+
+    if(v < lb || v > ub || per__long_range(lb, ub, &range) < 0) {
+        /* Range error. */
+        return -1;
+    }
+
+    /*
+     * Fundamentally what we're doing is returning (v-lb).
+     * However, this triggers undefined behavior when the word width
+     * of signed (v) is the same as the size of unsigned (*output).
+     * In practice, it triggers the UndefinedSanitizer. Therefore we shall
+     * compute the ranges accurately to avoid C's undefined behavior.
+     */
+    if((v < 0) == (lb < 0)) {
+        *output = v-lb;
+        return 0;
+    } else if(v < 0) {
+        unsigned long rebased = 1 + (unsigned long)-(v+1) + (unsigned long)lb;
+        assert(rebased <= range);   /* By construction */
+        *output = rebased;
+        return 0;
+    } else if(lb < 0) {
+        unsigned long rebased = 1 + (unsigned long)-(lb+1) + (unsigned long)v;
+        assert(rebased <= range);   /* By construction */
+        *output = rebased;
+        return 0;
+    } else {
+        assert(!"Unreachable");
+        return -1;
+    }
+}
+
+int
+per_long_range_unrebase(unsigned long inp, long lb, long ub, long *outp) {
+    unsigned long range;
+
+    if(per__long_range(lb, ub, &range) != 0) {
+        return -1;
+    }
+
+    if(inp > range) {
+        /*
+         * We can encode something in the given number of bits that technically
+         * exceeds the range. This is an avenue for security errors,
+         * so we don't allow that.
+         */
+        return -1;
+    }
+
+    if(inp <= LONG_MAX) {
+        *outp = (long)inp + lb;
+    } else {
+        *outp = (lb + LONG_MAX + 1) + (long)((inp - LONG_MAX) - 1);
+    }
+
+    return 0;
+}

+ 112 - 0
lib/asn1/per_support.h

@@ -0,0 +1,112 @@
+/*
+ * Copyright (c) 2005-2017 Lev Walkin <vlm@lionet.info>. All rights reserved.
+ * Redistribution and modifications are permitted subject to BSD license.
+ */
+#ifndef	_PER_SUPPORT_H_
+#define	_PER_SUPPORT_H_
+
+#include <asn_system.h>		/* Platform-specific types */
+#include <asn_bit_data.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Pre-computed PER constraints.
+ */
+typedef 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 struct asn_per_constraints_s {
+	asn_per_constraint_t value;
+	asn_per_constraint_t size;
+	int (*value2code)(unsigned int value);
+	int (*code2value)(unsigned int code);
+} asn_per_constraints_t;
+
+/* Temporary compatibility layer. Will get removed. */
+typedef struct asn_bit_data_s asn_per_data_t;
+#define per_get_few_bits(data, bits)   asn_get_few_bits(data, bits)
+#define per_get_undo(data, bits)   asn_get_undo(data, bits)
+#define per_get_many_bits(data, dst, align, bits) \
+    asn_get_many_bits(data, dst, align, bits)
+
+/*
+ * X.691 (08/2015) #11.9 "General rules for encoding a length determinant"
+ * Get the length "n" from the Unaligned PER stream.
+ */
+ssize_t uper_get_length(asn_per_data_t *pd, int effective_bound_bits,
+                        size_t lower_bound, 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);
+
+
+/* Temporary compatibility layer. Will get removed. */
+typedef struct asn_bit_outp_s asn_per_outp_t;
+#define per_put_few_bits(out, bits, obits) asn_put_few_bits(out, bits, obits)
+#define per_put_many_bits(out, src, nbits) asn_put_many_bits(out, src, nbits)
+#define per_put_aligned_flush(out) asn_put_aligned_flush(out)
+
+
+/*
+ * Rebase the given value as an offset into the range specified by the
+ * lower bound (lb) and upper bound (ub).
+ * RETURN VALUES:
+ *  -1: Conversion failed due to range problems.
+ *   0: Conversion was successful.
+ */
+int per_long_range_rebase(long v, long lb, long ub, unsigned long *output);
+/* The inverse operation: restores the value by the offset and its bounds. */
+int per_long_range_unrebase(unsigned long inp, long lb, long ub, long *outp);
+
+/* X.691-2008/11, #11.5 */
+int uper_put_constrained_whole_number_u(asn_per_outp_t *po, unsigned long v, int nbits);
+
+/*
+ * X.691 (08/2015) #11.9 "General rules for encoding a length determinant"
+ * Put the length "whole_length" to the Unaligned PER stream.
+ * If (opt_need_eom) is given, it will be set to 1 if final 0-length is needed.
+ * In that case, invoke uper_put_length(po, 0, 0) after encoding the last block.
+ * 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,
+                        int *opt_need_eom);
+
+/*
+ * 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_ */

+ 369 - 0
lib/asn1/xer_decoder.c

@@ -0,0 +1,369 @@
+/*
+ * Copyright (c) 2004-2017 Lev Walkin <vlm@lionet.info>. All rights reserved.
+ * Redistribution and modifications are permitted subject to BSD license.
+ */
+#include <asn_application.h>
+#include <asn_internal.h>
+#include <xer_support.h>		/* XER/XML parsing support */
+
+
+/*
+ * Decode the XER encoding of a given type.
+ */
+asn_dec_rval_t
+xer_decode(const asn_codec_ctx_t *opt_codec_ctx,
+           const 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->op->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 "</" */
+		size -= 3;	/* strip "</" and ">" */
+		ct = XCT_CLOSING;
+		if(size > 0 && buf[size-1] == CSLASH)
+			return XCT_BROKEN;	/* </abc/> */
+	} 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:
+					/* "<abc def/>": 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(const 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;
+	}
+}

+ 106 - 0
lib/asn1/xer_decoder.h

@@ -0,0 +1,106 @@
+/*-
+ * Copyright (c) 2004-2017 Lev Walkin <vlm@lionet.info>. All rights reserved.
+ * Redistribution and modifications are permitted subject to BSD license.
+ */
+#ifndef	_XER_DECODER_H_
+#define	_XER_DECODER_H_
+
+#include <asn_application.h>
+
+#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.
+ * Decodes CANONICAL-XER and BASIC-XER.
+ */
+asn_dec_rval_t xer_decode(
+    const struct asn_codec_ctx_s *opt_codec_ctx,
+    const 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)(
+    const asn_codec_ctx_t *opt_codec_ctx,
+    const 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(
+    const 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 <opening> tag */
+	XCT_CLOSING	= 2,	/* This is the </closing> tag */
+	XCT_BOTH	= 3,	/* This is the <modified/> tag */
+	XCT__UNK__MASK	= 4,	/* Mask of everything unexpected */
+	XCT_UNKNOWN_OP	= 5,	/* Unexpected <opening> tag */
+	XCT_UNKNOWN_CL	= 6,	/* Unexpected </closing> tag */
+	XCT_UNKNOWN_BO	= 7	/* Unexpected <modified/> 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_ */

+ 237 - 0
lib/asn1/xer_encoder.c

@@ -0,0 +1,237 @@
+/*-
+ * Copyright (c) 2003, 2004 Lev Walkin <vlm@lionet.info>. All rights reserved.
+ * Redistribution and modifications are permitted subject to BSD license.
+ */
+#include <asn_internal.h>
+#include <stdio.h>
+#include <errno.h>
+
+/*
+ * The XER encoder of any type. May be invoked by the application.
+ */
+asn_enc_rval_t
+xer_encode(const asn_TYPE_descriptor_t *td, const void *sptr,
+           enum xer_encoder_flags_e xer_flags, asn_app_consume_bytes_f *cb,
+           void *app_key) {
+    asn_enc_rval_t er = {0, 0, 0};
+	asn_enc_rval_t 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->op->xer_encoder(td, sptr, 1, xer_flags, cb, app_key);
+	if(tmper.encoded == -1) return tmper;
+	er.encoded += tmper.encoded;
+
+	ASN__CALLBACK3("</", 2, mname, mlen, ">\n", xcan);
+
+	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, const asn_TYPE_descriptor_t *td, const 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);
+}
+
+struct xer_buffer {
+    char *buffer;
+    size_t buffer_size;
+    size_t allocated_size;
+};
+
+static int
+xer__buffer_append(const void *buffer, size_t size, void *app_key) {
+    struct xer_buffer *xb = app_key;
+
+    while(xb->buffer_size + size + 1 > xb->allocated_size) {
+        size_t new_size = 2 * (xb->allocated_size ? xb->allocated_size : 64);
+        char *new_buf = MALLOC(new_size);
+        if(!new_buf) return -1;
+        if (xb->buffer) {
+            memcpy(new_buf, xb->buffer, xb->buffer_size);
+        }
+        FREEMEM(xb->buffer);
+        xb->buffer = new_buf;
+        xb->allocated_size = new_size;
+    }
+
+    memcpy(xb->buffer + xb->buffer_size, buffer, size);
+    xb->buffer_size += size;
+    xb->buffer[xb->buffer_size] = '\0';
+    return 0;
+}
+
+enum xer_equivalence_e
+xer_equivalent(const struct asn_TYPE_descriptor_s *td, const void *struct1,
+               const void *struct2, FILE *opt_debug_stream) {
+    struct xer_buffer xb1 = {0, 0, 0};
+    struct xer_buffer xb2 = {0, 0, 0};
+    asn_enc_rval_t e1, e2;
+    asn_dec_rval_t rval;
+    void *sptr = NULL;
+
+    if(!td || !struct1 || !struct2) {
+        if(opt_debug_stream) {
+            if(!td) fprintf(opt_debug_stream, "Type descriptor missing\n");
+            if(!struct1) fprintf(opt_debug_stream, "Structure 1 missing\n");
+            if(!struct2) fprintf(opt_debug_stream, "Structure 2 missing\n");
+        }
+        return XEQ_FAILURE;
+    }
+
+    e1 = xer_encode(td, struct1, XER_F_BASIC, xer__buffer_append, &xb1);
+    if(e1.encoded == -1) {
+        if(opt_debug_stream) {
+            fprintf(stderr, "XER Encoding of %s failed\n", td->name);
+        }
+        FREEMEM(xb1.buffer);
+        return XEQ_ENCODE1_FAILED;
+    }
+
+    e2 = xer_encode(td, struct2, XER_F_BASIC, xer__buffer_append, &xb2);
+    if(e2.encoded == -1) {
+        if(opt_debug_stream) {
+            fprintf(stderr, "XER Encoding of %s failed\n", td->name);
+        }
+        FREEMEM(xb1.buffer);
+        FREEMEM(xb2.buffer);
+        return XEQ_ENCODE1_FAILED;
+    }
+
+    if(xb1.buffer_size != xb2.buffer_size
+       || memcmp(xb1.buffer, xb2.buffer, xb1.buffer_size) != 0) {
+        if(opt_debug_stream) {
+            fprintf(opt_debug_stream,
+                    "Structures XER-encoded into different byte streams:\n=== "
+                    "Structure 1 ===\n%s\n=== Structure 2 ===\n%s\n",
+                    xb1.buffer, xb2.buffer);
+        }
+        FREEMEM(xb1.buffer);
+        FREEMEM(xb2.buffer);
+        return XEQ_DIFFERENT;
+    } else {
+        if(opt_debug_stream) {
+            fprintf(opt_debug_stream,
+                    "Both structures encoded into the same XER byte stream "
+                    "of size %" ASN_PRI_SIZE ":\n%s",
+                    xb1.buffer_size, xb1.buffer);
+        }
+    }
+
+    rval = xer_decode(NULL, td, (void **)&sptr, xb1.buffer,
+               xb1.buffer_size);
+    switch(rval.code) {
+    case RC_OK:
+        break;
+    case RC_WMORE:
+        if(opt_debug_stream) {
+            fprintf(opt_debug_stream,
+                    "Structure %s XER decode unexpectedly requires "
+                    "more data:\n%s\n",
+                    td->name, xb1.buffer);
+        }
+        /* Fall through */
+    case RC_FAIL:
+    default:
+        if(opt_debug_stream) {
+            fprintf(opt_debug_stream,
+                    "Structure %s XER decoding resulted in failure.\n",
+                    td->name);
+        }
+        ASN_STRUCT_FREE(*td, sptr);
+        FREEMEM(xb1.buffer);
+        FREEMEM(xb2.buffer);
+        return XEQ_DECODE_FAILED;
+    }
+
+    if(rval.consumed != xb1.buffer_size
+       && ((rval.consumed > xb1.buffer_size)
+           || xer_whitespace_span(xb1.buffer + rval.consumed,
+                                  xb1.buffer_size - rval.consumed)
+                  != (xb1.buffer_size - rval.consumed))) {
+        if(opt_debug_stream) {
+            fprintf(opt_debug_stream,
+                    "Round-trip decode of %s required less bytes (%" ASN_PRI_SIZE ") than "
+                    "encoded (%" ASN_PRI_SIZE ")\n",
+                    td->name, rval.consumed, xb1.buffer_size);
+        }
+        ASN_STRUCT_FREE(*td, sptr);
+        FREEMEM(xb1.buffer);
+        FREEMEM(xb2.buffer);
+        return XEQ_ROUND_TRIP_FAILED;
+    }
+
+    /*
+     * Reuse xb2 to encode newly decoded structure.
+     */
+    FREEMEM(xb2.buffer);
+    memset(&xb2, 0, sizeof(xb2));
+
+    e2 = xer_encode(td, sptr, XER_F_BASIC, xer__buffer_append, &xb2);
+    if(e2.encoded == -1) {
+        if(opt_debug_stream) {
+            fprintf(stderr, "XER Encoding of round-trip decode of %s failed\n",
+                    td->name);
+        }
+        ASN_STRUCT_FREE(*td, sptr);
+        FREEMEM(xb1.buffer);
+        FREEMEM(xb2.buffer);
+        return XEQ_ROUND_TRIP_FAILED;
+    }
+
+    ASN_STRUCT_FREE(*td, sptr);
+    sptr = 0;
+
+    if(xb1.buffer_size != xb2.buffer_size
+       || memcmp(xb1.buffer, xb2.buffer, xb1.buffer_size) != 0) {
+        if(opt_debug_stream) {
+            fprintf(opt_debug_stream,
+                    "XER Encoding of round-trip decode of %s resulted in "
+                    "different byte stream:\n"
+                    "=== Original ===\n%s\n"
+                    "=== Round-tripped ===\n%s\n",
+                    xb1.buffer, xb2.buffer, td->name);
+        }
+        FREEMEM(xb1.buffer);
+        FREEMEM(xb2.buffer);
+        return XEQ_ROUND_TRIP_FAILED;
+    }
+
+	FREEMEM(xb1.buffer);
+	FREEMEM(xb2.buffer);
+	return XEQ_SUCCESS;
+}
+

+ 83 - 0
lib/asn1/xer_encoder.h

@@ -0,0 +1,83 @@
+/*-
+ * Copyright (c) 2004-2017 Lev Walkin <vlm@lionet.info>. All rights reserved.
+ * Redistribution and modifications are permitted subject to BSD license.
+ */
+#ifndef	_XER_ENCODER_H_
+#define	_XER_ENCODER_H_
+
+#include <asn_application.h>
+
+#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.
+ * Produces CANONICAL-XER and BASIC-XER depending on the (xer_flags).
+ */
+asn_enc_rval_t xer_encode(const struct asn_TYPE_descriptor_s *type_descriptor,
+                          const 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, const struct asn_TYPE_descriptor_s *td,
+               const void *struct_ptr);
+
+/*
+ * A helper function that uses XER encoding/decoding to verify that:
+ * - Both structures encode into the same BASIC XER.
+ * - Both resulting XER byte streams can be decoded back.
+ * - Both decoded structures encode into the same BASIC XER (round-trip).
+ * All of this verifies equivalence between structures and a round-trip.
+ * ARGUMENTS:
+ *  (opt_debug_stream)  - If specified, prints ongoing details.
+ */
+enum xer_equivalence_e {
+    XEQ_SUCCESS,          /* The only completely positive return value */
+    XEQ_FAILURE,          /* General failure */
+    XEQ_ENCODE1_FAILED,   /* First sructure XER encoding failed */
+    XEQ_ENCODE2_FAILED,   /* Second structure XER encoding failed */
+    XEQ_DIFFERENT,        /* Structures encoded into different XER */
+    XEQ_DECODE_FAILED,    /* Decode of the XER data failed */
+    XEQ_ROUND_TRIP_FAILED /* Bad round-trip */
+};
+enum xer_equivalence_e xer_equivalent(
+    const struct asn_TYPE_descriptor_s *type_descriptor, const void *struct1,
+    const void *struct2, FILE *opt_debug_stream);
+
+/*
+ * Type of the generic XER encoder.
+ */
+typedef asn_enc_rval_t(xer_type_encoder_f)(
+    const struct asn_TYPE_descriptor_s *type_descriptor,
+    const 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_ */

+ 227 - 0
lib/asn1/xer_support.c

@@ -0,0 +1,227 @@
+/*
+ * Copyright (c) 2003, 2004 X/IO Labs, xiolabs.com.
+ * Copyright (c) 2003, 2004, 2005 Lev Walkin <vlm@lionet.info>.
+ * 	All rights reserved.
+ * Redistribution and modifications are permitted subject to BSD license.
+ */
+#include <asn_system.h>
+#include <xer_support.h>
+
+/* 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,	/* "<!--"[1] */
+	ST_COMMENT_WAIT_DASH2,	/* "<!--"[2] */
+	ST_COMMENT,
+	ST_COMMENT_CLO_DASH2,	/* "-->"[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;
+}
+

+ 55 - 0
lib/asn1/xer_support.h

@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2003, 2004 X/IO Labs, xiolabs.com.
+ * Copyright (c) 2003-2017 Lev Walkin <vlm@lionet.info>. All rights reserved.
+ * Redistribution and modifications are permitted subject to BSD license.
+ */
+#ifndef	_XER_SUPPORT_H_
+#define	_XER_SUPPORT_H_
+
+#include <asn_system.h>		/* 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 "<!--" and "-->". */
+	/* 
+	 * 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_ */

+ 42 - 0
passy_reader.c

@@ -26,6 +26,19 @@ size_t asn1_length(uint8_t data[3]) {
     return 0;
 }
 
+char asn1_log[PASSY_READER_MAX_BUFFER_SIZE];
+static int print_struct_callback(const void* buffer, size_t size, void* app_key) {
+    if(app_key) {
+        char* str = (char*)app_key;
+        size_t next = strlen(str);
+        strncpy(str + next, buffer, size);
+    } else {
+        uint8_t next = strlen(asn1_log);
+        strncpy(asn1_log + next, buffer, size);
+    }
+    return 0;
+}
+
 PassyReader* passy_reader_alloc(Passy* passy, Iso14443_4bPoller* iso14443_4b_poller) {
     PassyReader* passy_reader = malloc(sizeof(PassyReader));
     memset(passy_reader, 0, sizeof(PassyReader));
@@ -399,6 +412,35 @@ NfcCommand passy_reader_state_machine(Passy* passy, PassyReader* passy_reader) {
                 body_offset += sizeof(chunk);
             } while(body_offset < body_size);
             passy_log_bitbuffer(TAG, "DG1", passy_reader->DG1);
+
+        DG1_t* dg1 = 0;
+        dg1 = calloc(1, sizeof *dg1);
+        assert(dg1);
+        asn_dec_rval_t rval = asn_decode(0, ATS_DER, &asn_DEF_DG1, (void**)&dg1, bit_buffer_get_data(passy_reader->DG1), bit_buffer_get_size_bytes(passy_reader->DG1));
+
+        if(rval.code == RC_OK) {
+            FURI_LOG_I(TAG, "ASN.1 decode success");
+
+            char payloadDebug[384] = {0};
+            memset(payloadDebug, 0, sizeof(payloadDebug));
+            (&asn_DEF_DG1)
+                ->op->print_struct(
+                    &asn_DEF_DG1, dg1, 1, print_struct_callback, payloadDebug);
+            if(strlen(payloadDebug) > 0) {
+                FURI_LOG_D(TAG, "DG1: %s", payloadDebug);
+            } else {
+                FURI_LOG_D(TAG, "Received empty Payload");
+            }
+        } else {
+            FURI_LOG_E(TAG, "ASN.1 decode failed");
+        }
+
+        free(dg1);
+        dg1 = 0;
+
+
+
+
         } else if(passy->read_type == PassyReadDG2) {
             ret = passy_reader_select_file(passy_reader, 0x0102);
             if(ret != NfcCommandContinue) {

+ 3 - 0
passy_reader.h

@@ -8,6 +8,9 @@
 #include <lib/toolbox/stream/stream.h>
 #include <lib/toolbox/stream/file_stream.h>
 
+#define ASN_EMIT_DEBUG 0
+#include <lib/asn1/DG1.h>
+
 #include "passy_i.h"
 #include "passy_common.h"
 #include "secure_messaging.h"