| #include "libssh2_priv.h" |
| |
| #ifdef LIBSSH2_MBEDTLS /* compile only if we build with mbedtls */ |
| |
| /*******************************************************************/ |
| /* |
| * mbedTLS backend: Generic functions |
| */ |
| |
| void |
| _libssh2_mbedtls_init(void) |
| { |
| int ret; |
| |
| mbedtls_entropy_init(&_libssh2_mbedtls_entropy); |
| mbedtls_ctr_drbg_init(&_libssh2_mbedtls_ctr_drbg); |
| |
| ret = mbedtls_ctr_drbg_seed(&_libssh2_mbedtls_ctr_drbg, |
| mbedtls_entropy_func, |
| &_libssh2_mbedtls_entropy, NULL, 0); |
| if (ret != 0) |
| mbedtls_ctr_drbg_free(&_libssh2_mbedtls_ctr_drbg); |
| } |
| |
| void |
| _libssh2_mbedtls_free(void) |
| { |
| mbedtls_ctr_drbg_free(&_libssh2_mbedtls_ctr_drbg); |
| mbedtls_entropy_free(&_libssh2_mbedtls_entropy); |
| } |
| |
| int |
| _libssh2_mbedtls_random(unsigned char *buf, int len) |
| { |
| int ret; |
| ret = mbedtls_ctr_drbg_random(&_libssh2_mbedtls_ctr_drbg, buf, len); |
| return ret == 0 ? 0 : -1; |
| } |
| |
| static void |
| _libssh2_mbedtls_safe_free(void *buf, int len) |
| { |
| #ifndef LIBSSH2_CLEAR_MEMORY |
| (void)len; |
| #endif |
| |
| if (!buf) |
| return; |
| |
| #ifdef LIBSSH2_CLEAR_MEMORY |
| if (len > 0) |
| memset(buf, 0, len); |
| #endif |
| |
| mbedtls_free(buf); |
| } |
| |
| int |
| _libssh2_mbedtls_cipher_init(_libssh2_cipher_ctx *ctx, |
| _libssh2_cipher_type(algo), |
| unsigned char *iv, |
| unsigned char *secret, |
| int encrypt) |
| { |
| const mbedtls_cipher_info_t *cipher_info; |
| int ret, op; |
| |
| if (!ctx) |
| return -1; |
| |
| op = encrypt == 0 ? MBEDTLS_ENCRYPT : MBEDTLS_DECRYPT; |
| |
| cipher_info = mbedtls_cipher_info_from_type(algo); |
| if(!cipher_info) |
| return -1; |
| |
| mbedtls_cipher_init(ctx); |
| ret = mbedtls_cipher_setup(ctx, cipher_info); |
| if(!ret) |
| ret = mbedtls_cipher_setkey(ctx, secret, cipher_info->key_bitlen, op); |
| |
| if(!ret) |
| ret = mbedtls_cipher_set_iv(ctx, iv, cipher_info->iv_size); |
| |
| return ret == 0 ? 0 : -1; |
| } |
| |
| int |
| _libssh2_mbedtls_cipher_crypt(_libssh2_cipher_ctx *ctx, |
| _libssh2_cipher_type(algo), |
| int encrypt, |
| unsigned char *block, |
| size_t blocklen) |
| { |
| int ret; |
| unsigned char *output; |
| size_t osize, olen, finish_olen; |
| |
| (void) encrypt; |
| (void) algo; |
| |
| osize = blocklen+mbedtls_cipher_get_block_size(ctx); |
| |
| output = (unsigned char *)mbedtls_calloc(osize, sizeof(char)); |
| if(output) |
| { |
| ret = mbedtls_cipher_reset(ctx); |
| |
| if(!ret) |
| ret = mbedtls_cipher_update(ctx, block, blocklen, output, &olen); |
| |
| if(!ret) |
| ret = mbedtls_cipher_finish(ctx, output + olen, &finish_olen); |
| |
| if (!ret) { |
| olen += finish_olen; |
| memcpy(block, output, olen); |
| } |
| |
| _libssh2_mbedtls_safe_free(output, osize); |
| } |
| else |
| ret = -1; |
| |
| return ret == 0 ? 0 : -1; |
| } |
| |
| void |
| _libssh2_mbedtls_cipher_dtor(_libssh2_cipher_ctx *ctx) |
| { |
| mbedtls_cipher_free(ctx); |
| } |
| |
| |
| int |
| _libssh2_mbedtls_hash_init(mbedtls_md_context_t *ctx, |
| mbedtls_md_type_t mdtype, |
| const unsigned char *key, unsigned long keylen) |
| { |
| const mbedtls_md_info_t *md_info; |
| int ret, hmac; |
| |
| md_info = mbedtls_md_info_from_type(mdtype); |
| if(!md_info) |
| return 0; |
| |
| hmac = key == NULL ? 0 : 1; |
| |
| mbedtls_md_init(ctx); |
| ret = mbedtls_md_setup(ctx, md_info, hmac); |
| if (!ret){ |
| if (hmac) |
| ret = mbedtls_md_hmac_starts(ctx, key, keylen); |
| else |
| ret = mbedtls_md_starts(ctx); |
| } |
| |
| return ret == 0 ? 1 : 0; |
| } |
| |
| int |
| _libssh2_mbedtls_hash_final(mbedtls_md_context_t *ctx, unsigned char *hash) |
| { |
| int ret; |
| |
| ret = mbedtls_md_finish(ctx, hash); |
| mbedtls_md_free(ctx); |
| |
| return ret == 0 ? 0 : -1; |
| } |
| |
| int |
| _libssh2_mbedtls_hash(const unsigned char *data, unsigned long datalen, |
| mbedtls_md_type_t mdtype, unsigned char *hash) |
| { |
| const mbedtls_md_info_t *md_info; |
| int ret; |
| |
| md_info = mbedtls_md_info_from_type(mdtype); |
| if(!md_info) |
| return 0; |
| |
| ret = mbedtls_md(md_info, data, datalen, hash); |
| |
| return ret == 0 ? 0 : -1; |
| } |
| |
| /*******************************************************************/ |
| /* |
| * mbedTLS backend: BigNumber functions |
| */ |
| |
| _libssh2_bn * |
| _libssh2_mbedtls_bignum_init(void) |
| { |
| _libssh2_bn *bignum; |
| |
| bignum = (_libssh2_bn *)mbedtls_calloc(1, sizeof(_libssh2_bn)); |
| if (bignum) { |
| mbedtls_mpi_init(bignum); |
| } |
| |
| return bignum; |
| } |
| |
| static int |
| _libssh2_mbedtls_bignum_random(_libssh2_bn *bn, int bits, int top, int bottom) |
| { |
| size_t len; |
| int err; |
| int i; |
| |
| if (!bn || bits <= 0) |
| return -1; |
| |
| len = (bits + 7) >> 3; |
| err = mbedtls_mpi_fill_random(bn, len, mbedtls_ctr_drbg_random, &_libssh2_mbedtls_ctr_drbg); |
| if (err) |
| return -1; |
| |
| /* Zero unsued bits above the most significant bit*/ |
| for(i=len*8-1;bits<=i;--i) { |
| err = mbedtls_mpi_set_bit(bn, i, 0); |
| if (err) |
| return -1; |
| } |
| |
| /* If `top` is -1, the most significant bit of the random number can be zero. |
| If top is 0, the most significant bit of the random number is set to 1, |
| and if top is 1, the two most significant bits of the number will be set |
| to 1, so that the product of two such random numbers will always have 2*bits length. |
| */ |
| for(i=0;i<=top;++i) { |
| err = mbedtls_mpi_set_bit(bn, bits-i-1, 1); |
| if (err) |
| return -1; |
| } |
| |
| /* make odd by setting first bit in least significant byte */ |
| if (bottom) { |
| err = mbedtls_mpi_set_bit(bn, 0, 1); |
| if (err) |
| return -1; |
| } |
| |
| return 0; |
| } |
| |
| |
| /*******************************************************************/ |
| /* |
| * mbedTLS backend: RSA functions |
| */ |
| |
| int |
| _libssh2_mbedtls_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) |
| { |
| int ret; |
| libssh2_rsa_ctx *ctx; |
| |
| ctx = (libssh2_rsa_ctx *) mbedtls_calloc(1, sizeof(libssh2_rsa_ctx)); |
| if (ctx != NULL) { |
| mbedtls_rsa_init(ctx, MBEDTLS_RSA_PKCS_V15, 0); |
| } |
| else |
| return -1; |
| |
| if( (ret = mbedtls_mpi_read_binary(&(ctx->E), edata, elen) ) != 0 || |
| (ret = mbedtls_mpi_read_binary(&(ctx->N), ndata, nlen) ) != 0 ) |
| { |
| ret = -1; |
| } |
| |
| if (!ret) |
| { |
| ctx->len = mbedtls_mpi_size(&(ctx->N)); |
| } |
| |
| if (!ret && ddata) |
| { |
| if( (ret = mbedtls_mpi_read_binary(&(ctx->D) , ddata, dlen) ) != 0 || |
| (ret = mbedtls_mpi_read_binary(&(ctx->P) , pdata, plen) ) != 0 || |
| (ret = mbedtls_mpi_read_binary(&(ctx->Q) , qdata, qlen) ) != 0 || |
| (ret = mbedtls_mpi_read_binary(&(ctx->DP), e1data, e1len) ) != 0 || |
| (ret = mbedtls_mpi_read_binary(&(ctx->DQ), e2data, e2len) ) != 0 || |
| (ret = mbedtls_mpi_read_binary(&(ctx->QP), coeffdata, coefflen) ) != 0 ) |
| { |
| ret = -1; |
| } |
| ret = mbedtls_rsa_check_privkey(ctx); |
| } |
| else if (!ret) |
| { |
| ret = mbedtls_rsa_check_pubkey(ctx); |
| } |
| |
| if (ret && ctx) { |
| _libssh2_mbedtls_rsa_free(ctx); |
| ctx = NULL; |
| } |
| *rsa = ctx; |
| return ret; |
| } |
| |
| int |
| _libssh2_mbedtls_rsa_new_private(libssh2_rsa_ctx **rsa, |
| LIBSSH2_SESSION *session, |
| const char *filename, |
| const unsigned char *passphrase) |
| { |
| int ret; |
| mbedtls_pk_context pkey; |
| |
| *rsa = (libssh2_rsa_ctx *) LIBSSH2_ALLOC(session, sizeof(libssh2_rsa_ctx)); |
| if (*rsa == NULL) |
| return -1; |
| |
| mbedtls_rsa_init(*rsa, MBEDTLS_RSA_PKCS_V15, 0); |
| mbedtls_pk_init(&pkey); |
| |
| ret = mbedtls_pk_parse_keyfile(&pkey, filename, (char *)passphrase); |
| if( ret != 0 || mbedtls_pk_get_type(&pkey) != MBEDTLS_PK_RSA) |
| { |
| mbedtls_pk_free(&pkey); |
| mbedtls_rsa_free(*rsa); |
| LIBSSH2_FREE(session, *rsa); |
| *rsa = NULL; |
| return -1; |
| } |
| |
| mbedtls_rsa_context *pk_rsa = mbedtls_pk_rsa(pkey); |
| mbedtls_rsa_copy(*rsa, pk_rsa); |
| mbedtls_pk_free(&pkey); |
| |
| return 0; |
| } |
| |
| int |
| _libssh2_mbedtls_rsa_new_private_frommemory(libssh2_rsa_ctx **rsa, |
| LIBSSH2_SESSION *session, |
| const char *filedata, |
| size_t filedata_len, |
| unsigned const char *passphrase) |
| { |
| int ret; |
| mbedtls_pk_context pkey; |
| |
| *rsa = (libssh2_rsa_ctx *) mbedtls_calloc( 1, sizeof( libssh2_rsa_ctx ) ); |
| if (*rsa == NULL) |
| return -1; |
| |
| mbedtls_pk_init(&pkey); |
| |
| ret = mbedtls_pk_parse_key(&pkey, (unsigned char *)filedata, |
| filedata_len, NULL, 0); |
| if( ret != 0 || mbedtls_pk_get_type(&pkey) != MBEDTLS_PK_RSA) |
| { |
| mbedtls_pk_free(&pkey); |
| mbedtls_rsa_free(*rsa); |
| LIBSSH2_FREE(session, *rsa); |
| *rsa = NULL; |
| return -1; |
| } |
| |
| mbedtls_rsa_context *pk_rsa = mbedtls_pk_rsa(pkey); |
| mbedtls_rsa_copy(*rsa, pk_rsa); |
| mbedtls_pk_free(&pkey); |
| |
| return 0; |
| } |
| |
| int |
| _libssh2_mbedtls_rsa_sha1_verify(libssh2_rsa_ctx *rsa, |
| const unsigned char *sig, |
| unsigned long sig_len, |
| const unsigned char *m, |
| unsigned long m_len) |
| { |
| unsigned char hash[SHA_DIGEST_LENGTH]; |
| int ret; |
| |
| ret = _libssh2_mbedtls_hash(m, m_len, MBEDTLS_MD_SHA1, hash); |
| if(ret) |
| return -1; /* failure */ |
| |
| ret = mbedtls_rsa_pkcs1_verify(rsa, NULL, NULL, MBEDTLS_RSA_PUBLIC, |
| MBEDTLS_MD_SHA1, SHA_DIGEST_LENGTH, hash, sig); |
| |
| return (ret == 0) ? 0 : -1; |
| } |
| |
| int |
| _libssh2_mbedtls_rsa_sha1_sign(LIBSSH2_SESSION *session, |
| libssh2_rsa_ctx *rsa, |
| const unsigned char *hash, |
| size_t hash_len, |
| unsigned char **signature, |
| size_t *signature_len) |
| { |
| int ret; |
| unsigned char *sig; |
| unsigned int sig_len; |
| |
| (void)hash_len; |
| |
| sig_len = rsa->len; |
| sig = LIBSSH2_ALLOC(session, sig_len); |
| if (!sig) { |
| return -1; |
| } |
| |
| ret = mbedtls_rsa_pkcs1_sign(rsa, NULL, NULL, MBEDTLS_RSA_PRIVATE, |
| MBEDTLS_MD_SHA1, SHA_DIGEST_LENGTH, |
| hash, sig); |
| if (ret) { |
| LIBSSH2_FREE(session, sig); |
| return -1; |
| } |
| |
| *signature = sig; |
| *signature_len = sig_len; |
| |
| return (ret == 0) ? 0 : -1; |
| } |
| |
| void |
| _libssh2_mbedtls_rsa_free(libssh2_rsa_ctx *ctx) |
| { |
| mbedtls_rsa_free(ctx); |
| mbedtls_free(ctx); |
| } |
| |
| static unsigned char * |
| gen_publickey_from_rsa(LIBSSH2_SESSION *session, |
| mbedtls_rsa_context *rsa, |
| size_t *keylen) |
| { |
| int e_bytes, n_bytes; |
| unsigned long len; |
| unsigned char* key; |
| unsigned char* p; |
| |
| e_bytes = mbedtls_mpi_size(&rsa->E); |
| n_bytes = mbedtls_mpi_size(&rsa->N); |
| |
| /* Key form is "ssh-rsa" + e + n. */ |
| len = 4 + 7 + 4 + e_bytes + 4 + n_bytes; |
| |
| key = LIBSSH2_ALLOC(session, len); |
| if (!key) { |
| return NULL; |
| } |
| |
| /* Process key encoding. */ |
| p = key; |
| |
| _libssh2_htonu32(p, 7); /* Key type. */ |
| p += 4; |
| memcpy(p, "ssh-rsa", 7); |
| p += 7; |
| |
| _libssh2_htonu32(p, e_bytes); |
| p += 4; |
| mbedtls_mpi_write_binary(&rsa->E, p, e_bytes); |
| |
| _libssh2_htonu32(p, n_bytes); |
| p += 4; |
| mbedtls_mpi_write_binary(&rsa->N, p, n_bytes); |
| |
| *keylen = (size_t)(p - key); |
| return key; |
| } |
| |
| static int |
| _libssh2_mbedtls_pub_priv_key(LIBSSH2_SESSION *session, |
| unsigned char **method, |
| size_t *method_len, |
| unsigned char **pubkeydata, |
| size_t *pubkeydata_len, |
| mbedtls_pk_context *pkey) |
| { |
| unsigned char *key = NULL, *mth = NULL; |
| size_t keylen = 0, mthlen = 0; |
| int ret; |
| |
| if( mbedtls_pk_get_type(pkey) != MBEDTLS_PK_RSA ) |
| { |
| mbedtls_pk_free(pkey); |
| return _libssh2_error(session, LIBSSH2_ERROR_FILE, |
| "Key type not supported"); |
| } |
| |
| // write method |
| mthlen = 7; |
| mth = LIBSSH2_ALLOC(session, mthlen); |
| if (mth) { |
| memcpy(mth, "ssh-rsa", mthlen); |
| } else { |
| ret = -1; |
| } |
| |
| mbedtls_rsa_context *rsa = mbedtls_pk_rsa(*pkey); |
| key = gen_publickey_from_rsa(session, rsa, &keylen); |
| if (key == NULL) { |
| ret = -1; |
| } |
| |
| // write output |
| if (ret) { |
| if (mth) |
| LIBSSH2_FREE(session, mth); |
| if (key) |
| LIBSSH2_FREE(session, key); |
| } else { |
| *method = mth; |
| *method_len = mthlen; |
| *pubkeydata = key; |
| *pubkeydata_len = keylen; |
| } |
| |
| return ret; |
| } |
| |
| int |
| _libssh2_mbedtls_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) |
| { |
| mbedtls_pk_context pkey; |
| char buf[1024]; |
| int ret; |
| |
| mbedtls_pk_init(&pkey); |
| ret = mbedtls_pk_parse_keyfile(&pkey, privatekey, passphrase); |
| if( ret != 0 ) |
| { |
| mbedtls_strerror(ret, (char *)buf, sizeof(buf)); |
| mbedtls_pk_free(&pkey); |
| return _libssh2_error(session, LIBSSH2_ERROR_FILE, buf); |
| } |
| |
| ret = _libssh2_mbedtls_pub_priv_key(session, method, method_len, |
| pubkeydata, pubkeydata_len, &pkey); |
| |
| mbedtls_pk_free(&pkey); |
| |
| return ret; |
| } |
| |
| int |
| _libssh2_mbedtls_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) |
| { |
| mbedtls_pk_context pkey; |
| char buf[1024]; |
| int ret; |
| |
| mbedtls_pk_init(&pkey); |
| ret = mbedtls_pk_parse_key(&pkey, (unsigned char *)privatekeydata, |
| privatekeydata_len, NULL, 0); |
| if( ret != 0 ) |
| { |
| mbedtls_strerror(ret, (char *)buf, sizeof(buf)); |
| mbedtls_pk_free(&pkey); |
| return _libssh2_error(session, LIBSSH2_ERROR_FILE, buf); |
| } |
| |
| ret = _libssh2_mbedtls_pub_priv_key(session, method, method_len, |
| pubkeydata, pubkeydata_len, &pkey); |
| |
| mbedtls_pk_free(&pkey); |
| |
| return ret; |
| } |
| |
| void _libssh2_init_aes_ctr(void) |
| { |
| /* no implementation */ |
| } |
| |
| |
| /*******************************************************************/ |
| /* |
| * mbedTLS backend: Diffie-Hellman functions |
| */ |
| |
| void |
| _libssh2_dh_init(_libssh2_dh_ctx *dhctx) |
| { |
| *dhctx = _libssh2_mbedtls_bignum_init(); /* Random from client */ |
| } |
| |
| int |
| _libssh2_dh_key_pair(_libssh2_dh_ctx *dhctx, _libssh2_bn *public, |
| _libssh2_bn *g, _libssh2_bn *p, int group_order) |
| { |
| /* Generate x and e */ |
| _libssh2_mbedtls_bignum_random(*dhctx, group_order * 8 - 1, 0, -1); |
| mbedtls_mpi_exp_mod(public, g, *dhctx, p, NULL); |
| return 0; |
| } |
| |
| int |
| _libssh2_dh_secret(_libssh2_dh_ctx *dhctx, _libssh2_bn *secret, |
| _libssh2_bn *f, _libssh2_bn *p) |
| { |
| /* Compute the shared secret */ |
| mbedtls_mpi_exp_mod(secret, f, *dhctx, p, NULL); |
| return 0; |
| } |
| |
| void |
| _libssh2_dh_dtor(_libssh2_dh_ctx *dhctx) |
| { |
| mbedtls_mpi_free(*dhctx); |
| *dhctx = NULL; |
| } |
| |
| #endif /* LIBSSH2_MBEDTLS */ |