a07236826a15bad9dca31cc726a8ef9c5f9ef54d
[u/mdw/catacomb] / key-text.c
1 /* -*-c-*-
2 *
3 * $Id: key-text.c,v 1.5 2004/04/01 13:42:48 mdw Exp $
4 *
5 * Key textual encoding
6 *
7 * (c) 1999 Straylight/Edgeware
8 */
9
10 /*----- Licensing notice --------------------------------------------------*
11 *
12 * This file is part of Catacomb.
13 *
14 * Catacomb is free software; you can redistribute it and/or modify
15 * it under the terms of the GNU Library General Public License as
16 * published by the Free Software Foundation; either version 2 of the
17 * License, or (at your option) any later version.
18 *
19 * Catacomb is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 * GNU Library General Public License for more details.
23 *
24 * You should have received a copy of the GNU Library General Public
25 * License along with Catacomb; if not, write to the Free
26 * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
27 * MA 02111-1307, USA.
28 */
29
30 /*----- Revision history --------------------------------------------------*
31 *
32 * $Log: key-text.c,v $
33 * Revision 1.5 2004/04/01 13:42:48 mdw
34 * Missed off <ctype.h>\!
35 *
36 * Revision 1.4 2004/03/28 01:58:47 mdw
37 * Generate, store and retreive elliptic curve keys.
38 *
39 * Revision 1.3 2001/02/03 11:57:00 mdw
40 * Track mLib change: symbols no longer need to include a terminating
41 * null.
42 *
43 * Revision 1.2 2000/06/17 11:27:20 mdw
44 * Use secure memory interface from MP library.
45 *
46 * Revision 1.1 2000/02/12 18:21:02 mdw
47 * Overhaul of key management (again).
48 *
49 */
50
51 /*----- Header files ------------------------------------------------------*/
52
53 #include <ctype.h>
54 #include <stdlib.h>
55 #include <string.h>
56
57 #include <mLib/base64.h>
58 #include <mLib/bits.h>
59 #include <mLib/dstr.h>
60 #include <mLib/sub.h>
61 #include <mLib/sym.h>
62 #include <mLib/url.h>
63
64 #include "key-data.h"
65 #include "mp.h"
66 #include "mptext.h"
67
68 /*----- Main code ---------------------------------------------------------*/
69
70 /* --- @key_read@ --- *
71 *
72 * Arguments: @const char *p@ = pointer to textual key representation
73 * @key_data *k@ = pointer to output block for key data
74 * @char **pp@ = where to store the end pointer
75 *
76 * Returns: Zero if all went well, nonzero if there was a problem.
77 *
78 * Use: Parses a textual key description.
79 */
80
81 int key_read(const char *p, key_data *k, char **pp)
82 {
83 unsigned e;
84
85 /* --- Read the encoding type --- *
86 *
87 * The key format is `[FLAGS:]DATA'. If there is no encoding type
88 * named, assume that it's `binary' for backwards compatibility.
89 */
90
91 if (strchr(p, ':') == 0)
92 e = 0;
93 else {
94 char *q;
95 if (key_readflags(p, &q, &e, 0))
96 return (-1);
97 p = q + 1;
98 }
99
100 /* --- Now scan the data based on the encoding type --- */
101
102 k->e = e;
103 switch (e & KF_ENCMASK) {
104
105 /* --- Binary encoding --- *
106 *
107 * Simply read out the Base64-encoded data. Since `,' and `]' are our
108 * delimeter characters, and they can't appear in Base64-encoded data, I
109 * can just do a simple search to find the end of the encoded data.
110 */
111
112 case KENC_BINARY:
113 case KENC_ENCRYPT: {
114 dstr d = DSTR_INIT;
115 base64_ctx b;
116 size_t sz = strcspn(p, ",]");
117
118 base64_init(&b);
119 base64_decode(&b, p, sz, &d);
120 base64_decode(&b, 0, 0, &d);
121 k->u.k.k = sub_alloc(d.len);
122 k->u.k.sz = d.len;
123 memcpy(k->u.k.k, d.buf, d.len);
124 dstr_destroy(&d);
125 p += sz;
126 } break;
127
128 /* --- Multiprecision integer encoding --- *
129 *
130 * Multiprecision integers have a convenient reading function.
131 */
132
133 case KENC_MP: {
134 char *q;
135 mp *m = mp_readstring(k->e & KF_BURN ? MP_NEWSEC : MP_NEW, p, &q, 0);
136 if (!m)
137 return (-1);
138 k->u.m = m;
139 p = q;
140 } break;
141
142 /* --- String encoding --- *
143 *
144 * We use form-urlencoding to ensure that evil characters don't get out.
145 */
146
147 case KENC_STRING: {
148 dstr d = DSTR_INIT;
149 size_t sz = strcspn(p, ",]");
150 const char *l = p + sz;
151 unsigned int ch;
152 int x, n;
153
154 while (p < l) {
155 switch (*p) {
156 case '+':
157 DPUTC(&d, ' '); break;
158 case '%':
159 x = sscanf(p + 1, "%2x%n", &ch, &n);
160 if (x == 1) { DPUTC(&d, ch); p += n; break; }
161 default:
162 DPUTC(&d, *p); break;
163 }
164 p++;
165 }
166 DPUTZ(&d);
167 k->u.p = xstrdup(d.buf);
168 dstr_destroy(&d);
169 } break;
170
171 /* --- Elliptic curve encoding --- *
172 *
173 * Again, we have a convenient function. Assume for now that points
174 * aren't secret. (Reasonably safe.)
175 */
176
177 case KENC_EC: {
178 qd_parse qd;
179 qd.p = p;
180 qd.e = 0;
181 EC_CREATE(&k->u.e);
182 if (!ec_ptparse(&qd, &k->u.e))
183 return (-1);
184 p = qd.p;
185 } break;
186
187 /* --- Structured information encoding --- *
188 *
189 * The format for structured key data is `[NAME=KEY,...]', where the
190 * brackets are part of the syntax. Structured keys have no flags apart
191 * from the encoding.
192 *
193 * The binary encoding only allows names up to 255 bytes long. Check for
194 * this here.
195 */
196
197 case KENC_STRUCT: {
198 dstr d = DSTR_INIT;
199 char *q;
200
201 /* --- Read the opening bracket --- */
202
203 k->e &= KF_ENCMASK;
204 if (*p != '[')
205 return (-1);
206 p++;
207 sym_create(&k->u.s);
208
209 /* --- Read named key subparts --- */
210
211 for (;;) {
212 size_t sz;
213 key_struct *ks;
214
215 /* --- Stop if there's a close-bracket --- *
216 *
217 * This allows `[]' to be an empty structured key, which is good. It
218 * also makes `[foo=enc:bar,]' legal, and that's less good but I can
219 * live with it.
220 */
221
222 if (*p == ']')
223 break;
224
225 /* --- Read the name out and check the length --- */
226
227 if ((q = strchr(p, '=')) == 0)
228 goto fail;
229 sz = q - p;
230 if (sz >= 256)
231 goto fail;
232 DRESET(&d);
233 DPUTM(&d, p, sz);
234 DPUTZ(&d);
235
236 /* --- Add an appropriate block to the key table --- *
237 *
238 * Simply destroy old data if there's already a match.
239 */
240
241 {
242 unsigned f;
243 ks = sym_find(&k->u.s, d.buf, d.len, sizeof(*ks), &f);
244 if (f)
245 key_destroy(&ks->k);
246 }
247
248 /* --- Read the key data for the subkey --- */
249
250 if (key_read(q + 1, &ks->k, &q)) {
251 sym_remove(&k->u.s, ks);
252 goto fail;
253 }
254 p = q;
255
256 /* --- Read the comma or close-bracket --- */
257
258 if (*p == ']')
259 break;
260 else if (*p == ',')
261 p++;
262 else
263 goto fail;
264 }
265
266 /* --- Step past the close bracket --- */
267
268 p++;
269 dstr_destroy(&d);
270 break;
271
272 /* --- Tidy up after a failure --- */
273
274 fail:
275 dstr_destroy(&d);
276 key_destroy(k);
277 return (-1);
278 } break;
279
280 /* --- Anything else is unknown --- */
281
282 default:
283 return (-1);
284 }
285
286 /* --- Return the end pointer --- */
287
288 if (pp)
289 *pp = (char *)p;
290 return (0);
291 }
292
293 /* --- @key_write@ --- *
294 *
295 * Arguments: @key_data *k@ = pointer to key data
296 * @dstr *d@ = destination string to write on
297 * @const key_filter *kf@ = pointer to key selection block
298 *
299 * Returns: Nonzero if an item was actually written.
300 *
301 * Use: Writes a key in a textual encoding.
302 */
303
304 int key_write(key_data *k, dstr *d, const key_filter *kf)
305 {
306 int rc = 0;
307 if (!KEY_MATCH(k, kf))
308 return (0);
309 switch (k->e & KF_ENCMASK) {
310 case KENC_BINARY:
311 case KENC_ENCRYPT: {
312 base64_ctx b;
313
314 if ((k->e & KF_ENCMASK) == KENC_BINARY)
315 key_writeflags(k->e, d);
316 else
317 DPUTS(d, "encrypt,secret");
318 DPUTC(d, ':');
319 base64_init(&b);
320 b.indent = "";
321 b.maxline = 0;
322 base64_encode(&b, k->u.k.k, k->u.k.sz, d);
323 base64_encode(&b, 0, 0, d);
324 rc = 1;
325 } break;
326 case KENC_MP:
327 key_writeflags(k->e, d);
328 DPUTC(d, ':');
329 mp_writedstr(k->u.m, d, 10);
330 rc = 1;
331 break;
332 case KENC_STRING: {
333 const char *p = k->u.p;
334 key_writeflags(k->e, d);
335 DPUTC(d, ':');
336 while (*p) {
337 if (*p == ' ') DPUTC(d, '+');
338 else if (!isalnum((unsigned char)*p)) dstr_putf(d, "%%%02x", *p);
339 else DPUTC(d, *p);
340 p++;
341 }
342 rc = 1;
343 } break;
344 case KENC_EC:
345 key_writeflags(k->e, d);
346 DPUTS(d, ":0x"); mp_writedstr(k->u.e.x, d, 16);
347 DPUTS(d, ",0x"); mp_writedstr(k->u.e.y, d, 16);
348 rc = 1;
349 break;
350 case KENC_STRUCT: {
351 sym_iter i;
352 key_struct *ks;
353 char del = 0;
354 size_t n = d->len;
355
356 DPUTS(d, "struct:[");
357 for (sym_mkiter(&i, &k->u.s); (ks = sym_next(&i)) != 0; ) {
358 size_t o = d->len;
359 if (del)
360 DPUTC(d, del);
361 DPUTS(d, SYM_NAME(ks));
362 DPUTC(d, '=');
363 if (!key_write(&ks->k, d, kf))
364 d->len = o;
365 else {
366 del = ',';
367 rc = 1;
368 }
369 }
370 if (!rc)
371 d->len = n;
372 else
373 DPUTC(d, ']');
374 } break;
375 }
376 DPUTZ(d);
377
378 return (rc);
379 }
380
381 /*----- That's all, folks -------------------------------------------------*/