source: openpam/trunk/lib/liboath/oath_base64.c @ 770

Last change on this file since 770 was 770, checked in by Dag-Erling Smørgrav, 7 years ago

Fix base{32,64}_decode(). The former handled padding incorrectly; the
latter was derived from the former, and had a couple of copy-paste bugs
in addition to the padding bug.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 4.9 KB
Line 
1/*-
2 * Copyright (c) 2013-2014 Universitetet i Oslo
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 * 3. The name of the author may not be used to endorse or promote
14 *    products derived from this software without specific prior written
15 *    permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
28 *
29 * $Id: oath_base64.c 770 2014-03-06 12:35:47Z des $
30 */
31
32#ifdef HAVE_CONFIG_H
33# include "config.h"
34#endif
35
36#include <sys/types.h>
37
38#include <errno.h>
39#include <stdint.h>
40
41#include <security/oath.h>
42
43static const char b64[] =
44    "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
45    "abcdefghijklmnopqrstuvwxyz"
46    "0123456789+/";
47
48/*
49 * Encode data in RFC 3548 base 64 representation.  The target buffer must
50 * have room for base64_enclen(len) characters and a terminating NUL.
51 */
52int
53base64_enc(const uint8_t *in, size_t ilen, char *out, size_t *olen)
54{
55        uint32_t bits;
56
57        if (*olen <= base64_enclen(ilen))
58                return (-1);
59        *olen = 0;
60        while (ilen >= 3) {
61                bits = 0;
62                bits |= (uint32_t)in[0] << 16;
63                bits |= (uint32_t)in[1] << 8;
64                bits |= (uint32_t)in[2];
65                ilen -= 3;
66                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];
71                *olen += 4;
72                out += 4;
73        }
74        if (ilen > 0) {
75                bits = 0;
76                switch (ilen) {
77                case 2:
78                        bits |= (uint32_t)in[1] << 8;
79                case 1:
80                        bits |= (uint32_t)in[0] << 16;
81                }
82                out[0] = b64[bits >> 18 & 0x3f];
83                out[1] = b64[bits >> 12 & 0x3f];
84                out[2] = ilen > 1 ? b64[bits >> 6 & 0x3f] : '=';
85                out[3] = '=';
86                *olen += 4;
87                out += 4;
88        }
89        out[0] = '\0';
90        ++*olen;
91        return (0);
92}
93
94/*
95 * Decode data in RFC 2548 base 64 representation, stopping at the
96 * terminating NUL, the first invalid (non-base64, non-whitespace)
97 * character or after len characters, whichever comes first.
98 *
99 * The olen argument is used by the caller to pass the size of the buffer
100 * and by base64_dec() to return the amount of data successfully decoded.
101 * If the buffer is too small, base64_dec() discards the excess data, but
102 * returns the total amount.
103 */
104int
105base64_dec(const char *in, size_t ilen, uint8_t *out, size_t *olen)
106{
107        size_t len;
108        uint32_t bits;
109        int shift;
110
111        for (len = 0, bits = 0, shift = 24; ilen && *in; --ilen, ++in) {
112                if (*in == ' ' || *in == '\t' || *in == '\r' || *in == '\n') {
113                        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;
132                } else {
133                        goto bad;
134                }
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;
143                }
144        }
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                }
177        }
178        if (ilen)
179                goto bad;
180        *olen = len;
181        if (len > *olen)
182                return (-1);
183        return (0);
184bad:
185        *olen = 0;
186        return (-1);
187}
Note: See TracBrowser for help on using the repository browser.