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