Updated from latest NewPG project
[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 #ifdef HAVE_DOSISH_SYSTEM
36   #define LF "\r\n"
37 #else
38   #define LF "\n"
39 #endif
40
41 /* data used by the reader callbacks */
42 struct reader_cb_parm_s {
43   FILE *fp;
44   unsigned char line[1024];
45   int linelen;
46   int readpos;
47   int have_lf;
48   unsigned long line_counter;
49
50   int autodetect; /* try to detect the input encoding */
51   int assume_pem; /* assume input encoding is PEM */
52   int assume_base64; /* assume input is base64 encoded */
53
54   int identified;
55   int is_pem;
56   int is_base64;
57   int stop_seen;
58   int might_be_smime;
59
60   struct {
61     int idx;
62     unsigned char val;
63     int stop_seen;
64   } base64;
65 };
66
67 /* data used by the writer callbacks */
68 struct writer_cb_parm_s {
69   FILE *fp;
70   const char *pem_name;
71   
72   int wrote_begin;
73   int did_finish;
74
75   struct {
76     int idx;
77     int quad_count;
78     unsigned char radbuf[4];
79   } base64;
80
81 };
82
83
84 /* context for this module's functions */
85 struct base64_context_s {
86   union {
87     struct reader_cb_parm_s rparm;
88     struct writer_cb_parm_s wparm;
89   } u;
90 };
91
92
93 /* The base-64 character list */
94 static unsigned char bintoasc[64] = 
95        "ABCDEFGHIJKLMNOPQRSTUVWXYZ" 
96        "abcdefghijklmnopqrstuvwxyz" 
97        "0123456789+/"; 
98 /* The reverse base-64 list */
99 static unsigned char asctobin[256] = {
100   0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
101   0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
102   0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
103   0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3e, 0xff, 0xff, 0xff, 0x3f, 
104   0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0xff, 0xff, 
105   0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 
106   0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 
107   0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0xff, 0xff, 0xff, 0xff, 0xff, 
108   0xff, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 
109   0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 
110   0x31, 0x32, 0x33, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
111   0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
112   0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
113   0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
114   0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
115   0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
116   0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
117   0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
118   0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
119   0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
120   0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
121   0xff, 0xff, 0xff, 0xff
122 };
123
124
125 static int
126 has_only_base64 (const unsigned char *line, int linelen)
127 {
128   if (linelen < 20)
129     return 0;
130   for (; linelen; line++, linelen--)
131     {
132       if (*line == '\n' || (linelen > 1 && *line == '\r' && line[1] == '\n'))
133           break;
134       if ( !strchr (bintoasc, *line) )
135         return 0;
136     }
137   return 1;  /* yes */
138 }
139
140 static int
141 is_empty_line (const unsigned char *line, int linelen)
142 {
143   if (linelen >= 2 && *line == '\r' && line[1] == '\n')
144     return 1;
145   if (linelen >= 1 && *line == '\n')
146     return 1;
147   return 0;
148 }
149
150
151 static int
152 base64_reader_cb (void *cb_value, char *buffer, size_t count, size_t *nread)
153 {
154   struct reader_cb_parm_s *parm = cb_value;
155   size_t n;
156   int c, c2;
157
158   *nread = 0;
159   if (!buffer)
160     return -1; /* not supported */
161
162  next:
163   if (!parm->linelen)
164     {
165       /* read an entire line or up to the size of the buffer */
166       parm->line_counter++;
167       parm->have_lf = 0;
168       for (n=0; n < DIM(parm->line);)
169         {
170           c = getc (parm->fp);
171           if (c == EOF)
172             {
173               if (ferror (parm->fp))
174                 return -1;
175               break; 
176             }
177           parm->line[n++] = c;
178           if (c == '\n')
179             {
180               parm->have_lf = 1;
181               /* Fixme: we need to skip overlong lines while detecting
182                  the dashed lines */
183               break;
184             }
185         }
186       parm->linelen = n;
187       if (!n)
188         return -1; /* eof */
189       parm->readpos = 0;
190     }
191
192   if (!parm->identified)
193     {
194       if (!parm->autodetect)
195         {
196           if (parm->assume_pem)
197             {
198               /* wait for the header line */
199               parm->linelen = parm->readpos = 0;
200               if (!parm->have_lf || strncmp (parm->line, "-----BEGIN ", 11)
201                   || !strncmp (parm->line+11, "PGP ", 4))
202                 goto next;
203               parm->is_pem = 1;
204             }
205           else if (parm->assume_base64)
206             parm->is_base64 = 1;
207         }
208       else if (parm->line_counter == 1 && !parm->have_lf)
209         {
210           /* first line too long - assume DER encoding */
211           parm->is_pem = 0;
212         }
213       else if (parm->line_counter == 1 && parm->linelen && *parm->line == 0x30)
214         {
215           /* the very first byte does pretty much look like a SEQUENCE tag*/
216           parm->is_pem = 0;
217         }
218       else if ( parm->have_lf && !strncmp (parm->line, "-----BEGIN ", 11)
219                 && strncmp (parm->line+11, "PGP ", 4) )
220         {
221           /* Fixme: we must only compare if the line really starts at
222              the beginning */
223           parm->is_pem = 1;
224           parm->linelen = parm->readpos = 0;
225         }
226       else if ( parm->have_lf && parm->line_counter == 1
227                 && parm->linelen >= 13
228                 && !ascii_memcasecmp (parm->line, "Content-Type:", 13))
229         { /* might be a S/MIME body */
230           parm->might_be_smime = 1;
231           parm->linelen = parm->readpos = 0;
232           goto next;
233         }
234       else if (parm->might_be_smime == 1
235                && is_empty_line (parm->line, parm->linelen))
236         {
237           parm->might_be_smime = 2;
238           parm->linelen = parm->readpos = 0;
239           goto next;
240         }
241       else if (parm->might_be_smime == 2)
242         {
243           parm->might_be_smime = 0;
244           if ( !has_only_base64 (parm->line, parm->linelen))
245             {
246               parm->linelen = parm->readpos = 0;
247               goto next;
248             }
249           parm->is_pem = 1;
250         }
251       else
252         {
253           parm->linelen = parm->readpos = 0;
254           goto next;
255         }
256       parm->identified = 1;
257       parm->base64.stop_seen = 0;
258       parm->base64.idx = 0;
259     }
260   
261
262   n = 0;
263   if (parm->is_pem || parm->is_base64)
264     {  
265       if (parm->is_pem && parm->have_lf
266           && !strncmp (parm->line, "-----END ", 9))
267         { 
268           parm->identified = 0;
269           parm->linelen = parm->readpos = 0;
270           /* let us return 0 */
271         }
272       else if (parm->stop_seen)
273         { /* skip the rest of the line */
274           parm->linelen = parm->readpos = 0;
275         }
276       else
277         {
278           int idx = parm->base64.idx;
279           unsigned char val = parm->base64.val;
280
281           while (n < count && parm->readpos < parm->linelen )
282             {
283               c = parm->line[parm->readpos++];
284               if (c == '\n' || c == ' ' || c == '\r' || c == '\t')
285                 continue;
286               if (c == '=')
287                 { /* pad character: stop */
288                   if (idx == 1)
289                     buffer[n++] = val; 
290                   parm->stop_seen = 1;
291                   break;
292                 }
293               if( (c = asctobin[(c2=c)]) == 255 ) 
294                 {
295                   log_error (_("invalid radix64 character %02x skipped\n"),
296                              c2);
297                   continue;
298                 }
299               switch (idx) 
300                 {
301                 case 0: 
302                   val = c << 2;
303                   break;
304                 case 1: 
305                   val |= (c>>4)&3;
306                   buffer[n++] = val;
307                   val = (c<<4)&0xf0;
308                   break;
309                 case 2: 
310                   val |= (c>>2)&15;
311                   buffer[n++] = val;
312                   val = (c<<6)&0xc0;
313                   break;
314                 case 3: 
315                   val |= c&0x3f;
316                   buffer[n++] = val;
317                   break;
318                 }
319               idx = (idx+1) % 4;
320             }
321           if (parm->readpos == parm->linelen)
322             parm->linelen = parm->readpos = 0;
323
324           parm->base64.idx = idx;
325           parm->base64.val = val;
326         }
327     }
328   else
329     { /* DER encoded */
330       while (n < count && parm->readpos < parm->linelen)
331           buffer[n++] = parm->line[parm->readpos++];
332       if (parm->readpos == parm->linelen)
333         parm->linelen = parm->readpos = 0;
334     }
335
336   *nread = n;
337   return 0;
338 }
339
340
341
342 static int
343 simple_reader_cb (void *cb_value, char *buffer, size_t count, size_t *nread)
344 {
345   struct reader_cb_parm_s *parm = cb_value;
346   size_t n;
347   int c = 0;
348
349   *nread = 0;
350   if (!buffer)
351     return -1; /* not supported */
352
353   for (n=0; n < count; n++)
354     {
355       c = getc (parm->fp);
356       if (c == EOF)
357         {
358           if ( ferror (parm->fp) )
359             return -1;
360           if (n)
361             break; /* return what we have before an EOF */
362           return -1;
363         }
364       *(byte *)buffer++ = c;
365     }
366
367   *nread = n;
368   return 0;
369 }
370
371
372
373 \f
374 static int
375 base64_writer_cb (void *cb_value, const void *buffer, size_t count)
376 {
377   struct writer_cb_parm_s *parm = cb_value;
378   unsigned char radbuf[4];
379   int i, c, idx, quad_count;
380   const unsigned char *p;
381   FILE *fp = parm->fp;
382
383   if (!count)
384     return 0;
385
386   if (!parm->wrote_begin)
387     {
388       if (parm->pem_name)
389         {
390           fputs ("-----BEGIN ", fp);
391           fputs (parm->pem_name, fp);
392           fputs ("-----\n", fp);
393         }
394       parm->wrote_begin = 1;
395       parm->base64.idx = 0;
396       parm->base64.quad_count = 0;
397     }
398
399   idx = parm->base64.idx;
400   quad_count = parm->base64.quad_count;
401   for (i=0; i < idx; i++)
402     radbuf[i] = parm->base64.radbuf[i];
403
404   for (p=buffer; count; p++, count--)
405     {
406       radbuf[idx++] = *p;
407       if (idx > 2)
408         {
409           idx = 0;
410           c = bintoasc[(*radbuf >> 2) & 077];
411           putc (c, fp);
412           c = bintoasc[(((*radbuf<<4)&060)|((radbuf[1] >> 4)&017))&077];
413           putc (c, fp);
414           c = bintoasc[(((radbuf[1]<<2)&074)|((radbuf[2]>>6)&03))&077];
415           putc (c, fp);
416           c = bintoasc[radbuf[2]&077];
417           putc (c, fp);
418           if (++quad_count >= (64/4)) 
419             {
420               fputs (LF, fp);
421               quad_count = 0;
422             }
423         }
424     }
425   for (i=0; i < idx; i++)
426     parm->base64.radbuf[i] = radbuf[i];
427   parm->base64.idx = idx;
428   parm->base64.quad_count = quad_count;
429
430   return ferror (fp) ? KSBA_Write_Error:0;
431 }
432
433 static int
434 base64_finish_write (struct writer_cb_parm_s *parm)
435 {
436   unsigned char radbuf[4];
437   int i, c, idx, quad_count;
438   FILE *fp = parm->fp;
439
440   if (!parm->wrote_begin)
441     return 0; /* nothing written */
442
443   /* flush the base64 encoding */
444   idx = parm->base64.idx;
445   quad_count = parm->base64.quad_count;
446   for (i=0; i < idx; i++)
447     radbuf[i] = parm->base64.radbuf[i];
448
449   if (idx)
450     {
451       c = bintoasc[(*radbuf>>2)&077];
452       putc (c, fp);
453       if (idx == 1)
454         {
455           c = bintoasc[((*radbuf << 4) & 060) & 077];
456           putc (c, fp);
457           putc ('=', fp);
458           putc ('=', fp);
459         }
460       else 
461         { 
462           c = bintoasc[(((*radbuf<<4)&060)|((radbuf[1]>>4)&017))&077];
463           putc (c, fp);
464           c = bintoasc[((radbuf[1] << 2) & 074) & 077];
465           putc (c, fp);
466           putc ('=', fp);
467
468         }
469       if (++quad_count >= (64/4)) 
470         {
471           fputs (LF, fp);
472           quad_count = 0;
473         }
474     }
475
476   if (quad_count)
477     fputs (LF, fp);
478
479   if (parm->pem_name)
480     {
481       fputs ("-----END ", fp);
482       fputs (parm->pem_name, fp);
483       fputs ("-----\n", fp);
484     }
485   return ferror (fp)? GNUPG_Write_Error : 0;
486 }
487
488
489
490 \f
491 /* Create a reader for the given file descriptor.  Depending on the
492    control information an input decoding is automagically choosen.
493    The function returns a Base64Context object which must be passed to
494    the gpgme_destroy_reader function.  The created KsbaReader object
495    is also returned, but the caller must not call the
496    ksba_reader_release function on. */
497 int
498 gpgsm_create_reader (Base64Context *ctx,
499                      CTRL ctrl, FILE *fp, KsbaReader *r_reader)
500 {
501   int rc;
502   KsbaReader r;
503
504   *r_reader = NULL;
505   *ctx = xtrycalloc (1, sizeof **ctx);
506   if (!*ctx)
507     return seterr (Out_Of_Core);
508
509   r = ksba_reader_new ();
510   if (!r)
511     {
512       xfree (*ctx); *ctx = NULL;
513       return seterr (Out_Of_Core);
514     }
515
516   (*ctx)->u.rparm.fp = fp;
517   if (ctrl->is_pem)
518     {
519       (*ctx)->u.rparm.assume_pem = 1;
520       (*ctx)->u.rparm.assume_base64 = 1;
521       rc = ksba_reader_set_cb (r, base64_reader_cb, &(*ctx)->u.rparm);
522     }
523   else if (ctrl->is_base64)
524     {
525       (*ctx)->u.rparm.assume_base64 = 1;
526       rc = ksba_reader_set_cb (r, base64_reader_cb, &(*ctx)->u.rparm);
527     }
528   else if (ctrl->autodetect_encoding)
529     {
530       (*ctx)->u.rparm.autodetect = 1;
531       rc = ksba_reader_set_cb (r, base64_reader_cb, &(*ctx)->u.rparm);
532     }
533   else
534       rc = ksba_reader_set_cb (r, simple_reader_cb, &(*ctx)->u.rparm);
535
536   if (rc)
537     {
538       ksba_reader_release (r);
539       xfree (*ctx); *ctx = NULL;
540       return map_ksba_err (rc);
541     }
542
543   *r_reader = r;
544   return 0;
545 }
546
547
548 void
549 gpgsm_destroy_reader (Base64Context ctx)
550 {
551   xfree (ctx);
552 }
553
554
555 \f
556 /* Create a writer for the given stream.  Depending on the control
557    information an output encoding is automagically choosen.  The
558    function returns a Base64Context object which must be passed to the
559    gpgme_destroy_writer function.  The created KsbaWriter object is
560    also returned, but the caller must not call the ksba_reader_release
561    function on. */
562 int
563 gpgsm_create_writer (Base64Context *ctx,
564                      CTRL ctrl, FILE *fp, KsbaWriter *r_writer)
565 {
566   int rc;
567   KsbaWriter w;
568
569   *r_writer = NULL;
570   *ctx = xtrycalloc (1, sizeof **ctx);
571   if (!*ctx)
572     return seterr (Out_Of_Core);
573
574   w = ksba_writer_new ();
575   if (!w)
576     {
577       xfree (*ctx); *ctx = NULL;
578       return seterr (Out_Of_Core);
579     }
580
581   if (ctrl->create_pem || ctrl->create_base64)
582     {
583       (*ctx)->u.wparm.fp = fp;
584       if (ctrl->create_pem)
585         (*ctx)->u.wparm.pem_name = ctrl->pem_name? ctrl->pem_name
586                                                  : "CMS OBJECT";
587       rc = ksba_writer_set_cb (w, base64_writer_cb, &(*ctx)->u.wparm);
588     }
589   else
590     rc = ksba_writer_set_file (w, fp);
591
592   if (rc)
593     {
594       ksba_writer_release (w);
595       xfree (*ctx); *ctx = NULL;
596       return map_ksba_err (rc);
597     }
598
599   *r_writer = w;
600   return 0;
601 }
602
603
604 int
605 gpgsm_finish_writer (Base64Context ctx)
606 {
607   struct writer_cb_parm_s *parm;
608   
609   if (!ctx)
610     return GNUPG_Invalid_Value;
611   parm = &ctx->u.wparm;
612   if (parm->did_finish)
613     return 0; /* already done */
614   parm->did_finish = 1;
615   if (!parm->fp)
616     return 0; /* callback was not used */
617   return base64_finish_write (parm);
618 }
619
620 void
621 gpgsm_destroy_writer (Base64Context ctx)
622 {
623   xfree (ctx);
624 }