* base64.c: New. Changed all other functions to use this instead
[gnupg.git] / sm / base64.c
1 /* base64.c 
2  *      Copyright (C) 2001 Free Software Foundation, Inc.
3  *
4  * This file is part of GnuPG.
5  *
6  * GnuPG is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * GnuPG is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
19  */
20
21 #include <config.h>
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <errno.h>
26 #include <unistd.h> 
27 #include <time.h>
28 #include <assert.h>
29
30 #include <ksba.h>
31
32 #include "gpgsm.h"
33 #include "i18n.h"
34
35
36 /* data used by the reader callbacks */
37 struct reader_cb_parm_s {
38   FILE *fp;
39   unsigned char line[1024];
40   int linelen;
41   int readpos;
42   int have_lf;
43   unsigned long line_counter;
44
45   int autodetect; /* try to detect the input encoding */
46   int assume_pem; /* assume input encoding is PEM */
47   int assume_base64; /* assume inpout is base64 encoded */
48
49   int identified;
50   int is_pem;
51   int stop_seen;
52
53   struct {
54     int idx;
55     unsigned char val;
56     int stop_seen;
57   } base64;
58 };
59
60
61 struct base64_context_s {
62   struct reader_cb_parm_s rparm;
63 };
64
65
66 /* The base-64 character list */
67 static unsigned char bintoasc[64] = 
68        "ABCDEFGHIJKLMNOPQRSTUVWXYZ" 
69        "abcdefghijklmnopqrstuvwxyz" 
70        "0123456789+/"; 
71 /* The reverse base-64 list */
72 static unsigned char asctobin[256] = {
73   0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
74   0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
75   0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
76   0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3e, 0xff, 0xff, 0xff, 0x3f, 
77   0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0xff, 0xff, 
78   0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 
79   0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 
80   0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0xff, 0xff, 0xff, 0xff, 0xff, 
81   0xff, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 
82   0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 
83   0x31, 0x32, 0x33, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
84   0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
85   0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
86   0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
87   0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
88   0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
89   0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
90   0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
91   0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
92   0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
93   0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
94   0xff, 0xff, 0xff, 0xff
95 };
96
97
98
99 static int
100 base64_reader_cb (void *cb_value, char *buffer, size_t count, size_t *nread)
101 {
102   struct reader_cb_parm_s *parm = cb_value;
103   size_t n;
104   int c, c2;
105
106   *nread = 0;
107   if (!buffer)
108     return -1; /* not supported */
109
110  next:
111   if (!parm->linelen)
112     {
113       /* read an entire line or up to the size of the buffer */
114       parm->line_counter++;
115       parm->have_lf = 0;
116       for (n=0; n < DIM(parm->line);)
117         {
118           c = getc (parm->fp);
119           if (c == EOF)
120             {
121               if (ferror (parm->fp))
122                 return -1;
123               break; 
124             }
125           parm->line[n++] = c;
126           if (c == '\n')
127             {
128               parm->have_lf = 1;
129               /* FIXME: we need to skip overlong lines while detecting
130                  the dashed lines */
131               break;
132             }
133         }
134       parm->linelen = n;
135       if (!n)
136         return -1; /* eof */
137       parm->readpos = 0;
138     }
139
140   if (!parm->identified)
141     {
142       if (parm->line_counter == 1 && !parm->have_lf)
143         {
144           /* first line too long - assume DER encoding */
145           parm->is_pem = 0;
146         }
147       else if (parm->line_counter == 1 && parm->linelen && *parm->line == 0x30)
148         {
149           /* the very first bytes does pretty much look like a SEQUENCE tag*/
150           parm->is_pem = 0;
151         }
152       else if ( parm->have_lf && !strncmp (parm->line, "-----BEGIN ", 11)
153                 && strncmp (parm->line+11, "PGP ", 4) )
154         {
155           /* Fixme: we must only compare if the line really starts at
156              the beginning */
157           parm->is_pem = 1;
158           parm->linelen = parm->readpos = 0;
159         }
160       else
161         {
162           parm->linelen = parm->readpos = 0;
163           goto next;
164         }
165       parm->identified = 1;
166       parm->base64.stop_seen = 0;
167       parm->base64.idx = 0;
168     }
169   
170
171   n = 0;
172   if (parm->is_pem)
173     {  
174       if (parm->have_lf && !strncmp (parm->line, "-----END ", 9))
175         { 
176           parm->identified = 0;
177           parm->linelen = parm->readpos = 0;
178           /* let us return 0 */
179         }
180       else if (parm->stop_seen)
181         { /* skip the rest of the line */
182           parm->linelen = parm->readpos = 0;
183         }
184       else
185         {
186           int idx = parm->base64.idx;
187           unsigned char val = parm->base64.val;
188
189           while (n < count && parm->readpos < parm->linelen )
190             {
191               c = parm->line[parm->readpos++];
192               if (c == '\n' || c == ' ' || c == '\r' || c == '\t')
193                 continue;
194               if (c == '=')
195                 { /* pad character: stop */
196                   if (idx == 1)
197                     buffer[n++] = val; 
198                   parm->stop_seen = 1;
199                   break;
200                 }
201               if( (c = asctobin[(c2=c)]) == 255 ) 
202                 {
203                   log_error (_("invalid radix64 character %02x skipped\n"),
204                              c2);
205                   continue;
206                 }
207               switch (idx) 
208                 {
209                 case 0: 
210                   val = c << 2;
211                   break;
212                 case 1: 
213                   val |= (c>>4)&3;
214                   buffer[n++] = val;
215                   val = (c<<4)&0xf0;
216                   break;
217                 case 2: 
218                   val |= (c>>2)&15;
219                   buffer[n++] = val;
220                   val = (c<<6)&0xc0;
221                   break;
222                 case 3: 
223                   val |= c&0x3f;
224                   buffer[n++] = val;
225                   break;
226                 }
227               idx = (idx+1) % 4;
228             }
229           if (parm->readpos == parm->linelen)
230             parm->linelen = parm->readpos = 0;
231
232           parm->base64.idx = idx;
233           parm->base64.val = val;
234         }
235     }
236   else
237     { /* DER encoded */
238       while (n < count && parm->readpos < parm->linelen)
239           buffer[n++] = parm->line[parm->readpos++];
240       if (parm->readpos == parm->linelen)
241         parm->linelen = parm->readpos = 0;
242     }
243
244   *nread = n;
245   return 0;
246 }
247
248
249
250 static int
251 simple_reader_cb (void *cb_value, char *buffer, size_t count, size_t *nread)
252 {
253   struct reader_cb_parm_s *parm = cb_value;
254   size_t n;
255   int c = 0;
256
257   *nread = 0;
258   if (!buffer)
259     return -1; /* not supported */
260
261   for (n=0; n < count; n++)
262     {
263       c = getc (parm->fp);
264       if (c == EOF)
265         {
266           if ( ferror (parm->fp) )
267             return -1;
268           if (n)
269             break; /* return what we have before an EOF */
270           return -1;
271         }
272       *(byte *)buffer++ = c;
273     }
274
275   *nread = n;
276   return 0;
277 }
278
279
280
281
282 \f
283 /* Create a reader for the given file descriptor.  Depending on the
284    control information an input decoding is automagically choosen.
285    The function returns a Base64Context object which must be passed to
286    the gpgme_destroy_reader function.  The created KsbaReader object
287    is also returned, but the caller must not call the
288    ksba_reader_release function on. */
289 int
290 gpgsm_create_reader (Base64Context *ctx,
291                      CTRL ctrl, FILE *fp, KsbaReader *r_reader)
292 {
293   int rc;
294   KsbaReader r;
295
296   *r_reader = NULL;
297   *ctx = xtrycalloc (1, sizeof **ctx);
298   if (!*ctx)
299     return seterr (Out_Of_Core);
300
301   r = ksba_reader_new ();
302   if (!r)
303     {
304       xfree (*ctx); *ctx = NULL;
305       return seterr (Out_Of_Core);
306     }
307
308   (*ctx)->rparm.fp = fp;
309   if (ctrl->is_pem)
310     {
311       (*ctx)->rparm.assume_pem = 1;
312       (*ctx)->rparm.assume_base64 = 1;
313       rc = ksba_reader_set_cb (r, base64_reader_cb, &(*ctx)->rparm);
314     }
315   else if (ctrl->is_base64)
316     {
317       (*ctx)->rparm.assume_base64 = 1;
318       rc = ksba_reader_set_cb (r, base64_reader_cb, &(*ctx)->rparm);
319     }
320   else if (ctrl->autodetect_encoding)
321     {
322       (*ctx)->rparm.autodetect = 1;
323       rc = ksba_reader_set_cb (r, base64_reader_cb, &(*ctx)->rparm);
324     }
325   else
326       rc = ksba_reader_set_cb (r, simple_reader_cb, &(*ctx)->rparm);
327
328   if (rc)
329     {
330       ksba_reader_release (r);
331       xfree (*ctx); *ctx = NULL;
332       return map_ksba_err (rc);
333     }
334
335   *r_reader = r;
336   return 0;
337 }
338
339
340 void
341 gpgsm_destroy_reader (Base64Context ctx)
342 {
343   xfree (ctx);
344 }
345
346
347 \f
348 /* Create a writer for the given stream.  Depending on the control
349    information an output encoding is automagically choosen.  The
350    function returns a Base64Context object which must be passed to the
351    gpgme_destroy_writer function.  The created KsbaWriter object is
352    also returned, but the caller must not call the ksba_reader_release
353    function on. */
354 int
355 gpgsm_create_writer (Base64Context *ctx,
356                      CTRL ctrl, FILE *fp, KsbaWriter *r_writer)
357 {
358   int rc;
359   KsbaWriter w;
360
361   *r_writer = NULL;
362   *ctx = xtrycalloc (1, sizeof **ctx);
363   if (!*ctx)
364     return seterr (Out_Of_Core);
365
366   w = ksba_writer_new ();
367   if (!w)
368     {
369       xfree (*ctx); *ctx = NULL;
370       return seterr (Out_Of_Core);
371     }
372
373   if (ctrl->create_pem || ctrl->create_base64)
374     {
375       return seterr (Not_Implemented);
376     }
377   else
378     rc = ksba_writer_set_file (w, fp);
379
380   if (rc)
381     {
382       ksba_writer_release (w);
383       xfree (*ctx); *ctx = NULL;
384       return map_ksba_err (rc);
385     }
386
387   *r_writer = w;
388   return 0;
389 }
390
391
392 void
393 gpgsm_destroy_writer (Base64Context ctx)
394 {
395   xfree (ctx);
396 }
397
398
399
400
401