Native cryptography integration

Use M7 C Crypto when a C application needs explicit key, signature or RSA-OAEP operations. This guide describes the 0.3.0 source candidate; consult installation and release status first. No provider registration or network account is required for local operations.

Entry points and algorithms

Include m7crypto.h and use these exported getters:

Getter Operations
get_lib_m7crypto() key.load_private_pem, key.load_public_pem, key.load_secret, key.free; family sign/verify; buffer.free
get_lib_m7crypto_keygen() generate, export_private_pem, export_public_pem
get_lib_m7crypto_encryption() rsa_oaep.encrypt, rsa_oaep.decrypt

The signing tables recognize 18 profiles:

Table Exact algorithm identifiers
rsa RS256, RS384, RS512
pss PS256, PS384, PS512
ecdsa ES256, ES384, ES512, ES256K
eddsa Ed25519, Ed448
hmac HS256, HS384, HS512
ml_dsa ML-DSA-44, ML-DSA-65, ML-DSA-87

Select an exact algorithm through trusted application policy. Unknown names, the ambiguous EdDSA alias, wrong key types and unavailable providers fail; there is no algorithm fallback. This list is a library contract, not an identity provider's discovery document or permission to enable an algorithm for a client.

Sign and verify raw message bytes, not a precomputed digest. ECDSA output is fixed-width R || S, not DER. EdDSA and ML-DSA use pure mode with an empty context. Inputs and outputs have explicit lengths and may contain NUL bytes.

Minimal sign and verify example

Save as example.c. The key is generated for this example and never printed.

#include <m7crypto.h>

int main(void)
{
    const M7CryptoLib *crypto = get_lib_m7crypto();
    const M7CryptoKeyGenLib *keygen = get_lib_m7crypto_keygen();
    M7CryptoKey *key = NULL;
    M7CryptoBuffer signature = {0};
    M7CryptoError error = {0};
    const char message[] = "example payload";
    int result = 1;

    if (keygen->generate("RS256", 2048, &key, &error) != M7CRYPTO_OK)
        goto cleanup;
    if (crypto->rsa.sign(key, "RS256", message, sizeof(message) - 1,
                         &signature, &error) != M7CRYPTO_OK)
        goto cleanup;
    if (crypto->rsa.verify(key, "RS256", message, sizeof(message) - 1,
                           signature.data, signature.length, &error) != M7CRYPTO_OK)
        goto cleanup;
    result = 0;

cleanup:
    crypto->buffer.free(&signature);
    crypto->key.free(&key);
    return result;
}

With the absolute m7_crypto_prefix established during installation:

cc -std=c11 -Wall -Wextra -I"$m7_crypto_prefix/include" example.c \
  -L"$m7_crypto_prefix/lib" -lm7crypto \
  -Wl,-rpath,"$m7_crypto_prefix/lib" -o example
./example

Exit status zero means the example verified successfully. In a real verifier, import the expected public key separately and reject every nonzero status.

Import, generate and export keys

Private import accepts unencrypted PKCS#8, RSA PKCS#1 and EC SEC1 PEM. Public import accepts SPKI (BEGIN PUBLIC KEY), not a certificate or JWK. Protect or decrypt stored keys before import using your application's key management policy; the library does not prompt for passwords.

key.load_secret copies raw HMAC secret bytes. Import requires at least 32 bytes; HS384 and HS512 additionally require at least 48 and 64 bytes at use. Generate these secrets with a cryptographically secure random source. A secret handle and an asymmetric key are not interchangeable.

generate(algorithm, rsa_bits, &key, &error) supports the 15 asymmetric signing profiles above and RSA-OAEP-256. RSA accepts 2048, 3072, 4096 or 8192 bits; zero selects 4096. Other profiles require zero and select their fixed key type or curve. PS profiles generate ordinary RSA. HMAC generation is not exposed.

PEM export returns exact bytes with no trailing NUL: unencrypted PKCS#8 for private output and SPKI for public output, capped at 65536 bytes. Public export accepts a private or public-only asymmetric handle. Public-only keys cannot export private material, and secret handles cannot export PEM. Protect private output before storage and release it with buffer.free after use.

RSA-OAEP-256 encryption

Call the encryption getter's rsa_oaep.encrypt or rsa_oaep.decrypt with (key, "RSA-OAEP-256", input, input_length, &output, &error). Both return a status and use the same owned-buffer cleanup as signatures.

The profile fixes OAEP SHA-256, MGF1 SHA-256 and an empty label. It accepts ordinary RSA keys of 2048–8192 bits, rejecting RSA-PSS-restricted, non-RSA and secret keys. Encryption uses the public part; decryption needs the private key. The plaintext limit is ceil(key_bits / 8) - 66 bytes (190 for a 2048-bit key); ciphertext is ceil(key_bits / 8) bytes. Encryption uses fresh OAEP randomness.

Use this for a small secret such as a 32-byte content-encryption key. AES-GCM, JWE serialization, JWT validation and key-selection policy belong to the integrating application. Invalid ciphertext or a wrong decryption key produces the fixed decryption failure, without returning partial plaintext.

Ownership and failures

Initialize key outputs to NULL and buffers to {0}. Occupied outputs are rejected unchanged. Input pointers are borrowed for the call; imported keys and secrets become owned native handles. Release keys through key.free(&key) and successful output through buffer.free(&buffer), including on error cleanup paths. These functions clear the handle or pointer/length; buffer release also cleanses its bytes. Do not change a buffer's pointer or length before release, or give the library a caller-owned buffer to free. Native cleanup does not erase the caller's original PEM, messages, secrets or copies.

Only M7CRYPTO_OK (0) is success. Other statuses are invalid signature (1), invalid argument (2), unsupported algorithm (3), invalid key (4), OpenSSL error (5), out of memory (6), and decryption failed (7). Reject on every nonzero verification result; malformed signatures can produce an operational error as well as invalid-signature status. Diagnostics must not include key, secret or message material. Operations consume the calling thread's OpenSSL error queue.

Immutable function tables may be shared. Keep key handles independently owned by each worker; concurrent use of one handle is outside the supported contract. The library does not validate token issuer, audience, expiration or sender binding, and a successful signature check alone does not authorize a request.

Return to M7 C Crypto.