]> the.earth.li Git - onak.git/blob - armor.c
Switch charfuncs to returning number of read/written characters
[onak.git] / armor.c
1 /**
2  * @file armor.c
3  * @brief Routines to (de)armor OpenPGP packet streams.
4  *
5  * Copyright 2002-2004, 2011 Jonathan McDowell <noodles@earth.li>
6  *
7  * This program is free software: you can redistribute it and/or modify it
8  * under the terms of the GNU General Public License as published by the Free
9  * Software Foundation; version 2 of the License.
10  *
11  * This program is distributed in the hope that it will be useful, but WITHOUT
12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13  * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
14  * more details.
15  *
16  * You should have received a copy of the GNU General Public License along with
17  * this program.  If not, see <https://www.gnu.org/licenses/>.
18  */
19
20 #include <stdlib.h>
21
22 #include "build-config.h"
23
24 #include "armor.h"
25 #include "keystructs.h"
26 #include "parsekey.h"
27
28 /**
29  * @brief Line length we'll use for armored output
30  */
31 #define ARMOR_WIDTH 64
32
33 /**
34  * @brief CRC24 initialisation value
35  */
36 #define CRC24_INIT 0xb704ceL
37 /**
38  * @brief CRC24 polynomial value
39  */
40 #define CRC24_POLY 0x1864cfbL
41
42 /**
43  *
44  */
45 static unsigned char encode64(unsigned char c) {
46         if (c <= 25) {
47                 c += 'A';
48         } else if (c >= 26 && c <= 51) {
49                 c += 'a' - 26;
50         } else if (c >= 52 && c <= 61) {
51                 c += '0' - 52;
52         } else if (c == 62) {
53                 c = '+';
54         } else if (c == 63) {
55                 c = '/';
56         } else {
57                 c = '?';
58         }
59
60         return c;
61 }
62
63 /**
64  *
65  */
66 static unsigned char decode64(unsigned char c) {
67         if (c >= 'A' && c <= 'Z') {
68                 c -= 'A';
69         } else if (c >= 'a' && c <= 'z') {
70                 c -= 'a' - 26;
71         } else if (c >= '0' && c <= '9') {
72                 c -= '0' - 52;
73         } else if (c == '+') {
74                 c = 62;
75         } else if (c == '/') {
76                 c = 63;
77         } else if (c == '=' || c == '-') {
78                 c = 64;
79         } else {
80                 c = 65;
81         }
82
83         return c;
84 }
85
86 /**
87  * @brief Holds the context of an ongoing ASCII armor operation
88  */
89 struct armor_context {
90         /** The last octet we got. */
91         unsigned char lastoctet;
92         /** The current octet we're expecting (0, 1 or 2). */
93         int curoctet;
94         /** The number of octets we've seen. */
95         int count;
96         /** A running CRC24 of the data we've seen. */
97         long crc24;
98         /** The function to output a character. */
99         size_t (*putchar_func)(void *ctx, size_t count, void *c);
100         /** Context for putchar_func. */
101         void *ctx;
102 };
103
104 static void armor_init(struct armor_context *ctx)
105 {
106         ctx->curoctet = 0;
107         ctx->lastoctet = 0;
108         ctx->count = 0;
109         ctx->crc24 = CRC24_INIT;
110 }
111
112 static void armor_finish(struct armor_context *state)
113 {
114         unsigned char c;
115
116         switch (state->curoctet++) {
117         case 0:
118                 break;
119         case 1:
120                 c = encode64((state->lastoctet & 3) << 4);
121                 state->putchar_func(state->ctx, 1, &c);
122                 state->putchar_func(state->ctx, 1, (unsigned char *) "=");
123                 state->putchar_func(state->ctx, 1, (unsigned char *) "=");
124                 state->count += 3;
125                 if ((state->count % ARMOR_WIDTH) == 0) {
126                         state->putchar_func(state->ctx, 1,
127                                  (unsigned char *) "\n");
128                 }
129                 break;
130         case 2:
131                 c = encode64((state->lastoctet & 0xF) << 2);
132                 state->putchar_func(state->ctx, 1, &c);
133                 state->putchar_func(state->ctx, 1, (unsigned char *) "=");
134                 state->count += 2;
135                 if ((state->count % ARMOR_WIDTH) == 0) {
136                         state->putchar_func(state->ctx, 1,
137                                  (unsigned char *) "\n");
138                 }
139                 break;
140         }
141
142         state->crc24 &= 0xffffffL;
143         if ((state->count % ARMOR_WIDTH) != 0) {
144                 state->putchar_func(state->ctx, 1, (unsigned char *) "\n");
145         }
146         state->putchar_func(state->ctx, 1, (unsigned char *) "=");
147         c = encode64(state->crc24 >> 18);
148         state->putchar_func(state->ctx, 1, &c);
149         c = encode64((state->crc24 >> 12) & 0x3F);
150         state->putchar_func(state->ctx, 1, &c);
151         c = encode64((state->crc24 >> 6) & 0x3F);
152         state->putchar_func(state->ctx, 1, &c);
153         c = encode64(state->crc24 & 0x3F);
154         state->putchar_func(state->ctx, 1, &c);
155         state->putchar_func(state->ctx, 1, (unsigned char *) "\n");
156
157 }
158
159
160 static int armor_putchar_int(void *ctx, unsigned char c)
161 {
162         struct armor_context *state;
163         unsigned char t;
164         int i;
165
166         state = (struct armor_context *) ctx;
167
168         switch (state->curoctet++) {
169         case 0:
170                 t = encode64(c >> 2);
171                 state->putchar_func(state->ctx, 1, &t);
172                 state->count++;
173                 break;
174         case 1:
175                 t = encode64(((state->lastoctet & 3) << 4) + (c >> 4));
176                 state->putchar_func(state->ctx, 1, &t);
177                 state->count++;
178                 break;
179         case 2:
180                 t = encode64(((state->lastoctet & 0xF) << 2) + (c >> 6));
181                 state->putchar_func(state->ctx, 1, &t);
182                 t = encode64(c & 0x3F);
183                 state->putchar_func(state->ctx, 1, &t);
184                 state->count += 2;
185                 break;
186         }
187         state->curoctet %= 3;
188         state->lastoctet = c;
189         
190         state->crc24 ^= c << 16;
191         for (i = 0; i < 8; i++) {
192                 state->crc24 <<= 1;
193                 if (state->crc24 & 0x1000000) {
194                         state->crc24 ^= CRC24_POLY;
195                 }
196         }
197
198         if ((state->count % ARMOR_WIDTH) == 0) {
199                 state->putchar_func(state->ctx, 1, (unsigned char *) "\n");
200         }
201
202         return 0;
203 }
204
205
206 static size_t armor_putchar(void *ctx, size_t count, void *c)
207 {
208         int i;
209
210
211         for (i = 0; i < count; i++) {
212                 armor_putchar_int(ctx, ((char *) c)[i]);
213         }
214
215         return count;
216 }
217
218 /**
219  * @brief Holds the context of an ongoing ASCII dearmor operation
220  */
221 struct dearmor_context {
222         /** The last octet we got. */
223         unsigned char lastoctet;
224         /** The current octet we're expecting (0, 1 or 2). */
225         int curoctet;
226         /** The number of octets we've seen. */
227         int count;
228         /** A running CRC24 of the data we've seen. */
229         long crc24;
230         /** The function to get the next character. */
231         size_t (*getchar_func)(void *ctx, size_t count, void *c);
232         /** Context for getchar_func. */
233         void *ctx;
234 };
235
236 static void dearmor_init(struct dearmor_context *ctx)
237 {
238         ctx->curoctet = 0;
239         ctx->lastoctet = 0;
240         ctx->count = 0;
241         ctx->crc24 = CRC24_INIT;
242 }
243
244 static void dearmor_finish(struct dearmor_context *state)
245 {
246         /*
247          * Check the checksum
248          */
249
250         state->crc24 &= 0xffffffL;
251         /*
252         state->putchar_func(state->ctx, '\n');
253         state->putchar_func(state->ctx, '=');
254         state->putchar_func(state->ctx, encode64(state->crc24 >> 18));
255         state->putchar_func(state->ctx, encode64((state->crc24 >> 12) & 0x3F));
256         state->putchar_func(state->ctx, encode64((state->crc24 >> 6) & 0x3F));
257         state->putchar_func(state->ctx, encode64(state->crc24 & 0x3F));
258         */
259 }
260
261
262 static int dearmor_getchar(void *ctx, unsigned char *c)
263 {
264         struct dearmor_context *state;
265         unsigned char tmpc;
266         int i;
267
268         state = (struct dearmor_context *) ctx;
269         *c = 0;
270         
271         tmpc = 65;
272         while (tmpc == 65) {
273                 state->getchar_func(state->ctx, 1, &tmpc);
274                 tmpc = decode64(tmpc);
275         }
276
277         if (tmpc != 64) {
278                 switch (state->curoctet++) {
279                 case 0:
280                         state->lastoctet = tmpc;
281                         tmpc = 65;
282                         while (tmpc == 65) {
283                                 state->getchar_func(state->ctx, 1, &tmpc);
284                                 tmpc = decode64(tmpc);
285                         }
286                         *c = (state->lastoctet << 2) + (tmpc >> 4);
287                         break;
288                 case 1:
289                         *c = ((state->lastoctet & 0xF) << 4) + (tmpc >> 2);
290                         break;
291                 case 2:
292                         *c = ((state->lastoctet & 3) << 6) + tmpc;
293                         break;
294                 }
295         
296                 state->curoctet %= 3;
297                 state->lastoctet = tmpc;
298                 state->count++;
299                 
300                 state->crc24 ^= *c << 16;
301                 for (i = 0; i < 8; i++) {
302                         state->crc24 <<= 1;
303                         if (state->crc24 & 0x1000000) {
304                                 state->crc24 ^= CRC24_POLY;
305                         }
306                 }
307         }
308
309         return (tmpc == 64);
310 }
311
312 static size_t dearmor_getchar_c(void *ctx, size_t count, void *c)
313 {
314         int i, rc = 0;
315
316         for (i = 0; i < count && rc == 0; i++) {
317                 rc = dearmor_getchar(ctx, &((unsigned char *) c)[i]);
318         }
319
320         return (rc == 0) ? i : 0;
321 }
322
323 int armor_openpgp_stream(size_t (*putchar_func)(void *ctx, size_t count,
324                                                 void *c),
325                                 void *ctx,
326                                 struct openpgp_packet_list *packets)
327 {
328         struct armor_context armor_ctx;
329
330         /*
331          * Print armor header
332          */
333         putchar_func(ctx, sizeof("-----BEGIN PGP PUBLIC KEY BLOCK-----\n\n") - 1,
334                 (unsigned char *) "-----BEGIN PGP PUBLIC KEY BLOCK-----\n\n");
335
336         armor_init(&armor_ctx);
337         armor_ctx.putchar_func = putchar_func;
338         armor_ctx.ctx = ctx;
339         write_openpgp_stream(armor_putchar, &armor_ctx, packets);
340         armor_finish(&armor_ctx);
341
342         /*
343          * Print armor footer
344          */
345         putchar_func(ctx, sizeof("-----END PGP PUBLIC KEY BLOCK-----\n") - 1,
346                 (unsigned char *) "-----END PGP PUBLIC KEY BLOCK-----\n");
347
348         return 0;
349 }
350
351 int dearmor_openpgp_stream(size_t (*getchar_func)(void *ctx, size_t count,
352                                                 void *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) == 1) {
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 }