Changeset 771 in openpam for trunk/lib/liboath/oath_base64.c


Ignore:
Timestamp:
Mar 6, 2014, 5:54:58 PM (7 years ago)
Author:
Dag-Erling Smørgrav
Message:

Replace base{32,64}_decode() with table-driven implementations. The new
code is less strict about padding, thus ensuring compatibility with
implementations which do not understand padding, such as MIME::Base32.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/lib/liboath/oath_base64.c

    r770 r771  
    4141#include <security/oath.h>
    4242
    43 static const char b64[] =
     43static const char b64enc[] =
    4444    "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
    4545    "abcdefghijklmnopqrstuvwxyz"
    4646    "0123456789+/";
    4747
     48static const uint8_t b64dec[256] = {
     49        ['A'] =  0, ['B'] =  1, ['C'] =  2, ['D'] =  3,
     50        ['E'] =  4, ['F'] =  5, ['G'] =  6, ['H'] =  7,
     51        ['I'] =  8, ['J'] =  9, ['K'] = 10, ['L'] = 11,
     52        ['M'] = 12, ['N'] = 13, ['O'] = 14, ['P'] = 15,
     53        ['Q'] = 16, ['R'] = 17, ['S'] = 18, ['T'] = 19,
     54        ['U'] = 20, ['V'] = 21, ['W'] = 22, ['X'] = 23,
     55        ['Y'] = 24, ['Z'] = 25, ['a'] = 26, ['b'] = 27,
     56        ['c'] = 28, ['d'] = 29, ['e'] = 30, ['f'] = 31,
     57        ['g'] = 32, ['h'] = 33, ['i'] = 34, ['j'] = 35,
     58        ['k'] = 36, ['l'] = 37, ['m'] = 38, ['n'] = 39,
     59        ['o'] = 40, ['p'] = 41, ['q'] = 42, ['r'] = 43,
     60        ['s'] = 44, ['t'] = 45, ['u'] = 46, ['v'] = 47,
     61        ['w'] = 48, ['x'] = 49, ['y'] = 50, ['z'] = 51,
     62        ['0'] = 52, ['1'] = 53, ['2'] = 54, ['3'] = 55,
     63        ['4'] = 56, ['5'] = 57, ['6'] = 58, ['7'] = 59,
     64        ['8'] = 60, ['9'] = 61, ['+'] = 62, ['/'] = 63,
     65};
     66
    4867/*
    49  * Encode data in RFC 3548 base 64 representation.  The target buffer must
     68 * Encode data in RFC 4648 base 64 representation.  The target buffer must
    5069 * have room for base64_enclen(len) characters and a terminating NUL.
    5170 */
     
    6584                ilen -= 3;
    6685                in += 3;
    67                 out[0] = b64[bits >> 18 & 0x3f];
    68                 out[1] = b64[bits >> 12 & 0x3f];
    69                 out[2] = b64[bits >> 6 & 0x3f];
    70                 out[3] = b64[bits & 0x3f];
     86                out[0] = b64enc[bits >> 18 & 0x3f];
     87                out[1] = b64enc[bits >> 12 & 0x3f];
     88                out[2] = b64enc[bits >> 6 & 0x3f];
     89                out[3] = b64enc[bits & 0x3f];
    7190                *olen += 4;
    7291                out += 4;
     
    8099                        bits |= (uint32_t)in[0] << 16;
    81100                }
    82                 out[0] = b64[bits >> 18 & 0x3f];
    83                 out[1] = b64[bits >> 12 & 0x3f];
    84                 out[2] = ilen > 1 ? b64[bits >> 6 & 0x3f] : '=';
     101                out[0] = b64enc[bits >> 18 & 0x3f];
     102                out[1] = b64enc[bits >> 12 & 0x3f];
     103                out[2] = ilen > 1 ? b64enc[bits >> 6 & 0x3f] : '=';
    85104                out[3] = '=';
    86105                *olen += 4;
     
    93112
    94113/*
    95  * Decode data in RFC 2548 base 64 representation, stopping at the
     114 * Decode data in RFC 4648 base 64 representation, stopping at the
    96115 * terminating NUL, the first invalid (non-base64, non-whitespace)
    97116 * character or after len characters, whichever comes first.
     117 *
     118 * Padding is handled sloppily: any padding character following the data
     119 * is silently consumed.  This not only simplifies the code but ensures
     120 * compatibility with implementations which do not emit or understand
     121 * padding.
    98122 *
    99123 * The olen argument is used by the caller to pass the size of the buffer
     
    106130{
    107131        size_t len;
    108         uint32_t bits;
    109         int shift;
     132        int bits, shift, padding;
    110133
    111         for (len = 0, bits = 0, shift = 24; ilen && *in; --ilen, ++in) {
    112                 if (*in == ' ' || *in == '\t' || *in == '\r' || *in == '\n') {
     134        for (bits = shift = padding = len = 0; ilen && *in; --ilen, ++in) {
     135                if (*in == ' ' || *in == '\t' || *in == '\r' || *in == '\n' ||
     136                    (padding && *in == '=')) {
     137                        /* consume */
    113138                        continue;
    114                 } else if (*in >= 'A' && *in <= 'Z') {
    115                         shift -= 6;
    116                         bits |= (uint32_t)(*in - 'A') << shift;
    117                 } else if (*in >= 'a' && *in <= 'z') {
    118                         shift -= 6;
    119                         bits |= (uint32_t)(*in - 'a' + 26) << shift;
    120                 } else if (*in >= '0' && *in <= '9') {
    121                         shift -= 6;
    122                         bits |= (uint32_t)(*in - '0' + 52) << shift;
    123                 } else if (*in == '+') {
    124                         shift -= 6;
    125                         bits |= (uint32_t)62 << shift;
    126                 } else if (*in == '/') {
    127                         shift -= 6;
    128                         bits |= (uint32_t)63 << shift;
    129                 } else if (*in == '=') {
    130                         /* handled below */
    131                         break;
     139                } else if (!padding && b64dec[(int)*in]) {
     140                        /* shift into accumulator */
     141                        shift += 6;
     142                        bits = bits << 6 | b64dec[(int)*in];
     143                } else if (!padding && shift && *in == '=') {
     144                        /* final byte */
     145                        shift = 0;
     146                        padding = 1;
    132147                } else {
    133                         goto bad;
     148                        /* error */
     149                        *olen = 0;
     150                        errno = EINVAL;
     151                        return (-1);
    134152                }
    135                 if (shift == 0) {
    136                         if ((len += 3) <= *olen) {
    137                                 *out++ = (bits >> 16) & 0xff;
    138                                 *out++ = (bits >> 8) & 0xff;
    139                                 *out++ = bits & 0xff;
    140                         }
    141                         bits = 0;
    142                         shift = 24;
     153                if (shift >= 8) {
     154                        /* output accumulated byte */
     155                        shift -= 8;
     156                        if (len++ < *olen)
     157                                *out++ = (bits >> shift) & 0xff;
    143158                }
    144159        }
    145         if (ilen && *in == '=' && (shift == 12 || shift == 6)) {
    146                 /*
    147                  * Padding:
    148                  *
    149                  * 00            8 AA== 12
    150                  * 00 00        16 AAA=  6
    151                  *
    152                  * XXX We should check that the last few bits before the
    153                  * padding starts are zero.
    154                  */
    155                 switch (shift) {
    156                 case 6:
    157                         if (++len <= *olen)
    158                                 *out++ = (bits >> 16) & 0xff;
    159                         bits <<= 8;
    160                 case 12:
    161                         if (++len <= *olen)
    162                                 *out++ = (bits >> 16) & 0xff;
    163                         bits <<= 8;
    164                         break;
    165                 default:
    166                         goto bad;
    167                 }
    168                 /* consume remaining padding and whitespace */
    169                 for (; ilen && *in; --ilen, ++in) {
    170                         if (*in == ' ' || *in == '\t' || *in == '\r' || *in == '\n')
    171                                 continue;
    172                         else if (*in == '=' && shift)
    173                                 shift -= 6;
    174                         else
    175                                 goto bad;
    176                 }
     160        /* report decoded length */
     161        *olen = len;
     162        if (len > *olen) {
     163                /* overflow */
     164                errno = ENOSPC;
     165                return (-1);
    177166        }
    178         if (ilen)
    179                 goto bad;
    180         *olen = len;
    181         if (len > *olen)
    182                 return (-1);
    183167        return (0);
    184 bad:
    185         *olen = 0;
    186         return (-1);
    187168}
Note: See TracChangeset for help on using the changeset viewer.