asn1.c
Go to the documentation of this file.
1 /**
2  * @file asn1.c
3  * @brief ASN.1 (Abstract Syntax Notation One)
4  *
5  * @section License
6  *
7  * Copyright (C) 2010-2018 Oryx Embedded SARL. All rights reserved.
8  *
9  * This file is part of CycloneCrypto Open.
10  *
11  * This program is free software; you can redistribute it and/or
12  * modify it under the terms of the GNU General Public License
13  * as published by the Free Software Foundation; either version 2
14  * of the License, or (at your option) any later version.
15  *
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19  * GNU General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public License
22  * along with this program; if not, write to the Free Software Foundation,
23  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
24  *
25  * @author Oryx Embedded SARL (www.oryx-embedded.com)
26  * @version 1.9.0
27  **/
28 
29 //Switch to the appropriate trace level
30 #define TRACE_LEVEL CRYPTO_TRACE_LEVEL
31 
32 //Dependencies
33 #include "core/crypto.h"
34 #include "encoding/asn1.h"
35 #include "encoding/oid.h"
36 #include "debug.h"
37 
38 //Check crypto library configuration
39 #if (ASN1_SUPPORT == ENABLED)
40 
41 
42 /**
43  * @brief Read an ASN.1 tag from the input stream
44  * @param[in] data Input stream where to read the tag
45  * @param[in] length Number of bytes available in the input stream
46  * @param[out] tag Structure describing the ASN.1 tag
47  * @return Error code
48  **/
49 
50 error_t asn1ReadTag(const uint8_t *data, size_t length, Asn1Tag *tag)
51 {
52  uint_t i;
53  uint_t n;
54 
55  //Make sure the identifier octet is present
56  if(length == 0)
57  return ERROR_INVALID_TAG;
58 
59  //Save the class of the ASN.1 tag
60  tag->objClass = data[0] & ASN1_CLASS_MASK;
61  //Primitive or constructed encoding?
63 
64  //Check the tag number
65  if((data[0] & ASN1_TAG_NUMBER_MASK) < 31)
66  {
67  //Tag number is in the range 0 to 30
68  tag->objType = data[0] & ASN1_TAG_NUMBER_MASK;
69  //Point to the tag length field
70  i = 1;
71  }
72  else
73  {
74  //If the tag number is greater than or equal to 31,
75  //the subsequent octets will encode the tag number
76  tag->objType = 0;
77 
78  //Decode the tag number
79  for(i = 1; ; i++)
80  {
81  //The field cannot exceed 5 bytes
82  if(i > (sizeof(tag->objType) + 1))
83  return ERROR_INVALID_TAG;
84  //Insufficient number of bytes to decode the tag number?
85  if(!(length - i))
86  return ERROR_INVALID_TAG;
87 
88  //Update the tag number with bits 7 to 1
89  tag->objType = (tag->objType << 7) | (data[i] & 0x7F);
90 
91  //Bit 8 shall be set unless it is the last octet
92  if(!(data[i] & 0x80))
93  break;
94  }
95  //Point to the tag length field
96  i++;
97  }
98 
99  //Insufficient number of bytes to decode the tag length?
100  if(!(length - i))
101  return ERROR_INVALID_TAG;
102 
103  //Short form is used?
104  if(data[i] < 128)
105  {
106  //Bits 7 to 1 encode the number of bytes in the contents
107  tag->length = data[i];
108  //Point to the contents of the tag
109  i++;
110  }
111  //Long form is used?
112  else if(data[i] > 128 && data[i] < 255)
113  {
114  //Bits 7 to 1 encode the number of octets in the length field
115  n = data[i] & 0x7F;
116 
117  //The field cannot exceed 4 bytes
118  if(n > sizeof(tag->length))
119  return ERROR_INVALID_TAG;
120  //Insufficient number of bytes to decode the tag length?
121  if((length - i) < n)
122  return ERROR_INVALID_TAG;
123 
124  //Clear the tag length
125  tag->length = 0;
126  //Read the subsequent octets
127  for(i++; n > 0; n--)
128  tag->length = (tag->length << 8) | data[i++];
129  }
130  //Indefinite form is used?
131  else
132  {
133  //Indefinite form is not supported
134  return ERROR_INVALID_TAG;
135  }
136 
137  //Save the pointer to the tag contents
138  tag->value = data + i;
139  //Check the length of tag
140  if((length - i) < tag->length)
141  return ERROR_INVALID_TAG;
142 
143  //Total length occupied by the ASN.1 tag in the input stream
144  tag->totalLength = i + tag->length;
145  //ASN.1 tag successfully decoded
146  return NO_ERROR;
147 }
148 
149 
150 /**
151  * @brief Read an ASN.1 sequence from the input stream
152  * @param[in] data Input stream where to read the tag
153  * @param[in] length Number of bytes available in the input stream
154  * @param[out] tag Structure describing the ASN.1 tag
155  * @return Error code
156  **/
157 
158 error_t asn1ReadSequence(const uint8_t *data, size_t length, Asn1Tag *tag)
159 {
160  error_t error;
161 
162  //Read ASN.1 tag
163  error = asn1ReadTag(data, length, tag);
164 
165  //Check status code
166  if(!error)
167  {
168  //Enforce encoding, class and type
170  }
171 
172  //Return status code
173  return error;
174 }
175 
176 
177 /**
178  * @brief Read an integer from the input stream
179  * @param[in] data Input stream where to read the tag
180  * @param[in] length Number of bytes available in the input stream
181  * @param[out] tag Structure describing the ASN.1 tag
182  * @param[out] value Integer value
183  * @return Error code
184  **/
185 
186 error_t asn1ReadInt32(const uint8_t *data, size_t length, Asn1Tag *tag, int32_t *value)
187 {
188  error_t error;
189  size_t i;
190 
191  //Read ASN.1 tag
192  error = asn1ReadTag(data, length, tag);
193  //Failed to decode ASN.1 tag?
194  if(error)
195  return error;
196 
197  //Enforce encoding, class and type
199  //The tag does not match the criteria?
200  if(error)
201  return error;
202 
203  //The contents shall consist of one or more octets
204  if(tag->length < 1 || tag->length > 4)
205  return ERROR_INVALID_TAG;
206 
207  //The contents octets shall be a two's complement binary
208  //number equal to the integer value
209  *value = (tag->value[0] & 0x80) ? -1 : 0;
210 
211  //Process contents octets
212  for(i = 0; i < tag->length; i++)
213  {
214  //Rotate left operation
215  *value <<= 8;
216  //Reconstruct integer value
217  *value |= tag->value[i];
218  }
219 
220  //ASN.1 tag successfully decoded
221  return NO_ERROR;
222 }
223 
224 
225 /**
226  * @brief Write an ASN.1 tag
227  * @param[in] tag Structure describing the ASN.1 tag
228  * @param[in] reverse Use reverse encoding
229  * @param[out] data Output stream where to write the tag (optional parameter)
230  * @param[out] written Number of bytes written to the output stream (optional parameter)
231  * @return Error code
232  **/
233 
234 error_t asn1WriteTag(Asn1Tag *tag, bool_t reverse, uint8_t *data, size_t *written)
235 {
236  size_t i;
237  size_t m;
238  size_t n;
239 
240  //Compute the number of octets that are necessary to encode the tag number
241  if(tag->objType < 31)
242  m = 0;
243  else if(tag->objType < 128)
244  m = 1;
245  else if(tag->objType < 16384)
246  m = 2;
247  else if(tag->objType < 2097152)
248  m = 3;
249  else if(tag->objType < 268435456)
250  m = 4;
251  else
252  m = 5;
253 
254  //Compute the number of octets that are necessary to encode the length field
255  if(tag->length < 128)
256  n = 0;
257  else if(tag->length < 256)
258  n = 1;
259  else if(tag->length < 65536)
260  n = 2;
261  else if(tag->length < 16777216)
262  n = 3;
263  else
264  n = 4;
265 
266  //Valid output stream?
267  if(data != NULL)
268  {
269  //Use reverse encoding?
270  if(reverse)
271  {
272  //Any data to copy?
273  if(tag->value != NULL && tag->length > 0)
274  {
275  //Make room for the data
276  data -= tag->length;
277  //Copy data
278  cryptoMemmove(data, tag->value, tag->length);
279  }
280 
281  //Move backward
282  data -= m + n + 2;
283  }
284  else
285  {
286  //Any data to copy?
287  if(tag->value != NULL && tag->length > 0)
288  {
289  //Copy data
290  cryptoMemmove(data + m + n + 2, tag->value, tag->length);
291  }
292  }
293 
294  //Save the class of the ASN.1 tag
295  data[0] = tag->objClass;
296 
297  //Primitive or constructed encoding?
298  if(tag->constructed)
300 
301  //Encode the tag number
302  if(m == 0)
303  {
304  //Tag number is in the range 0 to 30
305  data[0] |= tag->objType;
306  }
307  else
308  {
309  //The tag number is greater than or equal to 31
311 
312  //The subsequent octets will encode the tag number
313  for(i = 0; i < m; i++)
314  {
315  //Bits 7 to 1 encode the tag number
316  data[m - i] = (tag->objType >> (i * 7)) & 0x7F;
317 
318  //Bit 8 of each octet shall be set to one unless it is the
319  //last octet of the identifier octets
320  if(i != 0)
321  data[m - i] |= 0x80;
322  }
323  }
324 
325  //Encode the length field
326  if(n == 0)
327  {
328  //Use short form encoding
329  data[1 + m] = tag->length & 0x7F;
330  }
331  else
332  {
333  //Bits 7 to 1 encode the number of octets in the length field
334  data[1 + m] = 0x80 | (n & 0x7F);
335 
336  //The subsequent octets will encode the length field
337  for(i = 0; i < n; i++)
338  {
339  data[1 + m + n - i] = (tag->length >> (i * 8)) & 0xFF;
340  }
341  }
342  }
343 
344  //Total length occupied by the ASN.1 tag
345  tag->totalLength = tag->length + m + n + 2;
346 
347  //The last parameter is optional
348  if(written != NULL)
349  {
350  //Number of bytes written to the output stream
351  *written = m + n + 2;
352 
353  //Any data copied?
354  if(tag->value != NULL)
355  *written += tag->length;
356  }
357 
358  //Successful processing
359  return NO_ERROR;
360 }
361 
362 
363 /**
364  * @brief Write an integer to the output stream
365  * @param[in] value Integer value
366  * @param[in] reverse Use reverse encoding
367  * @param[out] data Output stream where to write the tag (optional parameter)
368  * @param[out] written Number of bytes written to the output stream
369  * @return Error code
370  **/
371 
372 error_t asn1WriteInt32(int32_t value, bool_t reverse, uint8_t *data, size_t *written)
373 {
374  size_t i;
375  size_t n;
376  uint16_t msb;
377 
378  //An integer value is always encoded in the smallest possible number of octets
379  for(n = 4; n > 1; n--)
380  {
381  //Retrieve the upper 9 bits
382  msb = (value >> (n * 8 - 9)) & 0x01FF;
383 
384  //The upper 9 bits shall not have the same value (all 0 or all 1)
385  if(msb != 0x0000 && msb != 0x01FF)
386  break;
387  }
388 
389  //Valid output stream?
390  if(data != NULL)
391  {
392  //Use reverse encoding?
393  if(reverse)
394  data -= n + 2;
395 
396  //Write tag type
398  //Write tag length
399  data[1] = n & 0xFF;
400 
401  //Write contents octets
402  for(i = 0; i < n; i++)
403  {
404  data[1 + n - i] = (value >> (i * 8)) & 0xFF;
405  }
406  }
407 
408  //Number of bytes written to the output stream
409  if(written != NULL)
410  *written = n + 2;
411 
412  //Successful processing
413  return NO_ERROR;
414 }
415 
416 
417 /**
418  * @brief Enforce the type of a specified tag
419  * @param[in] tag Pointer to an ASN.1 tag
420  * @param[in] constructed Expected encoding (TRUE for constructed, FALSE for primitive)
421  * @param[in] objClass Expected tag class
422  * @param[in] objType Expected tag type
423  * @return Error code
424  **/
425 
426 error_t asn1CheckTag(const Asn1Tag *tag, bool_t constructed, uint_t objClass, uint_t objType)
427 {
428  //Check encoding
429  if(tag->constructed != constructed)
430  return ERROR_WRONG_ENCODING;
431  //Enforce class
432  if(tag->objClass != objClass)
433  return ERROR_INVALID_CLASS;
434  //Enforce type
435  if(tag->objType != objType)
436  return ERROR_INVALID_TYPE;
437 
438  //The tag matches all the criteria
439  return NO_ERROR;
440 }
441 
442 
443 /**
444  * @brief Check ASN.1 tag against a specified OID
445  * @param[in] tag Pointer to an ASN.1 tag
446  * @param[in] oid Expected object identifier (OID)
447  * @param[in] length Length of the OID
448  * @return Error code
449  **/
450 
451 error_t asn1CheckOid(const Asn1Tag *tag, const uint8_t *oid, size_t length)
452 {
453  error_t error;
454 
455  //Enforce encoding, class and type
457  //Any error to report?
458  if(error)
459  return error;
460 
461  //Compare OID against the specified value
462  if(oidComp(tag->value, tag->length, oid, length))
463  return ERROR_WRONG_IDENTIFIER;
464 
465  //The tag matches all the criteria
466  return NO_ERROR;
467 }
468 
469 
470 /**
471  * @brief Display an ASN.1 data object
472  * @param[in] data Pointer to the ASN.1 object to dump
473  * @param[in] length Length of the ASN.1 object
474  * @param[in] level Current level of recursion (this parameter shall be set to 0)
475  * @return Error code
476  **/
477 
478 error_t asn1DumpObject(const uint8_t *data, size_t length, uint_t level)
479 {
480 //Check debugging level
481 #if (TRACE_LEVEL >= TRACE_LEVEL_DEBUG)
482  error_t error;
483  uint_t i;
484  Asn1Tag tag;
485 
486  //ASN.1 universal types
487  static const char_t *label[32] =
488  {
489  "[0]",
490  "BOOLEAN",
491  "INTEGER",
492  "BIT STRING",
493  "OCTET STRING",
494  "NULL",
495  "OBJECT IDENTIFIER",
496  "OBJECT DESCRIPTOR",
497  "EXTERNAL",
498  "REAL",
499  "ENUMERATED",
500  "[11]",
501  "UTF8 STRING",
502  "[13]",
503  "[14]",
504  "[15]",
505  "SEQUENCE",
506  "SET",
507  "NUMERIC STRING",
508  "PRINTABLE STRING",
509  "TELETEX STRING",
510  "VIDEOTEX STRING",
511  "IA5 STRING",
512  "UTC TIME",
513  "GENERALIZED TIME",
514  "GRAPHIC STRING",
515  "VISIBLE STRING",
516  "GENERAL STRING",
517  "UNIVERSAL STRING",
518  "[29]",
519  "BMP STRING",
520  "[31]"
521  };
522 
523  //Prefix used to format the structure
524  static const char_t *prefix[10] =
525  {
526  "",
527  " ",
528  " ",
529  " ",
530  " ",
531  " ",
532  " ",
533  " ",
534  " ",
535  " "
536  };
537 
538  //Parse ASN.1 object
539  while(length > 0)
540  {
541  //Decode current ASN.1 tag
542  error = asn1ReadTag(data, length, &tag);
543  //Decoding failed?
544  if(error)
545  return error;
546 
547  //Point to the next field
548  data += tag.totalLength;
549  length -= tag.totalLength;
550 
551  //Dump tag number, tag class, and contents length fields
552  if(tag.objType < 32 && (tag.objClass & ASN1_CLASS_MASK) == ASN1_CLASS_UNIVERSAL)
553  TRACE_DEBUG("%s%s (%" PRIuSIZE " bytes)\r\n", prefix[level], label[tag.objType], tag.length);
554  else
555  TRACE_DEBUG("%s[%u] (%" PRIuSIZE " bytes)\r\n", prefix[level], tag.objType, tag.length);
556 
557  //Constructed type?
558  if(tag.constructed)
559  {
560  //Check whether the maximum level of recursion is reached
561  if(level < 8)
562  {
563  //Recursive decoding of the ASN.1 tag
564  error = asn1DumpObject(tag.value, tag.length, level + 1);
565  //Decoding failed?
566  if(error)
567  return error;
568  }
569  else
570  {
571  //If the maximum level of recursion is reached, then dump contents
572  TRACE_DEBUG_ARRAY(prefix[level + 1], tag.value, tag.length);
573  }
574  }
575  //Primitive type?
576  else
577  {
578  //Check the type of the current tag
579  switch(tag.objType)
580  {
581  //OID?
583  //Append prefix
584  TRACE_DEBUG(prefix[level + 1]);
585  //Print OID
586  TRACE_DEBUG("%s", oidToString(tag.value, tag.length, NULL, 0));
587  //Add a line feed
588  TRACE_DEBUG("\r\n");
589  break;
590  //String?
602  //Append prefix
603  TRACE_DEBUG("%s", prefix[level + 1]);
604  //Dump the entire string
605  for(i = 0; i < tag.length; i++)
606  TRACE_DEBUG("%c", tag.value[i]);
607  //Add a line feed
608  TRACE_DEBUG("\r\n");
609  break;
610  //UTC time?
611  case ASN1_TYPE_UTC_TIME:
612  //Check length
613  if(tag.length != 13)
614  return ERROR_WRONG_ENCODING;
615  //The encoding shall terminate with a "Z"
616  if(tag.value[tag.length - 1] != 'Z')
617  return ERROR_WRONG_ENCODING;
618 
619  //Append prefix
620  TRACE_DEBUG("%s", prefix[level + 1]);
621  //Display date
622  TRACE_DEBUG("%c%c/%c%c/%c%c ", tag.value[0], tag.value[1],
623  tag.value[2], tag.value[3], tag.value[4], tag.value[5]);
624  //Display time
625  TRACE_DEBUG("%c%c:%c%c:%c%c", tag.value[6], tag.value[7],
626  tag.value[8], tag.value[9], tag.value[10], tag.value[11]);
627  //Add a line feed
628  TRACE_DEBUG("\r\n");
629  break;
630  //Generalized time?
632  //Check length
633  if(tag.length != 15)
634  return ERROR_WRONG_ENCODING;
635  //The encoding shall terminate with a "Z"
636  if(tag.value[tag.length - 1] != 'Z')
637  return ERROR_WRONG_ENCODING;
638 
639  //Append prefix
640  TRACE_DEBUG("%s", prefix[level + 1]);
641  //Display date
642  TRACE_DEBUG("%c%c%c%c/%c%c/%c%c ", tag.value[0], tag.value[1], tag.value[2],
643  tag.value[3], tag.value[4], tag.value[5], tag.value[6], tag.value[7]);
644  //Display time
645  TRACE_DEBUG("%c%c:%c%c:%c%c", tag.value[8], tag.value[9],
646  tag.value[10], tag.value[11], tag.value[12], tag.value[13]);
647  //Add a line feed
648  TRACE_DEBUG("\r\n");
649  break;
650  //Any other type?
651  default:
652  //Dump the contents of the tag
653  TRACE_DEBUG_ARRAY(prefix[level + 1], tag.value, tag.length);
654  break;
655  }
656  }
657  }
658 #endif
659 
660  //ASN.1 object successfully decoded
661  return NO_ERROR;
662 }
663 
664 #endif
error_t asn1DumpObject(const uint8_t *data, size_t length, uint_t level)
Display an ASN.1 data object.
Definition: asn1.c:478
uint_t objType
Definition: asn1.h:98
char char_t
Definition: compiler_port.h:41
char_t * oidToString(const uint8_t *oid, size_t oidLen, char_t *str, size_t maxStrLen)
Convert a binary OID to a string representation.
Definition: oid.c:536
error_t asn1ReadInt32(const uint8_t *data, size_t length, Asn1Tag *tag, int32_t *value)
Read an integer from the input stream.
Definition: asn1.c:186
Debugging facilities.
size_t totalLength
Definition: asn1.h:101
General definitions for cryptographic algorithms.
error_t asn1CheckOid(const Asn1Tag *tag, const uint8_t *oid, size_t length)
Check ASN.1 tag against a specified OID.
Definition: asn1.c:451
#define cryptoMemmove(dest, src, length)
Definition: crypto.h:596
#define TRACE_DEBUG_ARRAY(p, a, n)
Definition: debug.h:99
uint8_t level
Definition: tls.h:1696
uint8_t m
Definition: ndp.h:299
OID (Object Identifier)
ASN.1 tag.
Definition: asn1.h:94
#define TRUE
Definition: os_port.h:48
error_t asn1WriteTag(Asn1Tag *tag, bool_t reverse, uint8_t *data, size_t *written)
Write an ASN.1 tag.
Definition: asn1.c:234
error_t asn1CheckTag(const Asn1Tag *tag, bool_t constructed, uint_t objClass, uint_t objType)
Enforce the type of a specified tag.
Definition: asn1.c:426
ASN.1 (Abstract Syntax Notation One)
error_t asn1ReadSequence(const uint8_t *data, size_t length, Asn1Tag *tag)
Read an ASN.1 sequence from the input stream.
Definition: asn1.c:158
error_t asn1ReadTag(const uint8_t *data, size_t length, Asn1Tag *tag)
Read an ASN.1 tag from the input stream.
Definition: asn1.c:50
size_t length
Definition: asn1.h:99
bool_t constructed
Definition: asn1.h:96
#define ASN1_ENCODING_CONSTRUCTED
Definition: asn1.h:41
int_t oidComp(const uint8_t *oid1, size_t oidLen1, const uint8_t *oid2, size_t oidLen2)
Compare object identifiers.
Definition: oid.c:99
error_t asn1WriteInt32(int32_t value, bool_t reverse, uint8_t *data, size_t *written)
Write an integer to the output stream.
Definition: asn1.c:372
Success.
Definition: error.h:42
Ipv6Addr prefix
error_t
Error codes.
Definition: error.h:40
unsigned int uint_t
Definition: compiler_port.h:43
#define ASN1_CLASS_MASK
Definition: asn1.h:44
uint8_t data[]
Definition: dtls_misc.h:167
#define PRIuSIZE
Definition: compiler_port.h:72
uint8_t value[]
Definition: dtls_misc.h:141
uint_t objClass
Definition: asn1.h:97
#define ASN1_CLASS_UNIVERSAL
Definition: asn1.h:45
uint8_t length
Definition: dtls_misc.h:140
uint8_t n
uint8_t oid[1]
Definition: mib_common.h:184
#define FALSE
Definition: os_port.h:44
int bool_t
Definition: compiler_port.h:47
#define ASN1_TAG_NUMBER_MASK
Definition: asn1.h:36
const uint8_t * value
Definition: asn1.h:100
#define TRACE_DEBUG(...)
Definition: debug.h:98