/*
 * gmcrypto-c — C ABI for gmcrypto-core (pure-Rust SM2/SM3/SM4 SDK).
 *
 * AUTO-GENERATED by cbindgen. DO NOT EDIT BY HAND.
 * Regenerate via: cargo build -p gmcrypto-c --features regen-header
 *
 * Failure-mode invariant (v0.4 W4 / Q4.8 of docs/v0.4-scope.md):
 * every int return is 0 on success, non-zero on failure. Non-zero
 * codes are NOT enumerated — they are equivalent `Failed` per the
 * crate's failure-mode discipline.
 *
 * Pointer / length preconditions (caller-upheld — violating them is
 * undefined behavior, NOT a GMCRYPTO_ERR): every non-null pointer argument
 * must point to at least the stated number of bytes (e.g. a key pointer to
 * its KEY_SIZE, an iv to 16 bytes), each *_len must not exceed the real
 * allocation, and out/out_capacity pairs must describe a writable buffer of
 * at least out_capacity bytes. A null pointer is reported as GMCRYPTO_ERR;
 * an out-of-bounds or mis-sized non-null pointer is undefined behavior.
 */

#ifndef GMCRYPTO_H_
#define GMCRYPTO_H_

/* Warning: this file is auto-generated by cbindgen; do not edit. */

#include <stdarg.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
#include <stddef.h>
#include <stdint.h>

/*
 * Output-buffer size required by gmcrypto_sm4_cbc_encrypt() for a plaintext of
 * `pt_len` bytes: PKCS#7 always appends a full padding block, so the size is
 * ((pt_len / 16) + 1) * 16. The caller must ensure this does not overflow
 * size_t.
 */
#define GMCRYPTO_SM4_CBC_OUTPUT_SIZE(pt_len) ((((size_t)(pt_len) / 16u) + 1u) * 16u)

/*
 * Best-effort secret scrub: overwrite `len` bytes at `ptr` with zeros through a
 * volatile pointer the compiler may not optimize away. Evaluates `ptr` and
 * `len` exactly once. Prefer a platform secure-zero (memset_s / explicit_bzero
 * / SecureZeroMemory) where one is available.
 */
#define GMCRYPTO_ZEROIZE(ptr, len) \
    do { \
        volatile unsigned char *gmcrypto_zptr_ = (volatile unsigned char *)(ptr); \
        size_t gmcrypto_zlen_ = (size_t)(len); \
        while (gmcrypto_zlen_ != 0u) { *gmcrypto_zptr_++ = 0u; gmcrypto_zlen_--; } \
    } while (0)


/*
 Success return code.
 */
#define GMCRYPTO_OK 0

/*
 Generic failure return code. All non-zero returns are equivalent
 per the failure-mode invariant; this constant exists only as a
 convenience for C callers that want a named symbol for the
 not-success case.
 */
#define GMCRYPTO_ERR -1

/*
 SM3 digest output size in bytes (32 = 256 bits).
 */
#define GMCRYPTO_SM3_DIGEST_SIZE 32

/*
 SM4 block size in bytes (16 = 128 bits).
 */
#define GMCRYPTO_SM4_BLOCK_SIZE 16

/*
 SM4 key size in bytes (16 = 128 bits).
 */
#define GMCRYPTO_SM4_KEY_SIZE 16

/*
 SM4-XTS key size in bytes (32 = `Key1 ‖ Key2`, two 128-bit keys).
 */
#define GMCRYPTO_SM4_XTS_KEY_SIZE (2 * GMCRYPTO_SM4_KEY_SIZE)

/*
 SEC1 uncompressed-point size for SM2 public keys
 (`04 || X || Y` = 65 bytes).
 */
#define GMCRYPTO_SM2_SEC1_UNCOMPRESSED_SIZE 65

/*
 SM2 private-key scalar size in bytes (32 = 256 bits big-endian).
 */
#define GMCRYPTO_SM2_SCALAR_SIZE 32

/*
 SM2 key-exchange confirmation-tag size in bytes (`S_A` / `S_B` are
 SM3 digests; v1.2). Ephemeral points `R_A` / `R_B` use
 [`GMCRYPTO_SM2_SEC1_UNCOMPRESSED_SIZE`] (65).
 */
#define GMCRYPTO_SM2_KX_CONFIRM_SIZE 32

/*
 Opaque handle for a streaming HMAC-SM3 keyed MAC.
 */
typedef struct gmcrypto_hmac_sm3_t gmcrypto_hmac_sm3_t;

/*
 Opaque handle for an SM2 key-exchange INITIATOR (party A; GM/T
 0003.3, v1.2). Born already awaiting the responder's reply —
 [`gmcrypto_sm2_kx_initiator_new`] samples the ephemeral internally
 and writes `R_A` immediately, so no pre-ephemeral state exists in
 C. Pair with exactly one [`gmcrypto_sm2_kx_initiator_confirm`]
 (which consumes + frees) **or** one
 [`gmcrypto_sm2_kx_initiator_free`].
 */
typedef struct gmcrypto_sm2_kx_initiator_t gmcrypto_sm2_kx_initiator_t;

/*
 Opaque handle for an SM2 key-exchange RESPONDER (party B; GM/T
 0003.3, v1.2). Lifecycle: [`gmcrypto_sm2_kx_responder_new`] →
 [`gmcrypto_sm2_kx_responder_respond`] (takes `R_A`, emits
 `R_B` + `S_B`) → [`gmcrypto_sm2_kx_responder_finish`] (takes
 `S_A`, releases `K`, consumes + frees). Pair with exactly one
 `_finish` **or** one [`gmcrypto_sm2_kx_responder_free`].
 */
typedef struct gmcrypto_sm2_kx_responder_t gmcrypto_sm2_kx_responder_t;

/*
 Opaque handle for an SM2 private key.
 */
typedef struct gmcrypto_sm2_privkey_t gmcrypto_sm2_privkey_t;

/*
 Opaque handle for an SM2 public key.
 */
typedef struct gmcrypto_sm2_pubkey_t gmcrypto_sm2_pubkey_t;

/*
 Opaque handle for a streaming SM3 hasher.
 */
typedef struct gmcrypto_sm3_t gmcrypto_sm3_t;

/*
 Opaque handle for a streaming SM4-CBC decryptor (v0.5 W1). Same
 buffer-back-by-one padding-oracle defense as the v0.3 W5 Rust
 streaming surface: the most recent decrypted block is held back
 from emission until [`gmcrypto_sm4_cbc_decryptor_finalize`]
 confirms it is the last block and validates the PKCS#7 padding.
 */
typedef struct gmcrypto_sm4_cbc_decryptor_t gmcrypto_sm4_cbc_decryptor_t;

/*
 Opaque handle for a streaming SM4-CBC encryptor (v0.5 W1).
 Construct with [`gmcrypto_sm4_cbc_encryptor_new`], feed plaintext via
 [`gmcrypto_sm4_cbc_encryptor_update`], emit the trailing PKCS#7-
 padded block(s) via [`gmcrypto_sm4_cbc_encryptor_finalize`].
 */
typedef struct gmcrypto_sm4_cbc_encryptor_t gmcrypto_sm4_cbc_encryptor_t;

/*
 Opaque handle for a streaming (incremental-input, output-BUFFERED)
 SM4-GCM decryptor (v0.10 W2). Commit-on-verify:
 [`gmcrypto_sm4_gcm_decryptor_update`] buffers ciphertext and emits
 **nothing**; [`gmcrypto_sm4_gcm_decryptor_finalize_verify`] releases
 the full plaintext only after a constant-time tag check. Memory is
 `O(message)`. Construct with [`gmcrypto_sm4_gcm_decryptor_new`].
 */
typedef struct gmcrypto_sm4_gcm_decryptor_t gmcrypto_sm4_gcm_decryptor_t;

/*
 Opaque handle for a streaming (incremental-input) SM4-GCM encryptor
 (v0.10 W1). Output-streaming: each
 [`gmcrypto_sm4_gcm_encryptor_update`] emits the ciphertext for its
 chunk; [`gmcrypto_sm4_gcm_encryptor_finalize`] emits the 16-byte tag.
 Construct with [`gmcrypto_sm4_gcm_encryptor_new`]; pair with exactly
 one finalize (which frees the handle) **or** one
 [`gmcrypto_sm4_gcm_encryptor_free`].
 */
typedef struct gmcrypto_sm4_gcm_encryptor_t gmcrypto_sm4_gcm_encryptor_t;

/*
 Opaque handle for an SM4 cipher (key-scheduled).
 */
typedef struct gmcrypto_sm4_t gmcrypto_sm4_t;

/*
 C ABI function pointer for caller-supplied RNG. Returns `0` on
 success and non-zero on failure. See module-level docs for the
 full contract.
 */
typedef int (*gmcrypto_rng_callback)(void *context, uint8_t *buf, uintptr_t buf_len);

/*
 Returns a NUL-terminated string with the `gmcrypto-c` crate version,
 tracking Cargo's `CARGO_PKG_VERSION` at build time (e.g. `"1.0.0"`).
 The returned pointer is to a static `&'static CStr` and must NOT be
 freed by the caller.
 */
 const char *gmcrypto_version(void) ;

/*
 Single-shot SM3 hash. Writes 32 bytes to `out_digest`.

 # Returns
 [`GMCRYPTO_OK`] on success; [`GMCRYPTO_ERR`] on invalid input
 (null `out_digest`, null `msg` with non-zero `msg_len`).
 */
 int gmcrypto_sm3_hash(const uint8_t *msg, uintptr_t msg_len, uint8_t *out_digest) ;

/*
 Construct a fresh streaming SM3 hasher. Returns an opaque handle;
 must be freed via [`gmcrypto_sm3_free`].

 Never returns NULL: construction is infallible apart from heap allocation,
 and allocation failure aborts the process via Rust's global allocator
 (it does not — and cannot, on stable Rust — return NULL here).
 */
 gmcrypto_sm3_t *gmcrypto_sm3_new(void) ;

/*
 Absorb `data` into the streaming SM3 hasher.
 */
 int gmcrypto_sm3_update(gmcrypto_sm3_t *hasher, const uint8_t *data, uintptr_t data_len) ;

/*
 Consume the streaming SM3 hasher and write the digest to
 `out_digest`. The handle is **freed** by this call; do not call
 [`gmcrypto_sm3_free`] on it afterwards.
 */
 int gmcrypto_sm3_finalize(gmcrypto_sm3_t *hasher, uint8_t *out_digest) ;

/*
 Free a streaming SM3 hasher. Passing NULL is a no-op.
 */
 void gmcrypto_sm3_free(gmcrypto_sm3_t *hasher) ;

/*
 Single-shot HMAC-SM3. Writes 32 bytes to `out_tag`.
 */

int gmcrypto_hmac_sm3(const uint8_t *key,
                      uintptr_t key_len,
                      const uint8_t *msg,
                      uintptr_t msg_len,
                      uint8_t *out_tag)
;

/*
 Construct a fresh streaming HMAC-SM3 instance keyed with `key`.
 Returns NULL on invalid input.
 */
 gmcrypto_hmac_sm3_t *gmcrypto_hmac_sm3_new(const uint8_t *key, uintptr_t key_len) ;

/*
 Absorb `data` into the streaming HMAC-SM3 instance.
 */
 int gmcrypto_hmac_sm3_update(gmcrypto_hmac_sm3_t *mac, const uint8_t *data, uintptr_t data_len) ;

/*
 Consume the streaming HMAC-SM3 instance and write the 32-byte tag
 to `out_tag`. The handle is **freed** by this call.
 */
 int gmcrypto_hmac_sm3_finalize(gmcrypto_hmac_sm3_t *mac, uint8_t *out_tag) ;

/*
 Consume the streaming HMAC-SM3 instance and verify the candidate
 tag in constant time. Returns [`GMCRYPTO_OK`] on match;
 [`GMCRYPTO_ERR`] on mismatch. The handle is **freed** by this call.
 */
 int gmcrypto_hmac_sm3_verify(gmcrypto_hmac_sm3_t *mac, const uint8_t *expected_tag) ;

/*
 Free a streaming HMAC-SM3 instance. NULL is a no-op.
 */
 void gmcrypto_hmac_sm3_free(gmcrypto_hmac_sm3_t *mac) ;

/*
 Derive `out_len` bytes via PBKDF2-HMAC-SM3 over `(pwd, salt,
 iterations)`. Writes into the caller-supplied `out` buffer.
 */

int gmcrypto_pbkdf2_hmac_sm3(const uint8_t *pwd,
                             uintptr_t pwd_len,
                             const uint8_t *salt,
                             uintptr_t salt_len,
                             uint32_t iterations,
                             uint8_t *out,
                             uintptr_t out_len)
;

/*
 Construct an SM4 cipher from a 16-byte key. Returns NULL on null
 key.
 */
 gmcrypto_sm4_t *gmcrypto_sm4_new(const uint8_t *key) ;

/*
 Encrypt one 16-byte block in place under the SM4 cipher.

 WARNING: this is the raw SM4 block, not a cipher mode. Calling it in a
 loop over a multi-block buffer is ECB — it leaks plaintext-block equality
 and has no semantic security. To encrypt messages use a mode
 (`gmcrypto_sm4_gcm_*` / `_ccm_*` authenticated; or `_cbc_*` / `_ctr_*` /
 `_xts_*` confidentiality-only with a unique IV/nonce/tweak).
 */
 int gmcrypto_sm4_encrypt_block(const gmcrypto_sm4_t *cipher, uint8_t *block) ;

/*
 Decrypt one 16-byte block in place under the SM4 cipher.

 WARNING: raw SM4 block, not a cipher mode (see
 `gmcrypto_sm4_encrypt_block`). Looping it over a buffer is ECB
 decryption — no semantic security, no authentication. Decrypt real
 messages with a mode.
 */
 int gmcrypto_sm4_decrypt_block(const gmcrypto_sm4_t *cipher, uint8_t *block) ;

/*
 Free an SM4 cipher. NULL is a no-op.
 */
 void gmcrypto_sm4_free(gmcrypto_sm4_t *cipher) ;

/*
 SM4-CBC single-shot encrypt with PKCS#7 padding. IV must be
 caller-supplied and unpredictable (per NIST SP 800-38A
 Appendix C). Output length is always `((pt_len / 16) + 1) * 16`.
 */

int gmcrypto_sm4_cbc_encrypt(const uint8_t *key,
                             const uint8_t *iv,
                             const uint8_t *pt,
                             uintptr_t pt_len,
                             uint8_t *out,
                             uintptr_t out_capacity,
                             uintptr_t *out_actual_len)
;

/*
 SM4-CBC single-shot decrypt. Single-`Failed` return on any
 failure mode (length not multiple of 16, bad padding, key/IV
 mismatch) per the failure-mode invariant.
 */

int gmcrypto_sm4_cbc_decrypt(const uint8_t *key,
                             const uint8_t *iv,
                             const uint8_t *ct,
                             uintptr_t ct_len,
                             uint8_t *out,
                             uintptr_t out_capacity,
                             uintptr_t *out_actual_len)
;

/*
 Construct a streaming SM4-CBC encryptor. `key` is exactly 16
 bytes; `iv` is exactly 16 bytes and MUST be caller-supplied
 unpredictable bytes (NIST SP 800-38A Appendix C). Returns NULL
 on invalid pointer input.
 */

gmcrypto_sm4_cbc_encryptor_t *gmcrypto_sm4_cbc_encryptor_new(const uint8_t *key,
                                                             const uint8_t *iv)
;

/*
 Absorb plaintext into the streaming SM4-CBC encryptor and emit
 zero or more full ciphertext blocks. The caller-allocated `out`
 buffer MUST be at least `pt_len + 16` bytes — that is the upper
 bound on bytes emitted by a single `_update` call (a buffered
 partial block from a prior call can produce one extra block when
 this call's input fills it). On insufficient capacity, the call
 returns [`GMCRYPTO_ERR`] and the encryptor state is left mid-
 stream (the ciphertext bytes that would have been emitted are
 lost). Callers should size the output buffer correctly up-front.
 */

int gmcrypto_sm4_cbc_encryptor_update(gmcrypto_sm4_cbc_encryptor_t *enc,
                                      const uint8_t *pt,
                                      uintptr_t pt_len,
                                      uint8_t *out,
                                      uintptr_t out_capacity,
                                      uintptr_t *out_actual_len)
;

/*
 Apply PKCS#7 padding to the buffered tail and emit the final
 ciphertext block(s). Consumes the encryptor — the handle is
 **freed** by this call; do NOT call
 [`gmcrypto_sm4_cbc_encryptor_free`] on it afterwards.

 Output is always exactly one block (16 bytes).
 */

int gmcrypto_sm4_cbc_encryptor_finalize(gmcrypto_sm4_cbc_encryptor_t *enc,
                                        uint8_t *out,
                                        uintptr_t out_capacity,
                                        uintptr_t *out_actual_len)
;

/*
 Free a streaming SM4-CBC encryptor. Passing NULL is a no-op. Do
 NOT call after [`gmcrypto_sm4_cbc_encryptor_finalize`] — that
 already consumed the handle.
 */
 void gmcrypto_sm4_cbc_encryptor_free(gmcrypto_sm4_cbc_encryptor_t *enc) ;

/*
 Construct a streaming SM4-CBC decryptor. `key` is exactly 16
 bytes; `iv` is exactly 16 bytes and must match the value used
 during encryption. Returns NULL on invalid pointer input.
 */

gmcrypto_sm4_cbc_decryptor_t *gmcrypto_sm4_cbc_decryptor_new(const uint8_t *key,
                                                             const uint8_t *iv)
;

/*
 Absorb ciphertext into the streaming SM4-CBC decryptor and emit
 zero or more full plaintext blocks. The final-candidate block is
 HELD BACK from emission until `_finalize` validates the trailing
 padding (buffer-back-by-one padding-oracle defense). Same buffer-
 size contract as the encryptor's `_update`: caller MUST allocate
 `out_capacity >= ct_len + 16` (strict upper bound on bytes emitted
 in one call). On insufficient capacity returns [`GMCRYPTO_ERR`]
 and the decryptor state is left mid-stream; size the buffer
 up-front.
 */

int gmcrypto_sm4_cbc_decryptor_update(gmcrypto_sm4_cbc_decryptor_t *dec,
                                      const uint8_t *ct,
                                      uintptr_t ct_len,
                                      uint8_t *out,
                                      uintptr_t out_capacity,
                                      uintptr_t *out_actual_len)
;

/*
 Strip PKCS#7 padding from the held-back final block and emit the
 last plaintext bytes. Consumes the decryptor — the handle is
 **freed** by this call; do NOT call
 [`gmcrypto_sm4_cbc_decryptor_free`] on it afterwards.

 Returns [`GMCRYPTO_ERR`] on any failure mode (length not multiple
 of 16, no full blocks seen, or padding-strip rejection) — single
 uninformative failure code per the failure-mode invariant. The
 caller-supplied `out_actual_len` is set to `0` on failure.
 */

int gmcrypto_sm4_cbc_decryptor_finalize(gmcrypto_sm4_cbc_decryptor_t *dec,
                                        uint8_t *out,
                                        uintptr_t out_capacity,
                                        uintptr_t *out_actual_len)
;

/*
 Free a streaming SM4-CBC decryptor. Passing NULL is a no-op. Do
 NOT call after [`gmcrypto_sm4_cbc_decryptor_finalize`] — that
 already consumed the handle.
 */
 void gmcrypto_sm4_cbc_decryptor_free(gmcrypto_sm4_cbc_decryptor_t *dec) ;

/*
 SM4-GCM single-shot encrypt. `ct_out` receives `pt_len` bytes (via
 the capacity/actual-len convention); `tag_out` receives exactly 16
 bytes. Returns [`GMCRYPTO_OK`] / [`GMCRYPTO_ERR`].
 */

int gmcrypto_sm4_gcm_encrypt(const uint8_t *key,
                             const uint8_t *nonce,
                             uintptr_t nonce_len,
                             const uint8_t *aad,
                             uintptr_t aad_len,
                             const uint8_t *pt,
                             uintptr_t pt_len,
                             uint8_t *ct_out,
                             uintptr_t ct_capacity,
                             uintptr_t *ct_actual_len,
                             uint8_t *tag_out)
;

/*
 SM4-GCM single-shot decrypt with a 16-byte tag. `pt_out` receives
 `ct_len` bytes. Returns [`GMCRYPTO_OK`] only if the tag verifies;
 [`GMCRYPTO_ERR`] on any failure (single failure mode).
 */

int gmcrypto_sm4_gcm_decrypt(const uint8_t *key,
                             const uint8_t *nonce,
                             uintptr_t nonce_len,
                             const uint8_t *aad,
                             uintptr_t aad_len,
                             const uint8_t *ct,
                             uintptr_t ct_len,
                             const uint8_t *tag,
                             uint8_t *pt_out,
                             uintptr_t pt_capacity,
                             uintptr_t *pt_actual_len)
;

/*
 SM4-GCM encrypt with a truncated tag. `tag_len` must be in
 `{4, 8, 12, 13, 14, 15, 16}`; `tag_out` receives `tag_len` bytes.
 Invalid `tag_len` → [`GMCRYPTO_ERR`].
 */

int gmcrypto_sm4_gcm_encrypt_with_tag_len(const uint8_t *key,
                                          const uint8_t *nonce,
                                          uintptr_t nonce_len,
                                          const uint8_t *aad,
                                          uintptr_t aad_len,
                                          const uint8_t *pt,
                                          uintptr_t pt_len,
                                          uintptr_t tag_len,
                                          uint8_t *ct_out,
                                          uintptr_t ct_capacity,
                                          uintptr_t *ct_actual_len,
                                          uint8_t *tag_out)
;

/*
 SM4-GCM decrypt with a truncated tag. `tag` is `tag_len` bytes;
 `tag_len` must be in `{4, 8, 12, 13, 14, 15, 16}`. `pt_out`
 receives `ct_len` bytes. [`GMCRYPTO_ERR`] on any failure.
 */

int gmcrypto_sm4_gcm_decrypt_with_tag_len(const uint8_t *key,
                                          const uint8_t *nonce,
                                          uintptr_t nonce_len,
                                          const uint8_t *aad,
                                          uintptr_t aad_len,
                                          const uint8_t *ct,
                                          uintptr_t ct_len,
                                          const uint8_t *tag,
                                          uintptr_t tag_len,
                                          uint8_t *pt_out,
                                          uintptr_t pt_capacity,
                                          uintptr_t *pt_actual_len)
;

/*
 SM4-CCM single-shot encrypt. `tag_len` must be in
 `{4, 6, 8, 10, 12, 14, 16}`; `nonce_len` in `[7, 13]`. `out`
 receives `pt_len + tag_len` bytes (`ciphertext ‖ tag`). Invalid
 parameters → [`GMCRYPTO_ERR`].
 */

int gmcrypto_sm4_ccm_encrypt(const uint8_t *key,
                             const uint8_t *nonce,
                             uintptr_t nonce_len,
                             const uint8_t *aad,
                             uintptr_t aad_len,
                             const uint8_t *pt,
                             uintptr_t pt_len,
                             uintptr_t tag_len,
                             uint8_t *out,
                             uintptr_t out_capacity,
                             uintptr_t *out_actual_len)
;

/*
 SM4-CCM single-shot decrypt. Input `ct` is `ct_len` bytes
 (`ciphertext ‖ tag`); `tag_len` must match the value used at
 encrypt time. `pt_out` receives `ct_len - tag_len` bytes.
 [`GMCRYPTO_ERR`] on any failure (single failure mode).
 */

int gmcrypto_sm4_ccm_decrypt(const uint8_t *key,
                             const uint8_t *nonce,
                             uintptr_t nonce_len,
                             const uint8_t *aad,
                             uintptr_t aad_len,
                             const uint8_t *ct,
                             uintptr_t ct_len,
                             uintptr_t tag_len,
                             uint8_t *pt_out,
                             uintptr_t pt_capacity,
                             uintptr_t *pt_actual_len)
;

/*
 SM4-XTS single-shot encrypt (GB/T 17964-2021, `xts_standard=GB`).
 `key` is exactly [`GMCRYPTO_SM4_XTS_KEY_SIZE`] (32) bytes (`Key1 ‖
 Key2`); `tweak` is exactly [`GMCRYPTO_SM4_BLOCK_SIZE`] (16) raw bytes
 (the data-unit/sector identifier — caller-unique per key). `out`
 receives `data_len` bytes (length-preserving) via the
 capacity/actual-len convention. Returns [`GMCRYPTO_OK`] /
 [`GMCRYPTO_ERR`] (single failure mode: `data_len` outside
 `[16, 16 MiB]`, `Key1 == Key2`, null pointer, or buffer too small —
 in which case `*out_actual_len` is set to the required length).
 **Confidentiality only — SM4-XTS does not authenticate.**
 */

int gmcrypto_sm4_xts_encrypt(const uint8_t *key,
                             const uint8_t *tweak,
                             const uint8_t *data,
                             uintptr_t data_len,
                             uint8_t *out,
                             uintptr_t out_capacity,
                             uintptr_t *out_actual_len)
;

/*
 SM4-XTS single-shot decrypt (GB/T 17964-2021, `xts_standard=GB`).
 Inverse of [`gmcrypto_sm4_xts_encrypt`] with the same argument shape;
 `out` receives `data_len` bytes. Returns [`GMCRYPTO_OK`] /
 [`GMCRYPTO_ERR`] (same single failure mode). XTS is unauthenticated,
 so decrypt cannot detect tampering — it only fails on invalid
 parameters (length / weak key / buffer).
 */

int gmcrypto_sm4_xts_decrypt(const uint8_t *key,
                             const uint8_t *tweak,
                             const uint8_t *data,
                             uintptr_t data_len,
                             uint8_t *out,
                             uintptr_t out_capacity,
                             uintptr_t *out_actual_len)
;

/*
 SM4-XTS in-place multi-sector encrypt (GB/T 17964-2021,
 `xts_standard=GB`). `key` is exactly [`GMCRYPTO_SM4_XTS_KEY_SIZE`] (32)
 bytes (`Key1 ‖ Key2`); `buf` is a contiguous run of `buf_len / sector_size`
 equal-size sectors transformed **in place** (`buf_len` must be a whole
 multiple of `sector_size`). Sector `i` is encrypted under
 tweak = little-endian-128(`start_sector + i`) — the data-unit / LBA
 convention; sector numbers must be unique within the XTS-key namespace
 (caller's contract). `start_sector` is a `uint64_t` (LBA width), so the
 addressable range is `[0, 2^64 − 1]`; for the full u128 sector space use
 the Rust `mode_xts::encrypt_sectors` API. Returns [`GMCRYPTO_OK`] /
 [`GMCRYPTO_ERR`] (single
 failure mode: `sector_size` outside `[16, 16 MiB]` or not a multiple of 16,
 `buf_len` not a whole multiple of `sector_size`, `Key1 == Key2`, or null
 pointer). **`buf` is untouched on [`GMCRYPTO_ERR`].** `buf_len == 0` is a
 vacuous [`GMCRYPTO_OK`] (but the key is still validated, so empty + weak key
 → [`GMCRYPTO_ERR`]). **Confidentiality only — SM4-XTS does not
 authenticate.**
 */

int gmcrypto_sm4_xts_encrypt_sectors(const uint8_t *key,
                                     uintptr_t sector_size,
                                     uint64_t start_sector,
                                     uint8_t *buf,
                                     uintptr_t buf_len)
;

/*
 SM4-XTS in-place multi-sector decrypt (GB/T 17964-2021, `xts_standard=GB`).
 Inverse of [`gmcrypto_sm4_xts_encrypt_sectors`] under the same
 `(key, sector_size, start_sector)`; same in-place contract, single failure
 mode, and `buf`-untouched-on-error guarantee. XTS is unauthenticated, so
 decrypt cannot detect tampering — it only fails on invalid parameters.
 */

int gmcrypto_sm4_xts_decrypt_sectors(const uint8_t *key,
                                     uintptr_t sector_size,
                                     uint64_t start_sector,
                                     uint8_t *buf,
                                     uintptr_t buf_len)
;

/*
 Construct a streaming SM4-GCM encryptor. `key` is exactly 16 bytes;
 `nonce` is `nonce_len` bytes (12 = canonical; other lengths invoke
 the extra GHASH J0-derivation per NIST SP 800-38D §8.2.2); `aad` is
 the full associated data (the message header, supplied up-front).
 Returns NULL on invalid pointer/length input. **Nonce uniqueness is
 the caller's responsibility** — reusing `(key, nonce)` is
 catastrophic for GCM.
 */

gmcrypto_sm4_gcm_encryptor_t *gmcrypto_sm4_gcm_encryptor_new(const uint8_t *key,
                                                             const uint8_t *nonce,
                                                             uintptr_t nonce_len,
                                                             const uint8_t *aad,
                                                             uintptr_t aad_len)
;

/*
 Encrypt `pt_len` bytes of plaintext, emitting the ciphertext for
 this chunk (length == `pt_len`; GCM does not pad or buffer). The
 `out` buffer MUST be at least `pt_len` bytes; on insufficient
 capacity returns [`GMCRYPTO_ERR`] (and the required length is written
 to `*out_actual_len`), and the encryptor state is left mid-stream
 (the chunk's ciphertext is lost — size the buffer correctly).
 Returns [`GMCRYPTO_ERR`] once the cumulative plaintext would exceed
 the GCM ceiling (`2^36 − 32` bytes); the encryptor is poisoned and
 all later calls also return [`GMCRYPTO_ERR`].
 */

int gmcrypto_sm4_gcm_encryptor_update(gmcrypto_sm4_gcm_encryptor_t *enc,
                                      const uint8_t *pt,
                                      uintptr_t pt_len,
                                      uint8_t *out,
                                      uintptr_t out_capacity,
                                      uintptr_t *out_actual_len)
;

/*
 Finish and emit the full 16-byte tag. **Consumes the encryptor —
 the handle is freed by this call** (even on error); do NOT call
 [`gmcrypto_sm4_gcm_encryptor_free`] on it afterwards. `tag_out`
 must be valid for exactly 16 bytes.
 */
 int gmcrypto_sm4_gcm_encryptor_finalize(gmcrypto_sm4_gcm_encryptor_t *enc, uint8_t *tag_out) ;

/*
 Finish and emit a truncated tag of `tag_len` bytes (`MSB_t` per NIST
 SP 800-38D §5.2.1.2). `tag_len` must be in `{4, 8, 12, 13, 14, 15,
 16}` (else [`GMCRYPTO_ERR`]). **Consumes the encryptor — the handle
 is freed by this call** (even on error); do NOT call
 [`gmcrypto_sm4_gcm_encryptor_free`] afterwards.
 */

int gmcrypto_sm4_gcm_encryptor_finalize_with_tag_len(gmcrypto_sm4_gcm_encryptor_t *enc,
                                                     uintptr_t tag_len,
                                                     uint8_t *out,
                                                     uintptr_t out_capacity,
                                                     uintptr_t *out_actual_len)
;

/*
 Free a streaming SM4-GCM encryptor without finalizing (abort path).
 Passing NULL is a no-op. Do NOT call after any `_finalize*` — those
 already consumed the handle.
 */
 void gmcrypto_sm4_gcm_encryptor_free(gmcrypto_sm4_gcm_encryptor_t *enc) ;

/*
 Construct a streaming SM4-GCM decryptor. Same parameter contract as
 [`gmcrypto_sm4_gcm_encryptor_new`]. Returns NULL on invalid input.
 */

gmcrypto_sm4_gcm_decryptor_t *gmcrypto_sm4_gcm_decryptor_new(const uint8_t *key,
                                                             const uint8_t *nonce,
                                                             uintptr_t nonce_len,
                                                             const uint8_t *aad,
                                                             uintptr_t aad_len)
;

/*
 Buffer `ct_len` bytes of ciphertext and fold them into the running
 GHASH. **Emits no plaintext** (commit-on-verify) — there is no
 output parameter. Returns [`GMCRYPTO_ERR`] only on null handle or
 invalid input pointer; a length-ceiling overflow is latched and
 surfaces as [`GMCRYPTO_ERR`] at
 [`gmcrypto_sm4_gcm_decryptor_finalize_verify`].
 */

int gmcrypto_sm4_gcm_decryptor_update(gmcrypto_sm4_gcm_decryptor_t *dec,
                                      const uint8_t *ct,
                                      uintptr_t ct_len)
;

/*
 Verify `tag` (`tag_len` bytes; the length is validated against the
 NIST-permitted set `{4, 8, 12, 13, 14, 15, 16}`) and, on success,
 write the full decrypted plaintext (length == total ciphertext fed)
 to `(out, out_capacity, out_actual_len)`.

 Returns [`GMCRYPTO_ERR`] in two cases, both of which still **consume
 and free the handle** (do NOT call
 [`gmcrypto_sm4_gcm_decryptor_free`] afterwards):

 - **Verification failure** (tag mismatch, invalid `tag_len`, or
   length-ceiling overflow): `*out_actual_len` is `0` and no
   plaintext is written (commit-on-verify; single failure mode).
 - **Tag verified but `out` too small**: `*out_actual_len` is set to
   the required plaintext length and no plaintext is written. The
   handle is consumed, so you cannot retry — size `out` to the total
   ciphertext length up-front (GCM plaintext is the same length).
 */

int gmcrypto_sm4_gcm_decryptor_finalize_verify(gmcrypto_sm4_gcm_decryptor_t *dec,
                                               const uint8_t *tag,
                                               uintptr_t tag_len,
                                               uint8_t *out,
                                               uintptr_t out_capacity,
                                               uintptr_t *out_actual_len)
;

/*
 Free a streaming SM4-GCM decryptor without verifying (abort path).
 NULL is a no-op. Do NOT call after
 [`gmcrypto_sm4_gcm_decryptor_finalize_verify`].
 */
 void gmcrypto_sm4_gcm_decryptor_free(gmcrypto_sm4_gcm_decryptor_t *dec) ;

/*
 Construct an SM2 private key from a 32-byte big-endian scalar.
 Returns NULL on out-of-range scalar (must be in `[1, n-2]`).
 */
 gmcrypto_sm2_privkey_t *gmcrypto_sm2_privkey_new(const uint8_t *d_be) ;

/*
 Construct an SM2 public key from a SEC1 uncompressed-point byte
 string (`04 || X || Y`, 65 bytes). Returns NULL on
 invalid input (off-curve, identity point, non-uncompressed
 prefix).
 */
 gmcrypto_sm2_pubkey_t *gmcrypto_sm2_pubkey_new(const uint8_t *sec1_uncompressed) ;

/*
 Export the SM2 private key as a 32-byte big-endian scalar.

 **Caller MUST zeroize the output buffer** after use. Per Q4.19,
 this entry point exists as `#[doc(hidden)]`-equivalent on the
 Rust side and is NOT SemVer-stable across v0.4.x.
 */
 int gmcrypto_sm2_privkey_to_sec1_be(const gmcrypto_sm2_privkey_t *key, uint8_t *out) ;

/*
 Export the SM2 public key as a SEC1 uncompressed-point byte
 string (`04 || X || Y`, 65 bytes).
 */
 int gmcrypto_sm2_pubkey_to_sec1_uncompressed(const gmcrypto_sm2_pubkey_t *key, uint8_t *out) ;

/*
 Free an SM2 private key. NULL is a no-op. The inner scalar is
 zeroized via `ZeroizeOnDrop` before the heap slot is freed.
 */
 void gmcrypto_sm2_privkey_free(gmcrypto_sm2_privkey_t *key) ;

/*
 Free an SM2 public key. NULL is a no-op.
 */
 void gmcrypto_sm2_pubkey_free(gmcrypto_sm2_pubkey_t *key) ;

/*
 Emit a password-encrypted PKCS#8 PEM blob containing the SM2
 private key. PBES2 / PBKDF2-HMAC-SM3 / SM4-CBC per RFC 8018.
 */

int gmcrypto_sm2_privkey_to_pkcs8(const gmcrypto_sm2_privkey_t *key,
                                  const uint8_t *password,
                                  uintptr_t pwd_len,
                                  uint32_t pbkdf2_iters,
                                  uint8_t *out_pem,
                                  uintptr_t out_capacity,
                                  uintptr_t *out_actual_len)
;

/*
 Load an SM2 private key from a password-encrypted PKCS#8 PEM blob.
 On success, writes the new handle to `*out_key` and returns
 [`GMCRYPTO_OK`]. Caller MUST free via [`gmcrypto_sm2_privkey_free`].
 */

int gmcrypto_sm2_privkey_from_pkcs8(const uint8_t *pem,
                                    uintptr_t pem_len,
                                    const uint8_t *password,
                                    uintptr_t pwd_len,
                                    gmcrypto_sm2_privkey_t **out_key)
;

/*
 Sign `msg` with the SM2 private key using the supplied
 `signer_id` (or [`DEFAULT_SIGNER_ID`] = `"1234567812345678"` if
 `signer_id_len == 0`). Output is DER-encoded
 `SEQUENCE { r, s }`. RNG is sourced from `getrandom::SysRng`.

 May return [`GMCRYPTO_ERR`] if the system RNG fails (in addition to the
 usual null / short-buffer errors); the error is terminal — do not retry
 on the same inputs expecting success.
 */

int gmcrypto_sm2_sign(const gmcrypto_sm2_privkey_t *key,
                      const uint8_t *signer_id,
                      uintptr_t signer_id_len,
                      const uint8_t *msg,
                      uintptr_t msg_len,
                      uint8_t *out_der_sig,
                      uintptr_t out_capacity,
                      uintptr_t *out_actual_len)
;

/*
 Verify a DER-encoded `(r, s)` signature against `msg` using the
 SM2 public key and `signer_id`. Returns [`GMCRYPTO_OK`] on
 valid; [`GMCRYPTO_ERR`] on invalid or any error.
 */

int gmcrypto_sm2_verify(const gmcrypto_sm2_pubkey_t *key,
                        const uint8_t *signer_id,
                        uintptr_t signer_id_len,
                        const uint8_t *msg,
                        uintptr_t msg_len,
                        const uint8_t *der_sig,
                        uintptr_t der_sig_len)
;

/*
 SM2 public-key encrypt. Output is GM/T 0009-2012 DER. RNG from
 `getrandom::SysRng`.

 May return [`GMCRYPTO_ERR`] if the system RNG fails (in addition to the
 usual null / short-buffer errors); the error is terminal.
 */

int gmcrypto_sm2_encrypt(const gmcrypto_sm2_pubkey_t *key,
                         const uint8_t *pt,
                         uintptr_t pt_len,
                         uint8_t *out_der_ct,
                         uintptr_t out_capacity,
                         uintptr_t *out_actual_len)
;

/*
 SM2 private-key decrypt of a GM/T 0009-2012 DER ciphertext.
 */

int gmcrypto_sm2_decrypt(const gmcrypto_sm2_privkey_t *key,
                         const uint8_t *der_ct,
                         uintptr_t der_ct_len,
                         uint8_t *out_pt,
                         uintptr_t out_capacity,
                         uintptr_t *out_actual_len)
;

/*
 SM2 public-key encrypt; output in the modern raw byte-concat
 `C1 || C3 || C2` format. `C1` is the 65-byte SEC1-uncompressed
 point (`0x04 || X || Y`); `C3` is the 32-byte SM3 MAC; `C2` is
 `msg_len` bytes of XOR-ed ciphertext. Output length is exactly
 `65 + 32 + msg_len`.

 RNG is sourced from `getrandom::SysRng` internally (same as
 [`gmcrypto_sm2_encrypt`]). The W3 RNG-callback variant lands as a
 separate workstream.

 Same failure-mode posture as [`gmcrypto_sm2_encrypt`]: single
 [`GMCRYPTO_ERR`] on any failure mode (identity public key, KDF-
 zero retries exhausted).
 */

int gmcrypto_sm2_encrypt_c1c3c2(const gmcrypto_sm2_pubkey_t *key,
                                const uint8_t *pt,
                                uintptr_t pt_len,
                                uint8_t *out_raw_ct,
                                uintptr_t out_capacity,
                                uintptr_t *out_actual_len)
;

/*
 SM2 private-key decrypt of a modern raw byte-concat
 `C1 || C3 || C2` ciphertext. Input length must be at least
 `65 + 32 + 1 = 98` bytes (C1 + C3 + at least one C2 byte).

 Same failure-mode posture as [`gmcrypto_sm2_decrypt`]: single
 [`GMCRYPTO_ERR`] on any failure mode (malformed input, off-curve
 C1, identity C1, MAC mismatch, or KDF-zero detection). Caller
 cannot distinguish wrong-key from corrupt-ciphertext via timing
 or return code.
 */

int gmcrypto_sm2_decrypt_c1c3c2(const gmcrypto_sm2_privkey_t *key,
                                const uint8_t *raw_ct,
                                uintptr_t raw_ct_len,
                                uint8_t *out_pt,
                                uintptr_t out_capacity,
                                uintptr_t *out_actual_len)
;

/*
 SM2 private-key decrypt of a **legacy** raw byte-concat
 `C1 || C2 || C3` ciphertext. Decrypt-only — there is no emit path
 for the legacy ordering, and there will not be one in any v0.5+
 version (per `CLAUDE.md` "Don't" entry).

 The two raw byte-concat orderings (`C1 || C3 || C2` modern vs
 `C1 || C2 || C3` legacy) are NOT auto-detected. The caller MUST
 know which format their wire-data follows. Mis-feeding modern
 ciphertext to this entry point or vice-versa will fail at the MAC
 check (`GMCRYPTO_ERR`); the failure-mode invariant precludes the
 caller from distinguishing wrong-format from wrong-key.

 Same failure-mode posture as [`gmcrypto_sm2_decrypt_c1c3c2`]:
 single [`GMCRYPTO_ERR`] on any failure.
 */

int gmcrypto_sm2_decrypt_c1c2c3_legacy(const gmcrypto_sm2_privkey_t *key,
                                       const uint8_t *raw_ct,
                                       uintptr_t raw_ct_len,
                                       uint8_t *out_pt,
                                       uintptr_t out_capacity,
                                       uintptr_t *out_actual_len)
;

/*
 `_with_rng` variant of [`gmcrypto_sm2_sign`]. Identical contract
 except RNG bytes come from the caller's `rng_callback` rather
 than `getrandom::SysRng`.

 Returns [`GMCRYPTO_OK`] on success; [`GMCRYPTO_ERR`] on any
 failure including:
 - null `key` pointer
 - null `rng_callback` pointer
 - callback returned non-zero on any draw
 - signing produced no valid signature within the retry budget

 Per the failure-mode invariant, the caller cannot distinguish
 callback-error from signing-failure via return code or timing.
 */

int gmcrypto_sm2_sign_with_rng(const gmcrypto_sm2_privkey_t *key,
                               const uint8_t *signer_id,
                               uintptr_t signer_id_len,
                               const uint8_t *msg,
                               uintptr_t msg_len,
                               gmcrypto_rng_callback rng_callback,
                               void *rng_context,
                               uint8_t *out_der_sig,
                               uintptr_t out_capacity,
                               uintptr_t *out_actual_len)
;

/*
 `_with_rng` variant of [`gmcrypto_sm2_encrypt`]. Identical
 contract except RNG bytes come from the caller's `rng_callback`
 rather than `getrandom::SysRng`.

 Output is GM/T 0009-2012 DER (same as `gmcrypto_sm2_encrypt`).
 For raw byte-concat output (`C1 || C3 || C2`), use
 `gmcrypto_sm2_encrypt_c1c3c2` — v0.5 doesn't ship a
 `_c1c3c2_with_rng` combined variant; if needed, callers can
 re-encode the DER output via gmcrypto-core's
 `asn1::ciphertext::decode` + `raw_ciphertext::encode_c1c3c2`.

 Same `GMCRYPTO_ERR`-on-any-failure posture as
 `gmcrypto_sm2_sign_with_rng`.
 */

int gmcrypto_sm2_encrypt_with_rng(const gmcrypto_sm2_pubkey_t *key,
                                  const uint8_t *pt,
                                  uintptr_t pt_len,
                                  gmcrypto_rng_callback rng_callback,
                                  void *rng_context,
                                  uint8_t *out_der_ct,
                                  uintptr_t out_capacity,
                                  uintptr_t *out_actual_len)
;

/*
 Construct a key-exchange INITIATOR (party A) and write its
 ephemeral point `R_A` (SEC1 uncompressed `04 ‖ X ‖ Y`, exactly
 [`GMCRYPTO_SM2_SEC1_UNCOMPRESSED_SIZE`] = 65 bytes) to `out_r_a`.
 The handle is created already awaiting the responder's reply: send
 `out_r_a` to the responder, then call
 [`gmcrypto_sm2_kx_initiator_confirm`] with its `(R_B, S_B)`.

 `local_privkey` is A's static key; `peer_pubkey` is B's static
 public key. `id_a` / `id_b` are the parties' identity strings
 (`len == 0` selects the GM/T default ID `"1234567812345678"`).
 `klen` is the agreed-key length in bytes (non-zero, under the KDF
 ceiling); the SAME `klen` sizes `key_out` at confirm time.
 Ephemeral randomness comes from the OS (`getrandom::SysRng`).

 Returns the handle, or NULL on any failure (null pointer, bad
 `klen`/`id`, RNG failure — indistinguishable by design). Pair with
 exactly one `_confirm` (which frees) **or** one `_free`.

 # Safety

 `local_privkey` / `peer_pubkey` must be valid handles from this
 library; `out_r_a` must be valid for 65 writes; `id_a` / `id_b`
 must be valid for their lengths when non-zero.
 */

gmcrypto_sm2_kx_initiator_t *gmcrypto_sm2_kx_initiator_new(const gmcrypto_sm2_privkey_t *local_privkey,
                                                           const gmcrypto_sm2_pubkey_t *peer_pubkey,
                                                           const uint8_t *id_a,
                                                           uintptr_t id_a_len,
                                                           const uint8_t *id_b,
                                                           uintptr_t id_b_len,
                                                           uintptr_t klen,
                                                           uint8_t *out_r_a)
;

/*
 `_with_rng` variant of [`gmcrypto_sm2_kx_initiator_new`]: identical
 contract except the ephemeral randomness comes from the caller's
 `rng_callback` (the v0.5 `gmcrypto_rng_callback` shape) rather than
 `getrandom::SysRng`. A null or failing callback returns NULL —
 indistinguishable from every other failure by design.

 # Safety

 As [`gmcrypto_sm2_kx_initiator_new`]; additionally `rng_callback`
 must be NULL or a valid function pointer honouring the callback
 contract (fill `buf_len` bytes, return 0 on success).
 */

gmcrypto_sm2_kx_initiator_t *gmcrypto_sm2_kx_initiator_new_with_rng(const gmcrypto_sm2_privkey_t *local_privkey,
                                                                    const gmcrypto_sm2_pubkey_t *peer_pubkey,
                                                                    const uint8_t *id_a,
                                                                    uintptr_t id_a_len,
                                                                    const uint8_t *id_b,
                                                                    uintptr_t id_b_len,
                                                                    uintptr_t klen,
                                                                    gmcrypto_rng_callback rng_callback,
                                                                    void *rng_context,
                                                                    uint8_t *out_r_a)
;

/*
 Receive the responder's reply and finish the initiator side:
 verify `S_B` (constant-time), and on success write the agreed key
 (exactly `klen` bytes, the `klen` given at `_new`) to `key_out`
 and the initiator's confirmation tag `S_A`
 ([`GMCRYPTO_SM2_KX_CONFIRM_SIZE`] = 32 bytes) to `out_s_a` — send
 `S_A` to the responder. `r_b` is the responder's ephemeral point
 (65 bytes); `s_b` its confirmation tag (32 bytes).

 CONSUMES + FREES the handle — even when the arguments are invalid
 or the confirmation fails (the `_finalize*` precedent); do NOT
 call `_free` afterwards. Returns [`GMCRYPTO_OK`] only when `S_B`
 verified and the key was written; every failure (invalid `R_B`,
 tag mismatch, null pointer) is the single [`GMCRYPTO_ERR`].
 **The caller owns wiping `key_out`.**

 # Safety

 `initiator` must be a live handle from a `_new` (not yet consumed
 or freed); `r_b` valid for 65 reads, `s_b` for 32 reads, `key_out`
 for `klen` writes, `out_s_a` for 32 writes.
 */

int gmcrypto_sm2_kx_initiator_confirm(gmcrypto_sm2_kx_initiator_t *initiator,
                                      const uint8_t *r_b,
                                      const uint8_t *s_b,
                                      uint8_t *key_out,
                                      uint8_t *out_s_a)
;

/*
 Free an UNCONSUMED initiator handle (abandonment path — e.g. the
 responder never replied). Safe on NULL. Do NOT call after
 `_confirm` (which already consumed + freed the handle).

 # Safety

 `initiator` must be NULL or a live handle from a `_new`.
 */
 void gmcrypto_sm2_kx_initiator_free(gmcrypto_sm2_kx_initiator_t *initiator) ;

/*
 Construct a key-exchange RESPONDER (party B). `local_privkey` is
 B's static key; `peer_pubkey` is A's static public key; ids and
 `klen` follow the [`gmcrypto_sm2_kx_initiator_new`] conventions
 (and must match the initiator's, or the confirmation tags will
 not verify). The handle then waits for the initiator's `R_A` —
 call [`gmcrypto_sm2_kx_responder_respond`].

 Returns the handle, or NULL on any failure. Pair with exactly one
 [`gmcrypto_sm2_kx_responder_finish`] (which frees) **or** one
 [`gmcrypto_sm2_kx_responder_free`].

 # Safety

 `local_privkey` / `peer_pubkey` must be valid handles from this
 library; `id_a` / `id_b` must be valid for their lengths when
 non-zero.
 */

gmcrypto_sm2_kx_responder_t *gmcrypto_sm2_kx_responder_new(const gmcrypto_sm2_privkey_t *local_privkey,
                                                           const gmcrypto_sm2_pubkey_t *peer_pubkey,
                                                           const uint8_t *id_a,
                                                           uintptr_t id_a_len,
                                                           const uint8_t *id_b,
                                                           uintptr_t id_b_len,
                                                           uintptr_t klen)
;

/*
 Receive the initiator's `R_A` (65 bytes) and produce the
 responder's reply: writes `R_B` (65 bytes) to `out_r_b` and the
 responder's confirmation tag `S_B` (32 bytes) to `out_s_b` — send
 both to the initiator, then call
 [`gmcrypto_sm2_kx_responder_finish`] with its `S_A`. Ephemeral
 randomness comes from the OS (`getrandom::SysRng`).

 The handle stays alive (it now holds the agreed key, wiped on
 drop, pending the initiator's confirmation). Calling `_respond`
 twice returns [`GMCRYPTO_ERR`] without disturbing the in-flight
 state. A FAILED respond (invalid `R_A`, RNG failure) spends the
 handle — every further call fails and the caller frees it.

 # Safety

 `responder` must be a live handle from `_new`; `r_a` valid for 65
 reads, `out_r_b` for 65 writes, `out_s_b` for 32 writes.
 */

int gmcrypto_sm2_kx_responder_respond(gmcrypto_sm2_kx_responder_t *responder,
                                      const uint8_t *r_a,
                                      uint8_t *out_r_b,
                                      uint8_t *out_s_b)
;

/*
 `_with_rng` variant of [`gmcrypto_sm2_kx_responder_respond`]:
 identical contract except the ephemeral randomness comes from the
 caller's `rng_callback` rather than `getrandom::SysRng`.

 # Safety

 As [`gmcrypto_sm2_kx_responder_respond`]; additionally
 `rng_callback` must be NULL or a valid function pointer honouring
 the callback contract.
 */

int gmcrypto_sm2_kx_responder_respond_with_rng(gmcrypto_sm2_kx_responder_t *responder,
                                               const uint8_t *r_a,
                                               gmcrypto_rng_callback rng_callback,
                                               void *rng_context,
                                               uint8_t *out_r_b,
                                               uint8_t *out_s_b)
;

/*
 Verify the initiator's confirmation tag `S_A` (32 bytes,
 constant-time) and on success write the agreed key (exactly
 `klen` bytes, the `klen` given at `_new`) to `key_out`.

 CONSUMES + FREES the handle — even when the arguments are invalid
 or the tag mismatches (the held key is wiped on drop in every
 case); do NOT call `_free` afterwards. Returns [`GMCRYPTO_OK`]
 only when `S_A` verified and the key was written; calling
 `_finish` before a successful `_respond` is the same single
 [`GMCRYPTO_ERR`]. **The caller owns wiping `key_out`.**

 # Safety

 `responder` must be a live handle from `_new` (not yet consumed
 or freed); `s_a` valid for 32 reads, `key_out` for `klen` writes.
 */

int gmcrypto_sm2_kx_responder_finish(gmcrypto_sm2_kx_responder_t *responder,
                                     const uint8_t *s_a,
                                     uint8_t *key_out)
;

/*
 Free an UNCONSUMED responder handle (abandonment path — e.g. the
 initiator never confirmed, or a `_respond` failure spent the
 handle). Safe on NULL. Do NOT call after `_finish` (which already
 consumed + freed the handle). Any held key material is wiped.

 # Safety

 `responder` must be NULL or a live handle from `_new`.
 */
 void gmcrypto_sm2_kx_responder_free(gmcrypto_sm2_kx_responder_t *responder) ;

#endif  /* GMCRYPTO_H_ */
