diff options
author | George Hazan <george.hazan@gmail.com> | 2023-06-09 21:40:16 +0300 |
---|---|---|
committer | George Hazan <george.hazan@gmail.com> | 2023-06-09 21:40:16 +0300 |
commit | 927f00cc19b7239a1fe12abe30b472d61b753d8d (patch) | |
tree | 68a190dd83dc2dcceb82464a1953f2701af2a109 /libs/libssh2/src/openssl.c | |
parent | 1b241cad53b8c3c5300409fe681de18e636dcf3d (diff) |
fixes #3551 (Update libssh2 to 1.11.0)
Diffstat (limited to 'libs/libssh2/src/openssl.c')
-rw-r--r-- | libs/libssh2/src/openssl.c | 2164 |
1 files changed, 1504 insertions, 660 deletions
diff --git a/libs/libssh2/src/openssl.c b/libs/libssh2/src/openssl.c index 04d5ec2ffd..2dc2340c3b 100644 --- a/libs/libssh2/src/openssl.c +++ b/libs/libssh2/src/openssl.c @@ -1,8 +1,7 @@ -/* Copyright (C) 2009, 2010 Simon Josefsson - * Copyright (C) 2006, 2007 The Written Word, Inc. All rights reserved. - * Copyright (c) 2004-2006, Sara Golemon <sarag@libssh2.org> - * - * Author: Simon Josefsson +/* Copyright (C) Simon Josefsson + * Copyright (C) The Written Word, Inc. + * Copyright (C) Sara Golemon <sarag@libssh2.org> + * All rights reserved. * * Redistribution and use in source and binary forms, * with or without modification, are permitted provided @@ -36,14 +35,14 @@ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY * OF SUCH DAMAGE. + * + * SPDX-License-Identifier: BSD-3-Clause */ -#include "libssh2_priv.h" - -#ifdef LIBSSH2_OPENSSL /* compile only if we build with openssl */ +#ifdef LIBSSH2_CRYPTO_C /* Compile this via crypto.c */ -#include <string.h> -#include "misc.h" +#include <stdlib.h> +#include <assert.h> #ifndef EVP_MAX_BLOCK_LENGTH #define EVP_MAX_BLOCK_LENGTH 32 @@ -56,6 +55,24 @@ read_openssh_private_key_from_memory(void **key_ctx, LIBSSH2_SESSION *session, size_t filedata_len, unsigned const char *passphrase); +static int +_libssh2_sk_pub_openssh_keyfilememory(LIBSSH2_SESSION *session, + void **key_ctx, + const char *key_type, + unsigned char **method, + size_t *method_len, + unsigned char **pubkeydata, + size_t *pubkeydata_len, + int *algorithm, + unsigned char *flags, + const char **application, + const unsigned char **key_handle, + size_t *handle_len, + const char *privatekeydata, + size_t privatekeydata_len, + unsigned const char *passphrase); + +#if LIBSSH2_RSA || LIBSSH2_DSA || LIBSSH2_ECDSA static unsigned char * write_bn(unsigned char *buf, const BIGNUM *bn, int bn_bytes) { @@ -73,7 +90,19 @@ write_bn(unsigned char *buf, const BIGNUM *bn, int bn_bytes) return p + bn_bytes; } +#endif + +int +_libssh2_openssl_random(void *buf, size_t len) +{ + if(len > INT_MAX) { + return -1; + } + + return RAND_bytes(buf, (int)len) == 1 ? 0 : -1; +} +#if LIBSSH2_RSA int _libssh2_rsa_new(libssh2_rsa_ctx ** rsa, const unsigned char *edata, @@ -102,29 +131,29 @@ _libssh2_rsa_new(libssh2_rsa_ctx ** rsa, BIGNUM * iqmp = 0; e = BN_new(); - BN_bin2bn(edata, elen, e); + BN_bin2bn(edata, (int) elen, e); n = BN_new(); - BN_bin2bn(ndata, nlen, n); + BN_bin2bn(ndata, (int) nlen, n); if(ddata) { d = BN_new(); - BN_bin2bn(ddata, dlen, d); + BN_bin2bn(ddata, (int) dlen, d); p = BN_new(); - BN_bin2bn(pdata, plen, p); + BN_bin2bn(pdata, (int) plen, p); q = BN_new(); - BN_bin2bn(qdata, qlen, q); + BN_bin2bn(qdata, (int) qlen, q); dmp1 = BN_new(); - BN_bin2bn(e1data, e1len, dmp1); + BN_bin2bn(e1data, (int) e1len, dmp1); dmq1 = BN_new(); - BN_bin2bn(e2data, e2len, dmq1); + BN_bin2bn(e2data, (int) e2len, dmq1); iqmp = BN_new(); - BN_bin2bn(coeffdata, coefflen, iqmp); + BN_bin2bn(coeffdata, (int) coefflen, iqmp); } *rsa = RSA_new(); @@ -154,21 +183,67 @@ _libssh2_rsa_new(libssh2_rsa_ctx ** rsa, } int -_libssh2_rsa_sha1_verify(libssh2_rsa_ctx * rsactx, +_libssh2_rsa_sha2_verify(libssh2_rsa_ctx * rsactx, + size_t hash_len, const unsigned char *sig, - unsigned long sig_len, - const unsigned char *m, unsigned long m_len) + size_t sig_len, + const unsigned char *m, size_t m_len) { - unsigned char hash[SHA_DIGEST_LENGTH]; int ret; + int nid_type; + unsigned char *hash = malloc(hash_len); + if(!hash) + return -1; + + if(hash_len == SHA_DIGEST_LENGTH) { + nid_type = NID_sha1; + ret = _libssh2_sha1(m, m_len, hash); + } + else if(hash_len == SHA256_DIGEST_LENGTH) { + nid_type = NID_sha256; + ret = _libssh2_sha256(m, m_len, hash); + + } + else if(hash_len == SHA512_DIGEST_LENGTH) { + nid_type = NID_sha512; + ret = _libssh2_sha512(m, m_len, hash); + } + else { +/* silence: + warning C4701: potentially uninitialized local variable 'nid_type' used */ +#if defined(_MSC_VER) + nid_type = 0; +#endif + ret = -1; /* unsupported digest */ + } - if(_libssh2_sha1(m, m_len, hash)) + if(ret) { + free(hash); return -1; /* failure */ - ret = RSA_verify(NID_sha1, hash, SHA_DIGEST_LENGTH, - (unsigned char *) sig, sig_len, rsactx); + } + + ret = RSA_verify(nid_type, hash, (unsigned int) hash_len, + (unsigned char *) sig, + (unsigned int) sig_len, rsactx); + + free(hash); + return (ret == 1) ? 0 : -1; } +#if LIBSSH2_RSA_SHA1 +int +_libssh2_rsa_sha1_verify(libssh2_rsa_ctx * rsactx, + const unsigned char *sig, + size_t sig_len, + const unsigned char *m, size_t m_len) +{ + return _libssh2_rsa_sha2_verify(rsactx, SHA_DIGEST_LENGTH, sig, sig_len, m, + m_len); +} +#endif +#endif + #if LIBSSH2_DSA int _libssh2_dsa_new(libssh2_dsa_ctx ** dsactx, @@ -189,20 +264,20 @@ _libssh2_dsa_new(libssh2_dsa_ctx ** dsactx, BIGNUM * priv_key = NULL; p_bn = BN_new(); - BN_bin2bn(p, p_len, p_bn); + BN_bin2bn(p, (int) p_len, p_bn); q_bn = BN_new(); - BN_bin2bn(q, q_len, q_bn); + BN_bin2bn(q, (int) q_len, q_bn); g_bn = BN_new(); - BN_bin2bn(g, g_len, g_bn); + BN_bin2bn(g, (int) g_len, g_bn); pub_key = BN_new(); - BN_bin2bn(y, y_len, pub_key); + BN_bin2bn(y, (int) y_len, pub_key); if(x_len) { priv_key = BN_new(); - BN_bin2bn(x, x_len, priv_key); + BN_bin2bn(x, (int) x_len, priv_key); } *dsactx = DSA_new(); @@ -227,7 +302,7 @@ _libssh2_dsa_new(libssh2_dsa_ctx ** dsactx, int _libssh2_dsa_sha1_verify(libssh2_dsa_ctx * dsactx, const unsigned char *sig, - const unsigned char *m, unsigned long m_len) + const unsigned char *m, size_t m_len) { unsigned char hash[SHA_DIGEST_LENGTH]; DSA_SIG * dsasig; @@ -259,16 +334,16 @@ _libssh2_dsa_sha1_verify(libssh2_dsa_ctx * dsactx, #if LIBSSH2_ECDSA -/* _libssh2_ecdsa_key_get_curve_type +/* _libssh2_ecdsa_get_curve_type * * returns key curve type that maps to libssh2_curve_type * */ libssh2_curve_type -_libssh2_ecdsa_key_get_curve_type(_libssh2_ec_key *key) +_libssh2_ecdsa_get_curve_type(libssh2_ecdsa_ctx *ec_ctx) { - const EC_GROUP *group = EC_KEY_get0_group(key); + const EC_GROUP *group = EC_KEY_get0_group(ec_ctx); return EC_GROUP_get_curve_name(group); } @@ -282,10 +357,9 @@ int _libssh2_ecdsa_curve_type_from_name(const char *name, libssh2_curve_type *out_type) { - int ret = 0; libssh2_curve_type type; - if(name == NULL || strlen(name) != 19) + if(!name || strlen(name) != 19) return -1; if(strcmp(name, "ecdsa-sha2-nistp256") == 0) @@ -295,14 +369,14 @@ _libssh2_ecdsa_curve_type_from_name(const char *name, else if(strcmp(name, "ecdsa-sha2-nistp521") == 0) type = LIBSSH2_EC_CURVE_NISTP521; else { - ret = -1; + return -1; } - if(ret == 0 && out_type) { + if(out_type) { *out_type = type; } - return ret; + return 0; } /* _libssh2_ecdsa_curve_name_with_octal_new @@ -328,51 +402,50 @@ _libssh2_ecdsa_curve_name_with_octal_new(libssh2_ecdsa_ctx ** ec_ctx, ret = EC_POINT_oct2point(ec_group, point, k, k_len, NULL); ret = EC_KEY_set_public_key(ec_key, point); - if(point != NULL) + if(point) EC_POINT_free(point); - if(ec_ctx != NULL) + if(ec_ctx) *ec_ctx = ec_key; } return (ret == 1) ? 0 : -1; } -#define LIBSSH2_ECDSA_VERIFY(digest_type) \ -{ \ - unsigned char hash[SHA##digest_type##_DIGEST_LENGTH]; \ - libssh2_sha##digest_type(m, m_len, hash); \ - ret = ECDSA_do_verify(hash, SHA##digest_type##_DIGEST_LENGTH, \ - ecdsa_sig, ec_key); \ - \ -} +#define LIBSSH2_ECDSA_VERIFY(digest_type) \ + do { \ + unsigned char hash[SHA##digest_type##_DIGEST_LENGTH]; \ + libssh2_sha##digest_type(m, m_len, hash); \ + ret = ECDSA_do_verify(hash, SHA##digest_type##_DIGEST_LENGTH, \ + ecdsa_sig, ec_key); \ + } while(0) int _libssh2_ecdsa_verify(libssh2_ecdsa_ctx * ctx, - const unsigned char *r, size_t r_len, - const unsigned char *s, size_t s_len, - const unsigned char *m, size_t m_len) + const unsigned char *r, size_t r_len, + const unsigned char *s, size_t s_len, + const unsigned char *m, size_t m_len) { int ret = 0; EC_KEY *ec_key = (EC_KEY*)ctx; - libssh2_curve_type type = _libssh2_ecdsa_key_get_curve_type(ec_key); + libssh2_curve_type type = _libssh2_ecdsa_get_curve_type(ec_key); #ifdef HAVE_OPAQUE_STRUCTS ECDSA_SIG *ecdsa_sig = ECDSA_SIG_new(); BIGNUM *pr = BN_new(); BIGNUM *ps = BN_new(); - BN_bin2bn(r, r_len, pr); - BN_bin2bn(s, s_len, ps); + BN_bin2bn(r, (int) r_len, pr); + BN_bin2bn(s, (int) s_len, ps); ECDSA_SIG_set0(ecdsa_sig, pr, ps); #else ECDSA_SIG ecdsa_sig_; ECDSA_SIG *ecdsa_sig = &ecdsa_sig_; ecdsa_sig_.r = BN_new(); - BN_bin2bn(r, r_len, ecdsa_sig_.r); + BN_bin2bn(r, (int) r_len, ecdsa_sig_.r); ecdsa_sig_.s = BN_new(); - BN_bin2bn(s, s_len, ecdsa_sig_.s); + BN_bin2bn(s, (int) s_len, ecdsa_sig_.s); #endif if(type == LIBSSH2_EC_CURVE_NISTP256) { @@ -404,9 +477,26 @@ _libssh2_cipher_init(_libssh2_cipher_ctx * h, unsigned char *iv, unsigned char *secret, int encrypt) { #ifdef HAVE_OPAQUE_STRUCTS +#if LIBSSH2_AES_GCM + const int is_aesgcm = (algo == EVP_aes_128_gcm) || + (algo == EVP_aes_256_gcm); +#endif /* LIBSSH2_AES_GCM */ + int rc; + *h = EVP_CIPHER_CTX_new(); - return !EVP_CipherInit(*h, algo(), secret, iv, encrypt); + rc = !EVP_CipherInit(*h, algo(), secret, iv, encrypt); +#if LIBSSH2_AES_GCM + if(is_aesgcm) { + /* Sets both fixed and invocation_counter parts of IV */ + rc |= !EVP_CIPHER_CTX_ctrl(*h, EVP_CTRL_AEAD_SET_IV_FIXED, -1, iv); + } +#endif /* LIBSSH2_AES_GCM */ + + return rc; #else +# if LIBSSH2_AES_GCM +# error AES-GCM is only supported with opaque structs in use +# endif /* LIBSSH2_AES_GCM */ EVP_CIPHER_CTX_init(h); return !EVP_CipherInit(h, algo(), secret, iv, encrypt); #endif @@ -415,229 +505,116 @@ _libssh2_cipher_init(_libssh2_cipher_ctx * h, int _libssh2_cipher_crypt(_libssh2_cipher_ctx * ctx, _libssh2_cipher_type(algo), - int encrypt, unsigned char *block, size_t blocksize) + int encrypt, unsigned char *block, size_t blocksize, + int firstlast) { unsigned char buf[EVP_MAX_BLOCK_LENGTH]; - int ret; - (void) algo; - (void) encrypt; + int ret = 1; + int rc = 1; -#ifdef HAVE_OPAQUE_STRUCTS - ret = EVP_Cipher(*ctx, buf, block, blocksize); +#if LIBSSH2_AES_GCM + const int is_aesgcm = (algo == EVP_aes_128_gcm) || + (algo == EVP_aes_256_gcm); + char lastiv[1]; #else - ret = EVP_Cipher(ctx, buf, block, blocksize); -#endif - if(ret == 1) { - memcpy(block, buf, blocksize); - } - return ret == 1 ? 0 : 1; -} - -#if LIBSSH2_AES_CTR && !defined(HAVE_EVP_AES_128_CTR) - -#include <openssl/aes.h> -#include <openssl/evp.h> - -typedef struct -{ - AES_KEY key; - EVP_CIPHER_CTX *aes_ctx; - unsigned char ctr[AES_BLOCK_SIZE]; -} aes_ctr_ctx; - -static int -aes_ctr_init(EVP_CIPHER_CTX *ctx, const unsigned char *key, - const unsigned char *iv, int enc) /* init key */ -{ - /* - * variable "c" is leaked from this scope, but is later freed - * in aes_ctr_cleanup - */ - aes_ctr_ctx *c; - const EVP_CIPHER *aes_cipher; - (void) enc; + const int is_aesgcm = 0; +#endif /* LIBSSH2_AES_GCM */ + /* length of AES-GCM Authentication Tag */ + const int authlen = is_aesgcm ? 16 : 0; + /* length of AAD, only on the first block */ + const int aadlen = (is_aesgcm && IS_FIRST(firstlast)) ? 4 : 0; + /* size of AT, if present */ + const int authenticationtag = IS_LAST(firstlast) ? authlen : 0; + /* length to encrypt */ + const int cryptlen = (unsigned int)blocksize - aadlen - authenticationtag; + + (void)algo; + + assert(blocksize <= sizeof(buf)); + assert(cryptlen >= 0); + +#if LIBSSH2_AES_GCM + /* First block */ + if(IS_FIRST(firstlast)) { + /* Increments invocation_counter portion of IV */ + if(is_aesgcm) { + ret = EVP_CIPHER_CTX_ctrl(*ctx, EVP_CTRL_GCM_IV_GEN, 1, lastiv); + } - switch(EVP_CIPHER_CTX_key_length(ctx)) { - case 16: - aes_cipher = EVP_aes_128_ecb(); - break; - case 24: - aes_cipher = EVP_aes_192_ecb(); - break; - case 32: - aes_cipher = EVP_aes_256_ecb(); - break; - default: - return 0; + if(aadlen) { + /* Include the 4 byte packet length as AAD */ + ret = EVP_Cipher(*ctx, NULL, block, aadlen); + } } - c = malloc(sizeof(*c)); - if(c == NULL) - return 0; - -#ifdef HAVE_OPAQUE_STRUCTS - c->aes_ctx = EVP_CIPHER_CTX_new(); -#else - c->aes_ctx = malloc(sizeof(EVP_CIPHER_CTX)); -#endif - if(c->aes_ctx == NULL) { - free(c); - return 0; + /* Last portion of block to encrypt/decrypt */ + if(IS_LAST(firstlast)) { + if(is_aesgcm && !encrypt) { + /* set tag on decryption */ + ret = EVP_CIPHER_CTX_ctrl(*ctx, EVP_CTRL_GCM_SET_TAG, authlen, + block + blocksize - authlen); + } } - - if(EVP_EncryptInit(c->aes_ctx, aes_cipher, key, NULL) != 1) { -#ifdef HAVE_OPAQUE_STRUCTS - EVP_CIPHER_CTX_free(c->aes_ctx); #else - free(c->aes_ctx); -#endif - free(c); - return 0; - } - - EVP_CIPHER_CTX_set_padding(c->aes_ctx, 0); - - memcpy(c->ctr, iv, AES_BLOCK_SIZE); - - EVP_CIPHER_CTX_set_app_data(ctx, c); - - return 1; -} - -static int -aes_ctr_do_cipher(EVP_CIPHER_CTX *ctx, unsigned char *out, - const unsigned char *in, - size_t inl) /* encrypt/decrypt data */ -{ - aes_ctr_ctx *c = EVP_CIPHER_CTX_get_app_data(ctx); - unsigned char b1[AES_BLOCK_SIZE]; - int outlen = 0; - - if(inl != 16) /* libssh2 only ever encrypt one block */ - return 0; - - if(c == NULL) { - return 0; - } + (void)encrypt; + (void)firstlast; +#endif /* LIBSSH2_AES_GCM */ -/* - To encrypt a packet P=P1||P2||...||Pn (where P1, P2, ..., Pn are each - blocks of length L), the encryptor first encrypts <X> with <cipher> - to obtain a block B1. The block B1 is then XORed with P1 to generate - the ciphertext block C1. The counter X is then incremented -*/ - - if(EVP_EncryptUpdate(c->aes_ctx, b1, &outlen, - c->ctr, AES_BLOCK_SIZE) != 1) { - return 0; - } - - _libssh2_xor_data(out, in, b1, AES_BLOCK_SIZE); - _libssh2_aes_ctr_increment(c->ctr, AES_BLOCK_SIZE); - - return 1; -} - -static int -aes_ctr_cleanup(EVP_CIPHER_CTX *ctx) /* cleanup ctx */ -{ - aes_ctr_ctx *c = EVP_CIPHER_CTX_get_app_data(ctx); - - if(c == NULL) { - return 1; - } - - if(c->aes_ctx != NULL) { + if(cryptlen > 0) { #ifdef HAVE_OPAQUE_STRUCTS - EVP_CIPHER_CTX_free(c->aes_ctx); + ret = EVP_Cipher(*ctx, buf + aadlen, block + aadlen, cryptlen); #else - _libssh2_cipher_dtor(c->aes_ctx); - free(c->aes_ctx); + ret = EVP_Cipher(ctx, buf + aadlen, block + aadlen, cryptlen); #endif } - free(c); - - return 1; -} - -static const EVP_CIPHER * -make_ctr_evp (size_t keylen, EVP_CIPHER **aes_ctr_cipher, int type) -{ -#ifdef HAVE_OPAQUE_STRUCTS - *aes_ctr_cipher = EVP_CIPHER_meth_new(type, 16, keylen); - if(*aes_ctr_cipher) { - EVP_CIPHER_meth_set_iv_length(*aes_ctr_cipher, 16); - EVP_CIPHER_meth_set_init(*aes_ctr_cipher, aes_ctr_init); - EVP_CIPHER_meth_set_do_cipher(*aes_ctr_cipher, aes_ctr_do_cipher); - EVP_CIPHER_meth_set_cleanup(*aes_ctr_cipher, aes_ctr_cleanup); - } -#else - (*aes_ctr_cipher)->nid = type; - (*aes_ctr_cipher)->block_size = 16; - (*aes_ctr_cipher)->key_len = keylen; - (*aes_ctr_cipher)->iv_len = 16; - (*aes_ctr_cipher)->init = aes_ctr_init; - (*aes_ctr_cipher)->do_cipher = aes_ctr_do_cipher; - (*aes_ctr_cipher)->cleanup = aes_ctr_cleanup; -#endif - - return *aes_ctr_cipher; -} - -const EVP_CIPHER * -_libssh2_EVP_aes_128_ctr(void) -{ -#ifdef HAVE_OPAQUE_STRUCTS - static EVP_CIPHER * aes_ctr_cipher; - return !aes_ctr_cipher ? - make_ctr_evp(16, &aes_ctr_cipher, NID_aes_128_ctr) : aes_ctr_cipher; +#if (defined(OPENSSL_VERSION_MAJOR) && OPENSSL_VERSION_MAJOR >= 3) || \ + defined(LIBSSH2_WOLFSSL) + if(ret != -1) #else - static EVP_CIPHER aes_ctr_cipher; - static EVP_CIPHER * aes_ctr_cipher_ptr = &aes_ctr_cipher; - return !aes_ctr_cipher.key_len ? - make_ctr_evp(16, &aes_ctr_cipher_ptr, 0) : &aes_ctr_cipher; + if(ret >= 1) #endif -} - -const EVP_CIPHER * -_libssh2_EVP_aes_192_ctr(void) -{ + { + rc = 0; + if(IS_LAST(firstlast)) { + /* This is the last block. + encrypt: compute tag, if applicable + decrypt: verify tag, if applicable + in!=NULL is equivalent to EVP_CipherUpdate + in==NULL is equivalent to EVP_CipherFinal */ #ifdef HAVE_OPAQUE_STRUCTS - static EVP_CIPHER * aes_ctr_cipher; - return !aes_ctr_cipher ? - make_ctr_evp(24, &aes_ctr_cipher, NID_aes_192_ctr) : aes_ctr_cipher; + ret = EVP_Cipher(*ctx, NULL, NULL, 0); /* final */ #else - static EVP_CIPHER aes_ctr_cipher; - static EVP_CIPHER * aes_ctr_cipher_ptr = &aes_ctr_cipher; - return !aes_ctr_cipher.key_len ? - make_ctr_evp(24, &aes_ctr_cipher_ptr, 0) : &aes_ctr_cipher; + ret = EVP_Cipher(ctx, NULL, NULL, 0); /* final */ #endif -} + if(ret < 0) { + ret = 0; + } + else { + ret = 1; +#if LIBSSH2_AES_GCM + if(is_aesgcm && encrypt) { + /* write the Authentication Tag a.k.a. MAC at the end + of the block */ + assert(authenticationtag == authlen); + ret = EVP_CIPHER_CTX_ctrl(*ctx, EVP_CTRL_GCM_GET_TAG, + authlen, block + blocksize - authenticationtag); + } +#endif /* LIBSSH2_AES_GCM */ + } + } + /* Copy en/decrypted data back to the caller. + The first aadlen should not be touched because they weren't + encrypted and are unmodified. */ + memcpy(block + aadlen, buf + aadlen, cryptlen); + rc = !ret; + } -const EVP_CIPHER * -_libssh2_EVP_aes_256_ctr(void) -{ -#ifdef HAVE_OPAQUE_STRUCTS - static EVP_CIPHER * aes_ctr_cipher; - return !aes_ctr_cipher ? - make_ctr_evp(32, &aes_ctr_cipher, NID_aes_256_ctr) : aes_ctr_cipher; -#else - static EVP_CIPHER aes_ctr_cipher; - static EVP_CIPHER * aes_ctr_cipher_ptr = &aes_ctr_cipher; - return !aes_ctr_cipher.key_len ? - make_ctr_evp(32, &aes_ctr_cipher_ptr, 0) : &aes_ctr_cipher; -#endif + /* TODO: the return code should distinguish between decryption errors and + invalid MACs */ + return rc; } -#endif /* LIBSSH2_AES_CTR */ - -#ifndef HAVE_EVP_AES_128_CTR -static EVP_CIPHER * aes_128_ctr_cipher = NULL; -static EVP_CIPHER * aes_192_ctr_cipher = NULL; -static EVP_CIPHER * aes_256_ctr_cipher = NULL; -#endif - void _libssh2_openssl_crypto_init(void) { #if OPENSSL_VERSION_NUMBER >= 0x10100000L && \ @@ -655,44 +632,22 @@ void _libssh2_openssl_crypto_init(void) ENGINE_register_all_complete(); #endif #endif -#ifndef HAVE_EVP_AES_128_CTR - aes_128_ctr_cipher = (EVP_CIPHER *)_libssh2_EVP_aes_128_ctr(); - aes_192_ctr_cipher = (EVP_CIPHER *)_libssh2_EVP_aes_192_ctr(); - aes_256_ctr_cipher = (EVP_CIPHER *)_libssh2_EVP_aes_256_ctr(); -#endif } void _libssh2_openssl_crypto_exit(void) { -#ifndef HAVE_EVP_AES_128_CTR -#ifdef HAVE_OPAQUE_STRUCTS - if(aes_128_ctr_cipher) { - EVP_CIPHER_meth_free(aes_128_ctr_cipher); - } - - if(aes_192_ctr_cipher) { - EVP_CIPHER_meth_free(aes_192_ctr_cipher); - } - - if(aes_256_ctr_cipher) { - EVP_CIPHER_meth_free(aes_256_ctr_cipher); - } -#endif - - aes_128_ctr_cipher = NULL; - aes_192_ctr_cipher = NULL; - aes_256_ctr_cipher = NULL; -#endif } +#if LIBSSH2_RSA || LIBSSH2_DSA || LIBSSH2_ECDSA || LIBSSH2_ED25519 /* TODO: Optionally call a passphrase callback specified by the * calling program */ static int passphrase_cb(char *buf, int size, int rwflag, char *passphrase) { - int passphrase_len = strlen(passphrase); - (void) rwflag; + int passphrase_len = (int) strlen(passphrase); + + (void)rwflag; if(passphrase_len > (size - 1)) { passphrase_len = size - 1; @@ -717,19 +672,24 @@ read_private_key_from_memory(void **key_ctx, *key_ctx = NULL; - bp = BIO_new_mem_buf((char *)filedata, filedata_len); +#if OPENSSL_VERSION_NUMBER >= 0x1000200fL + bp = BIO_new_mem_buf(filedata, (int)filedata_len); +#else + bp = BIO_new_mem_buf((char *)filedata, (int)filedata_len); +#endif if(!bp) { return -1; } + *key_ctx = read_private_key(bp, NULL, (pem_password_cb *) passphrase_cb, (void *) passphrase); BIO_free(bp); return (*key_ctx) ? 0 : -1; } +#endif - - +#if LIBSSH2_RSA || LIBSSH2_DSA || LIBSSH2_ECDSA static int read_private_key_from_file(void **key_ctx, pem_read_bio_func read_private_key, @@ -751,7 +711,9 @@ read_private_key_from_file(void **key_ctx, BIO_free(bp); return (*key_ctx) ? 0 : -1; } +#endif +#if LIBSSH2_RSA int _libssh2_rsa_new_private_frommemory(libssh2_rsa_ctx ** rsa, LIBSSH2_SESSION * session, @@ -762,7 +724,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(); @@ -774,7 +735,7 @@ _libssh2_rsa_new_private_frommemory(libssh2_rsa_ctx ** rsa, "ssh-rsa", filedata, filedata_len, passphrase); } -return rc; + return rc; } static unsigned char * @@ -800,7 +761,7 @@ gen_publickey_from_rsa(LIBSSH2_SESSION *session, RSA *rsa, len = 4 + 7 + 4 + e_bytes + 4 + n_bytes; key = LIBSSH2_ALLOC(session, len); - if(key == NULL) { + if(!key) { return NULL; } @@ -832,23 +793,23 @@ gen_publickey_from_rsa_evp(LIBSSH2_SESSION *session, unsigned char *method_buf = NULL; size_t key_len; - _libssh2_debug(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) { + if(!rsa) { /* Assume memory allocation error... what else could it be ? */ goto __alloc_error; } method_buf = LIBSSH2_ALLOC(session, 7); /* ssh-rsa. */ - if(method_buf == NULL) { + if(!method_buf) { goto __alloc_error; } key = gen_publickey_from_rsa(session, rsa, &key_len); - if(key == NULL) { + if(!key) { goto __alloc_error; } RSA_free(rsa); @@ -860,11 +821,11 @@ gen_publickey_from_rsa_evp(LIBSSH2_SESSION *session, *pubkeydata_len = key_len; return 0; - __alloc_error: - if(rsa != NULL) { +__alloc_error: + if(rsa) { RSA_free(rsa); } - if(method_buf != NULL) { + if(method_buf) { LIBSSH2_FREE(session, method_buf); } @@ -894,23 +855,23 @@ static int _libssh2_rsa_new_additional_parameters(RSA *rsa) #endif ctx = BN_CTX_new(); - if(ctx == NULL) + if(!ctx) return -1; aux = BN_new(); - if(aux == NULL) { + if(!aux) { rc = -1; goto out; } dmp1 = BN_new(); - if(dmp1 == NULL) { + if(!dmp1) { rc = -1; goto out; } dmq1 = BN_new(); - if(dmq1 == NULL) { + if(!dmq1) { rc = -1; goto out; } @@ -935,7 +896,7 @@ out: BN_clear_free(aux); BN_CTX_free(ctx); - if(rc != 0) { + if(rc) { if(dmp1) BN_clear_free(dmp1); if(dmq1) @@ -959,9 +920,9 @@ gen_publickey_from_rsa_openssh_priv_data(LIBSSH2_SESSION *session, unsigned char *n, *e, *d, *p, *q, *coeff, *comment; RSA *rsa = NULL; - _libssh2_debug(session, + _libssh2_debug((session, LIBSSH2_TRACE_AUTH, - "Computing RSA keys from private key data"); + "Computing RSA keys from private key data")); /* public key data */ if(_libssh2_get_bignum_bytes(decrypted, &n, &nlen)) { @@ -1007,19 +968,25 @@ gen_publickey_from_rsa_openssh_priv_data(LIBSSH2_SESSION *session, return -1; } - if((rc = _libssh2_rsa_new(&rsa, e, elen, n, nlen, d, dlen, p, plen, - q, qlen, NULL, 0, NULL, 0, - coeff, coefflen)) != 0) { - _libssh2_debug(session, + rc = _libssh2_rsa_new(&rsa, + e, (unsigned long)elen, + n, (unsigned long)nlen, + d, (unsigned long)dlen, + p, (unsigned long)plen, + q, (unsigned long)qlen, + NULL, 0, NULL, 0, + coeff, (unsigned long)coefflen); + if(rc) { + _libssh2_debug((session, LIBSSH2_TRACE_AUTH, - "Could not create RSA private key"); + "Could not create RSA private key")); goto fail; } - if(rsa != NULL) + if(rsa) rc = _libssh2_rsa_new_additional_parameters(rsa); - if(rsa != NULL && pubkeydata != NULL && method != NULL) { + if(rsa && pubkeydata && method) { EVP_PKEY *pk = EVP_PKEY_new(); EVP_PKEY_set1_RSA(pk, rsa); @@ -1031,7 +998,7 @@ gen_publickey_from_rsa_openssh_priv_data(LIBSSH2_SESSION *session, EVP_PKEY_free(pk); } - if(rsa_ctx != NULL) + if(rsa_ctx) *rsa_ctx = rsa; else RSA_free(rsa); @@ -1040,7 +1007,7 @@ gen_publickey_from_rsa_openssh_priv_data(LIBSSH2_SESSION *session, fail: - if(rsa != NULL) + if(rsa) RSA_free(rsa); return _libssh2_error(session, @@ -1059,7 +1026,7 @@ _libssh2_rsa_new_openssh_private(libssh2_rsa_ctx ** rsa, unsigned char *buf = NULL; struct string_buf *decrypted = NULL; - if(session == NULL) { + if(!session) { _libssh2_error(session, LIBSSH2_ERROR_PROTO, "Session is required"); return -1; @@ -1083,7 +1050,7 @@ _libssh2_rsa_new_openssh_private(libssh2_rsa_ctx ** rsa, /* We have a new key file, now try and parse it using supported types */ rc = _libssh2_get_string(decrypted, &buf, NULL); - if(rc != 0 || buf == NULL) { + if(rc || !buf) { _libssh2_error(session, LIBSSH2_ERROR_PROTO, "Public key type in decrypted key data not found"); return -1; @@ -1113,7 +1080,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(); @@ -1127,6 +1093,7 @@ _libssh2_rsa_new_private(libssh2_rsa_ctx ** rsa, return rc; } +#endif #if LIBSSH2_DSA int @@ -1139,16 +1106,18 @@ _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(); rc = read_private_key_from_memory((void **)dsa, read_dsa, - filedata, filedata_len, passphrase); + filedata, filedata_len, + passphrase); if(rc) { rc = read_openssh_private_key_from_memory((void **)dsa, session, - "ssh-dsa", filedata, filedata_len, passphrase); + "ssh-dsa", + filedata, filedata_len, + passphrase); } return rc; @@ -1189,7 +1158,7 @@ gen_publickey_from_dsa(LIBSSH2_SESSION* session, DSA *dsa, len = 4 + 7 + 4 + p_bytes + 4 + q_bytes + 4 + g_bytes + 4 + k_bytes; key = LIBSSH2_ALLOC(session, len); - if(key == NULL) { + if(!key) { return NULL; } @@ -1223,23 +1192,23 @@ gen_publickey_from_dsa_evp(LIBSSH2_SESSION *session, unsigned char *method_buf = NULL; size_t key_len; - _libssh2_debug(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) { + if(!dsa) { /* Assume memory allocation error... what else could it be ? */ goto __alloc_error; } method_buf = LIBSSH2_ALLOC(session, 7); /* ssh-dss. */ - if(method_buf == NULL) { + if(!method_buf) { goto __alloc_error; } key = gen_publickey_from_dsa(session, dsa, &key_len); - if(key == NULL) { + if(!key) { goto __alloc_error; } DSA_free(dsa); @@ -1251,11 +1220,11 @@ gen_publickey_from_dsa_evp(LIBSSH2_SESSION *session, *pubkeydata_len = key_len; return 0; - __alloc_error: - if(dsa != NULL) { +__alloc_error: + if(dsa) { DSA_free(dsa); } - if(method_buf != NULL) { + if(method_buf) { LIBSSH2_FREE(session, method_buf); } @@ -1278,9 +1247,9 @@ gen_publickey_from_dsa_openssh_priv_data(LIBSSH2_SESSION *session, unsigned char *p, *q, *g, *pub_key, *priv_key; DSA *dsa = NULL; - _libssh2_debug(session, + _libssh2_debug((session, LIBSSH2_TRACE_AUTH, - "Computing DSA keys from private key data"); + "Computing DSA keys from private key data")); if(_libssh2_get_bignum_bytes(decrypted, &p, &plen)) { _libssh2_error(session, LIBSSH2_ERROR_PROTO, @@ -1312,16 +1281,20 @@ gen_publickey_from_dsa_openssh_priv_data(LIBSSH2_SESSION *session, return -1; } - rc = _libssh2_dsa_new(&dsa, p, plen, q, qlen, g, glen, pub_key, pub_len, - priv_key, priv_len); - if(rc != 0) { - _libssh2_debug(session, + rc = _libssh2_dsa_new(&dsa, + p, (unsigned long)plen, + q, (unsigned long)qlen, + g, (unsigned long)glen, + pub_key, (unsigned long)pub_len, + priv_key, (unsigned long)priv_len); + if(rc) { + _libssh2_debug((session, LIBSSH2_ERROR_PROTO, - "Could not create DSA private key"); + "Could not create DSA private key")); goto fail; } - if(dsa != NULL && pubkeydata != NULL && method != NULL) { + if(dsa && pubkeydata && method) { EVP_PKEY *pk = EVP_PKEY_new(); EVP_PKEY_set1_DSA(pk, dsa); @@ -1333,7 +1306,7 @@ gen_publickey_from_dsa_openssh_priv_data(LIBSSH2_SESSION *session, EVP_PKEY_free(pk); } - if(dsa_ctx != NULL) + if(dsa_ctx) *dsa_ctx = dsa; else DSA_free(dsa); @@ -1342,7 +1315,7 @@ gen_publickey_from_dsa_openssh_priv_data(LIBSSH2_SESSION *session, fail: - if(dsa != NULL) + if(dsa) DSA_free(dsa); return _libssh2_error(session, @@ -1361,7 +1334,7 @@ _libssh2_dsa_new_openssh_private(libssh2_dsa_ctx ** dsa, unsigned char *buf = NULL; struct string_buf *decrypted = NULL; - if(session == NULL) { + if(!session) { _libssh2_error(session, LIBSSH2_ERROR_PROTO, "Session is required"); return -1; @@ -1385,7 +1358,7 @@ _libssh2_dsa_new_openssh_private(libssh2_dsa_ctx ** dsa, /* We have a new key file, now try and parse it using supported types */ rc = _libssh2_get_string(decrypted, &buf, NULL); - if(rc != 0 || buf == NULL) { + if(rc || !buf) { _libssh2_error(session, LIBSSH2_ERROR_PROTO, "Public key type in decrypted key data not found"); return -1; @@ -1415,7 +1388,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(); @@ -1429,147 +1401,204 @@ _libssh2_dsa_new_private(libssh2_dsa_ctx ** dsa, return rc; } - #endif /* LIBSSH_DSA */ #if LIBSSH2_ECDSA int _libssh2_ecdsa_new_private_frommemory(libssh2_ecdsa_ctx ** ec_ctx, - LIBSSH2_SESSION * session, - const char *filedata, size_t filedata_len, - unsigned const char *passphrase) + LIBSSH2_SESSION * session, + const char *filedata, + size_t filedata_len, + unsigned const char *passphrase) { int rc; pem_read_bio_func read_ec = (pem_read_bio_func) &PEM_read_bio_ECPrivateKey; - (void) session; _libssh2_init_if_needed(); rc = read_private_key_from_memory((void **) ec_ctx, read_ec, - filedata, filedata_len, passphrase); + filedata, filedata_len, + passphrase); if(rc) { rc = read_openssh_private_key_from_memory((void **)ec_ctx, session, - "ssh-ecdsa", filedata, - filedata_len, passphrase); + "ssh-ecdsa", + filedata, filedata_len, + passphrase); } return rc; } +int _libssh2_ecdsa_new_private_frommemory_sk(libssh2_ecdsa_ctx ** ec_ctx, + unsigned char *flags, + const char **application, + const unsigned char **key_handle, + size_t *handle_len, + LIBSSH2_SESSION * session, + const char *filedata, + size_t filedata_len, + unsigned const char *passphrase) +{ + int algorithm; + return _libssh2_sk_pub_openssh_keyfilememory(session, + (void **)ec_ctx, + "sk-ecdsa-sha2-nistp256@openssh.com", + NULL, + NULL, + NULL, + NULL, + &algorithm, + flags, + application, + key_handle, + handle_len, + filedata, + filedata_len, + passphrase); +} + #endif /* LIBSSH2_ECDSA */ #if LIBSSH2_ED25519 int -_libssh2_curve25519_new(LIBSSH2_SESSION *session, libssh2_x25519_ctx **out_ctx, +_libssh2_curve25519_new(LIBSSH2_SESSION *session, unsigned char **out_public_key, unsigned char **out_private_key) { EVP_PKEY *key = NULL; EVP_PKEY_CTX *pctx = NULL; - PKCS8_PRIV_KEY_INFO *info = NULL; - ASN1_OCTET_STRING *oct = NULL; - X509_PUBKEY *pubkey = NULL; - libssh2_ed25519_ctx *ctx = NULL; - const unsigned char *pkcs, *priv, *pub; - int privLen, pubLen, pkcsLen; + unsigned char *priv = NULL, *pub = NULL; + size_t privLen, pubLen; int rc = -1; pctx = EVP_PKEY_CTX_new_id(EVP_PKEY_X25519, NULL); - if(pctx == NULL) + if(!pctx) return -1; - EVP_PKEY_keygen_init(pctx); - EVP_PKEY_keygen(pctx, &key); - info = EVP_PKEY2PKCS8(key); - - if(info == NULL || !PKCS8_pkey_get0(NULL, &pkcs, &pkcsLen, NULL, info)) - goto cleanExit; - - oct = d2i_ASN1_OCTET_STRING(NULL, &pkcs, pkcsLen); - if(oct == NULL) { - goto cleanExit; + if(EVP_PKEY_keygen_init(pctx) != 1 || + EVP_PKEY_keygen(pctx, &key) != 1) { + goto clean_exit; } - priv = ASN1_STRING_get0_data(oct); - privLen = ASN1_STRING_length(oct); - - if(privLen != LIBSSH2_ED25519_KEY_LEN) - goto cleanExit; - - pubkey = X509_PUBKEY_new(); - if(pubkey == NULL || !X509_PUBKEY_set(&pubkey, key)) - goto cleanExit; - - if(!X509_PUBKEY_get0_param(NULL, &pub, &pubLen, NULL, pubkey)) - goto cleanExit; - - if(pubLen != LIBSSH2_ED25519_KEY_LEN) - goto cleanExit; - - if(out_private_key != NULL) { - *out_private_key = LIBSSH2_ALLOC(session, LIBSSH2_ED25519_KEY_LEN); - if(*out_private_key == NULL) - goto cleanExit; - - memcpy(*out_private_key, priv, LIBSSH2_ED25519_KEY_LEN); - } + if(out_private_key) { + privLen = LIBSSH2_ED25519_KEY_LEN; + priv = LIBSSH2_ALLOC(session, privLen); + if(!priv) + goto clean_exit; - if(out_public_key != NULL) { - *out_public_key = LIBSSH2_ALLOC(session, LIBSSH2_ED25519_KEY_LEN); - if(*out_public_key == NULL) - goto cleanExit; + if(EVP_PKEY_get_raw_private_key(key, priv, &privLen) != 1 || + privLen != LIBSSH2_ED25519_KEY_LEN) { + goto clean_exit; + } - memcpy(*out_public_key, pub, LIBSSH2_ED25519_KEY_LEN); + *out_private_key = priv; + priv = NULL; } - if(out_ctx != NULL) { - ctx = malloc(sizeof(libssh2_x25519_ctx)); - if(ctx == NULL) - 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(out_public_key) { + pubLen = LIBSSH2_ED25519_KEY_LEN; + pub = LIBSSH2_ALLOC(session, pubLen); + if(!pub) + goto clean_exit; - if(ctx->public_key == NULL || ctx->private_key == NULL) { - _libssh2_x25519_free(ctx); - goto cleanExit; + if(EVP_PKEY_get_raw_public_key(key, pub, &pubLen) != 1 || + pubLen != LIBSSH2_ED25519_KEY_LEN) { + goto clean_exit; } - *out_ctx = ctx; + *out_public_key = pub; + pub = NULL; } /* success */ rc = 0; -cleanExit: +clean_exit: - if(info) - PKCS8_PRIV_KEY_INFO_free(info); if(pctx) EVP_PKEY_CTX_free(pctx); - if(oct) - ASN1_OCTET_STRING_free(oct); - if(pubkey) - X509_PUBKEY_free(pubkey); if(key) EVP_PKEY_free(key); + if(priv) + LIBSSH2_FREE(session, priv); + if(pub) + LIBSSH2_FREE(session, pub); 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, (uint32_t) 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, @@ -1587,9 +1616,9 @@ gen_publickey_from_ed25519_openssh_priv_data(LIBSSH2_SESSION *session, size_t key_len = 0, tmp_len = 0; unsigned char *p; - _libssh2_debug(session, + _libssh2_debug((session, LIBSSH2_TRACE_AUTH, - "Computing ED25519 keys from private key data"); + "Computing ED25519 keys from private key data")); if(_libssh2_get_string(decrypted, &pub_key, &tmp_len) || tmp_len != LIBSSH2_ED25519_KEY_LEN) { @@ -1606,25 +1635,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)) { @@ -1636,12 +1651,12 @@ gen_publickey_from_ed25519_openssh_priv_data(LIBSSH2_SESSION *session, if(tmp_len > 0) { unsigned char *comment = LIBSSH2_CALLOC(session, tmp_len + 1); - if(comment != NULL) { + if(comment) { memcpy(comment, buf, tmp_len); memcpy(comment + tmp_len, "\0", 1); - _libssh2_debug(session, LIBSSH2_TRACE_AUTH, "Key comment: %s", - comment); + _libssh2_debug((session, LIBSSH2_TRACE_AUTH, "Key comment: %s", + comment)); LIBSSH2_FREE(session, comment); } @@ -1661,13 +1676,15 @@ gen_publickey_from_ed25519_openssh_priv_data(LIBSSH2_SESSION *session, } if(ret == 0) { - _libssh2_debug(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) { + if(!method_buf) { + _libssh2_error(session, LIBSSH2_ERROR_ALLOC, + "Unable to allocate memory for ED25519 key"); goto clean_exit; } @@ -1675,7 +1692,9 @@ gen_publickey_from_ed25519_openssh_priv_data(LIBSSH2_SESSION *session, pub_key(32). */ key_len = LIBSSH2_ED25519_KEY_LEN + 19; key = LIBSSH2_CALLOC(session, key_len); - if(key == NULL) { + if(!key) { + _libssh2_error(session, LIBSSH2_ERROR_ALLOC, + "Unable to allocate memory for ED25519 key"); goto clean_exit; } @@ -1686,25 +1705,168 @@ gen_publickey_from_ed25519_openssh_priv_data(LIBSSH2_SESSION *session, memcpy(method_buf, "ssh-ed25519", 11); - if(method != NULL) + if(method) *method = method_buf; else LIBSSH2_FREE(session, method_buf); - if(method_len != NULL) + if(method_len) *method_len = 11; - if(pubkeydata != NULL) + if(pubkeydata) *pubkeydata = key; else LIBSSH2_FREE(session, key); - if(pubkeydata_len != NULL) + if(pubkeydata_len) + *pubkeydata_len = key_len; + + if(out_ctx) + *out_ctx = ctx; + else if(ctx) + _libssh2_ed25519_free(ctx); + + return 0; + } + +clean_exit: + + if(ctx) + _libssh2_ed25519_free(ctx); + + if(method_buf) + LIBSSH2_FREE(session, method_buf); + + if(key) + LIBSSH2_FREE(session, key); + + return -1; +} + +static int +gen_publickey_from_sk_ed25519_openssh_priv_data(LIBSSH2_SESSION *session, + struct string_buf *decrypted, + unsigned char **method, + size_t *method_len, + unsigned char **pubkeydata, + size_t *pubkeydata_len, + unsigned char *flags, + const char **application, + const unsigned char **key_handle, + size_t *handle_len, + libssh2_ed25519_ctx **out_ctx) +{ + const char *key_type = "sk-ssh-ed25519@openssh.com"; + + libssh2_ed25519_ctx *ctx = NULL; + unsigned char *method_buf = NULL; + unsigned char *key = NULL; + int ret = 0; + unsigned char *pub_key, *app; + size_t key_len = 0, app_len = 0, tmp_len = 0; + unsigned char *p; + + _libssh2_debug((session, + LIBSSH2_TRACE_AUTH, + "Computing sk-ED25519 keys from private key data")); + + if(_libssh2_get_string(decrypted, &pub_key, &tmp_len) || + tmp_len != LIBSSH2_ED25519_KEY_LEN) { + _libssh2_error(session, LIBSSH2_ERROR_PROTO, + "Wrong public key length"); + return -1; + } + + if(_libssh2_get_string(decrypted, &app, &app_len)) { + _libssh2_error(session, LIBSSH2_ERROR_PROTO, + "No SK application."); + return -1; + } + + if(flags && _libssh2_get_byte(decrypted, flags)) { + _libssh2_error(session, LIBSSH2_ERROR_PROTO, + "No SK flags."); + return -1; + } + + if(key_handle && handle_len) { + unsigned char *handle = NULL; + if(_libssh2_get_string(decrypted, &handle, handle_len)) { + _libssh2_error(session, LIBSSH2_ERROR_PROTO, + "No SK key_handle."); + return -1; + } + + if(*handle_len > 0) { + *key_handle = LIBSSH2_ALLOC(session, *handle_len); + + if(key_handle) { + memcpy((void *)*key_handle, handle, *handle_len); + } + } + } + + ctx = EVP_PKEY_new_raw_public_key(EVP_PKEY_ED25519, NULL, + (const unsigned char *)pub_key, + LIBSSH2_ED25519_KEY_LEN); + + if(ret == 0) { + _libssh2_debug((session, + LIBSSH2_TRACE_AUTH, + "Computing public key from ED25519 " + "private key envelope")); + + /* sk-ssh-ed25519@openssh.com. */ + method_buf = LIBSSH2_ALLOC(session, strlen(key_type)); + if(!method_buf) { + _libssh2_error(session, LIBSSH2_ERROR_ALLOC, + "Unable to allocate memory for ED25519 key"); + goto clean_exit; + } + + /* Key form is: type_len(4) + type(26) + pub_key_len(4) + + pub_key(32) + application_len(4) + application(X). */ + key_len = LIBSSH2_ED25519_KEY_LEN + 38 + app_len; + key = LIBSSH2_CALLOC(session, key_len); + if(!key) { + _libssh2_error(session, LIBSSH2_ERROR_ALLOC, + "Unable to allocate memory for ED25519 key"); + goto clean_exit; + } + + p = key; + + _libssh2_store_str(&p, key_type, strlen(key_type)); + _libssh2_store_str(&p, (const char *)pub_key, LIBSSH2_ED25519_KEY_LEN); + _libssh2_store_str(&p, (const char *)app, app_len); + + if(application && app_len > 0) { + *application = (const char *)LIBSSH2_ALLOC(session, app_len + 1); + _libssh2_explicit_zero((void *)*application, app_len + 1); + memcpy((void *)*application, app, app_len); + } + + memcpy(method_buf, key_type, strlen(key_type)); + + if(method) + *method = method_buf; + else + LIBSSH2_FREE(session, method_buf); + + if(method_len) + *method_len = strlen(key_type); + + if(pubkeydata) + *pubkeydata = key; + else if(key) + LIBSSH2_FREE(session, key); + + if(pubkeydata_len) *pubkeydata_len = key_len; - if(out_ctx != NULL) + if(out_ctx) *out_ctx = ctx; - else if(ctx != NULL) + else if(ctx) _libssh2_ed25519_free(ctx); return 0; @@ -1721,9 +1883,20 @@ clean_exit: if(key) LIBSSH2_FREE(session, key); + if(application && *application) { + LIBSSH2_FREE(session, (void *)application); + *application = NULL; + } + + if(key_handle && *key_handle) { + LIBSSH2_FREE(session, (void *)key_handle); + *key_handle = NULL; + } + return -1; } + int _libssh2_ed25519_new_private(libssh2_ed25519_ctx ** ed_ctx, LIBSSH2_SESSION * session, @@ -1735,7 +1908,7 @@ _libssh2_ed25519_new_private(libssh2_ed25519_ctx ** ed_ctx, struct string_buf *decrypted = NULL; libssh2_ed25519_ctx *ctx = NULL; - if(session == NULL) { + if(!session) { _libssh2_error(session, LIBSSH2_ERROR_PROTO, "Session is required"); return -1; @@ -1759,7 +1932,7 @@ _libssh2_ed25519_new_private(libssh2_ed25519_ctx ** ed_ctx, /* We have a new key file, now try and parse it using supported types */ rc = _libssh2_get_string(decrypted, &buf, NULL); - if(rc != 0 || buf == NULL) { + if(rc || !buf) { _libssh2_error(session, LIBSSH2_ERROR_PROTO, "Public key type in decrypted key data not found"); return -1; @@ -1782,9 +1955,85 @@ _libssh2_ed25519_new_private(libssh2_ed25519_ctx ** ed_ctx, _libssh2_string_buf_free(session, decrypted); if(rc == 0) { - if(ed_ctx != NULL) + if(ed_ctx) + *ed_ctx = ctx; + else if(ctx) + _libssh2_ed25519_free(ctx); + } + + return rc; +} + +int +_libssh2_ed25519_new_private_sk(libssh2_ed25519_ctx **ed_ctx, + unsigned char *flags, + const char **application, + const unsigned char **key_handle, + size_t *handle_len, + LIBSSH2_SESSION *session, + const char *filename, + const uint8_t *passphrase) +{ + int rc; + FILE *fp; + unsigned char *buf; + struct string_buf *decrypted = NULL; + libssh2_ed25519_ctx *ctx = NULL; + + if(!session) { + _libssh2_error(session, LIBSSH2_ERROR_PROTO, + "Session is required"); + return -1; + } + + _libssh2_init_if_needed(); + + fp = fopen(filename, "r"); + if(!fp) { + _libssh2_error(session, LIBSSH2_ERROR_FILE, + "Unable to open ED25519 SK private key file"); + return -1; + } + + rc = _libssh2_openssh_pem_parse(session, passphrase, fp, &decrypted); + fclose(fp); + if(rc) { + return rc; + } + + /* We have a new key file, now try and parse it using supported types */ + rc = _libssh2_get_string(decrypted, &buf, NULL); + + if(rc || !buf) { + _libssh2_error(session, LIBSSH2_ERROR_PROTO, + "Public key type in decrypted key data not found"); + return -1; + } + + if(strcmp("sk-ssh-ed25519@openssh.com", (const char *)buf) == 0) { + rc = gen_publickey_from_sk_ed25519_openssh_priv_data(session, + decrypted, + NULL, + NULL, + NULL, + NULL, + flags, + application, + key_handle, + handle_len, + &ctx); + } + else { + rc = -1; + } + + if(decrypted) + _libssh2_string_buf_free(session, decrypted); + + if(rc == 0) { + if(ed_ctx) *ed_ctx = ctx; - else if(ctx != NULL) + else if(ctx) _libssh2_ed25519_free(ctx); } @@ -1798,6 +2047,25 @@ _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, @@ -1805,46 +2073,64 @@ _libssh2_ed25519_new_private_frommemory(libssh2_ed25519_ctx ** ed_ctx, } int +_libssh2_ed25519_new_private_frommemory_sk(libssh2_ed25519_ctx **ed_ctx, + unsigned char *flags, + const char **application, + const unsigned char **key_handle, + size_t *handle_len, + LIBSSH2_SESSION *session, + const char *filedata, + size_t filedata_len, + unsigned const char *passphrase) +{ + int algorithm; + return _libssh2_sk_pub_openssh_keyfilememory(session, + (void **)ed_ctx, + "sk-ssh-ed25519@openssh.com", + NULL, + NULL, + NULL, + NULL, + &algorithm, + flags, + application, + key_handle, + handle_len, + filedata, + filedata_len, + passphrase); +} + +int _libssh2_ed25519_new_public(libssh2_ed25519_ctx ** ed_ctx, LIBSSH2_SESSION * session, const unsigned char *raw_pub_key, - const uint8_t key_len) + const size_t key_len) { libssh2_ed25519_ctx *ctx = NULL; - EVP_PKEY *public_key = NULL; - if(ed_ctx == NULL) + if(!ed_ctx) 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) + if(ed_ctx) *ed_ctx = ctx; - else if(ctx != NULL) + else if(ctx) _libssh2_ed25519_free(ctx); return 0; } - #endif /* LIBSSH2_ED25519 */ + +#if LIBSSH2_RSA int -_libssh2_rsa_sha1_sign(LIBSSH2_SESSION * session, +_libssh2_rsa_sha2_sign(LIBSSH2_SESSION * session, libssh2_rsa_ctx * rsactx, const unsigned char *hash, size_t hash_len, @@ -1861,7 +2147,20 @@ _libssh2_rsa_sha1_sign(LIBSSH2_SESSION * session, return -1; } - ret = RSA_sign(NID_sha1, hash, hash_len, sig, &sig_len, rsactx); + if(hash_len == SHA_DIGEST_LENGTH) + ret = RSA_sign(NID_sha1, + hash, (unsigned int) hash_len, sig, &sig_len, rsactx); + else if(hash_len == SHA256_DIGEST_LENGTH) + ret = RSA_sign(NID_sha256, + hash, (unsigned int) hash_len, sig, &sig_len, rsactx); + else if(hash_len == SHA512_DIGEST_LENGTH) + ret = RSA_sign(NID_sha512, + hash, (unsigned int) hash_len, sig, &sig_len, rsactx); + else { + _libssh2_error(session, LIBSSH2_ERROR_PROTO, + "Unsupported hash digest length"); + ret = -1; + } if(!ret) { LIBSSH2_FREE(session, sig); @@ -1874,17 +2173,32 @@ _libssh2_rsa_sha1_sign(LIBSSH2_SESSION * session, return 0; } +#if LIBSSH2_RSA_SHA1 +int +_libssh2_rsa_sha1_sign(LIBSSH2_SESSION * session, + libssh2_rsa_ctx * rsactx, + const unsigned char *hash, + size_t hash_len, + unsigned char **signature, size_t *signature_len) +{ + return _libssh2_rsa_sha2_sign(session, rsactx, hash, hash_len, + signature, signature_len); +} +#endif +#endif + #if LIBSSH2_DSA int _libssh2_dsa_sha1_sign(libssh2_dsa_ctx * dsactx, const unsigned char *hash, - unsigned long hash_len, unsigned char *signature) + size_t hash_len, unsigned char *signature) { DSA_SIG *sig; const BIGNUM * r; const BIGNUM * s; int r_len, s_len; - (void) hash_len; + + (void)hash_len; sig = DSA_do_sign(hash, SHA_DIGEST_LENGTH, dsactx); if(!sig) { @@ -1923,8 +2237,8 @@ _libssh2_dsa_sha1_sign(libssh2_dsa_ctx * dsactx, int _libssh2_ecdsa_sign(LIBSSH2_SESSION * session, libssh2_ecdsa_ctx * ec_ctx, - const unsigned char *hash, unsigned long hash_len, - unsigned char **signature, size_t *signature_len) + const unsigned char *hash, size_t hash_len, + unsigned char **signature, size_t *signature_len) { int r_len, s_len; int rc = 0; @@ -1934,8 +2248,8 @@ _libssh2_ecdsa_sign(LIBSSH2_SESSION * session, libssh2_ecdsa_ctx * ec_ctx, unsigned char *temp_buffer = NULL; unsigned char *out_buffer = NULL; - ECDSA_SIG *sig = ECDSA_do_sign(hash, hash_len, ec_ctx); - if(sig == NULL) + ECDSA_SIG *sig = ECDSA_do_sign(hash, (int) hash_len, ec_ctx); + if(!sig) return -1; #ifdef HAVE_OPAQUE_STRUCTS ECDSA_SIG_get0(sig, &pr, &ps); @@ -1948,7 +2262,7 @@ _libssh2_ecdsa_sign(LIBSSH2_SESSION * session, libssh2_ecdsa_ctx * ec_ctx, s_len = BN_num_bytes(ps) + 1; temp_buffer = malloc(r_len + s_len + 8); - if(temp_buffer == NULL) { + if(!temp_buffer) { rc = -1; goto clean_exit; } @@ -1960,7 +2274,7 @@ _libssh2_ecdsa_sign(LIBSSH2_SESSION * session, libssh2_ecdsa_ctx * ec_ctx, out_buffer_len = (size_t)(sp - temp_buffer); out_buffer = LIBSSH2_CALLOC(session, out_buffer_len); - if(out_buffer == NULL) { + if(!out_buffer) { rc = -1; goto clean_exit; } @@ -1972,7 +2286,7 @@ _libssh2_ecdsa_sign(LIBSSH2_SESSION * session, libssh2_ecdsa_ctx * ec_ctx, clean_exit: - if(temp_buffer != NULL) + if(temp_buffer) free(temp_buffer); if(sig) @@ -1988,7 +2302,7 @@ _libssh2_sha1_init(libssh2_sha1_ctx *ctx) #ifdef HAVE_OPAQUE_STRUCTS *ctx = EVP_MD_CTX_new(); - if(*ctx == NULL) + if(!*ctx) return 0; if(EVP_DigestInit(*ctx, EVP_get_digestbyname("sha1"))) @@ -2005,13 +2319,13 @@ _libssh2_sha1_init(libssh2_sha1_ctx *ctx) } int -_libssh2_sha1(const unsigned char *message, unsigned long len, +_libssh2_sha1(const unsigned char *message, size_t len, unsigned char *out) { #ifdef HAVE_OPAQUE_STRUCTS EVP_MD_CTX * ctx = EVP_MD_CTX_new(); - if(ctx == NULL) + if(!ctx) return 1; /* error */ if(EVP_DigestInit(ctx, EVP_get_digestbyname("sha1"))) { @@ -2040,7 +2354,7 @@ _libssh2_sha256_init(libssh2_sha256_ctx *ctx) #ifdef HAVE_OPAQUE_STRUCTS *ctx = EVP_MD_CTX_new(); - if(*ctx == NULL) + if(!*ctx) return 0; if(EVP_DigestInit(*ctx, EVP_get_digestbyname("sha256"))) @@ -2057,13 +2371,13 @@ _libssh2_sha256_init(libssh2_sha256_ctx *ctx) } int -_libssh2_sha256(const unsigned char *message, unsigned long len, +_libssh2_sha256(const unsigned char *message, size_t len, unsigned char *out) { #ifdef HAVE_OPAQUE_STRUCTS EVP_MD_CTX * ctx = EVP_MD_CTX_new(); - if(ctx == NULL) + if(!ctx) return 1; /* error */ if(EVP_DigestInit(ctx, EVP_get_digestbyname("sha256"))) { @@ -2092,7 +2406,7 @@ _libssh2_sha384_init(libssh2_sha384_ctx *ctx) #ifdef HAVE_OPAQUE_STRUCTS *ctx = EVP_MD_CTX_new(); - if(*ctx == NULL) + if(!*ctx) return 0; if(EVP_DigestInit(*ctx, EVP_get_digestbyname("sha384"))) @@ -2109,13 +2423,13 @@ _libssh2_sha384_init(libssh2_sha384_ctx *ctx) } int -_libssh2_sha384(const unsigned char *message, unsigned long len, - unsigned char *out) +_libssh2_sha384(const unsigned char *message, size_t len, + unsigned char *out) { #ifdef HAVE_OPAQUE_STRUCTS EVP_MD_CTX * ctx = EVP_MD_CTX_new(); - if(ctx == NULL) + if(!ctx) return 1; /* error */ if(EVP_DigestInit(ctx, EVP_get_digestbyname("sha384"))) { @@ -2144,7 +2458,7 @@ _libssh2_sha512_init(libssh2_sha512_ctx *ctx) #ifdef HAVE_OPAQUE_STRUCTS *ctx = EVP_MD_CTX_new(); - if(*ctx == NULL) + if(!*ctx) return 0; if(EVP_DigestInit(*ctx, EVP_get_digestbyname("sha512"))) @@ -2161,13 +2475,13 @@ _libssh2_sha512_init(libssh2_sha512_ctx *ctx) } int -_libssh2_sha512(const unsigned char *message, unsigned long len, - unsigned char *out) +_libssh2_sha512(const unsigned char *message, size_t len, + unsigned char *out) { #ifdef HAVE_OPAQUE_STRUCTS EVP_MD_CTX * ctx = EVP_MD_CTX_new(); - if(ctx == NULL) + if(!ctx) return 1; /* error */ if(EVP_DigestInit(ctx, EVP_get_digestbyname("sha512"))) { @@ -2193,10 +2507,24 @@ _libssh2_sha512(const unsigned char *message, unsigned long len, int _libssh2_md5_init(libssh2_md5_ctx *ctx) { + /* MD5 digest is not supported in OpenSSL FIPS mode + * Trying to init it will result in a latent OpenSSL error: + * "digital envelope routines:FIPS_DIGESTINIT:disabled for fips" + * So, just return 0 in FIPS mode + */ +#if OPENSSL_VERSION_NUMBER >= 0x000907000L && \ + defined(OPENSSL_VERSION_MAJOR) && \ + OPENSSL_VERSION_MAJOR < 3 && \ + !defined(LIBRESSL_VERSION_NUMBER) + + if(FIPS_mode()) + return 0; +#endif + #ifdef HAVE_OPAQUE_STRUCTS *ctx = EVP_MD_CTX_new(); - if(*ctx == NULL) + if(!*ctx) return 0; if(EVP_DigestInit(*ctx, EVP_get_digestbyname("md5"))) @@ -2220,6 +2548,7 @@ gen_publickey_from_ec_evp(LIBSSH2_SESSION *session, size_t *method_len, unsigned char **pubkeydata, size_t *pubkeydata_len, + int is_sk, EVP_PKEY *pk) { int rc = 0; @@ -2235,40 +2564,47 @@ gen_publickey_from_ec_evp(LIBSSH2_SESSION *session, BN_CTX *bn_ctx; libssh2_curve_type type; - _libssh2_debug(session, - LIBSSH2_TRACE_AUTH, - "Computing public key from EC private key envelop"); + _libssh2_debug((session, + LIBSSH2_TRACE_AUTH, + "Computing public key from EC private key envelope")); bn_ctx = BN_CTX_new(); - if(bn_ctx == NULL) + if(!bn_ctx) return -1; ec = EVP_PKEY_get1_EC_KEY(pk); - if(ec == NULL) { + if(!ec) { rc = -1; goto clean_exit; } public_key = EC_KEY_get0_public_key(ec); group = EC_KEY_get0_group(ec); - type = _libssh2_ecdsa_key_get_curve_type(ec); + type = _libssh2_ecdsa_get_curve_type(ec); + + if(is_sk) + *method_len = 34; + else + *method_len = 19; - method_buf = LIBSSH2_ALLOC(session, 19); - if(method_buf == NULL) { + method_buf = LIBSSH2_ALLOC(session, *method_len); + if(!method_buf) { return _libssh2_error(session, LIBSSH2_ERROR_ALLOC, - "out of memory"); + "out of memory"); } - if(type == LIBSSH2_EC_CURVE_NISTP256) - memcpy(method_buf, "ecdsa-sha2-nistp256", 19); + if(is_sk) + memcpy(method_buf, "sk-ecdsa-sha2-nistp256@openssh.com", *method_len); + else if(type == LIBSSH2_EC_CURVE_NISTP256) + memcpy(method_buf, "ecdsa-sha2-nistp256", *method_len); else if(type == LIBSSH2_EC_CURVE_NISTP384) - memcpy(method_buf, "ecdsa-sha2-nistp384", 19); + memcpy(method_buf, "ecdsa-sha2-nistp384", *method_len); else if(type == LIBSSH2_EC_CURVE_NISTP521) - memcpy(method_buf, "ecdsa-sha2-nistp521", 19); + memcpy(method_buf, "ecdsa-sha2-nistp521", *method_len); else { - _libssh2_debug(session, - LIBSSH2_TRACE_ERROR, - "Unsupported EC private key type"); + _libssh2_debug((session, + LIBSSH2_TRACE_ERROR, + "Unsupported EC private key type")); rc = -1; goto clean_exit; } @@ -2283,7 +2619,7 @@ gen_publickey_from_ec_evp(LIBSSH2_SESSION *session, } octal_value = malloc(octal_len); - if(octal_value == NULL) { + if(!octal_value) { rc = -1; goto clean_exit; } @@ -2291,52 +2627,56 @@ gen_publickey_from_ec_evp(LIBSSH2_SESSION *session, /* convert to octal */ if(EC_POINT_point2oct(group, public_key, POINT_CONVERSION_UNCOMPRESSED, octal_value, octal_len, bn_ctx) != octal_len) { - rc = -1; - goto clean_exit; + rc = -1; + goto clean_exit; } - /* Key form is: type_len(4) + type(19) + domain_len(4) + domain(8) + - pub_key_len(4) + pub_key(~65). */ - key_len = 4 + 19 + 4 + 8 + 4 + octal_len; + /* Key form is: type_len(4) + type(method_len) + domain_len(4) + domain(8) + + pub_key_len(4) + pub_key(~65). */ + key_len = 4 + *method_len + 4 + 8 + 4 + octal_len; key = LIBSSH2_ALLOC(session, key_len); - if(key == NULL) { + if(!key) { rc = -1; - goto clean_exit; + goto clean_exit; } /* Process key encoding. */ p = key; /* Key type */ - _libssh2_store_str(&p, (const char *)method_buf, 19); + _libssh2_store_str(&p, (const char *)method_buf, *method_len); /* Name domain */ - _libssh2_store_str(&p, (const char *)method_buf + 11, 8); + if(is_sk) { + _libssh2_store_str(&p, "nistp256", 8); + } + else { + _libssh2_store_str(&p, (const char *)method_buf + 11, 8); + } /* Public key */ _libssh2_store_str(&p, (const char *)octal_value, octal_len); *method = method_buf; - *method_len = 19; *pubkeydata = key; *pubkeydata_len = key_len; clean_exit: - if(ec != NULL) + if(ec) EC_KEY_free(ec); - if(bn_ctx != NULL) { + if(bn_ctx) { BN_CTX_free(bn_ctx); } - if(octal_value != NULL) + if(octal_value) free(octal_value); if(rc == 0) return 0; - if(method_buf != NULL) + if(method_buf) LIBSSH2_FREE(session, method_buf); return -1; @@ -2358,9 +2698,9 @@ gen_publickey_from_ecdsa_openssh_priv_data(LIBSSH2_SESSION *session, EC_KEY *ec_key = NULL; BIGNUM *bn_exponent; - _libssh2_debug(session, + _libssh2_debug((session, LIBSSH2_TRACE_AUTH, - "Computing ECDSA keys from private key data"); + "Computing ECDSA keys from private key data")); if(_libssh2_get_string(decrypted, &curve, &curvelen) || curvelen == 0) { @@ -2381,35 +2721,40 @@ gen_publickey_from_ecdsa_openssh_priv_data(LIBSSH2_SESSION *session, return -1; } - if((rc = _libssh2_ecdsa_curve_name_with_octal_new(&ec_key, point_buf, - pointlen, curve_type)) != 0) { + rc = _libssh2_ecdsa_curve_name_with_octal_new(&ec_key, + point_buf, pointlen, + curve_type); + if(rc) { + rc = -1; _libssh2_error(session, LIBSSH2_ERROR_PROTO, "ECDSA could not create key"); goto fail; } bn_exponent = BN_new(); - if(bn_exponent == NULL) { + if(!bn_exponent) { rc = -1; + _libssh2_error(session, LIBSSH2_ERROR_ALLOC, + "Unable to allocate memory for private key data"); goto fail; } - BN_bin2bn(exponent, exponentlen, bn_exponent); + BN_bin2bn(exponent, (int) exponentlen, bn_exponent); rc = (EC_KEY_set_private_key(ec_key, bn_exponent) != 1); - if(rc == 0 && ec_key != NULL && pubkeydata != NULL && method != NULL) { + if(rc == 0 && ec_key && pubkeydata && method) { EVP_PKEY *pk = EVP_PKEY_new(); EVP_PKEY_set1_EC_KEY(pk, ec_key); rc = gen_publickey_from_ec_evp(session, method, method_len, pubkeydata, pubkeydata_len, - pk); + 0, pk); if(pk) EVP_PKEY_free(pk); } - if(ec_ctx != NULL) + if(ec_ctx) *ec_ctx = ec_key; else EC_KEY_free(ec_key); @@ -2417,17 +2762,152 @@ gen_publickey_from_ecdsa_openssh_priv_data(LIBSSH2_SESSION *session, return rc; fail: + if(ec_key) + EC_KEY_free(ec_key); + + return rc; +} + +static int +gen_publickey_from_sk_ecdsa_openssh_priv_data(LIBSSH2_SESSION *session, + struct string_buf *decrypted, + unsigned char **method, + size_t *method_len, + unsigned char **pubkeydata, + size_t *pubkeydata_len, + uint8_t *flags, + const char **application, + const unsigned char **key_handle, + size_t *handle_len, + libssh2_ecdsa_ctx **ec_ctx) +{ + int rc = 0; + size_t curvelen, pointlen, key_len, app_len; + unsigned char *curve, *point_buf, *p, *key, *app; + EC_KEY *ec_key = NULL; + + _libssh2_debug((session, + LIBSSH2_TRACE_AUTH, + "Extracting ECDSA-SK public key")); + + if(_libssh2_get_string(decrypted, &curve, &curvelen) || + curvelen == 0) { + _libssh2_error(session, LIBSSH2_ERROR_PROTO, + "ECDSA no curve"); + return -1; + } + + if(_libssh2_get_string(decrypted, &point_buf, &pointlen)) { + _libssh2_error(session, LIBSSH2_ERROR_PROTO, + "ECDSA no point"); + return -1; + } + + rc = _libssh2_ecdsa_curve_name_with_octal_new(&ec_key, + point_buf, pointlen, + LIBSSH2_EC_CURVE_NISTP256); + if(rc) { + rc = -1; + _libssh2_error(session, LIBSSH2_ERROR_PROTO, + "ECDSA could not create key"); + goto fail; + } + + if(_libssh2_get_string(decrypted, &app, &app_len)) { + _libssh2_error(session, LIBSSH2_ERROR_PROTO, + "No SK application."); + goto fail; + } + + if(flags && _libssh2_get_byte(decrypted, flags)) { + _libssh2_error(session, LIBSSH2_ERROR_PROTO, + "No SK flags."); + goto fail; + } + + if(key_handle && handle_len) { + unsigned char *handle = NULL; + if(_libssh2_get_string(decrypted, &handle, handle_len)) { + _libssh2_error(session, LIBSSH2_ERROR_PROTO, + "No SK key_handle."); + goto fail; + } + + if(*handle_len > 0) { + *key_handle = LIBSSH2_ALLOC(session, *handle_len); + + if(*key_handle) { + memcpy((void *)*key_handle, handle, *handle_len); + } + } + } + + if(rc == 0 && ec_key && pubkeydata && method) { + EVP_PKEY *pk = EVP_PKEY_new(); + EVP_PKEY_set1_EC_KEY(pk, ec_key); + + rc = gen_publickey_from_ec_evp(session, method, method_len, + pubkeydata, pubkeydata_len, + 1, pk); + + if(pk) + EVP_PKEY_free(pk); + } + + if(rc == 0 && pubkeydata) { + key_len = *pubkeydata_len + app_len + 4; + key = LIBSSH2_ALLOC(session, key_len); + + if(!key) { + rc = -1; + goto fail; + } + + p = key + *pubkeydata_len; + + memcpy(key, *pubkeydata, *pubkeydata_len); + _libssh2_store_str(&p, (const char *)app, app_len); - if(ec_key != NULL) + if(application && app_len > 0) { + *application = (const char *)LIBSSH2_ALLOC(session, app_len + 1); + _libssh2_explicit_zero((void *)*application, app_len + 1); + memcpy((void *)*application, app, app_len); + } + + LIBSSH2_FREE(session, *pubkeydata); + *pubkeydata_len = key_len; + + if(pubkeydata) + *pubkeydata = key; + else if(key) + LIBSSH2_FREE(session, key); + } + + if(ec_ctx) + *ec_ctx = ec_key; + else EC_KEY_free(ec_key); - return _libssh2_error(session, - LIBSSH2_ERROR_ALLOC, - "Unable to allocate memory for private key data"); + return rc; + +fail: + if(ec_key) + EC_KEY_free(ec_key); + if(application && *application) { + LIBSSH2_FREE(session, (void *)application); + *application = NULL; + } + if(key_handle && *key_handle) { + LIBSSH2_FREE(session, (void *)key_handle); + *key_handle = NULL; + } + + return rc; } + static int _libssh2_ecdsa_new_openssh_private(libssh2_ecdsa_ctx ** ec_ctx, LIBSSH2_SESSION * session, @@ -2440,9 +2920,9 @@ _libssh2_ecdsa_new_openssh_private(libssh2_ecdsa_ctx ** ec_ctx, libssh2_curve_type type; struct string_buf *decrypted = NULL; - if(session == NULL) { + if(!session) { _libssh2_error(session, LIBSSH2_ERROR_PROTO, - "Session is required"); + "Session is required"); return -1; } @@ -2464,7 +2944,7 @@ _libssh2_ecdsa_new_openssh_private(libssh2_ecdsa_ctx ** ec_ctx, /* We have a new key file, now try and parse it using supported types */ rc = _libssh2_get_string(decrypted, &buf, NULL); - if(rc != 0 || buf == NULL) { + if(rc || !buf) { _libssh2_error(session, LIBSSH2_ERROR_PROTO, "Public key type in decrypted key data not found"); return -1; @@ -2487,6 +2967,72 @@ _libssh2_ecdsa_new_openssh_private(libssh2_ecdsa_ctx ** ec_ctx, return rc; } +static int +_libssh2_ecdsa_new_openssh_private_sk(libssh2_ecdsa_ctx ** ec_ctx, + uint8_t *flags, + const char **application, + const unsigned char **key_handle, + size_t *handle_len, + LIBSSH2_SESSION * session, + const char *filename, + unsigned const char *passphrase) +{ + FILE *fp; + int rc; + unsigned char *buf = NULL; + struct string_buf *decrypted = NULL; + + if(!session) { + _libssh2_error(session, LIBSSH2_ERROR_PROTO, + "Session is required"); + return -1; + } + + _libssh2_init_if_needed(); + + fp = fopen(filename, "r"); + if(!fp) { + _libssh2_error(session, LIBSSH2_ERROR_FILE, + "Unable to open OpenSSH ECDSA private key file"); + return -1; + } + + rc = _libssh2_openssh_pem_parse(session, passphrase, fp, &decrypted); + fclose(fp); + if(rc) { + return rc; + } + + /* We have a new key file, now try and parse it using supported types */ + rc = _libssh2_get_string(decrypted, &buf, NULL); + + if(rc || !buf) { + _libssh2_error(session, LIBSSH2_ERROR_PROTO, + "Public key type in decrypted key data not found"); + return -1; + } + + if(strcmp("sk-ecdsa-sha2-nistp256@openssh.com", (const char *)buf) == 0) { + rc = gen_publickey_from_sk_ecdsa_openssh_priv_data(session, + decrypted, + NULL, 0, + NULL, 0, + flags, + application, + key_handle, + handle_len, + ec_ctx); + } + else { + rc = -1; + } + + if(decrypted) + _libssh2_string_buf_free(session, decrypted); + + return rc; +} + int _libssh2_ecdsa_new_private(libssh2_ecdsa_ctx ** ec_ctx, LIBSSH2_SESSION * session, @@ -2495,7 +3041,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(); @@ -2510,6 +3055,40 @@ _libssh2_ecdsa_new_private(libssh2_ecdsa_ctx ** ec_ctx, return rc; } +int +_libssh2_ecdsa_new_private_sk(libssh2_ecdsa_ctx ** ec_ctx, + unsigned char *flags, + const char **application, + const unsigned char **key_handle, + size_t *handle_len, + LIBSSH2_SESSION * session, + const char *filename, + unsigned const char *passphrase) +{ + int rc; + + pem_read_bio_func read_ec = (pem_read_bio_func) &PEM_read_bio_ECPrivateKey; + + _libssh2_init_if_needed(); + + rc = read_private_key_from_file((void **) ec_ctx, read_ec, + filename, passphrase); + + if(rc) { + return _libssh2_ecdsa_new_openssh_private_sk(ec_ctx, + flags, + application, + key_handle, + handle_len, + session, + filename, + passphrase); + } + + return rc; +} + + /* * _libssh2_ecdsa_create_key * @@ -2555,16 +3134,16 @@ _libssh2_ecdsa_create_key(LIBSSH2_SESSION *session, /* convert to octal */ if(EC_POINT_point2oct(group, public_key, POINT_CONVERSION_UNCOMPRESSED, octal_value, octal_len, bn_ctx) != octal_len) { - ret = -1; - goto clean_exit; + ret = -1; + goto clean_exit; } - if(out_private_key != NULL) + if(out_private_key) *out_private_key = private_key; if(out_public_key_octal) { *out_public_key_octal = LIBSSH2_ALLOC(session, octal_len); - if(*out_public_key_octal == NULL) { + if(!*out_public_key_octal) { ret = -1; goto clean_exit; } @@ -2572,7 +3151,7 @@ _libssh2_ecdsa_create_key(LIBSSH2_SESSION *session, memcpy(*out_public_key_octal, octal_value, octal_len); } - if(out_public_key_octal_len != NULL) + if(out_public_key_octal_len) *out_public_key_octal_len = octal_len; clean_exit: @@ -2605,13 +3184,13 @@ _libssh2_ecdh_gen_k(_libssh2_bn **k, _libssh2_ec_key *private_key, if(!bn_ctx) return -1; - if(k == NULL) + if(!k) return -1; private_key_group = EC_KEY_get0_group(private_key); server_public_key_point = EC_POINT_new(private_key_group); - if(server_public_key_point == NULL) + if(!server_public_key_point) return -1; rc = EC_POINT_oct2point(private_key_group, server_public_key_point, @@ -2636,17 +3215,17 @@ _libssh2_ecdh_gen_k(_libssh2_bn **k, _libssh2_ec_key *private_key, goto clean_exit; } - BN_bin2bn(secret, secret_len, *k); + BN_bin2bn(secret, (int) secret_len, *k); clean_exit: - if(server_public_key_point != NULL) + if(server_public_key_point) EC_POINT_free(server_public_key_point); - if(bn_ctx != NULL) + if(bn_ctx) BN_CTX_free(bn_ctx); - if(secret != NULL) + if(secret) free(secret); return ret; @@ -2667,8 +3246,8 @@ _libssh2_ed25519_sign(libssh2_ed25519_ctx *ctx, LIBSSH2_SESSION *session, size_t sig_len = 0; unsigned char *sig = NULL; - if(md_ctx != NULL) { - if(EVP_DigestSignInit(md_ctx, NULL, NULL, NULL, ctx->private_key) != 1) + if(md_ctx) { + 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; @@ -2677,7 +3256,7 @@ _libssh2_ed25519_sign(libssh2_ed25519_ctx *ctx, LIBSSH2_SESSION *session, goto clean_exit; sig = LIBSSH2_CALLOC(session, sig_len); - if(sig == NULL) + if(!sig) goto clean_exit; rc = EVP_DigestSign(md_ctx, sig, &sig_len, message, message_len); @@ -2698,7 +3277,7 @@ clean_exit: if(md_ctx) EVP_MD_CTX_free(md_ctx); - return (rc == 1 ? 0 : -1); + return (rc == 1) ? 0 : -1; } int @@ -2713,11 +3292,11 @@ _libssh2_curve25519_gen_k(_libssh2_bn **k, BN_CTX *bn_ctx = NULL; size_t out_len = 0; - if(k == NULL || *k == NULL) + if(!k || !*k) return -1; bn_ctx = BN_CTX_new(); - if(bn_ctx == NULL) + if(!bn_ctx) return -1; peer_key = EVP_PKEY_new_raw_public_key(EVP_PKEY_X25519, NULL, @@ -2728,27 +3307,33 @@ _libssh2_curve25519_gen_k(_libssh2_bn **k, private_key, LIBSSH2_ED25519_KEY_LEN); - if(peer_key == NULL || server_key == NULL) { - goto cleanExit; + if(!peer_key || !server_key) { + goto clean_exit; } server_key_ctx = EVP_PKEY_CTX_new(server_key, NULL); - if(server_key_ctx == NULL) { - goto cleanExit; + if(!server_key_ctx) { + goto clean_exit; } rc = EVP_PKEY_derive_init(server_key_ctx); - if(rc <= 0) goto cleanExit; + if(rc <= 0) { + goto clean_exit; + } rc = EVP_PKEY_derive_set_peer(server_key_ctx, peer_key); - if(rc <= 0) goto cleanExit; + if(rc <= 0) { + goto clean_exit; + } rc = EVP_PKEY_derive(server_key_ctx, NULL, &out_len); - if(rc <= 0) goto cleanExit; + if(rc <= 0) { + goto clean_exit; + } if(out_len != LIBSSH2_ED25519_KEY_LEN) { rc = -1; - goto cleanExit; + goto clean_exit; } rc = EVP_PKEY_derive(server_key_ctx, out_shared_key, &out_len); @@ -2760,7 +3345,7 @@ _libssh2_curve25519_gen_k(_libssh2_bn **k, rc = -1; } -cleanExit: +clean_exit: if(server_key_ctx) EVP_PKEY_CTX_free(server_key_ctx); @@ -2768,7 +3353,7 @@ cleanExit: EVP_PKEY_free(peer_key); if(server_key) EVP_PKEY_free(server_key); - if(bn_ctx != NULL) + if(bn_ctx) BN_CTX_free(bn_ctx); return (rc == 1) ? 0 : -1; @@ -2782,16 +3367,16 @@ _libssh2_ed25519_verify(libssh2_ed25519_ctx *ctx, const uint8_t *s, int ret = -1; EVP_MD_CTX *md_ctx = EVP_MD_CTX_new(); - if(NULL == md_ctx) + if(!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; ret = EVP_DigestVerify(md_ctx, s, s_len, m, m_len); - clean_exit: +clean_exit: EVP_MD_CTX_free(md_ctx); @@ -2814,7 +3399,7 @@ _libssh2_pub_priv_openssh_keyfile(LIBSSH2_SESSION *session, struct string_buf *decrypted = NULL; int rc = 0; - if(session == NULL) { + if(!session) { _libssh2_error(session, LIBSSH2_ERROR_PROTO, "Session is required"); return -1; @@ -2841,7 +3426,7 @@ _libssh2_pub_priv_openssh_keyfile(LIBSSH2_SESSION *session, /* We have a new key file, now try and parse it using supported types */ rc = _libssh2_get_string(decrypted, &buf, NULL); - if(rc != 0 || buf == NULL) { + if(rc || !buf) { _libssh2_error(session, LIBSSH2_ERROR_PROTO, "Public key type in decrypted key data not found"); return -1; @@ -2849,6 +3434,12 @@ _libssh2_pub_priv_openssh_keyfile(LIBSSH2_SESSION *session, rc = -1; + /* Avoid unused variable warnings when all branches below are disabled */ + (void)method; + (void)method_len; + (void)pubkeydata; + (void)pubkeydata_len; + #if LIBSSH2_ED25519 if(strcmp("ssh-ed25519", (const char *)buf) == 0) { rc = gen_publickey_from_ed25519_openssh_priv_data(session, decrypted, @@ -2895,7 +3486,7 @@ _libssh2_pub_priv_openssh_keyfile(LIBSSH2_SESSION *session, if(decrypted) _libssh2_string_buf_free(session, decrypted); - if(rc != 0) { + if(rc) { _libssh2_error(session, LIBSSH2_ERROR_FILE, "Unsupported OpenSSH key type"); } @@ -2918,13 +3509,13 @@ _libssh2_pub_priv_keyfile(LIBSSH2_SESSION *session, int pktype; int rc; - _libssh2_debug(session, + _libssh2_debug((session, LIBSSH2_TRACE_AUTH, "Computing public key from private key file: %s", - privatekey); + privatekey)); bp = BIO_new_file(privatekey, "r"); - if(bp == NULL) { + if(!bp) { return _libssh2_error(session, LIBSSH2_ERROR_FILE, "Unable to extract public key from private key " @@ -2935,7 +3526,7 @@ _libssh2_pub_priv_keyfile(LIBSSH2_SESSION *session, pk = PEM_read_bio_PrivateKey(bp, NULL, NULL, (void *)passphrase); BIO_free(bp); - if(pk == NULL) { + if(!pk) { /* Try OpenSSH format */ rc = _libssh2_pub_priv_openssh_keyfile(session, @@ -2943,7 +3534,7 @@ _libssh2_pub_priv_keyfile(LIBSSH2_SESSION *session, method_len, pubkeydata, pubkeydata_len, privatekey, passphrase); - if(rc != 0) { + if(rc) { return _libssh2_error(session, LIBSSH2_ERROR_FILE, "Unable to extract public key " @@ -2962,26 +3553,31 @@ _libssh2_pub_priv_keyfile(LIBSSH2_SESSION *session, #endif switch(pktype) { - case EVP_PKEY_RSA : +#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 */ +#if LIBSSH2_RSA + case EVP_PKEY_RSA: st = gen_publickey_from_rsa_evp( session, method, method_len, pubkeydata, pubkeydata_len, pk); break; - +#endif /* LIBSSH2_RSA */ #if LIBSSH2_DSA - case EVP_PKEY_DSA : + case EVP_PKEY_DSA: st = gen_publickey_from_dsa_evp( session, method, method_len, pubkeydata, pubkeydata_len, pk); break; -#endif /* LIBSSH_DSA */ - +#endif /* LIBSSH2_DSA */ #if LIBSSH2_ECDSA - case EVP_PKEY_EC : + case EVP_PKEY_EC: st = gen_publickey_from_ec_evp( - session, method, method_len, pubkeydata, pubkeydata_len, pk); + session, method, method_len, pubkeydata, pubkeydata_len, 0, pk); break; -#endif - - default : +#endif /* LIBSSH2_ECDSA */ + default: st = _libssh2_error(session, LIBSSH2_ERROR_FILE, "Unable to extract public key " @@ -3010,20 +3606,16 @@ _libssh2_pub_priv_openssh_keyfilememory(LIBSSH2_SESSION *session, unsigned char *buf = NULL; struct string_buf *decrypted = NULL; - if(key_ctx != NULL) + if(key_ctx) *key_ctx = NULL; - if(session == NULL) { - _libssh2_error(session, LIBSSH2_ERROR_PROTO, - "Session is required"); - return -1; - } + if(!session) + return _libssh2_error(session, LIBSSH2_ERROR_PROTO, + "Session is required"); - if(key_type != NULL && (strlen(key_type) > 11 || strlen(key_type) < 7)) { - _libssh2_error(session, LIBSSH2_ERROR_PROTO, - "type is invalid"); - return -1; - } + if(key_type && (strlen(key_type) > 11 || strlen(key_type) < 7)) + return _libssh2_error(session, LIBSSH2_ERROR_PROTO, + "type is invalid"); _libssh2_init_if_needed(); @@ -3031,24 +3623,28 @@ _libssh2_pub_priv_openssh_keyfilememory(LIBSSH2_SESSION *session, privatekeydata, privatekeydata_len, &decrypted); - if(rc) { + if(rc) return rc; - } - /* We have a new key file, now try and parse it using supported types */ - rc = _libssh2_get_string(decrypted, &buf, NULL); + /* We have a new key file, now try and parse it using supported types */ + rc = _libssh2_get_string(decrypted, &buf, NULL); - if(rc != 0 || buf == NULL) { - _libssh2_error(session, LIBSSH2_ERROR_PROTO, - "Public key type in decrypted key data not found"); - return -1; - } + if(rc || !buf) + return _libssh2_error(session, LIBSSH2_ERROR_PROTO, + "Public key type in decrypted " + "key data not found"); + + rc = LIBSSH2_ERROR_FILE; - rc = -1; + /* Avoid unused variable warnings when all branches below are disabled */ + (void)method; + (void)method_len; + (void)pubkeydata; + (void)pubkeydata_len; #if LIBSSH2_ED25519 if(strcmp("ssh-ed25519", (const char *)buf) == 0) { - if(key_type == NULL || strcmp("ssh-ed25519", key_type) == 0) { + if(!key_type || strcmp("ssh-ed25519", key_type) == 0) { rc = gen_publickey_from_ed25519_openssh_priv_data(session, decrypted, method, @@ -3057,47 +3653,187 @@ _libssh2_pub_priv_openssh_keyfilememory(LIBSSH2_SESSION *session, pubkeydata_len, (libssh2_ed25519_ctx**)key_ctx); } - } + } + + if(strcmp("sk-ssh-ed25519@openssh.com", (const char *)buf) == 0) { + if(!key_type || + strcmp("sk-ssh-ed25519@openssh.com", key_type) == 0) { + rc = gen_publickey_from_sk_ed25519_openssh_priv_data(session, + decrypted, + method, + method_len, + pubkeydata, + pubkeydata_len, + NULL, + NULL, + NULL, + NULL, + (libssh2_ed25519_ctx**)key_ctx); + } + } #endif #if LIBSSH2_RSA if(strcmp("ssh-rsa", (const char *)buf) == 0) { - if(key_type == NULL || strcmp("ssh-rsa", key_type) == 0) { + if(!key_type || strcmp("ssh-rsa", key_type) == 0) { rc = gen_publickey_from_rsa_openssh_priv_data(session, decrypted, method, method_len, pubkeydata, pubkeydata_len, - (libssh2_rsa_ctx**)key_ctx); + (libssh2_rsa_ctx**)key_ctx); } - } + } #endif #if LIBSSH2_DSA if(strcmp("ssh-dss", (const char *)buf) == 0) { - if(key_type == NULL || strcmp("ssh-dss", key_type) == 0) { + if(!key_type || strcmp("ssh-dss", key_type) == 0) { rc = gen_publickey_from_dsa_openssh_priv_data(session, decrypted, - method, method_len, + method, method_len, pubkeydata, pubkeydata_len, - (libssh2_dsa_ctx**)key_ctx); + (libssh2_dsa_ctx**)key_ctx); } - } + } #endif #if LIBSSH2_ECDSA { libssh2_curve_type type; - if(_libssh2_ecdsa_curve_type_from_name((const char *)buf, &type) == 0) { - if(key_type == NULL || strcmp("ssh-ecdsa", key_type) == 0) { - rc = gen_publickey_from_ecdsa_openssh_priv_data(session, type, - decrypted, + if(strcmp("sk-ecdsa-sha2-nistp256@openssh.com", (const char *)buf) == 0) { + rc = gen_publickey_from_sk_ecdsa_openssh_priv_data(session, decrypted, method, method_len, pubkeydata, pubkeydata_len, - (libssh2_ecdsa_ctx**)key_ctx); + NULL, + NULL, NULL, + NULL, + (libssh2_ecdsa_ctx**)key_ctx); + } + else if(_libssh2_ecdsa_curve_type_from_name((const char *)buf, &type) + == 0) { + if(!key_type || strcmp("ssh-ecdsa", key_type) == 0) { + rc = gen_publickey_from_ecdsa_openssh_priv_data(session, type, + decrypted, + method, method_len, + pubkeydata, + pubkeydata_len, + (libssh2_ecdsa_ctx**)key_ctx); } } } #endif + if(rc == LIBSSH2_ERROR_FILE) + rc = _libssh2_error(session, LIBSSH2_ERROR_FILE, + "Unable to extract public key from private key file: " + "invalid/unrecognized private key file format"); + + if(decrypted) + _libssh2_string_buf_free(session, decrypted); + + return rc; +} + +static int +_libssh2_sk_pub_openssh_keyfilememory(LIBSSH2_SESSION *session, + void **key_ctx, + const char *key_type, + unsigned char **method, + size_t *method_len, + unsigned char **pubkeydata, + size_t *pubkeydata_len, + int *algorithm, + unsigned char *flags, + const char **application, + const unsigned char **key_handle, + size_t *handle_len, + const char *privatekeydata, + size_t privatekeydata_len, + unsigned const char *passphrase) +{ + int rc; + unsigned char *buf = NULL; + struct string_buf *decrypted = NULL; + + if(key_ctx) + *key_ctx = NULL; + + if(!session) + return _libssh2_error(session, LIBSSH2_ERROR_PROTO, + "Session is required"); + + if(key_type && strlen(key_type) < 7) + return _libssh2_error(session, LIBSSH2_ERROR_PROTO, + "type is invalid"); + + _libssh2_init_if_needed(); + + rc = _libssh2_openssh_pem_parse_memory(session, passphrase, + privatekeydata, + privatekeydata_len, &decrypted); + + if(rc) + return rc; + + /* We have a new key file, now try and parse it using supported types */ + rc = _libssh2_get_string(decrypted, &buf, NULL); + + if(rc || !buf) + return _libssh2_error(session, LIBSSH2_ERROR_PROTO, + "Public key type in decrypted " + "key data not found"); + + rc = LIBSSH2_ERROR_FILE; + + /* Avoid unused variable warnings when all branches below are disabled */ + (void)method; + (void)method_len; + (void)pubkeydata; + (void)pubkeydata_len; + (void)algorithm; + (void)flags; + (void)application; + (void)key_handle; + (void)handle_len; + +#if LIBSSH2_ED25519 + if(strcmp("sk-ssh-ed25519@openssh.com", (const char *)buf) == 0) { + *algorithm = LIBSSH2_HOSTKEY_TYPE_ED25519; + if(!key_type || + strcmp("sk-ssh-ed25519@openssh.com", key_type) == 0) { + rc = gen_publickey_from_sk_ed25519_openssh_priv_data(session, + decrypted, + method, + method_len, + pubkeydata, + pubkeydata_len, + flags, + application, + key_handle, + handle_len, + (libssh2_ed25519_ctx**)key_ctx); + } + } +#endif +#if LIBSSH2_ECDSA + if(strcmp("sk-ecdsa-sha2-nistp256@openssh.com", (const char *)buf) == 0) { + *algorithm = LIBSSH2_HOSTKEY_TYPE_ECDSA_256; + rc = gen_publickey_from_sk_ecdsa_openssh_priv_data(session, decrypted, + method, method_len, + pubkeydata, + pubkeydata_len, + flags, + application, + key_handle, + handle_len, + (libssh2_ecdsa_ctx**)key_ctx); + } +#endif + + if(rc == LIBSSH2_ERROR_FILE) + rc = _libssh2_error(session, LIBSSH2_ERROR_FILE, + "Unable to extract public key from private key file: " + "invalid/unrecognized private key file format"); + if(decrypted) _libssh2_string_buf_free(session, decrypted); @@ -3117,6 +3853,10 @@ read_openssh_private_key_from_memory(void **key_ctx, LIBSSH2_SESSION *session, passphrase); } +#if OPENSSL_VERSION_NUMBER >= 0x30000000L +#define HAVE_SSLERROR_BAD_DECRYPT +#endif + int _libssh2_pub_priv_keyfilememory(LIBSSH2_SESSION *session, unsigned char **method, @@ -3131,21 +3871,31 @@ _libssh2_pub_priv_keyfilememory(LIBSSH2_SESSION *session, BIO* bp; EVP_PKEY* pk; int pktype; +#ifdef HAVE_SSLERROR_BAD_DECRYPT + unsigned long sslError; +#endif - _libssh2_debug(session, + _libssh2_debug((session, LIBSSH2_TRACE_AUTH, - "Computing public key from private key."); - - bp = BIO_new_mem_buf((char *)privatekeydata, privatekeydata_len); - if(!bp) { - return -1; - } + "Computing public key from private key.")); +#if OPENSSL_VERSION_NUMBER >= 0x1000200fL + bp = BIO_new_mem_buf(privatekeydata, (int)privatekeydata_len); +#else + bp = BIO_new_mem_buf((char *)privatekeydata, (int)privatekeydata_len); +#endif + if(!bp) + return _libssh2_error(session, LIBSSH2_ERROR_ALLOC, + "Unable to allocate memory when" + "computing public key"); BIO_reset(bp); pk = PEM_read_bio_PrivateKey(bp, NULL, NULL, (void *)passphrase); +#ifdef HAVE_SSLERROR_BAD_DECRYPT + sslError = ERR_get_error(); +#endif BIO_free(bp); - if(pk == NULL) { + if(!pk) { /* Try OpenSSH format */ st = _libssh2_pub_priv_openssh_keyfilememory(session, NULL, NULL, method, @@ -3154,17 +3904,23 @@ _libssh2_pub_priv_keyfilememory(LIBSSH2_SESSION *session, pubkeydata_len, privatekeydata, privatekeydata_len, - (unsigned const char *)passphrase); - if(st != 0) { - return _libssh2_error(session, - LIBSSH2_ERROR_FILE, - "Unable to extract public key " - "from private key file: " - "Wrong passphrase or invalid/unrecognized " - "private key file format"); - } - - return 0; + (unsigned const char *)passphrase); + if(st == 0) + return 0; + +#ifdef HAVE_SSLERROR_BAD_DECRYPT + if((ERR_GET_LIB(sslError) == ERR_LIB_PEM && + ERR_GET_REASON(sslError) == PEM_R_BAD_DECRYPT) || + (ERR_GET_LIB(sslError) == ERR_LIB_PROV && + ERR_GET_REASON(sslError) == EVP_R_BAD_DECRYPT)) + return _libssh2_error(session, LIBSSH2_ERROR_KEYFILE_AUTH_FAILED, + "Wrong passphrase for private key"); +#endif + return _libssh2_error(session, + LIBSSH2_ERROR_FILE, + "Unable to extract public key " + "from private key file: " + "Unsupported private key file format"); } #ifdef HAVE_OPAQUE_STRUCTS @@ -3174,23 +3930,32 @@ _libssh2_pub_priv_keyfilememory(LIBSSH2_SESSION *session, #endif switch(pktype) { - case EVP_PKEY_RSA : +#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 */ +#if LIBSSH2_RSA + case EVP_PKEY_RSA: st = gen_publickey_from_rsa_evp(session, method, method_len, pubkeydata, pubkeydata_len, pk); break; +#endif /* LIBSSH2_RSA */ #if LIBSSH2_DSA - case EVP_PKEY_DSA : + case EVP_PKEY_DSA: st = gen_publickey_from_dsa_evp(session, method, method_len, pubkeydata, pubkeydata_len, pk); break; -#endif /* LIBSSH_DSA */ +#endif /* LIBSSH2_DSA */ #if LIBSSH2_ECDSA - case EVP_PKEY_EC : + case EVP_PKEY_EC: st = gen_publickey_from_ec_evp(session, method, method_len, - pubkeydata, pubkeydata_len, pk); + pubkeydata, pubkeydata_len, + 0, pk); break; #endif /* LIBSSH2_ECDSA */ - default : + default: st = _libssh2_error(session, LIBSSH2_ERROR_FILE, "Unable to extract public key " @@ -3203,6 +3968,62 @@ _libssh2_pub_priv_keyfilememory(LIBSSH2_SESSION *session, return st; } +int +_libssh2_sk_pub_keyfilememory(LIBSSH2_SESSION *session, + unsigned char **method, + size_t *method_len, + unsigned char **pubkeydata, + size_t *pubkeydata_len, + int *algorithm, + unsigned char *flags, + const char **application, + const unsigned char **key_handle, + size_t *handle_len, + const char *privatekeydata, + size_t privatekeydata_len, + const char *passphrase) +{ + int st = -1; + BIO* bp; + EVP_PKEY* pk; + + _libssh2_debug((session, + LIBSSH2_TRACE_AUTH, + "Computing public key from private key.")); + +#if OPENSSL_VERSION_NUMBER >= 0x1000200fL + bp = BIO_new_mem_buf(privatekeydata, (int)privatekeydata_len); +#else + bp = BIO_new_mem_buf((char *)privatekeydata, (int)privatekeydata_len); +#endif + if(!bp) + return _libssh2_error(session, LIBSSH2_ERROR_ALLOC, + "Unable to allocate memory when" + "computing public key"); + BIO_reset(bp); + pk = PEM_read_bio_PrivateKey(bp, NULL, NULL, (void *)passphrase); + BIO_free(bp); + + if(!pk) { + /* Try OpenSSH format */ + st = _libssh2_sk_pub_openssh_keyfilememory(session, NULL, NULL, + method, + method_len, + pubkeydata, + pubkeydata_len, + algorithm, + flags, + application, + key_handle, + handle_len, + privatekeydata, + privatekeydata_len, + (unsigned const char *)passphrase); + } + + return st; +} + void _libssh2_dh_init(_libssh2_dh_ctx *dhctx) { @@ -3237,4 +4058,27 @@ _libssh2_dh_dtor(_libssh2_dh_ctx *dhctx) *dhctx = NULL; } -#endif /* LIBSSH2_OPENSSL */ +/* _libssh2_supported_key_sign_algorithms + * + * Return supported key hash algo upgrades, see crypto.h + * + */ + +const char * +_libssh2_supported_key_sign_algorithms(LIBSSH2_SESSION *session, + unsigned char *key_method, + size_t key_method_len) +{ + (void)session; + +#if LIBSSH2_RSA_SHA2 + if(key_method_len == 7 && + memcmp(key_method, "ssh-rsa", key_method_len) == 0) { + return "rsa-sha2-512,rsa-sha2-256,ssh-rsa"; + } +#endif + + return NULL; +} + +#endif /* LIBSSH2_CRYPTO_C */ |