--- /dev/null
+/*-\r
+ * Copyright (c) 2003, 2004 Lev Walkin <vlm@lionet.info>. All rights reserved.\r
+ * Redistribution and modifications are permitted subject to BSD license.\r
+ */\r
+#include <asn_internal.h>\r
+#include <errno.h>\r
+\r
+static ssize_t der_write_TL(ber_tlv_tag_t tag, ber_tlv_len_t len,\r
+ asn_app_consume_bytes_f *cb, void *app_key, int constructed);\r
+\r
+/*\r
+ * The DER encoder of any type.\r
+ */\r
+asn_enc_rval_t\r
+der_encode(const asn_TYPE_descriptor_t *type_descriptor, const void *struct_ptr,\r
+ asn_app_consume_bytes_f *consume_bytes, void *app_key) {\r
+ ASN_DEBUG("DER encoder invoked for %s",\r
+ type_descriptor->name);\r
+\r
+ /*\r
+ * Invoke type-specific encoder.\r
+ */\r
+ return type_descriptor->op->der_encoder(\r
+ type_descriptor, struct_ptr, /* Pointer to the destination structure */\r
+ 0, 0, consume_bytes, app_key);\r
+}\r
+\r
+/*\r
+ * Argument type and callback necessary for der_encode_to_buffer().\r
+ */\r
+typedef struct enc_to_buf_arg {\r
+ void *buffer;\r
+ size_t left;\r
+} enc_to_buf_arg;\r
+static int encode_to_buffer_cb(const void *buffer, size_t size, void *key) {\r
+ enc_to_buf_arg *arg = (enc_to_buf_arg *)key;\r
+\r
+ if(arg->left < size)\r
+ return -1; /* Data exceeds the available buffer size */\r
+\r
+ memcpy(arg->buffer, buffer, size);\r
+ arg->buffer = ((char *)arg->buffer) + size;\r
+ arg->left -= size;\r
+\r
+ return 0;\r
+}\r
+\r
+/*\r
+ * A variant of the der_encode() which encodes the data into the provided buffer\r
+ */\r
+asn_enc_rval_t\r
+der_encode_to_buffer(const asn_TYPE_descriptor_t *type_descriptor,\r
+ const void *struct_ptr, void *buffer, size_t buffer_size) {\r
+ enc_to_buf_arg arg;\r
+ asn_enc_rval_t ec;\r
+\r
+ arg.buffer = buffer;\r
+ arg.left = buffer_size;\r
+\r
+ ec = type_descriptor->op->der_encoder(type_descriptor,\r
+ struct_ptr, /* Pointer to the destination structure */\r
+ 0, 0, encode_to_buffer_cb, &arg);\r
+ if(ec.encoded != -1) {\r
+ assert(ec.encoded == (ssize_t)(buffer_size - arg.left));\r
+ /* Return the encoded contents size */\r
+ }\r
+ return ec;\r
+}\r
+\r
+\r
+/*\r
+ * Write out leading TL[v] sequence according to the type definition.\r
+ */\r
+ssize_t\r
+der_write_tags(const asn_TYPE_descriptor_t *sd, size_t struct_length,\r
+ int tag_mode, int last_tag_form,\r
+ ber_tlv_tag_t tag, /* EXPLICIT or IMPLICIT tag */\r
+ asn_app_consume_bytes_f *cb, void *app_key) {\r
+#define ASN1_DER_MAX_TAGS_COUNT 4\r
+ ber_tlv_tag_t\r
+ tags_buf_scratch[ASN1_DER_MAX_TAGS_COUNT * sizeof(ber_tlv_tag_t)];\r
+ ssize_t lens[ASN1_DER_MAX_TAGS_COUNT * sizeof(ssize_t)];\r
+ const ber_tlv_tag_t *tags; /* Copy of tags stream */\r
+ int tags_count; /* Number of tags */\r
+ size_t overall_length;\r
+ int i;\r
+\r
+ ASN_DEBUG("Writing tags (%s, tm=%d, tc=%d, tag=%s, mtc=%d)",\r
+ sd->name, tag_mode, sd->tags_count,\r
+ ber_tlv_tag_string(tag),\r
+ tag_mode\r
+ ?(sd->tags_count+1\r
+ -((tag_mode == -1) && sd->tags_count))\r
+ :sd->tags_count\r
+ );\r
+\r
+ if(sd->tags_count + 1 > ASN1_DER_MAX_TAGS_COUNT) {\r
+ ASN_DEBUG("System limit %d on tags count", ASN1_DER_MAX_TAGS_COUNT);\r
+ return -1;\r
+ }\r
+\r
+ if(tag_mode) {\r
+ /*\r
+ * Instead of doing shaman dance like we do in ber_check_tags(),\r
+ * allocate a small array on the stack\r
+ * and initialize it appropriately.\r
+ */\r
+ int stag_offset;\r
+ ber_tlv_tag_t *tags_buf = tags_buf_scratch;\r
+ tags_count = sd->tags_count\r
+ + 1 /* EXPLICIT or IMPLICIT tag is given */\r
+ - ((tag_mode == -1) && sd->tags_count);\r
+ /* Copy tags over */\r
+ tags_buf[0] = tag;\r
+ stag_offset = -1 + ((tag_mode == -1) && sd->tags_count);\r
+ for(i = 1; i < tags_count; i++)\r
+ tags_buf[i] = sd->tags[i + stag_offset];\r
+ tags = tags_buf;\r
+ } else {\r
+ tags = sd->tags;\r
+ tags_count = sd->tags_count;\r
+ }\r
+\r
+ /* No tags to write */\r
+ if(tags_count == 0)\r
+ return 0;\r
+\r
+ /*\r
+ * Array of tags is initialized.\r
+ * Now, compute the size of the TLV pairs, from right to left.\r
+ */\r
+ overall_length = struct_length;\r
+ for(i = tags_count - 1; i >= 0; --i) {\r
+ lens[i] = der_write_TL(tags[i], overall_length, 0, 0, 0);\r
+ if(lens[i] == -1) return -1;\r
+ overall_length += lens[i];\r
+ lens[i] = overall_length - lens[i];\r
+ }\r
+\r
+ if(!cb) return overall_length - struct_length;\r
+\r
+ ASN_DEBUG("Encoding %s TL sequence (%d elements)", sd->name,\r
+ tags_count);\r
+\r
+ /*\r
+ * Encode the TL sequence for real.\r
+ */\r
+ for(i = 0; i < tags_count; i++) {\r
+ ssize_t len;\r
+ int _constr;\r
+\r
+ /* Check if this tag happens to be constructed */\r
+ _constr = (last_tag_form || i < (tags_count - 1));\r
+\r
+ len = der_write_TL(tags[i], lens[i], cb, app_key, _constr);\r
+ if(len == -1) return -1;\r
+ }\r
+\r
+ return overall_length - struct_length;\r
+}\r
+\r
+static ssize_t\r
+der_write_TL(ber_tlv_tag_t tag, ber_tlv_len_t len,\r
+ asn_app_consume_bytes_f *cb, void *app_key,\r
+ int constructed) {\r
+ uint8_t buf[32];\r
+ size_t size = 0;\r
+ int buf_size = cb?sizeof(buf):0;\r
+ ssize_t tmp;\r
+\r
+ /* Serialize tag (T from TLV) into possibly zero-length buffer */\r
+ tmp = ber_tlv_tag_serialize(tag, buf, buf_size);\r
+ if(tmp == -1 || tmp > (ssize_t)sizeof(buf)) return -1;\r
+ size += tmp;\r
+\r
+ /* Serialize length (L from TLV) into possibly zero-length buffer */\r
+ tmp = der_tlv_length_serialize(len, buf+size, buf_size?buf_size-size:0);\r
+ if(tmp == -1) return -1;\r
+ size += tmp;\r
+\r
+ if(size > sizeof(buf))\r
+ return -1;\r
+\r
+ /*\r
+ * If callback is specified, invoke it, and check its return value.\r
+ */\r
+ if(cb) {\r
+ if(constructed) *buf |= 0x20;\r
+ if(cb(buf, size, app_key) < 0)\r
+ return -1;\r
+ }\r
+\r
+ return size;\r
+}\r