/* Copyright (C) 2009, 2010 Simon Josefsson * Copyright (C) 2006, 2007 The Written Word, Inc. All rights reserved. * Copyright (c) 2004-2006, Sara Golemon * * Author: Simon Josefsson * * Redistribution and use in source and binary forms, * with or without modification, are permitted provided * that the following conditions are met: * * Redistributions of source code must retain the above * copyright notice, this list of conditions and the * following disclaimer. * * Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following * disclaimer in the documentation and/or other materials * provided with the distribution. * * Neither the name of the copyright holder nor the names * of any other contributors may be used to endorse or * promote products derived from this software without * specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY * OF SUCH DAMAGE. */ #include "libssh2_priv.h" #ifdef LIBSSH2_OPENSSL /* compile only if we build with openssl */ #include #ifndef EVP_MAX_BLOCK_LENGTH #define EVP_MAX_BLOCK_LENGTH 32 #endif int _libssh2_rsa_new(libssh2_rsa_ctx ** rsa, const unsigned char *edata, unsigned long elen, const unsigned char *ndata, unsigned long nlen, const unsigned char *ddata, unsigned long dlen, const unsigned char *pdata, unsigned long plen, const unsigned char *qdata, unsigned long qlen, const unsigned char *e1data, unsigned long e1len, const unsigned char *e2data, unsigned long e2len, const unsigned char *coeffdata, unsigned long coefflen) { BIGNUM * e; BIGNUM * n; BIGNUM * d = 0; BIGNUM * p = 0; BIGNUM * q = 0; BIGNUM * dmp1 = 0; BIGNUM * dmq1 = 0; BIGNUM * iqmp = 0; e = BN_new(); BN_bin2bn(edata, elen, e); n = BN_new(); BN_bin2bn(ndata, nlen, n); if (ddata) { d = BN_new(); BN_bin2bn(ddata, dlen, d); p = BN_new(); BN_bin2bn(pdata, plen, p); q = BN_new(); BN_bin2bn(qdata, qlen, q); dmp1 = BN_new(); BN_bin2bn(e1data, e1len, dmp1); dmq1 = BN_new(); BN_bin2bn(e2data, e2len, dmq1); iqmp = BN_new(); BN_bin2bn(coeffdata, coefflen, iqmp); } *rsa = RSA_new(); #ifdef HAVE_OPAQUE_STRUCTS RSA_set0_key(*rsa, n, e, d); #else (*rsa)->e = e; (*rsa)->n = n; #endif #ifdef HAVE_OPAQUE_STRUCTS RSA_set0_factors(*rsa, p, q); #else (*rsa)->p = p; (*rsa)->q = q; #endif #ifdef HAVE_OPAQUE_STRUCTS RSA_set0_crt_params(*rsa, dmp1, dmq1, iqmp); #else (*rsa)->dmp1 = dmp1; (*rsa)->dmq1 = dmq1; (*rsa)->iqmp = iqmp; #endif return 0; } int _libssh2_rsa_sha1_verify(libssh2_rsa_ctx * rsactx, const unsigned char *sig, unsigned long sig_len, const unsigned char *m, unsigned long m_len) { unsigned char hash[SHA_DIGEST_LENGTH]; int ret; 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); return (ret == 1) ? 0 : -1; } #if LIBSSH2_DSA int _libssh2_dsa_new(libssh2_dsa_ctx ** dsactx, const unsigned char *p, unsigned long p_len, const unsigned char *q, unsigned long q_len, const unsigned char *g, unsigned long g_len, const unsigned char *y, unsigned long y_len, const unsigned char *x, unsigned long x_len) { BIGNUM * p_bn; BIGNUM * q_bn; BIGNUM * g_bn; BIGNUM * pub_key; BIGNUM * priv_key = NULL; p_bn = BN_new(); BN_bin2bn(p, p_len, p_bn); q_bn = BN_new(); BN_bin2bn(q, q_len, q_bn); g_bn = BN_new(); BN_bin2bn(g, g_len, g_bn); pub_key = BN_new(); BN_bin2bn(y, y_len, pub_key); if (x_len) { priv_key = BN_new(); BN_bin2bn(x, x_len, priv_key); } *dsactx = DSA_new(); #ifdef HAVE_OPAQUE_STRUCTS DSA_set0_pqg(*dsactx, p_bn, q_bn, g_bn); #else (*dsactx)->p = p_bn; (*dsactx)->g = g_bn; (*dsactx)->q = q_bn; #endif #ifdef HAVE_OPAQUE_STRUCTS DSA_set0_key(*dsactx, pub_key, priv_key); #else (*dsactx)->pub_key = pub_key; (*dsactx)->priv_key = priv_key; #endif return 0; } int _libssh2_dsa_sha1_verify(libssh2_dsa_ctx * dsactx, const unsigned char *sig, const unsigned char *m, unsigned long m_len) { unsigned char hash[SHA_DIGEST_LENGTH]; DSA_SIG * dsasig; BIGNUM * r; BIGNUM * s; int ret = -1; r = BN_new(); BN_bin2bn(sig, 20, r); s = BN_new(); BN_bin2bn(sig + 20, 20, s); dsasig = DSA_SIG_new(); #ifdef HAVE_OPAQUE_STRUCTS DSA_SIG_set0(dsasig, r, s); #else dsasig->r = r; dsasig->s = s; #endif if (!_libssh2_sha1(m, m_len, hash)) /* _libssh2_sha1() succeeded */ ret = DSA_do_verify(hash, SHA_DIGEST_LENGTH, dsasig, dsactx); DSA_SIG_free(dsasig); return (ret == 1) ? 0 : -1; } #endif /* LIBSSH_DSA */ int _libssh2_cipher_init(_libssh2_cipher_ctx * h, _libssh2_cipher_type(algo), unsigned char *iv, unsigned char *secret, int encrypt) { #ifdef HAVE_OPAQUE_STRUCTS *h = EVP_CIPHER_CTX_new(); return !EVP_CipherInit(*h, algo(), secret, iv, encrypt); #else EVP_CIPHER_CTX_init(h); return !EVP_CipherInit(h, algo(), secret, iv, encrypt); #endif } int _libssh2_cipher_crypt(_libssh2_cipher_ctx * ctx, _libssh2_cipher_type(algo), int encrypt, unsigned char *block, size_t blocksize) { unsigned char buf[EVP_MAX_BLOCK_LENGTH]; int ret; (void) algo; (void) encrypt; #ifdef HAVE_OPAQUE_STRUCTS ret = EVP_Cipher(*ctx, buf, block, blocksize); #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 #include 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; 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; } 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; } 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]; size_t i = 0; int outlen = 0; if (inl != 16) /* libssh2 only ever encrypt one block */ return 0; if (c == NULL) { return 0; } /* To encrypt a packet P=P1||P2||...||Pn (where P1, P2, ..., Pn are each blocks of length L), the encryptor first encrypts with 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; } for (i = 0; i < 16; i++) *out++ = *in++ ^ b1[i]; i = 15; while (c->ctr[i]++ == 0xFF) { if (i == 0) break; i--; } 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) { #ifdef HAVE_OPAQUE_STRUCTS EVP_CIPHER_CTX_free(c->aes_ctx); #else _libssh2_cipher_dtor(c->aes_ctx); free(c->aes_ctx); #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; #else static EVP_CIPHER aes_ctr_cipher; return !aes_ctr_cipher.key_len? make_ctr_evp (16, &aes_ctr_cipher, 0) : &aes_ctr_cipher; #endif } const EVP_CIPHER * _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; #else static EVP_CIPHER aes_ctr_cipher; return !aes_ctr_cipher.key_len? make_ctr_evp (24, &aes_ctr_cipher, 0) : &aes_ctr_cipher; #endif } 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; return !aes_ctr_cipher.key_len? make_ctr_evp (32, &aes_ctr_cipher, 0) : &aes_ctr_cipher; #endif } void _libssh2_init_aes_ctr(void) { _libssh2_EVP_aes_128_ctr(); _libssh2_EVP_aes_192_ctr(); _libssh2_EVP_aes_256_ctr(); } #else void _libssh2_init_aes_ctr(void) {} #endif /* LIBSSH2_AES_CTR */ /* 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; if (passphrase_len > (size - 1)) { passphrase_len = size - 1; } memcpy(buf, passphrase, passphrase_len); buf[passphrase_len] = '\0'; return passphrase_len; } typedef void * (*pem_read_bio_func)(BIO *, void **, pem_password_cb *, void * u); static int read_private_key_from_memory(void ** key_ctx, pem_read_bio_func read_private_key, const char * filedata, size_t filedata_len, unsigned const char *passphrase) { BIO * bp; *key_ctx = NULL; bp = BIO_new_mem_buf((char *)filedata, filedata_len); 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; } static int read_private_key_from_file(void ** key_ctx, pem_read_bio_func read_private_key, const char * filename, unsigned const char *passphrase) { BIO * bp; *key_ctx = NULL; bp = BIO_new_file(filename, "r"); 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; } int _libssh2_rsa_new_private_frommemory(libssh2_rsa_ctx ** rsa, LIBSSH2_SESSION * session, const char *filedata, size_t filedata_len, unsigned const char *passphrase) { 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); } int _libssh2_rsa_new_private(libssh2_rsa_ctx ** rsa, LIBSSH2_SESSION * session, const char *filename, unsigned const char *passphrase) { 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_file((void **) rsa, read_rsa, filename, passphrase); } #if LIBSSH2_DSA int _libssh2_dsa_new_private_frommemory(libssh2_dsa_ctx ** dsa, LIBSSH2_SESSION * session, const char *filedata, size_t filedata_len, unsigned const char *passphrase) { 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); } int _libssh2_dsa_new_private(libssh2_dsa_ctx ** dsa, LIBSSH2_SESSION * session, const char *filename, unsigned const char *passphrase) { 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_file((void **) dsa, read_dsa, filename, passphrase); } #endif /* LIBSSH_DSA */ 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) { int ret; unsigned char *sig; unsigned int sig_len; sig_len = RSA_size(rsactx); sig = LIBSSH2_ALLOC(session, sig_len); if (!sig) { return -1; } ret = RSA_sign(NID_sha1, hash, hash_len, sig, &sig_len, rsactx); if (!ret) { LIBSSH2_FREE(session, sig); return -1; } *signature = sig; *signature_len = sig_len; return 0; } #if LIBSSH2_DSA int _libssh2_dsa_sha1_sign(libssh2_dsa_ctx * dsactx, const unsigned char *hash, unsigned long hash_len, unsigned char *signature) { DSA_SIG *sig; const BIGNUM * r; const BIGNUM * s; int r_len, s_len; (void) hash_len; sig = DSA_do_sign(hash, SHA_DIGEST_LENGTH, dsactx); if (!sig) { return -1; } #ifdef HAVE_OPAQUE_STRUCTS DSA_SIG_get0(sig, &r, &s); #else r = sig->r; s = sig->s; #endif r_len = BN_num_bytes(r); 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) { DSA_SIG_free(sig); return -1; } memset(signature, 0, 40); BN_bn2bin(r, signature + (20 - r_len)); BN_bn2bin(s, signature + 20 + (20 - s_len)); DSA_SIG_free(sig); return 0; } #endif /* LIBSSH_DSA */ int _libssh2_sha1_init(libssh2_sha1_ctx *ctx) { #ifdef HAVE_OPAQUE_STRUCTS *ctx = EVP_MD_CTX_new(); if (*ctx == NULL) return 0; if (EVP_DigestInit(*ctx, EVP_get_digestbyname("sha1"))) return 1; EVP_MD_CTX_free(*ctx); *ctx = NULL; return 0; #else EVP_MD_CTX_init(ctx); return EVP_DigestInit(ctx, EVP_get_digestbyname("sha1")); #endif } int _libssh2_sha1(const unsigned char *message, unsigned long len, unsigned char *out) { #ifdef HAVE_OPAQUE_STRUCTS EVP_MD_CTX * ctx = EVP_MD_CTX_new(); if (ctx == NULL) return 1; /* error */ if (EVP_DigestInit(ctx, EVP_get_digestbyname("sha1"))) { EVP_DigestUpdate(ctx, message, len); EVP_DigestFinal(ctx, out, NULL); EVP_MD_CTX_free(ctx); return 0; /* success */ } EVP_MD_CTX_free(ctx); #else EVP_MD_CTX ctx; EVP_MD_CTX_init(&ctx); if (EVP_DigestInit(&ctx, EVP_get_digestbyname("sha1"))) { EVP_DigestUpdate(&ctx, message, len); EVP_DigestFinal(&ctx, out, NULL); return 0; /* success */ } #endif return 1; /* error */ } int _libssh2_sha256_init(libssh2_sha256_ctx *ctx) { #ifdef HAVE_OPAQUE_STRUCTS *ctx = EVP_MD_CTX_new(); if (*ctx == NULL) return 0; if (EVP_DigestInit(*ctx, EVP_get_digestbyname("sha256"))) return 1; EVP_MD_CTX_free(*ctx); *ctx = NULL; return 0; #else EVP_MD_CTX_init(ctx); return EVP_DigestInit(ctx, EVP_get_digestbyname("sha256")); #endif } int _libssh2_sha256(const unsigned char *message, unsigned long len, unsigned char *out) { #ifdef HAVE_OPAQUE_STRUCTS EVP_MD_CTX * ctx = EVP_MD_CTX_new(); if (ctx == NULL) return 1; /* error */ if(EVP_DigestInit(ctx, EVP_get_digestbyname("sha256"))) { EVP_DigestUpdate(ctx, message, len); EVP_DigestFinal(ctx, out, NULL); EVP_MD_CTX_free(ctx); return 0; /* success */ } EVP_MD_CTX_free(ctx); #else EVP_MD_CTX ctx; EVP_MD_CTX_init(&ctx); if(EVP_DigestInit(&ctx, EVP_get_digestbyname("sha256"))) { 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 *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 EVP_MD_CTX_init(ctx); return EVP_DigestInit(ctx, EVP_get_digestbyname("md5")); #endif } 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; } 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; } #if LIBSSH2_DSA 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; } #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) { 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"); } #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) { 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"); } #endif /* LIBSSH_DSA */ int _libssh2_pub_priv_keyfile(LIBSSH2_SESSION *session, unsigned char **method, size_t *method_len, unsigned char **pubkeydata, size_t *pubkeydata_len, const char *privatekey, const char *passphrase) { int st; BIO* bp; EVP_PKEY* pk; int pktype; _libssh2_debug(session, LIBSSH2_TRACE_AUTH, "Computing public key from private key file: %s", privatekey); bp = BIO_new_file(privatekey, "r"); 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 OPENSSL_VERSION_NUMBER < 0x10100000L 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(); } #endif BIO_reset(bp); 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"); } #ifdef HAVE_OPAQUE_STRUCTS pktype = EVP_PKEY_id(pk); #else pktype = pk->type; #endif switch (pktype) { case EVP_PKEY_RSA : st = gen_publickey_from_rsa_evp( session, method, method_len, pubkeydata, pubkeydata_len, pk); break; #if LIBSSH2_DSA case EVP_PKEY_DSA : st = gen_publickey_from_dsa_evp( session, method, method_len, pubkeydata, pubkeydata_len, pk); break; #endif /* LIBSSH_DSA */ default : st = _libssh2_error(session, LIBSSH2_ERROR_FILE, "Unable to extract public key " "from private key file: " "Unsupported private key file format"); break; } EVP_PKEY_free(pk); return st; } int _libssh2_pub_priv_keyfilememory(LIBSSH2_SESSION *session, unsigned char **method, size_t *method_len, unsigned char **pubkeydata, size_t *pubkeydata_len, const char *privatekeydata, size_t privatekeydata_len, const char *passphrase) { int st; BIO* bp; EVP_PKEY* pk; int pktype; _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; } #if OPENSSL_VERSION_NUMBER < 0x10100000L 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(); } #endif BIO_reset(bp); 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"); } #ifdef HAVE_OPAQUE_STRUCTS pktype = EVP_PKEY_id(pk); #else pktype = pk->type; #endif switch (pktype) { case EVP_PKEY_RSA : st = gen_publickey_from_rsa_evp(session, method, method_len, pubkeydata, pubkeydata_len, pk); break; #if LIBSSH2_DSA case EVP_PKEY_DSA : st = gen_publickey_from_dsa_evp(session, method, method_len, pubkeydata, pubkeydata_len, pk); break; #endif /* LIBSSH_DSA */ default : st = _libssh2_error(session, LIBSSH2_ERROR_FILE, "Unable to extract public key " "from private key file: " "Unsupported private key file format"); break; } EVP_PKEY_free(pk); return st; } #endif /* LIBSSH2_OPENSSL */