Line data Source code
1 : /* b64dec.c - Simple Base64 decoder.
2 : * Copyright (C) 2008, 2011 Free Software Foundation, Inc.
3 : * Copyright (C) 2008, 2011, 2016 g10 Code GmbH
4 : *
5 : * This file is part of GnuPG.
6 : *
7 : * This file is free software; you can redistribute it and/or modify
8 : * it under the terms of the GNU Lesser General Public License as
9 : * published by the Free Software Foundation; either version 2.1 of
10 : * the License, or (at your option) any later version.
11 : *
12 : * This file is distributed in the hope that it will be useful,
13 : * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 : * GNU Lesser General Public License for more details.
16 : *
17 : * You should have received a copy of the GNU Lesser General Public License
18 : * along with this program; if not, see <https://www.gnu.org/licenses/>.
19 : */
20 :
21 : #include <config.h>
22 : #include <stdio.h>
23 : #include <stdlib.h>
24 : #include <string.h>
25 : #include <assert.h>
26 :
27 : #include "gpgrt-int.h"
28 :
29 : struct _gpgrt_b64state
30 : {
31 : int idx;
32 : int quad_count;
33 : char *title;
34 : unsigned char radbuf[4];
35 : int stop_seen:1;
36 : int invalid_encoding:1;
37 : gpg_error_t lasterr;
38 : };
39 :
40 : /* The reverse base-64 list used for base-64 decoding. */
41 : static unsigned char const asctobin[128] =
42 : {
43 : 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
44 : 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
45 : 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
46 : 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
47 : 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
48 : 0xff, 0xff, 0xff, 0x3e, 0xff, 0xff, 0xff, 0x3f,
49 : 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b,
50 : 0x3c, 0x3d, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
51 : 0xff, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
52 : 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
53 : 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16,
54 : 0x17, 0x18, 0x19, 0xff, 0xff, 0xff, 0xff, 0xff,
55 : 0xff, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20,
56 : 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28,
57 : 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30,
58 : 0x31, 0x32, 0x33, 0xff, 0xff, 0xff, 0xff, 0xff
59 : };
60 :
61 : enum decoder_states
62 : {
63 : s_init, s_idle, s_lfseen, s_beginseen, s_waitheader, s_waitblank, s_begin,
64 : s_b64_0, s_b64_1, s_b64_2, s_b64_3,
65 : s_waitendtitle, s_waitend
66 : };
67 :
68 :
69 :
70 : /* Allocate and initialize the context for the base64 decoder. If
71 : TITLE is NULL a plain base64 decoding is done. If it is the empty
72 : string the decoder will skip everything until a "-----BEGIN " line
73 : has been seen, decoding ends at a "----END " line. */
74 : gpgrt_b64state_t
75 1 : _gpgrt_b64dec_start (const char *title)
76 : {
77 : gpgrt_b64state_t state;
78 : char *t = NULL;
79 :
80 1 : if (title)
81 : {
82 1 : t = strdup (title);
83 1 : if (!t)
84 : return NULL;
85 : }
86 :
87 1 : state = calloc (1, sizeof (struct _gpgrt_b64state));
88 1 : if (!state)
89 : {
90 0 : free (t);
91 0 : return NULL;
92 : }
93 :
94 1 : if (t)
95 : {
96 1 : state->title = t;
97 1 : state->idx = s_init;
98 : }
99 : else
100 0 : state->idx = s_b64_0;
101 :
102 : return state;
103 : }
104 :
105 :
106 : /* Do in-place decoding of base-64 data of LENGTH in BUFFER. Stores the
107 : new length of the buffer at R_NBYTES. */
108 : gpg_error_t
109 1 : _gpgrt_b64dec_proc (gpgrt_b64state_t state, void *buffer, size_t length,
110 : size_t *r_nbytes)
111 : {
112 1 : enum decoder_states ds = state->idx;
113 1 : unsigned char val = state->radbuf[0];
114 1 : int pos = state->quad_count;
115 : char *d, *s;
116 :
117 1 : if (state->lasterr)
118 : return state->lasterr;
119 :
120 1 : if (state->stop_seen)
121 : {
122 0 : *r_nbytes = 0;
123 0 : state->lasterr = gpg_error (GPG_ERR_EOF);
124 0 : free (state->title);
125 0 : state->title = NULL;
126 0 : return state->lasterr;
127 : }
128 :
129 332 : for (s=d=buffer; length && !state->stop_seen; length--, s++)
130 : {
131 : again:
132 333 : switch (ds)
133 : {
134 : case s_idle:
135 332 : if (*s == '\n')
136 : {
137 : ds = s_lfseen;
138 : pos = 0;
139 : }
140 : break;
141 : case s_init:
142 : ds = s_lfseen;
143 : case s_lfseen:
144 1 : if (*s != "-----BEGIN "[pos])
145 : {
146 : ds = s_idle;
147 : goto again;
148 : }
149 0 : else if (pos == 10)
150 : {
151 : pos = 0;
152 : ds = s_beginseen;
153 : }
154 : else
155 0 : pos++;
156 : break;
157 : case s_beginseen:
158 0 : if (*s != "PGP "[pos])
159 : ds = s_begin; /* Not a PGP armor. */
160 0 : else if (pos == 3)
161 : ds = s_waitheader;
162 : else
163 0 : pos++;
164 : break;
165 : case s_waitheader:
166 0 : if (*s == '\n')
167 : ds = s_waitblank;
168 : break;
169 : case s_waitblank:
170 0 : if (*s == '\n')
171 : ds = s_b64_0; /* blank line found. */
172 0 : else if (*s == ' ' || *s == '\r' || *s == '\t')
173 : ; /* Ignore spaces. */
174 : else
175 : {
176 : /* Armor header line. Note that we don't care that our
177 : * FSM accepts a header prefixed with spaces. */
178 : ds = s_waitheader; /* Wait for next header. */
179 : }
180 : break;
181 : case s_begin:
182 0 : if (*s == '\n')
183 : ds = s_b64_0;
184 : break;
185 : case s_b64_0:
186 : case s_b64_1:
187 : case s_b64_2:
188 : case s_b64_3:
189 : {
190 : int c;
191 :
192 0 : if (*s == '-' && state->title)
193 : {
194 : /* Not a valid Base64 character: assume end
195 : header. */
196 : ds = s_waitend;
197 : }
198 0 : else if (*s == '=')
199 : {
200 : /* Pad character: stop */
201 0 : if (ds == s_b64_1)
202 0 : *d++ = val;
203 0 : ds = state->title? s_waitendtitle : s_waitend;
204 : }
205 0 : else if (*s == '\n' || *s == ' ' || *s == '\r' || *s == '\t')
206 : ; /* Skip white spaces. */
207 0 : else if ( (*s & 0x80)
208 0 : || (c = asctobin[*(unsigned char *)s]) == 255)
209 : {
210 : /* Skip invalid encodings. */
211 0 : state->invalid_encoding = 1;
212 : }
213 0 : else if (ds == s_b64_0)
214 : {
215 0 : val = c << 2;
216 : ds = s_b64_1;
217 : }
218 0 : else if (ds == s_b64_1)
219 : {
220 0 : val |= (c>>4)&3;
221 0 : *d++ = val;
222 0 : val = (c<<4)&0xf0;
223 : ds = s_b64_2;
224 : }
225 0 : else if (ds == s_b64_2)
226 : {
227 0 : val |= (c>>2)&15;
228 0 : *d++ = val;
229 0 : val = (c<<6)&0xc0;
230 : ds = s_b64_3;
231 : }
232 : else
233 : {
234 0 : val |= c&0x3f;
235 0 : *d++ = val;
236 : ds = s_b64_0;
237 : }
238 : }
239 : break;
240 : case s_waitendtitle:
241 0 : if (*s == '-')
242 : ds = s_waitend;
243 : break;
244 : case s_waitend:
245 0 : if ( *s == '\n')
246 0 : state->stop_seen = 1;
247 : break;
248 : default:
249 0 : assert (!"invalid state");
250 : }
251 : }
252 :
253 :
254 1 : state->idx = ds;
255 1 : state->radbuf[0] = val;
256 1 : state->quad_count = pos;
257 1 : *r_nbytes = (d -(char*) buffer);
258 1 : return 0;
259 : }
260 :
261 :
262 : /* Return an error code in case an encoding error has been found
263 : during decoding. */
264 : gpg_error_t
265 1 : _gpgrt_b64dec_finish (gpgrt_b64state_t state)
266 : {
267 : gpg_error_t err;
268 :
269 1 : if (state->lasterr)
270 : err = state->lasterr;
271 : else
272 : {
273 1 : free (state->title);
274 1 : err = state->invalid_encoding? gpg_error(GPG_ERR_BAD_DATA): 0;
275 : }
276 1 : free (state);
277 :
278 1 : return err;
279 : }
|