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