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