The wolfSSL-libgcrypt integration demonstrates how a shim layer architecture can bridge two large, independently developed cryptographic libraries while maintaining API compatibility and achieving FIPS 140-3 compliance. This project specifically targets libgcrypt version 1.11.0 and the current and future work of the port can be viewed in the wolfSSL/libgcrypt-wolfssl repo.
Architecture: The Shim Layer Approach
The integration was designed as a non-invasive shim layer that keeps libgcrypt’s public API completely intact. It uses conditional compilation and dynamic function pointer assignment to redirect crypto operations to wolfSSL, all while being totally invisible to the apps using it.
Function Redirection Mechanism
At its core, the shim layer acts like a proxy, intercepting API calls and sending them to the right backend. You’ll find the main implementation in cipher/cipher.c, where the _gcry_cipher_setup_mode_ops function is the central switchboard for cipher operations.
When HAVE_WOLFSSL is defined, special preprocessor blocks containing switch statements on algorithm identifiers (like GCRY_CIPHER_AES) decide which function pointers get assigned to the cipher handle’s c->mode_ops structure. For example, if wolfSSL is enabled, an AES-GCM operation sets the encrypt function pointer to _wc_cipher_aes_gcm_encrypt instead of the usual _gcry_cipher_gcm_encrypt.
Data Structure Extension
Instead of replacing libgcrypt’s main context structures, the project extends them to keep things stable. The gcry_cipher_handle struct has new unions containing new wolfSSL-specific context structures (like wc_ccm_context_t and wc_gcm_context_t). These are only included when HAVE_WOLFSSL is defined and store state and buffers used only by the wolfSSL backend.
FIPS Compliance Through Build-Time Enforcement
The project’s FIPS 140-3 compliance model is enforced during the build process through conditional compilation. This provides stronger security guarantees than just checking at runtime. The –enable-wolfssl-fips flag defines the ENABLED_WOLFSSL_FIPS preprocessor macro and modifies algorithm lists to include only FIPS-approved primitives.
Algorithm Status with FIPS
Enabled Algorithms (wolfSSL backend):
- AES-128/192/256 (ECB, CBC, CTR, OFB modes)
- AES-GCM and AES-CCM authenticated encryption
- AES-CMAC message authentication
- RSA operations (sign, verify, encrypt, decrypt with key size ? 2048)
- ECDSA with NIST curves P-192, P-256, P-384, P-521
- SHA-1, SHA-224, SHA-256, SHA-384, SHA-512
- SHA3-224, SHA3-256, SHA3-384, SHA3-512
- HMAC with approved SHA algorithms
The codebase uses preprocessor macros like ENABLED_WOLFSSL_FIPS and HAVE_WOLFSSL to conditionally compile out entire algorithms, specific cipher modes, and non-compliant test cases. This means it’s impossible for the library to run non-approved algorithms in FIPS builds because the code simply isn’t there in the compiled binary.
Symmetric Cipher Implementation
AES Block Cipher Modes
The implementation for standard block cipher modes (ECB, CBC, OFB, CTR) in cipher/rijndael.c is a direct mapping between libgcrypt and wolfSSL APIs. Wrapper functions like _wc_aes_cbc_enc and _wc_aes_ctr_enc are just thin shims that translate libgcrypt calls into wolfSSL bulk API calls.
Necessary state (like initialization vectors and counter blocks) is managed within the wolfSSL Aes context. The wc_aes_setkey function initializes both encryption and decryption contexts within the RIJNDAEL_context struct, making sure all key schedules are ready when users set keys.
AES Authenticated Encryption (AEAD) Modes
Integrating AES-GCM and AES-CCM required solving a fundamental API mismatch. libgcrypt’s API supports streaming operations where data can be fed in incrementally.
The solution involves a buffering and re-computation strategy:
- Contextual Buffers: wolfSSL-specific context structures hold dynamically allocated buffers for AAD, plaintext, and ciphertext, plus members to track length and capacity.
- Data Accumulation: When users call libgcrypt functions, wolfSSL wrappers intercept and accumulate the data. For instance, _wc_cipher_aes_gcm_authenticate appends AAD chunks to internal buffers, resizing them as needed.
- Deferred and Repeated Execution: The actual wolfSSL function call is deferred and re-executed with each new data chunk. The _wc_cipher_aes_gcm_encrypt function passes entire accumulated buffers to wc_AesGcmEncrypt on every invocation, then calculates the correct offset into the newly generated ciphertext and copies only the portion corresponding to the most recent input.
Asymmetric Cryptography Implementation
Multi-Precision Integer (MPI) Interoperability
The biggest technical challenge was the fundamental incompatibility between libgcrypt’s gcry_mpi_t and wolfSSL’s mp_int multi-precision integer representations. A robust data marshalling layer was developed using raw byte arrays as a standardized intermediary format.
Key helper functions _libgcrypt_mpi_to_wc_mpi and _wc_mpi_to_libgcrypt_mpi handle the conversion:
- gcry_mpi_t to mp_int: Uses _gcry_mpi_aprint (with GCRYMPI_FMT_USG flag) to serialize libgcrypt MPI objects into unsigned, big-endian byte arrays, then mp_read_unsigned_bin to load values into wolfSSL mp_int structures.
- mp_int to gcry_mpi_t: Uses mp_to_unsigned_bin to serialize mp_int into byte arrays, followed by _gcry_mpi_scan to create new gcry_mpi_t objects.
Critical details include padding and alignment handling, as wolfSSL functions often need fixed-length inputs, while libgcrypt produces minimally sized arrays omitting leading zero bytes.
RSA Implementation
RSA operations in cipher/rsa.c use the MPI marshalling layer to translate key components and data before calling wolfSSL primitives. The wc_rsa_sign function extracts key components (n, e, d, p, q, u) from libgcrypt S-Expressions, converts them to wolfSSL RsaKey structures, performs signing operations via wc_RsaPSS_Sign or wc_RsaDirect, then converts results back to gcry_mpi_t objects.
ECC/ECDSA Implementation
ECDSA implementation follows the same marshalling pattern, but with additional complexities for curve identification and parameter alignment. The _gcry_ecc_ecdsa_sign function maps libgcrypt’s string-based curve names to wolfSSL integer constants, converts private keys and message hashes to byte arrays, manually right-aligns private key material into fixed-size buffers matching curve field sizes, then imports into wolfSSL ecc_key structures for signing operations.
Hashing and Message Authentication
Hash Algorithm Integration
Hash algorithm replacement was done by modifying core digest specification structures. For each algorithm, gcry_md_spec_t structures were updated (using conditional compilation) to redirect function pointers to wolfSSL wrapper functions when HAVE_WOLFSSL is defined.
Context structures like SHA256_CONTEXT were extended to include wolfSSL objects (e.g., wc_Sha256). Wrapper functions operate on these embedded contexts, with wolfssl_sha256_init calling wc_InitSha256 and wolfssl_sha256_final calling wc_Sha256Final.
HMAC and CMAC Integration
HMAC integration uses the generic hashing framework. The _gcry_md_open function checks for GCRY_MD_FLAG_HMAC and routes calls to _gcry_wc_md_open for wolfSSL context setup. The gcry_wc_md_context structure contains linked lists of GcryWcDigestEntry nodes, each holding wolfSSL Hmac contexts and algorithm identifiers, allowing single libgcrypt handles to manage multiple concurrent HMAC.
CMAC implementation in cipher/mac-cmac.c is more direct, populating wc_aes_cmac_ops structures with wolfSSL wrapper functions that call wolfSSL CMAC APIs like wc_InitCmac, wc_CmacUpdate, and wc_CmacFinal. FIPS-related modifications replaced non-FIPS-approved functions with compliant equivalents.
Questions?
If you have questions about any of the above, please contact us at facts@wolfssl.com or call +1 425 245 8247.
Download wolfSSL Now