diff options
Diffstat (limited to 'libssh/src/pki.c')
-rw-r--r-- | libssh/src/pki.c | 249 |
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); |