5 Author: Pekka Riikonen <priikone@silcnet.org>
7 Copyright (C) 2007 Pekka Riikonen
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; version 2 of the License.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
22 /************************* Static utility functions *************************/
24 /* Key fields destructor */
26 static void silc_ssh_field_dest(void *key, void *context, void *user_context)
32 /* Parse header line from key. Doesn't return the line termination
35 SilcBool silc_ssh_parse_line(SilcBuffer key, SilcBuffer line,
40 SilcBool valid = cont;
42 data_len = silc_buffer_len(key);
43 tmp = silc_buffer_data(key);
44 for (i = 0; i < data_len; i++) {
45 /* All header lines must have ':' character */
46 if (!cont && tmp[i] == ':')
49 if ((data_len - i >= 1 && tmp[i] == '\r') ||
50 (data_len - i >= 1 && tmp[i] == '\n')) {
56 silc_buffer_set(line, tmp, i);
58 if (data_len - i >= 2 && tmp[i] == '\r' && tmp[i + 1] == '\n')
59 silc_buffer_pull(key, i + 2);
61 silc_buffer_pull(key, i + 1);
70 /* Allocate fields hash table */
72 SilcHashTable silc_ssh_allocate_fields(void)
74 return silc_hash_table_alloc(NULL, 0, silc_hash_string, NULL,
75 silc_hash_string_compare, NULL,
76 silc_ssh_field_dest, NULL, TRUE);
79 /* Parse key headers and return them into a hash table */
81 SilcHashTable silc_ssh_parse_headers(SilcBuffer key)
84 unsigned char *field, *value;
85 SilcBufferStruct line, v;
86 SilcBool quoted = FALSE;
88 SILC_LOG_DEBUG(("Parsing SSH key headers"));
90 fields = silc_ssh_allocate_fields();
94 /* Parse the fields */
95 while (silc_buffer_len(key) > 0) {
96 if (!silc_ssh_parse_line(key, &line, FALSE))
101 field = strchr(silc_buffer_data(&line), ':');
104 if (field - silc_buffer_data(&line) > 64)
106 field = silc_memdup(silc_buffer_data(&line),
107 field - silc_buffer_data(&line));
111 /* Skip ':' and following whitespace */
112 if (!silc_buffer_pull(&line, strlen(field) + 2))
117 memset(&v, 0, sizeof(v));
118 silc_buffer_format(&v,
119 SILC_STR_DATA(silc_buffer_data(&line),
120 silc_buffer_len(&line)),
123 /* Handle quoted Comment lines by removing the quotation */
124 if (*silc_buffer_data(&v) == '"' && !strcmp(field, "Comment"))
127 /* Handle wrapping value lines */
128 while (silc_buffer_len(&v) > 0) {
129 if (*silc_buffer_data(&v) == '\\') {
130 if (!silc_ssh_parse_line(key, &line, TRUE))
132 silc_buffer_format(&v,
133 SILC_STR_DATA(silc_buffer_data(&line),
134 silc_buffer_len(&line)),
138 silc_buffer_pull(&v, 1);
140 silc_buffer_start(&v);
142 if (silc_buffer_len(&v) > 1024)
146 /* If the last character is quotation also, remove the quotation */
147 if (*(silc_buffer_data(&v) + silc_buffer_len(&v) - 1) == '"') {
148 silc_buffer_pull(&v, 1);
149 silc_buffer_push_tail(&v, 1);
153 value = silc_memdup(silc_buffer_data(&v), silc_buffer_len(&v));
156 silc_buffer_purge(&v);
158 /* Add to hash table */
159 SILC_LOG_DEBUG(("Header '%s' '%s'", field, value));
160 silc_hash_table_add(fields, field, value);
166 SILC_LOG_ERROR(("Malformed SSH2 key headers"));
167 silc_hash_table_free(fields);
171 /******************************* SILC SSH API *******************************/
173 /* Generate key pair */
175 SilcBool silc_ssh_generate_key(const char *algorithm,
176 int bits_len, SilcRng rng,
178 SilcPublicKey *ret_public_key,
179 SilcPrivateKey *ret_private_key)
181 SilcSshPublicKey pubkey;
182 SilcSshPrivateKey privkey;
183 const SilcPKCSAlgorithm *alg;
184 const SilcPKCSObject *pkcs;
186 SILC_LOG_DEBUG(("Generating SSH2 %s key pair with key length %d bits",
187 algorithm, bits_len));
192 pkcs = silc_pkcs_find_pkcs(SILC_PKCS_SSH2);
196 /* Allocate SSH public key */
197 pubkey = silc_calloc(1, sizeof(*pubkey));
201 /* Allocate algorithm */
202 alg = silc_pkcs_find_algorithm(algorithm, "ssh");
204 SILC_LOG_ERROR(("Public key algorithm %s/ssh not supported", algorithm));
209 pubkey->type = SILC_SSH_KEY_OPENSSH;
211 /* Allocate SSH private key */
212 privkey = silc_calloc(1, sizeof(*privkey));
218 privkey->type = SILC_SSH_KEY_OPENSSH;
220 /* Allocate public key */
221 *ret_public_key = silc_calloc(1, sizeof(**ret_public_key));
222 if (!(*ret_public_key)) {
227 (*ret_public_key)->pkcs = (SilcPKCSObject *)pkcs;
228 (*ret_public_key)->alg = alg;
229 (*ret_public_key)->public_key = pubkey;
231 /* Allocate private key */
232 *ret_private_key = silc_calloc(1, sizeof(**ret_private_key));
233 if (!(*ret_private_key)) {
236 silc_free(*ret_public_key);
239 (*ret_private_key)->pkcs = (SilcPKCSObject *)pkcs;
240 (*ret_private_key)->alg = alg;
241 (*ret_private_key)->private_key = privkey;
243 /* Generate the algorithm key pair */
244 if (!alg->generate_key(alg, bits_len, rng, &pubkey->public_key,
245 &privkey->private_key)) {
248 silc_free(*ret_public_key);
249 silc_free(*ret_private_key);
254 silc_ssh_public_key_add_field(pubkey, "Subject", subject);
259 /* Decode SSH public key. */
261 int silc_ssh_public_key_decode(unsigned char *key, SilcUInt32 key_len,
262 SilcSshPublicKey *ret_public_key)
264 SilcSshPublicKey public_key;
265 const SilcPKCSAlgorithm *alg;
266 SilcBufferStruct keybuf;
269 SILC_LOG_DEBUG(("Parse SSH2 public key"));
274 public_key = silc_calloc(1, sizeof(*public_key));
278 silc_buffer_set(&keybuf, key, key_len);
280 SILC_LOG_HEXDUMP(("SSH public key, len %d", key_len), key, key_len);
282 /* Parse public key type */
283 if (silc_buffer_unformat(&keybuf,
285 SILC_STR_UI32_STRING_ALLOC(&type),
287 SILC_LOG_ERROR(("Malformed SSH2 public key"));
291 SILC_LOG_DEBUG(("SSH2 public key type %s", type));
293 if (!strcmp(type, "ssh-rsa")) {
295 alg = silc_pkcs_find_algorithm("rsa", "ssh");
297 SILC_LOG_ERROR(("Unsupported SSH2 public key type '%s'", type));
300 public_key->pkcs = alg;
302 } else if (!strcmp(type, "ssh-dss")) {
304 alg = silc_pkcs_find_algorithm("dsa", "ssh");
306 SILC_LOG_ERROR(("Unsupported SSH2 public key type '%s'", type));
309 public_key->pkcs = alg;
312 SILC_LOG_ERROR(("Unsupported SSH2 public key type '%s'", type));
316 /* Parse the algorithm specific public key */
317 if (!alg->import_public_key(alg, silc_buffer_data(&keybuf),
318 silc_buffer_len(&keybuf),
319 &public_key->public_key))
324 *ret_public_key = public_key;
330 silc_free(public_key);
334 /* Encode SSH public key */
336 unsigned char *silc_ssh_public_key_encode(SilcStack stack,
337 SilcSshPublicKey public_key,
338 SilcUInt32 *ret_key_len)
340 const SilcPKCSAlgorithm *alg = public_key->pkcs;
341 SilcBufferStruct buf;
342 unsigned char *pk = NULL, tmp[16];
345 SILC_LOG_DEBUG(("Encode SSH2 public key"));
347 /* Get algorithm name */
348 if (!strcmp(alg->name, "rsa"))
349 silc_snprintf(tmp, sizeof(tmp), "ssh-rsa");
350 else if (!strcmp(alg->name, "dsa"))
351 silc_snprintf(tmp, sizeof(tmp), "ssh-dss");
355 /* Export PKCS algorithm public key */
356 if (alg->export_public_key)
357 pk = alg->export_public_key(alg, stack, public_key->public_key, &pk_len);
359 SILC_LOG_ERROR(("Error exporting PKCS algorithm key"));
363 /* Encode public key */
364 memset(&buf, 0, sizeof(buf));
365 if (silc_buffer_sformat(stack, &buf,
366 SILC_STR_UI_INT(strlen(tmp)),
367 SILC_STR_UI32_STRING(tmp),
368 SILC_STR_UI_XNSTRING(pk, pk_len),
370 silc_sfree(stack, pk);
374 silc_sfree(stack, pk);
375 pk = silc_buffer_steal(&buf, ret_key_len);
380 /* Free public key */
382 void silc_ssh_public_key_free(SilcSshPublicKey public_key)
384 if (public_key->fields)
385 silc_hash_table_free(public_key->fields);
386 public_key->pkcs->public_key_free(public_key->pkcs,
387 public_key->public_key);
388 silc_free(public_key);
391 /* Return public key header field value */
393 const char *silc_ssh_public_key_get_field(SilcSshPublicKey public_key,
398 if (!field || !public_key->fields)
401 if (!silc_hash_table_find(public_key->fields, (void *)field,
402 NULL, (void *)&value))
405 return (const char *)value;
408 /* Add public key header value */
410 SilcBool silc_ssh_public_key_add_field(SilcSshPublicKey public_key,
414 if (!field || !value)
417 if (!public_key->fields) {
419 silc_hash_table_alloc(NULL, 0, silc_hash_string, NULL,
420 silc_hash_string_compare, NULL,
421 silc_ssh_field_dest, NULL, TRUE);
422 if (!public_key->fields)
426 return silc_hash_table_add(public_key->fields, strdup(field), strdup(value));
429 /* Set public key type */
431 void silc_ssh_public_key_set_type(SilcSshPublicKey public_key,
434 public_key->type = type;