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