summaryrefslogtreecommitdiffstats
path: root/fmt_asn1generic.c
blob: 199ba92d753016af94f9afdd5a20dfd92a1f6663 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
#include <stdarg.h>
#include <string.h>
#include "asn1.h"

size_t fmt_asn1generic(char* dest,const char* fmt,...) {
  size_t containerstack[100];
  size_t curinstack=0;
  va_list args;
  unsigned long* application=0;
  struct string* s;
  struct oid* o;
  struct string S;
  size_t curlen=0;
  size_t cursor=0;
  size_t seqlen;
  unsigned long desttag=0;
  unsigned long appstore;
  int stringtype;
  va_start(args,fmt);
  while (*fmt) {
    char* realdest=dest?dest+cursor:NULL;
    switch (*fmt) {
    case '*':	// make next tag use APPLICATION with this tag
      appstore=va_arg(args,unsigned long);
      application=&appstore;
      break;
    case '0':	// UNIVERSAL PRIMITIVE NULL length 0
      if (application)
	curlen=fmt_asn1tag(realdest,APPLICATION,PRIMITIVE,_NULL);
      else
	curlen=fmt_asn1tag(realdest,UNIVERSAL,PRIMITIVE,_NULL);
      application=NULL;
      curlen+=fmt_asn1length(realdest?realdest+curlen:NULL,0);
      break;
    case 'B':	// send boolean
      {
	int i=va_arg(args,int);
	if (i!=0 && i!=1) {
	  va_end(args);
	  return 0;
	}
	if (application)
	  curlen=fmt_asn1int(realdest,APPLICATION,PRIMITIVE,*application,i);
	else
	  curlen=fmt_asn1int(realdest,UNIVERSAL,PRIMITIVE,BOOLEAN,i);
	application=NULL;
	break;
      }
    case 'i':	// send integer
      {
	unsigned long i=va_arg(args,unsigned long);
	if (application)
	  curlen=fmt_asn1int(realdest,APPLICATION,PRIMITIVE,*application,i);
	else
	  curlen=fmt_asn1int(realdest,UNIVERSAL,PRIMITIVE,INTEGER,i);
	application=NULL;
	break;
      }
    case 'b':	// send BIT_STRING, using struct string* as arg (expect l to be in bits, not bytes)
      s=va_arg(args,struct string*);
      if (application)
	curlen=fmt_asn1bitstring(realdest,APPLICATION,PRIMITIVE,*application,s->s,s->l);
      else
	curlen=fmt_asn1bitstring(realdest,UNIVERSAL,PRIMITIVE,BIT_STRING,s->s,s->l);
      application=NULL;
      break;
    case 'I':
      stringtype=BIT_STRING;
      goto stringcopy;
    case 'A':
      stringtype=IA5String;
      goto stringcopy;
    case 'P':
      stringtype=PrintableString;
      goto stringcopy;
    case 'S':	// send OCTET_STRING, using struct string* as arg
      stringtype=OCTET_STRING;
stringcopy:
      s=va_arg(args,struct string*);
copystring:
      if (application)
	curlen=fmt_asn1string(realdest,APPLICATION,PRIMITIVE,*application,s->s,s->l);
      else
	curlen=fmt_asn1string(realdest,UNIVERSAL,PRIMITIVE,stringtype,s->s,s->l);
      application=NULL;
      break;
    case 't':
      stringtype=UTCTIME;
      goto stringcopy_alt;
    case 'a':
      stringtype=IA5String;
      goto stringcopy_alt;
    case 'p':
      stringtype=PrintableString;
      goto stringcopy_alt;
    case 's':	// send OCTET_STRING, using const char* with strlen() as arg
      stringtype=OCTET_STRING;
stringcopy_alt:
      S.s=va_arg(args,const char*);
      S.l=strlen(S.s);
      s=&S;
      goto copystring;
    case 'o':	// send OBJECT_IDENTIFIER, using struct oid* as arg
      o=va_arg(args,struct oid*);
      if (application)
	curlen=fmt_asn1OID(realdest,APPLICATION,PRIMITIVE,*application,o->a,o->l);
      else
	curlen=fmt_asn1OID(realdest,UNIVERSAL,PRIMITIVE,OBJECT_IDENTIFIER,o->a,o->l);
      application=NULL;
      break;

    case 'C':	// copy raw ASN.1 DER data, take struct string*
      s=va_arg(args,struct string*);
      if (realdest) memcpy(realdest,s->s,s->l);
      curlen=s->l;
      break;

    case 'c':	// start context specific section
      desttag=va_arg(args,unsigned long);
      // fall through
    case '[':	// start SET
    case '{':	// start SEQUENCE
      if (application)
	curlen=fmt_asn1tag(realdest,APPLICATION,CONSTRUCTED,*application);
      else if (*fmt=='c')
	curlen=fmt_asn1tag(realdest,CONTEXT_SPECIFIC,CONSTRUCTED,desttag);
      else
	curlen=fmt_asn1tag(realdest,UNIVERSAL,CONSTRUCTED,*fmt=='{'?SEQUENCE_OF:SET_OF);
      containerstack[curinstack++]=cursor+curlen;
      application=NULL;
      break;
    case ']':	// end of SET
    case '}':	// end of SEQUENCE
      /* we just wrote the tag and the sequence.  Now that we wrote the
       * sequence, we know the length it took, and we need to move the
       * sequence data backwards to make room to write the ASN.1 length */
      {
	char* anfang;
	if (!curinstack) {
	  va_end(args);
	  return 0;
	}
	anfang=dest+containerstack[--curinstack];
	seqlen=dest+cursor-anfang;
	curlen=fmt_asn1length(NULL,seqlen);
	if (!dest) break;
	memmove(anfang+curlen,anfang,seqlen);
	fmt_asn1length(anfang,seqlen);
	break;
      }
    }
    cursor+=curlen;
    ++fmt;
  }
  va_end(args);
  return cursor;
}