1. Trang chủ
  2. » Công Nghệ Thông Tin

cryptography for developers 2006 phần 3 potx

44 301 0

Đang tải... (xem toàn văn)

Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống

THÔNG TIN TÀI LIỆU

Thông tin cơ bản

Tiêu đề ASN.1 Encoding
Trường học Syngress Publishing
Chuyên ngành Cryptography
Thể loại Bài viết
Năm xuất bản 2006
Thành phố Not specified
Định dạng
Số trang 44
Dung lượng 239 KB

Các công cụ chuyển đổi và chỉnh sửa cho tài liệu này

Nội dung

SEQUENCE EncodingsThe SEQUENCE type along with the SET and SET OF types are by far the most encom-passing types to develop.They require all of the ASN.1 functions including them as well!

Trang 1

belonged to (e.g., IA5 or PRINTABLE), a single encode/decoder could be written with an

additional input argument specifying which target code we are trying to use

For the sake of simplicity, the routines demonstrated were implemented individually

They are fairly small and in the grand scheme of things are not significant code size

contributors

UTCTIME Encodings

UTCTIME encoding has been simplified in the 2002 specifications to only include one

format.The time is encoded as a string in the format “YYMMDDHHMMSSZ” using two

digits per component

The year is encoded by examining the last two digits Nothing beyond the year 2069can be encoded with this format as it will be interpreted as 1970 (of course, by that time we

can just reprogram the software to treat it as 2070) Despite the Y2K debacle, they still used

two digits for the date

The actual byte encoding of the string is using the ASCII code, and fortunately, we have

to look no further than our PRINTABLE STRING routines for help

To efficiently handle dates we require some structure to hold the parameters We couldjust pass all six of them as arguments to the function However, a more useful way (as we

shall see when dealing with SEQUENCE types) is to have a structure.This structure is

stored in our ASN.1 header file

Trang 2

This function stores an integer in a two-digit ASCII representation.The number must

be in the range 0–99 for this function to succeed It updates the output pointer, which as weshall see in the next function is immediately useful

We are re-using the der_printable_char_encode() to convert our integers to the ASCIIcode required

025 int der_utctime_encode( UTCTIME *in,

034 /* store header and length */

035 der_put_header_length(&out, ASN1_DER_UTCTIME, 13, outlen);

036

037 /* store data */

Trang 3

double-OR bars (||) Specifically, that is always implemented from left to right and will

abort on the first nonzero return value without proceeding to the next case

This means, for example, that if after encoding the month the routine fails, it will notencode the rest and will proceed inside to the braced statements directly

Trang 4

This function decodes two bytes into the numerical value they represent It borrows thePRINTABLE STRING routines again to decode the bytes to characters It returns thenumber in the “dest” field and signals errors by the return code We will use the same trick

in the decoder to cascade a series of reads, which is why storing the result to a pointer vided by the caller is important

pro-033 int der_utctime_decode(unsigned char *in,

Trang 5

SEQUENCE Encodings

The SEQUENCE type along with the SET and SET OF types are by far the most

encom-passing types to develop.They require all of the ASN.1 functions including them as well!

We will demonstrate relatively simple SEQUENCE encodings and omit SET types forbrevity.The complete listing includes routines to handle SET types and the reader is encour-

aged to seek them out

The first thing we need before we can encode a SEQUENCE is some way of senting it in the C programming language In this case, we are going to use an array of a

repre-structure to represent the elements of the SEQUENCE

This is the structure for a SEQUENCE element type (it will also be used for SET

types).The type indicates the ASN.1 type of the element, length the length or size of the

value, and data is a pointer to the native representation of the given data type An array of

this type describes the elements of a SEQUENCE

The length and data fields have different meanings depending on what the ASN.1 typeis.Table 2.8 describes their use

Table 2.8Definitions for the ASN.1_List Type

ASN.1 Type Meaning of “Data” Meaning of “Length”

BIT STRING Pointer to array of unsigned char Number of bits

OCTET STRING Pointer to array of unsigned char Number of octets

OBJECT IDENTIFIER Pointer to array of unsigned long Number of words in the OID

IA5 STRING Pointer to array of unsigned char Number of characters

PRINTABLE STRING Pointer to array of unsigned char Number of characters

UTCTIME Pointer to a UTCTIME structure Ignored

SEQUENCE Pointer to an array of asn1_list Number of elements in the

listThe use of the length field takes on a dual role depending on whether we are encodingfor decoding For all but the SEQUENCE type where the length is not ignored, the length

Trang 6

specifies the maximum output size when decoding It will be updated by the decoder to theactual length of the object decoded (in terms of what you would pass to the encoder).For example, if you specify a length of 16 for a BIT STRING element and the decoderplaces a 7 in its place, that means that the decoder read a BIT STRING of seven bits.

We will also define a macro that is handy for creating lists at runtime, but first let usproceed through the SEQUENCE functions

der_sequence_length.c:

001 #include “asn1.h"

002 unsigned long der_sequence_paylen(asn1_list *list,

to be encoded, whereas, for example, in the case of OID, length means the number of words

in the OID encoding

Trang 7

058 unsigned long der_sequence_length(asn1_list *list,

002 int der_sequence_encode(asn1_list *list,

006 {

007 unsigned long i, x;

009

010 /* check output size */

011 if (der_sequence_length(list, length) > *outlen) {

Trang 8

017 der_sequence_paylen(list, length),

019

020 /* now encode each element */

021 for (i = 0; i < length; i++) {

055 err = der_null_encode(out, &x);

Trang 9

071 if (err < 0) return err;

084 err = der_utctime_encode(list[i].data, out, &x);

002 int der_sequence_decode(unsigned char *in,

Trang 10

022 for (i = 0; i < length; i++) {

051 ret = der_null_decode(in, payload_length);

Trang 11

Second, the decoding structure must match exactly or the decoding routine will abort.

When we are encoding a structure, the calling application has to understand what it is

encoding.The lists being encoded are meant to be generated at runtime instead of at

com-pile time.This allows us a great level of flexibility as to how we interact with the modifiers

One of the key aspects of the implementation is that it allows new ASN.1 types to be

sup-ported by adding a minimal amount of code to these three routines

Despite the ability to generate encoding SEQUENCEs at runtime, decoding is stillproblematic.The only way to use these functions to decode speculative SEQUENCEs is to

continuously update the list using decoding failures as feedback For example, if an element

is listed as DEFAULT, then the de facto decoding list will have the element If the decoding

fails, the next logical step is to attempt decoding without the default item present

For simple SEQUENCEs this can work, but as they grow in size (such as an X.509 tificate), this approach is completely inappropriate One solution to this would be to have a

cer-more complete decoder that understands the modifiers and can perform the required

ele-ment lookahead (much like a parser in any programming language who lookahead for

tokens) to speculatively decode However, this is actually much more work than the ideal

solution requires

Trang 12

ASN.1 Flexi Decoder

Our approach to this problem is to employ a Flexible Decoder (A.K.A.The Flexi Decoder),which decodes the ASN.1 data on the fly and generates its own linked list of ASN.1 objects.This allows us to decode essentially any ASN.1 data as long as we recognize the ASN.1 typesencoded.The linked list grows in two directions—left to right and parent to child.The left

to right links are for elements that are at the same depth level, and the parent to child linksare for depth changes (e.g., SEQUENCE)

This is our Flexi list, which resembles the asn1_list except that it has the doubly linked

list pointers inside it From a caller’s point of view, they simply pass in an array of bytes andget as output a linked list structure for all the elements decoded

While this solution allows us to easily decode arbitrary ASN.1 data, it still does notimmediately yield a method of parsing it First, we present the decoding algorithm, and inthe next section present how to parse with it

der_flexi_decode.c:

001 #include “asn1.h"

002 #include <stdlib.h>

003 int der_flexi_decode(unsigned char *in,

006 {

007 asn1_flexi *list, *tlist;

008 unsigned long len, x, y;

We start with an empty list (line 11) and handle its construction later We then proceed

to parse data as long as there are at least two bytes left.This allows us to soft error out when

we hit the end of the perceived ASN.1 data

014 /* make sure list points to a valid node */

015 if (list == NULL) {

016 list = calloc(1, sizeof(asn1_flexi));

017 if (list == NULL) return -3;

019 list->next = calloc(1, sizeof(asn1_flexi));

Trang 13

walk the list in any direction.

028 /* decode the payload length */

The encoded payload length immediately reveals this For other types such as BIT STRING

and OID, their maximum size can be calculated from the payload length, which is good

enough for our purposes

048 case ASN1_DER_BOOLEAN: /* BOOLEAN */

049 list->type = ASN1_DER_BOOLEAN;

051 list->data = calloc(1, sizeof(int));

052 if (list->data == NULL) { goto MEM_ERR; }

053

054 err = der_boolean_decode(in, inlen, list->data);

055 if (err < 0) { goto DEC_ERR; }

Trang 14

063 list->data = calloc(1, sizeof(long));

064 if (list->data == NULL) { goto MEM_ERR; }

065

066 err = der_integer_decode(in, inlen, list->data);

067 if (err < 0) { goto DEC_ERR; }

068

069 len = der_integer_length(*((long *)list->data));

076 calloc(list->length, sizeof(unsigned char));

077 if (list->data == NULL) { goto MEM_ERR; }

087 case ASN1_DER_OCTETSTRING: /* OCTET STRING */

Trang 15

120 len = der_oid_length(list->data, list->length);

Similar as for the BIT STRING, we have to estimate the length here In this case, weuse the fact that there cannot be more OID words than there are bytes of payload (the

smallest word encoding is one byte).The wasted memory here can be several words, which

for most circumstances will require to a trivial amount of storage

The interested reader may want to throw a realloc() call in there to free up the unusedwords

123 case ASN1_DER_PRINTABLESTRING: /* PRINTABLE STRING */

Trang 16

157 list->length = 1;

158 list->data = calloc(1, sizeof(UTCTIME));

159 if (list->data == NULL) { goto MEM_ERR; }

160

161 err = der_utctime_decode(in, inlen, list->data);

162 if (err < 0) { goto DEC_ERR; }

171 /* we know the length of the objects in

173 err = der_flexi_decode(in+2, len, &list->child);

174 if (err < 0) { goto DEC_ERR; }

175 list->child->parent = list;

176

177 /* len is the payload length, we have

When we encounter a type we do not recognize (line 181), we do not give a fatal error;

we simply reset the lengths, terminate the decoding, and exit We have to set the payloadlength to zero (line 184) to avoid having the subtraction of payload (line 187) going belowzero

We use the der_*_length() function to compute the length of the decoded type and

store the value back into len By time we get to the end (line 187), we know how much to

move the input pointer up by and decrease the remaining input length by

Trang 17

At this point, we may have added one too many nodes to the list.This is determined byhaving a type of zero, which is not a valid ASN.1 type If this is the case, we get the previous

link, free the leaf node, and update the pointer (lines 193 through 196)

Putting It All Together

At this point, we have all the code we require to implement public key (PK) standards such

as PKCS or ANSI X9.62.The first thing we must master is working with SEQUENCEs No

common PK standard will use the ASN.1 types (other than SEQUENCE) directly

Building Lists

Essentially container ASN.1 type is an array of the asn1_list type So let us examine how we

convert a simple container to code these routines can use

Trang 18

asn1_set(mylist, i++, type, length, data);

If we did not first make a copy of the index, it would be different in every instance itwas used during the macro Similarly, we could have

asn1_set(mylist++, 0, type, length, data);

The use of the do-while loop, allows us to use the macro as a single C statement Forexample:

if (x > 0)

asn1_set(mylist, x, type, length, data);

Now we can reconsider the RSAKey definition

asn1_set(RSAKey, 0, ASN1_DER_INTEGER, 1, &N);

asn1_set(RSAKey, 1, ASN1_DER_INTEGER, 1, &E);

This is much more sensible and pleasant to the eyes

Now suppose we have the RSA key with a modulus N=17*13=221 and E=7 and want

to encode this First, we need a place to store the key

unsigned char output[100];

unsigned char output_length;

Now we can encode the key Let us put the entire example together

asn1_list RSAKey[2];

unsigned char output[100];

unsigned char output_length;

Trang 19

asn1_set(RSAKey, 0, ASN1_DER_INTEGER, 1, &N);

asn1_set(RSAKey, 1, ASN1_DER_INTEGER, 1, &E);

printf("We encoded a SEQUENCE into %lu bytes\n", output_length);

At this point the array output[0 output_length – 1] contains the DER encoding of the

RSAKey SEQUENCE

Nested Lists

Handling SEQUENCEs within a SEQUENCE is essentially an extension of what we

already know Let us consider the following ASN.1 construction

User := SEQUENCE {

Name PRINTABLE STRING,

Credentials SEQUENCE { passwdHash OCTET STRING }

}

As before, we build two lists Here is the complete example

asn1_list User[3], Credentials;

unsigned char output[100];

unsigned char output_length;

unsigned char Name[MAXLEN+1], passwdHash[HASHLEN];

/* build the first list */

asn1_set(User, 0, ASN1_DER_PRINTABLESTRING, strlen(Name), Name);

asn1_set(User, 1, ASN1_DER_INTEGER, 1, &Age);

asn1_set(User, 2, ASN1_DER_SEQUENCE, 1, &Credentials);

/* build second list */

asn1_set(Credentials, 0, ASN1_DER_OCTETSTRING, HASHLEN, passwdHash);

/* encode it */

Trang 20

output_length = sizeof(output);

err = der_sequence_encode(User, 3, output, &output_length);

if (err < 0) { printf("Error encoding %d\n", err); exit(EXIT_FAILURE); }

When building the first list we pass a pointer to the second list (the third entry in theUser array).The corresponding “1” marked in the length field for the third entry is actuallythe length of the second list.That is, if the Credentials list had two items we would see a two

in place of the one

Note that we encode the entire construction by invoking the encoder once with theUser list.The encoder will follow the pointer to the second list and encode it in turn aswell

Now we have to setup the list

asn1_set(User, 0, ASN1_DER_PRINTABLESTRING, sizeof(Name)-1, Name);

asn1_set(User, 1, ASN1_DER_INTEGER, 1, &Age);

asn1_set(User, 2, ASN1_DER_BITSTRING, sizeof(Flags), Flags);

Note that we are using sizeof(Name)-1 and not just the size of the object.This allows us

to have a trailing NUL byte so that the C string functions can work on the data upondecoding it

Let us put this entire example together:

unsigned char Name[MAXNAMELEN+1], Flags[MAXFLAGSLEN];

asn1_list User[3];

asn1_set(User, 0, ASN1_DER_PRINTABLESTRING, sizeof(Name)-1, Name);

asn1_set(User, 1, ASN1_DER_INTEGER, 1, &Age);

asn1_set(User, 2, ASN1_DER_BITSTRING, sizeof(Flags), Flags);

memset(Name, 0, sizeof(Name));

err = der_sequence_decode(input, input_len, &User, 3);

if (err < 0) {

Trang 21

printf("Error decoding the sequence: %d\n", err);

exit(EXIT_FAILURE);

}

printf("Decoded the sequence\n");

printf("User Name[%lu] == [%s]\n", User[0].length, Name);

printf("Age == %ld\n", Age);

This example assumes that some array of unsigned char called input has been providedand its length is input_len Upon successful decoding, the program output will display the

user name and the age For instance, the output may resemble the following

Decoded the sequence

User Name[3] == [Tom]

Age == 24

As see the User array is updated for specific elements such as the STRING types.

User[0].length will hold the length of the decoded value Note that in our example we first

memset the array to zero.This allows us to use the decoded array as a valid C string

regard-less of the length

FlexiLists

As we discussed in the previous section the SEQUENCE decoder is not very amenable to

speculative encodings.The flexi decoder allows us to decode ASN.1 data without knowing

the order or even the types of the elements that were encoded

While the flexi decoder allows for speculative decoding, it does not allow for speculativeparsing What we are given by the decoder is simply a multi-way doubly linked list Each

node of the list contains the ASN.1 type, its respective length parameter and a pointer (if

appropriate) to the decoding of the data (in a respective format)

The encodings do not tell us which element of the constructed type we are looking at

For example, consider the following SEQUENCE

pres-we will get two depths to the list.The first depth will contain just the SEQUENCE and the

child of that node will be a list of up to three items (see Figure 2.4)

Trang 22

Figure 2.4Organization of the Flexi Decoding of RSAKey

Suppose we store the outcome in MyKey such as the following:

asn1_flexi *MyKey;

der_flexi_decode(keyPacket, keyPacketLen, &MyKey);

In this instance, MyKey would point to the SEQUENCE node of the list Its next and

prev pointers will be NULL and it will have a pointer to a child node.The child node will be

the “D” INTEGER, if it is present, and it will have a pointer to the “E” INTEGER through

the next pointer.

Given the MyKey pointer, we can move to the child node with the following code:

MyKey = MyKey->next; /* now we point to "E" */

MyKey = MyKey->parent; /* this is invalid */

Now how do we know if we have a private or public key? The simplest method in thisparticular case is to count the number of children:

If the x variable is two, it is a public key; otherwise, it is a private key This approach

works well for simple OPTIONAL types but only if there is one OPTIONAL element.Similarly, for CHOICE modifiers we cannot simply look at the length but need also theSequence

Ngày đăng: 12/08/2014, 20:22

TỪ KHÓA LIÊN QUAN