diff options
Diffstat (limited to 'libs/libssh2/src/openssl.c')
-rw-r--r-- | libs/libssh2/src/openssl.c | 2620 |
1 files changed, 2334 insertions, 286 deletions
diff --git a/libs/libssh2/src/openssl.c b/libs/libssh2/src/openssl.c index 4f63ef92bc..04d5ec2ffd 100644 --- a/libs/libssh2/src/openssl.c +++ b/libs/libssh2/src/openssl.c @@ -43,12 +43,38 @@ #ifdef LIBSSH2_OPENSSL /* compile only if we build with openssl */ #include <string.h> +#include "misc.h" #ifndef EVP_MAX_BLOCK_LENGTH #define EVP_MAX_BLOCK_LENGTH 32 #endif int +read_openssh_private_key_from_memory(void **key_ctx, LIBSSH2_SESSION *session, + const char *key_type, + const char *filedata, + size_t filedata_len, + unsigned const char *passphrase); + +static unsigned char * +write_bn(unsigned char *buf, const BIGNUM *bn, int bn_bytes) +{ + unsigned char *p = buf; + + /* Left space for bn size which will be written below. */ + p += 4; + + *p = 0; + BN_bn2bin(bn, p + 1); + if(!(*(p + 1) & 0x80)) { + memmove(p, p + 1, --bn_bytes); + } + _libssh2_htonu32(p - 4, bn_bytes); /* Post write bn size. */ + + return p + bn_bytes; +} + +int _libssh2_rsa_new(libssh2_rsa_ctx ** rsa, const unsigned char *edata, unsigned long elen, @@ -81,7 +107,7 @@ _libssh2_rsa_new(libssh2_rsa_ctx ** rsa, n = BN_new(); BN_bin2bn(ndata, nlen, n); - if (ddata) { + if(ddata) { d = BN_new(); BN_bin2bn(ddata, dlen, d); @@ -107,6 +133,7 @@ _libssh2_rsa_new(libssh2_rsa_ctx ** rsa, #else (*rsa)->e = e; (*rsa)->n = n; + (*rsa)->d = d; #endif #ifdef HAVE_OPAQUE_STRUCTS @@ -135,7 +162,7 @@ _libssh2_rsa_sha1_verify(libssh2_rsa_ctx * rsactx, unsigned char hash[SHA_DIGEST_LENGTH]; int ret; - if (_libssh2_sha1(m, m_len, hash)) + if(_libssh2_sha1(m, m_len, hash)) return -1; /* failure */ ret = RSA_verify(NID_sha1, hash, SHA_DIGEST_LENGTH, (unsigned char *) sig, sig_len, rsactx); @@ -173,7 +200,7 @@ _libssh2_dsa_new(libssh2_dsa_ctx ** dsactx, pub_key = BN_new(); BN_bin2bn(y, y_len, pub_key); - if (x_len) { + if(x_len) { priv_key = BN_new(); BN_bin2bn(x, x_len, priv_key); } @@ -220,7 +247,7 @@ _libssh2_dsa_sha1_verify(libssh2_dsa_ctx * dsactx, dsasig->r = r; dsasig->s = s; #endif - if (!_libssh2_sha1(m, m_len, hash)) + if(!_libssh2_sha1(m, m_len, hash)) /* _libssh2_sha1() succeeded */ ret = DSA_do_verify(hash, SHA_DIGEST_LENGTH, dsasig, dsactx); @@ -230,6 +257,147 @@ _libssh2_dsa_sha1_verify(libssh2_dsa_ctx * dsactx, } #endif /* LIBSSH_DSA */ +#if LIBSSH2_ECDSA + +/* _libssh2_ecdsa_key_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) +{ + const EC_GROUP *group = EC_KEY_get0_group(key); + return EC_GROUP_get_curve_name(group); +} + +/* _libssh2_ecdsa_curve_type_from_name + * + * returns 0 for success, key curve type that maps to libssh2_curve_type + * + */ + +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) + return -1; + + if(strcmp(name, "ecdsa-sha2-nistp256") == 0) + type = LIBSSH2_EC_CURVE_NISTP256; + else if(strcmp(name, "ecdsa-sha2-nistp384") == 0) + type = LIBSSH2_EC_CURVE_NISTP384; + else if(strcmp(name, "ecdsa-sha2-nistp521") == 0) + type = LIBSSH2_EC_CURVE_NISTP521; + else { + ret = -1; + } + + if(ret == 0 && out_type) { + *out_type = type; + } + + return ret; +} + +/* _libssh2_ecdsa_curve_name_with_octal_new + * + * Creates a new public key given an octal string, length and type + * + */ + +int +_libssh2_ecdsa_curve_name_with_octal_new(libssh2_ecdsa_ctx ** ec_ctx, + const unsigned char *k, + size_t k_len, libssh2_curve_type curve) +{ + + int ret = 0; + const EC_GROUP *ec_group = NULL; + EC_KEY *ec_key = EC_KEY_new_by_curve_name(curve); + EC_POINT *point = NULL; + + if(ec_key) { + ec_group = EC_KEY_get0_group(ec_key); + point = EC_POINT_new(ec_group); + ret = EC_POINT_oct2point(ec_group, point, k, k_len, NULL); + ret = EC_KEY_set_public_key(ec_key, point); + + if(point != NULL) + EC_POINT_free(point); + + if(ec_ctx != NULL) + *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); \ + \ +} + +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) +{ + int ret = 0; + EC_KEY *ec_key = (EC_KEY*)ctx; + libssh2_curve_type type = _libssh2_ecdsa_key_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); + 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); + ecdsa_sig_.s = BN_new(); + BN_bin2bn(s, s_len, ecdsa_sig_.s); +#endif + + if(type == LIBSSH2_EC_CURVE_NISTP256) { + LIBSSH2_ECDSA_VERIFY(256); + } + else if(type == LIBSSH2_EC_CURVE_NISTP384) { + LIBSSH2_ECDSA_VERIFY(384); + } + else if(type == LIBSSH2_EC_CURVE_NISTP521) { + LIBSSH2_ECDSA_VERIFY(512); + } + +#ifdef HAVE_OPAQUE_STRUCTS + if(ecdsa_sig) + ECDSA_SIG_free(ecdsa_sig); +#else + BN_clear_free(ecdsa_sig_.s); + BN_clear_free(ecdsa_sig_.r); +#endif + + return (ret == 1) ? 0 : -1; +} + +#endif /* LIBSSH2_ECDSA */ + int _libssh2_cipher_init(_libssh2_cipher_ctx * h, _libssh2_cipher_type(algo), @@ -259,7 +427,7 @@ _libssh2_cipher_crypt(_libssh2_cipher_ctx * ctx, #else ret = EVP_Cipher(ctx, buf, block, blocksize); #endif - if (ret == 1) { + if(ret == 1) { memcpy(block, buf, blocksize); } return ret == 1 ? 0 : 1; @@ -289,7 +457,7 @@ aes_ctr_init(EVP_CIPHER_CTX *ctx, const unsigned char *key, const EVP_CIPHER *aes_cipher; (void) enc; - switch (EVP_CIPHER_CTX_key_length(ctx)) { + switch(EVP_CIPHER_CTX_key_length(ctx)) { case 16: aes_cipher = EVP_aes_128_ecb(); break; @@ -304,7 +472,7 @@ aes_ctr_init(EVP_CIPHER_CTX *ctx, const unsigned char *key, } c = malloc(sizeof(*c)); - if (c == NULL) + if(c == NULL) return 0; #ifdef HAVE_OPAQUE_STRUCTS @@ -312,12 +480,12 @@ aes_ctr_init(EVP_CIPHER_CTX *ctx, const unsigned char *key, #else c->aes_ctx = malloc(sizeof(EVP_CIPHER_CTX)); #endif - if (c->aes_ctx == NULL) { + if(c->aes_ctx == NULL) { free(c); return 0; } - if (EVP_EncryptInit(c->aes_ctx, aes_cipher, key, NULL) != 1) { + if(EVP_EncryptInit(c->aes_ctx, aes_cipher, key, NULL) != 1) { #ifdef HAVE_OPAQUE_STRUCTS EVP_CIPHER_CTX_free(c->aes_ctx); #else @@ -343,13 +511,12 @@ aes_ctr_do_cipher(EVP_CIPHER_CTX *ctx, unsigned char *out, { aes_ctr_ctx *c = EVP_CIPHER_CTX_get_app_data(ctx); unsigned char b1[AES_BLOCK_SIZE]; - size_t i = 0; int outlen = 0; - if (inl != 16) /* libssh2 only ever encrypt one block */ + if(inl != 16) /* libssh2 only ever encrypt one block */ return 0; - if (c == NULL) { + if(c == NULL) { return 0; } @@ -360,19 +527,13 @@ aes_ctr_do_cipher(EVP_CIPHER_CTX *ctx, unsigned char *out, the ciphertext block C1. The counter X is then incremented */ - if (EVP_EncryptUpdate(c->aes_ctx, b1, &outlen, c->ctr, AES_BLOCK_SIZE) != 1) { + if(EVP_EncryptUpdate(c->aes_ctx, b1, &outlen, + c->ctr, AES_BLOCK_SIZE) != 1) { return 0; } - for (i = 0; i < 16; i++) - *out++ = *in++ ^ b1[i]; - - i = 15; - while (c->ctr[i]++ == 0xFF) { - if (i == 0) - break; - i--; - } + _libssh2_xor_data(out, in, b1, AES_BLOCK_SIZE); + _libssh2_aes_ctr_increment(c->ctr, AES_BLOCK_SIZE); return 1; } @@ -382,11 +543,11 @@ aes_ctr_cleanup(EVP_CIPHER_CTX *ctx) /* cleanup ctx */ { aes_ctr_ctx *c = EVP_CIPHER_CTX_get_app_data(ctx); - if (c == NULL) { + if(c == NULL) { return 1; } - if (c->aes_ctx != NULL) { + if(c->aes_ctx != NULL) { #ifdef HAVE_OPAQUE_STRUCTS EVP_CIPHER_CTX_free(c->aes_ctx); #else @@ -401,27 +562,27 @@ aes_ctr_cleanup(EVP_CIPHER_CTX *ctx) /* cleanup ctx */ } static const EVP_CIPHER * -make_ctr_evp (size_t keylen, EVP_CIPHER *aes_ctr_cipher, int type) +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); + *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; + (*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; + return *aes_ctr_cipher; } const EVP_CIPHER * @@ -429,12 +590,13 @@ _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; + return !aes_ctr_cipher ? + make_ctr_evp(16, &aes_ctr_cipher, NID_aes_128_ctr) : aes_ctr_cipher; #else static EVP_CIPHER aes_ctr_cipher; - return !aes_ctr_cipher.key_len? - make_ctr_evp (16, &aes_ctr_cipher, 0) : &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; #endif } @@ -443,12 +605,13 @@ _libssh2_EVP_aes_192_ctr(void) { #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; + return !aes_ctr_cipher ? + make_ctr_evp(24, &aes_ctr_cipher, NID_aes_192_ctr) : aes_ctr_cipher; #else static EVP_CIPHER aes_ctr_cipher; - return !aes_ctr_cipher.key_len? - make_ctr_evp (24, &aes_ctr_cipher, 0) : &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; #endif } @@ -457,25 +620,70 @@ _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; + 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; - return !aes_ctr_cipher.key_len? - make_ctr_evp (32, &aes_ctr_cipher, 0) : &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 } -void _libssh2_init_aes_ctr(void) +#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) { - _libssh2_EVP_aes_128_ctr(); - _libssh2_EVP_aes_192_ctr(); - _libssh2_EVP_aes_256_ctr(); +#if OPENSSL_VERSION_NUMBER >= 0x10100000L && \ + !defined(LIBRESSL_VERSION_NUMBER) +#ifndef OPENSSL_NO_ENGINE + ENGINE_load_builtin_engines(); + ENGINE_register_all_complete(); +#endif +#else + OpenSSL_add_all_algorithms(); + OpenSSL_add_all_ciphers(); + OpenSSL_add_all_digests(); +#ifndef OPENSSL_NO_ENGINE + ENGINE_load_builtin_engines(); + 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 } -#else -void _libssh2_init_aes_ctr(void) {} -#endif /* LIBSSH2_AES_CTR */ +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 +} /* TODO: Optionally call a passphrase callback specified by the * calling program @@ -486,7 +694,7 @@ passphrase_cb(char *buf, int size, int rwflag, char *passphrase) int passphrase_len = strlen(passphrase); (void) rwflag; - if (passphrase_len > (size - 1)) { + if(passphrase_len > (size - 1)) { passphrase_len = size - 1; } memcpy(buf, passphrase, passphrase_len); @@ -496,12 +704,12 @@ passphrase_cb(char *buf, int size, int rwflag, char *passphrase) } typedef void * (*pem_read_bio_func)(BIO *, void **, pem_password_cb *, - void * u); + void *u); static int -read_private_key_from_memory(void ** key_ctx, +read_private_key_from_memory(void **key_ctx, pem_read_bio_func read_private_key, - const char * filedata, + const char *filedata, size_t filedata_len, unsigned const char *passphrase) { @@ -510,7 +718,7 @@ read_private_key_from_memory(void ** key_ctx, *key_ctx = NULL; bp = BIO_new_mem_buf((char *)filedata, filedata_len); - if (!bp) { + if(!bp) { return -1; } *key_ctx = read_private_key(bp, NULL, (pem_password_cb *) passphrase_cb, @@ -520,10 +728,12 @@ read_private_key_from_memory(void ** key_ctx, return (*key_ctx) ? 0 : -1; } + + static int -read_private_key_from_file(void ** key_ctx, +read_private_key_from_file(void **key_ctx, pem_read_bio_func read_private_key, - const char * filename, + const char *filename, unsigned const char *passphrase) { BIO * bp; @@ -531,7 +741,7 @@ read_private_key_from_file(void ** key_ctx, *key_ctx = NULL; bp = BIO_new_file(filename, "r"); - if (!bp) { + if(!bp) { return -1; } @@ -548,14 +758,350 @@ _libssh2_rsa_new_private_frommemory(libssh2_rsa_ctx ** rsa, const char *filedata, size_t filedata_len, unsigned const char *passphrase) { + int rc; + pem_read_bio_func read_rsa = (pem_read_bio_func) &PEM_read_bio_RSAPrivateKey; (void) session; _libssh2_init_if_needed(); - return read_private_key_from_memory((void **) rsa, read_rsa, - filedata, filedata_len, passphrase); + rc = read_private_key_from_memory((void **) rsa, read_rsa, + filedata, filedata_len, passphrase); + + if(rc) { + rc = read_openssh_private_key_from_memory((void **)rsa, session, + "ssh-rsa", filedata, filedata_len, passphrase); + } + +return rc; +} + +static unsigned char * +gen_publickey_from_rsa(LIBSSH2_SESSION *session, RSA *rsa, + size_t *key_len) +{ + int e_bytes, n_bytes; + unsigned long len; + unsigned char *key; + unsigned char *p; + const BIGNUM * e; + const BIGNUM * n; +#ifdef HAVE_OPAQUE_STRUCTS + RSA_get0_key(rsa, &n, &e, NULL); +#else + e = rsa->e; + n = rsa->n; +#endif + e_bytes = BN_num_bytes(e) + 1; + n_bytes = BN_num_bytes(n) + 1; + + /* Key form is "ssh-rsa" + e + n. */ + len = 4 + 7 + 4 + e_bytes + 4 + n_bytes; + + key = LIBSSH2_ALLOC(session, len); + if(key == NULL) { + return NULL; + } + + /* Process key encoding. */ + p = key; + + _libssh2_htonu32(p, 7); /* Key type. */ + p += 4; + memcpy(p, "ssh-rsa", 7); + p += 7; + + p = write_bn(p, e, e_bytes); + p = write_bn(p, n, n_bytes); + + *key_len = (size_t)(p - key); + return key; +} + +static int +gen_publickey_from_rsa_evp(LIBSSH2_SESSION *session, + unsigned char **method, + size_t *method_len, + unsigned char **pubkeydata, + size_t *pubkeydata_len, + EVP_PKEY *pk) +{ + RSA* rsa = NULL; + unsigned char *key; + unsigned char *method_buf = NULL; + size_t key_len; + + _libssh2_debug(session, + LIBSSH2_TRACE_AUTH, + "Computing public key from RSA private key envelop"); + + rsa = EVP_PKEY_get1_RSA(pk); + if(rsa == NULL) { + /* Assume memory allocation error... what else could it be ? */ + goto __alloc_error; + } + + method_buf = LIBSSH2_ALLOC(session, 7); /* ssh-rsa. */ + if(method_buf == NULL) { + goto __alloc_error; + } + + key = gen_publickey_from_rsa(session, rsa, &key_len); + if(key == NULL) { + goto __alloc_error; + } + RSA_free(rsa); + + memcpy(method_buf, "ssh-rsa", 7); + *method = method_buf; + *method_len = 7; + *pubkeydata = key; + *pubkeydata_len = key_len; + return 0; + + __alloc_error: + if(rsa != NULL) { + RSA_free(rsa); + } + if(method_buf != NULL) { + LIBSSH2_FREE(session, method_buf); + } + + return _libssh2_error(session, + LIBSSH2_ERROR_ALLOC, + "Unable to allocate memory for private key data"); +} + +static int _libssh2_rsa_new_additional_parameters(RSA *rsa) +{ + BN_CTX *ctx = NULL; + BIGNUM *aux = NULL; + BIGNUM *dmp1 = NULL; + BIGNUM *dmq1 = NULL; + const BIGNUM *p = NULL; + const BIGNUM *q = NULL; + const BIGNUM *d = NULL; + int rc = 0; + +#ifdef HAVE_OPAQUE_STRUCTS + RSA_get0_key(rsa, NULL, NULL, &d); + RSA_get0_factors(rsa, &p, &q); +#else + d = (*rsa).d; + p = (*rsa).p; + q = (*rsa).q; +#endif + + ctx = BN_CTX_new(); + if(ctx == NULL) + return -1; + + aux = BN_new(); + if(aux == NULL) { + rc = -1; + goto out; + } + + dmp1 = BN_new(); + if(dmp1 == NULL) { + rc = -1; + goto out; + } + + dmq1 = BN_new(); + if(dmq1 == NULL) { + rc = -1; + goto out; + } + + if((BN_sub(aux, q, BN_value_one()) == 0) || + (BN_mod(dmq1, d, aux, ctx) == 0) || + (BN_sub(aux, p, BN_value_one()) == 0) || + (BN_mod(dmp1, d, aux, ctx) == 0)) { + rc = -1; + goto out; + } + +#ifdef HAVE_OPAQUE_STRUCTS + RSA_set0_crt_params(rsa, dmp1, dmq1, NULL); +#else + (*rsa).dmp1 = dmp1; + (*rsa).dmq1 = dmq1; +#endif + +out: + if(aux) + BN_clear_free(aux); + BN_CTX_free(ctx); + + if(rc != 0) { + if(dmp1) + BN_clear_free(dmp1); + if(dmq1) + BN_clear_free(dmq1); + } + + return rc; +} + +static int +gen_publickey_from_rsa_openssh_priv_data(LIBSSH2_SESSION *session, + struct string_buf *decrypted, + unsigned char **method, + size_t *method_len, + unsigned char **pubkeydata, + size_t *pubkeydata_len, + libssh2_rsa_ctx **rsa_ctx) +{ + int rc = 0; + size_t nlen, elen, dlen, plen, qlen, coefflen, commentlen; + unsigned char *n, *e, *d, *p, *q, *coeff, *comment; + RSA *rsa = NULL; + + _libssh2_debug(session, + LIBSSH2_TRACE_AUTH, + "Computing RSA keys from private key data"); + + /* public key data */ + if(_libssh2_get_bignum_bytes(decrypted, &n, &nlen)) { + _libssh2_error(session, LIBSSH2_ERROR_PROTO, + "RSA no n"); + return -1; + } + + if(_libssh2_get_bignum_bytes(decrypted, &e, &elen)) { + _libssh2_error(session, LIBSSH2_ERROR_PROTO, + "RSA no e"); + return -1; + } + + /* private key data */ + if(_libssh2_get_bignum_bytes(decrypted, &d, &dlen)) { + _libssh2_error(session, LIBSSH2_ERROR_PROTO, + "RSA no d"); + return -1; + } + + if(_libssh2_get_bignum_bytes(decrypted, &coeff, &coefflen)) { + _libssh2_error(session, LIBSSH2_ERROR_PROTO, + "RSA no coeff"); + return -1; + } + + if(_libssh2_get_bignum_bytes(decrypted, &p, &plen)) { + _libssh2_error(session, LIBSSH2_ERROR_PROTO, + "RSA no p"); + return -1; + } + + if(_libssh2_get_bignum_bytes(decrypted, &q, &qlen)) { + _libssh2_error(session, LIBSSH2_ERROR_PROTO, + "RSA no q"); + return -1; + } + + if(_libssh2_get_string(decrypted, &comment, &commentlen)) { + _libssh2_error(session, LIBSSH2_ERROR_PROTO, + "RSA no comment"); + 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, + LIBSSH2_TRACE_AUTH, + "Could not create RSA private key"); + goto fail; + } + + if(rsa != NULL) + rc = _libssh2_rsa_new_additional_parameters(rsa); + + if(rsa != NULL && pubkeydata != NULL && method != NULL) { + EVP_PKEY *pk = EVP_PKEY_new(); + EVP_PKEY_set1_RSA(pk, rsa); + + rc = gen_publickey_from_rsa_evp(session, method, method_len, + pubkeydata, pubkeydata_len, + pk); + + if(pk) + EVP_PKEY_free(pk); + } + + if(rsa_ctx != NULL) + *rsa_ctx = rsa; + else + RSA_free(rsa); + + return rc; + +fail: + + if(rsa != NULL) + RSA_free(rsa); + + return _libssh2_error(session, + LIBSSH2_ERROR_ALLOC, + "Unable to allocate memory for private key data"); +} + +static int +_libssh2_rsa_new_openssh_private(libssh2_rsa_ctx ** rsa, + 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 == NULL) { + _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 RSA 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 != 0 || buf == NULL) { + _libssh2_error(session, LIBSSH2_ERROR_PROTO, + "Public key type in decrypted key data not found"); + return -1; + } + + if(strcmp("ssh-rsa", (const char *)buf) == 0) { + rc = gen_publickey_from_rsa_openssh_priv_data(session, decrypted, + NULL, 0, + NULL, 0, rsa); + } + else { + rc = -1; + } + + if(decrypted) + _libssh2_string_buf_free(session, decrypted); + + return rc; } int @@ -563,14 +1109,23 @@ _libssh2_rsa_new_private(libssh2_rsa_ctx ** rsa, LIBSSH2_SESSION * session, const char *filename, unsigned const char *passphrase) { + int rc; + pem_read_bio_func read_rsa = (pem_read_bio_func) &PEM_read_bio_RSAPrivateKey; (void) session; - _libssh2_init_if_needed (); + _libssh2_init_if_needed(); + + rc = read_private_key_from_file((void **) rsa, read_rsa, + filename, passphrase); + + if(rc) { + rc = _libssh2_rsa_new_openssh_private(rsa, session, + filename, passphrase); + } - return read_private_key_from_file((void **) rsa, read_rsa, - filename, passphrase); + return rc; } #if LIBSSH2_DSA @@ -580,14 +1135,275 @@ _libssh2_dsa_new_private_frommemory(libssh2_dsa_ctx ** dsa, const char *filedata, size_t filedata_len, unsigned const char *passphrase) { + int rc; + pem_read_bio_func read_dsa = (pem_read_bio_func) &PEM_read_bio_DSAPrivateKey; (void) session; _libssh2_init_if_needed(); - return read_private_key_from_memory((void **) dsa, read_dsa, - filedata, filedata_len, passphrase); + rc = read_private_key_from_memory((void **)dsa, read_dsa, + filedata, filedata_len, passphrase); + + if(rc) { + rc = read_openssh_private_key_from_memory((void **)dsa, session, + "ssh-dsa", filedata, filedata_len, passphrase); + } + + return rc; +} + +static unsigned char * +gen_publickey_from_dsa(LIBSSH2_SESSION* session, DSA *dsa, + size_t *key_len) +{ + int p_bytes, q_bytes, g_bytes, k_bytes; + unsigned long len; + unsigned char *key; + unsigned char *p; + + const BIGNUM * p_bn; + const BIGNUM * q; + const BIGNUM * g; + const BIGNUM * pub_key; +#ifdef HAVE_OPAQUE_STRUCTS + DSA_get0_pqg(dsa, &p_bn, &q, &g); +#else + p_bn = dsa->p; + q = dsa->q; + g = dsa->g; +#endif + +#ifdef HAVE_OPAQUE_STRUCTS + DSA_get0_key(dsa, &pub_key, NULL); +#else + pub_key = dsa->pub_key; +#endif + p_bytes = BN_num_bytes(p_bn) + 1; + q_bytes = BN_num_bytes(q) + 1; + g_bytes = BN_num_bytes(g) + 1; + k_bytes = BN_num_bytes(pub_key) + 1; + + /* Key form is "ssh-dss" + p + q + g + pub_key. */ + len = 4 + 7 + 4 + p_bytes + 4 + q_bytes + 4 + g_bytes + 4 + k_bytes; + + key = LIBSSH2_ALLOC(session, len); + if(key == NULL) { + return NULL; + } + + /* Process key encoding. */ + p = key; + + _libssh2_htonu32(p, 7); /* Key type. */ + p += 4; + memcpy(p, "ssh-dss", 7); + p += 7; + + p = write_bn(p, p_bn, p_bytes); + p = write_bn(p, q, q_bytes); + p = write_bn(p, g, g_bytes); + p = write_bn(p, pub_key, k_bytes); + + *key_len = (size_t)(p - key); + return key; +} + +static int +gen_publickey_from_dsa_evp(LIBSSH2_SESSION *session, + unsigned char **method, + size_t *method_len, + unsigned char **pubkeydata, + size_t *pubkeydata_len, + EVP_PKEY *pk) +{ + DSA* dsa = NULL; + unsigned char *key; + unsigned char *method_buf = NULL; + size_t key_len; + + _libssh2_debug(session, + LIBSSH2_TRACE_AUTH, + "Computing public key from DSA private key envelop"); + + dsa = EVP_PKEY_get1_DSA(pk); + if(dsa == NULL) { + /* Assume memory allocation error... what else could it be ? */ + goto __alloc_error; + } + + method_buf = LIBSSH2_ALLOC(session, 7); /* ssh-dss. */ + if(method_buf == NULL) { + goto __alloc_error; + } + + key = gen_publickey_from_dsa(session, dsa, &key_len); + if(key == NULL) { + goto __alloc_error; + } + DSA_free(dsa); + + memcpy(method_buf, "ssh-dss", 7); + *method = method_buf; + *method_len = 7; + *pubkeydata = key; + *pubkeydata_len = key_len; + return 0; + + __alloc_error: + if(dsa != NULL) { + DSA_free(dsa); + } + if(method_buf != NULL) { + LIBSSH2_FREE(session, method_buf); + } + + return _libssh2_error(session, + LIBSSH2_ERROR_ALLOC, + "Unable to allocate memory for private key data"); +} + +static int +gen_publickey_from_dsa_openssh_priv_data(LIBSSH2_SESSION *session, + struct string_buf *decrypted, + unsigned char **method, + size_t *method_len, + unsigned char **pubkeydata, + size_t *pubkeydata_len, + libssh2_dsa_ctx **dsa_ctx) +{ + int rc = 0; + size_t plen, qlen, glen, pub_len, priv_len; + unsigned char *p, *q, *g, *pub_key, *priv_key; + DSA *dsa = NULL; + + _libssh2_debug(session, + LIBSSH2_TRACE_AUTH, + "Computing DSA keys from private key data"); + + if(_libssh2_get_bignum_bytes(decrypted, &p, &plen)) { + _libssh2_error(session, LIBSSH2_ERROR_PROTO, + "DSA no p"); + return -1; + } + + if(_libssh2_get_bignum_bytes(decrypted, &q, &qlen)) { + _libssh2_error(session, LIBSSH2_ERROR_PROTO, + "DSA no q"); + return -1; + } + + if(_libssh2_get_bignum_bytes(decrypted, &g, &glen)) { + _libssh2_error(session, LIBSSH2_ERROR_PROTO, + "DSA no g"); + return -1; + } + + if(_libssh2_get_bignum_bytes(decrypted, &pub_key, &pub_len)) { + _libssh2_error(session, LIBSSH2_ERROR_PROTO, + "DSA no public key"); + return -1; + } + + if(_libssh2_get_bignum_bytes(decrypted, &priv_key, &priv_len)) { + _libssh2_error(session, LIBSSH2_ERROR_PROTO, + "DSA no private key"); + 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, + LIBSSH2_ERROR_PROTO, + "Could not create DSA private key"); + goto fail; + } + + if(dsa != NULL && pubkeydata != NULL && method != NULL) { + EVP_PKEY *pk = EVP_PKEY_new(); + EVP_PKEY_set1_DSA(pk, dsa); + + rc = gen_publickey_from_dsa_evp(session, method, method_len, + pubkeydata, pubkeydata_len, + pk); + + if(pk) + EVP_PKEY_free(pk); + } + + if(dsa_ctx != NULL) + *dsa_ctx = dsa; + else + DSA_free(dsa); + + return rc; + +fail: + + if(dsa != NULL) + DSA_free(dsa); + + return _libssh2_error(session, + LIBSSH2_ERROR_ALLOC, + "Unable to allocate memory for private key data"); +} + +static int +_libssh2_dsa_new_openssh_private(libssh2_dsa_ctx ** dsa, + 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 == NULL) { + _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 DSA 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 != 0 || buf == NULL) { + _libssh2_error(session, LIBSSH2_ERROR_PROTO, + "Public key type in decrypted key data not found"); + return -1; + } + + if(strcmp("ssh-dss", (const char *)buf) == 0) { + rc = gen_publickey_from_dsa_openssh_priv_data(session, decrypted, + NULL, 0, + NULL, 0, dsa); + } + else { + rc = -1; + } + + if(decrypted) + _libssh2_string_buf_free(session, decrypted); + + return rc; } int @@ -595,17 +1411,438 @@ _libssh2_dsa_new_private(libssh2_dsa_ctx ** dsa, LIBSSH2_SESSION * session, const char *filename, unsigned const char *passphrase) { + int rc; + pem_read_bio_func read_dsa = (pem_read_bio_func) &PEM_read_bio_DSAPrivateKey; (void) session; - _libssh2_init_if_needed (); + _libssh2_init_if_needed(); + + rc = read_private_key_from_file((void **) dsa, read_dsa, + filename, passphrase); + + if(rc) { + rc = _libssh2_dsa_new_openssh_private(dsa, session, + filename, passphrase); + } - return read_private_key_from_file((void **) dsa, read_dsa, - filename, passphrase); + 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) +{ + 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); + + if(rc) { + rc = read_openssh_private_key_from_memory((void **)ec_ctx, session, + "ssh-ecdsa", filedata, + filedata_len, passphrase); + } + + return rc; +} + +#endif /* LIBSSH2_ECDSA */ + + +#if LIBSSH2_ED25519 + +int +_libssh2_curve25519_new(LIBSSH2_SESSION *session, libssh2_x25519_ctx **out_ctx, + 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; + int rc = -1; + + pctx = EVP_PKEY_CTX_new_id(EVP_PKEY_X25519, NULL); + if(pctx == NULL) + 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; + } + + 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_public_key != NULL) { + *out_public_key = LIBSSH2_ALLOC(session, LIBSSH2_ED25519_KEY_LEN); + if(*out_public_key == NULL) + goto cleanExit; + + memcpy(*out_public_key, pub, LIBSSH2_ED25519_KEY_LEN); + } + + 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(ctx->public_key == NULL || ctx->private_key == NULL) { + _libssh2_x25519_free(ctx); + goto cleanExit; + } + + *out_ctx = ctx; + } + + /* success */ + rc = 0; + +cleanExit: + + 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); + + return rc; +} + +static int +gen_publickey_from_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, + libssh2_ed25519_ctx **out_ctx) +{ + libssh2_ed25519_ctx *ctx = NULL; + unsigned char *method_buf = NULL; + unsigned char *key = NULL; + int i, ret = 0; + unsigned char *pub_key, *priv_key, *buf; + size_t key_len = 0, tmp_len = 0; + unsigned char *p; + + _libssh2_debug(session, + LIBSSH2_TRACE_AUTH, + "Computing 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, &priv_key, &tmp_len) || + tmp_len != LIBSSH2_ED25519_PRIVATE_KEY_LEN) { + _libssh2_error(session, LIBSSH2_ERROR_PROTO, + "Wrong private key length"); + ret = -1; + 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); + + /* comment */ + if(_libssh2_get_string(decrypted, &buf, &tmp_len)) { + _libssh2_error(session, LIBSSH2_ERROR_PROTO, + "Unable to read comment"); + ret = -1; + goto clean_exit; + } + + if(tmp_len > 0) { + unsigned char *comment = LIBSSH2_CALLOC(session, tmp_len + 1); + if(comment != NULL) { + memcpy(comment, buf, tmp_len); + memcpy(comment + tmp_len, "\0", 1); + + _libssh2_debug(session, LIBSSH2_TRACE_AUTH, "Key comment: %s", + comment); + + LIBSSH2_FREE(session, comment); + } + } + + /* Padding */ + i = 1; + while(decrypted->dataptr < decrypted->data + decrypted->len) { + if(*decrypted->dataptr != i) { + _libssh2_error(session, LIBSSH2_ERROR_PROTO, + "Wrong padding"); + ret = -1; + goto clean_exit; + } + i++; + decrypted->dataptr++; + } + + if(ret == 0) { + _libssh2_debug(session, + LIBSSH2_TRACE_AUTH, + "Computing public key from ED25519 " + "private key envelop"); + + method_buf = LIBSSH2_ALLOC(session, 11); /* ssh-ed25519. */ + if(method_buf == NULL) { + goto clean_exit; + } + + /* Key form is: type_len(4) + type(11) + pub_key_len(4) + + pub_key(32). */ + key_len = LIBSSH2_ED25519_KEY_LEN + 19; + key = LIBSSH2_CALLOC(session, key_len); + if(key == NULL) { + goto clean_exit; + } + + p = key; + + _libssh2_store_str(&p, "ssh-ed25519", 11); + _libssh2_store_str(&p, (const char *)pub_key, LIBSSH2_ED25519_KEY_LEN); + + memcpy(method_buf, "ssh-ed25519", 11); + + if(method != NULL) + *method = method_buf; + else + LIBSSH2_FREE(session, method_buf); + + if(method_len != NULL) + *method_len = 11; + + if(pubkeydata != NULL) + *pubkeydata = key; + else + LIBSSH2_FREE(session, key); + + if(pubkeydata_len != NULL) + *pubkeydata_len = key_len; + + if(out_ctx != NULL) + *out_ctx = ctx; + else if(ctx != NULL) + _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; +} + +int +_libssh2_ed25519_new_private(libssh2_ed25519_ctx ** ed_ctx, + 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 == NULL) { + _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 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 != 0 || buf == NULL) { + _libssh2_error(session, LIBSSH2_ERROR_PROTO, + "Public key type in decrypted key data not found"); + return -1; + } + + if(strcmp("ssh-ed25519", (const char *)buf) == 0) { + rc = gen_publickey_from_ed25519_openssh_priv_data(session, + decrypted, + NULL, + NULL, + NULL, + NULL, + &ctx); + } + else { + rc = -1; + } + + if(decrypted) + _libssh2_string_buf_free(session, decrypted); + + if(rc == 0) { + if(ed_ctx != NULL) + *ed_ctx = ctx; + else if(ctx != NULL) + _libssh2_ed25519_free(ctx); + } + + return rc; +} + +int +_libssh2_ed25519_new_private_frommemory(libssh2_ed25519_ctx ** ed_ctx, + LIBSSH2_SESSION * session, + const char *filedata, + size_t filedata_len, + unsigned const char *passphrase) +{ + return read_openssh_private_key_from_memory((void **)ed_ctx, session, + "ssh-ed25519", + 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) +{ + 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) { + 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) + _libssh2_ed25519_free(ctx); + + return 0; +} + +#endif /* LIBSSH2_ED25519 */ + int _libssh2_rsa_sha1_sign(LIBSSH2_SESSION * session, libssh2_rsa_ctx * rsactx, @@ -620,13 +1857,13 @@ _libssh2_rsa_sha1_sign(LIBSSH2_SESSION * session, sig_len = RSA_size(rsactx); sig = LIBSSH2_ALLOC(session, sig_len); - if (!sig) { + if(!sig) { return -1; } ret = RSA_sign(NID_sha1, hash, hash_len, sig, &sig_len, rsactx); - if (!ret) { + if(!ret) { LIBSSH2_FREE(session, sig); return -1; } @@ -650,7 +1887,7 @@ _libssh2_dsa_sha1_sign(libssh2_dsa_ctx * dsactx, (void) hash_len; sig = DSA_do_sign(hash, SHA_DIGEST_LENGTH, dsactx); - if (!sig) { + if(!sig) { return -1; } @@ -661,12 +1898,12 @@ _libssh2_dsa_sha1_sign(libssh2_dsa_ctx * dsactx, s = sig->s; #endif r_len = BN_num_bytes(r); - if (r_len < 1 || r_len > 20) { + if(r_len < 1 || r_len > 20) { DSA_SIG_free(sig); return -1; } s_len = BN_num_bytes(s); - if (s_len < 1 || s_len > 20) { + if(s_len < 1 || s_len > 20) { DSA_SIG_free(sig); return -1; } @@ -682,16 +1919,79 @@ _libssh2_dsa_sha1_sign(libssh2_dsa_ctx * dsactx, } #endif /* LIBSSH_DSA */ +#if LIBSSH2_ECDSA + +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) +{ + int r_len, s_len; + int rc = 0; + size_t out_buffer_len = 0; + unsigned char *sp; + const BIGNUM *pr = NULL, *ps = NULL; + unsigned char *temp_buffer = NULL; + unsigned char *out_buffer = NULL; + + ECDSA_SIG *sig = ECDSA_do_sign(hash, hash_len, ec_ctx); + if(sig == NULL) + return -1; +#ifdef HAVE_OPAQUE_STRUCTS + ECDSA_SIG_get0(sig, &pr, &ps); +#else + pr = sig->r; + ps = sig->s; +#endif + + r_len = BN_num_bytes(pr) + 1; + s_len = BN_num_bytes(ps) + 1; + + temp_buffer = malloc(r_len + s_len + 8); + if(temp_buffer == NULL) { + rc = -1; + goto clean_exit; + } + + sp = temp_buffer; + sp = write_bn(sp, pr, r_len); + sp = write_bn(sp, ps, s_len); + + out_buffer_len = (size_t)(sp - temp_buffer); + + out_buffer = LIBSSH2_CALLOC(session, out_buffer_len); + if(out_buffer == NULL) { + rc = -1; + goto clean_exit; + } + + memcpy(out_buffer, temp_buffer, out_buffer_len); + + *signature = out_buffer; + *signature_len = out_buffer_len; + +clean_exit: + + if(temp_buffer != NULL) + free(temp_buffer); + + if(sig) + ECDSA_SIG_free(sig); + + return rc; +} +#endif /* LIBSSH2_ECDSA */ + int _libssh2_sha1_init(libssh2_sha1_ctx *ctx) { #ifdef HAVE_OPAQUE_STRUCTS *ctx = EVP_MD_CTX_new(); - if (*ctx == NULL) + if(*ctx == NULL) return 0; - if (EVP_DigestInit(*ctx, EVP_get_digestbyname("sha1"))) + if(EVP_DigestInit(*ctx, EVP_get_digestbyname("sha1"))) return 1; EVP_MD_CTX_free(*ctx); @@ -711,10 +2011,10 @@ _libssh2_sha1(const unsigned char *message, unsigned long len, #ifdef HAVE_OPAQUE_STRUCTS EVP_MD_CTX * ctx = EVP_MD_CTX_new(); - if (ctx == NULL) + if(ctx == NULL) return 1; /* error */ - if (EVP_DigestInit(ctx, EVP_get_digestbyname("sha1"))) { + if(EVP_DigestInit(ctx, EVP_get_digestbyname("sha1"))) { EVP_DigestUpdate(ctx, message, len); EVP_DigestFinal(ctx, out, NULL); EVP_MD_CTX_free(ctx); @@ -725,7 +2025,7 @@ _libssh2_sha1(const unsigned char *message, unsigned long len, EVP_MD_CTX ctx; EVP_MD_CTX_init(&ctx); - if (EVP_DigestInit(&ctx, EVP_get_digestbyname("sha1"))) { + if(EVP_DigestInit(&ctx, EVP_get_digestbyname("sha1"))) { EVP_DigestUpdate(&ctx, message, len); EVP_DigestFinal(&ctx, out, NULL); return 0; /* success */ @@ -740,10 +2040,10 @@ _libssh2_sha256_init(libssh2_sha256_ctx *ctx) #ifdef HAVE_OPAQUE_STRUCTS *ctx = EVP_MD_CTX_new(); - if (*ctx == NULL) + if(*ctx == NULL) return 0; - if (EVP_DigestInit(*ctx, EVP_get_digestbyname("sha256"))) + if(EVP_DigestInit(*ctx, EVP_get_digestbyname("sha256"))) return 1; EVP_MD_CTX_free(*ctx); @@ -763,7 +2063,7 @@ _libssh2_sha256(const unsigned char *message, unsigned long len, #ifdef HAVE_OPAQUE_STRUCTS EVP_MD_CTX * ctx = EVP_MD_CTX_new(); - if (ctx == NULL) + if(ctx == NULL) return 1; /* error */ if(EVP_DigestInit(ctx, EVP_get_digestbyname("sha256"))) { @@ -787,15 +2087,15 @@ _libssh2_sha256(const unsigned char *message, unsigned long len, } int -_libssh2_md5_init(libssh2_md5_ctx *ctx) +_libssh2_sha384_init(libssh2_sha384_ctx *ctx) { #ifdef HAVE_OPAQUE_STRUCTS *ctx = EVP_MD_CTX_new(); - if (*ctx == NULL) + if(*ctx == NULL) return 0; - if (EVP_DigestInit(*ctx, EVP_get_digestbyname("md5"))) + if(EVP_DigestInit(*ctx, EVP_get_digestbyname("sha384"))) return 1; EVP_MD_CTX_free(*ctx); @@ -804,237 +2104,804 @@ _libssh2_md5_init(libssh2_md5_ctx *ctx) return 0; #else EVP_MD_CTX_init(ctx); - return EVP_DigestInit(ctx, EVP_get_digestbyname("md5")); + return EVP_DigestInit(ctx, EVP_get_digestbyname("sha384")); #endif } -static unsigned char * -write_bn(unsigned char *buf, const BIGNUM *bn, int bn_bytes) +int +_libssh2_sha384(const unsigned char *message, unsigned long len, + unsigned char *out) { - unsigned char *p = buf; +#ifdef HAVE_OPAQUE_STRUCTS + EVP_MD_CTX * ctx = EVP_MD_CTX_new(); - /* Left space for bn size which will be written below. */ - p += 4; + if(ctx == NULL) + return 1; /* error */ - *p = 0; - BN_bn2bin(bn, p + 1); - if (!(*(p + 1) & 0x80)) { - memmove(p, p + 1, --bn_bytes); + if(EVP_DigestInit(ctx, EVP_get_digestbyname("sha384"))) { + EVP_DigestUpdate(ctx, message, len); + EVP_DigestFinal(ctx, out, NULL); + EVP_MD_CTX_free(ctx); + return 0; /* success */ } - _libssh2_htonu32(p - 4, bn_bytes); /* Post write bn size. */ + EVP_MD_CTX_free(ctx); +#else + EVP_MD_CTX ctx; - return p + bn_bytes; + EVP_MD_CTX_init(&ctx); + if(EVP_DigestInit(&ctx, EVP_get_digestbyname("sha384"))) { + EVP_DigestUpdate(&ctx, message, len); + EVP_DigestFinal(&ctx, out, NULL); + return 0; /* success */ + } +#endif + return 1; /* error */ } -static unsigned char * -gen_publickey_from_rsa(LIBSSH2_SESSION *session, RSA *rsa, - size_t *key_len) +int +_libssh2_sha512_init(libssh2_sha512_ctx *ctx) { - int e_bytes, n_bytes; - unsigned long len; - unsigned char* key; - unsigned char* p; - const BIGNUM * e; - const BIGNUM * n; #ifdef HAVE_OPAQUE_STRUCTS - RSA_get0_key(rsa, &n, &e, NULL); -#else - e = rsa->e; - n = rsa->n; -#endif - e_bytes = BN_num_bytes(e) + 1; - n_bytes = BN_num_bytes(n) + 1; - - /* Key form is "ssh-rsa" + e + n. */ - len = 4 + 7 + 4 + e_bytes + 4 + n_bytes; - - key = LIBSSH2_ALLOC(session, len); - if (key == NULL) { - return NULL; - } + *ctx = EVP_MD_CTX_new(); - /* Process key encoding. */ - p = key; + if(*ctx == NULL) + return 0; - _libssh2_htonu32(p, 7); /* Key type. */ - p += 4; - memcpy(p, "ssh-rsa", 7); - p += 7; + if(EVP_DigestInit(*ctx, EVP_get_digestbyname("sha512"))) + return 1; - p = write_bn(p, e, e_bytes); - p = write_bn(p, n, n_bytes); + EVP_MD_CTX_free(*ctx); + *ctx = NULL; - *key_len = (size_t)(p - key); - return key; + return 0; +#else + EVP_MD_CTX_init(ctx); + return EVP_DigestInit(ctx, EVP_get_digestbyname("sha512")); +#endif } -#if LIBSSH2_DSA -static unsigned char * -gen_publickey_from_dsa(LIBSSH2_SESSION* session, DSA *dsa, - size_t *key_len) +int +_libssh2_sha512(const unsigned char *message, unsigned long len, + unsigned char *out) { - int p_bytes, q_bytes, g_bytes, k_bytes; - unsigned long len; - unsigned char* key; - unsigned char* p; - - const BIGNUM * p_bn; - const BIGNUM * q; - const BIGNUM * g; - const BIGNUM * pub_key; #ifdef HAVE_OPAQUE_STRUCTS - DSA_get0_pqg(dsa, &p_bn, &q, &g); + EVP_MD_CTX * ctx = EVP_MD_CTX_new(); + + if(ctx == NULL) + return 1; /* error */ + + if(EVP_DigestInit(ctx, EVP_get_digestbyname("sha512"))) { + EVP_DigestUpdate(ctx, message, len); + EVP_DigestFinal(ctx, out, NULL); + EVP_MD_CTX_free(ctx); + return 0; /* success */ + } + EVP_MD_CTX_free(ctx); #else - p_bn = dsa->p; - q = dsa->q; - g = dsa->g; + EVP_MD_CTX ctx; + + EVP_MD_CTX_init(&ctx); + if(EVP_DigestInit(&ctx, EVP_get_digestbyname("sha512"))) { + EVP_DigestUpdate(&ctx, message, len); + EVP_DigestFinal(&ctx, out, NULL); + return 0; /* success */ + } #endif + return 1; /* error */ +} +int +_libssh2_md5_init(libssh2_md5_ctx *ctx) +{ #ifdef HAVE_OPAQUE_STRUCTS - DSA_get0_key(dsa, &pub_key, NULL); + *ctx = EVP_MD_CTX_new(); + + if(*ctx == NULL) + return 0; + + if(EVP_DigestInit(*ctx, EVP_get_digestbyname("md5"))) + return 1; + + EVP_MD_CTX_free(*ctx); + *ctx = NULL; + + return 0; #else - pub_key = dsa->pub_key; + EVP_MD_CTX_init(ctx); + return EVP_DigestInit(ctx, EVP_get_digestbyname("md5")); #endif - p_bytes = BN_num_bytes(p_bn) + 1; - q_bytes = BN_num_bytes(q) + 1; - g_bytes = BN_num_bytes(g) + 1; - k_bytes = BN_num_bytes(pub_key) + 1; +} - /* Key form is "ssh-dss" + p + q + g + pub_key. */ - len = 4 + 7 + 4 + p_bytes + 4 + q_bytes + 4 + g_bytes + 4 + k_bytes; +#if LIBSSH2_ECDSA - key = LIBSSH2_ALLOC(session, len); - if (key == NULL) { - return NULL; +static int +gen_publickey_from_ec_evp(LIBSSH2_SESSION *session, + unsigned char **method, + size_t *method_len, + unsigned char **pubkeydata, + size_t *pubkeydata_len, + EVP_PKEY *pk) +{ + int rc = 0; + EC_KEY *ec = NULL; + unsigned char *p; + unsigned char *method_buf = NULL; + unsigned char *key; + size_t key_len = 0; + unsigned char *octal_value = NULL; + size_t octal_len; + const EC_POINT *public_key; + const EC_GROUP *group; + BN_CTX *bn_ctx; + libssh2_curve_type type; + + _libssh2_debug(session, + LIBSSH2_TRACE_AUTH, + "Computing public key from EC private key envelop"); + + bn_ctx = BN_CTX_new(); + if(bn_ctx == NULL) + return -1; + + ec = EVP_PKEY_get1_EC_KEY(pk); + if(ec == NULL) { + 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); + + method_buf = LIBSSH2_ALLOC(session, 19); + if(method_buf == NULL) { + return _libssh2_error(session, LIBSSH2_ERROR_ALLOC, + "out of memory"); + } + + if(type == LIBSSH2_EC_CURVE_NISTP256) + memcpy(method_buf, "ecdsa-sha2-nistp256", 19); + else if(type == LIBSSH2_EC_CURVE_NISTP384) + memcpy(method_buf, "ecdsa-sha2-nistp384", 19); + else if(type == LIBSSH2_EC_CURVE_NISTP521) + memcpy(method_buf, "ecdsa-sha2-nistp521", 19); + else { + _libssh2_debug(session, + LIBSSH2_TRACE_ERROR, + "Unsupported EC private key type"); + rc = -1; + goto clean_exit; + } + + /* get length */ + octal_len = EC_POINT_point2oct(group, public_key, + POINT_CONVERSION_UNCOMPRESSED, + NULL, 0, bn_ctx); + if(octal_len > EC_MAX_POINT_LEN) { + rc = -1; + goto clean_exit; + } + + octal_value = malloc(octal_len); + if(octal_value == NULL) { + rc = -1; + goto clean_exit; + } + + /* 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; + } + + /* 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 = LIBSSH2_ALLOC(session, key_len); + if(key == NULL) { + rc = -1; + goto clean_exit; } /* Process key encoding. */ p = key; - _libssh2_htonu32(p, 7); /* Key type. */ - p += 4; - memcpy(p, "ssh-dss", 7); - p += 7; + /* Key type */ + _libssh2_store_str(&p, (const char *)method_buf, 19); - p = write_bn(p, p_bn, p_bytes); - p = write_bn(p, q, q_bytes); - p = write_bn(p, g, g_bytes); - p = write_bn(p, pub_key, k_bytes); + /* Name domain */ + _libssh2_store_str(&p, (const char *)method_buf + 11, 8); - *key_len = (size_t)(p - key); - return key; + /* 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) + EC_KEY_free(ec); + + if(bn_ctx != NULL) { + BN_CTX_free(bn_ctx); + } + + if(octal_value != NULL) + free(octal_value); + + if(rc == 0) + return 0; + + if(method_buf != NULL) + LIBSSH2_FREE(session, method_buf); + + return -1; } -#endif /* LIBSSH_DSA */ static int -gen_publickey_from_rsa_evp(LIBSSH2_SESSION *session, - unsigned char **method, - size_t *method_len, - unsigned char **pubkeydata, - size_t *pubkeydata_len, - EVP_PKEY *pk) +gen_publickey_from_ecdsa_openssh_priv_data(LIBSSH2_SESSION *session, + libssh2_curve_type curve_type, + struct string_buf *decrypted, + unsigned char **method, + size_t *method_len, + unsigned char **pubkeydata, + size_t *pubkeydata_len, + libssh2_ecdsa_ctx **ec_ctx) { - RSA* rsa = NULL; - unsigned char* key; - unsigned char* method_buf = NULL; - size_t key_len; + int rc = 0; + size_t curvelen, exponentlen, pointlen; + unsigned char *curve, *exponent, *point_buf; + EC_KEY *ec_key = NULL; + BIGNUM *bn_exponent; _libssh2_debug(session, LIBSSH2_TRACE_AUTH, - "Computing public key from RSA private key envelop"); + "Computing ECDSA keys from private key data"); - rsa = EVP_PKEY_get1_RSA(pk); - if (rsa == NULL) { - /* Assume memory allocation error... what else could it be ? */ - goto __alloc_error; + if(_libssh2_get_string(decrypted, &curve, &curvelen) || + curvelen == 0) { + _libssh2_error(session, LIBSSH2_ERROR_PROTO, + "ECDSA no curve"); + return -1; } - method_buf = LIBSSH2_ALLOC(session, 7); /* ssh-rsa. */ - if (method_buf == NULL) { - goto __alloc_error; + if(_libssh2_get_string(decrypted, &point_buf, &pointlen)) { + _libssh2_error(session, LIBSSH2_ERROR_PROTO, + "ECDSA no point"); + return -1; } - key = gen_publickey_from_rsa(session, rsa, &key_len); - if (key == NULL) { - goto __alloc_error; + if(_libssh2_get_bignum_bytes(decrypted, &exponent, &exponentlen)) { + _libssh2_error(session, LIBSSH2_ERROR_PROTO, + "ECDSA no exponent"); + return -1; } - RSA_free(rsa); - memcpy(method_buf, "ssh-rsa", 7); - *method = method_buf; - *method_len = 7; - *pubkeydata = key; - *pubkeydata_len = key_len; - return 0; + if((rc = _libssh2_ecdsa_curve_name_with_octal_new(&ec_key, point_buf, + pointlen, curve_type)) != 0) { + _libssh2_error(session, LIBSSH2_ERROR_PROTO, + "ECDSA could not create key"); + goto fail; + } - __alloc_error: - if (rsa != NULL) { - RSA_free(rsa); + bn_exponent = BN_new(); + if(bn_exponent == NULL) { + rc = -1; + goto fail; } - if (method_buf != NULL) { - LIBSSH2_FREE(session, method_buf); + + BN_bin2bn(exponent, exponentlen, bn_exponent); + rc = (EC_KEY_set_private_key(ec_key, bn_exponent) != 1); + + if(rc == 0 && ec_key != NULL && pubkeydata != NULL && method != NULL) { + 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); + + if(pk) + EVP_PKEY_free(pk); } + if(ec_ctx != NULL) + *ec_ctx = ec_key; + else + EC_KEY_free(ec_key); + + return rc; + +fail: + + if(ec_key != NULL) + EC_KEY_free(ec_key); + return _libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memory for private key data"); + + } -#if LIBSSH2_DSA static int -gen_publickey_from_dsa_evp(LIBSSH2_SESSION *session, - unsigned char **method, - size_t *method_len, - unsigned char **pubkeydata, - size_t *pubkeydata_len, - EVP_PKEY *pk) +_libssh2_ecdsa_new_openssh_private(libssh2_ecdsa_ctx ** ec_ctx, + LIBSSH2_SESSION * session, + const char *filename, + unsigned const char *passphrase) { - DSA* dsa = NULL; - unsigned char* key; - unsigned char* method_buf = NULL; - size_t key_len; + FILE *fp; + int rc; + unsigned char *buf = NULL; + libssh2_curve_type type; + struct string_buf *decrypted = NULL; + + if(session == NULL) { + _libssh2_error(session, LIBSSH2_ERROR_PROTO, + "Session is required"); + return -1; + } - _libssh2_debug(session, - LIBSSH2_TRACE_AUTH, - "Computing public key from DSA private key envelop"); + _libssh2_init_if_needed(); - dsa = EVP_PKEY_get1_DSA(pk); - if (dsa == NULL) { - /* Assume memory allocation error... what else could it be ? */ - goto __alloc_error; + fp = fopen(filename, "r"); + if(!fp) { + _libssh2_error(session, LIBSSH2_ERROR_FILE, + "Unable to open OpenSSH ECDSA private key file"); + return -1; } - method_buf = LIBSSH2_ALLOC(session, 7); /* ssh-dss. */ - if (method_buf == NULL) { - goto __alloc_error; + rc = _libssh2_openssh_pem_parse(session, passphrase, fp, &decrypted); + fclose(fp); + if(rc) { + return rc; } - key = gen_publickey_from_dsa(session, dsa, &key_len); - if (key == NULL) { - goto __alloc_error; + /* 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; } - DSA_free(dsa); - memcpy(method_buf, "ssh-dss", 7); - *method = method_buf; - *method_len = 7; - *pubkeydata = key; - *pubkeydata_len = key_len; - return 0; + rc = _libssh2_ecdsa_curve_type_from_name((const char *)buf, &type); - __alloc_error: - if (dsa != NULL) { - DSA_free(dsa); + if(rc == 0) { + rc = gen_publickey_from_ecdsa_openssh_priv_data(session, type, + decrypted, NULL, 0, + NULL, 0, ec_ctx); } - if (method_buf != NULL) { - LIBSSH2_FREE(session, method_buf); + else { + rc = -1; } - return _libssh2_error(session, - LIBSSH2_ERROR_ALLOC, - "Unable to allocate memory for private key data"); + if(decrypted) + _libssh2_string_buf_free(session, decrypted); + + return rc; +} + +int +_libssh2_ecdsa_new_private(libssh2_ecdsa_ctx ** ec_ctx, + 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; + (void) session; + + _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(ec_ctx, session, + filename, passphrase); + } + + return rc; +} + +/* + * _libssh2_ecdsa_create_key + * + * Creates a local private key based on input curve + * and returns octal value and octal length + * + */ + +int +_libssh2_ecdsa_create_key(LIBSSH2_SESSION *session, + _libssh2_ec_key **out_private_key, + unsigned char **out_public_key_octal, + size_t *out_public_key_octal_len, + libssh2_curve_type curve_type) +{ + int ret = 1; + size_t octal_len = 0; + unsigned char octal_value[EC_MAX_POINT_LEN]; + const EC_POINT *public_key = NULL; + EC_KEY *private_key = NULL; + const EC_GROUP *group = NULL; + + /* create key */ + BN_CTX *bn_ctx = BN_CTX_new(); + if(!bn_ctx) + return -1; + + private_key = EC_KEY_new_by_curve_name(curve_type); + group = EC_KEY_get0_group(private_key); + + EC_KEY_generate_key(private_key); + public_key = EC_KEY_get0_public_key(private_key); + + /* get length */ + octal_len = EC_POINT_point2oct(group, public_key, + POINT_CONVERSION_UNCOMPRESSED, + NULL, 0, bn_ctx); + if(octal_len > EC_MAX_POINT_LEN) { + ret = -1; + goto clean_exit; + } + + /* 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; + } + + if(out_private_key != NULL) + *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) { + ret = -1; + goto clean_exit; + } + + memcpy(*out_public_key_octal, octal_value, octal_len); + } + + if(out_public_key_octal_len != NULL) + *out_public_key_octal_len = octal_len; + +clean_exit: + + if(bn_ctx) + BN_CTX_free(bn_ctx); + + return (ret == 1) ? 0 : -1; +} + +/* _libssh2_ecdh_gen_k + * + * Computes the shared secret K given a local private key, + * remote public key and length + */ + +int +_libssh2_ecdh_gen_k(_libssh2_bn **k, _libssh2_ec_key *private_key, + const unsigned char *server_public_key, size_t server_public_key_len) +{ + int ret = 0; + int rc; + size_t secret_len; + unsigned char *secret = NULL; + const EC_GROUP *private_key_group; + EC_POINT *server_public_key_point; + + BN_CTX *bn_ctx = BN_CTX_new(); + + if(!bn_ctx) + return -1; + + if(k == NULL) + 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) + return -1; + + rc = EC_POINT_oct2point(private_key_group, server_public_key_point, + server_public_key, server_public_key_len, bn_ctx); + if(rc != 1) { + ret = -1; + goto clean_exit; + } + + secret_len = (EC_GROUP_get_degree(private_key_group) + 7) / 8; + secret = malloc(secret_len); + if(!secret) { + ret = -1; + goto clean_exit; + } + + secret_len = ECDH_compute_key(secret, secret_len, server_public_key_point, + private_key, NULL); + + if(secret_len <= 0 || secret_len > EC_MAX_POINT_LEN) { + ret = -1; + goto clean_exit; + } + + BN_bin2bn(secret, secret_len, *k); + +clean_exit: + + if(server_public_key_point != NULL) + EC_POINT_free(server_public_key_point); + + if(bn_ctx != NULL) + BN_CTX_free(bn_ctx); + + if(secret != NULL) + free(secret); + + return ret; +} + + +#endif /* LIBSSH2_ECDSA */ + +#if LIBSSH2_ED25519 + +int +_libssh2_ed25519_sign(libssh2_ed25519_ctx *ctx, LIBSSH2_SESSION *session, + uint8_t **out_sig, size_t *out_sig_len, + const uint8_t *message, size_t message_len) +{ + int rc = -1; + EVP_MD_CTX *md_ctx = EVP_MD_CTX_new(); + 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) + goto clean_exit; + if(EVP_DigestSign(md_ctx, NULL, &sig_len, message, message_len) != 1) + goto clean_exit; + + if(sig_len != LIBSSH2_ED25519_SIG_LEN) + goto clean_exit; + + sig = LIBSSH2_CALLOC(session, sig_len); + if(sig == NULL) + goto clean_exit; + + rc = EVP_DigestSign(md_ctx, sig, &sig_len, message, message_len); + } + + if(rc == 1) { + *out_sig = sig; + *out_sig_len = sig_len; + } + else { + *out_sig_len = 0; + *out_sig = NULL; + LIBSSH2_FREE(session, sig); + } + +clean_exit: + + if(md_ctx) + EVP_MD_CTX_free(md_ctx); + + return (rc == 1 ? 0 : -1); +} + +int +_libssh2_curve25519_gen_k(_libssh2_bn **k, + uint8_t private_key[LIBSSH2_ED25519_KEY_LEN], + uint8_t server_public_key[LIBSSH2_ED25519_KEY_LEN]) +{ + int rc = -1; + unsigned char out_shared_key[LIBSSH2_ED25519_KEY_LEN]; + EVP_PKEY *peer_key = NULL, *server_key = NULL; + EVP_PKEY_CTX *server_key_ctx = NULL; + BN_CTX *bn_ctx = NULL; + size_t out_len = 0; + + if(k == NULL || *k == NULL) + return -1; + + bn_ctx = BN_CTX_new(); + if(bn_ctx == NULL) + return -1; + + peer_key = EVP_PKEY_new_raw_public_key(EVP_PKEY_X25519, NULL, + server_public_key, + LIBSSH2_ED25519_KEY_LEN); + + server_key = EVP_PKEY_new_raw_private_key(EVP_PKEY_X25519, NULL, + private_key, + LIBSSH2_ED25519_KEY_LEN); + + if(peer_key == NULL || server_key == NULL) { + goto cleanExit; + } + + server_key_ctx = EVP_PKEY_CTX_new(server_key, NULL); + if(server_key_ctx == NULL) { + goto cleanExit; + } + + rc = EVP_PKEY_derive_init(server_key_ctx); + if(rc <= 0) goto cleanExit; + + rc = EVP_PKEY_derive_set_peer(server_key_ctx, peer_key); + if(rc <= 0) goto cleanExit; + + rc = EVP_PKEY_derive(server_key_ctx, NULL, &out_len); + if(rc <= 0) goto cleanExit; + + if(out_len != LIBSSH2_ED25519_KEY_LEN) { + rc = -1; + goto cleanExit; + } + + rc = EVP_PKEY_derive(server_key_ctx, out_shared_key, &out_len); + + if(rc == 1 && out_len == LIBSSH2_ED25519_KEY_LEN) { + BN_bin2bn(out_shared_key, LIBSSH2_ED25519_KEY_LEN, *k); + } + else { + rc = -1; + } + +cleanExit: + + if(server_key_ctx) + EVP_PKEY_CTX_free(server_key_ctx); + if(peer_key) + EVP_PKEY_free(peer_key); + if(server_key) + EVP_PKEY_free(server_key); + if(bn_ctx != NULL) + BN_CTX_free(bn_ctx); + + return (rc == 1) ? 0 : -1; +} + + +int +_libssh2_ed25519_verify(libssh2_ed25519_ctx *ctx, const uint8_t *s, + size_t s_len, const uint8_t *m, size_t m_len) +{ + int ret = -1; + + EVP_MD_CTX *md_ctx = EVP_MD_CTX_new(); + if(NULL == md_ctx) + return -1; + + ret = EVP_DigestVerifyInit(md_ctx, NULL, NULL, NULL, ctx->public_key); + if(ret != 1) + goto clean_exit; + + ret = EVP_DigestVerify(md_ctx, s, s_len, m, m_len); + + clean_exit: + + EVP_MD_CTX_free(md_ctx); + + return (ret == 1) ? 0 : -1; +} + +#endif /* LIBSSH2_ED25519 */ + +static int +_libssh2_pub_priv_openssh_keyfile(LIBSSH2_SESSION *session, + unsigned char **method, + size_t *method_len, + unsigned char **pubkeydata, + size_t *pubkeydata_len, + const char *privatekey, + const char *passphrase) +{ + FILE *fp; + unsigned char *buf = NULL; + struct string_buf *decrypted = NULL; + int rc = 0; + + if(session == NULL) { + _libssh2_error(session, LIBSSH2_ERROR_PROTO, + "Session is required"); + return -1; + } + + _libssh2_init_if_needed(); + + fp = fopen(privatekey, "r"); + if(!fp) { + _libssh2_error(session, LIBSSH2_ERROR_FILE, + "Unable to open private key file"); + return -1; + } + + rc = _libssh2_openssh_pem_parse(session, (const unsigned char *)passphrase, + fp, &decrypted); + fclose(fp); + if(rc) { + _libssh2_error(session, LIBSSH2_ERROR_FILE, + "Not an OpenSSH key file"); + 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 != 0 || buf == NULL) { + _libssh2_error(session, LIBSSH2_ERROR_PROTO, + "Public key type in decrypted key data not found"); + return -1; + } + + rc = -1; + +#if LIBSSH2_ED25519 + if(strcmp("ssh-ed25519", (const char *)buf) == 0) { + rc = gen_publickey_from_ed25519_openssh_priv_data(session, decrypted, + method, method_len, + pubkeydata, + pubkeydata_len, + NULL); + } +#endif +#if LIBSSH2_RSA + if(strcmp("ssh-rsa", (const char *)buf) == 0) { + rc = gen_publickey_from_rsa_openssh_priv_data(session, decrypted, + method, method_len, + pubkeydata, + pubkeydata_len, + NULL); + } +#endif +#if LIBSSH2_DSA + if(strcmp("ssh-dss", (const char *)buf) == 0) { + rc = gen_publickey_from_dsa_openssh_priv_data(session, decrypted, + method, method_len, + pubkeydata, + pubkeydata_len, + NULL); + } +#endif +#if LIBSSH2_ECDSA + { + libssh2_curve_type type; + + if(_libssh2_ecdsa_curve_type_from_name((const char *)buf, + &type) == 0) { + rc = gen_publickey_from_ecdsa_openssh_priv_data(session, type, + decrypted, + method, method_len, + pubkeydata, + pubkeydata_len, + NULL); + } + } +#endif + + if(decrypted) + _libssh2_string_buf_free(session, decrypted); + + if(rc != 0) { + _libssh2_error(session, LIBSSH2_ERROR_FILE, + "Unsupported OpenSSH key type"); + } + + return rc; } -#endif /* LIBSSH_DSA */ int _libssh2_pub_priv_keyfile(LIBSSH2_SESSION *session, @@ -1049,6 +2916,7 @@ _libssh2_pub_priv_keyfile(LIBSSH2_SESSION *session, BIO* bp; EVP_PKEY* pk; int pktype; + int rc; _libssh2_debug(session, LIBSSH2_TRACE_AUTH, @@ -1056,31 +2924,35 @@ _libssh2_pub_priv_keyfile(LIBSSH2_SESSION *session, privatekey); bp = BIO_new_file(privatekey, "r"); - if (bp == NULL) { + if(bp == NULL) { return _libssh2_error(session, LIBSSH2_ERROR_FILE, "Unable to extract public key from private key " "file: Unable to open private key file"); } - if (!EVP_get_cipherbyname("des")) { - /* If this cipher isn't loaded it's a pretty good indication that none - * are. I have *NO DOUBT* that there's a better way to deal with this - * ($#&%#$(%$#( Someone buy me an OpenSSL manual and I'll read up on - * it. - */ - OpenSSL_add_all_ciphers(); - } + BIO_reset(bp); - pk = PEM_read_bio_PrivateKey(bp, NULL, NULL, (void*)passphrase); + pk = PEM_read_bio_PrivateKey(bp, NULL, NULL, (void *)passphrase); BIO_free(bp); - if (pk == NULL) { - 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"); + if(pk == NULL) { + + /* Try OpenSSH format */ + rc = _libssh2_pub_priv_openssh_keyfile(session, + method, + method_len, + pubkeydata, pubkeydata_len, + privatekey, passphrase); + if(rc != 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; } #ifdef HAVE_OPAQUE_STRUCTS @@ -1089,7 +2961,7 @@ _libssh2_pub_priv_keyfile(LIBSSH2_SESSION *session, pktype = pk->type; #endif - switch (pktype) { + switch(pktype) { case EVP_PKEY_RSA : st = gen_publickey_from_rsa_evp( session, method, method_len, pubkeydata, pubkeydata_len, pk); @@ -1102,6 +2974,13 @@ _libssh2_pub_priv_keyfile(LIBSSH2_SESSION *session, break; #endif /* LIBSSH_DSA */ +#if LIBSSH2_ECDSA + case EVP_PKEY_EC : + st = gen_publickey_from_ec_evp( + session, method, method_len, pubkeydata, pubkeydata_len, pk); + break; +#endif + default : st = _libssh2_error(session, LIBSSH2_ERROR_FILE, @@ -1115,6 +2994,129 @@ _libssh2_pub_priv_keyfile(LIBSSH2_SESSION *session, return st; } +static int +_libssh2_pub_priv_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, + 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 != NULL) + *key_ctx = NULL; + + if(session == NULL) { + _libssh2_error(session, LIBSSH2_ERROR_PROTO, + "Session is required"); + return -1; + } + + if(key_type != NULL && (strlen(key_type) > 11 || strlen(key_type) < 7)) { + _libssh2_error(session, LIBSSH2_ERROR_PROTO, + "type is invalid"); + return -1; + } + + _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 != 0 || buf == NULL) { + _libssh2_error(session, LIBSSH2_ERROR_PROTO, + "Public key type in decrypted key data not found"); + return -1; + } + + rc = -1; + +#if LIBSSH2_ED25519 + if(strcmp("ssh-ed25519", (const char *)buf) == 0) { + if(key_type == NULL || strcmp("ssh-ed25519", key_type) == 0) { + rc = gen_publickey_from_ed25519_openssh_priv_data(session, + decrypted, + method, + method_len, + pubkeydata, + pubkeydata_len, + (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) { + rc = gen_publickey_from_rsa_openssh_priv_data(session, decrypted, + method, method_len, + pubkeydata, + pubkeydata_len, + (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) { + rc = gen_publickey_from_dsa_openssh_priv_data(session, decrypted, + method, method_len, + pubkeydata, + pubkeydata_len, + (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, + method, method_len, + pubkeydata, + pubkeydata_len, + (libssh2_ecdsa_ctx**)key_ctx); + } + } +} +#endif + + if(decrypted) + _libssh2_string_buf_free(session, decrypted); + + return rc; +} + +int +read_openssh_private_key_from_memory(void **key_ctx, LIBSSH2_SESSION *session, + const char *key_type, + const char *filedata, + size_t filedata_len, + unsigned const char *passphrase) +{ + return _libssh2_pub_priv_openssh_keyfilememory(session, key_ctx, key_type, + NULL, NULL, NULL, NULL, + filedata, filedata_len, + passphrase); +} + int _libssh2_pub_priv_keyfilememory(LIBSSH2_SESSION *session, unsigned char **method, @@ -1135,28 +3137,34 @@ _libssh2_pub_priv_keyfilememory(LIBSSH2_SESSION *session, "Computing public key from private key."); bp = BIO_new_mem_buf((char *)privatekeydata, privatekeydata_len); - if (!bp) { + if(!bp) { return -1; } - if (!EVP_get_cipherbyname("des")) { - /* If this cipher isn't loaded it's a pretty good indication that none - * are. I have *NO DOUBT* that there's a better way to deal with this - * ($#&%#$(%$#( Someone buy me an OpenSSL manual and I'll read up on - * it. - */ - OpenSSL_add_all_ciphers(); - } + BIO_reset(bp); - pk = PEM_read_bio_PrivateKey(bp, NULL, NULL, (void*)passphrase); + pk = PEM_read_bio_PrivateKey(bp, NULL, NULL, (void *)passphrase); BIO_free(bp); - if (pk == NULL) { - 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"); + if(pk == NULL) { + /* Try OpenSSH format */ + st = _libssh2_pub_priv_openssh_keyfilememory(session, NULL, NULL, + method, + method_len, + pubkeydata, + 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; } #ifdef HAVE_OPAQUE_STRUCTS @@ -1165,7 +3173,7 @@ _libssh2_pub_priv_keyfilememory(LIBSSH2_SESSION *session, pktype = pk->type; #endif - switch (pktype) { + switch(pktype) { case EVP_PKEY_RSA : st = gen_publickey_from_rsa_evp(session, method, method_len, pubkeydata, pubkeydata_len, pk); @@ -1176,6 +3184,12 @@ _libssh2_pub_priv_keyfilememory(LIBSSH2_SESSION *session, pubkeydata, pubkeydata_len, pk); break; #endif /* LIBSSH_DSA */ +#if LIBSSH2_ECDSA + case EVP_PKEY_EC : + st = gen_publickey_from_ec_evp(session, method, method_len, + pubkeydata, pubkeydata_len, pk); + break; +#endif /* LIBSSH2_ECDSA */ default : st = _libssh2_error(session, LIBSSH2_ERROR_FILE, @@ -1189,4 +3203,38 @@ _libssh2_pub_priv_keyfilememory(LIBSSH2_SESSION *session, return st; } +void +_libssh2_dh_init(_libssh2_dh_ctx *dhctx) +{ + *dhctx = BN_new(); /* Random from client */ +} + +int +_libssh2_dh_key_pair(_libssh2_dh_ctx *dhctx, _libssh2_bn *public, + _libssh2_bn *g, _libssh2_bn *p, int group_order, + _libssh2_bn_ctx *bnctx) +{ + /* Generate x and e */ + BN_rand(*dhctx, group_order * 8 - 1, 0, -1); + BN_mod_exp(public, g, *dhctx, p, bnctx); + return 0; +} + +int +_libssh2_dh_secret(_libssh2_dh_ctx *dhctx, _libssh2_bn *secret, + _libssh2_bn *f, _libssh2_bn *p, + _libssh2_bn_ctx *bnctx) +{ + /* Compute the shared secret */ + BN_mod_exp(secret, f, *dhctx, p, bnctx); + return 0; +} + +void +_libssh2_dh_dtor(_libssh2_dh_ctx *dhctx) +{ + BN_clear_free(*dhctx); + *dhctx = NULL; +} + #endif /* LIBSSH2_OPENSSL */ |