Dependents:   Nucleo

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers asn1.c Source File

asn1.c

Go to the documentation of this file.
00001 /**
00002  * @file asn1.c
00003  * @brief ASN.1 (Abstract Syntax Notation One)
00004  *
00005  * @section License
00006  *
00007  * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
00008  *
00009  * This file is part of CycloneCrypto Open.
00010  *
00011  * This program is free software; you can redistribute it and/or
00012  * modify it under the terms of the GNU General Public License
00013  * as published by the Free Software Foundation; either version 2
00014  * of the License, or (at your option) any later version.
00015  *
00016  * This program is distributed in the hope that it will be useful,
00017  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00018  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00019  * GNU General Public License for more details.
00020  *
00021  * You should have received a copy of the GNU General Public License
00022  * along with this program; if not, write to the Free Software Foundation,
00023  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
00024  *
00025  * @author Oryx Embedded SARL (www.oryx-embedded.com)
00026  * @version 1.7.6
00027  **/
00028 
00029 //Switch to the appropriate trace level
00030 #define TRACE_LEVEL CRYPTO_TRACE_LEVEL
00031 
00032 //Dependencies
00033 #include <string.h>
00034 #include "crypto.h"
00035 #include "asn1.h"
00036 #include "oid.h"
00037 #include "debug.h"
00038 
00039 //Check crypto library configuration
00040 #if (ASN1_SUPPORT == ENABLED)
00041 
00042 
00043 /**
00044  * @brief Read an ASN.1 tag from the input stream
00045  * @param[in] data Input stream where to read the tag
00046  * @param[in] length Number of bytes available in the input stream
00047  * @param[out] tag Structure describing the ASN.1 tag
00048  * @return Error code
00049  **/
00050 
00051 error_t asn1ReadTag(const uint8_t *data, size_t length, Asn1Tag *tag)
00052 {
00053    uint_t i;
00054    uint_t n;
00055 
00056    //Make sure the identifier octet is present
00057    if(!length)
00058       return ERROR_INVALID_TAG;
00059 
00060    //Save the class of the ASN.1 tag
00061    tag->objClass = data[0] & ASN1_CLASS_MASK;
00062    //Primitive or constructed encoding?
00063    tag->constructed = (data[0] & ASN1_ENCODING_CONSTRUCTED) ? TRUE : FALSE;
00064 
00065    //Check the tag number
00066    if((data[0] & ASN1_TAG_NUMBER_MASK) < 31)
00067    {
00068       //Tag number is in the range 0 to 30
00069       tag->objType = data[0] & ASN1_TAG_NUMBER_MASK;
00070       //Point to the tag length field
00071       i = 1;
00072    }
00073    else
00074    {
00075       //If the tag number is greater than or equal to 31,
00076       //the subsequent octets will encode the tag number
00077       tag->objType = 0;
00078 
00079       //Decode the tag number
00080       for(i = 1; ; i++)
00081       {
00082          //The field cannot exceed 5 bytes
00083          if(i > (sizeof(tag->objType) + 1))
00084             return ERROR_INVALID_TAG;
00085          //Insufficient number of bytes to decode the tag number?
00086          if(!(length - i))
00087             return ERROR_INVALID_TAG;
00088 
00089          //Update the tag number with bits 7 to 1
00090          tag->objType = (tag->objType << 7) | (data[i] & 0x7F);
00091 
00092          //Bit 8 shall be set unless it is the last octet
00093          if(!(data[i] & 0x80))
00094             break;
00095       }
00096       //Point to the tag length field
00097       i++;
00098    }
00099 
00100    //Insufficient number of bytes to decode the tag length?
00101    if(!(length - i))
00102       return ERROR_INVALID_TAG;
00103 
00104    //Short form is used?
00105    if(data[i] < 128)
00106    {
00107       //Bits 7 to 1 encode the number of bytes in the contents
00108       tag->length = data[i];
00109       //Point to the contents of the tag
00110       i++;
00111    }
00112    //Long form is used?
00113    else if(data[i] > 128 && data[i] < 255)
00114    {
00115       //Bits 7 to 1 encode the number of octets in the length field
00116       n = data[i] & 0x7F;
00117 
00118       //The field cannot exceed 4 bytes
00119       if(n > sizeof(tag->length))
00120          return ERROR_INVALID_TAG;
00121       //Insufficient number of bytes to decode the tag length?
00122       if((length - i) < n)
00123          return ERROR_INVALID_TAG;
00124 
00125       //Clear the tag length
00126       tag->length = 0;
00127       //Read the subsequent octets
00128       for(i++; n > 0; n--)
00129          tag->length = (tag->length << 8) | data[i++];
00130    }
00131    //Indefinite form is used?
00132    else
00133    {
00134       //Indefinite form is not supported
00135       return ERROR_INVALID_TAG;
00136    }
00137 
00138    //Save the pointer to the tag contents
00139    tag->value = data + i;
00140    //Check the length of tag
00141    if((length - i) < tag->length)
00142       return ERROR_INVALID_TAG;
00143 
00144    //Total length occupied by the ASN.1 tag in the input stream
00145    tag->totalLength = i + tag->length;
00146    //ASN.1 tag successfully decoded
00147    return NO_ERROR;
00148 }
00149 
00150 
00151 /**
00152  * @brief Read an integer from the input stream
00153  * @param[in] data Input stream where to read the tag
00154  * @param[in] length Number of bytes available in the input stream
00155  * @param[out] tag Structure describing the ASN.1 tag
00156  * @param[out] value Integer value
00157  * @return Error code
00158  **/
00159 
00160 error_t asn1ReadInt32(const uint8_t *data, size_t length, Asn1Tag *tag, int32_t *value)
00161 {
00162    error_t error;
00163    size_t i;
00164 
00165    //Read ASN.1 tag
00166    error = asn1ReadTag(data, length, tag);
00167    //Failed to decode ASN.1 tag?
00168    if(error)
00169       return error;
00170 
00171    //Enforce encoding, class and type
00172    error = asn1CheckTag(tag, FALSE, ASN1_CLASS_UNIVERSAL, ASN1_TYPE_INTEGER);
00173    //The tag does not match the criteria?
00174    if(error)
00175       return error;
00176 
00177    //The contents shall consist of one or more octets
00178    if(tag->length < 1 || tag->length > 4)
00179       return ERROR_INVALID_TAG;
00180 
00181    //The contents octets shall be a two's complement binary
00182    //number equal to the integer value
00183    *value = (tag->value[0] & 0x80) ? -1 : 0;
00184 
00185    //Process contents octets
00186    for(i = 0; i < tag->length; i++)
00187    {
00188       //Rotate left operation
00189       *value <<= 8;
00190       //Reconstruct integer value
00191       *value |= tag->value[i];
00192    }
00193 
00194    //ASN.1 tag successfully decoded
00195    return NO_ERROR;
00196 }
00197 
00198 
00199 /**
00200  * @brief Write an ASN.1 tag
00201  * @param[in] tag Structure describing the ASN.1 tag
00202  * @param[in] reverse Use reverse encoding
00203  * @param[out] data Output stream where to write the tag (optional parameter)
00204  * @param[out] written Number of bytes written to the output stream (optional parameter)
00205  * @return Error code
00206  **/
00207 
00208 error_t asn1WriteTag(Asn1Tag *tag, bool_t reverse, uint8_t *data, size_t *written)
00209 {
00210    size_t i;
00211    size_t m;
00212    size_t n;
00213 
00214    //Compute the number of octets that are necessary to encode the tag number
00215    if(tag->objType < 31)
00216       m = 0;
00217    else if(tag->objType < 128)
00218       m = 1;
00219    else if(tag->objType < 16384)
00220       m = 2;
00221    else if(tag->objType < 2097152)
00222       m = 3;
00223    else if(tag->objType < 268435456)
00224       m = 4;
00225    else
00226       m = 5;
00227 
00228    //Compute the number of octets that are necessary to encode the length field
00229    if(tag->length < 128)
00230       n = 0;
00231    else if(tag->length < 256)
00232       n = 1;
00233    else if(tag->length < 65536)
00234       n = 2;
00235    else if(tag->length < 16777216)
00236       n = 3;
00237    else
00238       n = 4;
00239 
00240    //Valid output stream?
00241    if(data != NULL)
00242    {
00243       //Use reverse encoding?
00244       if(reverse)
00245       {
00246          //Any data to copy?
00247          if(tag->value != NULL && tag->length > 0)
00248          {
00249             //Make room for the data
00250             data -= tag->length;
00251             //Copy data
00252             memmove(data, tag->value, tag->length);
00253          }
00254 
00255          //Move backward
00256          data -= m + n + 2;
00257       }
00258       else
00259       {
00260          //Any data to copy?
00261          if(tag->value != NULL && tag->length > 0)
00262          {
00263             //Copy data
00264             memmove(data + m + n + 2, tag->value, tag->length);
00265          }
00266       }
00267 
00268       //Save the class of the ASN.1 tag
00269       data[0] = tag->objClass;
00270 
00271       //Primitive or constructed encoding?
00272       if(tag->constructed)
00273          data[0] |= ASN1_ENCODING_CONSTRUCTED;
00274 
00275       //Encode the tag number
00276       if(m == 0)
00277       {
00278          //Tag number is in the range 0 to 30
00279          data[0] |= tag->objType;
00280       }
00281       else
00282       {
00283          //The tag number is greater than or equal to 31
00284          data[0] |= ASN1_TAG_NUMBER_MASK;
00285 
00286          //The subsequent octets will encode the tag number
00287          for(i = 0; i < m; i++)
00288          {
00289             //Bits 7 to 1 encode the tag number
00290             data[m - i] = (tag->objType >> (i * 7)) & 0x7F;
00291 
00292             //Bit 8 of each octet shall be set to one unless it is the
00293             //last octet of the identifier octets
00294             if(i != 0)
00295                data[m - i] |= 0x80;
00296          }
00297       }
00298 
00299       //Encode the length field
00300       if(n == 0)
00301       {
00302          //Use short form encoding
00303          data[1 + m] = tag->length & 0x7F;
00304       }
00305       else
00306       {
00307          //Bits 7 to 1 encode the number of octets in the length field
00308          data[1 + m] = 0x80 | (n & 0x7F);
00309 
00310          //The subsequent octets will encode the length field
00311          for(i = 0; i < n; i++)
00312          {
00313             data[1 + m + n - i] = (tag->length >> (i * 8)) & 0xFF;
00314          }
00315       }
00316    }
00317 
00318    //Total length occupied by the ASN.1 tag
00319    tag->totalLength = tag->length + m + n + 2;
00320 
00321    //The last parameter is optional
00322    if(written != NULL)
00323    {
00324       //Number of bytes written to the output stream
00325       *written = m + n + 2;
00326 
00327       //Any data copied?
00328       if(tag->value != NULL)
00329          *written += tag->length;
00330    }
00331 
00332    //Successful processing
00333    return NO_ERROR;
00334 }
00335 
00336 
00337 /**
00338  * @brief Write an integer to the output stream
00339  * @param[in] value Integer value
00340  * @param[in] reverse Use reverse encoding
00341  * @param[out] data Output stream where to write the tag (optional parameter)
00342  * @param[out] written Number of bytes written to the output stream
00343  * @return Error code
00344  **/
00345 
00346 error_t asn1WriteInt32(int32_t value, bool_t reverse, uint8_t *data, size_t *written)
00347 {
00348    size_t i;
00349    size_t n;
00350    uint16_t msb;
00351 
00352    //An integer value is always encoded in the smallest possible number of octets
00353    for(n = 4; n > 1; n--)
00354    {
00355       //Retrieve the upper 9 bits
00356       msb = (value >> (n * 8 - 9)) & 0x01FF;
00357 
00358       //The upper 9 bits shall not have the same value (all 0 or all 1)
00359       if(msb != 0x0000 && msb != 0x01FF)
00360          break;
00361    }
00362 
00363    //Valid output stream?
00364    if(data != NULL)
00365    {
00366       //Use reverse encoding?
00367       if(reverse)
00368          data -= n + 2;
00369 
00370       //Write tag type
00371       data[0] = ASN1_CLASS_UNIVERSAL | ASN1_TYPE_INTEGER;
00372       //Write tag length
00373       data[1] = n & 0xFF;
00374 
00375       //Write contents octets
00376       for(i = 0; i < n; i++)
00377       {
00378          data[1 + n - i] = (value >> (i * 8)) & 0xFF;
00379       }
00380    }
00381 
00382    //Number of bytes written to the output stream
00383    if(written != NULL)
00384       *written = n + 2;
00385 
00386    //Successful processing
00387    return NO_ERROR;
00388 }
00389 
00390 
00391 /**
00392  * @brief Enforce the type of a specified tag
00393  * @param[in] tag Pointer to an ASN.1 tag
00394  * @param[in] constructed Expected encoding (TRUE for constructed, FALSE for primitive)
00395  * @param[in] objClass Expected tag class
00396  * @param[in] objType Expected tag type
00397  * @return Error code
00398  **/
00399 
00400 error_t asn1CheckTag(const Asn1Tag *tag, bool_t constructed, uint_t objClass, uint_t objType)
00401 {
00402    //Check encoding
00403    if(tag->constructed != constructed)
00404       return ERROR_WRONG_ENCODING;
00405    //Enforce class
00406    if(tag->objClass != objClass)
00407       return ERROR_INVALID_CLASS;
00408    //Enforce type
00409    if(tag->objType != objType)
00410       return ERROR_INVALID_TYPE;
00411 
00412    //The tag matches all the criteria
00413    return NO_ERROR;
00414 }
00415 
00416 
00417 /**
00418  * @brief Check ASN.1 tag against a specified OID
00419  * @param[in] tag Pointer to an ASN.1 tag
00420  * @param[in] oid Expected object identifier (OID)
00421  * @param[in] length Length of the OID
00422  * @return Error code
00423  **/
00424 
00425 error_t asn1CheckOid(const Asn1Tag *tag, const uint8_t *oid, size_t length)
00426 {
00427    error_t error;
00428 
00429    //Enforce encoding, class and type
00430    error = asn1CheckTag(tag, FALSE, ASN1_CLASS_UNIVERSAL, ASN1_TYPE_OBJECT_IDENTIFIER);
00431    //Any error to report?
00432    if(error)
00433       return error;
00434 
00435    //Compare OID against the specified value
00436    if(oidComp(tag->value, tag->length, oid, length))
00437       return ERROR_WRONG_IDENTIFIER;
00438 
00439    //The tag matches all the criteria
00440    return NO_ERROR;
00441 }
00442 
00443 
00444 /**
00445  * @brief Display an ASN.1 data object
00446  * @param[in] data Pointer to the ASN.1 object to dump
00447  * @param[in] length Length of the ASN.1 object
00448  * @param[in] level Current level of recursion (this parameter shall be set to 0)
00449  * @return Error code
00450  **/
00451 
00452 error_t asn1DumpObject(const uint8_t *data, size_t length, uint_t level)
00453 {
00454 //Check debugging level
00455 #if (TRACE_LEVEL >= TRACE_LEVEL_DEBUG)
00456    error_t error;
00457    uint_t i;
00458    Asn1Tag tag;
00459 
00460    //ASN.1 universal types
00461    static const char_t *label[32] =
00462    {
00463       "[0]",
00464       "BOOLEAN",
00465       "INTEGER",
00466       "BIT STRING",
00467       "OCTET STRING",
00468       "NULL",
00469       "OBJECT IDENTIFIER",
00470       "OBJECT DESCRIPTOR",
00471       "EXTERNAL",
00472       "REAL",
00473       "ENUMERATED",
00474       "[11]",
00475       "UTF8 STRING",
00476       "[13]",
00477       "[14]",
00478       "[15]",
00479       "SEQUENCE",
00480       "SET",
00481       "NUMERIC STRING",
00482       "PRINTABLE STRING",
00483       "TELETEX STRING",
00484       "VIDEOTEX STRING",
00485       "IA5 STRING",
00486       "UTC TIME",
00487       "GENERALIZED TIME",
00488       "GRAPHIC STRING",
00489       "VISIBLE STRING",
00490       "GENERAL STRING",
00491       "UNIVERSAL STRING",
00492       "[29]",
00493       "BMP STRING",
00494       "[31]"
00495    };
00496 
00497    //Prefix used to format the structure
00498    static const char_t *prefix[8] =
00499    {
00500       "",
00501       "  ",
00502       "    ",
00503       "      ",
00504       "        ",
00505       "          ",
00506       "            ",
00507       "              "
00508    };
00509 
00510    //Parse ASN.1 object
00511    while(length > 0)
00512    {
00513       //Decode current ASN.1 tag
00514       error = asn1ReadTag(data, length, &tag);
00515       //Decoding failed?
00516       if(error)
00517          return error;
00518 
00519       //Point to the next field
00520       data += tag.totalLength;
00521       length -= tag.totalLength;
00522 
00523       //Dump tag number, tag class, and contents length fields
00524       if(tag.objType < 32 && (tag.objClass & ASN1_CLASS_MASK) == ASN1_CLASS_UNIVERSAL)
00525          TRACE_DEBUG("%s%s (%" PRIuSIZE " bytes)\r\n", prefix[level], label[tag.objType], tag.length);
00526       else
00527          TRACE_DEBUG("%s[%u] (%" PRIuSIZE " bytes)\r\n", prefix[level], tag.objType, tag.length);
00528 
00529       //Constructed type?
00530       if(tag.constructed)
00531       {
00532          //Check whether the maximum level of recursion is reached
00533          if(level < 7)
00534          {
00535             //Recursive decoding of the ASN.1 tag
00536             error = asn1DumpObject(tag.value, tag.length, level + 1);
00537             //Decoding failed?
00538             if(error)
00539                return error;
00540          }
00541          else
00542          {
00543             //If the maximum level of recursion is reached, then dump contents
00544             TRACE_DEBUG_ARRAY(prefix[level + 1], tag.value, tag.length);
00545          }
00546       }
00547       //Primitive type?
00548       else
00549       {
00550          //Check the type of the current tag
00551          switch(tag.objType)
00552          {
00553          //OID?
00554          case ASN1_TYPE_OBJECT_IDENTIFIER:
00555             //Append prefix
00556             TRACE_DEBUG(prefix[level + 1]);
00557             //Print OID
00558             TRACE_DEBUG("%s", oidToString(tag.value, tag.length, NULL, 0));
00559             //Add a line feed
00560             TRACE_DEBUG("\r\n");
00561             break;
00562          //String?
00563          case ASN1_TYPE_UTF8_STRING:
00564          case ASN1_TYPE_NUMERIC_STRING:
00565          case ASN1_TYPE_PRINTABLE_STRING:
00566          case ASN1_TYPE_TELETEX_STRING:
00567          case ASN1_TYPE_VIDEOTEX_STRING:
00568          case ASN1_TYPE_IA5_STRING:
00569          case ASN1_TYPE_GRAPHIC_STRING:
00570          case ASN1_TYPE_VISIBLE_STRING:
00571          case ASN1_TYPE_GENERAL_STRING:
00572          case ASN1_TYPE_UNIVERSAL_STRING:
00573          case ASN1_TYPE_BMP_STRING:
00574             //Append prefix
00575             TRACE_DEBUG("%s", prefix[level + 1]);
00576             //Dump the entire string
00577             for(i = 0; i < tag.length; i++)
00578                TRACE_DEBUG("%c", tag.value[i]);
00579             //Add a line feed
00580             TRACE_DEBUG("\r\n");
00581             break;
00582          //UTC time?
00583          case ASN1_TYPE_UTC_TIME:
00584             //Check length
00585             if(tag.length < 13)
00586                return ERROR_WRONG_ENCODING;
00587             //The encoding shall terminate with a "Z"
00588             if(tag.value[tag.length - 1] != 'Z')
00589                return ERROR_WRONG_ENCODING;
00590 
00591             //Append prefix
00592             TRACE_DEBUG("%s", prefix[level + 1]);
00593             //Display date
00594             TRACE_DEBUG("%c%c/%c%c/%c%c ", tag.value[0], tag.value[1],
00595                tag.value[2], tag.value[3], tag.value[4], tag.value[5]);
00596             //Display time
00597             TRACE_DEBUG("%c%c:%c%c:%c%c", tag.value[6], tag.value[7],
00598                tag.value[8], tag.value[9], tag.value[10], tag.value[11]);
00599             //Add a line feed
00600             TRACE_DEBUG("\r\n");
00601             break;
00602          //Generalized time?
00603          case ASN1_TYPE_GENERALIZED_TIME:
00604             //Check length
00605             if(tag.length < 13)
00606                return ERROR_WRONG_ENCODING;
00607             //The encoding shall terminate with a "Z"
00608             if(tag.value[tag.length - 1] != 'Z')
00609                return ERROR_WRONG_ENCODING;
00610 
00611             //Append prefix
00612             TRACE_DEBUG("%s", prefix[level + 1]);
00613             //Display date
00614             TRACE_DEBUG("%c%c%c%c/%c%c/%c%c ", tag.value[0], tag.value[1], tag.value[2],
00615                tag.value[3], tag.value[4], tag.value[5], tag.value[6], tag.value[7]);
00616             //Display time
00617             TRACE_DEBUG("%c%c:%c%c:%c%c", tag.value[8], tag.value[9],
00618                tag.value[10], tag.value[11], tag.value[12], tag.value[13]);
00619             //Add a line feed
00620             TRACE_DEBUG("\r\n");
00621             break;
00622          //Any other type?
00623          default:
00624             //Dump the contents of the tag
00625             TRACE_DEBUG_ARRAY(prefix[level + 1], tag.value, tag.length);
00626             break;
00627          }
00628       }
00629    }
00630 #endif
00631 
00632    //ASN.1 object successfully decoded
00633    return NO_ERROR;
00634 }
00635 
00636 #endif
00637