This document shows minimal examples and explains how to use the CryptoForge API in C programs.
Note: This guide provides minimal examples for getting started. For detailed information about return codes, error handling, optional parameters, and full API documentation, see the header files in: include/CryptoForge/ Each header contains a detailed description for all public functions.
- Symbols and Conventions
- Main API Headers
- Encoders
- Hash Functions
- Message Authentication Codes (MACs)
- Key Derivation Functions (KDFs)
- Symmetric Ciphers
- AEAD Constructions
- Utilities
- Compilation and Linking
- Runtime Notes
To make examples clear, here is the following symbols and notation:
| Symbol | Meaning |
|---|---|
$ |
Shell prompt / command line input (do not type $) |
[] |
Optional argument or flag |
{} |
Mandatory choice; select one of the options separated by ` |
< > |
Placeholder for a value (e.g., <file>, <directory>) |
// |
Inline comment in C code |
CF_* |
Public CryptoForge API functions |
Example:
#include <CryptoForge/cf_hash.h>
int main(void) {
CF_HASH_CTX ctx;
const CF_HASH *hash = CF_Hash_GetByFlag(CF_SHA256);
// Initialize hashing context
CF_Hash_Init(&ctx, hash, NULL);
// Hash some data
const unsigned char data[] = "Hello, CryptoForge!";
CF_Hash_Update(&ctx, data, sizeof(data) - 1);
// Finalize and get digest
unsigned char digest[CF_SHA256_DIGEST_SIZE];
CF_Hash_Finish(&ctx, digest);
return 0;
}The CryptoForge library organizes its headers to provide a clean and modular API. Here's the overview of the main headers:
-
cf.h– The main header. Including this automatically pulls in all high-level API headers (cf_hash.h,cf_mac.h,cf_kdf.h,cf_cipher.h,cf_aead.h,cf_enc.h, etc.) for convenience.#include <cf.h>
-
cf_flags.h– Contains all flags, enums, and constants used across the library. -
cf_status.h– DefinesCF_STATUSreturn codes for all API functions.#include <CryptoForge/cf_flags.h> #include <CryptoForge/cf_status.h>
Note: Unlike normal API headers in
include/cf_api,cf_status.his ininclude/utilsand handles status codes rather than API-specific flags.
-
cf_hash.h– Provides theCF_HASHAPI for SHA, SHA3, XOF, and cSHAKE variants.#include <CryptoForge/cf_hash.h>
-
cf_mac.h– Provides theCF_MACAPI for HMAC, KMAC, AES-CMAC, AES-GMAC, and Poly1305.#include <CryptoForge/cf_mac.h>
-
cf_kdf.h– Provides theCF_KDFAPI for HKDF, PBKDF2, and KMAC-XOF based derivation.#include <CryptoForge/cf_kdf.h>
-
cf_cipher.h– Provides theCF_CipherAPI for AES, ChaCha, and XChaCha in block and stream modes.#include <CryptoForge/cf_cipher.h>
-
cf_aead.h– Provides theCF_AEADAPI for authenticated encryption modes like AES-GCM and ChaCha20-Poly1305.#include <CryptoForge/cf_aead.h>
-
cf_enc.h– Provides theCF_ENCODERAPI for Base16, Base32, Base58, Base64, Base85, Z85, and ASCII85 encoding/decoding.#include <CryptoForge/cf_enc.h>
-
cf_util.h– Provides helper functions and utilities used across modules.#include <CryptoForge/cf_util.h>
Tip: For detailed usage examples, context structures, and optional parameters, refer to each header in the include/CryptoForge/ directory.
This section provides a comprehensive reference for using the CF_ENCODER API. It focuses on practical usage, context management, encoding/decoding operations, and flag definitions, which are all defined in the cf_enc.h file.
CF_ENCODER_CTX ctx;
CF_STATUS status = CF_Enc_Init(&ctx, CF_BASE64_STD_ENC, CF_BASE64_STD_DEC);
if (status != CF_SUCCESS) {
// handle error
}CF_STATUS status;
CF_ENCODER_CTX *ctx = CF_Enc_InitAlloc(CF_BASE32_ENC | CF_BASE32_ENC_NOPAD, CF_BASE32_DEC, &status);
if (!ctx || status != CF_SUCCESS) {
// handle allocation/init error
}uint8_t data[] = {0x01, 0x02, 0x03};
char out[16];
size_t out_len = sizeof(out);
CF_STATUS status = CF_Enc_Encode(&ctx, data, sizeof(data), out, &out_len);
if (status != CF_SUCCESS) {
// handle error
}size_t out_len;
CF_STATUS status;
char *encoded = CF_Enc_EncodeAlloc(&ctx, data, sizeof(data), &out_len, &status);
if (!encoded || status != CF_SUCCESS) {
// handle error
}
free(encoded); // free when doneCF_Enc_EncodeRawandCF_Enc_EncodeAllocRawacceptvoid*input.
char encoded[] = "AQID";
uint8_t decoded[3];
size_t decoded_len = sizeof(decoded);
CF_STATUS status = CF_Enc_Decode(&ctx, encoded, strlen(encoded), decoded, &decoded_len);
if (status != CF_SUCCESS) {
// handle error
}size_t decoded_len;
CF_STATUS status;
uint8_t *decoded = CF_Enc_DecodeAlloc(&ctx, encoded, strlen(encoded), &decoded_len, &status);
if (!decoded || status != CF_SUCCESS) {
// handle error
}
free(decoded);- Raw variants accept
void*input buffers.
- Reset a context:
CF_Enc_Reset(&ctx);- Free heap-allocated context:
CF_Enc_Free(&ctx_ptr); // ctx_ptr is CF_ENCODER_CTX*- Clone context:
CF_ENCODER_CTX clone;
CF_Enc_CloneCtx(&clone, &ctx);
CF_ENCODER_CTX *clone_heap = CF_Enc_CloneCtxAlloc(&ctx, &status);- Validate context integrity:
CF_STATUS status = CF_Enc_ValidateCtx(&ctx);- Get encoder name:
const char *name = CF_Enc_GetName(&ctx);
printf("Using encoder: %s\n", name);- Compute required buffer sizes:
size_t enc_len = CF_Enc_RequiredEncLen(CF_BASE64_STD_ENC, data_len);
size_t dec_len = CF_Enc_RequiredDecLen(CF_BASE64_STD_DEC, strlen(encoded));- Minimum block sizes:
size_t min_input = CF_Enc_MinInput(&ctx);
size_t min_output = CF_Enc_MinOutput(&ctx);- Input validation (pre-decode check):
bool valid = CF_Enc_IsValidInput(CF_BASE64_STD_DEC, encoded, strlen(encoded));| Variant | Flag | Notes |
|---|---|---|
| Uppercase Hex Encode | CF_BASE16_UPPER |
0-9, A-F |
| Lowercase Hex Encode | CF_BASE16_LOWER |
0-9, a-f |
| Decimal Only | CF_BASE16_DEC |
Only 0-9 |
| Variant | Flag | Notes |
|---|---|---|
| Encode | CF_BASE32_ENC |
Standard Base32 |
| Decode | CF_BASE32_DEC |
Standard Base32 decoding |
| Encode No-Pad | CF_BASE32_ENC_NOPAD |
Omits padding |
| Decode No-Pad | CF_BASE32_DEC_NOPAD |
Accepts unpadded input |
| Variant | Flag | Notes |
|---|---|---|
| Encode | CF_BASE58_ENC |
Bitcoin-style alphabet |
| Decode | CF_BASE58_DEC |
Decodes Base58 strings |
| Variant | Flag | Notes |
|---|---|---|
| Std Encode | CF_BASE64_STD_ENC |
A-Z a-z 0-9 + / |
| Std Decode | CF_BASE64_STD_DEC |
Accepts standard Base64 |
| URL Encode | CF_BASE64_URL_ENC |
A-Z a-z 0-9 - _ |
| URL Decode | CF_BASE64_URL_DEC |
Accepts URL-safe Base64 |
| Std Encode No-Pad | CF_BASE64_NOPAD_ENC |
Omits = |
| Std Decode No-Pad | CF_BASE64_NOPAD_DEC |
Accepts unpadded input |
| Variant | Flag | Notes |
|---|---|---|
| Std ASCII85 Encode | CF_BASE85_STD_ENC |
Supports 'z' compression |
| Std ASCII85 Decode | CF_BASE85_STD_DEC |
Decodes 'z' sequences |
| Extended ASCII85 Encode | CF_BASE85_EXT_ENC |
Optional 'y' compression |
| Extended ASCII85 Decode | CF_BASE85_EXT_DEC |
Decodes 'y' sequences |
| Z85 Encode | CF_BASE85_Z85_ENC |
Z85 variant (no z/y) |
| Z85 Decode | CF_BASE85_Z85_DEC |
Decodes Z85 input |
| Ignore Whitespace | CF_BASE85_IGNORE_WS |
Skip spaces/newlines when decoding |
Note: All encoding/decoding flags shown above are defined in the cf_flags.h file (
CF_ENCODING_FLAGS).
CF_STATUS status;
CF_ENCODER_CTX *ctx = CF_Enc_InitAlloc(CF_BASE64_STD_ENC, CF_BASE64_STD_DEC, &status);
uint8_t data[] = {0xDE, 0xAD, 0xBE, 0xEF};
size_t out_len;
char *encoded = CF_Enc_EncodeAlloc(ctx, data, sizeof(data), &out_len, &status);
if (encoded && status == CF_SUCCESS) {
printf("Encoded: %.*s\n", (int)out_len, encoded);
size_t decoded_len;
uint8_t *decoded = CF_Enc_DecodeAlloc(ctx, encoded, out_len, &decoded_len, &status);
if (decoded && status == CF_SUCCESS) {
// decoded contains original bytes
free(decoded);
}
free(encoded);
}
CF_Enc_Free(&ctx);This section provides a practical guide for using the CF_HASH API, including context initialization, one-shot and streaming operations, and optional XOF/cSHAKE customization, which are all defined in the cf_hash.h file.
CF_HASH_CTX ctx;
CF_STATUS status = CF_Hash_Init(&ctx, CF_Hash_GetByFlag(CF_SHA256), NULL);
if (status != CF_SUCCESS) {
// handle error
}CF_STATUS status;
CF_HASH_CTX *ctx = CF_Hash_InitAlloc(CF_Hash_GetByFlag(CF_SHA3_512), NULL, &status);
if (!ctx || status != CF_SUCCESS) {
// handle allocation/init error
}uint8_t data_chunk[] = {0x01, 0x02, 0x03};
CF_STATUS status = CF_Hash_Update(&ctx, data_chunk, sizeof(data_chunk));
if (status != CF_SUCCESS) {
// handle error
}- Can be called multiple times before finalizing.
- Not allowed after
CF_Hash_Finalunless context is reset.
uint8_t digest[CF_SHA256_DIGEST_SIZE];
CF_STATUS status = CF_Hash_Final(&ctx, digest, sizeof(digest));
if (status != CF_SUCCESS) {
// handle error
}- For XOFs (SHAKE/rawSHAKE/cSHAKE),
digest_lencan specify a custom output length. - After finalization, the context is marked finalized.
uint8_t data[] = {0xDE, 0xAD, 0xBE, 0xEF};
uint8_t digest[CF_SHA256_DIGEST_SIZE];
CF_STATUS status = CF_Hash_Compute(
CF_Hash_GetByFlag(CF_SHA256),
data, sizeof(data),
digest, sizeof(digest),
NULL // optional CF_HASH_OPTS
);CF_Hash_ComputeFixed(CF_Hash_GetByFlag(CF_SHA256), data, sizeof(data), digest);- Reset context:
CF_Hash_Reset(&ctx);- Free heap-allocated context:
CF_Hash_Free(&ctx_ptr); // ctx_ptr is CF_HASH_CTX*- Clone context (stack or heap):
CF_HASH_CTX clone;
CF_Hash_CloneCtx(&clone, &ctx);
CF_HASH_CTX *clone_heap = CF_Hash_CloneCtxAlloc(&ctx, &status);- Validate context:
CF_Hash_ValidateCtx(&ctx);- Initialize stack options:
CF_HASH_OPTS opts;
CF_HashOpts_Init(&opts, N_bytes, N_len, S_bytes, S_len);- Heap-allocated options:
CF_HASH_OPTS *opts = CF_HashOpts_InitAlloc(N_bytes, N_len, S_bytes, S_len, &status);- Reset / Free / Clone:
CF_HashOpts_Reset(&opts);
CF_HashOpts_Free(&opts_ptr);
CF_HashOpts_Clone(&dst, &src);;
CF_HASH_OPTS *clone_heap = CF_HashOpts_CloneCtxAlloc(&src, &status);size_t digest_size = CF_Hash_GetDigestSize(CF_Hash_GetByFlag(CF_SHA256));
size_t block_size = CF_Hash_GetBlockSize(CF_Hash_GetByFlag(CF_SHA256));
const char *name = CF_Hash_GetName(CF_Hash_GetByFlag(CF_SHA256));| Algorithm | Flag | Digest Size | Block Size |
|---|---|---|---|
| MD5 | CF_MD5 |
16 | 64 |
| SHA-1 | CF_SHA1 |
20 | 64 |
| SHA-224 | CF_SHA224 |
28 | 64 |
| SHA-256 | CF_SHA256 |
32 | 64 |
| SHA-384 | CF_SHA384 |
48 | 128 |
| SHA-512 | CF_SHA512 |
64 | 128 |
| SHA3-224 | CF_SHA3_224 |
28 | 144 |
| SHA3-256 | CF_SHA3_256 |
32 | 136 |
| SHA3-384 | CF_SHA3_384 |
48 | 104 |
| SHA3-512 | CF_SHA3_512 |
64 | 72 |
| SHAKE128 | CF_SHAKE128 |
32* | 168 |
| SHAKE256 | CF_SHAKE256 |
64* | 136 |
| rawSHAKE128 | CF_RAWSHAKE128 |
32* | 168 |
| rawSHAKE256 | CF_RAWSHAKE256 |
64* | 136 |
| cSHAKE128 | CF_CSHAKE128 |
32* | 168 |
| cSHAKE256 | CF_CSHAKE256 |
64* | 136 |
* Default digest size for XOFs; can be overridden in CF_Hash_Final or CF_Hash_Compute.
Note: For a complete reference of hash algorithm flags, digest sizes, and block sizes, see the cf_flags.h file. Digest sizes are defined in
CF_DIGEST_SIZE(orCF_DIGEST_DEFAULT_SIZEfor XOFs), and block sizes are defined inCF_HASH_BLOCK_SIZE.
CF_STATUS status;
CF_HASH_CTX *hash_ctx = CF_Hash_InitAlloc(CF_SHA256, &status);
uint8_t data[] = {0xDE, 0xAD, 0xBE, 0xEF};
uint8_t digest[CF_SHA256_DIGEST_SIZE]; // 32 bytes for SHA-256
if (hash_ctx && status == CF_SUCCESS) {
status = CF_Hash_Update(hash_ctx, data, sizeof(data));
if (status == CF_SUCCESS) {
status = CF_Hash_Final(hash_ctx, digest, sizeof(digest));
if (status == CF_SUCCESS) {
printf("SHA-256 Digest: ");
for (size_t i = 0; i < sizeof(digest); i++) {
printf("%02X", digest[i]);
}
printf("\n");
}
}
CF_Hash_Free(&hash_ctx);
}This section provides a comprehensive reference for using the CF_MAC API, including context initialization, one-shot and streaming operations, optional parameters, and flag definitions, as defined in the cf_mac.h file.
CF_MAC_CTX ctx;
CF_STATUS status = CF_MAC_Init(&ctx, CF_MAC_GetByFlag(CF_HMAC), NULL, key, key_len, CF_MD5);
if (status != CF_SUCCESS) {
// handle error
}CF_STATUS status;
CF_MAC_CTX *ctx = CF_MAC_InitAlloc(CF_MAC_GetByFlag(CF_KMAC_STD), NULL, key, key_len, CF_KMAC128, &status);
if (!ctx || status != CF_SUCCESS) {
// handle allocation/init error
}uint8_t data[] = {0x01, 0x02, 0x03};
CF_STATUS status = CF_MAC_Update(&ctx, data, sizeof(data));
if (status != CF_SUCCESS) {
// handle error
}- Can be called multiple times before
CF_MAC_Final.
uint8_t tag[32]; // desired tag length
CF_STATUS status = CF_MAC_Final(&ctx, tag, sizeof(tag));uint8_t tag[CF_SHA256_DIGEST_SIZE]; // 32 bytes for SHA-256
CF_STATUS status = CF_MAC_Compute(CF_MAC_GetByFlag(CF_HMAC), key, key_len, data, sizeof(data), tag, sizeof(tag), NULL, CF_SHA256);- Reset context:
CF_MAC_Reset(&ctx);- Free heap-allocated context:
CF_MAC_Free(&ctx_ptr); // ctx_ptr is CF_MAC_CTX*- Clone context (stack or heap):
CF_MAC_CTX clone;
CF_MAC_CloneCtx(&clone, &ctx);
CF_MAC_CTX *clone_heap = CF_MAC_CloneCtxAlloc(&ctx, &status);- Validate context:
CF_MAC_ValidateCtx(&ctx);| Algorithm | Flag |
|---|---|
| HMAC | CF_HMAC |
| KMAC | CF_KMAC_STD |
| AES-CMAC | CF_AES_CMAC |
| AES-GMAC | CF_AES_GMAC |
| Poly1305 | CF_POLY1305 |
Note: HMAC variants are selected via the
subflagsparameter using hash algorithm flags (CF_HASH_FLAGS). Any non-XOF hash can be used.
KMAC variants are selected via thesubflagsparameter usingCF_KMAC_TYPE_FLAGS. Overall MAC algorithm flags are defined inCF_MAC_FLAGS.
| Type | Flag |
|---|---|
| KMAC128 | CF_KMAC128 |
| KMAC256 | CF_KMAC256 |
| KMAC_XOF128 | CF_KMAC_XOF128 |
| KMAC_XOF256 | CF_KMAC_XOF256 |
Note: KMAC variants are chosen via the
subflagsparameter. Any type, including XOF variants, is allowed. These subflags are defined inCF_KMAC_TYPE_FLAGS.
- Get MAC name:
const char *name = CF_MAC_GetName(mac);- Get full descriptive name:
const char *full = CF_MAC_GetFullName(ctx);- Validate key/tag length:
bool valid_key = CF_MAC_IsValidKeyLength(mac, key_len);
bool valid_tag = CF_MAC_IsValidTagLength(mac, tag_len);- Get valid key/tag sizes:
size_t key_count = 0;
size_t tag_count = 0;
CF_MAC_GetValidKeySizes(mac, &key_count);
CF_MAC_GetValidTagSizes(mac, &tag_count);- Stack initialization:
CF_MAC_OPTS opts;
CF_MACOpts_Init(&opts, iv, iv_len, custom, custom_len);- Heap initialization:
CF_MAC_OPTS *opts = CF_MACOpts_InitAlloc(iv, iv_len, custom, custom_len, &status);- Reset / Free / Clone:
CF_MACOpts_Reset(&opts);
CF_MACOpts_Free(&opts_ptr);
CF_MACOpts_CloneCtx(&dst, &src);
CF_MAC_OPTS *clone_heap = CF_MACOpts_CloneCtxAlloc(&src, &status);CF_STATUS status;
uint8_t key[CF_KEY_128_SIZE] = {0x00}; // example 16-byte key
uint8_t iv[12] = {0x00}; // example 96-bit IV
uint8_t data[] = {0xDE, 0xAD, 0xBE, 0xEF};
uint8_t tag[CF_TAG_128_SIZE]; // GMAC tag (AES block size)
CF_MAC_CTX *ctx = CF_MAC_InitAlloc(
CF_MAC_GetByFlag(CF_GMAC), NULL,
key, sizeof(key),
CF_AES_128, iv, sizeof(iv),
&status
);
if (ctx && status == CF_SUCCESS) {
status = CF_MAC_Update(ctx, data, sizeof(data));
if (status == CF_SUCCESS) {
status = CF_MAC_Final(ctx, tag, sizeof(tag));
if (status == CF_SUCCESS) {
printf("GMAC Tag: ");
for (size_t i = 0; i < sizeof(tag); i++) {
printf("%02X", tag[i]);
}
printf("\n");
}
}
CF_MAC_Free(&ctx);
}This section provides a comprehensive guide for using the CF_KDF API, including context initialization, one-shot and streaming operations, optional parameters, and flag definitions. All functions and structures are defined in the cf_kdf.h file.
CF_KDF_CTX ctx;
CF_STATUS status = CF_KDF_Init(
&ctx,
CF_KDF_GetByFlag(CF_HKDF),
NULL, // optional CF_KDF_OPTSS
ikm, ikm_len, // input keying material
CF_SHA256 // subflags (hash variant)
);
if (status != CF_SUCCESS) {
// handle error
}CF_STATUS status;
CF_KDF_CTX *ctx = CF_KDF_InitAlloc(
CF_KDF_GetByFlag(CF_PBKDF2),
opts, // optional CF_KDF_OPTSS
ikm, ikm_len,
CF_SHA512, // subflags
&status
);
if (!ctx || status != CF_SUCCESS) {
// handle allocation/init error
}CF_STATUS status = CF_KDF_Extract(ctx, salt, salt_len);
if (status != CF_SUCCESS) {
// handle error
}uint8_t derived_key[32];
CF_STATUS status = CF_KDF_Expand(ctx, derived_key, sizeof(derived_key));
if (status != CF_SUCCESS) {
// handle error
}uint8_t derived_key[32];
CF_STATUS status = CF_KDF_Compute(
CF_KDF_GetByFlag(CF_HKDF),
ikm, ikm_len,
salt, salt_len,
derived_key, sizeof(derived_key),
opts, CF_SHA256
);
if (status == CF_SUCCESS) {
// derived_key now contains the key material
}- Reset context:
CF_KDF_Reset(&ctx);- Free heap-allocated context:
CF_KDF_Free(&ctx_ptr); // ctx_ptr is CF_KDF_CTX*- Clone context:
CF_KDF_CTX clone;
CF_KDF_CloneCtx(&clone, &ctx);
CF_KDF_CTX *clone_heap = CF_KDF_CloneCtxAlloc(&ctx, &status);- Validate context:
CF_KDF_ValidateCtx(&ctx);- Get short name of KDF type:
const char *name = CF_KDF_GetName(CF_KDF_GetByFlag(CF_HKDF));
printf("KDF Type: %s\n", name);- Get full descriptive name of KDF context:
const char *full_name = CF_KDF_GetFullName(&ctx);
printf("Full KDF Context: %s\n", full_name);- Stack initialization:
CF_KDF_OPTS opts;
CF_KDFOpts_Init(&opts, info, info_len, custom, custom_len, iterations);- Heap initialization:
CF_KDF_OPTS *opts = CF_KDFOpts_InitAlloc(info, info_len, custom, custom_len, iterations, &status);- Set new info in KDF options:
CF_STATUS status = CF_KDFOpts_SetNewInfo(opts, new_info, new_info_len);- Reset / Free / Clone:
CF_KDFOpts_Reset(&opts);
CF_KDFOpts_Free(&opts_ptr);
CF_KDFOpts_CloneCtx(&dst, &src);
CF_KDF_OPTS *clone_heap = CF_MACOpts_CloneCtxAlloc(&src, &status);| Algorithm | Flag |
|---|---|
| HKDF | CF_HKDF |
| PBKDF2 | CF_PBKDF2 |
| KMAC XOF | CF_KMAC_XOF |
Note: KDF variants are selected via the
subflagsparameter. Hash-based variants (like HKDF or PBKDF2) useCF_HASH_FLAGSfor the underlying hash selection. KMAC-XOF variants useCF_KMAC_TYPE_FLAGSfor the subflags. Overall KDF algorithm flags are defined inCF_KDF_FLAGS.
| Type | Flag |
|---|---|
| KMAC_XOF128 | CF_KMAC_XOF128 |
| KMAC_XOF256 | CF_KMAC_XOF256 |
Note: KMAC-XOF variants are chosen via the
subflagsparameter, usingCF_KMAC_TYPE_FLAGS.
CF_STATUS status;
uint8_t ikm[] = {0x01, 0x02, 0x03, 0x04};
uint8_t salt[] = {0x0A, 0x0B, 0x0C};
uint8_t derived_key[32];
CF_KDF_CTX *ctx = CF_KDF_InitAlloc(
CF_KDF_GetByFlag(CF_HKDF),
NULL, // optional opts
ikm, sizeof(ikm),
CF_SHA256, // subflags
&status
);
if (ctx && status == CF_SUCCESS) {
status = CF_KDF_Extract(ctx, salt, sizeof(salt));
if (status == CF_SUCCESS) {
status = CF_KDF_Expand(ctx, derived_key, sizeof(derived_key));
if (status == CF_SUCCESS) {
printf("Derived Key: ");
for (size_t i = 0; i < sizeof(derived_key); i++) {
printf("%02X", derived_key[i]);
}
printf("\n");
}
}
CF_KDF_Free(&ctx);
}This section provides a comprehensive guide for using the CF_Cipher API, including context initialization, one-shot and streaming operations, optional parameters, flag definitions, and block/stream cipher behavior. All functions and structures are defined in the cf_cipher.h file.
CF_CIPHER_CTX ctx;
CF_STATUS status = CF_Cipher_Init(
&ctx,
CF_Cipher_GetByFlag(CF_AES_CBC),
opts, // optional CF_CIPHER_OPTS
key, key_len, // encryption/decryption key
CF_OP_ENCRYPT // operation mode
);
if (status != CF_SUCCESS) {
// handle error
}CF_STATUS status;
CF_CIPHER_CTX *ctx = CF_Cipher_InitAlloc(
CF_Cipher_GetByFlag(CF_CHACHA20),
opts, // optional CF_CIPHER_OPTS
key, key_len,
CF_OP_DECRYPT, // operation mode
&status
);
if (!ctx || status != CF_SUCCESS) {
// handle allocation/init error
}uint8_t out_buf[256];
size_t out_len;
CF_STATUS status = CF_Cipher_Process(&ctx, input, input_len, out_buf, &out_len);
if (status != CF_SUCCESS) {
// handle error
}Note: For AES-ECB and AES-CBC (with or without PKCS7 padding), input length must be a multiple of 16 bytes (or padded). Modes that behave like stream ciphers — AES-OFB, AES-CFB, AES-CTR, ChaCha, and XChaCha — support arbitrary input lengths.
uint8_t ciphertext[256];
size_t ct_len;
CF_STATUS status = CF_Cipher_Encrypt(
CF_Cipher_GetByFlag(CF_AES_ECB),
key, key_len,
plaintext, pt_len,
ciphertext, &ct_len,
opts
);
uint8_t plaintext_out[256];
size_t pt_out_len;
status = CF_Cipher_Decrypt(
CF_Cipher_GetByFlag(CF_AES_ECB),
key, key_len,
ciphertext, ct_len,
plaintext_out, &pt_out_len,
opts
);- Reset context:
CF_Cipher_Reset(&ctx);- Free heap-allocated context:
CF_Cipher_Free(&ctx_ptr); // ctx_ptr is CF_CIPHER_CTX*- Clone context:
CF_CIPHER_CTX clone;
CF_Cipher_CloneCtx(&clone, &ctx);
CF_CIPHER_CTX *clone_heap = CF_Cipher_CloneCtxAlloc(&ctx, &status);- Validate context:
CF_Cipher_ValidateCtx(&ctx);- Get short name of cipher:
const char *name = CF_Cipher_GetName(CF_Cipher_GetByFlag(CF_AES_CBC));
printf("Cipher: %s\n", name);- Get full name including key size:
const char *full_name = CF_Cipher_GetFullName(&ctx);
printf("Full Cipher: %s\n", full_name);- Check key length validity:
bool valid = CF_Cipher_IsValidKeyLength(CF_Cipher_GetByFlag(CF_AES_ECB), 32);- Get valid key sizes:
size_t count;
const size_t *sizes = CF_Cipher_GetValidKeySizes(CF_Cipher_GetByFlag(CF_AES_CBC), &count);- Get block size / output length:
size_t block_size = CF_Cipher_GetBlockSize(&ctx);
size_t out_len = CF_Cipher_GetOutputLength(&ctx, input_len);- Stack initialization:
CF_CipherOpts_Init(&opts, iv, iv_len, ctr_block, chacha_counter);- Heap initialization:
CF_CIPHER_OPTS *opts = CF_CipherOpts_InitAlloc(iv, iv_len, ctr_block, chacha_counter, &status);- Reset / Free / Clone:
CF_CipherOpts_Reset(&opts);
CF_CipherOpts_Free(&opts_ptr);
CF_CipherOpts_CloneCtx(&dst, &src);
CF_CIPHER_OPTS *clone_heap = CF_CipherOpts_CloneCtxAlloc(&src, &status);| Cipher | Flag | Block Size (bytes) | IV Required | IV Length (bytes) | CTR Counter | ChaCha Counter | Type | Supported Key Sizes (bytes) |
|---|---|---|---|---|---|---|---|---|
| AES-ECB | CF_AES_ECB |
16 | No | 0 | No | No | Block | 16, 24, 32 |
| AES-CBC | CF_AES_CBC |
16 | Yes | 16 | No | No | Block | 16, 24, 32 |
| AES-CBC-PKCS7 | CF_AES_CBC_PKCS7 |
16 | Yes | 16 | No | No | Block | 16, 24, 32 |
| AES-OFB | CF_AES_OFB |
16 | Yes | 16 | No | No | Stream | 16, 24, 32 |
| AES-CFB8 | CF_AES_CFB8 |
16 | Yes | 16 | No | No | Stream | 16, 24, 32 |
| AES-CFB128 | CF_AES_CFB128 |
16 | Yes | 16 | No | No | Stream | 16, 24, 32 |
| AES-CTR | CF_AES_CTR |
16 | Yes | 16 | Yes | No | Stream | 16, 24, 32 |
| ChaCha8 | CF_CHACHA8 |
0 | No | 0 | No | Yes | Stream | 32 |
| ChaCha12 | CF_CHACHA12 |
0 | No | 0 | No | Yes | Stream | 32 |
| ChaCha20 | CF_CHACHA20 |
0 | No | 0 | No | Yes | Stream | 32 |
| XChaCha8 | CF_XCHACHA8 |
0 | Yes | 24 | No | Yes | Stream | 32 |
| XChaCha12 | CF_XCHACHA12 |
0 | Yes | 24 | No | Yes | Stream | 32 |
| XChaCha20 | CF_XCHACHA20 |
0 | Yes | 24 | No | Yes | Stream | 32 |
Note: For a complete reference of cipher flags, block sizes, IV requirements, and counter usage, see the cf_flags.h file. Key sizes are defined in the
CF_KEY_SIZEenum, while cipher mode flags can be found in theCF_AES_MODE_FLAGSandCF_CHACHA_MODE_FLAGSenums.
CF_STATUS status;
uint8_t key[CF_KEY_256_SIZE] = {0};
uint8_t iv[16] = {0};
uint8_t plaintext[64] = {0};
uint8_t ciphertext[64];
size_t ct_len;
CF_CIPHER_OPTS opts;
CF_CipherOpts_Init(&opts, iv, sizeof(iv), NULL, 0);
CF_CIPHER_CTX *ctx = CF_Cipher_InitAlloc(
CF_Cipher_GetByFlag(CF_AES_CBC),
&opts,
key, sizeof(key),
CF_OP_ENCRYPT,
&status
);
if (ctx && status == CF_SUCCESS) {
status = CF_Cipher_Process(ctx, plaintext, sizeof(plaintext), ciphertext, &ct_len);
if (status == CF_SUCCESS) {
printf("Ciphertext length: %zu\n", ct_len);
}
CF_Cipher_Free(&ctx);
}This section provides a comprehensive guide for using the CF_AEAD API, including context initialization, one-shot and streaming operations, optional parameters, flag definitions, tag/key behavior, and AEAD-specific utilities. All functions and structures are defined in the cf_aead.h file.
CF_AEAD_CTX ctx;
CF_STATUS status = CF_AEAD_Init(
&ctx,
CF_AEAD_GetByFlag(CF_AES_GCM),
key, key_len,
iv, iv_len,
aad, aad_len,
CF_OP_ENCRYPT
);
if (status != CF_SUCCESS) {
// handle error
}CF_STATUS status;
CF_AEAD_CTX *ctx = CF_AEAD_InitAlloc(
CF_AEAD_GetByFlag(CF_CHACHA20_POLY1305),
key, key_len,
iv, iv_len,
aad, aad_len,
CF_OP_DECRYPT,
&status
);
if (!ctx || status != CF_SUCCESS) {
// handle allocation/init error
}uint8_t out_buf[256];
size_t out_len;
CF_STATUS status = CF_AEAD_Update(&ctx, input, input_len, out_buf, &out_len);
if (status != CF_SUCCESS) {
// handle error
}uint8_t tag[CF_AEAD_TAG_128_SIZE]; // 16-Bytes tag
status = CF_AEAD_Final(&ctx, tag, sizeof(tag));
if (status != CF_SUCCESS) {
// handle error
}Note: AEAD modes support arbitrary input lengths and require a valid authentication tag length when finalizing.
uint8_t ciphertext[256];
size_t ct_len;
uint8_t tag[CF_AEAD_TAG_128_SIZE]; // 16-Bytes tag size
CF_STATUS status = CF_AEAD_Encrypt(
CF_AEAD_GetByFlag(CF_AES_GCM),
key, key_len,
iv, iv_len,
aad, aad_len,
plaintext, pt_len,
ciphertext, &ct_len,
tag, sizeof(tag)
);
uint8_t plaintext_out[256];
size_t pt_out_len;
status = CF_AEAD_Decrypt(
CF_AEAD_GetByFlag(CF_AES_GCM),
key, key_len,
iv, iv_len,
aad, aad_len,
ciphertext, ct_len,
plaintext_out, &pt_out_len,
tag, sizeof(tag)
);uint8_t out_combined[272]; // ciphertext + tag
size_t out_len;
status = CF_AEAD_EncryptAppendTag(
CF_AEAD_GetByFlag(CF_AES_GCM),
key, key_len,
iv, iv_len,
aad, aad_len,
plaintext, pt_len,
out_combined, &out_len
);
uint8_t plaintext_out[256];
size_t pt_out_len;
status = CF_AEAD_DecryptAppendTag(
CF_AEAD_GetByFlag(CF_AES_GCM),
key, key_len,
iv, iv_len,
aad, aad_len,
out_combined, out_len,
plaintext_out, &pt_out_len
);Note: These functions internally allocate a temporary stack AEAD context and combine the authentication tag with the ciphertext. They always use the maximum tag size supported by the AEAD construction.
- Reset context:
CF_AEAD_Reset(&ctx);- Free heap-allocated context:
CF_AEAD_Free(&ctx_ptr); // ctx_ptr is CF_AEAD_CTX*- Clone context:
CF_AEAD_CTX clone;
CF_AEAD_CloneCtx(&clone, &ctx);
CF_AEAD_CTX *clone_heap = CF_AEAD_CloneCtxAlloc(&ctx, &status);- Validate context:
CF_AEAD_ValidateCtx(&ctx);- Get short name of AEAD:
const char *name = CF_AEAD_GetName(CF_AEAD_GetByFlag(CF_AES_GCM));
printf("AEAD: %s\n", name);- Get full name including key size:
const char *full_name = CF_AEAD_GetFullName(&ctx);
printf("Full AEAD: %s\n", full_name);- Check key length validity:
bool valid = CF_AEAD_IsValidKeyLength(CF_AEAD_GetByFlag(CF_AES_GCM), 32);- Check tag length validity:
bool valid_tag = CF_AEAD_IsValidTagLength(CF_AEAD_GetByFlag(CF_AES_GCM), 16);- Get valid key sizes:
size_t count;
const size_t *sizes = CF_AEAD_GetValidKeySizes(CF_AEAD_GetByFlag(CF_AES_GCM), &count);- Get valid tag sizes / max tag size:
const size_t *tag_sizes = CF_AEAD_GetValidTagSizes(CF_AEAD_GetByFlag(CF_AES_GCM), &count);
size_t max_tag = CF_AEAD_GetMaxTagSize(CF_AEAD_GetByFlag(CF_AES_GCM));| AEAD | Flag | IV Sizes (bytes) | Tag Sizes (bytes) | Key Sizes (bytes) |
|---|---|---|---|---|
| AES-GCM | CF_AES_GCM |
12 | 4, 8, 12, 16 | 16, 24, 32 |
| ChaCha20-Poly1305 | CF_CHACHA20_POLY1305 |
12 | 4, 8, 12, 16 | 32 |
| XChaCha20-Poly1305 | CF_XCHACHA20_POLY1305 |
24 | 4, 8, 12, 16 | 32 |
Note: For a complete reference of AEAD constructions, supported IVs, and counter usage, see the cf_flags.h file.
Key sizes are defined in theCF_KEY_SIZEenum, AEAD-specific flags are in theCF_AEAD_MODE_FLAGSenum, and tag sizes are defined in theCF_AEAD_TAG_SIZEenum.
CF_STATUS status;
uint8_t key[CF_KEY_256_SIZE] = {0};
uint8_t iv[12] = {0};
uint8_t aad[16] = {0};
uint8_t plaintext[64] = {0};
uint8_t ciphertext[64];
uint8_t tag[CF_AEAD_TAG_128_SIZE]; // 16-Bytes tag size
size_t ct_len;
CF_AEAD_CTX *ctx = CF_AEAD_InitAlloc(
CF_AEAD_GetByFlag(CF_AES_GCM),
key, sizeof(key),
iv, sizeof(iv),
aad, sizeof(aad),
CF_OP_ENCRYPT,
&status
);
if (ctx && status == CF_SUCCESS) {
status = CF_AEAD_Update(ctx, plaintext, sizeof(plaintext), ciphertext, &ct_len);
if (status == CF_SUCCESS) {
CF_AEAD_Final(ctx, tag, sizeof(tag));
printf("Ciphertext length: %zu, Tag length: %zu\n", ct_len, sizeof(tag));
}
CF_AEAD_Free(&ctx);
}This section provides a detailed guide for using the CF_UTILS API, including constant-time comparisons, lexicographical operations, entropy generation, and other utility functions. All functions are declared in the cf_utils.h file.
uint8_t buf1[32] = {0};
uint8_t buf2[32] = {0};
int cmp = CF_Compare(buf1, buf2, sizeof(buf1));
if (cmp == 1) {
printf("Buffers are equal\n");
} else if (cmp == 0) {
printf("Buffers differ\n");
} else {
printf("Error occurred: %d\n", cmp);
}Note:
CF_Compareensures a constant-time comparison to mitigate timing attacks. Returns1for equality,0for inequality, and negative values for errors.
uint8_t a[16] = {0x01, 0x02};
uint8_t b[16] = {0x01, 0x03};
int lex_cmp = CF_CompareLex(a, b, sizeof(a));
if (lex_cmp < 0) {
printf("a < b\n");
} else if (lex_cmp == 0) {
printf("a == b\n");
} else {
printf("a > b\n");
}Note:
CF_CompareLexperforms a constant-time lexicographical comparison. Returns-1ifa < b,0if equal,1ifa > b, and negative values for errors.
uint8_t entropy[64];
CF_STATUS status = CF_EntropyFromOS(entropy, sizeof(entropy));
if (status != CF_SUCCESS) {
printf("Failed to generate entropy: %d\n", status);
} else {
printf("Entropy generated successfully\n");
}Note:
- On Linux: uses
getrandom()if available, falling back to/dev/urandom.- On Windows: uses
BCryptGenRandomon MSVC, and falls back to the older CryptoAPI (CryptGenRandom) on other compilers.- Returns
CF_SUCCESSon success, or error codes such asCF_ERR_NULL_PTRorCF_ERR_OS_FAIL.
CF_STATUS status;
uint8_t buf1[32] = {0};
uint8_t buf2[32] = {0};
uint8_t entropy[32];
// Generate entropy
status = CF_EntropyFromOS(entropy, sizeof(entropy));
if (status != CF_SUCCESS) {
// handle error
}
// Compare buffers
int cmp = CF_Compare(buf1, buf2, sizeof(buf1));
if (cmp == 1) {
printf("Buffers match\n");
} else if (cmp == 0) {
printf("Buffers differ\n");
}
// Lexicographical comparison
int lex_cmp = CF_CompareLex(buf1, buf2, sizeof(buf1));
printf("Lexicographical comparison result: %d\n", lex_cmp);| Function | Error Codes | Description |
|---|---|---|
CF_Compare |
<0 |
General error (CF_ERR_* code) |
CF_CompareLex |
<0 |
General error (CF_ERR_* code) |
CF_EntropyFromOS |
CF_ERR_NULL_PTR |
Buffer pointer is NULL or length is zero |
CF_ERR_OS_FAIL |
OS entropy source failed |
Note: All CryptoForge utility functions are designed for safety, timing-attack resistance, and cross-platform compatibility.
This guide covers compiling and linking programs with CryptoForge on various platforms and compilers, including dynamic linking examples.
gcc main.c -IC:/path/to/CryptoForge/include -L/path/to/lib -lcryptoforge -o main.execl main.c /I C:\path\to\CryptoForge\include /link /LIBPATH:C:\path\to\lib cryptoforge.lib /OUT:main.execlang main.c -IC:/path/to/CryptoForge/include -LC:/path/to/lib -lcryptoforge -o main.exe- When building the library, define an output binary location to make linking easier.
- If your text editor doesn't properly show or locate headers, coding and referencing becomes harder. Recommended approaches:
- Keep this guide open side by side while coding.
- Or copy the headers into your project folder to simplify access.
- All algorithms are fully endian-safe. Both little- and big-endian platforms are supported transparently.
- Functions with the
*Allocprefix return heap-allocated buffers; the caller is responsible for freeing them. - Functions without
*Alloc(e.g., initialization, cloning) use user-provided memory; the library does not free these buffers.
- Descriptor contexts (
CF_HASH,CF_ENCODER,CF_MAC,CF_KDF,CF_CIPHER,CF_AEAD) are always thread-safe. - Cloned contexts are safe for concurrent use across threads.
- Original contexts that are not cloned are not safe for simultaneous access.
- All contexts are automatically reset during initialization, even if the user did not manually reset them.
- Internal state is securely zeroed and managed automatically before context destruction.
- Each
CF_*_CTXcontains a magic value for integrity verification. - For
CF_NAME_CTX, the magic is XORed with the associated descriptor; functions will reject tampered contexts. - For
CF_NAME_OPTS, the magic value is used directly for verification.
- Contexts cannot be reused after finalization functions (
Finish,Finalize,EncryptFinal, etc.). - Functions meant to run once cannot be executed twice on the same context.
- Exceptions: XOF-based hashes, MACs, and KDFs can be “expanded” or squeezed multiple times after finalization.
- All sensitive buffers and internal contexts are zeroed automatically.
- Function pointers and internal descriptors are validated via the magic value before use, preventing tampered contexts from executing.