8d9e82c1b1e00a5ce9bac82bdcb801f895a9d17b
[ric-app/kpimon.git] / asn1c_defs / all-defs / der_encoder.c
1 /*-\r
2  * Copyright (c) 2003, 2004 Lev Walkin <vlm@lionet.info>. All rights reserved.\r
3  * Redistribution and modifications are permitted subject to BSD license.\r
4  */\r
5 #include <asn_internal.h>\r
6 #include <errno.h>\r
7 \r
8 static ssize_t der_write_TL(ber_tlv_tag_t tag, ber_tlv_len_t len,\r
9         asn_app_consume_bytes_f *cb, void *app_key, int constructed);\r
10 \r
11 /*\r
12  * The DER encoder of any type.\r
13  */\r
14 asn_enc_rval_t\r
15 der_encode(const asn_TYPE_descriptor_t *type_descriptor, const void *struct_ptr,\r
16            asn_app_consume_bytes_f *consume_bytes, void *app_key) {\r
17     ASN_DEBUG("DER encoder invoked for %s",\r
18                 type_descriptor->name);\r
19 \r
20         /*\r
21          * Invoke type-specific encoder.\r
22          */\r
23     return type_descriptor->op->der_encoder(\r
24         type_descriptor, struct_ptr, /* Pointer to the destination structure */\r
25         0, 0, consume_bytes, app_key);\r
26 }\r
27 \r
28 /*\r
29  * Argument type and callback necessary for der_encode_to_buffer().\r
30  */\r
31 typedef struct enc_to_buf_arg {\r
32         void *buffer;\r
33         size_t left;\r
34 } enc_to_buf_arg;\r
35 static int encode_to_buffer_cb(const void *buffer, size_t size, void *key) {\r
36         enc_to_buf_arg *arg = (enc_to_buf_arg *)key;\r
37 \r
38         if(arg->left < size)\r
39                 return -1;      /* Data exceeds the available buffer size */\r
40 \r
41         memcpy(arg->buffer, buffer, size);\r
42         arg->buffer = ((char *)arg->buffer) + size;\r
43         arg->left -= size;\r
44 \r
45         return 0;\r
46 }\r
47 \r
48 /*\r
49  * A variant of the der_encode() which encodes the data into the provided buffer\r
50  */\r
51 asn_enc_rval_t\r
52 der_encode_to_buffer(const asn_TYPE_descriptor_t *type_descriptor,\r
53                      const void *struct_ptr, void *buffer, size_t buffer_size) {\r
54     enc_to_buf_arg arg;\r
55         asn_enc_rval_t ec;\r
56 \r
57         arg.buffer = buffer;\r
58         arg.left = buffer_size;\r
59 \r
60         ec = type_descriptor->op->der_encoder(type_descriptor,\r
61                 struct_ptr,     /* Pointer to the destination structure */\r
62                 0, 0, encode_to_buffer_cb, &arg);\r
63         if(ec.encoded != -1) {\r
64                 assert(ec.encoded == (ssize_t)(buffer_size - arg.left));\r
65                 /* Return the encoded contents size */\r
66         }\r
67         return ec;\r
68 }\r
69 \r
70 \r
71 /*\r
72  * Write out leading TL[v] sequence according to the type definition.\r
73  */\r
74 ssize_t\r
75 der_write_tags(const asn_TYPE_descriptor_t *sd, size_t struct_length,\r
76                int tag_mode, int last_tag_form,\r
77                ber_tlv_tag_t tag, /* EXPLICIT or IMPLICIT tag */\r
78                asn_app_consume_bytes_f *cb, void *app_key) {\r
79 #define ASN1_DER_MAX_TAGS_COUNT 4\r
80     ber_tlv_tag_t\r
81         tags_buf_scratch[ASN1_DER_MAX_TAGS_COUNT * sizeof(ber_tlv_tag_t)];\r
82     ssize_t lens[ASN1_DER_MAX_TAGS_COUNT * sizeof(ssize_t)];\r
83     const ber_tlv_tag_t *tags; /* Copy of tags stream */\r
84     int tags_count;            /* Number of tags */\r
85     size_t overall_length;\r
86     int i;\r
87 \r
88     ASN_DEBUG("Writing tags (%s, tm=%d, tc=%d, tag=%s, mtc=%d)",\r
89                 sd->name, tag_mode, sd->tags_count,\r
90                 ber_tlv_tag_string(tag),\r
91                 tag_mode\r
92                         ?(sd->tags_count+1\r
93                                 -((tag_mode == -1) && sd->tags_count))\r
94                         :sd->tags_count\r
95         );\r
96 \r
97     if(sd->tags_count + 1 > ASN1_DER_MAX_TAGS_COUNT) {\r
98         ASN_DEBUG("System limit %d on tags count", ASN1_DER_MAX_TAGS_COUNT);\r
99         return -1;\r
100     }\r
101 \r
102         if(tag_mode) {\r
103                 /*\r
104                  * Instead of doing shaman dance like we do in ber_check_tags(),\r
105                  * allocate a small array on the stack\r
106                  * and initialize it appropriately.\r
107                  */\r
108                 int stag_offset;\r
109                 ber_tlv_tag_t *tags_buf = tags_buf_scratch;\r
110                 tags_count = sd->tags_count\r
111                         + 1     /* EXPLICIT or IMPLICIT tag is given */\r
112                         - ((tag_mode == -1) && sd->tags_count);\r
113                 /* Copy tags over */\r
114                 tags_buf[0] = tag;\r
115                 stag_offset = -1 + ((tag_mode == -1) && sd->tags_count);\r
116                 for(i = 1; i < tags_count; i++)\r
117                         tags_buf[i] = sd->tags[i + stag_offset];\r
118                 tags = tags_buf;\r
119         } else {\r
120                 tags = sd->tags;\r
121                 tags_count = sd->tags_count;\r
122         }\r
123 \r
124         /* No tags to write */\r
125         if(tags_count == 0)\r
126                 return 0;\r
127 \r
128         /*\r
129          * Array of tags is initialized.\r
130          * Now, compute the size of the TLV pairs, from right to left.\r
131          */\r
132         overall_length = struct_length;\r
133         for(i = tags_count - 1; i >= 0; --i) {\r
134                 lens[i] = der_write_TL(tags[i], overall_length, 0, 0, 0);\r
135                 if(lens[i] == -1) return -1;\r
136                 overall_length += lens[i];\r
137                 lens[i] = overall_length - lens[i];\r
138         }\r
139 \r
140         if(!cb) return overall_length - struct_length;\r
141 \r
142         ASN_DEBUG("Encoding %s TL sequence (%d elements)", sd->name,\r
143                   tags_count);\r
144 \r
145         /*\r
146          * Encode the TL sequence for real.\r
147          */\r
148         for(i = 0; i < tags_count; i++) {\r
149                 ssize_t len;\r
150                 int _constr;\r
151 \r
152                 /* Check if this tag happens to be constructed */\r
153                 _constr = (last_tag_form || i < (tags_count - 1));\r
154 \r
155                 len = der_write_TL(tags[i], lens[i], cb, app_key, _constr);\r
156                 if(len == -1) return -1;\r
157         }\r
158 \r
159         return overall_length - struct_length;\r
160 }\r
161 \r
162 static ssize_t\r
163 der_write_TL(ber_tlv_tag_t tag, ber_tlv_len_t len,\r
164                 asn_app_consume_bytes_f *cb, void *app_key,\r
165                 int constructed) {\r
166         uint8_t buf[32];\r
167         size_t size = 0;\r
168         int buf_size = cb?sizeof(buf):0;\r
169         ssize_t tmp;\r
170 \r
171         /* Serialize tag (T from TLV) into possibly zero-length buffer */\r
172         tmp = ber_tlv_tag_serialize(tag, buf, buf_size);\r
173         if(tmp == -1 || tmp > (ssize_t)sizeof(buf)) return -1;\r
174         size += tmp;\r
175 \r
176         /* Serialize length (L from TLV) into possibly zero-length buffer */\r
177         tmp = der_tlv_length_serialize(len, buf+size, buf_size?buf_size-size:0);\r
178         if(tmp == -1) return -1;\r
179         size += tmp;\r
180 \r
181         if(size > sizeof(buf))\r
182                 return -1;\r
183 \r
184         /*\r
185          * If callback is specified, invoke it, and check its return value.\r
186          */\r
187         if(cb) {\r
188                 if(constructed) *buf |= 0x20;\r
189                 if(cb(buf, size, app_key) < 0)\r
190                         return -1;\r
191         }\r
192 \r
193         return size;\r
194 }\r