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.
23 #include "silcssh_pkcs.h"
25 /************************** Types and definitions ***************************/
27 /* RFC 4716 public key file markers */
28 #define SILC_SSH_PUBLIC_KEY_BEGIN "---- BEGIN SSH2 PUBLIC KEY ----"
29 #define SILC_SSH_PUBLIC_KEY_END "---- END SSH2 PUBLIC KEY ----"
31 /* OpenSSH private key file markers */
32 #define SILC_SSH_RSA_BEGIN "-----BEGIN RSA PRIVATE KEY-----"
33 #define SILC_SSH_RSA_END "-----END RSA PRIVATE KEY-----"
34 #define SILC_SSH_DSA_BEGIN "-----BEGIN DSA PRIVATE KEY-----"
35 #define SILC_SSH_DSA_END "-----END DSA PRIVATE KEY-----"
37 /****************************** SSH2 PKCS API *******************************/
39 /* Get algorithm context */
41 SILC_PKCS_GET_ALGORITHM(silc_pkcs_ssh_get_algorithm)
43 SilcSshPublicKey pubkey = public_key;
47 /* Import public key file */
49 SILC_PKCS_IMPORT_PUBLIC_KEY_FILE(silc_pkcs_ssh_import_public_key_file)
51 SilcSshPublicKey pubkey;
52 SilcBufferStruct keybuf, line;
58 SILC_LOG_DEBUG(("Parsing SSH2 public key file"));
62 if (encoding == SILC_PKCS_FILE_BIN)
65 silc_buffer_set(&keybuf, filedata, filedata_len);
67 /* Check for RFC 4716 style public key markers */
68 if (!silc_ssh_parse_line(&keybuf, &line, TRUE)) {
69 SILC_LOG_DEBUG(("Malformed SSH2 public key markers"));
72 if ((silc_buffer_len(&keybuf) < (strlen(SILC_SSH_PUBLIC_KEY_BEGIN) +
73 strlen(SILC_SSH_PUBLIC_KEY_END))) ||
74 strncmp(silc_buffer_data(&line), SILC_SSH_PUBLIC_KEY_BEGIN,
75 silc_buffer_len(&line))) {
76 /* We assume the key is OpenSSH style public key. */
77 type = SILC_SSH_KEY_OPENSSH;
78 silc_buffer_set(&keybuf, filedata, filedata_len);
80 /* Get subject name from the end of the file */
81 if (!silc_buffer_strchr(&keybuf, ' ', FALSE)) {
82 SILC_LOG_DEBUG(("Malformed SSH2 public key"));
85 if (!silc_buffer_pull(&keybuf, 1)) {
86 SILC_LOG_DEBUG(("Malformed SSH2 public key"));
89 if (!silc_buffer_len(&keybuf)) {
90 SILC_LOG_DEBUG(("Malformed SSH2 public key"));
94 /* Add subject name to public key headers */
95 fields = silc_ssh_allocate_fields();
98 silc_hash_table_add(fields, strdup("Subject"),
99 silc_memdup(silc_buffer_data(&keybuf),
100 silc_buffer_len(&keybuf)));
102 filedata_len = silc_buffer_headlen(&keybuf) - 1;
103 SILC_LOG_DEBUG(("Add Subject header to public key"));
105 /* Skip algorithm name */
106 silc_buffer_start(&keybuf);
107 if (!silc_buffer_strchr(&keybuf, ' ', TRUE)) {
108 SILC_LOG_DEBUG(("Malformed SSH2 public key"));
109 silc_hash_table_free(fields);
112 if (!silc_buffer_pull(&keybuf, 1)) {
113 SILC_LOG_DEBUG(("Malformed SSH2 public key"));
114 silc_hash_table_free(fields);
117 if (silc_buffer_len(&keybuf) < filedata_len) {
118 SILC_LOG_DEBUG(("Malformed SSH2 public key"));
119 silc_hash_table_free(fields);
123 filedata = silc_buffer_data(&keybuf);
124 SILC_LOG_DEBUG(("Public key is OpenSSH public key"));
127 /* RFC 4716 style public key */
128 type = SILC_SSH_KEY_SSH2;
130 filedata = silc_buffer_data(&keybuf);
131 filedata_len = silc_buffer_len(&keybuf) - strlen(SILC_SSH_PUBLIC_KEY_END);
132 silc_buffer_set(&keybuf, filedata, filedata_len);
134 /* Parse public key headers */
135 fields = silc_ssh_parse_headers(&keybuf);
139 filedata = silc_buffer_data(&keybuf);
140 filedata_len = silc_buffer_len(&keybuf);
142 SILC_LOG_DEBUG(("Public key is standard SSH2 public key"));
146 data = silc_base64_decode(NULL, filedata, filedata_len, &filedata_len);
148 silc_hash_table_free(fields);
153 /* Decode the public key */
154 ret = silc_pkcs_ssh_import_public_key(pkcs, NULL, filedata, filedata_len,
155 (void *)&pubkey, ret_alg);
159 pubkey->fields = fields;
161 *ret_public_key = pubkey;
162 SILC_LOG_DEBUG(("SSH2 public key file imported successfully"));
166 silc_hash_table_free(fields);
170 /* Import public key */
172 SILC_PKCS_IMPORT_PUBLIC_KEY(silc_pkcs_ssh_import_public_key)
174 SilcSshPublicKey pubkey;
177 ret = silc_ssh_public_key_decode(key, key_len, &pubkey);
180 *ret_alg = pubkey->pkcs;
182 *ret_public_key = pubkey;
188 /* Export public key file */
190 SILC_PKCS_EXPORT_PUBLIC_KEY_FILE(silc_pkcs_ssh_export_public_key_file)
192 SilcSshPublicKey pubkey = public_key;
193 SilcHashTableList htl;
194 SilcBufferStruct buf, fields;
195 unsigned char *key, *data;
197 char *field, *value, tmp[1024], tmp2[1024 + 24];
200 SILC_LOG_DEBUG(("Encoding %s public key file",
201 pubkey->type == SILC_SSH_KEY_SSH2 ? "SSH2" : "OpenSSH"));
204 key = silc_pkcs_ssh_export_public_key(pkcs, stack, pubkey, &key_len);
208 /* Base64 encode the key data */
209 if (pubkey->type == SILC_SSH_KEY_SSH2)
210 data = silc_base64_encode_file(stack, key, key_len);
212 data = silc_base64_encode(stack, key, key_len);
215 silc_sfree(stack, key);
217 key_len = strlen(data);
219 memset(&buf, 0, sizeof(buf));
220 memset(&fields, 0, sizeof(fields));
221 memset(tmp, 0, sizeof(tmp));
222 memset(tmp2, 0, sizeof(tmp2));
224 switch (pubkey->type) {
225 case SILC_SSH_KEY_SSH2:
226 /* RFC 4716 style public key file */
228 if (pubkey->fields) {
229 /* Encode public key headers */
230 silc_hash_table_list(pubkey->fields, &htl);
231 while (silc_hash_table_get(&htl, (void *)&field, (void *)&value)) {
232 /* Wrap lines with over 72 characters */
233 silc_snprintf(tmp, sizeof(tmp), "%s: %s", field, value);
234 for (i = 0, j = 0, c = 1; i < strlen(tmp); i++, c++) {
247 if (silc_buffer_sstrformat(stack, &fields, tmp2, SILC_STRFMT_END) < 0) {
248 silc_buffer_spurge(stack, &fields);
249 silc_sfree(stack, key);
253 memset(tmp2, 0, sizeof(tmp2));
255 silc_hash_table_list_reset(&htl);
258 /* Encode the file */
259 if (silc_buffer_sformat(stack, &buf,
260 SILC_STR_UI32_STRING(SILC_SSH_PUBLIC_KEY_BEGIN),
261 SILC_STR_UI32_STRING("\n"),
262 SILC_STR_UI_XNSTRING(silc_buffer_data(&fields),
263 silc_buffer_len(&fields)),
264 SILC_STR_UI_XNSTRING(key, key_len),
265 SILC_STR_UI32_STRING("\n"),
266 SILC_STR_UI32_STRING(SILC_SSH_PUBLIC_KEY_END),
267 SILC_STR_UI32_STRING("\n"),
269 silc_buffer_spurge(stack, &fields);
270 silc_sfree(stack, key);
276 case SILC_SSH_KEY_OPENSSH:
277 /* OpenSSH style public key file */
279 if (!strcmp(pubkey->pkcs->name, "rsa"))
280 silc_snprintf(tmp, sizeof(tmp), "ssh-rsa ");
281 else if (!strcmp(pubkey->pkcs->name, "dsa"))
282 silc_snprintf(tmp, sizeof(tmp), "ssh-dss ");
285 value = (char *)silc_ssh_public_key_get_field(pubkey, "Subject");
287 /* Encode the file */
288 if (silc_buffer_sformat(stack, &buf,
289 SILC_STR_UI32_STRING(tmp),
290 SILC_STR_UI_XNSTRING(key, key_len),
291 SILC_STR_UI32_STRING(" "),
292 SILC_STR_UI32_STRING(value),
293 SILC_STR_UI32_STRING("\n"),
295 silc_buffer_spurge(stack, &buf);
296 silc_sfree(stack, key);
303 silc_sfree(stack, key);
308 silc_sfree(stack, key);
309 key = silc_buffer_steal(&buf, ret_len);
311 silc_buffer_spurge(stack, &fields);
316 /* Export public key */
318 SILC_PKCS_EXPORT_PUBLIC_KEY(silc_pkcs_ssh_export_public_key)
320 return silc_ssh_public_key_encode(stack, public_key, ret_len);
323 /* Return key length in bits */
325 SILC_PKCS_PUBLIC_KEY_BITLEN(silc_pkcs_ssh_public_key_bitlen)
327 SilcSshPublicKey pubkey = public_key;
328 return pubkey->pkcs->public_key_bitlen(pubkey->pkcs, pubkey->public_key);
331 /* Copy public key */
333 SILC_PKCS_PUBLIC_KEY_COPY(silc_pkcs_ssh_public_key_copy)
335 SilcSshPublicKey pubkey = public_key, new_pubkey;
336 SilcHashTableList htl;
339 new_pubkey = silc_calloc(1, sizeof(*new_pubkey));
342 new_pubkey->pkcs = pubkey->pkcs;
343 new_pubkey->type = pubkey->type;
345 new_pubkey->public_key =
346 pubkey->pkcs->public_key_copy(pubkey->pkcs, pubkey->public_key);
347 if (!new_pubkey->public_key) {
348 silc_free(new_pubkey);
352 if (pubkey->fields) {
353 new_pubkey->fields = silc_ssh_allocate_fields();
354 if (!new_pubkey->fields) {
355 pubkey->pkcs->public_key_free(pubkey->pkcs, pubkey->public_key);
356 silc_free(new_pubkey);
360 silc_hash_table_list(pubkey->fields, &htl);
361 while (silc_hash_table_get(&htl, (void *)&field, (void *)&value))
362 silc_hash_table_add(new_pubkey->fields, strdup(field), strdup(value));
363 silc_hash_table_list_reset(&htl);
369 /* Compare two public keys */
371 SILC_PKCS_PUBLIC_KEY_COMPARE(silc_pkcs_ssh_public_key_compare)
373 SilcSshPublicKey k1 = key1, k2 = key2;
374 SilcHashTableList htl;
375 char *field, *value, *value2;
377 if (strcmp(k1->pkcs->name, k2->pkcs->name))
380 if (k1->fields && !k2->fields)
382 if (!k1->fields && k2->fields)
385 if (k1->fields && k2->fields) {
386 if (silc_hash_table_count(k1->fields) != silc_hash_table_count(k2->fields))
389 silc_hash_table_list(k1->fields, &htl);
390 while (silc_hash_table_get(&htl, (void *)&field, (void *)&value)) {
391 value2 = (char *)silc_ssh_public_key_get_field(k2, field);
394 if (strcmp(value, value2))
397 silc_hash_table_list_reset(&htl);
400 return k1->pkcs->public_key_compare(k1->pkcs, k1->public_key,
404 /* Free public key */
406 SILC_PKCS_PUBLIC_KEY_FREE(silc_pkcs_ssh_public_key_free)
408 silc_ssh_public_key_free(public_key);
411 /* Import private key file. Supports only OpenSSH (OpenSSL to be exact)
412 private key files. */
414 SILC_PKCS_IMPORT_PRIVATE_KEY_FILE(silc_pkcs_ssh_import_private_key_file)
416 const SilcPKCSAlgorithm *alg;
417 SilcSshPrivateKey privkey = NULL;
418 SilcHashTable fields;
419 SilcBufferStruct keybuf, line;
420 unsigned char *data, iv[8], key[32];
422 char *proctype, *dekinfo;
427 SILC_LOG_DEBUG(("Parsing SSH2 private key file"));
429 if (!ret_private_key)
431 if (encoding == SILC_PKCS_FILE_BIN)
434 silc_buffer_set(&keybuf, filedata, filedata_len);
436 /* Check for private key markers */
437 if (!silc_ssh_parse_line(&keybuf, &line, TRUE)) {
438 SILC_LOG_DEBUG(("Malformed SSH2 private key markers"));
441 if ((silc_buffer_len(&keybuf) < (strlen(SILC_SSH_RSA_BEGIN) +
442 strlen(SILC_SSH_RSA_END))) ||
443 (strncmp(silc_buffer_data(&line), SILC_SSH_RSA_BEGIN,
444 silc_buffer_len(&line)) &&
445 strncmp(silc_buffer_data(&line), SILC_SSH_DSA_BEGIN,
446 silc_buffer_len(&line)))) {
447 SILC_LOG_DEBUG(("Malformed SSH2 private key markers"));
451 /* Get PKCS algorithm */
452 if (!strncmp(silc_buffer_data(&line), SILC_SSH_RSA_BEGIN,
453 silc_buffer_len(&line))) {
454 alg = silc_pkcs_find_algorithm("rsa", "ssh");
456 SILC_LOG_ERROR(("Unsupported PKCS algorithm rsa/ssh"));
459 } else if (!strncmp(silc_buffer_data(&line), SILC_SSH_DSA_BEGIN,
460 silc_buffer_len(&line))) {
461 alg = silc_pkcs_find_algorithm("dsa", "ssh");
463 SILC_LOG_ERROR(("Unsupported PKCS algorithm dsa/ssh"));
469 type = SILC_SSH_KEY_OPENSSH;
470 filedata = silc_buffer_data(&keybuf);
472 /* Skip end marker */
473 if (!silc_buffer_strchr(&keybuf, '-', FALSE)) {
474 SILC_LOG_DEBUG(("Malformed SSH2 private key markers"));
477 filedata_len = silc_buffer_data(&keybuf) - filedata;
478 silc_buffer_set(&keybuf, filedata, filedata_len);
480 /* Parse private key headers. They define how the private key has been
482 fields = silc_ssh_parse_headers(&keybuf);
486 /* Skip empty line after headers */
487 if (silc_hash_table_count(fields) > 0)
488 silc_ssh_parse_line(&keybuf, NULL, TRUE);
490 filedata = silc_buffer_data(&keybuf);
491 filedata_len = silc_buffer_len(&keybuf);
494 data = silc_base64_decode(NULL, filedata, filedata_len, &filedata_len);
496 SILC_LOG_DEBUG(("Malformed SSH2 private key"));
501 SILC_LOG_DEBUG(("Private key is %s", (silc_hash_table_count(fields) ?
502 "encrypted" : "not encrypted")));
504 if (silc_hash_table_count(fields) > 0 && passphrase) {
507 /* Get encryption info */
508 if (!silc_hash_table_find(fields, "Proc-Type", NULL, (void *)&proctype)) {
509 SILC_LOG_ERROR(("Malformed SSH2 private key"));
512 if (strcmp(proctype, "4,ENCRYPTED")) {
513 SILC_LOG_ERROR(("Malformed SSH2 private key"));
517 /* OpenSSH uses 3DES-EDE only */
518 if (!silc_hash_table_find(fields, "DEK-Info", NULL, (void *)&dekinfo)) {
519 SILC_LOG_ERROR(("Malformed SSH2 private key"));
522 if (strncmp(dekinfo, "DES-EDE3-CBC", strlen("DES-EDE3-CBC"))) {
523 SILC_LOG_ERROR(("Unsupported SSH2 private key cipher '%s'", dekinfo));
527 /* Allocate cipher */
528 if (!silc_cipher_alloc("3des-168-cbc", &des)) {
529 SILC_LOG_ERROR(("Unsupported algorithm 3des-168-cbc"));
534 if (!silc_hash_alloc("md5", &md5)) {
535 SILC_LOG_ERROR(("Unsupported hash algorithm md5"));
539 /* Get IV from private key file */
540 dekinfo = strchr(dekinfo, ',');
541 if (!dekinfo || strlen(dekinfo) < 16) {
542 SILC_LOG_ERROR(("Malformed SSH2 private key"));
546 silc_hex2data(dekinfo, iv, sizeof(iv), NULL);
548 /* Generate key from passphrase and IV as salt. The passphrase is
549 hashed with the IV, then rehashed with the previous hash, passphrase
550 and the IV to produce the final key, which is the concatenation of
553 silc_hash_update(md5, passphrase, passphrase_len);
554 silc_hash_update(md5, iv, 8);
555 silc_hash_final(md5, key);
557 silc_hash_update(md5, key, 16);
558 silc_hash_update(md5, passphrase, passphrase_len);
559 silc_hash_update(md5, iv, 8);
560 silc_hash_final(md5, key + 16);
563 silc_cipher_set_key(des, key, 192, FALSE);
564 if (!silc_cipher_decrypt(des, filedata, filedata, filedata_len, iv)) {
565 SILC_LOG_ERROR(("Malformed SSH2 private key"));
566 silc_cipher_free(des);
571 silc_cipher_free(des);
575 /* Decode the private key */
576 ret = silc_pkcs_ssh_import_private_key(pkcs, alg, filedata, filedata_len,
577 (void *)&privkey, ret_alg);
581 privkey->fields = fields;
582 privkey->type = type;
583 *ret_private_key = privkey;
584 SILC_LOG_DEBUG(("SSH2 private key file imported successfully"));
590 silc_hash_table_free(fields);
594 /* Import private key. The key format for RSA is PKCS#1 compliant and for
595 DSA is equivalent to our DSA implementation, so we just simply call the
596 algorithm specific import function to do the magic. */
598 SILC_PKCS_IMPORT_PRIVATE_KEY(silc_pkcs_ssh_import_private_key)
600 SilcSshPrivateKey privkey;
603 if (!ret_private_key || !alg)
606 /* Allocate SSH private key context */
607 privkey = silc_calloc(1, sizeof(*privkey));
611 /* Import PKCS algorithm private key */
612 ret = alg->import_private_key(alg, key, key_len, &privkey->private_key);
619 privkey->type = SILC_SSH_KEY_OPENSSH;
621 *ret_private_key = privkey;
628 /* Export private key file */
630 SILC_PKCS_EXPORT_PRIVATE_KEY_FILE(silc_pkcs_ssh_export_private_key_file)
632 SilcSshPrivateKey privkey = private_key;
633 const SilcPKCSAlgorithm *alg = privkey->pkcs;
634 SilcBufferStruct buf;
635 unsigned char *key, *keyenc, ivdata[8], iv[16 + 1], enc[32];
636 SilcUInt32 key_len, pad_len;
637 SilcCipher des = NULL;
640 SILC_LOG_DEBUG(("Encode SSH2 private key file"));
642 /* Export the private key */
643 key = silc_pkcs_ssh_export_private_key(pkcs, stack, private_key, &key_len);
647 memset(&buf, 0, sizeof(buf));
648 if (!strcmp(alg->name, "rsa")) {
649 if (silc_buffer_sformat(stack, &buf,
651 SILC_STR_UI32_STRING(SILC_SSH_RSA_BEGIN),
652 SILC_STR_UI32_STRING("\n"),
655 } else if (!strcmp(alg->name, "dsa")) {
656 if (silc_buffer_sformat(stack, &buf,
658 SILC_STR_UI32_STRING(SILC_SSH_DSA_BEGIN),
659 SILC_STR_UI32_STRING("\n"),
665 if (passphrase && strlen(passphrase) > 0) {
666 /* Encrypt the key */
668 /* Allocate cipher */
669 if (!silc_cipher_alloc("3des-168-cbc", &des)) {
670 SILC_LOG_ERROR(("Unsupported algorithm 3des-168-cbc"));
675 if (!silc_hash_alloc("md5", &md5)) {
676 SILC_LOG_ERROR(("Unsupported hash algorithm md5"));
681 silc_rng_get_rn_data(rng, sizeof(ivdata), ivdata, sizeof(ivdata));
682 silc_data2hex(ivdata, sizeof(ivdata), iv, sizeof(iv));
685 if (silc_buffer_sformat(stack, &buf,
687 SILC_STR_UI32_STRING("Proc-Type: 4,ENCRYPTED\n"),
688 SILC_STR_UI32_STRING("DEK-Info: DES-EDE3-CBC,"),
689 SILC_STR_UI32_STRING(iv),
690 SILC_STR_UI32_STRING("\n\n"),
694 /* Generate key from passphrase and IV as salt. The passphrase is
695 hashed with the IV, then rehashed with the previous hash, passphrase
696 and the IV to produce the final key, which is the concatenation of
699 silc_hash_update(md5, passphrase, passphrase_len);
700 silc_hash_update(md5, ivdata, 8);
701 silc_hash_final(md5, enc);
703 silc_hash_update(md5, enc, 16);
704 silc_hash_update(md5, passphrase, passphrase_len);
705 silc_hash_update(md5, ivdata, 8);
706 silc_hash_final(md5, enc + 16);
709 pad_len = (-key_len) % 8;
711 keyenc = silc_smalloc(stack, (key_len + pad_len) * sizeof(*keyenc));
714 memset(keyenc + key_len, 'F', pad_len);
715 memcpy(keyenc, key, key_len);
717 keyenc = silc_smemdup(stack, key, key_len);
723 silc_cipher_set_key(des, enc, 192, TRUE);
724 silc_cipher_encrypt(des, keyenc, keyenc, key_len + pad_len, ivdata);
726 silc_sfree(stack, key);
730 silc_cipher_free(des);
735 keyenc = silc_base64_encode_file(stack, key, key_len);
739 silc_sfree(stack, key);
741 key_len = strlen(keyenc);
743 /* Encode rest of the public key */
744 if (!strcmp(alg->name, "rsa")) {
745 if (silc_buffer_sformat(stack, &buf,
747 SILC_STR_DATA(key, key_len),
748 SILC_STR_UI32_STRING("\n"),
749 SILC_STR_UI32_STRING(SILC_SSH_RSA_END),
750 SILC_STR_UI32_STRING("\n"),
753 } else if (!strcmp(alg->name, "dsa")) {
754 if (silc_buffer_sformat(stack, &buf,
756 SILC_STR_DATA(key, key_len),
757 SILC_STR_UI32_STRING("\n"),
758 SILC_STR_UI32_STRING(SILC_SSH_DSA_END),
759 SILC_STR_UI32_STRING("\n"),
764 silc_sfree(stack, key);
765 key = silc_buffer_steal(&buf, ret_len);
770 silc_cipher_free(des);
773 silc_sfree(stack, key);
777 /* Export private key */
779 SILC_PKCS_EXPORT_PRIVATE_KEY(silc_pkcs_ssh_export_private_key)
781 SilcSshPrivateKey privkey = private_key;
782 const SilcPKCSAlgorithm *alg = privkey->pkcs;
784 SILC_LOG_DEBUG(("Encode SSH2 private key"));
786 /* Export PKCS algorithm private key */
787 if (alg->export_private_key)
788 return alg->export_private_key(alg, stack,
789 privkey->private_key, ret_len);
793 /* Return key length in bits */
795 SILC_PKCS_PRIVATE_KEY_BITLEN(silc_pkcs_ssh_private_key_bitlen)
797 SilcSshPrivateKey privkey = private_key;
798 return privkey->pkcs->private_key_bitlen(privkey->pkcs,
799 privkey->private_key);
802 /* Free private key */
804 SILC_PKCS_PRIVATE_KEY_FREE(silc_pkcs_ssh_private_key_free)
806 SilcSshPrivateKey privkey = private_key;
808 privkey->pkcs->private_key_free(privkey->pkcs,
809 privkey->private_key);
812 silc_hash_table_free(privkey->fields);
818 SILC_PKCS_ENCRYPT(silc_pkcs_ssh_encrypt)
820 SilcSshPublicKey pubkey = public_key;
822 if (!pubkey->pkcs->encrypt) {
823 encrypt_cb(FALSE, NULL, 0, context);
827 return pubkey->pkcs->encrypt(pubkey->pkcs, pubkey->public_key,
828 src, src_len, rng, encrypt_cb, context);
833 SILC_PKCS_DECRYPT(silc_pkcs_ssh_decrypt)
835 SilcSshPrivateKey privkey = private_key;
837 if (!privkey->pkcs->decrypt) {
838 decrypt_cb(FALSE, NULL, 0, context);
842 return privkey->pkcs->decrypt(privkey->pkcs, privkey->private_key,
843 src, src_len, decrypt_cb, context);
849 SilcSshPrivateKey privkey;
850 SilcPKCSSignCb sign_cb;
854 /* Sign callback. This formats the signature into SSH2 protocool compliant
857 static void silc_pkcs_ssh_sign_cb(SilcBool success,
858 const unsigned char *signature,
859 SilcUInt32 signature_len,
862 SilcSshSign sign = context;
863 SilcStack stack = sign->stack;
864 unsigned char rbuf[20], sbuf[20];
865 SilcBufferStruct sig;
869 memset(&sig, 0, sizeof(sig));
871 /* Format the signature. RSA is easy because PKCS#1 is already in
872 correct format. For DSA the returned signature is in PKIX compliant
873 format and we have to reformat it for SSH2. */
874 if (!strcmp(sign->privkey->pkcs->name, "dsa")) {
875 asn1 = silc_asn1_alloc(stack);
877 sign->sign_cb(FALSE, NULL, 0, sign->context);
878 silc_sfree(stack, sign);
879 silc_stack_free(stack);
883 /* Decode the signature */
884 silc_buffer_set(&sig, (unsigned char *)signature, signature_len);
885 if (!silc_asn1_decode(asn1, &sig,
889 SILC_ASN1_END, SILC_ASN1_END)) {
890 sign->sign_cb(FALSE, NULL, 0, sign->context);
891 silc_asn1_free(asn1);
892 silc_sfree(stack, sign);
893 silc_stack_free(stack);
897 /* Encode the integers */
898 memset(rbuf, 0, sizeof(rbuf));
899 memset(sbuf, 0, sizeof(sbuf));
900 silc_mp_mp2bin_noalloc(&r, rbuf, sizeof(rbuf));
901 silc_mp_mp2bin_noalloc(&s, sbuf, sizeof(sbuf));
903 silc_asn1_free(asn1);
905 /* Encode SSH2 DSS signature */
906 if (silc_buffer_sformat(stack, &sig,
908 SILC_STR_UI32_STRING("ssh-dss"),
909 SILC_STR_UI_INT(sizeof(rbuf) + sizeof(sbuf)),
910 SILC_STR_DATA(rbuf, sizeof(rbuf)),
911 SILC_STR_DATA(sbuf, sizeof(sbuf)),
913 sign->sign_cb(FALSE, NULL, 0, sign->context);
914 silc_sfree(stack, sign);
915 silc_stack_free(stack);
919 /* Encode SSH2 RSA signature */
920 if (silc_buffer_sformat(stack, &sig,
922 SILC_STR_UI32_STRING("ssh-rsa"),
923 SILC_STR_UI_INT(signature_len),
924 SILC_STR_DATA(signature, signature_len),
926 sign->sign_cb(FALSE, NULL, 0, sign->context);
927 silc_sfree(stack, sign);
928 silc_stack_free(stack);
934 sign->sign_cb(TRUE, silc_buffer_data(&sig), silc_buffer_len(&sig),
937 silc_buffer_spurge(stack, &sig);
938 silc_sfree(stack, sign);
939 silc_stack_free(stack);
944 SILC_PKCS_SIGN(silc_pkcs_ssh_sign)
946 SilcSshPrivateKey privkey = private_key;
950 if (!privkey->pkcs->sign) {
951 sign_cb(FALSE, NULL, 0, context);
955 stack = silc_stack_alloc(0, silc_crypto_stack());
957 sign = silc_scalloc(stack, 1, sizeof(*sign));
959 sign_cb(FALSE, NULL, 0, context);
960 silc_stack_free(stack);
965 sign->privkey = privkey;
966 sign->sign_cb = sign_cb;
967 sign->context = context;
969 /* Sign. The callback will format it to SSH2 compliant format. */
970 return privkey->pkcs->sign(privkey->pkcs, privkey->private_key,
972 compute_hash, hash, rng,
973 silc_pkcs_ssh_sign_cb, sign);
978 SILC_PKCS_VERIFY(silc_pkcs_ssh_verify)
980 SilcSshPublicKey pubkey = public_key;
981 SilcAsyncOperation op;
982 SilcBufferStruct sig, r, s;
983 unsigned char *signame;
984 SilcStack stack = NULL;
987 if (!pubkey->pkcs->verify) {
988 verify_cb(FALSE, context);
992 /* Decode the SSH2 protocol style signature encoding. */
993 silc_buffer_set(&sig, signature, signature_len);
994 if (silc_buffer_unformat(&sig,
995 SILC_STR_UI32_STRING_ALLOC(&signame),
996 SILC_STR_UI32_NSTRING(&signature, &signature_len),
998 verify_cb(FALSE, context);
1001 memset(&sig, 0, sizeof(sig));
1003 /* DSS signature must be formatted to PKIX compliant format since our
1004 implementation expects that. */
1005 if (!strcmp(signame, "ssh-dss")) {
1006 /* The integers must be 160 bits each */
1007 if (signature_len != 40) {
1008 verify_cb(FALSE, context);
1013 silc_buffer_set(&r, signature, 20);
1014 silc_buffer_set(&s, signature + 20, 20);
1016 stack = silc_stack_alloc(0, silc_crypto_stack());
1018 asn1 = silc_asn1_alloc(stack);
1020 verify_cb(FALSE, context);
1022 silc_stack_free(stack);
1026 /* Encode signature to PKIX compliant format. */
1027 if (!silc_asn1_encode(asn1, &sig,
1028 SILC_ASN1_OPTS(SILC_ASN1_ALLOC),
1030 SILC_ASN1_ANY_PRIMITIVE(SILC_ASN1_TAG_INTEGER, &r),
1031 SILC_ASN1_ANY_PRIMITIVE(SILC_ASN1_TAG_INTEGER, &s),
1032 SILC_ASN1_END, SILC_ASN1_END)) {
1033 verify_cb(FALSE, context);
1035 silc_asn1_free(asn1);
1036 silc_stack_free(stack);
1040 signature = silc_buffer_steal(&sig, &signature_len);
1042 silc_asn1_free(asn1);
1046 op = pubkey->pkcs->verify(pubkey->pkcs, pubkey->public_key,
1047 signature, signature_len,
1048 data, data_len, hash, rng,
1049 verify_cb, context);
1052 silc_buffer_spurge(stack, &sig);
1053 silc_stack_free(stack);
1058 /************************** SSH2 PKCS RSA Alg API ***************************/
1060 /* The SSH2 RSA PKCS Algorithm API. We implement here only the necessary
1061 parts of the API and the common code is used from PKCS#1 Algorithm API
1062 in silccrypt/silcpkcs1.c. Basically everything else is PKCS#1 except
1063 the format of the public key. */
1065 /* Import RSA public key. Both RFC 4716 and OpenSSH have same format. */
1067 SILC_PKCS_ALG_IMPORT_PUBLIC_KEY(silc_ssh_rsa_import_public_key)
1069 SilcBufferStruct alg_key;
1070 RsaPublicKey *pubkey;
1071 unsigned char *n, *e;
1072 SilcUInt32 n_len, e_len;
1075 SILC_LOG_DEBUG(("Import public key"));
1077 if (!ret_public_key)
1080 /* Allocate RSA public key */
1081 *ret_public_key = pubkey = silc_calloc(1, sizeof(*pubkey));
1085 /* Parse SSH2 RSA public key */
1086 silc_buffer_set(&alg_key, key, key_len);
1087 ret = silc_buffer_unformat(&alg_key,
1088 SILC_STR_UI32_NSTRING(&e, &e_len),
1089 SILC_STR_UI32_NSTRING(&n, &n_len),
1093 if (!n_len || !e_len)
1096 /* Get MP integers */
1097 silc_mp_init(&pubkey->n);
1098 silc_mp_init(&pubkey->e);
1099 silc_mp_bin2mp(n, n_len, &pubkey->n);
1100 silc_mp_bin2mp(e, e_len, &pubkey->e);
1102 /* Set key length */
1103 pubkey->bits = ((silc_mp_sizeinbase(&pubkey->n, 2) + 7) / 8) * 8;
1112 /* Export RSA public key. Both RFC 4716 and OpenSSH have same format. */
1114 SILC_PKCS_ALG_EXPORT_PUBLIC_KEY(silc_ssh_rsa_export_public_key)
1116 RsaPublicKey *pubkey = public_key;
1117 SilcBufferStruct alg_key;
1118 unsigned char *n = NULL, *e = NULL, *ret;
1119 SilcUInt32 n_len, e_len;
1121 SILC_LOG_DEBUG(("Export public key"));
1123 /* Encode MP integers */
1124 n = silc_mp_mp2bin(&pubkey->n, 0, &n_len);
1127 e = silc_mp_mp2bin(&pubkey->e, 0, &e_len);
1131 /* Encode SSH2 RSA public key */
1132 memset(&alg_key, 0, sizeof(alg_key));
1133 if (silc_buffer_sformat(stack, &alg_key,
1134 SILC_STR_UI_INT(e_len),
1135 SILC_STR_DATA(e, e_len),
1136 SILC_STR_UI_INT(n_len),
1137 SILC_STR_DATA(n, n_len),
1144 ret = silc_buffer_steal(&alg_key, ret_len);
1153 /************************** SSH2 PKCS DSA Alg API ***************************/
1155 /* The SSH2 DSA PKCS Algorithm API. We implement here only the necessary
1156 parts of the API and the common code is used from DSS Algorithm API
1157 in silccrypt/dsa.c. */
1159 /* Import DSA public key. Both RFC 4716 and OpenSSH have same format. */
1161 SILC_PKCS_ALG_IMPORT_PUBLIC_KEY(silc_ssh_dsa_import_public_key)
1163 SilcBufferStruct alg_key;
1164 DsaPublicKey *pubkey;
1165 unsigned char *p, *q, *g, *y;
1166 SilcUInt32 p_len, q_len, g_len, y_len;
1169 SILC_LOG_DEBUG(("Import public key"));
1171 if (!ret_public_key)
1174 /* Allocate DSA public key */
1175 *ret_public_key = pubkey = silc_calloc(1, sizeof(*pubkey));
1179 /* Parse SSH2 DSA public key */
1180 silc_buffer_set(&alg_key, key, key_len);
1181 ret = silc_buffer_unformat(&alg_key,
1182 SILC_STR_UI32_NSTRING(&p, &p_len),
1183 SILC_STR_UI32_NSTRING(&q, &q_len),
1184 SILC_STR_UI32_NSTRING(&g, &g_len),
1185 SILC_STR_UI32_NSTRING(&y, &y_len),
1189 if (!p_len || !q_len || !g_len || !y_len)
1192 /* Get MP integers */
1193 silc_mp_init(&pubkey->p);
1194 silc_mp_init(&pubkey->q);
1195 silc_mp_init(&pubkey->g);
1196 silc_mp_init(&pubkey->y);
1197 silc_mp_bin2mp(p, p_len, &pubkey->p);
1198 silc_mp_bin2mp(q, q_len, &pubkey->q);
1199 silc_mp_bin2mp(g, g_len, &pubkey->g);
1200 silc_mp_bin2mp(y, y_len, &pubkey->y);
1202 /* Set key length */
1203 pubkey->bits = ((silc_mp_sizeinbase(&pubkey->p, 2) + 7) / 8) * 8;
1212 /* Export DSA public key. Both RFC 4716 and OpenSSH have same format. */
1214 SILC_PKCS_ALG_EXPORT_PUBLIC_KEY(silc_ssh_dsa_export_public_key)
1216 DsaPublicKey *pubkey = public_key;
1217 SilcBufferStruct alg_key;
1218 unsigned char *p = NULL, *q = NULL, *g = NULL, *y = NULL, *ret;
1219 SilcUInt32 p_len, q_len, g_len, y_len;
1221 SILC_LOG_DEBUG(("Export public key"));
1223 /* Encode MP integers */
1224 p = silc_mp_mp2bin(&pubkey->p, 0, &p_len);
1227 q = silc_mp_mp2bin(&pubkey->q, 0, &q_len);
1230 g = silc_mp_mp2bin(&pubkey->g, 0, &g_len);
1233 y = silc_mp_mp2bin(&pubkey->y, 0, &y_len);
1237 /* Encode SSH2 DSA public key */
1238 memset(&alg_key, 0, sizeof(alg_key));
1239 if (silc_buffer_sformat(stack, &alg_key,
1240 SILC_STR_UI_INT(p_len),
1241 SILC_STR_DATA(p, p_len),
1242 SILC_STR_UI_INT(q_len),
1243 SILC_STR_DATA(q, q_len),
1244 SILC_STR_UI_INT(g_len),
1245 SILC_STR_DATA(g, g_len),
1246 SILC_STR_UI_INT(y_len),
1247 SILC_STR_DATA(y, y_len),
1256 ret = silc_buffer_steal(&alg_key, ret_len);