Tải bản đầy đủ - 0 (trang)
4 Turning ASCII Hex Keys (or Other ASCII Hex Data) into Binary

# 4 Turning ASCII Hex Keys (or Other ASCII Hex Data) into Binary

Tải bản đầy đủ - 0trang

leading “0x” if it exists. The exception is when there is whitespace. This function

passes back the number of bytes written in its second parameter. If that parameter is

negative, an error occurred.

Discussion

The spc_hex2bin( ) function shown in this section converts an ASCII string into a

binary string. Spaces and tabs are ignored. A leading “0x” or “0X” is ignored. There

are two cases in which this function can fail. First, if it sees a non-hexadecimal digit,

it assumes that the string is not in the right format, and it returns NULL, setting the

error parameter to ERR_NOT_HEX. Second, if there is an odd number of hex digits in the

string, it returns NULL, setting the error parameter to ERR_BAD_SIZE.

#include

#include

#include

#define ERR_NOT_HEX -1

#define ERR_NO_MEM

-3

unsigned char *spc_hex2bin(const unsigned char *input, size_t *l) {

unsigned char

shift = 4, value = 0;

unsigned char

*r, *ret;

const unsigned char *p;

if (!(r = ret = (unsigned char *)malloc(strlen(input) / 2))) {

*l = ERR_NO_MEM;

return 0;

}

for (p = input; isspace(*p); p++);

if (p[0] = = '0' && (p[1] = = 'x' || p[1] = = 'X')) p += 2;

while (p[0]) {

switch (p[0]) {

case '0': case '1': case '2': case '3': case '4':

case '5': case '6': case '7': case '8': case '9':

value |= (*p++ - '0') << shift;

break;

case 'a': case 'b': case 'c':

case 'd': case 'e': case 'f':

value |= (*p++ - 'a' + 0xa) << shift;

break;

case 'A': case 'B': case 'C':

case 'D': case 'E': case 'F':

value |= (*p++ - 'A' + 0xa) << shift;

break;

case 0:

if (!shift) {

*l = ERR_NOT_HEX;

free(ret);

return 0;

122

|

Chapter 4: Symmetric Cryptography Fundamentals

This is the Title of the Book, eMatter Edition

}

break;

default:

if (isspace(p[0])) p++;

else {

*l = ERR_NOT_HEX;

free(ret);

return 0;

}

}

if ((shift = (shift + 4) % 8) != 0) {

*r++ = value;

value = 0;

}

}

if (!shift) {

free(ret);

return 0;

}

*l = (r - ret);

return (unsigned char *)realloc(ret, *l);

}

4.5

Performing Base64 Encoding

Problem

You want to represent binary data in as compact a textual representation as is reasonable, but the data must be easy to encode and decode, and it must use printable

text characters.

Solution

Base64 encoding encodes six bits of data at a time, meaning that every six bits of

input map to one character of output. The characters in the output will be a numeric

digit, a letter (uppercase or lowercase), a forward slash, a plus, or the equal sign

(which is a special padding character).

Note that four output characters map exactly to three input characters. As a result, if

the input string isn’t a multiple of three characters, you’ll need to do some padding

(explained in the “Discussion” section).

Discussion

The base64 alphabet takes 6-bit binary values representing numbers from 0 to 63

and maps them to a set of printable ASCII characters. The values 0 through 25 map

to the uppercase letters in order. The values 26 through 51 map to the lowercase letters. Then come the decimal digits from 0 to 9, and finally + and /.

Performing Base64 Encoding | 123

This is the Title of the Book, eMatter Edition

If the length of the input string isn’t a multiple of three bytes, the leftover bits are

padded to a multiple of six with zeros; then the last character is encoded. If only one

byte would have been needed in the input to make it a multiple of three, the pad

character (=) is added to the end of the string. Otherwise, two pad characters are

#include

static char b64table[64] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"

"abcdefghijklmnopqrstuvwxyz"

"0123456789+/";

/* Accepts a binary buffer with an associated size.

* Returns a base64 encoded, NULL-terminated string.

*/

unsigned char *spc_base64_encode(unsigned char *input, size_t len, int wrap) {

unsigned char *output, *p;

size_t

i = 0, mod = len % 3, toalloc;

toalloc =

if (wrap)

toalloc

if (len

}

(len / 3) * 4 + (3 - mod) % 3 + 1;

{

+= len / 57;

% 57) toalloc++;

p = output = (unsigned char *)malloc(((len / 3) + (mod ? 1 : 0)) * 4 + 1);

if (!p) return 0;

while (i < len - mod) {

*p++ = b64table[input[i++] >> 2];

*p++ = b64table[((input[i - 1] << 4) | (input[i] >> 4)) & 0x3f];

*p++ = b64table[((input[i] << 2) | (input[i + 1] >> 6)) & 0x3f];

*p++ = b64table[input[i + 1] & 0x3f];

i += 2;

if (wrap && !(i % 57)) *p++ = '\n';

}

if (!mod) {

if (wrap && i % 57) *p++ = '\n';

*p = 0;

return output;

} else {

*p++ = b64table[input[i++] >> 2];

*p++ = b64table[((input[i - 1] << 4) | (input[i] >> 4)) & 0x3f];

if (mod = = 1) {

*p++ = '=';

*p++ = '=';

if (wrap) *p++ = '\n';

*p = 0;

return output;

} else {

*p++ = b64table[(input[i] << 2) & 0x3f];

*p++ = '=';

if (wrap) *p++ = '\n';

124

|

Chapter 4: Symmetric Cryptography Fundamentals

This is the Title of the Book, eMatter Edition

*p = 0;

return output;

}

}

}

The public interface to the above code is the following:

unsigned char *spc base64_encode(unsigned char *input, size_t len, int wrap);

The result is a NULL-terminated string allocated internally via malloc( ). Some protocols may expect you to “wrap” base64-encoded data so that, when printed, it takes

up less than 80 columns. If such behavior is necessary, you can pass in a non-zero

value for the final parameter, which will cause this code to insert newlines once every

76 characters. In that case, the string will always end with a newline (followed by the

expected NULL-terminator).

If the call to malloc( ) fails because there is no memory, this function returns 0.

Recipe 4.6

4.6

Performing Base64 Decoding

Problem

You have a base64-encoded string that you’d like to decode.

Solution

Use the inverse of the algorithm for encoding, presented in Recipe 4.5. This is most

easily done via table lookup, mapping each character in the input to six bits of output.

Discussion

Following is our code for decoding a base64-encoded string. We look at each byte

separately, mapping it to its associated 6-bit value. If the byte is NULL, we know that

we’ve reached the end of the string. If it represents a character not in the base64 set,

we ignore it unless the strict argument is non-zero, in which case we return an

error.

The RFC that specifies this encoding says you should silently ignore

any unnecessary characters in the input stream. If you don’t have to do

so, we recommend you don’t, as this constitutes a covert channel in

any protocol using this encoding.

Performing Base64 Decoding

This is the Title of the Book, eMatter Edition

| 125

Note that we check to ensure strings are properly padded. If the string isn’t properly

padded or otherwise terminates prematurely, we return an error.

#include

#include

static char b64revtb[256]

-3, -1, -1, -1, -1, -1,

-1, -1, -1, -1, -1, -1,

-1, -1, -1, -1, -1, -1,

52, 53, 54, 55, 56, 57,

-1, 0, 1, 2, 3, 4,

15, 16, 17, 18, 19, 20,

-1, 26, 27, 28, 29, 30,

41, 42, 43, 44, 45, 46,

-1, -1, -1, -1, -1, -1,

-1, -1, -1, -1, -1, -1,

-1, -1, -1, -1, -1, -1,

-1, -1, -1, -1, -1, -1,

-1, -1, -1, -1, -1, -1,

-1, -1, -1, -1, -1, -1,

-1, -1, -1, -1, -1, -1,

-1, -1, -1, -1, -1, -1,

};

= {

-1,

-1,

-1,

58,

5,

21,

31,

47,

-1,

-1,

-1,

-1,

-1,

-1,

-1,

-1,

-1,

-1,

-1,

59,

6,

22,

32,

48,

-1,

-1,

-1,

-1,

-1,

-1,

-1,

-1,

-1,

-1,

-1,

60,

7,

23,

33,

49,

-1,

-1,

-1,

-1,

-1,

-1,

-1,

-1,

-1,

-1,

-1,

61,

8,

24,

34,

50,

-1,

-1,

-1,

-1,

-1,

-1,

-1,

-1,

-1,

-1,

-1,

-1,

9,

25,

35,

51,

-1,

-1,

-1,

-1,

-1,

-1,

-1,

-1,

-1,

-1,

62,

-1,

10,

-1,

36,

-1,

-1,

-1,

-1,

-1,

-1,

-1,

-1,

-1,

-1,

-1,

-1,

-1,

11,

-1,

37,

-1,

-1,

-1,

-1,

-1,

-1,

-1,

-1,

-1,

-1,

-1,

-1,

-2,

12,

-1,

38,

-1,

-1,

-1,

-1,

-1,

-1,

-1,

-1,

-1,

-1,

-1,

-1,

-1,

13,

-1,

39,

-1,

-1,

-1,

-1,

-1,

-1,

-1,

-1,

-1,

-1,

-1,

63,

-1,

14,

-1,

40,

-1,

-1,

-1,

-1,

-1,

-1,

-1,

-1,

-1

/*0-15*/

/*16-31*/

/*32-47*/

/*48-63*/

/*64-79*/

/*80-95*/

/*96-111*/

/*112-127*/

/*128-143*/

/*144-159*/

/*160-175*/

/*176-191*/

/*192-207*/

/*208-223*/

/*224-239*/

/*240-255*/

static unsigned int raw_base64_decode(unsigned char *in, unsigned char *out,

int strict, int *err) {

unsigned int result = 0, x;

unsigned char buf[3], *p = in, pad = 0;

*err = 0;

switch ((x = b64revtb[*p++])) {

case -3: /* NULL TERMINATOR */

if (((p - 1) - in) % 4) *err = 1;

return result;

case -2: /* PADDING CHARACTER. INVALID HERE */

if (((p - 1) - in) % 4 < 2) {

*err = 1;

return result;

} else if (((p - 1) - in) % 4 = = 2) {

/* Make sure there's appropriate padding */

if (*p != '=') {

*err = 1;

return result;

}

buf[2] = 0;

result++;

break;

} else {

result += 2;

break;

126

|

Chapter 4: Symmetric Cryptography Fundamentals

This is the Title of the Book, eMatter Edition

}

return result;

case -1:

if (strict) {

*err = 2;

return result;

}

break;

default:

switch (((p - 1) - in) % 4) {

case 0:

buf[0] = x << 2;

break;

case 1:

buf[0] |= (x >> 4);

buf[1] = x << 4;

break;

case 2:

buf[1] |= (x >> 2);

buf[2] = x << 6;

break;

case 3:

buf[2] |= x;

result += 3;

for (x = 0; x < 3 - pad;

break;

}

break;

}

}

for (x = 0; x < 3 - pad;

return result;

x++) *out++ = buf[x];

x++) *out++ = buf[x];

}

/* If err is non-zero on exit, then there was an incorrect padding error. We

* allocate enough space for all circumstances, but when there is padding, or

* there are characters outside the character set in the string (which we are

* supposed to ignore), then we end up allocating too much space. You can

* realloc( ) to the correct length if you wish.

*/

unsigned char *spc_base64_decode(unsigned char *buf, size_t *len, int strict,

int *err) {

unsigned char *outbuf;

outbuf = (unsigned char *)malloc(3 * (strlen(buf) / 4 + 1));

if (!outbuf) {

*err = -3;

*len = 0;

return 0;

}

*len = raw_base64_decode(buf, outbuf, strict, err);

if (*err) {

free(outbuf);

Performing Base64 Decoding

This is the Title of the Book, eMatter Edition

| 127

*len = 0;

outbuf = 0;

}

return outbuf;

}

The public API to this code is:

unsigned char *spc_base64_decode(unsigned char *buf, size_t *len, int strict, int

*err);

The API assumes that buf is a NULL-terminated string. The len parameter is a pointer

that receives the length of the binary output. If there is an error, the memory pointed

to by len will be 0, and the value pointed to by err will be non-zero. The error will be

-1 if there is a padding error, -2 if strict checking was requested, but a character outside the strict set is found, and -3 if malloc( ) fails.

Recipe 4.5

4.7

Representing Keys (or Other Binary Data)

as English Text

Problem

You want to use an easy-to-read format for displaying keys (or fingerprints or some

other interesting binary data). English would work better than a hexadecimal representation because people’s ability to recognize the key as correct by sight will be better.

Solution

Map a particular number of bits to a dictionary of words. The dictionary should be

of such a size that an exact mapping to a number of bits is possible. That is, the dictionary should have a number of entries that is a power of two.

Discussion

The spc_bin2words() function shown here converts a binary string of the specified

number of bytes into a string of English words. This function takes two arguments:

str is the binary string to convert, and len is the number of bytes to be converted.

#include

#include

#include "wordlist.h"

#define BITS_IN_LIST 11

128

|

Chapter 4: Symmetric Cryptography Fundamentals

This is the Title of the Book, eMatter Edition

#define MAX_WORDLEN

4

/* len parameter is measured in bytes. Remaining bits padded with 0. */

unsigned char *spc_bin2words(const unsigned char *str, size_t len) {

short

size_t

i, leftbits, leftovers, scratch = 0, scratch_bits = 0;

unsigned char *p, *res;

res = (unsigned char *)malloc((len * 8 / BITS_IN_LIST + 1) * (MAX_WORDLEN + 1));

if (!res) abort( );

res[0] = 0;

for (i = 0; i < len; i++) {

leftovers = str[i];

leftbits = 8;

while (leftbits) {

if (scratch_bits + leftbits <= BITS_IN_LIST) {

scratch |= (leftovers << (BITS_IN_LIST - leftbits - scratch_bits));

scratch_bits += leftbits;

leftbits = 0;

} else {

scratch |= (leftovers >> (leftbits - (BITS_IN_LIST - scratch_bits)));

leftbits -= (BITS_IN_LIST - scratch_bits);

leftovers &= ((1 << leftbits) - 1);

scratch_bits = BITS_IN_LIST;

}

if (scratch_bits = = BITS_IN_LIST) {

p = words[scratch];

/* The strcats are a bit inefficient because they start from the front of

* the string each time. But, they're less confusing, and these strings

* should never get more than a few words long, so efficiency will

* probably never be a real concern.

*/

strcat(res, p);

scratch = scratch_bits = 0;

}

}

}

if (scratch_bits) { /* Emit the final word */

p = words[scratch];

strcat(res, p);

}

res = (unsigned char *)realloc(res, strlen(res) + 1);

if (!res) abort( ); /* realloc failed; should never happen, as size shrinks */

return res;

}

To save space, the dictionary file (wordlist.h) is not provided here. Instead, you can

find it on the book’s web site.

Representing Keys (or Other Binary Data) as English Text | 129

This is the Title of the Book, eMatter Edition