Simplify integer loading in ASN.1 encoding
[krb5.git] / src / lib / krb5 / asn.1 / asn1_encode.c
1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /* lib/krb5/asn.1/asn1_encode.c */
3 /*
4  * Copyright 1994, 2008 by the Massachusetts Institute of Technology.
5  * All Rights Reserved.
6  *
7  * Export of this software from the United States of America may
8  *   require a specific license from the United States Government.
9  *   It is the responsibility of any person or organization contemplating
10  *   export to obtain such a license before exporting.
11  *
12  * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
13  * distribute this software and its documentation for any purpose and
14  * without fee is hereby granted, provided that the above copyright
15  * notice appear in all copies and that both that copyright notice and
16  * this permission notice appear in supporting documentation, and that
17  * the name of M.I.T. not be used in advertising or publicity pertaining
18  * to distribution of the software without specific, written prior
19  * permission.  Furthermore if you modify this software you must label
20  * your software as modified software and not distribute it in such a
21  * fashion that it might be confused with the original M.I.T. software.
22  * M.I.T. makes no representations about the suitability of
23  * this software for any purpose.  It is provided "as is" without express
24  * or implied warranty.
25  */
26
27 /* ASN.1 primitive encoders */
28
29 #include "asn1_encode.h"
30 #include "asn1_make.h"
31
32 asn1_error_code
33 asn1_encode_boolean(asn1buf *buf, asn1_intmax val, unsigned int *retlen)
34 {
35     asn1_octet bval = val ? 0xFF : 0x00;
36
37     *retlen = 1;
38     return asn1buf_insert_octet(buf, bval);
39 }
40
41 asn1_error_code
42 asn1_encode_integer(asn1buf *buf, asn1_intmax val, unsigned int *retlen)
43 {
44     asn1_error_code retval;
45     unsigned int length = 0;
46     long valcopy;
47     int digit;
48
49     valcopy = val;
50     do {
51         digit = (int) (valcopy&0xFF);
52         retval = asn1buf_insert_octet(buf,(asn1_octet) digit);
53         if (retval) return retval;
54         length++;
55         valcopy = valcopy >> 8;
56     } while (valcopy != 0 && valcopy != ~0);
57
58     if ((val > 0) && ((digit&0x80) == 0x80)) { /* make sure the high bit is */
59         retval = asn1buf_insert_octet(buf,0); /* of the proper signed-ness */
60         if (retval) return retval;
61         length++;
62     } else if ((val < 0) && ((digit&0x80) != 0x80)) {
63         retval = asn1buf_insert_octet(buf,0xFF);
64         if (retval) return retval;
65         length++;
66     }
67
68
69     *retlen = length;
70     return 0;
71 }
72
73 asn1_error_code
74 asn1_encode_unsigned_integer(asn1buf *buf, asn1_uintmax val,
75                              unsigned int *retlen)
76 {
77     asn1_error_code retval;
78     unsigned int length = 0;
79     unsigned long valcopy;
80     int digit;
81
82     valcopy = val;
83     do {
84         digit = (int) (valcopy&0xFF);
85         retval = asn1buf_insert_octet(buf,(asn1_octet) digit);
86         if (retval) return retval;
87         length++;
88         valcopy = valcopy >> 8;
89     } while (valcopy != 0);
90
91     if (digit&0x80) {                     /* make sure the high bit is */
92         retval = asn1buf_insert_octet(buf,0); /* of the proper signed-ness */
93         if (retval) return retval;
94         length++;
95     }
96
97     *retlen = length;
98     return 0;
99 }
100
101 asn1_error_code
102 asn1_encode_bytestring(asn1buf *buf, unsigned int len, const void *val,
103                        unsigned int *retlen)
104 {
105     if (len > 0 && val == NULL) return ASN1_MISSING_FIELD;
106     *retlen = len;
107     return asn1buf_insert_octetstring(buf, len, val);
108 }
109
110 asn1_error_code
111 asn1_encode_generaltime(asn1buf *buf, time_t val, unsigned int *retlen)
112 {
113     struct tm *gtime, gtimebuf;
114     char s[16], *sp;
115     time_t gmt_time = val;
116
117     /*
118      * Time encoding: YYYYMMDDhhmmssZ
119      */
120     if (gmt_time == 0) {
121         sp = "19700101000000Z";
122     } else {
123         int len;
124
125         /*
126          * Sanity check this just to be paranoid, as gmtime can return NULL,
127          * and some bogus implementations might overrun on the sprintf.
128          */
129 #ifdef HAVE_GMTIME_R
130 # ifdef GMTIME_R_RETURNS_INT
131         if (gmtime_r(&gmt_time, &gtimebuf) != 0)
132             return ASN1_BAD_GMTIME;
133 # else
134         if (gmtime_r(&gmt_time, &gtimebuf) == NULL)
135             return ASN1_BAD_GMTIME;
136 # endif
137 #else
138         gtime = gmtime(&gmt_time);
139         if (gtime == NULL)
140             return ASN1_BAD_GMTIME;
141         memcpy(&gtimebuf, gtime, sizeof(gtimebuf));
142 #endif
143         gtime = &gtimebuf;
144
145         if (gtime->tm_year > 8099 || gtime->tm_mon > 11 ||
146             gtime->tm_mday > 31 || gtime->tm_hour > 23 ||
147             gtime->tm_min > 59 || gtime->tm_sec > 59)
148             return ASN1_BAD_GMTIME;
149         len = snprintf(s, sizeof(s), "%04d%02d%02d%02d%02d%02dZ",
150                        1900+gtime->tm_year, gtime->tm_mon+1,
151                        gtime->tm_mday, gtime->tm_hour,
152                        gtime->tm_min, gtime->tm_sec);
153         if (SNPRINTF_OVERFLOW(len, sizeof(s)))
154             /* Shouldn't be possible given above tests.  */
155             return ASN1_BAD_GMTIME;
156         sp = s;
157     }
158
159     return asn1_encode_bytestring(buf, 15, sp, retlen);
160 }
161
162 asn1_error_code
163 asn1_encode_bitstring(asn1buf *buf, unsigned int len, const void *val,
164                       unsigned int *retlen)
165 {
166     asn1_error_code retval;
167
168     retval = asn1buf_insert_octetstring(buf, len, val);
169     if (retval) return retval;
170     *retlen = len + 1;
171     return asn1buf_insert_octet(buf, '\0');
172 }
173
174 /*
175  * ASN.1 constructed type encoder engine
176  *
177  * Two entry points here:
178  *
179  * krb5int_asn1_encode_type: Incrementally adds the contents-only encoding of
180  * an object to an already-initialized asn1buf, and returns its tag
181  * information.
182  *
183  * krb5int_asn1_do_full_encode: Returns a completed encoding, in the
184  * correct byte order, in an allocated krb5_data.
185  */
186
187 #ifdef POINTERS_ARE_ALL_THE_SAME
188 #define LOADPTR(PTR,TYPE)                       \
189     (*(const void *const *)(PTR))
190 #else
191 #define LOADPTR(PTR,PTRINFO)                                            \
192     (assert((PTRINFO)->loadptr != NULL), (PTRINFO)->loadptr(PTR))
193 #endif
194
195 static unsigned int
196 get_nullterm_sequence_len(const void *valp, const struct atype_info *seq)
197 {
198     unsigned int i;
199     const struct atype_info *a;
200     const struct ptr_info *ptr;
201     const void *elt, *eltptr;
202
203     a = seq;
204     i = 0;
205     assert(a->type == atype_ptr);
206     assert(seq->size != 0);
207     ptr = a->tinfo;
208
209     while (1) {
210         eltptr = (const char *) valp + i * seq->size;
211         elt = LOADPTR(eltptr, ptr);
212         if (elt == NULL)
213             break;
214         i++;
215     }
216     return i;
217 }
218 static asn1_error_code
219 encode_sequence_of(asn1buf *buf, unsigned int seqlen, const void *val,
220                    const struct atype_info *eltinfo,
221                    unsigned int *retlen);
222
223 static asn1_error_code
224 encode_nullterm_sequence_of(asn1buf *buf, const void *val,
225                             const struct atype_info *type,
226                             int can_be_empty,
227                             unsigned int *retlen)
228 {
229     unsigned int length = get_nullterm_sequence_len(val, type);
230     if (!can_be_empty && length == 0) return ASN1_MISSING_FIELD;
231     return encode_sequence_of(buf, length, val, type, retlen);
232 }
233
234 static asn1_intmax
235 load_int(const void *val, size_t size)
236 {
237     switch (size) {
238     case 1: return *(signed char *)val;
239     case 2: return *(krb5_int16 *)val;
240     case 4: return *(krb5_int32 *)val;
241     case 8: return *(INT64_TYPE *)val;
242     default: abort();
243     }
244 }
245
246 static asn1_uintmax
247 load_uint(const void *val, size_t size)
248 {
249     switch (size) {
250     case 1: return *(unsigned char *)val;
251     case 2: return *(krb5_ui_2 *)val;
252     case 4: return *(krb5_ui_4 *)val;
253     case 8: return *(UINT64_TYPE *)val;
254     default: abort();
255     }
256 }
257
258 static asn1_error_code
259 just_encode_sequence(asn1buf *buf, const void *val,
260                      const struct seq_info *seq,
261                      unsigned int *retlen);
262 static asn1_error_code
263 encode_a_field(asn1buf *buf, const void *val, const struct field_info *field,
264                taginfo *rettag);
265
266 /* Encode a value (contents only, no outer tag) according to a type, and return
267  * its encoded tag information. */
268 asn1_error_code
269 krb5int_asn1_encode_type(asn1buf *buf, const void *val,
270                          const struct atype_info *a, taginfo *rettag)
271 {
272     asn1_error_code retval;
273
274     switch (a->type) {
275     case atype_primitive: {
276         const struct primitive_info *prim = a->tinfo;
277         assert(prim->enc != NULL);
278         retval = prim->enc(buf, val, &rettag->length);
279         if (retval) return retval;
280         rettag->asn1class = UNIVERSAL;
281         rettag->construction = PRIMITIVE;
282         rettag->tagnum = prim->tagval;
283         break;
284     }
285     case atype_fn: {
286         const struct fn_info *fn = a->tinfo;
287         assert(fn->enc != NULL);
288         return fn->enc(buf, val, rettag);
289     }
290     case atype_sequence:
291         assert(a->tinfo != NULL);
292         retval = just_encode_sequence(buf, val, a->tinfo, &rettag->length);
293         if (retval)
294             return retval;
295         rettag->asn1class = UNIVERSAL;
296         rettag->construction = CONSTRUCTED;
297         rettag->tagnum = ASN1_SEQUENCE;
298         break;
299     case atype_ptr: {
300         const struct ptr_info *ptr = a->tinfo;
301         assert(ptr->basetype != NULL);
302         return krb5int_asn1_encode_type(buf, LOADPTR(val, ptr), ptr->basetype,
303                                         rettag);
304     }
305     case atype_field:
306         assert(a->tinfo != NULL);
307         return encode_a_field(buf, val, a->tinfo, rettag);
308     case atype_nullterm_sequence_of:
309     case atype_nonempty_nullterm_sequence_of:
310         assert(a->tinfo != NULL);
311         retval = encode_nullterm_sequence_of(buf, val, a->tinfo,
312                                              a->type ==
313                                              atype_nullterm_sequence_of,
314                                              &rettag->length);
315         if (retval)
316             return retval;
317         rettag->asn1class = UNIVERSAL;
318         rettag->construction = CONSTRUCTED;
319         rettag->tagnum = ASN1_SEQUENCE;
320         break;
321     case atype_tagged_thing: {
322         const struct tagged_info *tag = a->tinfo;
323         retval = krb5int_asn1_encode_type(buf, val, tag->basetype, rettag);
324         if (retval)
325             return retval;
326         if (!tag->implicit) {
327             unsigned int tlen;
328             retval = asn1_make_tag(buf, rettag->asn1class,
329                                    rettag->construction, rettag->tagnum,
330                                    rettag->length, &tlen);
331             if (retval)
332                 return retval;
333             rettag->length += tlen;
334             rettag->construction = tag->construction;
335         }
336         rettag->asn1class = tag->tagtype;
337         rettag->tagnum = tag->tagval;
338         break;
339     }
340     case atype_int: {
341         retval = asn1_encode_integer(buf, load_int(val, a->size),
342                                      &rettag->length);
343         if (retval)
344             return retval;
345         rettag->asn1class = UNIVERSAL;
346         rettag->construction = PRIMITIVE;
347         rettag->tagnum = ASN1_INTEGER;
348         break;
349     }
350     case atype_uint: {
351         retval = asn1_encode_unsigned_integer(buf, load_uint(val, a->size),
352                                               &rettag->length);
353         if (retval)
354             return retval;
355         rettag->asn1class = UNIVERSAL;
356         rettag->construction = PRIMITIVE;
357         rettag->tagnum = ASN1_INTEGER;
358         break;
359     }
360     case atype_min:
361     case atype_max:
362     case atype_string:          /* only usable with field_string */
363     case atype_der:             /* only usable with field_der */
364     case atype_choice:          /* only usable with field_choice */
365     default:
366         assert(a->type > atype_min);
367         assert(a->type < atype_max);
368         assert(a->type != atype_string);
369         assert(a->type != atype_der);
370         assert(a->type != atype_choice);
371         abort();
372     }
373
374     return 0;
375 }
376
377 static asn1_error_code
378 encode_type_and_tag(asn1buf *buf, const void *val, const struct atype_info *a,
379                     unsigned int *retlen)
380 {
381     taginfo t;
382     asn1_error_code retval;
383     unsigned int tlen;
384
385     retval = krb5int_asn1_encode_type(buf, val, a, &t);
386     if (retval)
387         return retval;
388     retval = asn1_make_tag(buf, t.asn1class, t.construction, t.tagnum,
389                            t.length, &tlen);
390     if (retval)
391         return retval;
392     *retlen = t.length + tlen;
393     return 0;
394 }
395
396 static asn1_error_code
397 get_field_len(const void *val, const struct field_info *field,
398               unsigned int *retlen)
399 {
400     const void *lenptr = (const char *)val + field->lenoff;
401
402     assert(field->lentype != NULL);
403     assert(field->lentype->type == atype_int ||
404            field->lentype->type == atype_uint);
405     assert(sizeof(int) <= sizeof(asn1_intmax));
406     assert(sizeof(unsigned int) <= sizeof(asn1_uintmax));
407     if (field->lentype->type == atype_int) {
408         asn1_intmax xlen = load_int(lenptr, field->lentype->size);
409         if (xlen < 0)
410             return EINVAL;
411         if ((unsigned int)xlen != (asn1_uintmax)xlen)
412             return EINVAL;
413         if ((unsigned int)xlen > UINT_MAX)
414             return EINVAL;
415         *retlen = (unsigned int)xlen;
416     } else {
417         asn1_uintmax xlen = load_uint(lenptr, field->lentype->size);
418         if ((unsigned int)xlen != xlen)
419             return EINVAL;
420         if (xlen > UINT_MAX)
421             return EINVAL;
422         *retlen = (unsigned int)xlen;
423     }
424     return 0;
425 }
426
427 /* Split a DER encoding into tag and contents.  Insert the contents into buf,
428  * then return the length of the contents and the tag. */
429 static asn1_error_code
430 split_der(asn1buf *buf, const unsigned char *der, unsigned int len,
431           taginfo *rettag)
432 {
433     asn1buf der_buf;
434     krb5_data der_data = make_data((unsigned char *)der, len);
435     asn1_error_code retval;
436
437     retval = asn1buf_wrap_data(&der_buf, &der_data);
438     if (retval)
439         return retval;
440     retval = asn1_get_tag_2(&der_buf, rettag);
441     if (retval)
442         return retval;
443     if ((unsigned int)asn1buf_remains(&der_buf, 0) != rettag->length)
444         return EINVAL;
445     return asn1buf_insert_bytestring(buf, rettag->length,
446                                      der + len - rettag->length);
447 }
448
449 /* Encode part of a value (contents only, no tag) according to a field
450  * descriptor and return its encoded length and tag. */
451 static asn1_error_code
452 encode_a_field(asn1buf *buf, const void *val, const struct field_info *field,
453                taginfo *rettag)
454 {
455     asn1_error_code retval;
456
457     if (val == NULL) return ASN1_MISSING_FIELD;
458     assert(!(field->tag_implicit && field->tag < 0));
459
460     switch (field->ftype) {
461     case field_immediate: {
462         retval = asn1_encode_integer(buf, (asn1_intmax) field->dataoff,
463                                      &rettag->length);
464         if (retval)
465             return retval;
466         rettag->asn1class = UNIVERSAL;
467         rettag->construction = PRIMITIVE;
468         rettag->tagnum = ASN1_INTEGER;
469         break;
470     }
471     case field_sequenceof_len: {
472         const void *dataptr = (const char *)val + field->dataoff;
473         unsigned int slen;
474         const struct ptr_info *ptrinfo;
475
476         /*
477          * The field holds a pointer to the array of objects.  So the
478          * address we compute is a pointer-to-pointer, and that's what
479          * field->atype must help us dereference.
480          */
481         assert(field->atype->type == atype_ptr);
482         ptrinfo = field->atype->tinfo;
483         dataptr = LOADPTR(dataptr, ptrinfo);
484         retval = get_field_len(val, field, &slen);
485         if (retval)
486             return retval;
487         if (slen != 0 && dataptr == NULL)
488             return ASN1_MISSING_FIELD;
489         retval = encode_sequence_of(buf, slen, dataptr, ptrinfo->basetype,
490                                     &rettag->length);
491         if (retval)
492             return retval;
493         rettag->asn1class = UNIVERSAL;
494         rettag->construction = CONSTRUCTED;
495         rettag->tagnum = ASN1_SEQUENCE;
496         break;
497     }
498     case field_normal: {
499         const void *dataptr = (const char *)val + field->dataoff;
500         retval = krb5int_asn1_encode_type(buf, dataptr, field->atype, rettag);
501         if (retval)
502             return retval;
503         break;
504     }
505     case field_string: {
506         const void *dataptr = (const char *)val + field->dataoff;
507         const struct atype_info *a;
508         unsigned int slen;
509         const struct string_info *string;
510
511         a = field->atype;
512         assert(a->type == atype_string);
513         retval = get_field_len(val, field, &slen);
514         if (retval)
515             return retval;
516         string = a->tinfo;
517         dataptr = LOADPTR(dataptr, string);
518         if (dataptr == NULL && slen != 0)
519             return ASN1_MISSING_FIELD;
520         assert(string->enclen != NULL);
521         retval = string->enclen(buf, slen, dataptr, &rettag->length);
522         if (retval)
523             return retval;
524         rettag->asn1class = UNIVERSAL;
525         rettag->construction = PRIMITIVE;
526         rettag->tagnum = string->tagval;
527         break;
528     }
529     case field_der: {
530         const void *dataptr = (const char *)val + field->dataoff;
531         const struct atype_info *a;
532         unsigned int slen;
533         const struct ptr_info *ptr;
534
535         a = field->atype;
536         assert(a->type == atype_der);
537         retval = get_field_len(val, field, &slen);
538         if (retval)
539             return retval;
540         ptr = a->tinfo;
541         dataptr = LOADPTR(dataptr, ptr);
542         if (dataptr == NULL && slen != 0)
543             return ASN1_MISSING_FIELD;
544         retval = split_der(buf, dataptr, slen, rettag);
545         if (retval)
546             return retval;
547         break;
548     }
549     case field_choice: {
550         const void *dataptr = (const char *)val + field->dataoff;
551         unsigned int choice;
552         const struct seq_info *seq;
553
554         assert(field->atype->type == atype_choice);
555         seq = field->atype->tinfo;
556         retval = get_field_len(val, field, &choice);
557         if (retval)
558             return retval;
559         if (choice > seq->n_fields)
560             return ASN1_MISSING_FIELD;
561         retval = encode_a_field(buf, dataptr, &seq->fields[choice], rettag);
562         if (retval)
563             return retval;
564         break;
565     }
566     default:
567         assert(field->ftype > field_min);
568         assert(field->ftype < field_max);
569         assert(__LINE__ == 0);
570         abort();
571     }
572
573     if (field->tag >= 0) {
574         if (!field->tag_implicit) {
575             unsigned int tlen;
576             retval = asn1_make_tag(buf, rettag->asn1class,
577                                    rettag->construction, rettag->tagnum,
578                                    rettag->length, &tlen);
579             if (retval)
580                 return retval;
581             rettag->length += tlen;
582             rettag->construction = CONSTRUCTED;
583         }
584         rettag->asn1class = CONTEXT_SPECIFIC;
585         rettag->tagnum = field->tag;
586     }
587     return 0;
588 }
589
590 static asn1_error_code
591 encode_fields(asn1buf *buf, const void *val,
592               const struct field_info *fields, size_t nfields,
593               unsigned int optional,
594               unsigned int *retlen)
595 {
596     size_t i;
597     unsigned int sum = 0;
598     for (i = nfields; i > 0; i--) {
599         const struct field_info *f = fields+i-1;
600         taginfo t;
601         asn1_error_code retval;
602
603         if (f->opt != -1 && !((1u << f->opt) & optional))
604             continue;
605         retval = encode_a_field(buf, val, f, &t);
606         if (retval)
607             return retval;
608         sum += t.length;
609         retval = asn1_make_tag(buf, t.asn1class, t.construction, t.tagnum,
610                                t.length, &t.length);
611         if (retval)
612             return retval;
613         sum += t.length;
614     }
615     *retlen = sum;
616     return 0;
617 }
618
619 static asn1_error_code
620 just_encode_sequence(asn1buf *buf, const void *val,
621                      const struct seq_info *seq,
622                      unsigned int *retlen)
623 {
624     unsigned int optional;
625
626     /* If any fields might be optional, get a bitmask of optional fields. */
627     optional = (seq->optional == NULL) ? 0 : seq->optional(val);
628     return encode_fields(buf, val, seq->fields, seq->n_fields, optional,
629                          retlen);
630 }
631
632 static asn1_error_code
633 encode_sequence_of(asn1buf *buf, unsigned int seqlen, const void *val,
634                    const struct atype_info *eltinfo,
635                    unsigned int *retlen)
636 {
637     asn1_error_code retval;
638     unsigned int sum = 0, i;
639
640     for (i = seqlen; i > 0; i--) {
641         const void *eltptr;
642         unsigned int length;
643         const struct atype_info *a = eltinfo;
644
645         assert(eltinfo->size != 0);
646         eltptr = (const char *)val + (i - 1) * eltinfo->size;
647         retval = encode_type_and_tag(buf, eltptr, a, &length);
648         if (retval)
649             return retval;
650         sum += length;
651     }
652     *retlen = sum;
653     return 0;
654 }
655
656 krb5_error_code
657 krb5int_asn1_do_full_encode(const void *rep, krb5_data **code,
658                             const struct atype_info *a)
659 {
660     unsigned int length;
661     asn1_error_code retval;
662     asn1buf *buf = NULL;
663     krb5_data *d;
664
665     *code = NULL;
666
667     if (rep == NULL)
668         return ASN1_MISSING_FIELD;
669
670     retval = asn1buf_create(&buf);
671     if (retval)
672         return retval;
673
674     retval = encode_type_and_tag(buf, rep, a, &length);
675     if (retval)
676         goto cleanup;
677     retval = asn12krb5_buf(buf, &d);
678     if (retval)
679         goto cleanup;
680     *code = d;
681 cleanup:
682     asn1buf_destroy(&buf);
683     return retval;
684 }