1
2
3
4
5
6
7
8#include <linux/asn1_encoder.h>
9#include <linux/bug.h>
10#include <linux/string.h>
11#include <linux/module.h>
12
13
14
15
16
17
18
19
20
21
22
23unsigned char *
24asn1_encode_integer(unsigned char *data, const unsigned char *end_data,
25 s64 integer)
26{
27 int data_len = end_data - data;
28 unsigned char *d = &data[2];
29 bool found = false;
30 int i;
31
32 if (WARN(integer < 0,
33 "BUG: integer encode only supports positive integers"))
34 return ERR_PTR(-EINVAL);
35
36 if (IS_ERR(data))
37 return data;
38
39
40 if (data_len < 3)
41 return ERR_PTR(-EINVAL);
42
43
44 data_len -= 2;
45
46 data[0] = _tag(UNIV, PRIM, INT);
47 if (integer == 0) {
48 *d++ = 0;
49 goto out;
50 }
51
52 for (i = sizeof(integer); i > 0 ; i--) {
53 int byte = integer >> (8 * (i - 1));
54
55 if (!found && byte == 0)
56 continue;
57
58
59
60
61
62
63
64 if (!found && (byte & 0x80)) {
65
66
67
68
69 *d++ = 0;
70 data_len--;
71 }
72
73 found = true;
74 if (data_len == 0)
75 return ERR_PTR(-EINVAL);
76
77 *d++ = byte;
78 data_len--;
79 }
80
81 out:
82 data[1] = d - data - 2;
83
84 return d;
85}
86EXPORT_SYMBOL_GPL(asn1_encode_integer);
87
88
89static int asn1_encode_oid_digit(unsigned char **_data, int *data_len, u32 oid)
90{
91 unsigned char *data = *_data;
92 int start = 7 + 7 + 7 + 7;
93 int ret = 0;
94
95 if (*data_len < 1)
96 return -EINVAL;
97
98
99 if (oid == 0) {
100 *data++ = 0x80;
101 (*data_len)--;
102 goto out;
103 }
104
105 while (oid >> start == 0)
106 start -= 7;
107
108 while (start > 0 && *data_len > 0) {
109 u8 byte;
110
111 byte = oid >> start;
112 oid = oid - (byte << start);
113 start -= 7;
114 byte |= 0x80;
115 *data++ = byte;
116 (*data_len)--;
117 }
118
119 if (*data_len > 0) {
120 *data++ = oid;
121 (*data_len)--;
122 } else {
123 ret = -EINVAL;
124 }
125
126 out:
127 *_data = data;
128 return ret;
129}
130
131
132
133
134
135
136
137
138
139
140unsigned char *
141asn1_encode_oid(unsigned char *data, const unsigned char *end_data,
142 u32 oid[], int oid_len)
143{
144 int data_len = end_data - data;
145 unsigned char *d = data + 2;
146 int i, ret;
147
148 if (WARN(oid_len < 2, "OID must have at least two elements"))
149 return ERR_PTR(-EINVAL);
150
151 if (WARN(oid_len > 32, "OID is too large"))
152 return ERR_PTR(-EINVAL);
153
154 if (IS_ERR(data))
155 return data;
156
157
158
159 if (data_len < 3)
160 return ERR_PTR(-EINVAL);
161
162 data[0] = _tag(UNIV, PRIM, OID);
163 *d++ = oid[0] * 40 + oid[1];
164
165 data_len -= 3;
166
167 ret = 0;
168
169 for (i = 2; i < oid_len; i++) {
170 ret = asn1_encode_oid_digit(&d, &data_len, oid[i]);
171 if (ret < 0)
172 return ERR_PTR(ret);
173 }
174
175 data[1] = d - data - 2;
176
177 return d;
178}
179EXPORT_SYMBOL_GPL(asn1_encode_oid);
180
181
182
183
184
185
186
187
188
189
190
191
192
193static int asn1_encode_length(unsigned char **data, int *data_len, int len)
194{
195 if (*data_len < 1)
196 return -EINVAL;
197
198 if (len < 0) {
199 *((*data)++) = 0;
200 (*data_len)--;
201 return 0;
202 }
203
204 if (len <= 0x7f) {
205 *((*data)++) = len;
206 (*data_len)--;
207 return 0;
208 }
209
210 if (*data_len < 2)
211 return -EINVAL;
212
213 if (len <= 0xff) {
214 *((*data)++) = 0x81;
215 *((*data)++) = len & 0xff;
216 *data_len -= 2;
217 return 0;
218 }
219
220 if (*data_len < 3)
221 return -EINVAL;
222
223 if (len <= 0xffff) {
224 *((*data)++) = 0x82;
225 *((*data)++) = (len >> 8) & 0xff;
226 *((*data)++) = len & 0xff;
227 *data_len -= 3;
228 return 0;
229 }
230
231 if (WARN(len > 0xffffff, "ASN.1 length can't be > 0xffffff"))
232 return -EINVAL;
233
234 if (*data_len < 4)
235 return -EINVAL;
236 *((*data)++) = 0x83;
237 *((*data)++) = (len >> 16) & 0xff;
238 *((*data)++) = (len >> 8) & 0xff;
239 *((*data)++) = len & 0xff;
240 *data_len -= 4;
241
242 return 0;
243}
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271unsigned char *
272asn1_encode_tag(unsigned char *data, const unsigned char *end_data,
273 u32 tag, const unsigned char *string, int len)
274{
275 int data_len = end_data - data;
276 int ret;
277
278 if (WARN(tag > 30, "ASN.1 tag can't be > 30"))
279 return ERR_PTR(-EINVAL);
280
281 if (!string && WARN(len > 127,
282 "BUG: recode tag is too big (>127)"))
283 return ERR_PTR(-EINVAL);
284
285 if (IS_ERR(data))
286 return data;
287
288 if (!string && len > 0) {
289
290
291
292
293
294 data -= 2;
295 data_len = 2;
296 }
297
298 if (data_len < 2)
299 return ERR_PTR(-EINVAL);
300
301 *(data++) = _tagn(CONT, CONS, tag);
302 data_len--;
303 ret = asn1_encode_length(&data, &data_len, len);
304 if (ret < 0)
305 return ERR_PTR(ret);
306
307 if (!string)
308 return data;
309
310 if (data_len < len)
311 return ERR_PTR(-EINVAL);
312
313 memcpy(data, string, len);
314 data += len;
315
316 return data;
317}
318EXPORT_SYMBOL_GPL(asn1_encode_tag);
319
320
321
322
323
324
325
326
327
328
329unsigned char *
330asn1_encode_octet_string(unsigned char *data,
331 const unsigned char *end_data,
332 const unsigned char *string, u32 len)
333{
334 int data_len = end_data - data;
335 int ret;
336
337 if (IS_ERR(data))
338 return data;
339
340
341 if (data_len < 2)
342 return ERR_PTR(-EINVAL);
343
344 *(data++) = _tag(UNIV, PRIM, OTS);
345 data_len--;
346
347 ret = asn1_encode_length(&data, &data_len, len);
348 if (ret)
349 return ERR_PTR(ret);
350
351 if (data_len < len)
352 return ERR_PTR(-EINVAL);
353
354 memcpy(data, string, len);
355 data += len;
356
357 return data;
358}
359EXPORT_SYMBOL_GPL(asn1_encode_octet_string);
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374unsigned char *
375asn1_encode_sequence(unsigned char *data, const unsigned char *end_data,
376 const unsigned char *seq, int len)
377{
378 int data_len = end_data - data;
379 int ret;
380
381 if (!seq && WARN(len > 127,
382 "BUG: recode sequence is too big (>127)"))
383 return ERR_PTR(-EINVAL);
384
385 if (IS_ERR(data))
386 return data;
387
388 if (!seq && len >= 0) {
389
390
391
392
393
394 data -= 2;
395 data_len = 2;
396 }
397
398 if (data_len < 2)
399 return ERR_PTR(-EINVAL);
400
401 *(data++) = _tag(UNIV, CONS, SEQ);
402 data_len--;
403
404 ret = asn1_encode_length(&data, &data_len, len);
405 if (ret)
406 return ERR_PTR(ret);
407
408 if (!seq)
409 return data;
410
411 if (data_len < len)
412 return ERR_PTR(-EINVAL);
413
414 memcpy(data, seq, len);
415 data += len;
416
417 return data;
418}
419EXPORT_SYMBOL_GPL(asn1_encode_sequence);
420
421
422
423
424
425
426
427unsigned char *
428asn1_encode_boolean(unsigned char *data, const unsigned char *end_data,
429 bool val)
430{
431 int data_len = end_data - data;
432
433 if (IS_ERR(data))
434 return data;
435
436
437 if (data_len < 3)
438 return ERR_PTR(-EINVAL);
439
440 *(data++) = _tag(UNIV, PRIM, BOOL);
441 data_len--;
442
443 asn1_encode_length(&data, &data_len, 1);
444
445 if (val)
446 *(data++) = 1;
447 else
448 *(data++) = 0;
449
450 return data;
451}
452EXPORT_SYMBOL_GPL(asn1_encode_boolean);
453
454MODULE_LICENSE("GPL");
455