+++ /dev/null
-/*
-
- silcssh_pkcs.c
-
- Author: Pekka Riikonen <priikone@silcnet.org>
-
- Copyright (C) 2007 Pekka Riikonen
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; version 2 of the License.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
-*/
-
-#include "silc.h"
-#include "rsa.h"
-#include "dsa.h"
-#include "silcssh_pkcs.h"
-
-/************************** Types and definitions ***************************/
-
-/* RFC 4716 public key file markers */
-#define SILC_SSH_PUBLIC_KEY_BEGIN "---- BEGIN SSH2 PUBLIC KEY ----"
-#define SILC_SSH_PUBLIC_KEY_END "---- END SSH2 PUBLIC KEY ----"
-
-/* OpenSSH private key file markers */
-#define SILC_SSH_RSA_BEGIN "-----BEGIN RSA PRIVATE KEY-----"
-#define SILC_SSH_RSA_END "-----END RSA PRIVATE KEY-----"
-#define SILC_SSH_DSA_BEGIN "-----BEGIN DSA PRIVATE KEY-----"
-#define SILC_SSH_DSA_END "-----END DSA PRIVATE KEY-----"
-
-/****************************** SSH2 PKCS API *******************************/
-
-/* Get algorithm context */
-
-SILC_PKCS_GET_ALGORITHM(silc_pkcs_ssh_get_algorithm)
-{
- SilcSshPublicKey pubkey = public_key;
- return pubkey->pkcs;
-}
-
-/* Import public key file */
-
-SILC_PKCS_IMPORT_PUBLIC_KEY_FILE(silc_pkcs_ssh_import_public_key_file)
-{
- SilcSshPublicKey pubkey;
- SilcBufferStruct keybuf, line;
- SilcHashTable fields;
- unsigned char *data;
- SilcSshKeyType type;
- int ret;
-
- SILC_LOG_DEBUG(("Parsing SSH2 public key file"));
-
- if (!ret_public_key)
- return FALSE;
- if (encoding == SILC_PKCS_FILE_BIN)
- return FALSE;
-
- silc_buffer_set(&keybuf, filedata, filedata_len);
-
- /* Check for RFC 4716 style public key markers */
- if (!silc_ssh_parse_line(&keybuf, &line, TRUE)) {
- SILC_LOG_DEBUG(("Malformed SSH2 public key markers"));
- return FALSE;
- }
- if ((silc_buffer_len(&keybuf) < (strlen(SILC_SSH_PUBLIC_KEY_BEGIN) +
- strlen(SILC_SSH_PUBLIC_KEY_END))) ||
- strncmp(silc_buffer_data(&line), SILC_SSH_PUBLIC_KEY_BEGIN,
- silc_buffer_len(&line))) {
- /* We assume the key is OpenSSH style public key. */
- type = SILC_SSH_KEY_OPENSSH;
- silc_buffer_set(&keybuf, filedata, filedata_len);
-
- /* Get subject name from the end of the file */
- if (!silc_buffer_strchr(&keybuf, ' ', FALSE)) {
- SILC_LOG_DEBUG(("Malformed SSH2 public key"));
- return FALSE;
- }
- if (!silc_buffer_pull(&keybuf, 1)) {
- SILC_LOG_DEBUG(("Malformed SSH2 public key"));
- return FALSE;
- }
- if (!silc_buffer_len(&keybuf)) {
- SILC_LOG_DEBUG(("Malformed SSH2 public key"));
- return FALSE;
- }
-
- /* Add subject name to public key headers */
- fields = silc_ssh_allocate_fields();
- if (!fields)
- return FALSE;
- silc_hash_table_add(fields, strdup("Subject"),
- silc_memdup(silc_buffer_data(&keybuf),
- silc_buffer_len(&keybuf)));
-
- filedata_len = silc_buffer_headlen(&keybuf) - 1;
- SILC_LOG_DEBUG(("Add Subject header to public key"));
-
- /* Skip algorithm name */
- silc_buffer_start(&keybuf);
- if (!silc_buffer_strchr(&keybuf, ' ', TRUE)) {
- SILC_LOG_DEBUG(("Malformed SSH2 public key"));
- silc_hash_table_free(fields);
- return FALSE;
- }
- if (!silc_buffer_pull(&keybuf, 1)) {
- SILC_LOG_DEBUG(("Malformed SSH2 public key"));
- silc_hash_table_free(fields);
- return FALSE;
- }
- if (silc_buffer_len(&keybuf) < filedata_len) {
- SILC_LOG_DEBUG(("Malformed SSH2 public key"));
- silc_hash_table_free(fields);
- return FALSE;
- }
-
- filedata = silc_buffer_data(&keybuf);
- SILC_LOG_DEBUG(("Public key is OpenSSH public key"));
-
- } else {
- /* RFC 4716 style public key */
- type = SILC_SSH_KEY_SSH2;
-
- filedata = silc_buffer_data(&keybuf);
- filedata_len = silc_buffer_len(&keybuf) - strlen(SILC_SSH_PUBLIC_KEY_END);
- silc_buffer_set(&keybuf, filedata, filedata_len);
-
- /* Parse public key headers */
- fields = silc_ssh_parse_headers(&keybuf);
- if (!fields)
- return FALSE;
-
- filedata = silc_buffer_data(&keybuf);
- filedata_len = silc_buffer_len(&keybuf);
-
- SILC_LOG_DEBUG(("Public key is standard SSH2 public key"));
- }
-
- /* Decode */
- data = silc_base64_decode(NULL, filedata, filedata_len, &filedata_len);
- if (!data) {
- silc_hash_table_free(fields);
- return FALSE;
- }
- filedata = data;
-
- /* Decode the public key */
- ret = silc_pkcs_ssh_import_public_key(pkcs, NULL, filedata, filedata_len,
- (void *)&pubkey, ret_alg);
- silc_free(data);
-
- if (ret) {
- pubkey->fields = fields;
- pubkey->type = type;
- *ret_public_key = pubkey;
- SILC_LOG_DEBUG(("SSH2 public key file imported successfully"));
- return TRUE;
- }
-
- silc_hash_table_free(fields);
- return FALSE;
-}
-
-/* Import public key */
-
-SILC_PKCS_IMPORT_PUBLIC_KEY(silc_pkcs_ssh_import_public_key)
-{
- SilcSshPublicKey pubkey;
- int ret;
-
- ret = silc_ssh_public_key_decode(key, key_len, &pubkey);
- if (ret) {
- if (ret_alg)
- *ret_alg = pubkey->pkcs;
- if (ret_public_key)
- *ret_public_key = pubkey;
- }
-
- return ret;
-}
-
-/* Export public key file */
-
-SILC_PKCS_EXPORT_PUBLIC_KEY_FILE(silc_pkcs_ssh_export_public_key_file)
-{
- SilcSshPublicKey pubkey = public_key;
- SilcHashTableList htl;
- SilcBufferStruct buf, fields;
- unsigned char *key, *data;
- SilcUInt32 key_len;
- char *field, *value, tmp[1024], tmp2[1024 + 24];
- int i, j, c;
-
- SILC_LOG_DEBUG(("Encoding %s public key file",
- pubkey->type == SILC_SSH_KEY_SSH2 ? "SSH2" : "OpenSSH"));
-
- /* Export key */
- key = silc_pkcs_ssh_export_public_key(pkcs, stack, pubkey, &key_len);
- if (!key)
- return NULL;
-
- /* Base64 encode the key data */
- if (pubkey->type == SILC_SSH_KEY_SSH2)
- data = silc_base64_encode_file(stack, key, key_len);
- else
- data = silc_base64_encode(stack, key, key_len);
- if (!data)
- return NULL;
- silc_sfree(stack, key);
- key = data;
- key_len = strlen(data);
-
- memset(&buf, 0, sizeof(buf));
- memset(&fields, 0, sizeof(fields));
- memset(tmp, 0, sizeof(tmp));
- memset(tmp2, 0, sizeof(tmp2));
-
- switch (pubkey->type) {
- case SILC_SSH_KEY_SSH2:
- /* RFC 4716 style public key file */
-
- if (pubkey->fields) {
- /* Encode public key headers */
- silc_hash_table_list(pubkey->fields, &htl);
- while (silc_hash_table_get(&htl, (void *)&field, (void *)&value)) {
- /* Wrap lines with over 72 characters */
- silc_snprintf(tmp, sizeof(tmp), "%s: %s", field, value);
- for (i = 0, j = 0, c = 1; i < strlen(tmp); i++, c++) {
- if (c == 72) {
- tmp2[j++] = '\\';
- tmp2[j++] = '\n';
- i--;
- c = 0;
- continue;
- }
-
- tmp2[j++] = tmp[i];
- }
- tmp2[j++] = '\n';
-
- if (silc_buffer_sstrformat(stack, &fields, tmp2, SILC_STRFMT_END) < 0) {
- silc_buffer_spurge(stack, &fields);
- silc_sfree(stack, key);
- return NULL;
- }
-
- memset(tmp2, 0, sizeof(tmp2));
- }
- silc_hash_table_list_reset(&htl);
- }
-
- /* Encode the file */
- if (silc_buffer_sformat(stack, &buf,
- SILC_STR_UI32_STRING(SILC_SSH_PUBLIC_KEY_BEGIN),
- SILC_STR_UI32_STRING("\n"),
- SILC_STR_UI_XNSTRING(silc_buffer_data(&fields),
- silc_buffer_len(&fields)),
- SILC_STR_UI_XNSTRING(key, key_len),
- SILC_STR_UI32_STRING("\n"),
- SILC_STR_UI32_STRING(SILC_SSH_PUBLIC_KEY_END),
- SILC_STR_UI32_STRING("\n"),
- SILC_STR_END) < 0) {
- silc_buffer_spurge(stack, &fields);
- silc_sfree(stack, key);
- return NULL;
- }
-
- break;
-
- case SILC_SSH_KEY_OPENSSH:
- /* OpenSSH style public key file */
-
- if (!strcmp(pubkey->pkcs->name, "rsa"))
- silc_snprintf(tmp, sizeof(tmp), "ssh-rsa ");
- else if (!strcmp(pubkey->pkcs->name, "dsa"))
- silc_snprintf(tmp, sizeof(tmp), "ssh-dss ");
-
- /* Get subject */
- value = (char *)silc_ssh_public_key_get_field(pubkey, "Subject");
-
- /* Encode the file */
- if (silc_buffer_sformat(stack, &buf,
- SILC_STR_UI32_STRING(tmp),
- SILC_STR_UI_XNSTRING(key, key_len),
- SILC_STR_UI32_STRING(" "),
- SILC_STR_UI32_STRING(value),
- SILC_STR_UI32_STRING("\n"),
- SILC_STR_END) < 0) {
- silc_buffer_spurge(stack, &buf);
- silc_sfree(stack, key);
- return NULL;
- }
-
- break;
-
- default:
- silc_sfree(stack, key);
- return NULL;
- break;
- }
-
- silc_sfree(stack, key);
- key = silc_buffer_steal(&buf, ret_len);
-
- silc_buffer_spurge(stack, &fields);
-
- return key;
-}
-
-/* Export public key */
-
-SILC_PKCS_EXPORT_PUBLIC_KEY(silc_pkcs_ssh_export_public_key)
-{
- return silc_ssh_public_key_encode(stack, public_key, ret_len);
-}
-
-/* Return key length in bits */
-
-SILC_PKCS_PUBLIC_KEY_BITLEN(silc_pkcs_ssh_public_key_bitlen)
-{
- SilcSshPublicKey pubkey = public_key;
- return pubkey->pkcs->public_key_bitlen(pubkey->pkcs, pubkey->public_key);
-}
-
-/* Copy public key */
-
-SILC_PKCS_PUBLIC_KEY_COPY(silc_pkcs_ssh_public_key_copy)
-{
- SilcSshPublicKey pubkey = public_key, new_pubkey;
- SilcHashTableList htl;
- char *field, *value;
-
- new_pubkey = silc_calloc(1, sizeof(*new_pubkey));
- if (!new_pubkey)
- return NULL;
- new_pubkey->pkcs = pubkey->pkcs;
- new_pubkey->type = pubkey->type;
-
- new_pubkey->public_key =
- pubkey->pkcs->public_key_copy(pubkey->pkcs, pubkey->public_key);
- if (!new_pubkey->public_key) {
- silc_free(new_pubkey);
- return NULL;
- }
-
- if (pubkey->fields) {
- new_pubkey->fields = silc_ssh_allocate_fields();
- if (!new_pubkey->fields) {
- pubkey->pkcs->public_key_free(pubkey->pkcs, pubkey->public_key);
- silc_free(new_pubkey);
- return NULL;
- }
-
- silc_hash_table_list(pubkey->fields, &htl);
- while (silc_hash_table_get(&htl, (void *)&field, (void *)&value))
- silc_hash_table_add(new_pubkey->fields, strdup(field), strdup(value));
- silc_hash_table_list_reset(&htl);
- }
-
- return new_pubkey;
-}
-
-/* Compare two public keys */
-
-SILC_PKCS_PUBLIC_KEY_COMPARE(silc_pkcs_ssh_public_key_compare)
-{
- SilcSshPublicKey k1 = key1, k2 = key2;
- SilcHashTableList htl;
- char *field, *value, *value2;
-
- if (strcmp(k1->pkcs->name, k2->pkcs->name))
- return FALSE;
-
- if (k1->fields && !k2->fields)
- return FALSE;
- if (!k1->fields && k2->fields)
- return FALSE;
-
- if (k1->fields && k2->fields) {
- if (silc_hash_table_count(k1->fields) != silc_hash_table_count(k2->fields))
- return FALSE;
-
- silc_hash_table_list(k1->fields, &htl);
- while (silc_hash_table_get(&htl, (void *)&field, (void *)&value)) {
- value2 = (char *)silc_ssh_public_key_get_field(k2, field);
- if (!value2)
- return FALSE;
- if (strcmp(value, value2))
- return FALSE;
- }
- silc_hash_table_list_reset(&htl);
- }
-
- return k1->pkcs->public_key_compare(k1->pkcs, k1->public_key,
- k2->public_key);
-}
-
-/* Free public key */
-
-SILC_PKCS_PUBLIC_KEY_FREE(silc_pkcs_ssh_public_key_free)
-{
- silc_ssh_public_key_free(public_key);
-}
-
-/* Import private key file. Supports only OpenSSH (OpenSSL to be exact)
- private key files. */
-
-SILC_PKCS_IMPORT_PRIVATE_KEY_FILE(silc_pkcs_ssh_import_private_key_file)
-{
- const SilcPKCSAlgorithm *alg;
- SilcSshPrivateKey privkey = NULL;
- SilcHashTable fields;
- SilcBufferStruct keybuf, line;
- unsigned char *data, iv[8], key[32];
- SilcSshKeyType type;
- char *proctype, *dekinfo;
- SilcCipher des;
- SilcHash md5;
- int ret;
-
- SILC_LOG_DEBUG(("Parsing SSH2 private key file"));
-
- if (!ret_private_key)
- return FALSE;
- if (encoding == SILC_PKCS_FILE_BIN)
- return FALSE;
-
- silc_buffer_set(&keybuf, filedata, filedata_len);
-
- /* Check for private key markers */
- if (!silc_ssh_parse_line(&keybuf, &line, TRUE)) {
- SILC_LOG_DEBUG(("Malformed SSH2 private key markers"));
- return FALSE;
- }
- if ((silc_buffer_len(&keybuf) < (strlen(SILC_SSH_RSA_BEGIN) +
- strlen(SILC_SSH_RSA_END))) ||
- (strncmp(silc_buffer_data(&line), SILC_SSH_RSA_BEGIN,
- silc_buffer_len(&line)) &&
- strncmp(silc_buffer_data(&line), SILC_SSH_DSA_BEGIN,
- silc_buffer_len(&line)))) {
- SILC_LOG_DEBUG(("Malformed SSH2 private key markers"));
- return FALSE;
- }
-
- /* Get PKCS algorithm */
- if (!strncmp(silc_buffer_data(&line), SILC_SSH_RSA_BEGIN,
- silc_buffer_len(&line))) {
- alg = silc_pkcs_find_algorithm("rsa", "ssh");
- if (!alg) {
- SILC_LOG_ERROR(("Unsupported PKCS algorithm rsa/ssh"));
- return FALSE;
- }
- } else if (!strncmp(silc_buffer_data(&line), SILC_SSH_DSA_BEGIN,
- silc_buffer_len(&line))) {
- alg = silc_pkcs_find_algorithm("dsa", "ssh");
- if (!alg) {
- SILC_LOG_ERROR(("Unsupported PKCS algorithm dsa/ssh"));
- return FALSE;
- }
- } else
- return FALSE;
-
- type = SILC_SSH_KEY_OPENSSH;
- filedata = silc_buffer_data(&keybuf);
-
- /* Skip end marker */
- if (!silc_buffer_strchr(&keybuf, '-', FALSE)) {
- SILC_LOG_DEBUG(("Malformed SSH2 private key markers"));
- return FALSE;
- }
- filedata_len = silc_buffer_data(&keybuf) - filedata;
- silc_buffer_set(&keybuf, filedata, filedata_len);
-
- /* Parse private key headers. They define how the private key has been
- encrypted. */
- fields = silc_ssh_parse_headers(&keybuf);
- if (!fields)
- return FALSE;
-
- /* Skip empty line after headers */
- if (silc_hash_table_count(fields) > 0)
- silc_ssh_parse_line(&keybuf, NULL, TRUE);
-
- filedata = silc_buffer_data(&keybuf);
- filedata_len = silc_buffer_len(&keybuf);
-
- /* Decode */
- data = silc_base64_decode(NULL, filedata, filedata_len, &filedata_len);
- if (!data) {
- SILC_LOG_DEBUG(("Malformed SSH2 private key"));
- goto err;
- }
- filedata = data;
-
- SILC_LOG_DEBUG(("Private key is %s", (silc_hash_table_count(fields) ?
- "encrypted" : "not encrypted")));
-
- if (silc_hash_table_count(fields) > 0 && passphrase) {
- /* Decrypt */
-
- /* Get encryption info */
- if (!silc_hash_table_find(fields, "Proc-Type", NULL, (void *)&proctype)) {
- SILC_LOG_ERROR(("Malformed SSH2 private key"));
- goto err;
- }
- if (strcmp(proctype, "4,ENCRYPTED")) {
- SILC_LOG_ERROR(("Malformed SSH2 private key"));
- goto err;
- }
-
- /* OpenSSH uses 3DES-EDE only */
- if (!silc_hash_table_find(fields, "DEK-Info", NULL, (void *)&dekinfo)) {
- SILC_LOG_ERROR(("Malformed SSH2 private key"));
- goto err;
- }
- if (strncmp(dekinfo, "DES-EDE3-CBC", strlen("DES-EDE3-CBC"))) {
- SILC_LOG_ERROR(("Unsupported SSH2 private key cipher '%s'", dekinfo));
- goto err;
- }
-
- /* Allocate cipher */
- if (!silc_cipher_alloc("3des-168-cbc", &des)) {
- SILC_LOG_ERROR(("Unsupported algorithm 3des-168-cbc"));
- goto err;
- }
-
- /* Allocate hash */
- if (!silc_hash_alloc("md5", &md5)) {
- SILC_LOG_ERROR(("Unsupported hash algorithm md5"));
- goto err;
- }
-
- /* Get IV from private key file */
- dekinfo = strchr(dekinfo, ',');
- if (!dekinfo || strlen(dekinfo) < 16) {
- SILC_LOG_ERROR(("Malformed SSH2 private key"));
- goto err;
- }
- dekinfo++;
- silc_hex2data(dekinfo, iv, sizeof(iv), NULL);
-
- /* Generate key from passphrase and IV as salt. The passphrase is
- hashed with the IV, then rehashed with the previous hash, passphrase
- and the IV to produce the final key, which is the concatenation of
- the two hashes. */
- silc_hash_init(md5);
- silc_hash_update(md5, passphrase, passphrase_len);
- silc_hash_update(md5, iv, 8);
- silc_hash_final(md5, key);
- silc_hash_init(md5);
- silc_hash_update(md5, key, 16);
- silc_hash_update(md5, passphrase, passphrase_len);
- silc_hash_update(md5, iv, 8);
- silc_hash_final(md5, key + 16);
-
- /* Decrypt */
- silc_cipher_set_key(des, key, 192, FALSE);
- if (!silc_cipher_decrypt(des, filedata, filedata, filedata_len, iv)) {
- SILC_LOG_ERROR(("Malformed SSH2 private key"));
- silc_cipher_free(des);
- silc_hash_free(md5);
- goto err;
- }
-
- silc_cipher_free(des);
- silc_hash_free(md5);
- }
-
- /* Decode the private key */
- ret = silc_pkcs_ssh_import_private_key(pkcs, alg, filedata, filedata_len,
- (void *)&privkey, ret_alg);
- silc_free(data);
-
- if (ret) {
- privkey->fields = fields;
- privkey->type = type;
- *ret_private_key = privkey;
- SILC_LOG_DEBUG(("SSH2 private key file imported successfully"));
- return TRUE;
- }
-
- err:
- if (fields)
- silc_hash_table_free(fields);
- return FALSE;
-}
-
-/* Import private key. The key format for RSA is PKCS#1 compliant and for
- DSA is equivalent to our DSA implementation, so we just simply call the
- algorithm specific import function to do the magic. */
-
-SILC_PKCS_IMPORT_PRIVATE_KEY(silc_pkcs_ssh_import_private_key)
-{
- SilcSshPrivateKey privkey;
- int ret;
-
- if (!ret_private_key || !alg)
- return 0;
-
- /* Allocate SSH private key context */
- privkey = silc_calloc(1, sizeof(*privkey));
- if (!privkey)
- return 0;
-
- /* Import PKCS algorithm private key */
- ret = alg->import_private_key(alg, key, key_len, &privkey->private_key);
- if (!ret) {
- silc_free(privkey);
- return 0;
- }
-
- privkey->pkcs = alg;
- privkey->type = SILC_SSH_KEY_OPENSSH;
-
- *ret_private_key = privkey;
- if (ret_alg)
- *ret_alg = alg;
-
- return ret;
-}
-
-/* Export private key file */
-
-SILC_PKCS_EXPORT_PRIVATE_KEY_FILE(silc_pkcs_ssh_export_private_key_file)
-{
- SilcSshPrivateKey privkey = private_key;
- const SilcPKCSAlgorithm *alg = privkey->pkcs;
- SilcBufferStruct buf;
- unsigned char *key, *keyenc, ivdata[8], iv[16 + 1], enc[32];
- SilcUInt32 key_len, pad_len;
- SilcCipher des = NULL;
- SilcHash md5 = NULL;
-
- SILC_LOG_DEBUG(("Encode SSH2 private key file"));
-
- /* Export the private key */
- key = silc_pkcs_ssh_export_private_key(pkcs, stack, private_key, &key_len);
- if (!key)
- return NULL;
-
- memset(&buf, 0, sizeof(buf));
- if (!strcmp(alg->name, "rsa")) {
- if (silc_buffer_sformat(stack, &buf,
- SILC_STR_ADVANCE,
- SILC_STR_UI32_STRING(SILC_SSH_RSA_BEGIN),
- SILC_STR_UI32_STRING("\n"),
- SILC_STR_END) < 0)
- goto err;
- } else if (!strcmp(alg->name, "dsa")) {
- if (silc_buffer_sformat(stack, &buf,
- SILC_STR_ADVANCE,
- SILC_STR_UI32_STRING(SILC_SSH_DSA_BEGIN),
- SILC_STR_UI32_STRING("\n"),
- SILC_STR_END) < 0)
- goto err;
- } else
- goto err;
-
- if (passphrase && strlen(passphrase) > 0) {
- /* Encrypt the key */
-
- /* Allocate cipher */
- if (!silc_cipher_alloc("3des-168-cbc", &des)) {
- SILC_LOG_ERROR(("Unsupported algorithm 3des-168-cbc"));
- goto err;
- }
-
- /* Allocate hash */
- if (!silc_hash_alloc("md5", &md5)) {
- SILC_LOG_ERROR(("Unsupported hash algorithm md5"));
- goto err;
- }
-
- /* Generate IV */
- silc_rng_get_rn_data(rng, sizeof(ivdata), ivdata, sizeof(ivdata));
- silc_data2hex(ivdata, sizeof(ivdata), iv, sizeof(iv));
-
- /* Encode header */
- if (silc_buffer_sformat(stack, &buf,
- SILC_STR_ADVANCE,
- SILC_STR_UI32_STRING("Proc-Type: 4,ENCRYPTED\n"),
- SILC_STR_UI32_STRING("DEK-Info: DES-EDE3-CBC,"),
- SILC_STR_UI32_STRING(iv),
- SILC_STR_UI32_STRING("\n\n"),
- SILC_STR_END) < 0)
- goto err;
-
- /* Generate key from passphrase and IV as salt. The passphrase is
- hashed with the IV, then rehashed with the previous hash, passphrase
- and the IV to produce the final key, which is the concatenation of
- the two hashes. */
- silc_hash_init(md5);
- silc_hash_update(md5, passphrase, passphrase_len);
- silc_hash_update(md5, ivdata, 8);
- silc_hash_final(md5, enc);
- silc_hash_init(md5);
- silc_hash_update(md5, enc, 16);
- silc_hash_update(md5, passphrase, passphrase_len);
- silc_hash_update(md5, ivdata, 8);
- silc_hash_final(md5, enc + 16);
-
- /* Pad */
- pad_len = (-key_len) % 8;
- if (pad_len) {
- keyenc = silc_smalloc(stack, (key_len + pad_len) * sizeof(*keyenc));
- if (!keyenc)
- goto err;
- memset(keyenc + key_len, 'F', pad_len);
- memcpy(keyenc, key, key_len);
- } else {
- keyenc = silc_smemdup(stack, key, key_len);
- if (!keyenc)
- goto err;
- }
-
- /* Encrypt */
- silc_cipher_set_key(des, enc, 192, TRUE);
- silc_cipher_encrypt(des, keyenc, keyenc, key_len + pad_len, ivdata);
-
- silc_sfree(stack, key);
- key = keyenc;
- key_len += pad_len;
-
- silc_cipher_free(des);
- silc_hash_free(md5);
- }
-
- /* Base64 encode */
- keyenc = silc_base64_encode_file(stack, key, key_len);
- if (!keyenc)
- goto err;
-
- silc_sfree(stack, key);
- key = keyenc;
- key_len = strlen(keyenc);
-
- /* Encode rest of the public key */
- if (!strcmp(alg->name, "rsa")) {
- if (silc_buffer_sformat(stack, &buf,
- SILC_STR_ADVANCE,
- SILC_STR_DATA(key, key_len),
- SILC_STR_UI32_STRING("\n"),
- SILC_STR_UI32_STRING(SILC_SSH_RSA_END),
- SILC_STR_UI32_STRING("\n"),
- SILC_STR_END) < 0)
- goto err;
- } else if (!strcmp(alg->name, "dsa")) {
- if (silc_buffer_sformat(stack, &buf,
- SILC_STR_ADVANCE,
- SILC_STR_DATA(key, key_len),
- SILC_STR_UI32_STRING("\n"),
- SILC_STR_UI32_STRING(SILC_SSH_DSA_END),
- SILC_STR_UI32_STRING("\n"),
- SILC_STR_END) < 0)
- goto err;
- }
-
- silc_sfree(stack, key);
- key = silc_buffer_steal(&buf, ret_len);
- return key;
-
- err:
- if (des)
- silc_cipher_free(des);
- if (md5)
- silc_hash_free(md5);
- silc_sfree(stack, key);
- return NULL;
-}
-
-/* Export private key */
-
-SILC_PKCS_EXPORT_PRIVATE_KEY(silc_pkcs_ssh_export_private_key)
-{
- SilcSshPrivateKey privkey = private_key;
- const SilcPKCSAlgorithm *alg = privkey->pkcs;
-
- SILC_LOG_DEBUG(("Encode SSH2 private key"));
-
- /* Export PKCS algorithm private key */
- if (alg->export_private_key)
- return alg->export_private_key(alg, stack,
- privkey->private_key, ret_len);
- return NULL;
-}
-
-/* Return key length in bits */
-
-SILC_PKCS_PRIVATE_KEY_BITLEN(silc_pkcs_ssh_private_key_bitlen)
-{
- SilcSshPrivateKey privkey = private_key;
- return privkey->pkcs->private_key_bitlen(privkey->pkcs,
- privkey->private_key);
-}
-
-/* Free private key */
-
-SILC_PKCS_PRIVATE_KEY_FREE(silc_pkcs_ssh_private_key_free)
-{
- SilcSshPrivateKey privkey = private_key;
-
- privkey->pkcs->private_key_free(privkey->pkcs,
- privkey->private_key);
-
- if (privkey->fields)
- silc_hash_table_free(privkey->fields);
- silc_free(privkey);
-}
-
-/* Encrypt */
-
-SILC_PKCS_ENCRYPT(silc_pkcs_ssh_encrypt)
-{
- SilcSshPublicKey pubkey = public_key;
-
- if (!pubkey->pkcs->encrypt) {
- encrypt_cb(FALSE, NULL, 0, context);
- return NULL;
- }
-
- return pubkey->pkcs->encrypt(pubkey->pkcs, pubkey->public_key,
- src, src_len, rng, encrypt_cb, context);
-}
-
-/* Decrypt */
-
-SILC_PKCS_DECRYPT(silc_pkcs_ssh_decrypt)
-{
- SilcSshPrivateKey privkey = private_key;
-
- if (!privkey->pkcs->decrypt) {
- decrypt_cb(FALSE, NULL, 0, context);
- return NULL;
- }
-
- return privkey->pkcs->decrypt(privkey->pkcs, privkey->private_key,
- src, src_len, decrypt_cb, context);
-}
-
-/* Sign context */
-typedef struct {
- SilcStack stack;
- SilcSshPrivateKey privkey;
- SilcPKCSSignCb sign_cb;
- void *context;
-} *SilcSshSign;
-
-/* Sign callback. This formats the signature into SSH2 protocool compliant
- format. */
-
-static void silc_pkcs_ssh_sign_cb(SilcBool success,
- const unsigned char *signature,
- SilcUInt32 signature_len,
- void *context)
-{
- SilcSshSign sign = context;
- SilcStack stack = sign->stack;
- unsigned char rbuf[20], sbuf[20];
- SilcBufferStruct sig;
- SilcAsn1 asn1;
- SilcMPInt r, s;
-
- memset(&sig, 0, sizeof(sig));
-
- /* Format the signature. RSA is easy because PKCS#1 is already in
- correct format. For DSA the returned signature is in PKIX compliant
- format and we have to reformat it for SSH2. */
- if (!strcmp(sign->privkey->pkcs->name, "dsa")) {
- asn1 = silc_asn1_alloc(stack);
- if (!asn1) {
- sign->sign_cb(FALSE, NULL, 0, sign->context);
- silc_sfree(stack, sign);
- silc_stack_free(stack);
- return;
- }
-
- /* Decode the signature */
- silc_buffer_set(&sig, (unsigned char *)signature, signature_len);
- if (!silc_asn1_decode(asn1, &sig,
- SILC_ASN1_SEQUENCE,
- SILC_ASN1_INT(&r),
- SILC_ASN1_INT(&s),
- SILC_ASN1_END, SILC_ASN1_END)) {
- sign->sign_cb(FALSE, NULL, 0, sign->context);
- silc_asn1_free(asn1);
- silc_sfree(stack, sign);
- silc_stack_free(stack);
- return;
- }
-
- /* Encode the integers */
- memset(rbuf, 0, sizeof(rbuf));
- memset(sbuf, 0, sizeof(sbuf));
- silc_mp_mp2bin_noalloc(&r, rbuf, sizeof(rbuf));
- silc_mp_mp2bin_noalloc(&s, sbuf, sizeof(sbuf));
-
- silc_asn1_free(asn1);
-
- /* Encode SSH2 DSS signature */
- if (silc_buffer_sformat(stack, &sig,
- SILC_STR_UI_INT(7),
- SILC_STR_UI32_STRING("ssh-dss"),
- SILC_STR_UI_INT(sizeof(rbuf) + sizeof(sbuf)),
- SILC_STR_DATA(rbuf, sizeof(rbuf)),
- SILC_STR_DATA(sbuf, sizeof(sbuf)),
- SILC_STR_END) < 0) {
- sign->sign_cb(FALSE, NULL, 0, sign->context);
- silc_sfree(stack, sign);
- silc_stack_free(stack);
- return;
- }
- } else {
- /* Encode SSH2 RSA signature */
- if (silc_buffer_sformat(stack, &sig,
- SILC_STR_UI_INT(7),
- SILC_STR_UI32_STRING("ssh-rsa"),
- SILC_STR_UI_INT(signature_len),
- SILC_STR_DATA(signature, signature_len),
- SILC_STR_END) < 0) {
- sign->sign_cb(FALSE, NULL, 0, sign->context);
- silc_sfree(stack, sign);
- silc_stack_free(stack);
- return;
- }
- }
-
- /* Deliver result */
- sign->sign_cb(TRUE, silc_buffer_data(&sig), silc_buffer_len(&sig),
- sign->context);
-
- silc_buffer_spurge(stack, &sig);
- silc_sfree(stack, sign);
- silc_stack_free(stack);
-}
-
-/* Sign */
-
-SILC_PKCS_SIGN(silc_pkcs_ssh_sign)
-{
- SilcSshPrivateKey privkey = private_key;
- SilcStack stack;
- SilcSshSign sign;
-
- if (!privkey->pkcs->sign) {
- sign_cb(FALSE, NULL, 0, context);
- return NULL;
- }
-
- stack = silc_stack_alloc(0, silc_crypto_stack());
-
- sign = silc_scalloc(stack, 1, sizeof(*sign));
- if (!sign) {
- sign_cb(FALSE, NULL, 0, context);
- silc_stack_free(stack);
- return NULL;
- }
-
- sign->stack = stack;
- sign->privkey = privkey;
- sign->sign_cb = sign_cb;
- sign->context = context;
-
- /* Sign. The callback will format it to SSH2 compliant format. */
- return privkey->pkcs->sign(privkey->pkcs, privkey->private_key,
- src, src_len,
- compute_hash, hash, rng,
- silc_pkcs_ssh_sign_cb, sign);
-}
-
-/* Verify */
-
-SILC_PKCS_VERIFY(silc_pkcs_ssh_verify)
-{
- SilcSshPublicKey pubkey = public_key;
- SilcAsyncOperation op;
- SilcBufferStruct sig, r, s;
- unsigned char *signame;
- SilcStack stack = NULL;
- SilcAsn1 asn1;
-
- if (!pubkey->pkcs->verify) {
- verify_cb(FALSE, context);
- return NULL;
- }
-
- /* Decode the SSH2 protocol style signature encoding. */
- silc_buffer_set(&sig, signature, signature_len);
- if (silc_buffer_unformat(&sig,
- SILC_STR_UI32_STRING_ALLOC(&signame),
- SILC_STR_UI32_NSTRING(&signature, &signature_len),
- SILC_STR_END) < 0) {
- verify_cb(FALSE, context);
- return NULL;
- }
- memset(&sig, 0, sizeof(sig));
-
- /* DSS signature must be formatted to PKIX compliant format since our
- implementation expects that. */
- if (!strcmp(signame, "ssh-dss")) {
- /* The integers must be 160 bits each */
- if (signature_len != 40) {
- verify_cb(FALSE, context);
- silc_free(signame);
- return NULL;
- }
-
- silc_buffer_set(&r, signature, 20);
- silc_buffer_set(&s, signature + 20, 20);
-
- stack = silc_stack_alloc(0, silc_crypto_stack());
-
- asn1 = silc_asn1_alloc(stack);
- if (!asn1) {
- verify_cb(FALSE, context);
- silc_free(signame);
- silc_stack_free(stack);
- return NULL;
- }
-
- /* Encode signature to PKIX compliant format. */
- if (!silc_asn1_encode(asn1, &sig,
- SILC_ASN1_OPTS(SILC_ASN1_ALLOC),
- SILC_ASN1_SEQUENCE,
- SILC_ASN1_ANY_PRIMITIVE(SILC_ASN1_TAG_INTEGER, &r),
- SILC_ASN1_ANY_PRIMITIVE(SILC_ASN1_TAG_INTEGER, &s),
- SILC_ASN1_END, SILC_ASN1_END)) {
- verify_cb(FALSE, context);
- silc_free(signame);
- silc_asn1_free(asn1);
- silc_stack_free(stack);
- return NULL;
- }
-
- signature = silc_buffer_steal(&sig, &signature_len);
-
- silc_asn1_free(asn1);
- }
-
- /* Verify */
- op = pubkey->pkcs->verify(pubkey->pkcs, pubkey->public_key,
- signature, signature_len,
- data, data_len, hash, rng,
- verify_cb, context);
-
- silc_free(signame);
- silc_buffer_spurge(stack, &sig);
- silc_stack_free(stack);
-
- return op;
-}
-
-/************************** SSH2 PKCS RSA Alg API ***************************/
-
-/* The SSH2 RSA PKCS Algorithm API. We implement here only the necessary
- parts of the API and the common code is used from PKCS#1 Algorithm API
- in silccrypt/silcpkcs1.c. Basically everything else is PKCS#1 except
- the format of the public key. */
-
-/* Import RSA public key. Both RFC 4716 and OpenSSH have same format. */
-
-SILC_PKCS_ALG_IMPORT_PUBLIC_KEY(silc_ssh_rsa_import_public_key)
-{
- SilcBufferStruct alg_key;
- RsaPublicKey *pubkey;
- unsigned char *n, *e;
- SilcUInt32 n_len, e_len;
- int ret;
-
- SILC_LOG_DEBUG(("Import public key"));
-
- if (!ret_public_key)
- return 0;
-
- /* Allocate RSA public key */
- *ret_public_key = pubkey = silc_calloc(1, sizeof(*pubkey));
- if (!pubkey)
- return 0;
-
- /* Parse SSH2 RSA public key */
- silc_buffer_set(&alg_key, key, key_len);
- ret = silc_buffer_unformat(&alg_key,
- SILC_STR_UI32_NSTRING(&e, &e_len),
- SILC_STR_UI32_NSTRING(&n, &n_len),
- SILC_STR_END);
- if (ret < 0)
- goto err;
- if (!n_len || !e_len)
- goto err;
-
- /* Get MP integers */
- silc_mp_init(&pubkey->n);
- silc_mp_init(&pubkey->e);
- silc_mp_bin2mp(n, n_len, &pubkey->n);
- silc_mp_bin2mp(e, e_len, &pubkey->e);
-
- /* Set key length */
- pubkey->bits = ((silc_mp_sizeinbase(&pubkey->n, 2) + 7) / 8) * 8;
-
- return ret;
-
- err:
- silc_free(pubkey);
- return 0;
-}
-
-/* Export RSA public key. Both RFC 4716 and OpenSSH have same format. */
-
-SILC_PKCS_ALG_EXPORT_PUBLIC_KEY(silc_ssh_rsa_export_public_key)
-{
- RsaPublicKey *pubkey = public_key;
- SilcBufferStruct alg_key;
- unsigned char *n = NULL, *e = NULL, *ret;
- SilcUInt32 n_len, e_len;
-
- SILC_LOG_DEBUG(("Export public key"));
-
- /* Encode MP integers */
- n = silc_mp_mp2bin(&pubkey->n, 0, &n_len);
- if (!n)
- goto err;
- e = silc_mp_mp2bin(&pubkey->e, 0, &e_len);
- if (!e)
- goto err;
-
- /* Encode SSH2 RSA public key */
- memset(&alg_key, 0, sizeof(alg_key));
- if (silc_buffer_sformat(stack, &alg_key,
- SILC_STR_UI_INT(e_len),
- SILC_STR_DATA(e, e_len),
- SILC_STR_UI_INT(n_len),
- SILC_STR_DATA(n, n_len),
- SILC_STR_END) < 0)
- goto err;
-
- silc_free(n);
- silc_free(e);
-
- ret = silc_buffer_steal(&alg_key, ret_len);
- return ret;
-
- err:
- silc_free(n);
- silc_free(e);
- return NULL;
-}
-
-/************************** SSH2 PKCS DSA Alg API ***************************/
-
-/* The SSH2 DSA PKCS Algorithm API. We implement here only the necessary
- parts of the API and the common code is used from DSS Algorithm API
- in silccrypt/dsa.c. */
-
-/* Import DSA public key. Both RFC 4716 and OpenSSH have same format. */
-
-SILC_PKCS_ALG_IMPORT_PUBLIC_KEY(silc_ssh_dsa_import_public_key)
-{
- SilcBufferStruct alg_key;
- DsaPublicKey *pubkey;
- unsigned char *p, *q, *g, *y;
- SilcUInt32 p_len, q_len, g_len, y_len;
- int ret;
-
- SILC_LOG_DEBUG(("Import public key"));
-
- if (!ret_public_key)
- return 0;
-
- /* Allocate DSA public key */
- *ret_public_key = pubkey = silc_calloc(1, sizeof(*pubkey));
- if (!pubkey)
- return 0;
-
- /* Parse SSH2 DSA public key */
- silc_buffer_set(&alg_key, key, key_len);
- ret = silc_buffer_unformat(&alg_key,
- SILC_STR_UI32_NSTRING(&p, &p_len),
- SILC_STR_UI32_NSTRING(&q, &q_len),
- SILC_STR_UI32_NSTRING(&g, &g_len),
- SILC_STR_UI32_NSTRING(&y, &y_len),
- SILC_STR_END);
- if (ret < 0)
- goto err;
- if (!p_len || !q_len || !g_len || !y_len)
- goto err;
-
- /* Get MP integers */
- silc_mp_init(&pubkey->p);
- silc_mp_init(&pubkey->q);
- silc_mp_init(&pubkey->g);
- silc_mp_init(&pubkey->y);
- silc_mp_bin2mp(p, p_len, &pubkey->p);
- silc_mp_bin2mp(q, q_len, &pubkey->q);
- silc_mp_bin2mp(g, g_len, &pubkey->g);
- silc_mp_bin2mp(y, y_len, &pubkey->y);
-
- /* Set key length */
- pubkey->bits = ((silc_mp_sizeinbase(&pubkey->p, 2) + 7) / 8) * 8;
-
- return ret;
-
- err:
- silc_free(pubkey);
- return 0;
-}
-
-/* Export DSA public key. Both RFC 4716 and OpenSSH have same format. */
-
-SILC_PKCS_ALG_EXPORT_PUBLIC_KEY(silc_ssh_dsa_export_public_key)
-{
- DsaPublicKey *pubkey = public_key;
- SilcBufferStruct alg_key;
- unsigned char *p = NULL, *q = NULL, *g = NULL, *y = NULL, *ret;
- SilcUInt32 p_len, q_len, g_len, y_len;
-
- SILC_LOG_DEBUG(("Export public key"));
-
- /* Encode MP integers */
- p = silc_mp_mp2bin(&pubkey->p, 0, &p_len);
- if (!p)
- goto err;
- q = silc_mp_mp2bin(&pubkey->q, 0, &q_len);
- if (!q)
- goto err;
- g = silc_mp_mp2bin(&pubkey->g, 0, &g_len);
- if (!g)
- goto err;
- y = silc_mp_mp2bin(&pubkey->y, 0, &y_len);
- if (!y)
- goto err;
-
- /* Encode SSH2 DSA public key */
- memset(&alg_key, 0, sizeof(alg_key));
- if (silc_buffer_sformat(stack, &alg_key,
- SILC_STR_UI_INT(p_len),
- SILC_STR_DATA(p, p_len),
- SILC_STR_UI_INT(q_len),
- SILC_STR_DATA(q, q_len),
- SILC_STR_UI_INT(g_len),
- SILC_STR_DATA(g, g_len),
- SILC_STR_UI_INT(y_len),
- SILC_STR_DATA(y, y_len),
- SILC_STR_END) < 0)
- goto err;
-
- silc_free(p);
- silc_free(q);
- silc_free(g);
- silc_free(y);
-
- ret = silc_buffer_steal(&alg_key, ret_len);
- return ret;
-
- err:
- silc_free(p);
- silc_free(q);
- silc_free(g);
- silc_free(y);
- return NULL;
-}