mbedtls: add GCM support#1764
Conversation
|
@AdrianMoranMontes Sorry for the delay on this, could you please pull to master and we'll get it integrated. Thanks! |
9ce7aaa to
3061d4b
Compare
|
@willco007 Done! |
| } gcm; | ||
| #endif | ||
| } ctx; | ||
| } _libssh2_mbedtls_cipher_ctx; |
There was a problem hiding this comment.
I'd suggest converting the typedef to a struct type and referring to it
as struct <name> throughout the code.
There was a problem hiding this comment.
Pull request overview
This PR aims to add AES-128-GCM and AES-256-GCM cipher support to the mbedTLS crypto backend so libssh2 can negotiate and use aes{128,256}-gcm@openssh.com with servers requiring GCM.
Changes:
- Refactors the mbedTLS cipher handling to use a unified cipher context structure intended to support both classic ciphers and AES-GCM.
- Adds an AES-GCM encrypt/decrypt path using
mbedtls_gcm_*APIs (currently guarded by#if LIBSSH2_AES_GCM). - Updates the mbedTLS cipher init/crypt/dtor plumbing to use the new unified context.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 6 comments.
| File | Description |
|---|---|
src/mbedtls.h |
Adjusts the cipher-init macro signature (but does not currently enable/define the advertised GCM support). |
src/mbedtls.c |
Introduces a unified cipher context and implements a GCM code path alongside existing cipher handling. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| _libssh2_mbedtls_cipher_ctx *cctx = *(_libssh2_mbedtls_cipher_ctx **)ctx; | ||
|
|
||
| if(!cctx) | ||
| return; | ||
|
|
There was a problem hiding this comment.
_libssh2_mbedtls_cipher_dtor() also assumes _libssh2_cipher_ctx is a pointer-like container (*(_libssh2_mbedtls_cipher_ctx **)ctx). This has the same undefined-behavior/ABI problem as init/crypt. Once _libssh2_cipher_ctx is corrected for mbedTLS, make sure the dtor reads the context in a type-correct way and clears the stored pointer (e.g., set it to NULL) to avoid accidental double-free on repeated teardown.
| #define _libssh2_cipher_init(h, type, iv, secret, encrypt) \ | ||
| _libssh2_mbedtls_cipher_init(h, type, iv, secret, encrypt) |
There was a problem hiding this comment.
The PR description says AES-GCM is enabled for the mbedTLS backend, but in the current codebase LIBSSH2_AES_GCM is still defined as 0 in src/mbedtls.h and there are no _libssh2_cipher_aes{128,256}gcm mappings for mbedTLS. As a result, all the new GCM code in mbedtls.c is compiled out and GCM won’t actually be negotiable/usable. Please flip LIBSSH2_AES_GCM to 1, add the required mbedtls/gcm.h include, and add the cipher type mappings (consistent with how openssl.h exposes _libssh2_cipher_aes128gcm/_libssh2_cipher_aes256gcm).
|
|
||
| /* Store the context pointer */ | ||
| *(void **)h = cctx; | ||
| return 0; | ||
| } |
There was a problem hiding this comment.
_libssh2_mbedtls_cipher_init() stores cctx via *(void **)h = cctx;, but in the mbedTLS backend _libssh2_cipher_ctx is defined as mbedtls_cipher_context_t (a struct), not a pointer type. This is undefined behavior (strict aliasing/alignment) and makes the _libssh2_cipher_ctx ABI inconsistent. Fix by redefining _libssh2_cipher_ctx for mbedTLS to be a pointer type (and updating init/crypt/dtor accordingly) or by embedding the unified context directly in _libssh2_cipher_ctx.
| cctx = *(_libssh2_mbedtls_cipher_ctx **)ctx; | ||
| if(!cctx) | ||
| return -1; |
There was a problem hiding this comment.
_libssh2_mbedtls_cipher_crypt() assumes ctx points to a pointer (cctx = *(_libssh2_mbedtls_cipher_ctx **)ctx;). This depends on _libssh2_cipher_ctx being pointer-compatible storage; with the current #define _libssh2_cipher_ctx mbedtls_cipher_context_t, this is undefined behavior and can break under optimization. Fix this together with the _libssh2_cipher_ctx type issue in _libssh2_mbedtls_cipher_init().
| memcpy(block + blocksize - authlen, tag, authlen); | ||
| } | ||
| else { | ||
| if(memcmp(tag, block + blocksize - authlen, authlen) != 0) |
There was a problem hiding this comment.
In the GCM decrypt path, the authentication tag is compared with memcmp(), which is not timing-safe. This codebase already has _libssh2_timingsafe_bcmp() for MAC/tag verification; use it here as well to avoid timing side-channels on tag checks.
| if(memcmp(tag, block + blocksize - authlen, authlen) != 0) | |
| if(_libssh2_timingsafe_bcmp(tag, block + blocksize - authlen, | |
| authlen) != 0) |
| if(cryptlen > 0) { | ||
| buf = (unsigned char *)mbedtls_calloc(cryptlen, 1); | ||
| if(!buf) | ||
| return -1; | ||
| } |
There was a problem hiding this comment.
The temporary buf in the GCM path can hold plaintext during decrypt, but it’s freed with mbedtls_free() (and on some error paths too) without being wiped. Use _libssh2_mbedtls_safe_free(buf, cryptlen) (or equivalent explicit zeroization) for all frees of this buffer to avoid leaving plaintext in heap memory.
cfd1774 to
14bf697
Compare
|
@vszakats I think all your comments are now fullfiled. |
Add AES-GCM support for mbedTLS backend
Summary
This PR implements AES-128-GCM and AES-256-GCM cipher support for the mbedTLS backend, enabling connections to SSH servers that require GCM encryption algorithms.
Problem
The mbedTLS backend had GCM support disabled (
LIBSSH2_AES_GCM = 0) even though mbedTLS provides full GCM implementation through itsmbedtls_gcm_*API.Changes
mbedtls.h
#define LIBSSH2_AES_GCM 1#include <mbedtls/gcm.h>_libssh2_cipher_aes128gcmand_libssh2_cipher_aes256gcmmbedtls.c
_libssh2_mbedtls_cipher_ctxwith union to handle both regular ciphers and GCM_libssh2_mbedtls_cipher_init()to initialize GCM contexts with proper key setup_libssh2_mbedtls_cipher_crypt():_libssh2_mbedtls_cipher_dtor()to properly free GCM contextsTechnical Details
mbedtls_gcm_starts/update_ad/update/finish) rather than generic cipher APITesting
Tested successfully with SFTP servers requiring GCM-only encryption.
Related
This PR has been built including bugfix of #1758