summaryrefslogtreecommitdiffstats
path: root/libssh/src/pki.c
diff options
context:
space:
mode:
Diffstat (limited to 'libssh/src/pki.c')
-rw-r--r--libssh/src/pki.c249
1 files changed, 228 insertions, 21 deletions
diff --git a/libssh/src/pki.c b/libssh/src/pki.c
index ec5a6883..af472ebb 100644
--- a/libssh/src/pki.c
+++ b/libssh/src/pki.c
@@ -3,7 +3,7 @@
* This file is part of the SSH Library
*
* Copyright (c) 2010 by Aris Adamantiadis
- * Copyright (c) 2011-2012 Andreas Schneider <asn@cryptomilk.org>
+ * Copyright (c) 2011-2013 Andreas Schneider <asn@cryptomilk.org>
*
* The SSH Library is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
@@ -99,6 +99,25 @@ enum ssh_keytypes_e pki_privatekey_type_from_string(const char *privkey) {
}
/**
+ * @brief returns the ECDSA key name ("ecdsa-sha2-nistp256" for example)
+ *
+ * @param[in] key the ssh_key whose ECDSA name to get
+ *
+ * @returns the ECDSA key name ("ecdsa-sha2-nistp256" for example)
+ *
+ * @returns "unknown" if the ECDSA key name is not known
+ */
+const char *ssh_pki_key_ecdsa_name(const ssh_key key)
+{
+#ifdef HAVE_OPENSSL_ECC /* FIXME Better ECC check needed */
+ return pki_key_ecdsa_nid_to_name(key->ecdsa_nid);
+#else
+ (void) key; /* unused */
+ return NULL;
+#endif
+}
+
+/**
* @brief creates a new empty SSH key
* @returns an empty ssh_key handle, or NULL on error.
*/
@@ -138,6 +157,11 @@ void ssh_key_clean (ssh_key key){
if(key->ecdsa) EC_KEY_free(key->ecdsa);
#endif /* HAVE_OPENSSL_ECC */
#endif
+ if (key->ed25519_privkey != NULL){
+ BURN_BUFFER(key->ed25519_privkey, sizeof(ed25519_privkey));
+ SAFE_FREE(key->ed25519_privkey);
+ }
+ SAFE_FREE(key->ed25519_pubkey);
key->flags=SSH_KEY_FLAG_EMPTY;
key->type=SSH_KEYTYPE_UNKNOWN;
key->ecdsa_nid = 0;
@@ -188,6 +212,8 @@ const char *ssh_key_type_to_char(enum ssh_keytypes_e type) {
return "ssh-rsa1";
case SSH_KEYTYPE_ECDSA:
return "ssh-ecdsa";
+ case SSH_KEYTYPE_ED25519:
+ return "ssh-ed25519";
case SSH_KEYTYPE_UNKNOWN:
return NULL;
}
@@ -226,6 +252,8 @@ enum ssh_keytypes_e ssh_key_type_from_name(const char *name) {
|| strcmp(name, "ecdsa-sha2-nistp384") == 0
|| strcmp(name, "ecdsa-sha2-nistp521") == 0) {
return SSH_KEYTYPE_ECDSA;
+ } else if (strcmp(name, "ssh-ed25519") == 0){
+ return SSH_KEYTYPE_ED25519;
}
return SSH_KEYTYPE_UNKNOWN;
@@ -281,7 +309,7 @@ int ssh_key_cmp(const ssh_key k1,
}
if (k1->type != k2->type) {
- ssh_pki_log("key types don't macth!");
+ ssh_pki_log("key types don't match!");
return 1;
}
@@ -292,6 +320,10 @@ int ssh_key_cmp(const ssh_key k1,
}
}
+ if (k1->type == SSH_KEYTYPE_ED25519) {
+ return pki_ed25519_key_cmp(k1, k2, what);
+ }
+
return pki_key_compare(k1, k2, what);
}
@@ -331,6 +363,13 @@ void ssh_signature_free(ssh_signature sig)
#endif
break;
case SSH_KEYTYPE_ECDSA:
+#if defined(HAVE_LIBCRYPTO) && defined(HAVE_OPENSSL_ECC)
+ ECDSA_SIG_free(sig->ecdsa_sig);
+#endif
+ break;
+ case SSH_KEYTYPE_ED25519:
+ SAFE_FREE(sig->ed25519_sig);
+ break;
case SSH_KEYTYPE_UNKNOWN:
break;
}
@@ -476,6 +515,65 @@ int ssh_pki_import_privkey_file(const char *filename,
return SSH_OK;
}
+/**
+ * @brief Export a private key to a pam file on disk.
+ *
+ * @param[in] privkey The private key to export.
+ *
+ * @param[in] passphrase The passphrase to use to encrypt the key with or
+ * NULL. An empty string means no passphrase.
+ *
+ * @param[in] auth_fn An auth function you may want to use or NULL.
+ *
+ * @param[in] auth_data Private data passed to the auth function.
+ *
+ * @param[in] filename The path where to store the pem file.
+ *
+ * @return SSH_OK on success, SSH_ERROR on error.
+ */
+int ssh_pki_export_privkey_file(const ssh_key privkey,
+ const char *passphrase,
+ ssh_auth_callback auth_fn,
+ void *auth_data,
+ const char *filename)
+{
+ ssh_string blob;
+ FILE *fp;
+ int rc;
+
+ if (privkey == NULL || !ssh_key_is_private(privkey)) {
+ return SSH_ERROR;
+ }
+
+ fp = fopen(filename, "wb");
+ if (fp == NULL) {
+ SSH_LOG(SSH_LOG_FUNCTIONS, "Error opening %s: %s",
+ filename, strerror(errno));
+ return SSH_EOF;
+ }
+
+
+ blob = pki_private_key_to_pem(privkey,
+ passphrase,
+ auth_fn,
+ auth_data);
+ if (blob == NULL) {
+ fclose(fp);
+ return -1;
+ }
+
+ rc = fwrite(ssh_string_data(blob), ssh_string_len(blob), 1, fp);
+ ssh_string_free(blob);
+ if (rc != 1 || ferror(fp)) {
+ fclose(fp);
+ unlink(filename);
+ return SSH_ERROR;
+ }
+ fclose(fp);
+
+ return SSH_OK;
+}
+
/* temporary function to migrate seemlessly to ssh_key */
ssh_public_key ssh_pki_convert_key_to_publickey(const ssh_key key) {
ssh_public_key pub;
@@ -661,10 +759,36 @@ static int pki_import_pubkey_buffer(ssh_buffer buffer,
if (rc < 0) {
goto fail;
}
+
+ /* Update key type */
+ key->type_c = ssh_pki_key_ecdsa_name(key);
}
break;
#endif
+ case SSH_KEYTYPE_ED25519:
+ {
+ ssh_string pubkey = buffer_get_ssh_string(buffer);
+ if (ssh_string_len(pubkey) != ED25519_PK_LEN) {
+ ssh_pki_log("Invalid public key length");
+ ssh_string_burn(pubkey);
+ ssh_string_free(pubkey);
+ goto fail;
+ }
+
+ key->ed25519_pubkey = malloc(ED25519_PK_LEN);
+ if (key->ed25519_pubkey == NULL) {
+ ssh_string_burn(pubkey);
+ ssh_string_free(pubkey);
+ goto fail;
+ }
+
+ memcpy(key->ed25519_pubkey, ssh_string_data(pubkey), ED25519_PK_LEN);
+ ssh_string_burn(pubkey);
+ ssh_string_free(pubkey);
+ }
+ break;
case SSH_KEYTYPE_UNKNOWN:
+ default:
ssh_pki_log("Unknown public key protocol %d", type);
goto fail;
}
@@ -752,7 +876,7 @@ int ssh_pki_import_pubkey_blob(const ssh_string key_blob,
return SSH_ERROR;
}
- rc = buffer_add_data(buffer, ssh_string_data(key_blob),
+ rc = ssh_buffer_add_data(buffer, ssh_string_data(key_blob),
ssh_string_len(key_blob));
if (rc < 0) {
ssh_pki_log("Out of memory!");
@@ -917,10 +1041,20 @@ int ssh_pki_generate(enum ssh_keytypes_e type, int parameter,
case SSH_KEYTYPE_ECDSA:
#ifdef HAVE_ECC
rc = pki_key_generate_ecdsa(key, parameter);
- if(rc == SSH_ERROR)
+ if (rc == SSH_ERROR) {
goto error;
+ }
+
+ /* Update key type */
+ key->type_c = ssh_pki_key_ecdsa_name(key);
break;
#endif
+ case SSH_KEYTYPE_ED25519:
+ rc = pki_key_generate_ed25519(key);
+ if (rc == SSH_ERROR) {
+ goto error;
+ }
+ break;
case SSH_KEYTYPE_UNKNOWN:
goto error;
}
@@ -999,11 +1133,11 @@ int ssh_pki_export_pubkey_blob(const ssh_key key,
}
/**
- * @brief Convert a public key to a base64 hased key.
+ * @brief Convert a public key to a base64 encoded key.
*
* @param[in] key The key to hash
*
- * @param[out] b64_key A pointer to store the allocated base64 hashed key. You
+ * @param[out] b64_key A pointer to store the allocated base64 encoded key. You
* need to free the buffer.
*
* @return SSH_OK on success, SSH_ERROR on error.
@@ -1177,9 +1311,9 @@ int ssh_pki_import_signature_blob(const ssh_string sig_blob,
return SSH_ERROR;
}
- rc = buffer_add_data(buf,
- ssh_string_data(sig_blob),
- ssh_string_len(sig_blob));
+ rc = ssh_buffer_add_data(buf,
+ ssh_string_data(sig_blob),
+ ssh_string_len(sig_blob));
if (rc < 0) {
ssh_buffer_free(buf);
return SSH_ERROR;
@@ -1236,12 +1370,19 @@ int ssh_pki_signature_verify_blob(ssh_session session,
evp(key->ecdsa_nid, digest, dlen, ehash, &elen);
+#ifdef DEBUG_CRYPTO
+ ssh_print_hexa("Hash to be verified with ecdsa",
+ ehash, elen);
+#endif
+
rc = pki_signature_verify(session,
sig,
key,
ehash,
elen);
#endif
+ } else if (key->type == SSH_KEYTYPE_ED25519) {
+ rc = pki_signature_verify(session, sig, key, digest, dlen);
} else {
unsigned char hash[SHA_DIGEST_LEN] = {0};
@@ -1302,8 +1443,36 @@ ssh_string ssh_pki_do_sign(ssh_session session,
evp_update(ctx, buffer_get_rest(sigbuf), buffer_get_rest_len(sigbuf));
evp_final(ctx, ehash, &elen);
+#ifdef DEBUG_CRYPTO
+ ssh_print_hexa("Hash being signed", ehash, elen);
+#endif
+
sig = pki_do_sign(privkey, ehash, elen);
#endif
+ } else if (privkey->type == SSH_KEYTYPE_ED25519){
+ ssh_buffer buf;
+
+ buf = ssh_buffer_new();
+ if (buf == NULL) {
+ ssh_string_free(session_id);
+ return NULL;
+ }
+
+ ssh_buffer_set_secure(buf);
+ rc = ssh_buffer_pack(buf,
+ "SP",
+ session_id,
+ buffer_get_rest_len(sigbuf), buffer_get_rest(sigbuf));
+ if (rc != SSH_OK) {
+ ssh_string_free(session_id);
+ ssh_buffer_free(buf);
+ return NULL;
+ }
+
+ sig = pki_do_sign(privkey,
+ ssh_buffer_get_begin(buf),
+ ssh_buffer_get_len(buf));
+ ssh_buffer_free(buf);
} else {
unsigned char hash[SHA_DIGEST_LEN] = {0};
SHACTX ctx;
@@ -1395,10 +1564,8 @@ ssh_string ssh_srv_pki_do_sign_sessionid(ssh_session session,
const ssh_key privkey)
{
struct ssh_crypto_struct *crypto;
- unsigned char hash[SHA_DIGEST_LEN] = {0};
ssh_signature sig;
ssh_string sig_blob;
- SHACTX ctx;
int rc;
if (session == NULL || privkey == NULL || !ssh_key_is_private(privkey)) {
@@ -1407,24 +1574,64 @@ ssh_string ssh_srv_pki_do_sign_sessionid(ssh_session session,
crypto = session->next_crypto ? session->next_crypto :
session->current_crypto;
- ctx = sha1_init();
- if (ctx == NULL) {
- return NULL;
- }
if (crypto->secret_hash == NULL){
ssh_set_error(session,SSH_FATAL,"Missing secret_hash");
return NULL;
}
- sha1_update(ctx, crypto->secret_hash, crypto->digest_len);
- sha1_final(hash, ctx);
+
+ if (privkey->type == SSH_KEYTYPE_ECDSA) {
+#ifdef HAVE_ECC
+ unsigned char ehash[EVP_DIGEST_LEN] = {0};
+ uint32_t elen;
+
+ evp(privkey->ecdsa_nid, crypto->secret_hash, crypto->digest_len,
+ ehash, &elen);
#ifdef DEBUG_CRYPTO
- ssh_print_hexa("Hash being signed", hash, SHA_DIGEST_LEN);
+ ssh_print_hexa("Hash being signed", ehash, elen);
#endif
- sig = pki_do_sign_sessionid(privkey, hash, SHA_DIGEST_LEN);
- if (sig == NULL) {
- return NULL;
+ sig = pki_do_sign_sessionid(privkey, ehash, elen);
+ if (sig == NULL) {
+ return NULL;
+ }
+#endif
+ } else if (privkey->type == SSH_KEYTYPE_ED25519) {
+ sig = ssh_signature_new();
+ if (sig == NULL){
+ return NULL;
+ }
+
+ sig->type = privkey->type;
+ sig->type_c = privkey->type_c;
+
+ rc = pki_ed25519_sign(privkey,
+ sig,
+ crypto->secret_hash,
+ crypto->digest_len);
+ if (rc != SSH_OK){
+ ssh_signature_free(sig);
+ sig = NULL;
+ }
+ } else {
+ unsigned char hash[SHA_DIGEST_LEN] = {0};
+ SHACTX ctx;
+
+ ctx = sha1_init();
+ if (ctx == NULL) {
+ return NULL;
+ }
+ sha1_update(ctx, crypto->secret_hash, crypto->digest_len);
+ sha1_final(hash, ctx);
+
+#ifdef DEBUG_CRYPTO
+ ssh_print_hexa("Hash being signed", hash, SHA_DIGEST_LEN);
+#endif
+
+ sig = pki_do_sign_sessionid(privkey, hash, SHA_DIGEST_LEN);
+ if (sig == NULL) {
+ return NULL;
+ }
}
rc = ssh_pki_export_signature_blob(sig, &sig_blob);