mirror of
https://git.libssh.org/projects/libssh.git
synced 2026-02-04 12:20:42 +09:00
Use Ed25519 in OpenSSL through the EVP_PKEY API
... instead of keeping around public and private key blobs. Signed-off-by: Jakub Jelen <jjelen@redhat.com> Reviewed-by: Sahana Prasad <sahana@redhat.com>
This commit is contained in:
@@ -63,11 +63,9 @@ struct ssh_key_struct {
|
||||
mbedtls_pk_context *pk;
|
||||
mbedtls_ecdsa_context *ecdsa;
|
||||
#elif defined(HAVE_LIBCRYPTO)
|
||||
/* This holds either ENGINE key for PKCS#11 support or just key in
|
||||
* high-level format */
|
||||
/* This holds either ENGINE/PROVIDER key for PKCS#11 support
|
||||
* or just key in high-level format */
|
||||
EVP_PKEY *key;
|
||||
uint8_t *ed25519_pubkey;
|
||||
uint8_t *ed25519_privkey;
|
||||
#endif /* HAVE_LIBGCRYPT */
|
||||
#ifndef HAVE_LIBCRYPTO
|
||||
ed25519_pubkey *ed25519_pubkey;
|
||||
|
||||
@@ -148,6 +148,7 @@ int pki_signature_from_ed25519_blob(ssh_signature sig, ssh_string sig_blob);
|
||||
int pki_privkey_build_ed25519(ssh_key key,
|
||||
ssh_string pubkey,
|
||||
ssh_string privkey);
|
||||
int pki_pubkey_build_ed25519(ssh_key key, ssh_string pubkey);
|
||||
|
||||
/* PKI Container OpenSSH */
|
||||
ssh_key ssh_pki_openssh_pubkey_import(const char *text_key);
|
||||
|
||||
@@ -129,6 +129,7 @@ int evp_build_pkey(const char* name, OSSL_PARAM_BLD *param_bld, EVP_PKEY **pkey,
|
||||
int evp_dup_dsa_pkey(const ssh_key key, ssh_key new_key, int demote);
|
||||
int evp_dup_rsa_pkey(const ssh_key key, ssh_key new_key, int demote);
|
||||
int evp_dup_ecdsa_pkey(const ssh_key key, ssh_key new_key, int demote);
|
||||
int evp_dup_ed25519_pkey(const ssh_key key, ssh_key new_key, int demote);
|
||||
#endif /* HAVE_LIBCRYPTO && OPENSSL_VERSION_NUMBER */
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
@@ -1543,6 +1543,12 @@ int evp_dup_ecdsa_pkey(const ssh_key key, ssh_key new_key, int demote)
|
||||
{
|
||||
return evp_dup_pkey("EC", key, demote, new_key);
|
||||
}
|
||||
|
||||
int evp_dup_ed25519_pkey(const ssh_key key, ssh_key new_key, int demote)
|
||||
{
|
||||
return evp_dup_pkey("ED25519", key, demote, new_key);
|
||||
}
|
||||
|
||||
#endif /* OPENSSL_VERSION_NUMBER */
|
||||
|
||||
ssh_string
|
||||
|
||||
26
src/pki.c
26
src/pki.c
@@ -143,18 +143,13 @@ void ssh_key_clean (ssh_key key)
|
||||
|
||||
pki_key_clean(key);
|
||||
|
||||
if (key->ed25519_privkey != NULL){
|
||||
#ifdef HAVE_LIBCRYPTO
|
||||
/* In OpenSSL implementation the private key is only the private
|
||||
* original seed. In the internal implementation the private key is the
|
||||
* concatenation of the original private seed with the public key.*/
|
||||
explicit_bzero(key->ed25519_privkey, ED25519_KEY_LEN);
|
||||
#else
|
||||
#ifndef HAVE_LIBCRYPTO
|
||||
if (key->ed25519_privkey != NULL) {
|
||||
explicit_bzero(key->ed25519_privkey, sizeof(ed25519_privkey));
|
||||
#endif /* HAVE_LIBCRYPTO*/
|
||||
SAFE_FREE(key->ed25519_privkey);
|
||||
}
|
||||
SAFE_FREE(key->ed25519_pubkey);
|
||||
#endif /* HAVE_LIBCRYPTO */
|
||||
if (key->cert != NULL) {
|
||||
SSH_BUFFER_FREE(key->cert);
|
||||
}
|
||||
@@ -701,10 +696,12 @@ int ssh_key_cmp(const ssh_key k1,
|
||||
ssh_buffer_get_len(k1->cert));
|
||||
}
|
||||
|
||||
#ifndef HAVE_LIBCRYPTO
|
||||
if (k1->type == SSH_KEYTYPE_ED25519 ||
|
||||
k1->type == SSH_KEYTYPE_SK_ED25519) {
|
||||
return pki_ed25519_key_cmp(k1, k2, what);
|
||||
}
|
||||
#endif
|
||||
|
||||
return pki_key_compare(k1, k2, what);
|
||||
}
|
||||
@@ -1489,16 +1486,13 @@ static int pki_import_pubkey_buffer(ssh_buffer buffer,
|
||||
goto fail;
|
||||
}
|
||||
|
||||
key->ed25519_pubkey = malloc(ED25519_KEY_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_KEY_LEN);
|
||||
rc = pki_pubkey_build_ed25519(key, pubkey);
|
||||
ssh_string_burn(pubkey);
|
||||
SSH_STRING_FREE(pubkey);
|
||||
if (rc < 0) {
|
||||
SSH_LOG(SSH_LOG_TRACE, "Failed to build ED25519 public key");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (type == SSH_KEYTYPE_SK_ED25519) {
|
||||
ssh_string application = ssh_buffer_get_ssh_string(buffer);
|
||||
|
||||
319
src/pki_crypto.c
319
src/pki_crypto.c
@@ -415,6 +415,59 @@ err:
|
||||
}
|
||||
#endif /* HAVE_OPENSSL_ECC */
|
||||
|
||||
int pki_privkey_build_ed25519(ssh_key key,
|
||||
ssh_string pubkey,
|
||||
ssh_string privkey)
|
||||
{
|
||||
EVP_PKEY *pkey = NULL;
|
||||
|
||||
if (ssh_string_len(pubkey) != ED25519_KEY_LEN ||
|
||||
ssh_string_len(privkey) != (2 * ED25519_KEY_LEN)) {
|
||||
SSH_LOG(SSH_LOG_TRACE, "Invalid ed25519 key len");
|
||||
return SSH_ERROR;
|
||||
}
|
||||
|
||||
pkey = EVP_PKEY_new_raw_private_key(EVP_PKEY_ED25519,
|
||||
NULL,
|
||||
(const uint8_t *)ssh_string_data(privkey),
|
||||
ED25519_KEY_LEN);
|
||||
if (pkey == NULL) {
|
||||
SSH_LOG(SSH_LOG_TRACE,
|
||||
"Failed to create ed25519 EVP_PKEY: %s",
|
||||
ERR_error_string(ERR_get_error(), NULL));
|
||||
return SSH_ERROR;
|
||||
}
|
||||
|
||||
key->key = pkey;
|
||||
|
||||
return SSH_OK;
|
||||
}
|
||||
|
||||
int pki_pubkey_build_ed25519(ssh_key key, ssh_string pubkey)
|
||||
{
|
||||
EVP_PKEY *pkey = NULL;
|
||||
|
||||
if (ssh_string_len(pubkey) != ED25519_KEY_LEN) {
|
||||
SSH_LOG(SSH_LOG_TRACE, "Invalid ed25519 key len");
|
||||
return SSH_ERROR;
|
||||
}
|
||||
|
||||
pkey = EVP_PKEY_new_raw_public_key(EVP_PKEY_ED25519,
|
||||
NULL,
|
||||
(const uint8_t *)ssh_string_data(pubkey),
|
||||
ED25519_KEY_LEN);
|
||||
if (pkey == NULL) {
|
||||
SSH_LOG(SSH_LOG_TRACE,
|
||||
"Failed to create ed25519 EVP_PKEY: %s",
|
||||
ERR_error_string(ERR_get_error(), NULL));
|
||||
return SSH_ERROR;
|
||||
}
|
||||
|
||||
key->key = pkey;
|
||||
|
||||
return SSH_OK;
|
||||
}
|
||||
|
||||
ssh_key pki_key_dup(const ssh_key key, int demote)
|
||||
{
|
||||
ssh_key new = NULL;
|
||||
@@ -651,7 +704,7 @@ ssh_key pki_key_dup(const ssh_key key, int demote)
|
||||
break;
|
||||
#endif /* HAVE_OPENSSL_ECC */
|
||||
case SSH_KEYTYPE_ED25519:
|
||||
rc = pki_ed25519_key_dup(new, key);
|
||||
rc = evp_dup_ed25519_pkey(key, new, demote);
|
||||
if (rc != SSH_OK) {
|
||||
goto fail;
|
||||
}
|
||||
@@ -865,14 +918,13 @@ int pki_key_compare(const ssh_key k1, const ssh_key k2, enum ssh_keycmp_e what)
|
||||
#endif /* OPENSSL_VERSION_NUMBER */
|
||||
case SSH_KEYTYPE_RSA:
|
||||
case SSH_KEYTYPE_RSA1:
|
||||
case SSH_KEYTYPE_ED25519:
|
||||
case SSH_KEYTYPE_SK_ED25519:
|
||||
rc = EVP_PKEY_eq(k1->key, k2->key);
|
||||
if (rc != 1) {
|
||||
return 1;
|
||||
}
|
||||
break;
|
||||
case SSH_KEYTYPE_ED25519:
|
||||
case SSH_KEYTYPE_SK_ED25519:
|
||||
/* ed25519 keys handled globally */
|
||||
case SSH_KEYTYPE_UNKNOWN:
|
||||
default:
|
||||
return 1;
|
||||
@@ -902,6 +954,7 @@ ssh_string pki_private_key_to_pem(const ssh_key key,
|
||||
case SSH_KEYTYPE_ECDSA_P256:
|
||||
case SSH_KEYTYPE_ECDSA_P384:
|
||||
case SSH_KEYTYPE_ECDSA_P521:
|
||||
case SSH_KEYTYPE_ED25519:
|
||||
rc = EVP_PKEY_up_ref(key->key);
|
||||
if (rc != 1) {
|
||||
goto err;
|
||||
@@ -911,24 +964,6 @@ ssh_string pki_private_key_to_pem(const ssh_key key,
|
||||
/* Mark the operation as successful as for the other key types */
|
||||
rc = 1;
|
||||
|
||||
break;
|
||||
case SSH_KEYTYPE_ED25519:
|
||||
/* In OpenSSL, the input is the private key seed only, which means
|
||||
* the first half of the SSH private key (the second half is the
|
||||
* public key) */
|
||||
pkey = EVP_PKEY_new_raw_private_key(EVP_PKEY_ED25519,
|
||||
NULL,
|
||||
(const uint8_t *)key->ed25519_privkey,
|
||||
ED25519_KEY_LEN);
|
||||
if (pkey == NULL) {
|
||||
SSH_LOG(SSH_LOG_TRACE,
|
||||
"Failed to create ed25519 EVP_PKEY: %s",
|
||||
ERR_error_string(ERR_get_error(), NULL));
|
||||
goto err;
|
||||
}
|
||||
|
||||
/* Mark the operation as successful as for the other key types */
|
||||
rc = 1;
|
||||
break;
|
||||
case SSH_KEYTYPE_RSA_CERT01:
|
||||
case SSH_KEYTYPE_ECDSA_P256_CERT01:
|
||||
@@ -1008,8 +1043,6 @@ ssh_key pki_private_key_from_base64(const char *b64_key,
|
||||
#if OPENSSL_VERSION_NUMBER < 0x30000000L
|
||||
EC_KEY *ecdsa = NULL;
|
||||
#endif /* OPENSSL_VERSION_NUMBER */
|
||||
uint8_t *ed25519 = NULL;
|
||||
uint8_t *ed25519_pubkey = NULL;
|
||||
ssh_key key = NULL;
|
||||
enum ssh_keytypes_e type = SSH_KEYTYPE_UNKNOWN;
|
||||
EVP_PKEY *pkey = NULL;
|
||||
@@ -1068,57 +1101,8 @@ ssh_key pki_private_key_from_base64(const char *b64_key,
|
||||
break;
|
||||
#endif /* HAVE_OPENSSL_ECC */
|
||||
case EVP_PKEY_ED25519:
|
||||
{
|
||||
size_t key_len;
|
||||
int evp_rc = 0;
|
||||
|
||||
/* Get the key length */
|
||||
evp_rc = EVP_PKEY_get_raw_private_key(pkey, NULL, &key_len);
|
||||
if (evp_rc != 1) {
|
||||
SSH_LOG(SSH_LOG_TRACE,
|
||||
"Failed to get ed25519 raw private key length: %s",
|
||||
ERR_error_string(ERR_get_error(), NULL));
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (key_len != ED25519_KEY_LEN) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
ed25519 = malloc(key_len);
|
||||
if (ed25519 == NULL) {
|
||||
SSH_LOG(SSH_LOG_TRACE, "Out of memory");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
evp_rc = EVP_PKEY_get_raw_private_key(pkey, (uint8_t *)ed25519,
|
||||
&key_len);
|
||||
if (evp_rc != 1) {
|
||||
SSH_LOG(SSH_LOG_TRACE,
|
||||
"Failed to get ed25519 raw private key: %s",
|
||||
ERR_error_string(ERR_get_error(), NULL));
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* length matches the private key length */
|
||||
ed25519_pubkey = malloc(ED25519_KEY_LEN);
|
||||
if (ed25519_pubkey == NULL) {
|
||||
SSH_LOG(SSH_LOG_TRACE, "Out of memory");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
evp_rc = EVP_PKEY_get_raw_public_key(pkey, (uint8_t *)ed25519_pubkey,
|
||||
&key_len);
|
||||
if (evp_rc != 1) {
|
||||
SSH_LOG(SSH_LOG_TRACE,
|
||||
"Failed to get ed25519 raw public key: %s",
|
||||
ERR_error_string(ERR_get_error(), NULL));
|
||||
goto fail;
|
||||
}
|
||||
type = SSH_KEYTYPE_ED25519;
|
||||
|
||||
}
|
||||
break;
|
||||
break;
|
||||
default:
|
||||
SSH_LOG(SSH_LOG_TRACE, "Unknown or invalid private key type %d",
|
||||
EVP_PKEY_base_id(pkey));
|
||||
@@ -1135,8 +1119,6 @@ ssh_key pki_private_key_from_base64(const char *b64_key,
|
||||
key->type_c = ssh_key_type_to_char(type);
|
||||
key->flags = SSH_KEY_FLAG_PRIVATE | SSH_KEY_FLAG_PUBLIC;
|
||||
key->key = pkey;
|
||||
key->ed25519_privkey = ed25519;
|
||||
key->ed25519_pubkey = ed25519_pubkey;
|
||||
#ifdef HAVE_OPENSSL_ECC
|
||||
if (is_ecdsa_key_type(key->type)) {
|
||||
#if OPENSSL_VERSION_NUMBER < 0x30000000L
|
||||
@@ -1151,8 +1133,6 @@ ssh_key pki_private_key_from_base64(const char *b64_key,
|
||||
fail:
|
||||
EVP_PKEY_free(pkey);
|
||||
ssh_key_free(key);
|
||||
SAFE_FREE(ed25519);
|
||||
SAFE_FREE(ed25519_pubkey);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -1408,8 +1388,8 @@ fail:
|
||||
|
||||
ssh_string pki_key_to_blob(const ssh_key key, enum ssh_key_e type)
|
||||
{
|
||||
ssh_buffer buffer;
|
||||
ssh_string type_s;
|
||||
ssh_buffer buffer = NULL;
|
||||
ssh_string type_s = NULL;
|
||||
ssh_string str = NULL;
|
||||
ssh_string e = NULL;
|
||||
ssh_string n = NULL;
|
||||
@@ -1425,6 +1405,9 @@ ssh_string pki_key_to_blob(const ssh_key key, enum ssh_key_e type)
|
||||
*bd = NULL, *biqmp = NULL;
|
||||
OSSL_PARAM *params = NULL;
|
||||
#endif /* OPENSSL_VERSION_NUMBER */
|
||||
uint8_t *ed25519_pubkey = NULL;
|
||||
uint8_t *ed25519_privkey = NULL;
|
||||
size_t key_len = 0;
|
||||
|
||||
buffer = ssh_buffer_new();
|
||||
if (buffer == NULL) {
|
||||
@@ -1632,11 +1615,94 @@ ssh_string pki_key_to_blob(const ssh_key key, enum ssh_key_e type)
|
||||
}
|
||||
case SSH_KEYTYPE_ED25519:
|
||||
case SSH_KEYTYPE_SK_ED25519:
|
||||
if (type == SSH_KEY_PUBLIC) {
|
||||
rc = pki_ed25519_public_key_to_blob(buffer, key);
|
||||
if (rc == SSH_ERROR){
|
||||
rc = EVP_PKEY_get_raw_public_key(key->key, NULL, &key_len);
|
||||
if (rc != 1) {
|
||||
SSH_LOG(SSH_LOG_TRACE,
|
||||
"Failed to get ed25519 raw public key length: %s",
|
||||
ERR_error_string(ERR_get_error(), NULL));
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (key_len != ED25519_KEY_LEN) {
|
||||
SSH_LOG(SSH_LOG_TRACE,
|
||||
"Unexpected length of private key %zu. Expected %d.",
|
||||
key_len,
|
||||
ED25519_KEY_LEN);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
ed25519_pubkey = malloc(key_len);
|
||||
if (ed25519_pubkey == NULL) {
|
||||
SSH_LOG(SSH_LOG_TRACE, "Out of memory");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
rc = EVP_PKEY_get_raw_public_key(key->key,
|
||||
(uint8_t *)ed25519_pubkey,
|
||||
&key_len);
|
||||
if (rc != 1) {
|
||||
SSH_LOG(SSH_LOG_TRACE,
|
||||
"Failed to get ed25519 raw public key: %s",
|
||||
ERR_error_string(ERR_get_error(), NULL));
|
||||
goto fail;
|
||||
}
|
||||
|
||||
rc = ssh_buffer_pack(buffer,
|
||||
"dP",
|
||||
(uint32_t)ED25519_KEY_LEN,
|
||||
(size_t)ED25519_KEY_LEN,
|
||||
ed25519_pubkey);
|
||||
if (rc == SSH_ERROR) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (type == SSH_KEY_PRIVATE) {
|
||||
key_len = 0;
|
||||
rc = EVP_PKEY_get_raw_private_key(key->key, NULL, &key_len);
|
||||
if (rc != 1) {
|
||||
SSH_LOG(SSH_LOG_TRACE,
|
||||
"Failed to get ed25519 raw private key length: %s",
|
||||
ERR_error_string(ERR_get_error(), NULL));
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (key_len != ED25519_KEY_LEN) {
|
||||
SSH_LOG(SSH_LOG_TRACE,
|
||||
"Unexpected length of private key %zu. Expected %d.",
|
||||
key_len,
|
||||
ED25519_KEY_LEN);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
ed25519_privkey = malloc(key_len);
|
||||
if (ed25519_privkey == NULL) {
|
||||
SSH_LOG(SSH_LOG_TRACE, "Out of memory");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
rc = EVP_PKEY_get_raw_private_key(key->key,
|
||||
ed25519_privkey,
|
||||
&key_len);
|
||||
if (rc != 1) {
|
||||
SSH_LOG(SSH_LOG_TRACE,
|
||||
"Failed to get ed25519 raw private key: %s",
|
||||
ERR_error_string(ERR_get_error(), NULL));
|
||||
goto fail;
|
||||
}
|
||||
|
||||
rc = ssh_buffer_pack(buffer,
|
||||
"dPP",
|
||||
(uint32_t)(2 * ED25519_KEY_LEN),
|
||||
(size_t)ED25519_KEY_LEN,
|
||||
ed25519_privkey,
|
||||
(size_t)ED25519_KEY_LEN,
|
||||
ed25519_pubkey);
|
||||
if (rc == SSH_ERROR) {
|
||||
goto fail;
|
||||
}
|
||||
explicit_bzero(ed25519_privkey, ED25519_KEY_LEN);
|
||||
SAFE_FREE(ed25519_privkey);
|
||||
} else {
|
||||
/* public key can contain certificate sk information */
|
||||
if (key->type == SSH_KEYTYPE_SK_ED25519) {
|
||||
rc = ssh_buffer_add_ssh_string(buffer, key->sk_application);
|
||||
@@ -1644,12 +1710,8 @@ ssh_string pki_key_to_blob(const ssh_key key, enum ssh_key_e type)
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
rc = pki_ed25519_private_key_to_blob(buffer, key);
|
||||
if (rc == SSH_ERROR){
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
SAFE_FREE(ed25519_pubkey);
|
||||
break;
|
||||
case SSH_KEYTYPE_ECDSA_P256:
|
||||
case SSH_KEYTYPE_ECDSA_P384:
|
||||
@@ -1849,6 +1911,11 @@ fail:
|
||||
bignum_safe_free(biqmp);
|
||||
OSSL_PARAM_free(params);
|
||||
#endif /* OPENSSL_VERSION_NUMBER */
|
||||
free(ed25519_pubkey);
|
||||
if (ed25519_privkey) {
|
||||
explicit_bzero(ed25519_privkey, ED25519_KEY_LEN);
|
||||
free(ed25519_privkey);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
@@ -2300,6 +2367,10 @@ static EVP_PKEY *pki_key_to_pkey(ssh_key key)
|
||||
case SSH_KEYTYPE_ECDSA_P521_CERT01:
|
||||
case SSH_KEYTYPE_SK_ECDSA:
|
||||
case SSH_KEYTYPE_SK_ECDSA_CERT01:
|
||||
case SSH_KEYTYPE_ED25519:
|
||||
case SSH_KEYTYPE_ED25519_CERT01:
|
||||
case SSH_KEYTYPE_SK_ED25519:
|
||||
case SSH_KEYTYPE_SK_ED25519_CERT01:
|
||||
if (key->key == NULL) {
|
||||
SSH_LOG(SSH_LOG_TRACE, "NULL key->key");
|
||||
goto error;
|
||||
@@ -2311,37 +2382,6 @@ static EVP_PKEY *pki_key_to_pkey(ssh_key key)
|
||||
}
|
||||
pkey = key->key;
|
||||
break;
|
||||
case SSH_KEYTYPE_ED25519:
|
||||
case SSH_KEYTYPE_ED25519_CERT01:
|
||||
case SSH_KEYTYPE_SK_ED25519:
|
||||
case SSH_KEYTYPE_SK_ED25519_CERT01:
|
||||
if (ssh_key_is_private(key)) {
|
||||
if (key->ed25519_privkey == NULL) {
|
||||
SSH_LOG(SSH_LOG_TRACE, "NULL key->ed25519_privkey");
|
||||
goto error;
|
||||
}
|
||||
/* In OpenSSL, the input is the private key seed only, which means
|
||||
* the first half of the SSH private key (the second half is the
|
||||
* public key). Both keys have the same length (32 bytes) */
|
||||
pkey = EVP_PKEY_new_raw_private_key(EVP_PKEY_ED25519, NULL,
|
||||
(const uint8_t *)key->ed25519_privkey,
|
||||
ED25519_KEY_LEN);
|
||||
} else {
|
||||
if (key->ed25519_pubkey == NULL) {
|
||||
SSH_LOG(SSH_LOG_TRACE, "NULL key->ed25519_pubkey");
|
||||
goto error;
|
||||
}
|
||||
pkey = EVP_PKEY_new_raw_public_key(EVP_PKEY_ED25519, NULL,
|
||||
(const uint8_t *)key->ed25519_pubkey,
|
||||
ED25519_KEY_LEN);
|
||||
}
|
||||
if (pkey == NULL) {
|
||||
SSH_LOG(SSH_LOG_TRACE,
|
||||
"Failed to create ed25519 EVP_PKEY: %s",
|
||||
ERR_error_string(ERR_get_error(), NULL));
|
||||
return NULL;
|
||||
}
|
||||
break;
|
||||
case SSH_KEYTYPE_UNKNOWN:
|
||||
default:
|
||||
SSH_LOG(SSH_LOG_TRACE, "Unknown private key algorithm for type: %d",
|
||||
@@ -2628,8 +2668,6 @@ int pki_key_generate_ed25519(ssh_key key)
|
||||
int evp_rc;
|
||||
EVP_PKEY_CTX *pctx = NULL;
|
||||
EVP_PKEY *pkey = NULL;
|
||||
size_t privkey_len = ED25519_KEY_LEN;
|
||||
size_t pubkey_len = ED25519_KEY_LEN;
|
||||
|
||||
if (key == NULL) {
|
||||
return SSH_ERROR;
|
||||
@@ -2658,41 +2696,9 @@ int pki_key_generate_ed25519(ssh_key key)
|
||||
ERR_error_string(ERR_get_error(), NULL));
|
||||
goto error;
|
||||
}
|
||||
|
||||
key->ed25519_privkey = malloc(ED25519_KEY_LEN);
|
||||
if (key->ed25519_privkey == NULL) {
|
||||
SSH_LOG(SSH_LOG_TRACE,
|
||||
"Failed to allocate memory for ed25519 private key");
|
||||
goto error;
|
||||
}
|
||||
|
||||
key->ed25519_pubkey = malloc(ED25519_KEY_LEN);
|
||||
if (key->ed25519_pubkey == NULL) {
|
||||
SSH_LOG(SSH_LOG_TRACE,
|
||||
"Failed to allocate memory for ed25519 public key");
|
||||
goto error;
|
||||
}
|
||||
|
||||
evp_rc = EVP_PKEY_get_raw_private_key(pkey, (uint8_t *)key->ed25519_privkey,
|
||||
&privkey_len);
|
||||
if (evp_rc != 1) {
|
||||
SSH_LOG(SSH_LOG_TRACE,
|
||||
"Failed to get ed25519 raw private key: %s",
|
||||
ERR_error_string(ERR_get_error(), NULL));
|
||||
goto error;
|
||||
}
|
||||
|
||||
evp_rc = EVP_PKEY_get_raw_public_key(pkey, (uint8_t *)key->ed25519_pubkey,
|
||||
&pubkey_len);
|
||||
if (evp_rc != 1) {
|
||||
SSH_LOG(SSH_LOG_TRACE,
|
||||
"Failed to get ed25519 raw public key: %s",
|
||||
ERR_error_string(ERR_get_error(), NULL));
|
||||
goto error;
|
||||
}
|
||||
key->key = pkey;
|
||||
|
||||
EVP_PKEY_CTX_free(pctx);
|
||||
EVP_PKEY_free(pkey);
|
||||
return SSH_OK;
|
||||
|
||||
error:
|
||||
@@ -2702,8 +2708,6 @@ error:
|
||||
if (pkey != NULL) {
|
||||
EVP_PKEY_free(pkey);
|
||||
}
|
||||
SAFE_FREE(key->ed25519_privkey);
|
||||
SAFE_FREE(key->ed25519_pubkey);
|
||||
|
||||
return SSH_ERROR;
|
||||
}
|
||||
@@ -2872,6 +2876,9 @@ int pki_uri_import(const char *uri_name,
|
||||
|
||||
break;
|
||||
#endif
|
||||
case EVP_PKEY_ED25519:
|
||||
type = SSH_KEYTYPE_ED25519;
|
||||
break;
|
||||
default:
|
||||
SSH_LOG(SSH_LOG_TRACE, "Unknown or invalid public key type %d",
|
||||
EVP_PKEY_base_id(pkey));
|
||||
|
||||
@@ -28,6 +28,205 @@
|
||||
#include "libssh/ed25519.h"
|
||||
#include "libssh/buffer.h"
|
||||
|
||||
int pki_pubkey_build_ed25519(ssh_key key, ssh_string pubkey)
|
||||
{
|
||||
if (ssh_string_len(pubkey) != ED25519_KEY_LEN) {
|
||||
SSH_LOG(SSH_LOG_TRACE, "Invalid ed25519 key len");
|
||||
return SSH_ERROR;
|
||||
}
|
||||
|
||||
key->ed25519_pubkey = malloc(ED25519_KEY_LEN);
|
||||
if (key->ed25519_pubkey == NULL) {
|
||||
return SSH_ERROR;
|
||||
}
|
||||
|
||||
memcpy(key->ed25519_pubkey, ssh_string_data(pubkey), ED25519_KEY_LEN);
|
||||
|
||||
return SSH_OK;
|
||||
}
|
||||
|
||||
int pki_privkey_build_ed25519(ssh_key key,
|
||||
ssh_string pubkey,
|
||||
ssh_string privkey)
|
||||
{
|
||||
if (ssh_string_len(pubkey) != ED25519_KEY_LEN ||
|
||||
ssh_string_len(privkey) != (2 * ED25519_KEY_LEN)) {
|
||||
SSH_LOG(SSH_LOG_TRACE, "Invalid ed25519 key len");
|
||||
return SSH_ERROR;
|
||||
}
|
||||
|
||||
/* In the internal implementation, the private key is the concatenation of
|
||||
* the private seed with the public key. */
|
||||
key->ed25519_privkey = malloc(2 * ED25519_KEY_LEN);
|
||||
if (key->ed25519_privkey == NULL) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
key->ed25519_pubkey = malloc(ED25519_KEY_LEN);
|
||||
if (key->ed25519_pubkey == NULL) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
memcpy(key->ed25519_privkey, ssh_string_data(privkey), 2 * ED25519_KEY_LEN);
|
||||
memcpy(key->ed25519_pubkey, ssh_string_data(pubkey), ED25519_KEY_LEN);
|
||||
|
||||
return SSH_OK;
|
||||
|
||||
error:
|
||||
SAFE_FREE(key->ed25519_privkey);
|
||||
SAFE_FREE(key->ed25519_pubkey);
|
||||
|
||||
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;
|
||||
}
|
||||
/* In the internal implementation, the private key is the concatenation
|
||||
* of the private seed with the public key. */
|
||||
cmp = memcmp(k1->ed25519_privkey,
|
||||
k2->ed25519_privkey,
|
||||
2 * ED25519_KEY_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_KEY_LEN);
|
||||
if (cmp != 0) {
|
||||
return 1;
|
||||
}
|
||||
break;
|
||||
case SSH_KEY_CMP_CERTIFICATE:
|
||||
/* handled globally */
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*
|
||||
* @brief Duplicate an Ed25519 key
|
||||
*
|
||||
* @param[out] new Pre-initialized ssh_key structure
|
||||
*
|
||||
* @param[in] key Key to copy
|
||||
*
|
||||
* @return SSH_ERROR on error, SSH_OK on success
|
||||
*/
|
||||
int pki_ed25519_key_dup(ssh_key new_key, const ssh_key key)
|
||||
{
|
||||
if (key->ed25519_privkey == NULL && key->ed25519_pubkey == NULL) {
|
||||
return SSH_ERROR;
|
||||
}
|
||||
|
||||
if (key->ed25519_privkey != NULL) {
|
||||
/* In the internal implementation, the private key is the concatenation
|
||||
* of the private seed with the public key. */
|
||||
new_key->ed25519_privkey = malloc(2 * ED25519_KEY_LEN);
|
||||
if (new_key->ed25519_privkey == NULL) {
|
||||
return SSH_ERROR;
|
||||
}
|
||||
memcpy(new_key->ed25519_privkey,
|
||||
key->ed25519_privkey,
|
||||
2 * ED25519_KEY_LEN);
|
||||
}
|
||||
|
||||
if (key->ed25519_pubkey != NULL) {
|
||||
new_key->ed25519_pubkey = malloc(ED25519_KEY_LEN);
|
||||
if (new_key->ed25519_pubkey == NULL) {
|
||||
SAFE_FREE(new_key->ed25519_privkey);
|
||||
return SSH_ERROR;
|
||||
}
|
||||
memcpy(new_key->ed25519_pubkey, key->ed25519_pubkey, ED25519_KEY_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_KEY_LEN,
|
||||
(size_t)ED25519_KEY_LEN,
|
||||
key->ed25519_pubkey);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/** @internal
|
||||
* @brief exports a ed25519 private key to a string blob.
|
||||
* @param[in] privkey private key to convert
|
||||
* @param[out] buffer buffer to write the blob in.
|
||||
* @returns SSH_OK on success
|
||||
*/
|
||||
int pki_ed25519_private_key_to_blob(ssh_buffer buffer, const ssh_key privkey)
|
||||
{
|
||||
int rc;
|
||||
|
||||
if (privkey->type != SSH_KEYTYPE_ED25519) {
|
||||
SSH_LOG(SSH_LOG_TRACE, "Type %s not supported", privkey->type_c);
|
||||
return SSH_ERROR;
|
||||
}
|
||||
if (privkey->ed25519_privkey == NULL || privkey->ed25519_pubkey == NULL) {
|
||||
return SSH_ERROR;
|
||||
}
|
||||
rc = ssh_buffer_pack(buffer,
|
||||
"dPdPP",
|
||||
(uint32_t)ED25519_KEY_LEN,
|
||||
(size_t)ED25519_KEY_LEN,
|
||||
privkey->ed25519_pubkey,
|
||||
(uint32_t)(2 * ED25519_KEY_LEN),
|
||||
(size_t)ED25519_KEY_LEN,
|
||||
privkey->ed25519_privkey,
|
||||
(size_t)ED25519_KEY_LEN,
|
||||
privkey->ed25519_pubkey);
|
||||
return rc;
|
||||
}
|
||||
|
||||
int pki_key_generate_ed25519(ssh_key key)
|
||||
{
|
||||
int rc;
|
||||
|
||||
@@ -27,213 +27,6 @@
|
||||
#include "libssh/pki_priv.h"
|
||||
#include "libssh/buffer.h"
|
||||
|
||||
int pki_privkey_build_ed25519(ssh_key key,
|
||||
ssh_string pubkey,
|
||||
ssh_string privkey)
|
||||
{
|
||||
if (ssh_string_len(pubkey) != ED25519_KEY_LEN ||
|
||||
ssh_string_len(privkey) != (2 * ED25519_KEY_LEN))
|
||||
{
|
||||
SSH_LOG(SSH_LOG_TRACE, "Invalid ed25519 key len");
|
||||
return SSH_ERROR;
|
||||
}
|
||||
|
||||
#ifdef HAVE_LIBCRYPTO
|
||||
/* In OpenSSL implementation, the private key is the original private seed,
|
||||
* without the public key. */
|
||||
key->ed25519_privkey = malloc(ED25519_KEY_LEN);
|
||||
#else
|
||||
/* In the internal implementation, the private key is the concatenation of
|
||||
* the private seed with the public key. */
|
||||
key->ed25519_privkey = malloc(2 * ED25519_KEY_LEN);
|
||||
#endif
|
||||
if (key->ed25519_privkey == NULL) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
key->ed25519_pubkey = malloc(ED25519_KEY_LEN);
|
||||
if (key->ed25519_pubkey == NULL) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
#ifdef HAVE_LIBCRYPTO
|
||||
memcpy(key->ed25519_privkey, ssh_string_data(privkey),
|
||||
ED25519_KEY_LEN);
|
||||
#else
|
||||
memcpy(key->ed25519_privkey, ssh_string_data(privkey),
|
||||
2 * ED25519_KEY_LEN);
|
||||
#endif
|
||||
memcpy(key->ed25519_pubkey, ssh_string_data(pubkey),
|
||||
ED25519_KEY_LEN);
|
||||
|
||||
return SSH_OK;
|
||||
|
||||
error:
|
||||
SAFE_FREE(key->ed25519_privkey);
|
||||
SAFE_FREE(key->ed25519_pubkey);
|
||||
|
||||
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;
|
||||
}
|
||||
#ifdef HAVE_LIBCRYPTO
|
||||
/* In OpenSSL implementation, the private key is the original private
|
||||
* seed, without the public key. */
|
||||
cmp = memcmp(k1->ed25519_privkey, k2->ed25519_privkey, ED25519_KEY_LEN);
|
||||
#else
|
||||
/* In the internal implementation, the private key is the concatenation
|
||||
* of the private seed with the public key. */
|
||||
cmp = memcmp(k1->ed25519_privkey, k2->ed25519_privkey,
|
||||
2 * ED25519_KEY_LEN);
|
||||
#endif
|
||||
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_KEY_LEN);
|
||||
if (cmp != 0) {
|
||||
return 1;
|
||||
}
|
||||
break;
|
||||
case SSH_KEY_CMP_CERTIFICATE:
|
||||
/* handled globally */
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*
|
||||
* @brief Duplicate an Ed25519 key
|
||||
*
|
||||
* @param[out] new Pre-initialized ssh_key structure
|
||||
*
|
||||
* @param[in] key Key to copy
|
||||
*
|
||||
* @return SSH_ERROR on error, SSH_OK on success
|
||||
*/
|
||||
int pki_ed25519_key_dup(ssh_key new_key, const ssh_key key)
|
||||
{
|
||||
if (key->ed25519_privkey == NULL && key->ed25519_pubkey == NULL) {
|
||||
return SSH_ERROR;
|
||||
}
|
||||
|
||||
if (key->ed25519_privkey != NULL) {
|
||||
#ifdef HAVE_LIBCRYPTO
|
||||
/* In OpenSSL implementation, the private key is the original private
|
||||
* seed, without the public key. */
|
||||
new_key->ed25519_privkey = malloc(ED25519_KEY_LEN);
|
||||
#else
|
||||
/* In the internal implementation, the private key is the concatenation
|
||||
* of the private seed with the public key. */
|
||||
new_key->ed25519_privkey = malloc(2 * ED25519_KEY_LEN);
|
||||
#endif
|
||||
if (new_key->ed25519_privkey == NULL) {
|
||||
return SSH_ERROR;
|
||||
}
|
||||
#ifdef HAVE_LIBCRYPTO
|
||||
memcpy(new_key->ed25519_privkey, key->ed25519_privkey, ED25519_KEY_LEN);
|
||||
#else
|
||||
memcpy(new_key->ed25519_privkey, key->ed25519_privkey, 2 * ED25519_KEY_LEN);
|
||||
#endif
|
||||
}
|
||||
|
||||
if (key->ed25519_pubkey != NULL) {
|
||||
new_key->ed25519_pubkey = malloc(ED25519_KEY_LEN);
|
||||
if (new_key->ed25519_pubkey == NULL) {
|
||||
SAFE_FREE(new_key->ed25519_privkey);
|
||||
return SSH_ERROR;
|
||||
}
|
||||
memcpy(new_key->ed25519_pubkey, key->ed25519_pubkey, ED25519_KEY_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_KEY_LEN,
|
||||
(size_t)ED25519_KEY_LEN, key->ed25519_pubkey);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/** @internal
|
||||
* @brief exports a ed25519 private key to a string blob.
|
||||
* @param[in] privkey private key to convert
|
||||
* @param[out] buffer buffer to write the blob in.
|
||||
* @returns SSH_OK on success
|
||||
*/
|
||||
int pki_ed25519_private_key_to_blob(ssh_buffer buffer, const ssh_key privkey)
|
||||
{
|
||||
int rc;
|
||||
|
||||
if (privkey->type != SSH_KEYTYPE_ED25519) {
|
||||
SSH_LOG(SSH_LOG_TRACE, "Type %s not supported", privkey->type_c);
|
||||
return SSH_ERROR;
|
||||
}
|
||||
if (privkey->ed25519_privkey == NULL ||
|
||||
privkey->ed25519_pubkey == NULL) {
|
||||
return SSH_ERROR;
|
||||
}
|
||||
rc = ssh_buffer_pack(buffer,
|
||||
"dPdPP",
|
||||
(uint32_t)ED25519_KEY_LEN,
|
||||
(size_t)ED25519_KEY_LEN, privkey->ed25519_pubkey,
|
||||
(uint32_t)(2 * ED25519_KEY_LEN),
|
||||
(size_t)ED25519_KEY_LEN, privkey->ed25519_privkey,
|
||||
(size_t)ED25519_KEY_LEN, privkey->ed25519_pubkey);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*
|
||||
|
||||
Reference in New Issue
Block a user