root/openpgpsdk/trunk/src/armour.c

Revision 411 (checked in by rachel, 7 years ago)

Get some doxygen documentation from this file

Line 
1 /** \file
2  * \brief Code for dealing with ASCII-armoured packets
3  */
4
5 #include <openpgpsdk/configure.h>
6 #include <openpgpsdk/armour.h>
7 #include <openpgpsdk/util.h>
8 #include <openpgpsdk/crypto.h>
9 #include <openpgpsdk/create.h>
10 #include <openpgpsdk/signature.h>
11 #include <openpgpsdk/version.h>
12
13 #include <string.h>
14 #include <assert.h>
15
16 #include <openpgpsdk/final.h>
17
18 #define CRC24_INIT 0xb704ceL
19 #define CRC24_POLY 0x1864cfbL
20
21 /**
22  * \struct
23  */
24 typedef struct
25     {
26     enum
27         {
28         OUTSIDE_BLOCK=0,
29         BASE64,
30         AT_TRAILER_NAME,
31         } state;
32     ops_parse_info_t *parse_info;
33     ops_boolean_t seen_nl:1;
34     ops_boolean_t prev_nl:1;
35     ops_boolean_t allow_headers_without_gap:1; /*!< allow headers in
36                                                   armoured data that
37                                                   are not separated
38                                                   from the data by a
39                                                   blank line */
40     ops_boolean_t allow_no_gap:1; /*!< allow no blank line at the
41                                        start of armoured data */
42     ops_boolean_t allow_trailing_whitespace:1; /*!< allow armoured
43                                                  stuff to have
44                                                  trailing whitespace
45                                                  where we wouldn't
46                                                  strictly expect it */
47
48     // base64 stuff
49     unsigned buffered;
50     unsigned char buffer[3];
51     ops_boolean_t eof64;
52     unsigned long checksum;
53     unsigned long read_checksum;
54     // unarmoured text blocks
55     unsigned char unarmoured[8192];
56     size_t num_unarmoured;
57     // pushed back data (stored backwards)
58     unsigned char *pushed_back;
59     unsigned npushed_back;
60     // armoured block headers
61     ops_headers_t headers;
62     } dearmour_arg_t;
63
64 // FIXME: move these to a common header
65 #define CB(cbinfo,t,pc) do { (pc)->tag=(t); if(ops_parse_cb((pc),(cbinfo)) == OPS_RELEASE_MEMORY) ops_parser_content_free(pc); } while(0)
66 #define ERR(cbinfo,err) do { content.content.error.error=err; content.tag=OPS_PARSER_ERROR; ops_parse_cb(&content,(cbinfo)); return -1; } while(0)
67
68 static void push_back(dearmour_arg_t *arg,const unsigned char *buf,
69                       unsigned length)
70     {
71     unsigned n;
72
73     assert(!arg->pushed_back);
74     arg->pushed_back=malloc(length);
75     for(n=0 ; n < length ; ++n)
76         arg->pushed_back[n]=buf[length-n-1];
77     arg->npushed_back=length;
78     }
79    
80 static int read_char(dearmour_arg_t *arg,ops_error_t **errors,
81                      ops_reader_info_t *rinfo,
82                      ops_parse_cb_info_t *cbinfo,
83                      ops_boolean_t skip)
84     {
85     unsigned char c[1];
86
87     do
88         {
89         if(arg->npushed_back)
90             {
91             c[0]=arg->pushed_back[--arg->npushed_back];
92             if(!arg->npushed_back)
93                 {
94                 free(arg->pushed_back);
95                 arg->pushed_back=NULL;
96                 }
97             }
98         /* XXX: should ops_stacked_read exist? Shouldn't this be a limited_read? */
99         else if(ops_stacked_read(c,1,errors,rinfo,cbinfo) != 1)
100             return -1;
101         }
102     while(skip && c[0] == '\r');
103
104     arg->prev_nl=arg->seen_nl;
105     arg->seen_nl=c[0] == '\n';
106
107     return c[0];
108     }
109
110 static int eat_whitespace(int first,
111                           dearmour_arg_t *arg,ops_error_t **errors,
112                           ops_reader_info_t *rinfo,
113                           ops_parse_cb_info_t *cbinfo,
114                           ops_boolean_t skip)
115     {
116     int c=first;
117
118     while(c == ' ' || c == '\t')
119         c=read_char(arg,errors,rinfo,cbinfo,skip);
120
121     return c;
122     }
123
124 static int read_and_eat_whitespace(dearmour_arg_t *arg,
125                                    ops_error_t **errors,
126                                    ops_reader_info_t *rinfo,
127                                    ops_parse_cb_info_t *cbinfo,
128                                    ops_boolean_t skip)
129     {
130     int c;
131
132     do
133         c=read_char(arg,errors,rinfo,cbinfo,skip);
134     while(c == ' ' || c == '\t');
135
136     return c;
137     }
138
139 static void flush(dearmour_arg_t *arg,ops_parse_cb_info_t *cbinfo)
140     {
141     ops_parser_content_t content;
142
143     if(arg->num_unarmoured == 0)
144         return;
145
146     content.content.unarmoured_text.data=arg->unarmoured;
147     content.content.unarmoured_text.length=arg->num_unarmoured;
148     CB(cbinfo,OPS_PTAG_CT_UNARMOURED_TEXT,&content);
149     arg->num_unarmoured=0;
150     }
151
152 static int unarmoured_read_char(dearmour_arg_t *arg,ops_error_t **errors,
153                                 ops_reader_info_t *rinfo,
154                                 ops_parse_cb_info_t *cbinfo,
155                                 ops_boolean_t skip)
156     {
157     int c;
158
159     do
160         {
161         c=read_char(arg,errors,rinfo,cbinfo,ops_false);
162         if(c < 0)
163             return c;
164         arg->unarmoured[arg->num_unarmoured++]=c;
165         if(arg->num_unarmoured == sizeof arg->unarmoured)
166             flush(arg,cbinfo);
167         }
168     while(skip && c == '\r');
169
170     return c;
171     }
172
173 /**
174  * \param ops_headers_t
175  * \param key
176  *
177  * \return header value if found, otherwise NULL
178  */
179 const char *ops_find_header(ops_headers_t *headers,const char *key)
180     {
181     unsigned n;
182
183     for(n=0 ; n < headers->nheaders ; ++n)
184         if(!strcmp(headers->headers[n].key,key))
185             return headers->headers[n].value;
186     return NULL;
187     }
188
189 /**
190  * \param dest
191  * \param src
192  */
193 void ops_dup_headers(ops_headers_t *dest,const ops_headers_t *src)
194     {
195     unsigned n;
196
197     dest->headers=malloc(src->nheaders*sizeof *dest->headers);
198     dest->nheaders=src->nheaders;
199
200     for(n=0 ; n < src->nheaders ; ++n)
201         {
202         dest->headers[n].key=strdup(src->headers[n].key);
203         dest->headers[n].value=strdup(src->headers[n].value);
204         }
205     }
206
207 /* Note that this skips CRs so implementations always see just
208    straight LFs as line terminators */
209 static int process_dash_escaped(dearmour_arg_t *arg,ops_error_t **errors,
210                                 ops_reader_info_t *rinfo,
211                                 ops_parse_cb_info_t *cbinfo)
212     {
213     ops_parser_content_t content;
214     ops_parser_content_t content2;
215     ops_signed_cleartext_body_t *body=&content.content.signed_cleartext_body;
216     ops_signed_cleartext_trailer_t *trailer
217         =&content2.content.signed_cleartext_trailer;
218     const char *hashstr;
219     ops_hash_t *hash;
220     int total;
221
222     hash=malloc(sizeof *hash);
223     hashstr=ops_find_header(&arg->headers,"Hash");
224     if(hashstr)
225         {
226         ops_hash_algorithm_t alg;
227
228         alg=ops_hash_algorithm_from_text(hashstr);
229         if(alg == OPS_HASH_UNKNOWN)
230             {
231             free(hash);
232             ERR(cbinfo,"Unknown hash algorithm");
233             }
234         ops_hash_any(hash,alg);
235         }
236     else
237         ops_hash_md5(hash);
238
239     hash->init(hash);
240
241     body->length=0;
242     total=0;
243     for( ; ; )
244         {
245         int c;
246         unsigned count;
247
248         if((c=read_char(arg,errors,rinfo,cbinfo,ops_true)) < 0)
249             return -1;
250         if(arg->prev_nl && c == '-')
251             {
252             if((c=read_char(arg,errors,rinfo,cbinfo,ops_false)) < 0)
253                 return -1;
254             if(c != ' ')
255                 {
256                 /* then this had better be a trailer! */
257                 if(c != '-')
258                     ERR(cbinfo,"Bad dash-escaping");
259                 for(count=2 ; count < 5 ; ++count)
260                     {
261                     if((c=read_char(arg,errors,rinfo,cbinfo,ops_false)) < 0)
262                         return -1;
263                     if(c != '-')
264                         ERR(cbinfo,"Bad dash-escaping (2)");
265                     }
266                 arg->state=AT_TRAILER_NAME;
267                 break;
268                 }
269             /* otherwise we read the next character */
270             if((c=read_char(arg,errors,rinfo,cbinfo,ops_false)) < 0)
271                 return -1;
272             }
273         if(c == '\n' && body->length)
274             {
275             assert(memchr(body->data+1,'\n',body->length-1) == NULL);
276             if(body->data[0] == '\n')
277                 hash->add(hash,(unsigned char *)"\r",1);
278             hash->add(hash,body->data,body->length);
279             CB(cbinfo,OPS_PTAG_CT_SIGNED_CLEARTEXT_BODY,&content);
280             body->length=0;
281             }
282                
283         body->data[body->length++]=c;
284         ++total;
285         if(body->length == sizeof body->data)
286             {
287             CB(cbinfo,OPS_PTAG_CT_SIGNED_CLEARTEXT_BODY,&content);
288             body->length=0;
289             }
290         }
291
292     assert(body->data[0] == '\n');
293     assert(body->length == 1);
294     /* don't send that one character, because its part of the trailer. */
295
296     trailer->hash=hash;
297     CB(cbinfo,OPS_PTAG_CT_SIGNED_CLEARTEXT_TRAILER,&content2);
298
299     return total;
300     }
301
302 static void add_header(dearmour_arg_t *arg,const char *key,const char
303                        *value)
304     {
305     arg->headers.headers=realloc(arg->headers.headers,
306                                  (arg->headers.nheaders+1)
307                                  *sizeof *arg->headers.headers);
308     arg->headers.headers[arg->headers.nheaders].key=strdup(key);
309     arg->headers.headers[arg->headers.nheaders].value=strdup(value);
310     ++arg->headers.nheaders;
311     }
312
313 static int parse_headers(dearmour_arg_t *arg,ops_error_t **errors,
314                          ops_reader_info_t *rinfo,ops_parse_cb_info_t *cbinfo)
315     {
316     char *buf;
317     unsigned nbuf;
318     unsigned size;
319     ops_boolean_t first=ops_true;
320     ops_parser_content_t content;
321
322     buf=NULL;
323     nbuf=size=0;
324
325     for( ;  ; )
326         {
327         int c;
328
329         if((c=read_char(arg,errors,rinfo,cbinfo,ops_true)) < 0)
330             return -1;
331
332         if(c == '\n')
333             {
334             char *s;
335
336             if(nbuf == 0)
337                 break;
338
339             assert(nbuf < size);
340             buf[nbuf]='\0';
341
342             s=strchr(buf,':');
343             if(!s)
344                 if(!first && !arg->allow_headers_without_gap)
345                     // then we have seriously malformed armour
346                     ERR(cbinfo,"No colon in armour header");
347                 else
348                     {
349                     if(first &&
350                        !(arg->allow_headers_without_gap || arg->allow_no_gap))
351                         ERR(cbinfo,"No colon in armour header (2)");
352                     // then we have a nasty armoured block with no
353                     // headers, not even a blank line.
354                     buf[nbuf]='\n';
355                     push_back(arg,(unsigned char *)buf,nbuf+1);
356                     break;
357                     }
358             else
359                 {
360                 *s='\0';
361                 if(s[1] != ' ')
362                     ERR(cbinfo,"No space in armour header");
363                 add_header(arg,buf,s+2);
364                 nbuf=0;
365                 }
366             first=ops_false;
367             }
368         else
369             {
370             if(size <= nbuf+1)
371                 {
372                 size+=size+80;
373                 buf=realloc(buf,size);
374                 }
375             buf[nbuf++]=c;
376             }
377         }
378
379     free(buf);
380
381     return 1;
382     }
383
384 static int read4(dearmour_arg_t *arg,ops_error_t **errors,
385                  ops_reader_info_t *rinfo,ops_parse_cb_info_t *cbinfo,
386                  int *pc,unsigned *pn,unsigned long *pl)
387     {
388     int n,c;
389     unsigned long l=0;
390
391     for(n=0 ; n < 4 ; ++n)
392         {
393         c=read_char(arg,errors,rinfo,cbinfo,ops_true);
394         if(c < 0)
395             {
396             arg->eof64=ops_true;
397             return -1;
398             }
399         if(c == '-')
400             break;
401         if(c == '=')
402             break;
403         l <<= 6;
404         if(c >= 'A' && c <= 'Z')
405             l+=c-'A';
406         else if(c >= 'a' && c <= 'z')
407             l+=c-'a'+26;
408         else if(c >= '0' && c <= '9')
409             l+=c-'0'+52;
410         else if(c == '+')
411             l+=62;
412         else if(c == '/')
413             l+=63;
414         else
415             {
416             --n;
417             l >>= 6;
418             }
419         }
420
421     *pc=c;
422     *pn=n;
423     *pl=l;
424
425     return 4;
426     }
427
428 static unsigned crc24(unsigned checksum,unsigned char c)
429     {
430     unsigned i;
431
432     checksum ^= c << 16;
433     for(i=0 ; i < 8 ; i++)
434         {
435         checksum <<= 1;
436         if(checksum & 0x1000000)
437             checksum ^= CRC24_POLY;
438         }
439     return checksum&0xffffffL;
440     }
441
442 static int decode64(dearmour_arg_t *arg,ops_error_t **errors,
443                     ops_reader_info_t *rinfo,ops_parse_cb_info_t *cbinfo)
444     {
445     unsigned n;
446     int n2;
447     unsigned long l;
448     ops_parser_content_t content;
449     int c;
450     int ret;
451
452     assert(arg->buffered == 0);
453
454     ret=read4(arg,errors,rinfo,cbinfo,&c,&n,&l);
455     if(ret < 0)
456         ERR(cbinfo,"Badly formed base64");
457
458     if(n == 3)
459         {
460         assert(c == '=');
461         arg->buffered=2;
462         arg->eof64=ops_true;
463         l >>= 2;
464         }
465     else if(n == 2)
466         {
467         assert(c == '=');
468         arg->buffered=1;
469         arg->eof64=ops_true;
470         l >>= 4;
471         c=read_char(arg,errors,rinfo,cbinfo,ops_false);
472         if(c != '=')
473             ERR(cbinfo,"Badly terminated base64");
474         }
475     else if(n == 0)
476         {
477         assert(arg->prev_nl && c == '=');
478         arg->buffered=0;
479         }
480     else
481         {
482         assert(n == 4);
483         arg->buffered=3;
484         assert(c != '-' && c != '=');
485         }
486
487     if(arg->buffered < 3 && arg->buffered > 0)
488         {
489         // then we saw padding
490         assert(c == '=');
491         c=read_and_eat_whitespace(arg,errors,rinfo,cbinfo,ops_true);
492         if(c != '\n')
493             ERR(cbinfo,"No newline at base64 end");
494         c=read_char(arg,errors,rinfo,cbinfo,ops_false);
495         if(c != '=')
496             ERR(cbinfo,"No checksum at base64 end");
497         }
498
499     if(c == '=')
500         {
501         // now we are at the checksum
502         ret=read4(arg,errors,rinfo,cbinfo,&c,&n,&arg->read_checksum);
503         if(ret < 0 || n != 4)
504             ERR(cbinfo,"Error in checksum");
505         c=read_char(arg,errors,rinfo,cbinfo,ops_true);
506         if(arg->allow_trailing_whitespace)
507             c=eat_whitespace(c,arg,errors,rinfo,cbinfo,ops_true);
508         if(c != '\n')
509             ERR(cbinfo,"Badly terminated checksum");
510         c=read_char(arg,errors,rinfo,cbinfo,ops_false);
511         if(c != '-')
512             ERR(cbinfo,"Bad base64 trailer (2)");
513         }
514
515     if(c == '-')
516         {
517         for(n=0 ; n < 4 ; ++n)
518             if(read_char(arg,errors,rinfo,cbinfo,ops_false) != '-')
519                 ERR(cbinfo,"Bad base64 trailer");
520         arg->eof64=ops_true;
521         }
522     else
523         assert(arg->buffered);
524
525     for(n=0 ; n < arg->buffered ; ++n)
526         {
527         arg->buffer[n]=l;
528         l >>= 8;
529         }
530
531     for(n2=arg->buffered-1 ; n2 >= 0 ; --n2)
532         arg->checksum=crc24(arg->checksum,arg->buffer[n2]);
533
534     if(arg->eof64 && arg->read_checksum != arg->checksum)
535         ERR(cbinfo,"Checksum mismatch");
536
537     return 1;
538     }
539
540 static void base64(dearmour_arg_t *arg)
541     {
542     arg->state=BASE64;
543     arg->checksum=CRC24_INIT;
544     arg->eof64=ops_false;
545     arg->buffered=0;
546     }
547
548 // This reader is rather strange in that it can generate callbacks for
549 // content - this is because plaintext is not encapsulated in PGP
550 // packets... it also calls back for the text between the blocks.
551
552 static int armoured_data_reader(void *dest_,size_t length,ops_error_t **errors,
553                                 ops_reader_info_t *rinfo,
554                                 ops_parse_cb_info_t *cbinfo)
555      {
556      dearmour_arg_t *arg=ops_reader_get_arg(rinfo);
557      ops_parser_content_t content;
558      int ret;
559      ops_boolean_t first;
560      unsigned char *dest=dest_;
561      int saved=length;
562
563      if(arg->eof64 && !arg->buffered)
564          assert(arg->state == OUTSIDE_BLOCK || arg->state == AT_TRAILER_NAME);
565
566      while(length > 0)
567          {
568          unsigned count;
569          unsigned n;
570          char buf[1024];
571          int c;
572
573          flush(arg,cbinfo);
574          switch(arg->state)
575              {
576          case OUTSIDE_BLOCK:
577              /* This code returns EOF rather than EARLY_EOF because if
578                 we don't see a header line at all, then it is just an
579                 EOF (and not a BLOCK_END) */
580              while(!arg->seen_nl)
581                  if((c=unarmoured_read_char(arg,errors,rinfo,cbinfo,ops_true)) < 0)
582                      return 0;
583
584              /* flush at this point so we definitely have room for the
585                 header, and so we can easily erase it from the buffer */
586              flush(arg,cbinfo);
587              /* Find and consume the 5 leading '-' */
588              for(count=0 ; count < 5 ; ++count)
589                  {
590                  if((c=unarmoured_read_char(arg,errors,rinfo,cbinfo,ops_false)) < 0)
591                      return 0;
592                  if(c != '-')
593                      goto reloop;
594                  }
595
596              /* Now find the block type */
597              for(n=0 ; n < sizeof buf-1 ; )
598                  {
599                  if((c=unarmoured_read_char(arg,errors,rinfo,cbinfo,ops_false)) < 0)
600                      return 0;
601                  if(c == '-')
602                      goto got_minus;
603                  buf[n++]=c;
604                  }
605              /* then I guess this wasn't a proper header */
606              break;
607
608          got_minus:
609              buf[n]='\0';
610
611              /* Consume trailing '-' */
612              for(count=1 ; count < 5 ; ++count)
613                  {
614                  if((c=unarmoured_read_char(arg,errors,rinfo,cbinfo,ops_false)) < 0)
615                      return 0;
616                  if(c != '-')
617                      /* wasn't a header after all */
618                      goto reloop;
619                  }
620
621              /* Consume final NL */
622              if((c=unarmoured_read_char(arg,errors,rinfo,cbinfo,ops_true)) < 0)
623                  return 0;
624              if(arg->allow_trailing_whitespace)
625                  if((c=eat_whitespace(c,arg,errors,rinfo,cbinfo,
626                                       ops_true)) < 0)
627                     return 0;
628              if(c != '\n')
629                  /* wasn't a header line after all */
630                  break;
631
632              /* Now we've seen the header, scrub it from the buffer */
633              arg->num_unarmoured=0;
634
635              /* But now we've seen a header line, then errors are
636                 EARLY_EOF */
637              if((ret=parse_headers(arg,errors,rinfo,cbinfo)) <= 0)
638                  return -1;
639
640              if(!strcmp(buf,"BEGIN PGP SIGNED MESSAGE"))
641                  {
642                  ops_dup_headers(&content.content.signed_cleartext_header.headers,&arg->headers);
643                  CB(cbinfo,OPS_PTAG_CT_SIGNED_CLEARTEXT_HEADER,&content);
644
645                  ret=process_dash_escaped(arg,errors,rinfo,cbinfo);
646                  if(ret <= 0)
647                      return ret;
648                  }
649              else
650                  {
651                  content.content.armour_header.type=buf;
652                  content.content.armour_header.headers=arg->headers;
653                  memset(&arg->headers,'\0',sizeof arg->headers);
654                  CB(cbinfo,OPS_PTAG_CT_ARMOUR_HEADER,&content);
655                  base64(arg);
656                  }
657              break;
658
659          case BASE64:
660              first=ops_true;
661              while(length > 0)
662                  {
663                  if(!arg->buffered)
664                      {
665                      if(!arg->eof64)
666                          {
667                          ret=decode64(arg,errors,rinfo,cbinfo);
668                          if(ret <= 0)
669                              return ret;
670                          }
671                      if(!arg->buffered)
672                          {
673                          assert(arg->eof64);
674                          if(first)
675                              {
676                              arg->state=AT_TRAILER_NAME;
677                              goto reloop;
678                              }
679                          return -1;
680                          }
681                      }
682
683                  assert(arg->buffered);
684                  *dest=arg->buffer[--arg->buffered];
685                  ++dest;
686                  --length;
687                  first=ops_false;
688                  }
689              if(arg->eof64 && !arg->buffered)
690                  arg->state=AT_TRAILER_NAME;
691              break;
692
693          case AT_TRAILER_NAME:
694              for(n=0 ; n < sizeof buf-1 ; )
695                  {
696                  if((c=read_char(arg,errors,rinfo,cbinfo,ops_false)) < 0)
697                      return -1;
698                  if(c == '-')
699                      goto got_minus2;
700                  buf[n++]=c;
701                  }
702              /* then I guess this wasn't a proper trailer */
703              ERR(cbinfo,"Bad ASCII armour trailer");
704              break;
705
706          got_minus2:
707              buf[n]='\0';
708
709              /* Consume trailing '-' */
710              for(count=1 ; count < 5 ; ++count)
711                  {
712                  if((c=read_char(arg,errors,rinfo,cbinfo,ops_false)) < 0)
713                      return -1;
714                  if(c != '-')
715                      /* wasn't a trailer after all */
716                      ERR(cbinfo,"Bad ASCII armour trailer (2)");
717                  }
718
719              /* Consume final NL */
720              if((c=read_char(arg,errors,rinfo,cbinfo,ops_true)) < 0)
721                  return -1;
722              if(arg->allow_trailing_whitespace)
723                  if((c=eat_whitespace(c,arg,errors,rinfo,cbinfo,
724                                       ops_true)) < 0)
725                     return 0;
726              if(c != '\n')
727                  /* wasn't a trailer line after all */
728                  ERR(cbinfo,"Bad ASCII armour trailer (3)");
729
730              if(!strncmp(buf,"BEGIN ",6))
731                  {
732                  if((ret=parse_headers(arg,errors,rinfo,cbinfo)) <= 0)
733                      return ret;
734                  content.content.armour_header.type=buf;
735                  content.content.armour_header.headers=arg->headers;
736                  memset(&arg->headers,'\0',sizeof arg->headers);
737                  CB(cbinfo,OPS_PTAG_CT_ARMOUR_HEADER,&content);
738                  base64(arg);
739                  }
740              else
741                  {
742                  content.content.armour_trailer.type=buf;
743                  CB(cbinfo,OPS_PTAG_CT_ARMOUR_TRAILER,&content);
744                 arg->state=OUTSIDE_BLOCK;
745                 }
746             break;
747             }
748     reloop:
749         continue;
750         }
751
752     return saved;
753     }
754
755 /**
756  * \param parse_info
757  * \param without_gap
758  * \param no_gap
759  * \param trailing_whitespace
760  */
761 void ops_reader_push_dearmour(ops_parse_info_t *parse_info,
762                               ops_boolean_t without_gap,
763                               ops_boolean_t no_gap,
764                               ops_boolean_t trailing_whitespace)
765     {
766     dearmour_arg_t *arg;
767
768     arg=ops_mallocz(sizeof *arg);
769     arg->seen_nl=ops_true;
770     arg->allow_headers_without_gap=without_gap;
771     arg->allow_no_gap=no_gap;
772     arg->allow_trailing_whitespace=trailing_whitespace;
773
774     ops_reader_push(parse_info,armoured_data_reader,arg);
775     }
776
777 /**
778  * \param parse_info
779  */
780 void ops_reader_pop_dearmour(ops_parse_info_t *parse_info)
781     {
782     dearmour_arg_t *arg=ops_reader_get_arg(ops_parse_get_rinfo(parse_info));
783
784     free(arg);
785     }
786
787 /**
788  * \struct
789  */
790 typedef struct
791     {
792     ops_boolean_t seen_nl:1;
793     ops_boolean_t seen_cr:1;
794     ops_create_signature_t *sig;
795     ops_memory_t *trailing;
796     } dash_escaped_arg_t;
797
798 static ops_boolean_t dash_escaped_writer(const unsigned char *src,
799                                          unsigned length,
800                                          ops_error_t **errors,
801                                          ops_writer_info_t *winfo)
802     {
803     dash_escaped_arg_t *arg=ops_writer_get_arg(winfo);
804     unsigned n;
805
806     // XXX: make this efficient
807     for(n=0 ; n < length ; ++n)
808         {
809         unsigned l;
810
811         if(arg->seen_nl)
812             {
813             if(src[n] == '-' && !ops_stacked_write("- ",2,errors,winfo))
814                 return ops_false;
815             arg->seen_nl=ops_false;
816             }
817
818         arg->seen_nl=src[n] == '\n';
819
820         if(arg->seen_nl && !arg->seen_cr)
821             {
822             if(!ops_stacked_write("\r",1,errors,winfo))
823                 return ops_false;
824             ops_signature_add_data(arg->sig,"\r",1);
825             }
826
827         arg->seen_cr=src[n] == '\r';
828
829         if(!ops_stacked_write(&src[n],1,errors,winfo))
830             return ops_false;
831
832         /* trailing whitespace isn't included in the signature */
833         if(src[n] == ' ' || src[n] == '\t')
834             ops_memory_add(arg->trailing,&src[n],1);
835         else
836             {
837             if((l=ops_memory_get_length(arg->trailing)))
838                 {
839                 if(!arg->seen_nl && !arg->seen_cr)
840                     ops_signature_add_data(arg->sig,
841                                            ops_memory_get_data(arg->trailing),
842                                            l);
843                 ops_memory_clear(arg->trailing);
844                 }
845             ops_signature_add_data(arg->sig,&src[n],1);
846             }
847         }
848
849     return ops_true;
850     }
851
852 /**
853  * \param winfo
854  */
855 void dash_escaped_destroyer(ops_writer_info_t *winfo)
856     {
857     dash_escaped_arg_t *arg=ops_writer_get_arg(winfo);
858
859     ops_memory_free(arg->trailing);
860     free(arg);
861     }
862
863 // XXX: should return errors.
864 /**
865  * \param info
866  * \param sig
867  * \todo should return errors
868  */
869 void ops_writer_push_dash_escaped(ops_create_info_t *info,
870                                   ops_create_signature_t *sig)
871     {
872     static char header[]="-----BEGIN PGP SIGNED MESSAGE-----\r\nHash: ";
873     const char *hash=ops_text_from_hash(ops_signature_get_hash(sig));
874     dash_escaped_arg_t *arg=ops_mallocz(sizeof *arg);
875
876     ops_write(header,sizeof header-1,info);
877     ops_write(hash,strlen(hash),info);
878     ops_write("\r\n\r\n",4,info);
879     arg->seen_nl=ops_true;
880     arg->sig=sig;
881     arg->trailing=ops_memory_new();
882     ops_writer_push(info,dash_escaped_writer,NULL,dash_escaped_destroyer,arg);
883     }
884
885 /**
886  * \struct
887  */
888 typedef struct
889     {
890     unsigned pos;
891     unsigned char t;
892     unsigned checksum;
893     } base64_arg_t;
894
895 static char b64map[]="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
896 "0123456789+/";
897
898 static ops_boolean_t base64_writer(const unsigned char *src,
899                                    unsigned length,ops_error_t **errors,
900                                    ops_writer_info_t *winfo)
901     {
902     base64_arg_t *arg=ops_writer_get_arg(winfo);
903     unsigned n;
904
905     for(n=0 ; n < length ; )
906         {
907         arg->checksum=crc24(arg->checksum,src[n]);
908         if(arg->pos == 0)
909             {
910             /* XXXXXX00 00000000 00000000 */
911             if(!ops_stacked_write(&b64map[src[n] >> 2],1,errors,winfo))
912                 return ops_false;
913
914             /* 000000XX xxxx0000 00000000 */
915             arg->t=(src[n++]&3) << 4;
916             arg->pos=1;
917             }
918         else if(arg->pos == 1)
919             {
920             /* 000000xx XXXX0000 00000000 */
921             arg->t+=src[n] >> 4;
922             if(!ops_stacked_write(&b64map[arg->t],1,errors,winfo))
923                 return ops_false;
924
925             /* 00000000 0000XXXX xx000000 */
926             arg->t=(src[n++]&0xf) << 2;
927             arg->pos=2;
928             }
929         else if(arg->pos == 2)
930             {
931             /* 00000000 0000xxxx XX000000 */
932             arg->t+=src[n] >> 6;
933             if(!ops_stacked_write(&b64map[arg->t],1,errors,winfo))
934                 return ops_false;
935
936             /* 00000000 00000000 00XXXXXX */
937             if(!ops_stacked_write(&b64map[src[n++]&0x3f],1,errors,winfo))
938                 return ops_false;
939
940             arg->pos=0;
941             }
942         }
943
944     return ops_true;
945     }
946
947 static ops_boolean_t signature_finaliser(ops_error_t **errors,
948                                          ops_writer_info_t *winfo)
949     {
950     base64_arg_t *arg=ops_writer_get_arg(winfo);
951     static char trailer[]="\r\n-----END PGP SIGNATURE-----\r\n";
952     unsigned char c[3];
953
954     if(arg->pos)
955         {
956         if(!ops_stacked_write(&b64map[arg->t],1,errors,winfo))
957             return ops_false;
958         if(arg->pos == 1 && !ops_stacked_write("==",2,errors,winfo))
959             return ops_false;
960         if(arg->pos == 2 && !ops_stacked_write("=",1,errors,winfo))
961             return ops_false;
962         }
963     /* Ready for the checksum */
964     if(!ops_stacked_write("\r\n=",3,errors,winfo))
965         return ops_false;
966
967     arg->pos=0; /* get ready to write the checksum */
968
969     c[0]=arg->checksum >> 16;
970     c[1]=arg->checksum >> 8;
971     c[2]=arg->checksum;
972     /* push the checksum through our own writer */
973     if(!base64_writer(c,3,errors,winfo))
974         return ops_false;
975
976     return ops_stacked_write(trailer,sizeof trailer-1,errors,winfo);
977     }
978
979 /**
980  * \struct
981  */
982 typedef struct
983     {
984     unsigned pos;
985     } linebreak_arg_t;
986
987 #define BREAKPOS        76
988
989 static ops_boolean_t linebreak_writer(const unsigned char *src,
990                                          unsigned length,
991                                          ops_error_t **errors,
992                                          ops_writer_info_t *winfo)
993     {
994     linebreak_arg_t *arg=ops_writer_get_arg(winfo);
995     unsigned n;
996
997     for(n=0 ; n < length ; ++n,++arg->pos)
998         {
999         if(src[n] == '\r' || src[n] == '\n')
1000             arg->pos=0;
1001
1002         if(arg->pos == BREAKPOS)
1003             {
1004             if(!ops_stacked_write("\r\n",2,errors,winfo))
1005                 return ops_false;
1006             arg->pos=0;
1007             }
1008         if(!ops_stacked_write(&src[n],1,errors,winfo))
1009             return ops_false;
1010         }
1011
1012     return ops_true;
1013     }
1014
1015 // XXX: should return errors.
1016 /**
1017  * \param info
1018  */
1019 void ops_writer_switch_to_signature(ops_create_info_t *info)
1020     {
1021     static char header[]="\r\n-----BEGIN PGP SIGNATURE-----\r\nVersion: "
1022         OPS_VERSION_STRING "\r\n\r\n";
1023     base64_arg_t *base64;
1024
1025     ops_writer_pop(info);
1026     ops_write(header,sizeof header-1,info);
1027
1028     ops_writer_push(info,linebreak_writer,NULL,ops_writer_generic_destroyer,
1029                     ops_mallocz(sizeof(linebreak_arg_t)));
1030
1031     base64=ops_mallocz(sizeof *base64);
1032     base64->checksum=CRC24_INIT;
1033     ops_writer_push(info,base64_writer,signature_finaliser,
1034                     ops_writer_generic_destroyer,base64);
1035     }
Note: See TracBrowser for help on using the browser.