blob: 73b5959c5e64b82b8254f85e7c2cfb8d348938cf [file] [log] [blame]
Lev Walkinf15320b2004-06-03 03:38:44 +00001/*-
2 * Copyright (c) 2003 Lev Walkin <vlm@lionet.info>. All rights reserved.
3 * Redistribution and modifications are permitted subject to BSD license.
4 */
Lev Walkina9cc46e2004-09-22 16:06:28 +00005#include <asn_internal.h>
Lev Walkinf15320b2004-06-03 03:38:44 +00006#include <INTEGER.h>
7#include <assert.h>
8#include <errno.h>
9
10/*
11 * INTEGER basic type description.
12 */
13static ber_tlv_tag_t asn1_DEF_INTEGER_tags[] = {
14 (ASN_TAG_CLASS_UNIVERSAL | (2 << 2))
15};
16asn1_TYPE_descriptor_t asn1_DEF_INTEGER = {
17 "INTEGER",
Lev Walkina9cc46e2004-09-22 16:06:28 +000018 INTEGER_free,
19 INTEGER_print,
Lev Walkinf15320b2004-06-03 03:38:44 +000020 asn_generic_no_constraint,
21 INTEGER_decode_ber,
22 INTEGER_encode_der,
Lev Walkina9cc46e2004-09-22 16:06:28 +000023 0, /* Not implemented yet */
24 INTEGER_encode_xer,
Lev Walkinf15320b2004-06-03 03:38:44 +000025 0, /* Use generic outmost tag fetcher */
26 asn1_DEF_INTEGER_tags,
Lev Walkin188ed2c2004-09-13 08:31:01 +000027 sizeof(asn1_DEF_INTEGER_tags) / sizeof(asn1_DEF_INTEGER_tags[0]),
28 asn1_DEF_INTEGER_tags, /* Same as above */
29 sizeof(asn1_DEF_INTEGER_tags) / sizeof(asn1_DEF_INTEGER_tags[0]),
Lev Walkind9bd7752004-06-05 08:17:50 +000030 0, /* Always in primitive form */
Lev Walkin449f8322004-08-20 13:23:42 +000031 0, 0, /* No members */
Lev Walkind9bd7752004-06-05 08:17:50 +000032 0 /* No specifics */
Lev Walkinf15320b2004-06-03 03:38:44 +000033};
34
35/*
36 * Decode INTEGER type.
37 */
38ber_dec_rval_t
39INTEGER_decode_ber(asn1_TYPE_descriptor_t *td,
40 void **int_structure, void *buf_ptr, size_t size, int tag_mode) {
Lev Walkinc2346572004-08-11 09:07:36 +000041 INTEGER_t *st = (INTEGER_t *)*int_structure;
Lev Walkinf15320b2004-06-03 03:38:44 +000042 ber_dec_rval_t rval;
Lev Walkinf15320b2004-06-03 03:38:44 +000043 ber_tlv_len_t length;
44
45 /*
46 * If the structure is not there, allocate it.
47 */
48 if(st == NULL) {
Lev Walkinc2346572004-08-11 09:07:36 +000049 (void *)st = *int_structure = CALLOC(1, sizeof(*st));
Lev Walkinf15320b2004-06-03 03:38:44 +000050 if(st == NULL) {
51 rval.code = RC_FAIL;
52 rval.consumed = 0;
53 return rval;
54 }
55 }
56
57 ASN_DEBUG("Decoding %s as INTEGER (tm=%d)",
58 td->name, tag_mode);
59
60 /*
61 * Check tags.
62 */
Lev Walkin5ccf1eb2004-09-24 20:58:47 +000063 rval = ber_check_tags(td, 0, buf_ptr, size, tag_mode, &length, 0);
Lev Walkinf15320b2004-06-03 03:38:44 +000064 if(rval.code != RC_OK)
65 return rval;
66
67 ASN_DEBUG("%s length is %d bytes", td->name, (int)length);
68
69 /*
70 * Make sure we have this length.
71 */
Lev Walkin4ce78ca2004-08-25 01:34:11 +000072 buf_ptr = ((char *)buf_ptr) + rval.consumed;
Lev Walkinf15320b2004-06-03 03:38:44 +000073 size -= rval.consumed;
Lev Walkind9bd7752004-06-05 08:17:50 +000074 if(length > (ber_tlv_len_t)size) {
Lev Walkinf15320b2004-06-03 03:38:44 +000075 rval.code = RC_WMORE;
76 rval.consumed = 0;
77 return rval;
78 }
79
Lev Walkinc2346572004-08-11 09:07:36 +000080 st->buf = (uint8_t *)MALLOC(length);
Lev Walkinf15320b2004-06-03 03:38:44 +000081 if(st->buf) {
82 st->size = length;
83 } else {
84 rval.code = RC_FAIL;
85 rval.consumed = 0;
86 return rval;
87 }
88
89 memcpy(st->buf, buf_ptr, st->size);
90
91 rval.code = RC_OK;
92 rval.consumed += length;
93
94 ASN_DEBUG("Took %ld/%ld bytes to encode %s",
95 (long)rval.consumed,
96 (long)length, td->name);
97
98 return rval;
99}
100
101/*
102 * Encode INTEGER type using DER.
103 */
Lev Walkina9cc46e2004-09-22 16:06:28 +0000104asn_enc_rval_t
Lev Walkinf15320b2004-06-03 03:38:44 +0000105INTEGER_encode_der(asn1_TYPE_descriptor_t *sd, void *ptr,
106 int tag_mode, ber_tlv_tag_t tag,
107 asn_app_consume_bytes_f *cb, void *app_key) {
Lev Walkina9cc46e2004-09-22 16:06:28 +0000108 asn_enc_rval_t erval;
Lev Walkinc2346572004-08-11 09:07:36 +0000109 INTEGER_t *st = (INTEGER_t *)ptr;
Lev Walkinf15320b2004-06-03 03:38:44 +0000110
111 ASN_DEBUG("%s %s as INTEGER (tm=%d)",
112 cb?"Encoding":"Estimating", sd->name, tag_mode);
113
114 /*
115 * Canonicalize integer in the buffer.
116 * (Remove too long sign extension, remove some first 0x00 bytes)
117 */
118 if(st->buf) {
119 uint8_t *buf = st->buf;
120 uint8_t *end1 = buf + st->size - 1;
121 int shift;
122
123 /* Compute the number of superfluous leading bytes */
124 for(; buf < end1; buf++) {
125 /*
126 * If the contents octets of an integer value encoding
127 * consist of more than one octet, then the bits of the
128 * first octet and bit 8 of the second octet:
129 * a) shall not all be ones; and
130 * b) shall not all be zero.
131 */
132 switch(*buf) {
133 case 0x00: if((buf[1] & 0x80) == 0)
134 continue;
135 break;
136 case 0xff: if((buf[1] & 0x80))
137 continue;
138 break;
139 }
140 break;
141 }
142
143 /* Remove leading superfluous bytes from the integer */
144 shift = buf - st->buf;
145 if(shift) {
146 uint8_t *nb = st->buf;
147 uint8_t *end;
148
149 st->size -= shift; /* New size, minus bad bytes */
150 end = nb + st->size;
151
152 for(; nb < end; nb++, buf++)
153 *nb = *buf;
154 }
155
156 } /* if(1) */
157
158 erval.encoded = der_write_tags(sd, st->size, tag_mode, tag,
159 cb, app_key);
160 ASN_DEBUG("INTEGER %s wrote tags %d", sd->name, (int)erval.encoded);
161 if(erval.encoded == -1) {
162 erval.failed_type = sd;
163 erval.structure_ptr = ptr;
164 return erval;
165 }
166
167 if(cb && st->buf) {
168 ssize_t ret;
169
170 ret = cb(st->buf, st->size, app_key);
171 if(ret == -1) {
172 erval.encoded = -1;
173 erval.failed_type = sd;
174 erval.structure_ptr = ptr;
175 return erval;
176 }
177 } else {
178 assert(st->buf || st->size == 0);
179 }
180
181 erval.encoded += st->size;
182
183 return erval;
184}
185
186/*
187 * INTEGER specific human-readable output.
188 */
Lev Walkina9cc46e2004-09-22 16:06:28 +0000189static ssize_t
190INTEGER__dump(const INTEGER_t *st, asn_app_consume_bytes_f *cb, void *app_key) {
Lev Walkindb13f512004-07-19 17:30:25 +0000191 char scratch[32]; /* Enough for 64-bit integer */
Lev Walkinf15320b2004-06-03 03:38:44 +0000192 uint8_t *buf = st->buf;
193 uint8_t *buf_end = st->buf + st->size;
194 signed long accum;
Lev Walkina9cc46e2004-09-22 16:06:28 +0000195 ssize_t wrote = 0;
Lev Walkinf15320b2004-06-03 03:38:44 +0000196 char *p;
197 int ret;
198
Lev Walkina9cc46e2004-09-22 16:06:28 +0000199 if(st->size == 0) {
200 return (cb("0", 1, app_key) < 0) ? -1 : 1;
201 }
Lev Walkinf15320b2004-06-03 03:38:44 +0000202
Lev Walkindb13f512004-07-19 17:30:25 +0000203 /*
204 * Advance buf pointer until the start of the value's body.
205 * This will make us able to process large integers using simple case,
206 * when the actual value is small
207 * (0x0000000000abcdef would yield a fine 0x00abcdef)
208 */
209 /* Skip the insignificant leading bytes */
210 for(; buf < buf_end-1; buf++) {
211 switch(*buf) {
212 case 0x00: if((buf[1] & 0x80) == 0) continue; break;
213 case 0xff: if((buf[1] & 0x80) != 0) continue; break;
214 }
215 break;
216 }
217
Lev Walkinf15320b2004-06-03 03:38:44 +0000218 /* Simple case: the integer size is small */
Lev Walkindb13f512004-07-19 17:30:25 +0000219 if((size_t)(buf_end - buf) <= sizeof(accum)) {
220 accum = (*buf & 0x80) ? -1 : 0;
Lev Walkinf15320b2004-06-03 03:38:44 +0000221 for(; buf < buf_end; buf++)
222 accum = (accum << 8) | *buf;
223 ret = snprintf(scratch, sizeof(scratch), "%ld", accum);
Lev Walkind9bd7752004-06-05 08:17:50 +0000224 assert(ret > 0 && ret < (int)sizeof(scratch));
Lev Walkina9cc46e2004-09-22 16:06:28 +0000225 return (cb(scratch, ret, app_key) < 0) ? -1 : ret;
Lev Walkinf15320b2004-06-03 03:38:44 +0000226 }
227
228 /* Output in the long xx:yy:zz... format */
Lev Walkindb13f512004-07-19 17:30:25 +0000229 /* TODO: replace with generic algorithm (Knuth TAOCP Vol 2, 4.3.1) */
Lev Walkinf15320b2004-06-03 03:38:44 +0000230 for(p = scratch; buf < buf_end; buf++) {
Lev Walkin4d9528c2004-08-11 08:10:13 +0000231 static const char *h2c = "0123456789ABCDEF";
Lev Walkindb13f512004-07-19 17:30:25 +0000232 if((p - scratch) >= (ssize_t)(sizeof(scratch) - 4)) {
Lev Walkinf15320b2004-06-03 03:38:44 +0000233 /* Flush buffer */
Lev Walkina9cc46e2004-09-22 16:06:28 +0000234 if(cb(scratch, p - scratch, app_key) < 0)
Lev Walkinf15320b2004-06-03 03:38:44 +0000235 return -1;
Lev Walkina9cc46e2004-09-22 16:06:28 +0000236 wrote += p - scratch;
Lev Walkinf15320b2004-06-03 03:38:44 +0000237 p = scratch;
238 }
239 *p++ = h2c[*buf >> 4];
240 *p++ = h2c[*buf & 0x0F];
241 *p++ = ':';
242 }
Lev Walkindb13f512004-07-19 17:30:25 +0000243 if(p != scratch)
244 p--; /* Remove the last ':' */
Lev Walkinf15320b2004-06-03 03:38:44 +0000245
Lev Walkina9cc46e2004-09-22 16:06:28 +0000246 wrote += p - scratch;
247 return (cb(scratch, p - scratch, app_key) < 0) ? -1 : wrote;
248}
249
250/*
251 * INTEGER specific human-readable output.
252 */
253int
254INTEGER_print(asn1_TYPE_descriptor_t *td, const void *sptr, int ilevel,
255 asn_app_consume_bytes_f *cb, void *app_key) {
256 const INTEGER_t *st = (const INTEGER_t *)sptr;
257
258 (void)td;
259 (void)ilevel;
260
261 if(!st && !st->buf) return cb("<absent>", 8, app_key);
262
263 return (INTEGER__dump(st, cb, app_key) < 0) ? -1 : 0;
264}
265
266asn_enc_rval_t
267INTEGER_encode_xer(asn1_TYPE_descriptor_t *td, void *sptr,
268 int ilevel, enum xer_encoder_flags_e flags,
269 asn_app_consume_bytes_f *cb, void *app_key) {
270 const INTEGER_t *st = (const INTEGER_t *)sptr;
271 asn_enc_rval_t er;
272
273 (void)ilevel;
274 (void)flags;
275
276 if(!st && !st->buf)
277 _ASN_ENCODE_FAILED;
278
279 er.encoded = INTEGER__dump(st, cb, app_key);
280 if(er.encoded < 0) _ASN_ENCODE_FAILED;
281
282 return er;
Lev Walkinf15320b2004-06-03 03:38:44 +0000283}
284
285void
286INTEGER_free(asn1_TYPE_descriptor_t *td, void *ptr, int contents_only) {
Lev Walkinc2346572004-08-11 09:07:36 +0000287 INTEGER_t *st = (INTEGER_t *)ptr;
Lev Walkinf15320b2004-06-03 03:38:44 +0000288
289 if(!td || !st)
290 return;
291
292 ASN_DEBUG("Freeing %s as INTEGER (%d, %p, %p)",
293 td->name, contents_only, st, st->buf);
294
295 if(st->buf) {
296 FREEMEM(st->buf);
297 }
298
299 if(!contents_only) {
300 FREEMEM(st);
301 }
302}
303
304int
305asn1_INTEGER2long(const INTEGER_t *iptr, long *lptr) {
306 uint8_t *b, *end;
307 size_t size;
308 long l;
309
310 /* Sanity checking */
311 if(!iptr || !iptr->buf || !lptr) {
312 errno = EINVAL;
313 return -1;
314 }
315
316 /* Cache the begin/end of the buffer */
317 b = iptr->buf; /* Start of the INTEGER buffer */
318 size = iptr->size;
319 end = b + size; /* Where to stop */
320
321 if(size > sizeof(long)) {
322 uint8_t *end1 = end - 1;
323 /*
324 * Slightly more advanced processing,
325 * able to >sizeof(long) bytes,
326 * when the actual value is small
327 * (0x0000000000abcdef would yield a fine 0x00abcdef)
328 */
329 /* Skip out the insignificant leading bytes */
330 for(; b < end1; b++) {
331 switch(*b) {
332 case 0x00: if((b[1] & 0x80) == 0) continue; break;
333 case 0xff: if((b[1] & 0x80) != 0) continue; break;
334 }
335 break;
336 }
337
338 size = end - b;
339 if(size > sizeof(long)) {
340 /* Still cannot fit the long */
341 errno = ERANGE;
342 return -1;
343 }
344 }
345
346 /* Shortcut processing of a corner case */
347 if(end == b) {
348 *lptr = 0;
349 return 0;
350 }
351
352 /* Perform the sign initialization */
353 /* Actually l = -(*b >> 7); gains nothing, yet unreadable! */
354 if((*b >> 7)) l = -1; else l = 0;
355
356 /* Conversion engine */
357 for(; b < end; b++)
358 l = (l << 8) | *b;
359
360 *lptr = l;
361 return 0;
362}