diff options
author | Aris <aris@badcode.be> | 2014-09-03 09:44:10 +0200 |
---|---|---|
committer | Andreas Schneider <asn@cryptomilk.org> | 2014-09-07 22:07:34 +0200 |
commit | 93c7b81b4ea1046bd2f65f4a510d5966786e8d3d (patch) | |
tree | 2c0a3f5f94731bb390627757e4491f00600a428b | |
parent | 93e82fa0c0f930609cb6f352b3e5d7c45945bac7 (diff) | |
download | libssh-93c7b81b4ea1046bd2f65f4a510d5966786e8d3d.tar.gz libssh-93c7b81b4ea1046bd2f65f4a510d5966786e8d3d.tar.xz libssh-93c7b81b4ea1046bd2f65f4a510d5966786e8d3d.zip |
ed25519: Generate, sign and verify keys.
Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
-rw-r--r-- | include/libssh/pki.h | 5 | ||||
-rw-r--r-- | include/libssh/pki_priv.h | 14 | ||||
-rw-r--r-- | src/CMakeLists.txt | 1 | ||||
-rw-r--r-- | src/pki.c | 81 | ||||
-rw-r--r-- | src/pki_crypto.c | 56 | ||||
-rw-r--r-- | src/pki_ed25519.c | 305 |
6 files changed, 460 insertions, 2 deletions
diff --git a/include/libssh/pki.h b/include/libssh/pki.h index 89a0f982..9f9ddf4a 100644 --- a/include/libssh/pki.h +++ b/include/libssh/pki.h @@ -21,6 +21,7 @@ #ifndef PKI_H_ #define PKI_H_ +#include "libssh/priv.h" #ifdef HAVE_OPENSSL_EC_H #include <openssl/ec.h> #endif @@ -29,6 +30,7 @@ #endif #include "libssh/crypto.h" +#include "libssh/ed25519.h" #define MAX_PUBKEY_SIZE 0x100000 /* 1M */ #define MAX_PRIVKEY_SIZE 0x400000 /* 4M */ @@ -55,6 +57,8 @@ struct ssh_key_struct { void *ecdsa; #endif /* HAVE_OPENSSL_EC_H */ #endif + ed25519_pubkey *ed25519_pubkey; + ed25519_privkey *ed25519_privkey; void *cert; }; @@ -74,6 +78,7 @@ struct ssh_signature_struct { void *ecdsa_sig; # endif #endif + ed25519_signature *ed25519_sig; }; typedef struct ssh_signature_struct *ssh_signature; diff --git a/include/libssh/pki_priv.h b/include/libssh/pki_priv.h index 0cbe8b72..0aaadb60 100644 --- a/include/libssh/pki_priv.h +++ b/include/libssh/pki_priv.h @@ -41,6 +41,8 @@ ssh_key pki_key_dup(const ssh_key key, int demote); int pki_key_generate_rsa(ssh_key key, int parameter); int pki_key_generate_dss(ssh_key key, int parameter); int pki_key_generate_ecdsa(ssh_key key, int parameter); +int pki_key_generate_ed25519(ssh_key key); + int pki_key_compare(const ssh_key k1, const ssh_key k2, enum ssh_keycmp_e what); @@ -91,4 +93,16 @@ ssh_signature pki_do_sign(const ssh_key privkey, ssh_signature pki_do_sign_sessionid(const ssh_key key, const unsigned char *hash, size_t hlen); +int pki_ed25519_sign(const ssh_key privkey, ssh_signature sig, + const unsigned char *hash, size_t hlen); +int pki_ed25519_verify(const ssh_key pubkey, ssh_signature sig, + const unsigned char *hash, size_t hlen); +int pki_ed25519_key_cmp(const ssh_key k1, + const ssh_key k2, + enum ssh_keycmp_e what); +int pki_ed25519_key_dup(ssh_key new, const ssh_key key); +int pki_ed25519_public_key_to_blob(ssh_buffer buffer, ssh_key key); +ssh_string pki_ed25519_sig_to_blob(ssh_signature sig); +int pki_ed25519_sig_from_blob(ssh_signature sig, ssh_string sig_blob); + #endif /* PKI_PRIV_H_ */ diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 446238ae..a4bc8595 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -139,6 +139,7 @@ set(libssh_SRCS packet_crypt.c pcap.c pki.c + pki_ed25519.c poll.c session.c sc25519.c @@ -157,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; @@ -207,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; } @@ -245,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; @@ -300,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; } @@ -311,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); } @@ -354,6 +367,9 @@ void ssh_signature_free(ssh_signature sig) ECDSA_SIG_free(sig->ecdsa_sig); #endif break; + case SSH_KEYTYPE_ED25519: + SAFE_FREE(sig->ed25519_sig); + break; case SSH_KEYTYPE_UNKNOWN: break; } @@ -749,7 +765,26 @@ static int pki_import_pubkey_buffer(ssh_buffer buffer, } 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"); + } + + key->ed25519_pubkey = malloc(ED25519_PK_LEN); + if (key->ed25519_pubkey == NULL) { + 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; } @@ -1010,6 +1045,12 @@ int ssh_pki_generate(enum ssh_keytypes_e type, int parameter, 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; } @@ -1336,6 +1377,8 @@ int ssh_pki_signature_verify_blob(ssh_session session, 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}; @@ -1402,6 +1445,25 @@ ssh_string ssh_pki_do_sign(ssh_session session, 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); + ssh_buffer_pack(buf, + "SP", + session_id, + buffer_get_rest_len(sigbuf), buffer_get_rest(sigbuf)); + + 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; @@ -1525,6 +1587,23 @@ ssh_string ssh_srv_pki_do_sign_sessionid(ssh_session session, 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; diff --git a/src/pki_crypto.c b/src/pki_crypto.c index 425e535d..5706fdf0 100644 --- a/src/pki_crypto.c +++ b/src/pki_crypto.c @@ -214,6 +214,7 @@ int pki_pubkey_build_ecdsa(ssh_key key, int nid, ssh_string e) ssh_key pki_key_dup(const ssh_key key, int demote) { ssh_key new; + int rc; new = ssh_key_new(); if (new == NULL) { @@ -371,7 +372,14 @@ ssh_key pki_key_dup(const ssh_key key, int demote) } break; #endif + case SSH_KEYTYPE_ED25519: + rc = pki_ed25519_key_dup(new, key); + if (rc != SSH_OK) { + goto fail; + } + break; case SSH_KEYTYPE_UNKNOWN: + default: ssh_key_free(new); return NULL; } @@ -533,7 +541,10 @@ int pki_key_compare(const ssh_key k1, break; } #endif + case SSH_KEYTYPE_ED25519: + /* ed25519 keys handled globaly */ case SSH_KEYTYPE_UNKNOWN: + default: return 1; } @@ -636,6 +647,7 @@ ssh_string pki_private_key_to_pem(const ssh_key key, } break; #endif + case SSH_KEYTYPE_ED25519: case SSH_KEYTYPE_UNKNOWN: BIO_free(mem); ssh_pki_log("Unkown or invalid private key type %d", key->type); @@ -759,6 +771,7 @@ ssh_key pki_private_key_from_base64(const char *b64_key, break; #endif + case SSH_KEYTYPE_ED25519: case SSH_KEYTYPE_UNKNOWN: BIO_free(mem); ssh_pki_log("Unkown or invalid private key type %d", type); @@ -997,7 +1010,14 @@ ssh_string pki_publickey_to_blob(const ssh_key key) break; #endif + case SSH_KEYTYPE_ED25519: + rc = pki_ed25519_public_key_to_blob(buffer, key); + if (rc == SSH_ERROR){ + goto fail; + } + break; case SSH_KEYTYPE_UNKNOWN: + default: goto fail; } @@ -1216,6 +1236,10 @@ ssh_string pki_signature_to_blob(const ssh_signature sig) break; } #endif + case SSH_KEYTYPE_ED25519: + sig_blob = pki_ed25519_sig_to_blob(sig); + break; + default: case SSH_KEYTYPE_UNKNOWN: ssh_pki_log("Unknown signature key type: %s", sig->type_c); return NULL; @@ -1293,6 +1317,7 @@ ssh_signature pki_signature_from_blob(const ssh_key pubkey, ssh_string r; ssh_string s; size_t len; + int rc; sig = ssh_signature_new(); if (sig == NULL) { @@ -1369,7 +1394,6 @@ ssh_signature pki_signature_from_blob(const ssh_key pubkey, { /* build ecdsa siganature */ ssh_buffer b; uint32_t rlen; - int rc; b = ssh_buffer_new(); if (b == NULL) { @@ -1437,6 +1461,14 @@ ssh_signature pki_signature_from_blob(const ssh_key pubkey, break; #endif + case SSH_KEYTYPE_ED25519: + rc = pki_ed25519_sig_from_blob(sig, sig_blob); + if (rc == SSH_ERROR){ + ssh_signature_free(sig); + return NULL; + } + break; + default: case SSH_KEYTYPE_UNKNOWN: ssh_pki_log("Unknown signature type"); ssh_signature_free(sig); @@ -1484,6 +1516,15 @@ int pki_signature_verify(ssh_session session, return SSH_ERROR; } break; + case SSH_KEYTYPE_ED25519: + rc = pki_ed25519_verify(key, sig, hash, hlen); + if (rc != SSH_OK){ + ssh_set_error(session, + SSH_FATAL, + "ed25519 signature verification error"); + return SSH_ERROR; + } + break; case SSH_KEYTYPE_ECDSA: #ifdef HAVE_OPENSSL_ECC rc = ECDSA_do_verify(hash, @@ -1500,6 +1541,7 @@ int pki_signature_verify(ssh_session session, break; #endif case SSH_KEYTYPE_UNKNOWN: + default: ssh_set_error(session, SSH_FATAL, "Unknown public key type"); return SSH_ERROR; } @@ -1511,6 +1553,7 @@ ssh_signature pki_do_sign(const ssh_key privkey, const unsigned char *hash, size_t hlen) { ssh_signature sig; + int rc; sig = ssh_signature_new(); if (sig == NULL) { @@ -1558,7 +1601,15 @@ ssh_signature pki_do_sign(const ssh_key privkey, break; #endif /* HAVE_OPENSSL_ECC */ + case SSH_KEYTYPE_ED25519: + rc = pki_ed25519_sign(privkey, sig, hash, hlen); + if (rc != SSH_OK){ + ssh_signature_free(sig); + return NULL; + } + break; case SSH_KEYTYPE_UNKNOWN: + default: ssh_signature_free(sig); return NULL; } @@ -1605,7 +1656,10 @@ ssh_signature pki_do_sign_sessionid(const ssh_key key, } break; #endif + case SSH_KEYTYPE_ED25519: + /* ED25519 handled in caller */ case SSH_KEYTYPE_UNKNOWN: + default: ssh_signature_free(sig); return NULL; } diff --git a/src/pki_ed25519.c b/src/pki_ed25519.c new file mode 100644 index 00000000..918db843 --- /dev/null +++ b/src/pki_ed25519.c @@ -0,0 +1,305 @@ +/* + * pki_ed25519 .c - PKI infrastructure using ed25519 + * + * This file is part of the SSH Library + * + * Copyright (c) 2014 by Aris Adamantiadis + * + * 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 + * the Free Software Foundation; either version 2.1 of the License, or (at your + * option) any later version. + * + * The SSH Library 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 Lesser General Public + * License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with the SSH Library; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, + * MA 02111-1307, USA. + */ + +#include "libssh/pki.h" +#include "libssh/pki_priv.h" +#include "libssh/ed25519.h" +#include "libssh/buffer.h" + +int pki_key_generate_ed25519(ssh_key key) +{ + int rc; + + key->ed25519_privkey = malloc(sizeof (ed25519_privkey)); + if (key->ed25519_privkey == NULL) { + goto error; + } + + key->ed25519_pubkey = malloc(sizeof (ed25519_privkey)); + if (key->ed25519_privkey == NULL) { + goto error; + } + + rc = crypto_sign_ed25519_keypair(*key->ed25519_pubkey, + *key->ed25519_privkey); + if (rc != 0) { + goto error; + } + + return SSH_OK; +error: + SAFE_FREE(key->ed25519_privkey); + SAFE_FREE(key->ed25519_pubkey); + + return SSH_ERROR; +} + +int pki_ed25519_sign(const ssh_key privkey, + ssh_signature sig, + const unsigned char *hash, + size_t hlen) +{ + uint8_t *buffer = malloc(hlen + ED25519_SIG_LEN); + unsigned long long dlen = 0; + int rc; + + buffer = malloc(hlen + ED25519_SIG_LEN); + if (buffer == NULL) { + return SSH_ERROR; + } + + rc = crypto_sign_ed25519(buffer, + &dlen, + hash, + hlen, + *privkey->ed25519_privkey); + if (rc != 0) { + goto error; + } + sig->ed25519_sig = malloc(ED25519_SIG_LEN); + if (sig->ed25519_sig == NULL) { + goto error; + } + + /* This shouldn't happen */ + if (dlen - hlen != ED25519_SIG_LEN) { + goto error; + } + memcpy(sig->ed25519_sig, buffer, dlen - hlen); + SAFE_FREE(buffer); + + return SSH_OK; +error: + SAFE_FREE(buffer); + return SSH_ERROR; +} + +int pki_ed25519_verify(const ssh_key pubkey, + ssh_signature sig, + const unsigned char *hash, + size_t hlen) +{ + unsigned long long mlen = 0; + uint8_t *buffer; + uint8_t *buffer2; + int rc; + + if (pubkey == NULL || sig == NULL || + hash == NULL || sig->ed25519_sig == NULL) { + return SSH_ERROR; + } + + buffer = malloc(hlen + ED25519_SIG_LEN); + if (buffer == NULL) { + return SSH_ERROR; + } + + buffer2 = malloc(hlen + ED25519_SIG_LEN); + if (buffer2 == NULL) { + goto error; + } + + memcpy(buffer, sig->ed25519_sig, ED25519_SIG_LEN); + memcpy(buffer + ED25519_SIG_LEN, hash, hlen); + + rc = crypto_sign_ed25519_open(buffer2, + &mlen, + buffer, + hlen + ED25519_SIG_LEN, + *pubkey->ed25519_pubkey); + + BURN_BUFFER(buffer, hlen + ED25519_SIG_LEN); + BURN_BUFFER(buffer2, hlen); + SAFE_FREE(buffer); + SAFE_FREE(buffer2); + if (rc == 0) { + return SSH_OK; + } else { + return SSH_ERROR; + } +error: + SAFE_FREE(buffer); + SAFE_FREE(buffer2); + + return SSH_ERROR; +} + +/** + * @internal + * + * @brief Compare ed25519 keys if they are equal. + * + * @param[in] k1 The first key to compare. + * + * @param[in] k2 The second key to compare. + * + * @param[in] what What part or type of the key do you want to compare. + * + * @return 0 if equal, 1 if not. + */ +int pki_ed25519_key_cmp(const ssh_key k1, + const ssh_key k2, + enum ssh_keycmp_e what) +{ + int cmp; + + switch(what) { + case SSH_KEY_CMP_PRIVATE: + if (k1->ed25519_privkey == NULL || k2->ed25519_privkey == NULL) { + return 1; + } + cmp = memcmp(k1->ed25519_privkey, k2->ed25519_privkey, ED25519_SK_LEN); + if (cmp != 0) { + return 1; + } + /* FALL THROUGH */ + case SSH_KEY_CMP_PUBLIC: + if (k1->ed25519_pubkey == NULL || k2->ed25519_pubkey == NULL) { + return 1; + } + cmp = memcmp(k1->ed25519_pubkey, k2->ed25519_pubkey, ED25519_PK_LEN); + if (cmp != 0) { + return 1; + } + } + + return 0; +} + +/** + * @internal + * + * @brief duplicate an ed25519 key + * + * @param[out\ new preinitialized output ssh_ke + * + * @param[in] key key to copy + * + * @return SSH_ERROR on error, SSH_OK on success + */ +int pki_ed25519_key_dup(ssh_key new, const ssh_key key) +{ + if (key->ed25519_privkey == NULL || key->ed25519_pubkey == NULL) { + return SSH_ERROR; + } + + new->ed25519_privkey = malloc(ED25519_SK_LEN); + if (new->ed25519_privkey == NULL) { + return SSH_ERROR; + } + + new->ed25519_pubkey = malloc(ED25519_PK_LEN); + if (new->ed25519_privkey == NULL || new->ed25519_pubkey == NULL){ + SAFE_FREE(new->ed25519_privkey); + return SSH_ERROR; + } + + memcpy(new->ed25519_privkey, key->ed25519_privkey, ED25519_SK_LEN); + memcpy(new->ed25519_pubkey, key->ed25519_pubkey, ED25519_PK_LEN); + + return SSH_OK; +} + +/** + * @internal + * + * @brief outputs an ed25519 public key in a blob buffer. + * + * @param[out] buffer output buffer + * + * @param[in] key key to output + * + * @return SSH_ERROR on error, SSH_OK on success + */ +int pki_ed25519_public_key_to_blob(ssh_buffer buffer, ssh_key key) +{ + int rc; + + if (key->ed25519_pubkey == NULL){ + return SSH_ERROR; + } + + rc = ssh_buffer_pack(buffer, + "dP", + (uint32_t)ED25519_PK_LEN, + (size_t)ED25519_PK_LEN, key->ed25519_pubkey); + + return rc; +} + +/** + * @internal + * + * @brief output a signature blob from an ed25519 signature + * + * @param[in] sig signature to convert + * + * @return Signature blob in SSH string, or NULL on error + */ +ssh_string pki_ed25519_sig_to_blob(ssh_signature sig) +{ + ssh_string sig_blob; + + if (sig->ed25519_sig == NULL) { + return NULL; + } + + sig_blob = ssh_string_new(ED25519_SIG_LEN); + if (sig_blob == NULL) { + return NULL; + } + ssh_string_fill(sig_blob, sig->ed25519_sig, ED25519_SIG_LEN); + + return sig_blob; +} + +/** + * @internal + * + * @brief Convert a signature blob in an ed25519 signature. + * + * @param[out] sig a preinitialized signature + * + * @param[in] sig_blob a signature blob + * + * @return SSH_ERROR on error, SSH_OK on success + */ +int pki_ed25519_sig_from_blob(ssh_signature sig, ssh_string sig_blob) +{ + size_t len; + + len = ssh_string_len(sig_blob); + if (len != ED25519_SIG_LEN){ + ssh_pki_log("Invalid ssh-ed25519 signature len: %zu", len); + return SSH_ERROR; + } + + sig->ed25519_sig = malloc(ED25519_SIG_LEN); + if (sig->ed25519_sig == NULL){ + return SSH_ERROR; + } + + memcpy(sig->ed25519_sig, ssh_string_data(sig_blob), ED25519_SIG_LEN); + + return SSH_OK; +} |