# 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](https://m7.org/docs/sdk/m7-c-crypto/integration/installation.md) and [release status](https://m7.org/docs/sdk/m7-c-crypto/releases.md) 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.

```c
#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:

```sh
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](https://m7.org/docs/sdk/m7-c-crypto/README.md).
