From 24fd419bde2c7695bacda870512f365a6dea5623 Mon Sep 17 00:00:00 2001 From: Zenju Date: Fri, 13 Dec 2019 22:35:00 +0100 Subject: [PATCH] ssh-ed25519: Support PKIX + calc pubkey from private (#416) Files: openssl.c/h Author: Zenju Notes: Adds support for PKIX key reading by fixing: _libssh2_pub_priv_keyfile() is missing the code to extract the ed25519 public key from a given private key _libssh2_ed25519_new_private_frommemory is only parsing the openssh key format but does not understand PKIX (as retrieved via PEM_read_bio_PrivateKey) --- src/openssl.c | 176 ++++++++++++++++++++++++++++++++------------------ src/openssl.h | 23 ++----- 2 files changed, 117 insertions(+), 82 deletions(-) diff --git a/src/openssl.c b/src/openssl.c index 027e5780..d4cf401d 100644 --- a/src/openssl.c +++ b/src/openssl.c @@ -759,7 +759,6 @@ _libssh2_rsa_new_private_frommemory(libssh2_rsa_ctx ** rsa, pem_read_bio_func read_rsa = (pem_read_bio_func) &PEM_read_bio_RSAPrivateKey; - (void) session; _libssh2_init_if_needed(); @@ -831,7 +830,7 @@ gen_publickey_from_rsa_evp(LIBSSH2_SESSION *session, _libssh2_debug(session, LIBSSH2_TRACE_AUTH, - "Computing public key from RSA private key envelop"); + "Computing public key from RSA private key envelope"); rsa = EVP_PKEY_get1_RSA(pk); if(rsa == NULL) { @@ -1110,7 +1109,6 @@ _libssh2_rsa_new_private(libssh2_rsa_ctx ** rsa, pem_read_bio_func read_rsa = (pem_read_bio_func) &PEM_read_bio_RSAPrivateKey; - (void) session; _libssh2_init_if_needed(); @@ -1136,7 +1134,6 @@ _libssh2_dsa_new_private_frommemory(libssh2_dsa_ctx ** dsa, pem_read_bio_func read_dsa = (pem_read_bio_func) &PEM_read_bio_DSAPrivateKey; - (void) session; _libssh2_init_if_needed(); @@ -1222,7 +1219,7 @@ gen_publickey_from_dsa_evp(LIBSSH2_SESSION *session, _libssh2_debug(session, LIBSSH2_TRACE_AUTH, - "Computing public key from DSA private key envelop"); + "Computing public key from DSA private key envelope"); dsa = EVP_PKEY_get1_DSA(pk); if(dsa == NULL) { @@ -1412,7 +1409,6 @@ _libssh2_dsa_new_private(libssh2_dsa_ctx ** dsa, pem_read_bio_func read_dsa = (pem_read_bio_func) &PEM_read_bio_DSAPrivateKey; - (void) session; _libssh2_init_if_needed(); @@ -1441,7 +1437,6 @@ _libssh2_ecdsa_new_private_frommemory(libssh2_ecdsa_ctx ** ec_ctx, pem_read_bio_func read_ec = (pem_read_bio_func) &PEM_read_bio_ECPrivateKey; - (void) session; _libssh2_init_if_needed(); @@ -1526,25 +1521,11 @@ _libssh2_curve25519_new(LIBSSH2_SESSION *session, libssh2_x25519_ctx **out_ctx, } if(out_ctx != NULL) { - ctx = malloc(sizeof(libssh2_x25519_ctx)); - if(ctx == NULL) + ctx = EVP_PKEY_new_raw_private_key(EVP_PKEY_X25519, NULL, priv, + LIBSSH2_ED25519_KEY_LEN); + if(!ctx) goto cleanExit; - ctx->private_key = - EVP_PKEY_new_raw_private_key(EVP_PKEY_X25519, NULL, - (const unsigned char *)priv, - LIBSSH2_ED25519_KEY_LEN); - - ctx->public_key = - EVP_PKEY_new_raw_public_key(EVP_PKEY_X25519, NULL, - (const unsigned char *)pub, - LIBSSH2_ED25519_KEY_LEN); - - if(ctx->public_key == NULL || ctx->private_key == NULL) { - _libssh2_x25519_free(ctx); - goto cleanExit; - } - *out_ctx = ctx; } @@ -1567,6 +1548,72 @@ cleanExit: return rc; } + +static int +gen_publickey_from_ed_evp(LIBSSH2_SESSION *session, + unsigned char **method, + size_t *method_len, + unsigned char **pubkeydata, + size_t *pubkeydata_len, + EVP_PKEY *pk) +{ + const char methodName[] = "ssh-ed25519"; + unsigned char *methodBuf = NULL; + size_t rawKeyLen = 0; + unsigned char *keyBuf = NULL; + size_t bufLen = 0; + unsigned char *bufPos = NULL; + + _libssh2_debug(session, LIBSSH2_TRACE_AUTH, + "Computing public key from ED private key envelope"); + + methodBuf = LIBSSH2_ALLOC(session, sizeof(methodName) - 1); + if(!methodBuf) { + _libssh2_error(session, LIBSSH2_ERROR_ALLOC, + "Unable to allocate memory for private key data"); + goto fail; + } + memcpy(methodBuf, methodName, sizeof(methodName) - 1); + + if(EVP_PKEY_get_raw_public_key(pk, NULL, &rawKeyLen) != 1) { + _libssh2_error(session, LIBSSH2_ERROR_PROTO, + "EVP_PKEY_get_raw_public_key failed"); + goto fail; + } + + /* Key form is: type_len(4) + type(11) + pub_key_len(4) + pub_key(32). */ + bufLen = 4 + sizeof(methodName) - 1 + 4 + rawKeyLen; + bufPos = keyBuf = LIBSSH2_ALLOC(session, bufLen); + if(!keyBuf) { + _libssh2_error(session, LIBSSH2_ERROR_ALLOC, + "Unable to allocate memory for private key data"); + goto fail; + } + + _libssh2_store_str(&bufPos, methodName, sizeof(methodName) - 1); + _libssh2_store_u32(&bufPos, rawKeyLen); + + if(EVP_PKEY_get_raw_public_key(pk, bufPos, &rawKeyLen) != 1) { + _libssh2_error(session, LIBSSH2_ERROR_PROTO, + "EVP_PKEY_get_raw_public_key failed"); + goto fail; + } + + *method = methodBuf; + *method_len = sizeof(methodName) - 1; + *pubkeydata = keyBuf; + *pubkeydata_len = bufLen; + return 0; + +fail: + if(methodBuf) + LIBSSH2_FREE(session, methodBuf); + if(keyBuf) + LIBSSH2_FREE(session, keyBuf); + return -1; +} + + static int gen_publickey_from_ed25519_openssh_priv_data(LIBSSH2_SESSION *session, struct string_buf *decrypted, @@ -1603,25 +1650,11 @@ gen_publickey_from_ed25519_openssh_priv_data(LIBSSH2_SESSION *session, goto clean_exit; } - ctx = _libssh2_ed25519_new_ctx(); - if(ctx == NULL) { - _libssh2_error(session, LIBSSH2_ERROR_ALLOC, - "Unable to allocate memory for ed25519 key"); - ret = -1; - goto clean_exit; - } - /* first 32 bytes of priv_key is the private key, the last 32 bytes are the public key */ - ctx->private_key = - EVP_PKEY_new_raw_private_key(EVP_PKEY_ED25519, NULL, - (const unsigned char *)priv_key, - LIBSSH2_ED25519_KEY_LEN); - - ctx->public_key = - EVP_PKEY_new_raw_public_key(EVP_PKEY_ED25519, NULL, - (const unsigned char *)pub_key, - LIBSSH2_ED25519_KEY_LEN); + ctx = EVP_PKEY_new_raw_private_key(EVP_PKEY_ED25519, NULL, + (const unsigned char *)priv_key, + LIBSSH2_ED25519_KEY_LEN); /* comment */ if(_libssh2_get_string(decrypted, &buf, &tmp_len)) { @@ -1661,7 +1694,7 @@ gen_publickey_from_ed25519_openssh_priv_data(LIBSSH2_SESSION *session, _libssh2_debug(session, LIBSSH2_TRACE_AUTH, "Computing public key from ED25519 " - "private key envelop"); + "private key envelope"); method_buf = LIBSSH2_ALLOC(session, 11); /* ssh-ed25519. */ if(method_buf == NULL) { @@ -1795,6 +1828,24 @@ _libssh2_ed25519_new_private_frommemory(libssh2_ed25519_ctx ** ed_ctx, size_t filedata_len, unsigned const char *passphrase) { + libssh2_ed25519_ctx *ctx = NULL; + + _libssh2_init_if_needed(); + + if(read_private_key_from_memory((void **)&ctx, + (pem_read_bio_func) + &PEM_read_bio_PrivateKey, + filedata, filedata_len, passphrase) == 0) { + if(EVP_PKEY_id(ctx) != EVP_PKEY_ED25519) { + _libssh2_ed25519_free(ctx); + return _libssh2_error(session, LIBSSH2_ERROR_PROTO, + "Private key is not an ED25519 key"); + } + + *ed_ctx = ctx; + return 0; + } + return read_openssh_private_key_from_memory((void **)ed_ctx, session, "ssh-ed25519", filedata, filedata_len, @@ -1808,38 +1859,26 @@ _libssh2_ed25519_new_public(libssh2_ed25519_ctx ** ed_ctx, const uint8_t key_len) { libssh2_ed25519_ctx *ctx = NULL; - EVP_PKEY *public_key = NULL; if(ed_ctx == NULL) return -1; - public_key = - EVP_PKEY_new_raw_public_key(EVP_PKEY_ED25519, NULL, - (const unsigned char *)raw_pub_key, - key_len); - if(public_key == NULL) { + ctx = EVP_PKEY_new_raw_public_key(EVP_PKEY_ED25519, NULL, + raw_pub_key, key_len); + if(!ctx) return _libssh2_error(session, LIBSSH2_ERROR_PROTO, "could not create ED25519 public key"); - } - - ctx = _libssh2_ed25519_new_ctx(); - if(ctx == NULL) { - return _libssh2_error(session, LIBSSH2_ERROR_ALLOC, - "could not alloc public/private key"); - } - - ctx->public_key = public_key; if(ed_ctx != NULL) *ed_ctx = ctx; - else if(ctx != NULL) + else if(ctx) _libssh2_ed25519_free(ctx); return 0; } - #endif /* LIBSSH2_ED25519 */ + int _libssh2_rsa_sha1_sign(LIBSSH2_SESSION * session, libssh2_rsa_ctx * rsactx, @@ -2234,7 +2273,7 @@ gen_publickey_from_ec_evp(LIBSSH2_SESSION *session, _libssh2_debug(session, LIBSSH2_TRACE_AUTH, - "Computing public key from EC private key envelop"); + "Computing public key from EC private key envelope"); bn_ctx = BN_CTX_new(); if(bn_ctx == NULL) @@ -2492,7 +2531,6 @@ _libssh2_ecdsa_new_private(libssh2_ecdsa_ctx ** ec_ctx, int rc; pem_read_bio_func read_ec = (pem_read_bio_func) &PEM_read_bio_ECPrivateKey; - (void) session; _libssh2_init_if_needed(); @@ -2665,7 +2703,7 @@ _libssh2_ed25519_sign(libssh2_ed25519_ctx *ctx, LIBSSH2_SESSION *session, unsigned char *sig = NULL; if(md_ctx != NULL) { - if(EVP_DigestSignInit(md_ctx, NULL, NULL, NULL, ctx->private_key) != 1) + if(EVP_DigestSignInit(md_ctx, NULL, NULL, NULL, ctx) != 1) goto clean_exit; if(EVP_DigestSign(md_ctx, NULL, &sig_len, message, message_len) != 1) goto clean_exit; @@ -2782,7 +2820,7 @@ _libssh2_ed25519_verify(libssh2_ed25519_ctx *ctx, const uint8_t *s, if(NULL == md_ctx) return -1; - ret = EVP_DigestVerifyInit(md_ctx, NULL, NULL, NULL, ctx->public_key); + ret = EVP_DigestVerifyInit(md_ctx, NULL, NULL, NULL, ctx); if(ret != 1) goto clean_exit; @@ -2959,6 +2997,12 @@ _libssh2_pub_priv_keyfile(LIBSSH2_SESSION *session, #endif switch(pktype) { +#if LIBSSH2_ED25519 + case EVP_PKEY_ED25519 : + st = gen_publickey_from_ed_evp( + session, method, method_len, pubkeydata, pubkeydata_len, pk); + break; +#endif /* LIBSSH2_ED25519 */ case EVP_PKEY_RSA : st = gen_publickey_from_rsa_evp( session, method, method_len, pubkeydata, pubkeydata_len, pk); @@ -3171,6 +3215,12 @@ _libssh2_pub_priv_keyfilememory(LIBSSH2_SESSION *session, #endif switch(pktype) { +#if LIBSSH2_ED25519 + case EVP_PKEY_ED25519 : + st = gen_publickey_from_ed_evp( + session, method, method_len, pubkeydata, pubkeydata_len, pk); + break; +#endif /* LIBSSH2_ED25519 */ case EVP_PKEY_RSA : st = gen_publickey_from_rsa_evp(session, method, method_len, pubkeydata, pubkeydata_len, pk); diff --git a/src/openssl.h b/src/openssl.h index 34ec7a64..a65d710e 100644 --- a/src/openssl.h +++ b/src/openssl.h @@ -322,26 +322,11 @@ libssh2_curve_type; #endif /* LIBSSH2_ECDSA */ #if LIBSSH2_ED25519 +#define libssh2_ed25519_ctx EVP_PKEY +#define libssh2_x25519_ctx EVP_PKEY -typedef struct { - EVP_PKEY *public_key; - EVP_PKEY *private_key; -} libssh2_curve25519_keys; - -#define libssh2_ed25519_ctx libssh2_curve25519_keys -#define libssh2_x25519_ctx libssh2_curve25519_keys - -#define _libssh2_ed25519_new_ctx() calloc(1, sizeof(libssh2_ed25519_ctx)) -#define _libssh2_ed25519_free(ctx) do { \ - if(ctx) { \ - if(ctx->public_key) EVP_PKEY_free(ctx->public_key); \ - if(ctx->private_key) EVP_PKEY_free(ctx->private_key); \ - free(ctx); \ - } \ -} while(0) - -#define _libssh2_x25519_free(ctx) _libssh2_ed25519_free(ctx) - +#define _libssh2_ed25519_free(ctx) EVP_PKEY_free(ctx) +#define _libssh2_x25519_free(ctx) EVP_PKEY_free(ctx) #endif /* ED25519 */ #define _libssh2_cipher_type(name) const EVP_CIPHER *(*name)(void)