5 Author: Pekka Riikonen <priikone@silcnet.org>
7 Copyright (C) 2007 - 2008 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.
20 #include "silccrypto.h"
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, NULL, 0, filedata,
577 filedata_len, (void *)&privkey,
582 privkey->fields = fields;
583 privkey->type = type;
584 *ret_private_key = privkey;
585 SILC_LOG_DEBUG(("SSH2 private key file imported successfully"));
591 silc_hash_table_free(fields);
595 /* Import private key. The key format for RSA is PKCS#1 compliant and for
596 DSA is equivalent to our DSA implementation, so we just simply call the
597 algorithm specific import function to do the magic. */
599 SILC_PKCS_IMPORT_PRIVATE_KEY(silc_pkcs_ssh_import_private_key)
601 SilcSshPrivateKey privkey;
604 if (!ret_private_key || !alg)
607 /* Allocate SSH private key context */
608 privkey = silc_calloc(1, sizeof(*privkey));
612 /* Import PKCS algorithm private key */
613 ret = alg->import_private_key(alg, key, key_len, &privkey->private_key);
620 privkey->type = SILC_SSH_KEY_OPENSSH;
622 *ret_private_key = privkey;
629 /* Export private key file */
631 SILC_PKCS_EXPORT_PRIVATE_KEY_FILE(silc_pkcs_ssh_export_private_key_file)
633 SilcSshPrivateKey privkey = private_key;
634 const SilcPKCSAlgorithm *alg = privkey->pkcs;
635 SilcBufferStruct buf;
636 unsigned char *key, *keyenc, ivdata[8], iv[16 + 1], enc[32];
637 SilcUInt32 key_len, pad_len;
638 SilcCipher des = NULL;
641 SILC_LOG_DEBUG(("Encode SSH2 private key file"));
643 /* Export the private key */
644 key = silc_pkcs_ssh_export_private_key(pkcs, stack, private_key, &key_len);
648 memset(&buf, 0, sizeof(buf));
649 if (!strcmp(alg->name, "rsa")) {
650 if (silc_buffer_sformat(stack, &buf,
652 SILC_STR_UI32_STRING(SILC_SSH_RSA_BEGIN),
653 SILC_STR_UI32_STRING("\n"),
656 } else if (!strcmp(alg->name, "dsa")) {
657 if (silc_buffer_sformat(stack, &buf,
659 SILC_STR_UI32_STRING(SILC_SSH_DSA_BEGIN),
660 SILC_STR_UI32_STRING("\n"),
666 if (passphrase && strlen(passphrase) > 0) {
667 /* Encrypt the key */
669 /* Allocate cipher */
670 if (!silc_cipher_alloc("3des-168-cbc", &des)) {
671 SILC_LOG_ERROR(("Unsupported algorithm 3des-168-cbc"));
676 if (!silc_hash_alloc("md5", &md5)) {
677 SILC_LOG_ERROR(("Unsupported hash algorithm md5"));
682 silc_rng_get_rn_data(rng, sizeof(ivdata), ivdata, sizeof(ivdata));
683 silc_data2hex(ivdata, sizeof(ivdata), iv, sizeof(iv));
686 if (silc_buffer_sformat(stack, &buf,
688 SILC_STR_UI32_STRING("Proc-Type: 4,ENCRYPTED\n"),
689 SILC_STR_UI32_STRING("DEK-Info: DES-EDE3-CBC,"),
690 SILC_STR_UI32_STRING(iv),
691 SILC_STR_UI32_STRING("\n\n"),
695 /* Generate key from passphrase and IV as salt. The passphrase is
696 hashed with the IV, then rehashed with the previous hash, passphrase
697 and the IV to produce the final key, which is the concatenation of
700 silc_hash_update(md5, passphrase, passphrase_len);
701 silc_hash_update(md5, ivdata, 8);
702 silc_hash_final(md5, enc);
704 silc_hash_update(md5, enc, 16);
705 silc_hash_update(md5, passphrase, passphrase_len);
706 silc_hash_update(md5, ivdata, 8);
707 silc_hash_final(md5, enc + 16);
710 pad_len = (-key_len) % 8;
712 keyenc = silc_smalloc(stack, (key_len + pad_len) * sizeof(*keyenc));
715 memset(keyenc + key_len, 'F', pad_len);
716 memcpy(keyenc, key, key_len);
718 keyenc = silc_smemdup(stack, key, key_len);
724 silc_cipher_set_key(des, enc, 192, TRUE);
725 silc_cipher_encrypt(des, keyenc, keyenc, key_len + pad_len, ivdata);
727 silc_sfree(stack, key);
731 silc_cipher_free(des);
736 keyenc = silc_base64_encode_file(stack, key, key_len);
740 silc_sfree(stack, key);
742 key_len = strlen(keyenc);
744 /* Encode rest of the public key */
745 if (!strcmp(alg->name, "rsa")) {
746 if (silc_buffer_sformat(stack, &buf,
748 SILC_STR_DATA(key, key_len),
749 SILC_STR_UI32_STRING("\n"),
750 SILC_STR_UI32_STRING(SILC_SSH_RSA_END),
751 SILC_STR_UI32_STRING("\n"),
754 } else if (!strcmp(alg->name, "dsa")) {
755 if (silc_buffer_sformat(stack, &buf,
757 SILC_STR_DATA(key, key_len),
758 SILC_STR_UI32_STRING("\n"),
759 SILC_STR_UI32_STRING(SILC_SSH_DSA_END),
760 SILC_STR_UI32_STRING("\n"),
765 silc_sfree(stack, key);
766 key = silc_buffer_steal(&buf, ret_len);
771 silc_cipher_free(des);
774 silc_sfree(stack, key);
778 /* Export private key */
780 SILC_PKCS_EXPORT_PRIVATE_KEY(silc_pkcs_ssh_export_private_key)
782 SilcSshPrivateKey privkey = private_key;
783 const SilcPKCSAlgorithm *alg = privkey->pkcs;
785 SILC_LOG_DEBUG(("Encode SSH2 private key"));
787 /* Export PKCS algorithm private key */
788 if (alg->export_private_key)
789 return alg->export_private_key(alg, stack,
790 privkey->private_key, ret_len);
794 /* Return key length in bits */
796 SILC_PKCS_PRIVATE_KEY_BITLEN(silc_pkcs_ssh_private_key_bitlen)
798 SilcSshPrivateKey privkey = private_key;
799 return privkey->pkcs->private_key_bitlen(privkey->pkcs,
800 privkey->private_key);
803 /* Free private key */
805 SILC_PKCS_PRIVATE_KEY_FREE(silc_pkcs_ssh_private_key_free)
807 SilcSshPrivateKey privkey = private_key;
809 privkey->pkcs->private_key_free(privkey->pkcs,
810 privkey->private_key);
813 silc_hash_table_free(privkey->fields);
819 SILC_PKCS_ENCRYPT(silc_pkcs_ssh_encrypt)
821 SilcSshPublicKey pubkey = public_key;
823 if (!pubkey->pkcs->encrypt) {
824 encrypt_cb(FALSE, NULL, 0, context);
828 return pubkey->pkcs->encrypt(pubkey->pkcs, pubkey->public_key,
829 src, src_len, rng, encrypt_cb, context);
834 SILC_PKCS_DECRYPT(silc_pkcs_ssh_decrypt)
836 SilcSshPrivateKey privkey = private_key;
838 if (!privkey->pkcs->decrypt) {
839 decrypt_cb(FALSE, NULL, 0, context);
843 return privkey->pkcs->decrypt(privkey->pkcs, privkey->private_key,
844 src, src_len, decrypt_cb, context);
850 SilcSshPrivateKey privkey;
851 SilcPKCSSignCb sign_cb;
855 /* Sign callback. This formats the signature into SSH2 protocool compliant
858 static void silc_pkcs_ssh_sign_cb(SilcBool success,
859 const unsigned char *signature,
860 SilcUInt32 signature_len,
863 SilcSshSign sign = context;
864 SilcStack stack = sign->stack;
865 unsigned char rbuf[20], sbuf[20];
866 SilcBufferStruct sig;
870 memset(&sig, 0, sizeof(sig));
872 /* Format the signature. RSA is easy because PKCS#1 is already in
873 correct format. For DSA the returned signature is in PKIX compliant
874 format and we have to reformat it for SSH2. */
875 if (!strcmp(sign->privkey->pkcs->name, "dsa")) {
876 asn1 = silc_asn1_alloc(stack);
878 sign->sign_cb(FALSE, NULL, 0, sign->context);
879 silc_sfree(stack, sign);
880 silc_stack_free(stack);
884 /* Decode the signature */
885 silc_buffer_set(&sig, (unsigned char *)signature, signature_len);
886 if (!silc_asn1_decode(asn1, &sig,
890 SILC_ASN1_END, SILC_ASN1_END)) {
891 sign->sign_cb(FALSE, NULL, 0, sign->context);
892 silc_asn1_free(asn1);
893 silc_sfree(stack, sign);
894 silc_stack_free(stack);
898 /* Encode the integers */
899 memset(rbuf, 0, sizeof(rbuf));
900 memset(sbuf, 0, sizeof(sbuf));
901 silc_mp_mp2bin_noalloc(&r, rbuf, sizeof(rbuf));
902 silc_mp_mp2bin_noalloc(&s, sbuf, sizeof(sbuf));
904 silc_asn1_free(asn1);
906 /* Encode SSH2 DSS signature */
907 if (silc_buffer_sformat(stack, &sig,
909 SILC_STR_UI32_STRING("ssh-dss"),
910 SILC_STR_UI_INT(sizeof(rbuf) + sizeof(sbuf)),
911 SILC_STR_DATA(rbuf, sizeof(rbuf)),
912 SILC_STR_DATA(sbuf, sizeof(sbuf)),
914 sign->sign_cb(FALSE, NULL, 0, sign->context);
915 silc_sfree(stack, sign);
916 silc_stack_free(stack);
920 /* Encode SSH2 RSA signature */
921 if (silc_buffer_sformat(stack, &sig,
923 SILC_STR_UI32_STRING("ssh-rsa"),
924 SILC_STR_UI_INT(signature_len),
925 SILC_STR_DATA(signature, signature_len),
927 sign->sign_cb(FALSE, NULL, 0, sign->context);
928 silc_sfree(stack, sign);
929 silc_stack_free(stack);
935 sign->sign_cb(TRUE, silc_buffer_data(&sig), silc_buffer_len(&sig),
938 silc_buffer_spurge(stack, &sig);
939 silc_sfree(stack, sign);
940 silc_stack_free(stack);
945 SILC_PKCS_SIGN(silc_pkcs_ssh_sign)
947 SilcSshPrivateKey privkey = private_key;
951 if (!privkey->pkcs->sign) {
952 sign_cb(FALSE, NULL, 0, context);
956 stack = silc_stack_alloc(0, silc_crypto_stack());
958 sign = silc_scalloc(stack, 1, sizeof(*sign));
960 sign_cb(FALSE, NULL, 0, context);
961 silc_stack_free(stack);
966 sign->privkey = privkey;
967 sign->sign_cb = sign_cb;
968 sign->context = context;
970 /* Sign. The callback will format it to SSH2 compliant format. */
971 return privkey->pkcs->sign(privkey->pkcs, privkey->private_key,
973 compute_hash, hash, rng,
974 silc_pkcs_ssh_sign_cb, sign);
979 SILC_PKCS_VERIFY(silc_pkcs_ssh_verify)
981 SilcSshPublicKey pubkey = public_key;
982 SilcAsyncOperation op;
983 SilcBufferStruct sig, r, s;
984 unsigned char *signame;
985 SilcStack stack = NULL;
988 if (!pubkey->pkcs->verify) {
989 verify_cb(FALSE, context);
993 /* Decode the SSH2 protocol style signature encoding. */
994 silc_buffer_set(&sig, signature, signature_len);
995 if (silc_buffer_unformat(&sig,
996 SILC_STR_UI32_STRING_ALLOC(&signame),
997 SILC_STR_UI32_NSTRING(&signature, &signature_len),
999 verify_cb(FALSE, context);
1002 memset(&sig, 0, sizeof(sig));
1004 /* DSS signature must be formatted to PKIX compliant format since our
1005 implementation expects that. */
1006 if (!strcmp(signame, "ssh-dss")) {
1007 /* The integers must be 160 bits each */
1008 if (signature_len != 40) {
1009 verify_cb(FALSE, context);
1014 silc_buffer_set(&r, signature, 20);
1015 silc_buffer_set(&s, signature + 20, 20);
1017 stack = silc_stack_alloc(0, silc_crypto_stack());
1019 asn1 = silc_asn1_alloc(stack);
1021 verify_cb(FALSE, context);
1023 silc_stack_free(stack);
1027 /* Encode signature to PKIX compliant format. */
1028 if (!silc_asn1_encode(asn1, &sig,
1029 SILC_ASN1_OPTS(SILC_ASN1_ALLOC),
1031 SILC_ASN1_ANY_PRIMITIVE(SILC_ASN1_TAG_INTEGER, &r),
1032 SILC_ASN1_ANY_PRIMITIVE(SILC_ASN1_TAG_INTEGER, &s),
1033 SILC_ASN1_END, SILC_ASN1_END)) {
1034 verify_cb(FALSE, context);
1036 silc_asn1_free(asn1);
1037 silc_stack_free(stack);
1041 signature = silc_buffer_steal(&sig, &signature_len);
1043 silc_asn1_free(asn1);
1047 op = pubkey->pkcs->verify(pubkey->pkcs, pubkey->public_key,
1048 signature, signature_len,
1049 data, data_len, compute_hash, hash, rng,
1050 verify_cb, context);
1053 silc_buffer_spurge(stack, &sig);
1054 silc_stack_free(stack);
1059 /************************** SSH2 PKCS RSA Alg API ***************************/
1061 /* The SSH2 RSA PKCS Algorithm API. We implement here only the necessary
1062 parts of the API and the common code is used from PKCS#1 Algorithm API
1063 in silccrypt/silcpkcs1.c. Basically everything else is PKCS#1 except
1064 the format of the public key. */
1066 /* Import RSA public key. Both RFC 4716 and OpenSSH have same format. */
1068 SILC_PKCS_ALG_IMPORT_PUBLIC_KEY(silc_ssh_rsa_import_public_key)
1070 SilcBufferStruct alg_key;
1071 RsaPublicKey *pubkey;
1072 unsigned char *n, *e;
1073 SilcUInt32 n_len, e_len;
1076 SILC_LOG_DEBUG(("Import public key"));
1078 if (!ret_public_key)
1081 /* Allocate RSA public key */
1082 *ret_public_key = pubkey = silc_calloc(1, sizeof(*pubkey));
1086 /* Parse SSH2 RSA public key */
1087 silc_buffer_set(&alg_key, key, key_len);
1088 ret = silc_buffer_unformat(&alg_key,
1089 SILC_STR_UI32_NSTRING(&e, &e_len),
1090 SILC_STR_UI32_NSTRING(&n, &n_len),
1094 if (!n_len || !e_len)
1097 /* Get MP integers */
1098 silc_mp_init(&pubkey->n);
1099 silc_mp_init(&pubkey->e);
1100 silc_mp_bin2mp(n, n_len, &pubkey->n);
1101 silc_mp_bin2mp(e, e_len, &pubkey->e);
1103 /* Set key length */
1104 pubkey->bits = ((silc_mp_sizeinbase(&pubkey->n, 2) + 7) / 8) * 8;
1113 /* Export RSA public key. Both RFC 4716 and OpenSSH have same format. */
1115 SILC_PKCS_ALG_EXPORT_PUBLIC_KEY(silc_ssh_rsa_export_public_key)
1117 RsaPublicKey *pubkey = public_key;
1118 SilcBufferStruct alg_key;
1119 unsigned char *n = NULL, *e = NULL, *ret;
1120 SilcUInt32 n_len, e_len;
1122 SILC_LOG_DEBUG(("Export public key"));
1124 /* Encode MP integers */
1125 n = silc_mp_mp2bin(&pubkey->n, 0, &n_len);
1128 e = silc_mp_mp2bin(&pubkey->e, 0, &e_len);
1132 /* Encode SSH2 RSA public key */
1133 memset(&alg_key, 0, sizeof(alg_key));
1134 if (silc_buffer_sformat(stack, &alg_key,
1135 SILC_STR_UI_INT(e_len),
1136 SILC_STR_DATA(e, e_len),
1137 SILC_STR_UI_INT(n_len),
1138 SILC_STR_DATA(n, n_len),
1145 ret = silc_buffer_steal(&alg_key, ret_len);
1154 /************************** SSH2 PKCS DSA Alg API ***************************/
1156 /* The SSH2 DSA PKCS Algorithm API. We implement here only the necessary
1157 parts of the API and the common code is used from DSS Algorithm API
1158 in silccrypt/dsa.c. */
1160 /* Import DSA public key. Both RFC 4716 and OpenSSH have same format. */
1162 SILC_PKCS_ALG_IMPORT_PUBLIC_KEY(silc_ssh_dsa_import_public_key)
1164 SilcBufferStruct alg_key;
1165 DsaPublicKey *pubkey;
1166 unsigned char *p, *q, *g, *y;
1167 SilcUInt32 p_len, q_len, g_len, y_len;
1170 SILC_LOG_DEBUG(("Import public key"));
1172 if (!ret_public_key)
1175 /* Allocate DSA public key */
1176 *ret_public_key = pubkey = silc_calloc(1, sizeof(*pubkey));
1180 /* Parse SSH2 DSA public key */
1181 silc_buffer_set(&alg_key, key, key_len);
1182 ret = silc_buffer_unformat(&alg_key,
1183 SILC_STR_UI32_NSTRING(&p, &p_len),
1184 SILC_STR_UI32_NSTRING(&q, &q_len),
1185 SILC_STR_UI32_NSTRING(&g, &g_len),
1186 SILC_STR_UI32_NSTRING(&y, &y_len),
1190 if (!p_len || !q_len || !g_len || !y_len)
1193 /* Get MP integers */
1194 silc_mp_init(&pubkey->p);
1195 silc_mp_init(&pubkey->q);
1196 silc_mp_init(&pubkey->g);
1197 silc_mp_init(&pubkey->y);
1198 silc_mp_bin2mp(p, p_len, &pubkey->p);
1199 silc_mp_bin2mp(q, q_len, &pubkey->q);
1200 silc_mp_bin2mp(g, g_len, &pubkey->g);
1201 silc_mp_bin2mp(y, y_len, &pubkey->y);
1203 /* Set key length */
1204 pubkey->bits = ((silc_mp_sizeinbase(&pubkey->p, 2) + 7) / 8) * 8;
1213 /* Export DSA public key. Both RFC 4716 and OpenSSH have same format. */
1215 SILC_PKCS_ALG_EXPORT_PUBLIC_KEY(silc_ssh_dsa_export_public_key)
1217 DsaPublicKey *pubkey = public_key;
1218 SilcBufferStruct alg_key;
1219 unsigned char *p = NULL, *q = NULL, *g = NULL, *y = NULL, *ret;
1220 SilcUInt32 p_len, q_len, g_len, y_len;
1222 SILC_LOG_DEBUG(("Export public key"));
1224 /* Encode MP integers */
1225 p = silc_mp_mp2bin(&pubkey->p, 0, &p_len);
1228 q = silc_mp_mp2bin(&pubkey->q, 0, &q_len);
1231 g = silc_mp_mp2bin(&pubkey->g, 0, &g_len);
1234 y = silc_mp_mp2bin(&pubkey->y, 0, &y_len);
1238 /* Encode SSH2 DSA public key */
1239 memset(&alg_key, 0, sizeof(alg_key));
1240 if (silc_buffer_sformat(stack, &alg_key,
1241 SILC_STR_UI_INT(p_len),
1242 SILC_STR_DATA(p, p_len),
1243 SILC_STR_UI_INT(q_len),
1244 SILC_STR_DATA(q, q_len),
1245 SILC_STR_UI_INT(g_len),
1246 SILC_STR_DATA(g, g_len),
1247 SILC_STR_UI_INT(y_len),
1248 SILC_STR_DATA(y, y_len),
1257 ret = silc_buffer_steal(&alg_key, ret_len);