root/openpgpsdk/trunk/src/armour.c

Revision 371 (checked in by ben, 7 years ago)

Make sure dmalloc happens last.

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