]> git.sommitrealweird.co.uk Git - onak.git/blob - armor.c
autoconf onak-mail.pl config
[onak.git] / armor.c
1 /*
2  * armor.c - Routines to (de)armor OpenPGP packet streams.
3  *
4  * Jonathan McDowell <noodles@earth.li>
5  *
6  * Copyright 2002 Project Purple
7  */
8
9 #include <stdlib.h>
10
11 #include "armor.h"
12 #include "keystructs.h"
13 #include "log.h"
14 #include "onak-conf.h"
15 #include "parsekey.h"
16
17 #define ARMOR_WIDTH 64
18
19 #define CRC24_INIT 0xb704ceL
20 #define CRC24_POLY 0x1864cfbL
21
22 /**
23  *
24  */
25 static unsigned char encode64(unsigned char c) {
26         if (c <= 25) {
27                 c += 'A';
28         } else if (c >= 26 && c <= 51) {
29                 c += 'a' - 26;
30         } else if (c >= 52 && c <= 61) {
31                 c += '0' - 52;
32         } else if (c == 62) {
33                 c = '+';
34         } else if (c == 63) {
35                 c = '/';
36         } else {
37                 log_assert(c < 64);
38         }
39
40         return c;
41 }
42
43 /**
44  *
45  */
46 static unsigned char decode64(unsigned char c) {
47         if (c >= 'A' && c <= 'Z') {
48                 c -= 'A';
49         } else if (c >= 'a' && c <= 'z') {
50                 c -= 'a' - 26;
51         } else if (c >= '0' && c <= '9') {
52                 c -= '0' - 52;
53         } else if (c == '+') {
54                 c = 62;
55         } else if (c == '/') {
56                 c = 63;
57         } else if (c == '=' || c == '-') {
58                 c = 64;
59         } else {
60                 c = 65;
61         }
62
63         return c;
64 }
65
66 /**
67  *      @lastoctet: The last octet we got.
68  *      @curoctet: The current octet we're expecting (0, 1 or 2).
69  *      @count: The number of octets we've seen.
70  *      @crc24: A running CRC24 of the data we've seen.
71  *      @putchar_func: The function to output a character.
72  *      @ctx: Context for putchar_func.
73  */
74 struct armor_context {
75         unsigned char lastoctet;
76         int curoctet;
77         int count;
78         long crc24;
79         int (*putchar_func)(void *ctx, size_t count, unsigned char *c);
80         void *ctx;
81 };
82
83 static void armor_init(struct armor_context *ctx)
84 {
85         ctx->curoctet = 0;
86         ctx->lastoctet = 0;
87         ctx->count = 0;
88         ctx->crc24 = CRC24_INIT;
89 }
90
91 static void armor_finish(struct armor_context *state)
92 {
93         unsigned char c;
94
95         switch (state->curoctet++) {
96         case 0:
97                 break;
98         case 1:
99                 c = encode64((state->lastoctet & 3) << 4);
100                 state->putchar_func(state->ctx, 1, &c);
101                 state->putchar_func(state->ctx, 1, (unsigned char *) "=");
102                 state->putchar_func(state->ctx, 1, (unsigned char *) "=");
103                 state->count += 3;
104                 if ((state->count % ARMOR_WIDTH) == 0) {
105                         state->putchar_func(state->ctx, 1,
106                                  (unsigned char *) "\n");
107                 }
108                 break;
109         case 2:
110                 c = encode64((state->lastoctet & 0xF) << 2);
111                 state->putchar_func(state->ctx, 1, &c);
112                 state->putchar_func(state->ctx, 1, (unsigned char *) "=");
113                 state->count += 2;
114                 if ((state->count % ARMOR_WIDTH) == 0) {
115                         state->putchar_func(state->ctx, 1,
116                                  (unsigned char *) "\n");
117                 }
118                 break;
119         }
120
121         state->crc24 &= 0xffffffL;
122         if ((state->count % ARMOR_WIDTH) != 0) {
123                 state->putchar_func(state->ctx, 1, (unsigned char *) "\n");
124         }
125         state->putchar_func(state->ctx, 1, (unsigned char *) "=");
126         c = encode64(state->crc24 >> 18);
127         state->putchar_func(state->ctx, 1, &c);
128         c = encode64((state->crc24 >> 12) & 0x3F);
129         state->putchar_func(state->ctx, 1, &c);
130         c = encode64((state->crc24 >> 6) & 0x3F);
131         state->putchar_func(state->ctx, 1, &c);
132         c = encode64(state->crc24 & 0x3F);
133         state->putchar_func(state->ctx, 1, &c);
134         state->putchar_func(state->ctx, 1, (unsigned char *) "\n");
135
136 }
137
138
139 static int armor_putchar_int(void *ctx, unsigned char c)
140 {
141         struct armor_context *state;
142         unsigned char t;
143         int i;
144
145         log_assert(ctx != NULL);
146         state = (struct armor_context *) ctx;
147
148         switch (state->curoctet++) {
149         case 0:
150                 t = encode64(c >> 2);
151                 state->putchar_func(state->ctx, 1, &t);
152                 state->count++;
153                 break;
154         case 1:
155                 t = encode64(((state->lastoctet & 3) << 4) + (c >> 4));
156                 state->putchar_func(state->ctx, 1, &t);
157                 state->count++;
158                 break;
159         case 2:
160                 t = encode64(((state->lastoctet & 0xF) << 2) + (c >> 6));
161                 state->putchar_func(state->ctx, 1, &t);
162                 t = encode64(c & 0x3F);
163                 state->putchar_func(state->ctx, 1, &t);
164                 state->count += 2;
165                 break;
166         }
167         state->curoctet %= 3;
168         state->lastoctet = c;
169         
170         state->crc24 ^= c << 16;
171         for (i = 0; i < 8; i++) {
172                 state->crc24 <<= 1;
173                 if (state->crc24 & 0x1000000) {
174                         state->crc24 ^= CRC24_POLY;
175                 }
176         }
177
178         if ((state->count % ARMOR_WIDTH) == 0) {
179                 state->putchar_func(state->ctx, 1, (unsigned char *) "\n");
180         }
181
182         return 0;
183 }
184
185
186 static int armor_putchar(void *ctx, size_t count, unsigned char *c)
187 {
188         int i;
189
190         for (i = 0; i < count; i++) {
191                 armor_putchar_int(ctx, c[i]);
192         }
193         
194         return 0;
195 }
196
197 /**
198  *      @lastoctet: The last octet we got.
199  *      @curoctet: The current octet we're expecting (0, 1 or 2).
200  *      @count: The number of octets we've seen.
201  *      @crc24: A running CRC24 of the data we've seen.
202  *      @putchar_func: The function to output a character.
203  *      @ctx: Context for putchar_func.
204  */
205 struct dearmor_context {
206         unsigned char lastoctet;
207         int curoctet;
208         int count;
209         long crc24;
210         int (*getchar_func)(void *ctx, size_t count, unsigned char *c);
211         void *ctx;
212 };
213
214 static void dearmor_init(struct dearmor_context *ctx)
215 {
216         ctx->curoctet = 0;
217         ctx->lastoctet = 0;
218         ctx->count = 0;
219         ctx->crc24 = CRC24_INIT;
220 }
221
222 static void dearmor_finish(struct dearmor_context *state)
223 {
224         /*
225          * Check the checksum
226          */
227
228         state->crc24 &= 0xffffffL;
229         /*
230         state->putchar_func(state->ctx, '\n');
231         state->putchar_func(state->ctx, '=');
232         state->putchar_func(state->ctx, encode64(state->crc24 >> 18));
233         state->putchar_func(state->ctx, encode64((state->crc24 >> 12) & 0x3F));
234         state->putchar_func(state->ctx, encode64((state->crc24 >> 6) & 0x3F));
235         state->putchar_func(state->ctx, encode64(state->crc24 & 0x3F));
236         */
237 }
238
239
240 static int dearmor_getchar(void *ctx, unsigned char *c)
241 {
242         struct dearmor_context *state;
243         unsigned char tmpc;
244         int i;
245
246         log_assert(ctx != NULL);
247         state = (struct dearmor_context *) ctx;
248         *c = 0;
249         
250         tmpc = 65;
251         while (tmpc == 65) {
252                 state->getchar_func(state->ctx, 1, &tmpc);
253                 tmpc = decode64(tmpc);
254         }
255
256         if (tmpc != 64) {
257                 switch (state->curoctet++) {
258                 case 0:
259                         state->lastoctet = tmpc;
260                         tmpc = 65;
261                         while (tmpc == 65) {
262                                 state->getchar_func(state->ctx, 1, &tmpc);
263                                 tmpc = decode64(tmpc);
264                         }
265                         *c = (state->lastoctet << 2) + (tmpc >> 4);
266                         break;
267                 case 1:
268                         *c = ((state->lastoctet & 0xF) << 4) + (tmpc >> 2);
269                         break;
270                 case 2:
271                         *c = ((state->lastoctet & 3) << 6) + tmpc;
272                         break;
273                 }
274         
275                 state->curoctet %= 3;
276                 state->lastoctet = tmpc;
277                 state->count++;
278                 
279                 state->crc24 ^= *c << 16;
280                 for (i = 0; i < 8; i++) {
281                         state->crc24 <<= 1;
282                         if (state->crc24 & 0x1000000) {
283                                 state->crc24 ^= CRC24_POLY;
284                         }
285                 }
286         }
287
288         return (tmpc == 64);
289 }
290
291 static int dearmor_getchar_c(void *ctx, size_t count, unsigned char *c)
292 {
293         int i, rc = 0;
294
295         for (i = 0; i < count && rc == 0; i++) {
296                 rc = dearmor_getchar(ctx, &c[i]);
297         }
298
299         return rc;
300 }
301
302 /**
303  *      armor_openpgp_stream - Takes a list of OpenPGP packets and armors it.
304  *      @putchar_func: The function to output the next armor character.
305  *      @ctx: The context pointer for putchar_func.
306  *      @packets: The list of packets to output.
307  *
308  *      This function ASCII armors a list of OpenPGP packets and outputs it
309  *      using putchar_func.
310  */
311 int armor_openpgp_stream(int (*putchar_func)(void *ctx, size_t count,
312                                                 unsigned char *c),
313                                 void *ctx,
314                                 struct openpgp_packet_list *packets)
315 {
316         struct armor_context armor_ctx;
317
318         /*
319          * Print armor header
320          */
321         putchar_func(ctx, sizeof("-----BEGIN PGP PUBLIC KEY BLOCK-----\n") - 1,
322                 (unsigned char *) "-----BEGIN PGP PUBLIC KEY BLOCK-----\n");
323         putchar_func(ctx, sizeof("Version: onak " VERSION "\n\n") - 1,
324                 (unsigned char *) "Version: onak " VERSION "\n\n");
325         
326         armor_init(&armor_ctx);
327         armor_ctx.putchar_func = putchar_func;
328         armor_ctx.ctx = ctx;
329         write_openpgp_stream(armor_putchar, &armor_ctx, packets);
330         armor_finish(&armor_ctx);
331
332         /*
333          * Print armor footer
334          */
335         putchar_func(ctx, sizeof("-----END PGP PUBLIC KEY BLOCK-----\n") - 1,
336                 (unsigned char *) "-----END PGP PUBLIC KEY BLOCK-----\n");
337
338         return 0;
339 }
340
341 /**
342  *      dearmor_openpgp_stream - Reads & decodes an ACSII armored OpenPGP msg.
343  *      @getchar_func: The function to get the next character from the stream.
344  *      @ctx: The context pointer for getchar_func.
345  *      @packets: The list of packets.
346  *
347  *      This function uses getchar_func to read characters from an ASCII
348  *      armored OpenPGP stream and outputs the data as a linked list of
349  *      packets.
350  */
351 int dearmor_openpgp_stream(int (*getchar_func)(void *ctx, size_t count,
352                                                 unsigned char *c),
353                                 void *ctx,
354                                 struct openpgp_packet_list **packets)
355 {
356         struct dearmor_context dearmor_ctx;
357         unsigned char curchar;
358         int state = 0;
359         int count = 0;
360
361         /*
362          * Look for armor header. We want "-----BEGIN.*\n", then some headers
363          * with :s in them, then a blank line, then the data.
364          */
365         state = 1;
366         while (state != 4 && !getchar_func(ctx, 1, &curchar)) {
367                 switch (state) {
368                         case 0:
369                                 if (curchar == '\n') {
370                                         count = 0;
371                                         state = 1;
372                                 }
373                                 break;
374                         case 1:
375                                 if (curchar == '-') {
376                                         count++;
377                                         if (count == 5) {
378                                                 state = 2;
379                                         }
380                                 } else if (curchar != '\n') {
381                                         state = 0;
382                                 }
383                                 break;
384                         case 2:
385                                 if (curchar == 'B') {
386                                         count = 0;
387                                         state = 3;
388                                 } else {
389                                         state = 0;
390                                 }
391                                 break;
392                         case 3:
393                                 if (curchar == '\n') {
394                                         count++;
395                                         if (count == 2) {
396                                                 state = 4;
397                                         }
398                                 } else if (curchar != '\r') {
399                                         count = 0;
400                                 }
401                                 break;
402                 }
403         }
404
405         if (state == 4) {
406                 dearmor_init(&dearmor_ctx);
407                 dearmor_ctx.getchar_func = getchar_func;
408                 dearmor_ctx.ctx = ctx;
409                 read_openpgp_stream(dearmor_getchar_c, &dearmor_ctx,
410                         packets, 0);
411                 dearmor_finish(&dearmor_ctx);
412                 /*
413                  * TODO: Look for armor footer
414                  */
415         }
416
417         return 0;
418 }