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