mirror of
https://github.com/libssh2/libssh2.git
synced 2025-08-05 20:55:47 +03:00
Add support for AES-GCM crypto protocols (#797)
Add support for aes256-gcm@openssh.com and aes128-gcm@openssh.com ciphers, which are the OpenSSH implementations of AES-GCM cryptography. It is similar to RFC5647 but has changes to the MAC protocol negotiation. These are implemented for recent versions of OpenSSL only. The ciphers work differently than most previous ones in two big areas: the cipher includes its own integrated MAC, and the packet length field in the SSH frame is left unencrypted. The code changes necessary are gated by flags in the LIBSSH2_CRYPT_METHOD configuration structure. These differences mean that both the first and last parts of a block require special handling during encryption. The first part is where the packet length field is, which must be kept out of the encryption path but in the authenticated part (as AAD). The last part is where the Authentication Tag is found, which is calculated and appended during encryption or removed and validated on decryption. As encryption/ decryption is performed on each packet in a loop, one block at a time, flags indicating when the first and last blocks are being processed are passed down to the encryption layers. The strict block-by-block encryption that occurs with other protocols is inappropriate for AES-GCM, since the packet length shifts the first encrypted byte 4 bytes into the block. Additionally, the final part of the block must contain the AES-GCM's Authentication Tag, so it must be presented to the lower encryption layer whole. These requirements mean added code to consolidate blocks as they are passed down. When AES-GCM is negotiated as the cipher, its built-in MAC is automatically used as the SSH MAC so further MAC negotiation is not necessary. The SSH negotiation is skipped when _libssh2_mac_override() indicates that such a cipher is in use. The virtual MAC configuration block mac_method_hmac_aesgcm is then used as the MAC placeholder. This work was sponsored by Anders Borum. Integration-patches-by: Viktor Szakats * fix checksrc errors * fix openssl.c warning * fix transport.c warnings * switch to `LIBSSH2_MIN/MAX()` from `MIN()`/`MAX()` * fix indent * fix libgcrypt unused warning * fix mbedtls unused warning * fix wincng unused warning * fix old openssl unused variable warnings * delete blank lines * updates to help merging with the ETM patch
This commit is contained in:
@@ -323,7 +323,8 @@ int _libssh2_cipher_crypt(_libssh2_cipher_ctx *ctx,
|
|||||||
_libssh2_cipher_type(algo),
|
_libssh2_cipher_type(algo),
|
||||||
int encrypt,
|
int encrypt,
|
||||||
unsigned char *block,
|
unsigned char *block,
|
||||||
size_t blocksize);
|
size_t blocksize,
|
||||||
|
int firstlast);
|
||||||
Encrypt or decrypt in-place data at (block, blocksize) using the given
|
Encrypt or decrypt in-place data at (block, blocksize) using the given
|
||||||
context and/or algorithm.
|
context and/or algorithm.
|
||||||
Return 0 if OK, else -1.
|
Return 0 if OK, else -1.
|
||||||
|
41
src/crypt.c
41
src/crypt.c
@@ -54,7 +54,7 @@
|
|||||||
*/
|
*/
|
||||||
static int
|
static int
|
||||||
crypt_none_crypt(LIBSSH2_SESSION * session, unsigned char *buf,
|
crypt_none_crypt(LIBSSH2_SESSION * session, unsigned char *buf,
|
||||||
void **abstract)
|
void **abstract, int firstlast)
|
||||||
{
|
{
|
||||||
/* Do nothing to the data! */
|
/* Do nothing to the data! */
|
||||||
return 0;
|
return 0;
|
||||||
@@ -106,12 +106,12 @@ crypt_init(LIBSSH2_SESSION * session,
|
|||||||
|
|
||||||
static int
|
static int
|
||||||
crypt_encrypt(LIBSSH2_SESSION * session, unsigned char *block,
|
crypt_encrypt(LIBSSH2_SESSION * session, unsigned char *block,
|
||||||
size_t blocksize, void **abstract)
|
size_t blocksize, void **abstract, int firstlast)
|
||||||
{
|
{
|
||||||
struct crypt_ctx *cctx = *(struct crypt_ctx **) abstract;
|
struct crypt_ctx *cctx = *(struct crypt_ctx **) abstract;
|
||||||
(void) session;
|
(void) session;
|
||||||
return _libssh2_cipher_crypt(&cctx->h, cctx->algo, cctx->encrypt, block,
|
return _libssh2_cipher_crypt(&cctx->h, cctx->algo, cctx->encrypt, block,
|
||||||
blocksize);
|
blocksize, firstlast);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
@@ -126,6 +126,34 @@ crypt_dtor(LIBSSH2_SESSION * session, void **abstract)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if LIBSSH2_AES_GCM
|
||||||
|
static const LIBSSH2_CRYPT_METHOD libssh2_crypt_method_aes256_gcm = {
|
||||||
|
"aes256-gcm@openssh.com",
|
||||||
|
"",
|
||||||
|
16, /* blocksize */
|
||||||
|
12, /* initial value length */
|
||||||
|
32, /* secret length -- 32*8 == 256bit */
|
||||||
|
LIBSSH2_CRYPT_FLAG_INTEGRATED_MAC | LIBSSH2_CRYPT_FLAG_PKTLEN_AAD,
|
||||||
|
&crypt_init,
|
||||||
|
&crypt_encrypt,
|
||||||
|
&crypt_dtor,
|
||||||
|
_libssh2_cipher_aes256gcm
|
||||||
|
};
|
||||||
|
|
||||||
|
static const LIBSSH2_CRYPT_METHOD libssh2_crypt_method_aes128_gcm = {
|
||||||
|
"aes128-gcm@openssh.com",
|
||||||
|
"",
|
||||||
|
16, /* blocksize */
|
||||||
|
12, /* initial value length */
|
||||||
|
16, /* secret length -- 16*8 == 128bit */
|
||||||
|
LIBSSH2_CRYPT_FLAG_INTEGRATED_MAC | LIBSSH2_CRYPT_FLAG_PKTLEN_AAD,
|
||||||
|
&crypt_init,
|
||||||
|
&crypt_encrypt,
|
||||||
|
&crypt_dtor,
|
||||||
|
_libssh2_cipher_aes128gcm
|
||||||
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
#if LIBSSH2_AES_CTR
|
#if LIBSSH2_AES_CTR
|
||||||
static const LIBSSH2_CRYPT_METHOD libssh2_crypt_method_aes128_ctr = {
|
static const LIBSSH2_CRYPT_METHOD libssh2_crypt_method_aes128_ctr = {
|
||||||
"aes128-ctr",
|
"aes128-ctr",
|
||||||
@@ -269,7 +297,8 @@ crypt_init_arcfour128(LIBSSH2_SESSION * session,
|
|||||||
size_t discard = 1536;
|
size_t discard = 1536;
|
||||||
for(; discard; discard -= 8)
|
for(; discard; discard -= 8)
|
||||||
_libssh2_cipher_crypt(&cctx->h, cctx->algo, cctx->encrypt, block,
|
_libssh2_cipher_crypt(&cctx->h, cctx->algo, cctx->encrypt, block,
|
||||||
method->blocksize);
|
method->blocksize, MIDDLE_BLOCK);
|
||||||
|
/* Not all middle, but here it doesn't matter */
|
||||||
}
|
}
|
||||||
|
|
||||||
return rc;
|
return rc;
|
||||||
@@ -322,6 +351,10 @@ static const LIBSSH2_CRYPT_METHOD libssh2_crypt_method_3des_cbc = {
|
|||||||
/* These are the crypt methods that are available to be negotiated. Methods
|
/* These are the crypt methods that are available to be negotiated. Methods
|
||||||
towards the start are chosen in preference to ones further down the list. */
|
towards the start are chosen in preference to ones further down the list. */
|
||||||
static const LIBSSH2_CRYPT_METHOD *_libssh2_crypt_methods[] = {
|
static const LIBSSH2_CRYPT_METHOD *_libssh2_crypt_methods[] = {
|
||||||
|
#if LIBSSH2_AES_GCM
|
||||||
|
&libssh2_crypt_method_aes256_gcm,
|
||||||
|
&libssh2_crypt_method_aes128_gcm,
|
||||||
|
#endif /* LIBSSH2_AES_GCM */
|
||||||
#if LIBSSH2_AES_CTR
|
#if LIBSSH2_AES_CTR
|
||||||
&libssh2_crypt_method_aes256_ctr,
|
&libssh2_crypt_method_aes256_ctr,
|
||||||
&libssh2_crypt_method_aes192_ctr,
|
&libssh2_crypt_method_aes192_ctr,
|
||||||
|
@@ -332,7 +332,8 @@ int _libssh2_cipher_init(_libssh2_cipher_ctx * h,
|
|||||||
|
|
||||||
int _libssh2_cipher_crypt(_libssh2_cipher_ctx * ctx,
|
int _libssh2_cipher_crypt(_libssh2_cipher_ctx * ctx,
|
||||||
_libssh2_cipher_type(algo),
|
_libssh2_cipher_type(algo),
|
||||||
int encrypt, unsigned char *block, size_t blocksize);
|
int encrypt, unsigned char *block, size_t blocksize,
|
||||||
|
int firstlast);
|
||||||
|
|
||||||
int _libssh2_pub_priv_keyfile(LIBSSH2_SESSION *session,
|
int _libssh2_pub_priv_keyfile(LIBSSH2_SESSION *session,
|
||||||
unsigned char **method,
|
unsigned char **method,
|
||||||
|
11
src/kex.c
11
src/kex.c
@@ -3582,9 +3582,18 @@ static int kex_agree_mac(LIBSSH2_SESSION * session,
|
|||||||
size_t mac_len)
|
size_t mac_len)
|
||||||
{
|
{
|
||||||
const LIBSSH2_MAC_METHOD **macp = _libssh2_mac_methods();
|
const LIBSSH2_MAC_METHOD **macp = _libssh2_mac_methods();
|
||||||
|
const LIBSSH2_MAC_METHOD *override;
|
||||||
unsigned char *s;
|
unsigned char *s;
|
||||||
(void)session;
|
(void)session;
|
||||||
|
|
||||||
|
override = _libssh2_mac_override(endpoint->crypt);
|
||||||
|
if(override) {
|
||||||
|
/* This crypto method has its own hmac method built-in, so a separate
|
||||||
|
* negotiation (and use) of a separate hmac method is unnecessary */
|
||||||
|
endpoint->mac = override;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
if(endpoint->mac_prefs) {
|
if(endpoint->mac_prefs) {
|
||||||
s = (unsigned char *) endpoint->mac_prefs;
|
s = (unsigned char *) endpoint->mac_prefs;
|
||||||
|
|
||||||
@@ -3747,6 +3756,8 @@ static int kex_agree_methods(LIBSSH2_SESSION * session, unsigned char *data,
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* This must happen after kex_agree_crypt since some MACs depend on the
|
||||||
|
negotiated crypto method */
|
||||||
if(kex_agree_mac(session, &session->local, mac_cs, mac_cs_len) ||
|
if(kex_agree_mac(session, &session->local, mac_cs, mac_cs_len) ||
|
||||||
kex_agree_mac(session, &session->remote, mac_sc, mac_sc_len)) {
|
kex_agree_mac(session, &session->remote, mac_sc, mac_sc_len)) {
|
||||||
return -1;
|
return -1;
|
||||||
|
@@ -607,11 +607,13 @@ _libssh2_cipher_init(_libssh2_cipher_ctx * h,
|
|||||||
int
|
int
|
||||||
_libssh2_cipher_crypt(_libssh2_cipher_ctx * ctx,
|
_libssh2_cipher_crypt(_libssh2_cipher_ctx * ctx,
|
||||||
_libssh2_cipher_type(algo),
|
_libssh2_cipher_type(algo),
|
||||||
int encrypt, unsigned char *block, size_t blklen)
|
int encrypt, unsigned char *block, size_t blklen,
|
||||||
|
int firstlast)
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
(void)algo;
|
(void)algo;
|
||||||
|
(void)firstlast;
|
||||||
|
|
||||||
if(encrypt) {
|
if(encrypt) {
|
||||||
ret = gcry_cipher_encrypt(*ctx, block, blklen, block, blklen);
|
ret = gcry_cipher_encrypt(*ctx, block, blklen, block, blklen);
|
||||||
|
@@ -51,6 +51,7 @@
|
|||||||
|
|
||||||
#define LIBSSH2_AES 1
|
#define LIBSSH2_AES 1
|
||||||
#define LIBSSH2_AES_CTR 1
|
#define LIBSSH2_AES_CTR 1
|
||||||
|
#define LIBSSH2_AES_GCM 0
|
||||||
#define LIBSSH2_BLOWFISH 1
|
#define LIBSSH2_BLOWFISH 1
|
||||||
#define LIBSSH2_RC4 1
|
#define LIBSSH2_RC4 1
|
||||||
#define LIBSSH2_CAST 1
|
#define LIBSSH2_CAST 1
|
||||||
|
@@ -948,12 +948,36 @@ struct _LIBSSH2_CRYPT_METHOD
|
|||||||
int *free_iv, unsigned char *secret, int *free_secret,
|
int *free_iv, unsigned char *secret, int *free_secret,
|
||||||
int encrypt, void **abstract);
|
int encrypt, void **abstract);
|
||||||
int (*crypt) (LIBSSH2_SESSION * session, unsigned char *block,
|
int (*crypt) (LIBSSH2_SESSION * session, unsigned char *block,
|
||||||
size_t blocksize, void **abstract);
|
size_t blocksize, void **abstract, int firstlast);
|
||||||
int (*dtor) (LIBSSH2_SESSION * session, void **abstract);
|
int (*dtor) (LIBSSH2_SESSION * session, void **abstract);
|
||||||
|
|
||||||
_libssh2_cipher_type(algo);
|
_libssh2_cipher_type(algo);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* Bit flags for _LIBSSH2_CRYPT_METHOD */
|
||||||
|
|
||||||
|
/* Crypto method has integrated message authentication */
|
||||||
|
#define LIBSSH2_CRYPT_FLAG_INTEGRATED_MAC 1
|
||||||
|
/* Crypto method does not encrypt the packet length */
|
||||||
|
#define LIBSSH2_CRYPT_FLAG_PKTLEN_AAD 2
|
||||||
|
|
||||||
|
/* Convenience macros for accessing crypt flags */
|
||||||
|
/* Local crypto flags */
|
||||||
|
#define CRYPT_FLAG_L(session, flag) ((session)->local.crypt && \
|
||||||
|
((session)->local.crypt->flags & LIBSSH2_CRYPT_FLAG_##flag))
|
||||||
|
/* Remote crypto flags */
|
||||||
|
#define CRYPT_FLAG_R(session, flag) ((session)->remote.crypt && \
|
||||||
|
((session)->remote.crypt->flags & LIBSSH2_CRYPT_FLAG_##flag))
|
||||||
|
|
||||||
|
/* Values for firstlast */
|
||||||
|
#define FIRST_BLOCK 1
|
||||||
|
#define MIDDLE_BLOCK 0
|
||||||
|
#define LAST_BLOCK 2
|
||||||
|
|
||||||
|
/* Convenience macros for accessing firstlast */
|
||||||
|
#define IS_FIRST(firstlast) (firstlast & FIRST_BLOCK)
|
||||||
|
#define IS_LAST(firstlast) (firstlast & LAST_BLOCK)
|
||||||
|
|
||||||
struct _LIBSSH2_COMP_METHOD
|
struct _LIBSSH2_COMP_METHOD
|
||||||
{
|
{
|
||||||
const char *name;
|
const char *name;
|
||||||
|
29
src/mac.c
29
src/mac.c
@@ -423,3 +423,32 @@ _libssh2_mac_methods(void)
|
|||||||
{
|
{
|
||||||
return mac_methods;
|
return mac_methods;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if LIBSSH2_AES_GCM
|
||||||
|
/* Stub for aes256-gcm@openssh.com crypto type, which has an integrated
|
||||||
|
HMAC method. This must not be added to mac_methods[] since it cannot be
|
||||||
|
negotiated separately. */
|
||||||
|
static const LIBSSH2_MAC_METHOD mac_method_hmac_aesgcm = {
|
||||||
|
"INTEGRATED-AES-GCM", /* made up name for display only */
|
||||||
|
16,
|
||||||
|
16,
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
};
|
||||||
|
#endif /* LIBSSH2_AES_GCM */
|
||||||
|
|
||||||
|
/* See if the negotiated crypto method has its own authentication scheme that
|
||||||
|
* obviates the need for a separate negotiated hmac method */
|
||||||
|
const LIBSSH2_MAC_METHOD *
|
||||||
|
_libssh2_mac_override(const LIBSSH2_CRYPT_METHOD *crypt)
|
||||||
|
{
|
||||||
|
#if LIBSSH2_AES_GCM
|
||||||
|
if(!strcmp(crypt->name, "aes256-gcm@openssh.com") ||
|
||||||
|
!strcmp(crypt->name, "aes128-gcm@openssh.com"))
|
||||||
|
return &mac_method_hmac_aesgcm;
|
||||||
|
#else
|
||||||
|
(void) crypt;
|
||||||
|
#endif /* LIBSSH2_AES_GCM */
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
@@ -62,5 +62,7 @@ struct _LIBSSH2_MAC_METHOD
|
|||||||
typedef struct _LIBSSH2_MAC_METHOD LIBSSH2_MAC_METHOD;
|
typedef struct _LIBSSH2_MAC_METHOD LIBSSH2_MAC_METHOD;
|
||||||
|
|
||||||
const LIBSSH2_MAC_METHOD **_libssh2_mac_methods(void);
|
const LIBSSH2_MAC_METHOD **_libssh2_mac_methods(void);
|
||||||
|
const LIBSSH2_MAC_METHOD *_libssh2_mac_override(
|
||||||
|
const LIBSSH2_CRYPT_METHOD *crypt);
|
||||||
|
|
||||||
#endif /* __LIBSSH2_MAC_H */
|
#endif /* __LIBSSH2_MAC_H */
|
||||||
|
@@ -139,7 +139,7 @@ _libssh2_mbedtls_cipher_crypt(_libssh2_cipher_ctx *ctx,
|
|||||||
_libssh2_cipher_type(algo),
|
_libssh2_cipher_type(algo),
|
||||||
int encrypt,
|
int encrypt,
|
||||||
unsigned char *block,
|
unsigned char *block,
|
||||||
size_t blocklen)
|
size_t blocklen, int firstlast)
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
unsigned char *output;
|
unsigned char *output;
|
||||||
@@ -147,6 +147,7 @@ _libssh2_mbedtls_cipher_crypt(_libssh2_cipher_ctx *ctx,
|
|||||||
|
|
||||||
(void)encrypt;
|
(void)encrypt;
|
||||||
(void)algo;
|
(void)algo;
|
||||||
|
(void)firstlast;
|
||||||
|
|
||||||
osize = blocklen + mbedtls_cipher_get_block_size(ctx);
|
osize = blocklen + mbedtls_cipher_get_block_size(ctx);
|
||||||
|
|
||||||
|
@@ -67,6 +67,7 @@
|
|||||||
|
|
||||||
#define LIBSSH2_AES 1
|
#define LIBSSH2_AES 1
|
||||||
#define LIBSSH2_AES_CTR 1
|
#define LIBSSH2_AES_CTR 1
|
||||||
|
#define LIBSSH2_AES_GCM 0
|
||||||
#ifdef MBEDTLS_CIPHER_BLOWFISH_CBC
|
#ifdef MBEDTLS_CIPHER_BLOWFISH_CBC
|
||||||
# define LIBSSH2_BLOWFISH 1
|
# define LIBSSH2_BLOWFISH 1
|
||||||
#else
|
#else
|
||||||
@@ -390,8 +391,8 @@ typedef enum {
|
|||||||
|
|
||||||
#define _libssh2_cipher_init(ctx, type, iv, secret, encrypt) \
|
#define _libssh2_cipher_init(ctx, type, iv, secret, encrypt) \
|
||||||
_libssh2_mbedtls_cipher_init(ctx, type, iv, secret, encrypt)
|
_libssh2_mbedtls_cipher_init(ctx, type, iv, secret, encrypt)
|
||||||
#define _libssh2_cipher_crypt(ctx, type, encrypt, block, blocklen) \
|
#define _libssh2_cipher_crypt(ctx, type, encrypt, block, blocklen, fl) \
|
||||||
_libssh2_mbedtls_cipher_crypt(ctx, type, encrypt, block, blocklen)
|
_libssh2_mbedtls_cipher_crypt(ctx, type, encrypt, block, blocklen, fl)
|
||||||
#define _libssh2_cipher_dtor(ctx) \
|
#define _libssh2_cipher_dtor(ctx) \
|
||||||
_libssh2_mbedtls_cipher_dtor(ctx)
|
_libssh2_mbedtls_cipher_dtor(ctx)
|
||||||
|
|
||||||
@@ -472,7 +473,7 @@ _libssh2_mbedtls_cipher_crypt(_libssh2_cipher_ctx *ctx,
|
|||||||
_libssh2_cipher_type(type),
|
_libssh2_cipher_type(type),
|
||||||
int encrypt,
|
int encrypt,
|
||||||
unsigned char *block,
|
unsigned char *block,
|
||||||
size_t blocklen);
|
size_t blocklen, int firstlast);
|
||||||
void
|
void
|
||||||
_libssh2_mbedtls_cipher_dtor(_libssh2_cipher_ctx *ctx);
|
_libssh2_mbedtls_cipher_dtor(_libssh2_cipher_ctx *ctx);
|
||||||
|
|
||||||
|
121
src/openssl.c
121
src/openssl.c
@@ -40,6 +40,7 @@
|
|||||||
|
|
||||||
#ifdef LIBSSH2_CRYPTO_C /* Compile this via crypto.c */
|
#ifdef LIBSSH2_CRYPTO_C /* Compile this via crypto.c */
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
#ifndef EVP_MAX_BLOCK_LENGTH
|
#ifndef EVP_MAX_BLOCK_LENGTH
|
||||||
@@ -481,9 +482,26 @@ _libssh2_cipher_init(_libssh2_cipher_ctx * h,
|
|||||||
unsigned char *iv, unsigned char *secret, int encrypt)
|
unsigned char *iv, unsigned char *secret, int encrypt)
|
||||||
{
|
{
|
||||||
#ifdef HAVE_OPAQUE_STRUCTS
|
#ifdef HAVE_OPAQUE_STRUCTS
|
||||||
|
#if LIBSSH2_AES_GCM
|
||||||
|
const int is_aesgcm = (algo == EVP_aes_128_gcm) ||
|
||||||
|
(algo == EVP_aes_256_gcm);
|
||||||
|
#endif /* LIBSSH2_AES_GCM */
|
||||||
|
int rc;
|
||||||
|
|
||||||
*h = EVP_CIPHER_CTX_new();
|
*h = EVP_CIPHER_CTX_new();
|
||||||
return !EVP_CipherInit(*h, algo(), secret, iv, encrypt);
|
rc = !EVP_CipherInit(*h, algo(), secret, iv, encrypt);
|
||||||
|
#if LIBSSH2_AES_GCM
|
||||||
|
if(is_aesgcm) {
|
||||||
|
/* Sets both fixed and invocation_counter parts of IV */
|
||||||
|
rc |= !EVP_CIPHER_CTX_ctrl(*h, EVP_CTRL_AEAD_SET_IV_FIXED, -1, iv);
|
||||||
|
}
|
||||||
|
#endif /* LIBSSH2_AES_GCM */
|
||||||
|
|
||||||
|
return rc;
|
||||||
#else
|
#else
|
||||||
|
# if LIBSSH2_AES_GCM
|
||||||
|
# error AES-GCM is only supported with opaque structs in use
|
||||||
|
# endif /* LIBSSH2_AES_GCM */
|
||||||
EVP_CIPHER_CTX_init(h);
|
EVP_CIPHER_CTX_init(h);
|
||||||
return !EVP_CipherInit(h, algo(), secret, iv, encrypt);
|
return !EVP_CipherInit(h, algo(), secret, iv, encrypt);
|
||||||
#endif
|
#endif
|
||||||
@@ -492,32 +510,113 @@ _libssh2_cipher_init(_libssh2_cipher_ctx * h,
|
|||||||
int
|
int
|
||||||
_libssh2_cipher_crypt(_libssh2_cipher_ctx * ctx,
|
_libssh2_cipher_crypt(_libssh2_cipher_ctx * ctx,
|
||||||
_libssh2_cipher_type(algo),
|
_libssh2_cipher_type(algo),
|
||||||
int encrypt, unsigned char *block, size_t blocksize)
|
int encrypt, unsigned char *block, size_t blocksize,
|
||||||
|
int firstlast)
|
||||||
{
|
{
|
||||||
unsigned char buf[EVP_MAX_BLOCK_LENGTH];
|
unsigned char buf[EVP_MAX_BLOCK_LENGTH];
|
||||||
int ret;
|
int ret = 1;
|
||||||
int rc = 1;
|
int rc = 1;
|
||||||
|
|
||||||
(void)algo;
|
#if LIBSSH2_AES_GCM
|
||||||
(void)encrypt;
|
const int is_aesgcm = (algo == EVP_aes_128_gcm) ||
|
||||||
|
(algo == EVP_aes_256_gcm);
|
||||||
#ifdef HAVE_OPAQUE_STRUCTS
|
char lastiv[1];
|
||||||
ret = EVP_Cipher(*ctx, buf, block, (unsigned int) blocksize);
|
|
||||||
#else
|
#else
|
||||||
ret = EVP_Cipher(ctx, buf, block, (unsigned int) blocksize);
|
const int is_aesgcm = 0;
|
||||||
|
#endif /* LIBSSH2_AES_GCM */
|
||||||
|
/* length of AES-GCM Authentication Tag */
|
||||||
|
const int authlen = is_aesgcm ? 16 : 0;
|
||||||
|
/* length of AAD, only on the first block */
|
||||||
|
const int aadlen = (is_aesgcm && IS_FIRST(firstlast)) ? 4 : 0;
|
||||||
|
/* size of AT, if present */
|
||||||
|
const int authenticationtag = IS_LAST(firstlast) ? authlen : 0;
|
||||||
|
/* length to encrypt */
|
||||||
|
const int cryptlen = (unsigned int)blocksize - aadlen - authenticationtag;
|
||||||
|
|
||||||
|
(void)algo;
|
||||||
|
|
||||||
|
assert(blocksize <= sizeof(buf));
|
||||||
|
assert(cryptlen >= 0);
|
||||||
|
|
||||||
|
#if LIBSSH2_AES_GCM
|
||||||
|
/* First block */
|
||||||
|
if(IS_FIRST(firstlast)) {
|
||||||
|
/* Increments invocation_counter portion of IV */
|
||||||
|
if(is_aesgcm) {
|
||||||
|
ret = EVP_CIPHER_CTX_ctrl(*ctx, EVP_CTRL_GCM_IV_GEN, 1, lastiv);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(aadlen) {
|
||||||
|
/* Include the 4 byte packet length as AAD */
|
||||||
|
ret = EVP_Cipher(*ctx, NULL, block, aadlen);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Last portion of block to encrypt/decrypt */
|
||||||
|
if(IS_LAST(firstlast)) {
|
||||||
|
if(is_aesgcm && !encrypt) {
|
||||||
|
/* set tag on decryption */
|
||||||
|
ret = EVP_CIPHER_CTX_ctrl(*ctx, EVP_CTRL_GCM_SET_TAG, authlen,
|
||||||
|
block + blocksize - authlen);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
(void)encrypt;
|
||||||
|
(void)firstlast;
|
||||||
|
#endif /* LIBSSH2_AES_GCM */
|
||||||
|
|
||||||
|
if(cryptlen > 0) {
|
||||||
|
#ifdef HAVE_OPAQUE_STRUCTS
|
||||||
|
ret = EVP_Cipher(*ctx, buf + aadlen, block + aadlen, cryptlen);
|
||||||
|
#else
|
||||||
|
ret = EVP_Cipher(ctx, buf + aadlen, block + aadlen, cryptlen);
|
||||||
#endif
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
#if (defined(OPENSSL_VERSION_MAJOR) && OPENSSL_VERSION_MAJOR >= 3) || \
|
#if (defined(OPENSSL_VERSION_MAJOR) && OPENSSL_VERSION_MAJOR >= 3) || \
|
||||||
defined(LIBSSH2_WOLFSSL)
|
defined(LIBSSH2_WOLFSSL)
|
||||||
if(ret != -1)
|
if(ret != -1)
|
||||||
#else
|
#else
|
||||||
if(ret == 1)
|
if(ret >= 1)
|
||||||
#endif
|
#endif
|
||||||
{
|
{
|
||||||
rc = 0;
|
rc = 0;
|
||||||
memcpy(block, buf, blocksize);
|
if(IS_LAST(firstlast)) {
|
||||||
|
/* This is the last block.
|
||||||
|
encrypt: compute tag, if applicable
|
||||||
|
decrypt: verify tag, if applicable
|
||||||
|
in!=NULL is equivalent to EVP_CipherUpdate
|
||||||
|
in==NULL is equivalent to EVP_CipherFinal */
|
||||||
|
#ifdef HAVE_OPAQUE_STRUCTS
|
||||||
|
ret = EVP_Cipher(*ctx, NULL, NULL, 0); /* final */
|
||||||
|
#else
|
||||||
|
ret = EVP_Cipher(ctx, NULL, NULL, 0); /* final */
|
||||||
|
#endif
|
||||||
|
if(ret < 0) {
|
||||||
|
ret = 0;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
ret = 1;
|
||||||
|
#if LIBSSH2_AES_GCM
|
||||||
|
if(is_aesgcm && encrypt) {
|
||||||
|
/* write the Authentication Tag a.k.a. MAC at the end
|
||||||
|
of the block */
|
||||||
|
assert(authenticationtag == authlen);
|
||||||
|
ret = EVP_CIPHER_CTX_ctrl(*ctx, EVP_CTRL_GCM_GET_TAG,
|
||||||
|
authlen, block + blocksize - authenticationtag);
|
||||||
|
}
|
||||||
|
#endif /* LIBSSH2_AES_GCM */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* Copy en/decrypted data back to the caller.
|
||||||
|
The first aadlen should not be touched because they weren't
|
||||||
|
encrypted and are unmodified. */
|
||||||
|
memcpy(block + aadlen, buf + aadlen, cryptlen);
|
||||||
|
rc = !ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* TODO: the return code should distinguish between decryption errors and
|
||||||
|
invalid MACs */
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -159,6 +159,12 @@
|
|||||||
# define LIBSSH2_AES 0
|
# define LIBSSH2_AES 0
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if (OPENSSL_VERSION_NUMBER >= 0x01010100fL && !defined(OPENSSL_NO_AES))
|
||||||
|
# define LIBSSH2_AES_GCM 1
|
||||||
|
#else
|
||||||
|
# define LIBSSH2_AES_GCM 0
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef OPENSSL_NO_BF
|
#ifdef OPENSSL_NO_BF
|
||||||
# define LIBSSH2_BLOWFISH 0
|
# define LIBSSH2_BLOWFISH 0
|
||||||
#else
|
#else
|
||||||
@@ -395,6 +401,9 @@ libssh2_curve_type;
|
|||||||
#define _libssh2_cipher_ctx EVP_CIPHER_CTX
|
#define _libssh2_cipher_ctx EVP_CIPHER_CTX
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#define _libssh2_cipher_aes256gcm EVP_aes_256_gcm
|
||||||
|
#define _libssh2_cipher_aes128gcm EVP_aes_128_gcm
|
||||||
|
|
||||||
#define _libssh2_cipher_aes256 EVP_aes_256_cbc
|
#define _libssh2_cipher_aes256 EVP_aes_256_cbc
|
||||||
#define _libssh2_cipher_aes192 EVP_aes_192_cbc
|
#define _libssh2_cipher_aes192 EVP_aes_192_cbc
|
||||||
#define _libssh2_cipher_aes128 EVP_aes_128_cbc
|
#define _libssh2_cipher_aes128 EVP_aes_128_cbc
|
||||||
|
@@ -1097,7 +1097,8 @@ _libssh2_cipher_init(_libssh2_cipher_ctx *h, _libssh2_cipher_type(algo),
|
|||||||
int
|
int
|
||||||
_libssh2_cipher_crypt(_libssh2_cipher_ctx *ctx,
|
_libssh2_cipher_crypt(_libssh2_cipher_ctx *ctx,
|
||||||
_libssh2_cipher_type(algo),
|
_libssh2_cipher_type(algo),
|
||||||
int encrypt, unsigned char *block, size_t blocksize)
|
int encrypt, unsigned char *block, size_t blocksize,
|
||||||
|
int firstlast)
|
||||||
{
|
{
|
||||||
Qus_EC_t errcode;
|
Qus_EC_t errcode;
|
||||||
int outlen;
|
int outlen;
|
||||||
|
@@ -171,6 +171,7 @@
|
|||||||
|
|
||||||
#define LIBSSH2_AES 1
|
#define LIBSSH2_AES 1
|
||||||
#define LIBSSH2_AES_CTR 1
|
#define LIBSSH2_AES_CTR 1
|
||||||
|
#define LIBSSH2_AES_GCM 0
|
||||||
#define LIBSSH2_BLOWFISH 0
|
#define LIBSSH2_BLOWFISH 0
|
||||||
#define LIBSSH2_RC4 1
|
#define LIBSSH2_RC4 1
|
||||||
#define LIBSSH2_CAST 0
|
#define LIBSSH2_CAST 0
|
||||||
|
12
src/pem.c
12
src/pem.c
@@ -260,7 +260,11 @@ _libssh2_pem_parse(LIBSSH2_SESSION * session,
|
|||||||
|
|
||||||
while(len_decrypted <= (int)*datalen - blocksize) {
|
while(len_decrypted <= (int)*datalen - blocksize) {
|
||||||
if(method->crypt(session, *data + len_decrypted, blocksize,
|
if(method->crypt(session, *data + len_decrypted, blocksize,
|
||||||
&abstract)) {
|
&abstract,
|
||||||
|
len_decrypted == 0 ? FIRST_BLOCK :
|
||||||
|
((len_decrypted == (int)*datalen - blocksize) ?
|
||||||
|
LAST_BLOCK : MIDDLE_BLOCK)
|
||||||
|
)) {
|
||||||
ret = LIBSSH2_ERROR_DECRYPT;
|
ret = LIBSSH2_ERROR_DECRYPT;
|
||||||
_libssh2_explicit_zero((char *)secret, sizeof(secret));
|
_libssh2_explicit_zero((char *)secret, sizeof(secret));
|
||||||
method->dtor(session, &abstract);
|
method->dtor(session, &abstract);
|
||||||
@@ -589,7 +593,11 @@ _libssh2_openssh_pem_parse_data(LIBSSH2_SESSION * session,
|
|||||||
while((size_t)len_decrypted <= decrypted.len - blocksize) {
|
while((size_t)len_decrypted <= decrypted.len - blocksize) {
|
||||||
if(method->crypt(session, decrypted.data + len_decrypted,
|
if(method->crypt(session, decrypted.data + len_decrypted,
|
||||||
blocksize,
|
blocksize,
|
||||||
&abstract)) {
|
&abstract,
|
||||||
|
len_decrypted == 0 ? FIRST_BLOCK : (
|
||||||
|
((size_t)len_decrypted == decrypted.len - blocksize) ?
|
||||||
|
LAST_BLOCK : MIDDLE_BLOCK)
|
||||||
|
)) {
|
||||||
ret = LIBSSH2_ERROR_DECRYPT;
|
ret = LIBSSH2_ERROR_DECRYPT;
|
||||||
method->dtor(session, &abstract);
|
method->dtor(session, &abstract);
|
||||||
goto out;
|
goto out;
|
||||||
|
151
src/transport.c
151
src/transport.c
@@ -130,18 +130,38 @@ debugdump(LIBSSH2_SESSION * session,
|
|||||||
|
|
||||||
static int
|
static int
|
||||||
decrypt(LIBSSH2_SESSION * session, unsigned char *source,
|
decrypt(LIBSSH2_SESSION * session, unsigned char *source,
|
||||||
unsigned char *dest, ssize_t len)
|
unsigned char *dest, ssize_t len, int firstlast)
|
||||||
{
|
{
|
||||||
struct transportpacket *p = &session->packet;
|
struct transportpacket *p = &session->packet;
|
||||||
int blocksize = session->remote.crypt->blocksize;
|
int blocksize = session->remote.crypt->blocksize;
|
||||||
|
|
||||||
/* if we get called with a len that isn't an even number of blocksizes
|
/* if we get called with a len that isn't an even number of blocksizes
|
||||||
we risk losing those extra bytes */
|
we risk losing those extra bytes. AAD is an exception, since those first
|
||||||
assert((len % blocksize) == 0);
|
few bytes aren't encrypted so it throws off the rest of the count. */
|
||||||
|
if(!CRYPT_FLAG_L(session, PKTLEN_AAD))
|
||||||
|
assert((len % blocksize) == 0);
|
||||||
|
|
||||||
while(len >= blocksize) {
|
while(len > 0) {
|
||||||
if(session->remote.crypt->crypt(session, source, blocksize,
|
/* normally decrypt up to blocksize bytes at a time */
|
||||||
&session->remote.crypt_abstract)) {
|
ssize_t decryptlen = LIBSSH2_MIN(blocksize, len);
|
||||||
|
/* The first block is special (since it needs to be decoded to get the
|
||||||
|
length of the remainder of the block) and takes priority. When the
|
||||||
|
length finally gets to the last blocksize bytes, and there's no
|
||||||
|
more data to come, it's the end. */
|
||||||
|
int lowerfirstlast = IS_FIRST(firstlast) ? FIRST_BLOCK :
|
||||||
|
((len <= blocksize) ? firstlast : MIDDLE_BLOCK);
|
||||||
|
/* If the last block would be less than a whole blocksize, combine it
|
||||||
|
with the previous block to make it larger. This ensures that the
|
||||||
|
whole MAC is included in a single decrypt call. */
|
||||||
|
if(CRYPT_FLAG_L(session, PKTLEN_AAD) && IS_LAST(firstlast)
|
||||||
|
&& (len < blocksize*2)) {
|
||||||
|
decryptlen = len;
|
||||||
|
lowerfirstlast = LAST_BLOCK;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(session->remote.crypt->crypt(session, source, decryptlen,
|
||||||
|
&session->remote.crypt_abstract,
|
||||||
|
lowerfirstlast)) {
|
||||||
LIBSSH2_FREE(session, p->payload);
|
LIBSSH2_FREE(session, p->payload);
|
||||||
return LIBSSH2_ERROR_DECRYPT;
|
return LIBSSH2_ERROR_DECRYPT;
|
||||||
}
|
}
|
||||||
@@ -149,11 +169,11 @@ decrypt(LIBSSH2_SESSION * session, unsigned char *source,
|
|||||||
/* if the crypt() function would write to a given address it
|
/* if the crypt() function would write to a given address it
|
||||||
wouldn't have to memcpy() and we could avoid this memcpy()
|
wouldn't have to memcpy() and we could avoid this memcpy()
|
||||||
too */
|
too */
|
||||||
memcpy(dest, source, blocksize);
|
memcpy(dest, source, decryptlen);
|
||||||
|
|
||||||
len -= blocksize; /* less bytes left */
|
len -= decryptlen; /* less bytes left */
|
||||||
dest += blocksize; /* advance write pointer */
|
dest += decryptlen; /* advance write pointer */
|
||||||
source += blocksize; /* advance read pointer */
|
source += decryptlen; /* advance read pointer */
|
||||||
}
|
}
|
||||||
return LIBSSH2_ERROR_NONE; /* all is fine */
|
return LIBSSH2_ERROR_NONE; /* all is fine */
|
||||||
}
|
}
|
||||||
@@ -174,7 +194,7 @@ fullpacket(LIBSSH2_SESSION * session, int encrypted /* 1 or 0 */ )
|
|||||||
session->fullpacket_macstate = LIBSSH2_MAC_CONFIRMED;
|
session->fullpacket_macstate = LIBSSH2_MAC_CONFIRMED;
|
||||||
session->fullpacket_payload_len = p->packet_length - 1;
|
session->fullpacket_payload_len = p->packet_length - 1;
|
||||||
|
|
||||||
if(encrypted) {
|
if(encrypted && !CRYPT_FLAG_L(session, INTEGRATED_MAC)) {
|
||||||
|
|
||||||
/* Calculate MAC hash */
|
/* Calculate MAC hash */
|
||||||
session->remote.mac->hash(session, macbuf, /* store hash here */
|
session->remote.mac->hash(session, macbuf, /* store hash here */
|
||||||
@@ -286,6 +306,7 @@ int _libssh2_transport_read(LIBSSH2_SESSION * session)
|
|||||||
int blocksize; /* minimum number of bytes we need before we can
|
int blocksize; /* minimum number of bytes we need before we can
|
||||||
use them */
|
use them */
|
||||||
int encrypted = 1; /* whether the packet is encrypted or not */
|
int encrypted = 1; /* whether the packet is encrypted or not */
|
||||||
|
int firstlast = FIRST_BLOCK; /* if the first or last block to decrypt */
|
||||||
|
|
||||||
/* default clear the bit */
|
/* default clear the bit */
|
||||||
session->socket_block_directions &= ~LIBSSH2_SESSION_BLOCK_INBOUND;
|
session->socket_block_directions &= ~LIBSSH2_SESSION_BLOCK_INBOUND;
|
||||||
@@ -423,12 +444,15 @@ int _libssh2_transport_read(LIBSSH2_SESSION * session)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if(encrypted) {
|
if(encrypted) {
|
||||||
rc = decrypt(session, &p->buf[p->readidx], block, blocksize);
|
/* first decrypted block */
|
||||||
|
rc = decrypt(session, &p->buf[p->readidx],
|
||||||
|
block, blocksize, FIRST_BLOCK);
|
||||||
if(rc != LIBSSH2_ERROR_NONE) {
|
if(rc != LIBSSH2_ERROR_NONE) {
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
/* save the first 5 bytes of the decrypted package, to be
|
/* Save the first 5 bytes of the decrypted package, to be
|
||||||
used in the hash calculation later down. */
|
used in the hash calculation later down.
|
||||||
|
This is ignored in the INTEGRATED_MAC case. */
|
||||||
memcpy(p->init, block, 5);
|
memcpy(p->init, block, 5);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@@ -451,12 +475,15 @@ int _libssh2_transport_read(LIBSSH2_SESSION * session)
|
|||||||
return LIBSSH2_ERROR_OUT_OF_BOUNDARY;
|
return LIBSSH2_ERROR_OUT_OF_BOUNDARY;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* padding_length has not been authenticated yet, but it won't
|
||||||
|
actually be used (except for the sanity check immediately
|
||||||
|
following) until after the entire packet is authenticated,
|
||||||
|
so this is safe. */
|
||||||
p->padding_length = block[4];
|
p->padding_length = block[4];
|
||||||
if(p->padding_length > p->packet_length - 1) {
|
if(p->padding_length > p->packet_length - 1) {
|
||||||
return LIBSSH2_ERROR_DECRYPT;
|
return LIBSSH2_ERROR_DECRYPT;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* total_num is the number of bytes following the initial
|
/* total_num is the number of bytes following the initial
|
||||||
(5 bytes) packet length and padding length fields */
|
(5 bytes) packet length and padding length fields */
|
||||||
total_num = p->packet_length - 1 +
|
total_num = p->packet_length - 1 +
|
||||||
@@ -524,35 +551,53 @@ int _libssh2_transport_read(LIBSSH2_SESSION * session)
|
|||||||
since it is used for the hash later on. */
|
since it is used for the hash later on. */
|
||||||
int skip = session->remote.mac->mac_len;
|
int skip = session->remote.mac->mac_len;
|
||||||
|
|
||||||
|
if(CRYPT_FLAG_R(session, INTEGRATED_MAC))
|
||||||
|
/* This crypto method DOES need the MAC to go through
|
||||||
|
decryption so it can be authenticated. */
|
||||||
|
skip = 0;
|
||||||
|
|
||||||
/* if what we have plus numbytes is bigger than the
|
/* if what we have plus numbytes is bigger than the
|
||||||
total minus the skip margin, we should lower the
|
total minus the skip margin, we should lower the
|
||||||
amount to decrypt even more */
|
amount to decrypt even more */
|
||||||
if((p->data_num + numbytes) > (p->total_num - skip)) {
|
if((p->data_num + numbytes) >= (p->total_num - skip)) {
|
||||||
numdecrypt = (p->total_num - skip) - p->data_num;
|
/* decrypt the entire rest of the package */
|
||||||
|
numdecrypt = LIBSSH2_MAX(0,
|
||||||
|
(int)(p->total_num - skip) - (int)p->data_num);
|
||||||
|
firstlast = LAST_BLOCK;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
ssize_t frac;
|
ssize_t frac;
|
||||||
numdecrypt = numbytes;
|
numdecrypt = numbytes;
|
||||||
frac = numdecrypt % blocksize;
|
frac = numdecrypt % blocksize;
|
||||||
if(frac) {
|
if(frac) {
|
||||||
/* not an aligned amount of blocks,
|
/* not an aligned amount of blocks, align it by reducing
|
||||||
align it */
|
the number of bytes processed this loop */
|
||||||
numdecrypt -= frac;
|
numdecrypt -= frac;
|
||||||
/* and make it no unencrypted data
|
/* and make it no unencrypted data
|
||||||
after it */
|
after it */
|
||||||
numbytes = 0;
|
numbytes = 0;
|
||||||
}
|
}
|
||||||
|
if(CRYPT_FLAG_R(session, INTEGRATED_MAC)) {
|
||||||
|
/* Make sure that we save enough bytes to make the last
|
||||||
|
* block large enough to hold the entire integrated MAC */
|
||||||
|
numdecrypt = LIBSSH2_MIN(numdecrypt,
|
||||||
|
(int)(p->total_num - skip - blocksize - p->data_num));
|
||||||
|
numbytes = 0;
|
||||||
|
}
|
||||||
|
firstlast = MIDDLE_BLOCK;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
/* unencrypted data should not be decrypted at all */
|
/* unencrypted data should not be decrypted at all */
|
||||||
numdecrypt = 0;
|
numdecrypt = 0;
|
||||||
}
|
}
|
||||||
|
assert(numdecrypt >= 0);
|
||||||
|
|
||||||
/* if there are bytes to decrypt, do that */
|
/* if there are bytes to decrypt, do that */
|
||||||
if(numdecrypt > 0) {
|
if(numdecrypt > 0) {
|
||||||
/* now decrypt the lot */
|
/* now decrypt the lot */
|
||||||
rc = decrypt(session, &p->buf[p->readidx], p->wptr, numdecrypt);
|
rc = decrypt(session, &p->buf[p->readidx], p->wptr, numdecrypt,
|
||||||
|
firstlast);
|
||||||
if(rc != LIBSSH2_ERROR_NONE) {
|
if(rc != LIBSSH2_ERROR_NONE) {
|
||||||
p->total_num = 0; /* no packet buffer available */
|
p->total_num = 0; /* no packet buffer available */
|
||||||
return rc;
|
return rc;
|
||||||
@@ -731,6 +776,7 @@ int _libssh2_transport_send(LIBSSH2_SESSION *session,
|
|||||||
int rc;
|
int rc;
|
||||||
const unsigned char *orgdata = data;
|
const unsigned char *orgdata = data;
|
||||||
size_t orgdata_len = data_len;
|
size_t orgdata_len = data_len;
|
||||||
|
size_t crypt_offset;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If the last read operation was interrupted in the middle of a key
|
* If the last read operation was interrupted in the middle of a key
|
||||||
@@ -829,12 +875,14 @@ int _libssh2_transport_send(LIBSSH2_SESSION *session,
|
|||||||
|
|
||||||
packet_length = data_len + 1 + 4; /* 1 is for padding_length field
|
packet_length = data_len + 1 + 4; /* 1 is for padding_length field
|
||||||
4 for the packet_length field */
|
4 for the packet_length field */
|
||||||
|
/* subtract 4 bytes of the packet_length field when padding AES-GCM */
|
||||||
|
crypt_offset = (encrypted && CRYPT_FLAG_R(session, PKTLEN_AAD)) ? 4 : 0;
|
||||||
|
|
||||||
/* at this point we have it all except the padding */
|
/* at this point we have it all except the padding */
|
||||||
|
|
||||||
/* first figure out our minimum padding amount to make it an even
|
/* first figure out our minimum padding amount to make it an even
|
||||||
block size */
|
block size */
|
||||||
padding_length = blocksize - (packet_length % blocksize);
|
padding_length = blocksize - ((packet_length - crypt_offset) % blocksize);
|
||||||
|
|
||||||
/* if the padding becomes too small we add another blocksize worth
|
/* if the padding becomes too small we add another blocksize worth
|
||||||
of it (taken from the original libssh2 where it didn't have any
|
of it (taken from the original libssh2 where it didn't have any
|
||||||
@@ -877,19 +925,62 @@ int _libssh2_transport_send(LIBSSH2_SESSION *session,
|
|||||||
/* Calculate MAC hash. Put the output at index packet_length,
|
/* Calculate MAC hash. Put the output at index packet_length,
|
||||||
since that size includes the whole packet. The MAC is
|
since that size includes the whole packet. The MAC is
|
||||||
calculated on the entire unencrypted packet, including all
|
calculated on the entire unencrypted packet, including all
|
||||||
fields except the MAC field itself. */
|
fields except the MAC field itself. This is skipped in the
|
||||||
session->local.mac->hash(session, p->outbuf + packet_length,
|
INTEGRATED_MAC case, where the crypto algorithm also does its
|
||||||
session->local.seqno, p->outbuf,
|
own hash. */
|
||||||
packet_length, NULL, 0,
|
if(!CRYPT_FLAG_R(session, INTEGRATED_MAC)) {
|
||||||
&session->local.mac_abstract);
|
session->local.mac->hash(session, p->outbuf + packet_length,
|
||||||
|
session->local.seqno, p->outbuf,
|
||||||
|
packet_length, NULL, 0,
|
||||||
|
&session->local.mac_abstract);
|
||||||
|
}
|
||||||
|
|
||||||
/* Encrypt the whole packet data, one block size at a time.
|
/* Encrypt the whole packet data, one block size at a time.
|
||||||
The MAC field is not encrypted. */
|
The MAC field is not encrypted unless INTEGRATED_MAC. */
|
||||||
for(i = 0; i < packet_length; i += session->local.crypt->blocksize) {
|
/* Some crypto back-ends could handle a single crypt() call for
|
||||||
|
encryption, but (presumably) others cannot, so break it up
|
||||||
|
into blocksize-sized chunks to satisfy them all. */
|
||||||
|
for(i = 0; i < packet_length;
|
||||||
|
i += session->local.crypt->blocksize) {
|
||||||
unsigned char *ptr = &p->outbuf[i];
|
unsigned char *ptr = &p->outbuf[i];
|
||||||
|
size_t bsize = LIBSSH2_MIN(session->local.crypt->blocksize,
|
||||||
|
(int)(packet_length-i));
|
||||||
|
/* The INTEGRATED_MAC case always has an extra call below,
|
||||||
|
so it will never be LAST_BLOCK up here. */
|
||||||
|
int firstlast = i == 0 ? FIRST_BLOCK :
|
||||||
|
(!CRYPT_FLAG_L(session, INTEGRATED_MAC)
|
||||||
|
&& (i == packet_length - session->local.crypt->blocksize)
|
||||||
|
? LAST_BLOCK: MIDDLE_BLOCK);
|
||||||
|
/* In the AAD case, the last block would be only 4 bytes
|
||||||
|
because everything is offset by 4 since the initial
|
||||||
|
packet_length isn't encrypted. In this case, combine that
|
||||||
|
last short packet with the previous one since AES-GCM
|
||||||
|
crypt() assumes that the entire MAC is available in that
|
||||||
|
packet so it can set that to the authentication tag. */
|
||||||
|
if(!CRYPT_FLAG_L(session, INTEGRATED_MAC))
|
||||||
|
if(i > packet_length - 2*bsize) {
|
||||||
|
/* increase the final block size */
|
||||||
|
bsize = packet_length - i;
|
||||||
|
/* advance the loop counter by the extra amount */
|
||||||
|
i += bsize - session->local.crypt->blocksize;
|
||||||
|
}
|
||||||
if(session->local.crypt->crypt(session, ptr,
|
if(session->local.crypt->crypt(session, ptr,
|
||||||
session->local.crypt->blocksize,
|
bsize,
|
||||||
&session->local.crypt_abstract))
|
&session->local.crypt_abstract,
|
||||||
|
firstlast))
|
||||||
|
return LIBSSH2_ERROR_ENCRYPT; /* encryption failure */
|
||||||
|
}
|
||||||
|
/* Call crypt() one last time so it can be filled in with
|
||||||
|
the MAC */
|
||||||
|
if(CRYPT_FLAG_L(session, INTEGRATED_MAC)) {
|
||||||
|
int authlen = session->local.mac->mac_len;
|
||||||
|
assert((size_t)total_length <=
|
||||||
|
packet_length + session->local.crypt->blocksize);
|
||||||
|
if(session->local.crypt->crypt(session,
|
||||||
|
&p->outbuf[packet_length],
|
||||||
|
authlen,
|
||||||
|
&session->local.crypt_abstract,
|
||||||
|
LAST_BLOCK))
|
||||||
return LIBSSH2_ERROR_ENCRYPT; /* encryption failure */
|
return LIBSSH2_ERROR_ENCRYPT; /* encryption failure */
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -2010,13 +2010,14 @@ _libssh2_wincng_cipher_crypt(_libssh2_cipher_ctx *ctx,
|
|||||||
_libssh2_cipher_type(type),
|
_libssh2_cipher_type(type),
|
||||||
int encrypt,
|
int encrypt,
|
||||||
unsigned char *block,
|
unsigned char *block,
|
||||||
size_t blocklen)
|
size_t blocklen, int firstlast)
|
||||||
{
|
{
|
||||||
unsigned char *pbOutput, *pbInput;
|
unsigned char *pbOutput, *pbInput;
|
||||||
unsigned long cbOutput, cbInput;
|
unsigned long cbOutput, cbInput;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
(void)type;
|
(void)type;
|
||||||
|
(void)firstlast;
|
||||||
|
|
||||||
cbInput = (unsigned long)blocklen;
|
cbInput = (unsigned long)blocklen;
|
||||||
|
|
||||||
|
@@ -59,6 +59,7 @@
|
|||||||
|
|
||||||
#define LIBSSH2_AES 1
|
#define LIBSSH2_AES 1
|
||||||
#define LIBSSH2_AES_CTR 1
|
#define LIBSSH2_AES_CTR 1
|
||||||
|
#define LIBSSH2_AES_GCM 0
|
||||||
#define LIBSSH2_BLOWFISH 0
|
#define LIBSSH2_BLOWFISH 0
|
||||||
#define LIBSSH2_RC4 1
|
#define LIBSSH2_RC4 1
|
||||||
#define LIBSSH2_CAST 0
|
#define LIBSSH2_CAST 0
|
||||||
@@ -377,8 +378,8 @@ struct _libssh2_wincng_cipher_type {
|
|||||||
|
|
||||||
#define _libssh2_cipher_init(ctx, type, iv, secret, encrypt) \
|
#define _libssh2_cipher_init(ctx, type, iv, secret, encrypt) \
|
||||||
_libssh2_wincng_cipher_init(ctx, type, iv, secret, encrypt)
|
_libssh2_wincng_cipher_init(ctx, type, iv, secret, encrypt)
|
||||||
#define _libssh2_cipher_crypt(ctx, type, encrypt, block, blocklen) \
|
#define _libssh2_cipher_crypt(ctx, type, encrypt, block, blocklen, fl) \
|
||||||
_libssh2_wincng_cipher_crypt(ctx, type, encrypt, block, blocklen)
|
_libssh2_wincng_cipher_crypt(ctx, type, encrypt, block, blocklen, fl)
|
||||||
#define _libssh2_cipher_dtor(ctx) \
|
#define _libssh2_cipher_dtor(ctx) \
|
||||||
_libssh2_wincng_cipher_dtor(ctx)
|
_libssh2_wincng_cipher_dtor(ctx)
|
||||||
|
|
||||||
@@ -606,7 +607,7 @@ _libssh2_wincng_cipher_crypt(_libssh2_cipher_ctx *ctx,
|
|||||||
_libssh2_cipher_type(type),
|
_libssh2_cipher_type(type),
|
||||||
int encrypt,
|
int encrypt,
|
||||||
unsigned char *block,
|
unsigned char *block,
|
||||||
size_t blocklen);
|
size_t blocklen, int firstlast);
|
||||||
void
|
void
|
||||||
_libssh2_wincng_cipher_dtor(_libssh2_cipher_ctx *ctx);
|
_libssh2_wincng_cipher_dtor(_libssh2_cipher_ctx *ctx);
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user