[package]
name = "gmcrypto-c"
description = "C ABI for gmcrypto-core — cdylib + staticlib exposing SM2/SM3/SM4/HMAC/PBKDF2 to C / C++ / Python / Go / Zig callers via opaque handles"
keywords = ["sm2", "sm3", "sm4", "ffi", "c-abi"]
categories = ["cryptography", "api-bindings"]
readme = "README.md"
version.workspace = true
edition.workspace = true
license.workspace = true
repository.workspace = true
rust-version.workspace = true

[lib]
crate-type = ["cdylib", "staticlib", "rlib"]

[dependencies]
# v0.4 W4 — the C ABI is a thin shim over gmcrypto-core. The FFI crate
# is `std`-OK (it's the language-binding layer, not the no_std crypto
# primitives). RNG is sourced via `getrandom::SysRng` internally
# per Q4.18; the C surface does not pass an RNG callback in v0.4.
#
# v0.23 W3 (A-1, 1.0 contract): the AEAD (SM4-GCM/CCM) and SM4-XTS
# modes are ALWAYS compiled into the C ABI. Unlike `gmcrypto-core`
# (where Rust callers opt in via cargo features), the C shim is the
# language-binding layer and must export ONE complete, permanent ABI:
# the committed `include/gmcrypto.h` always matches a default
# `cargo build -p gmcrypto-c`. So `sm4-aead` + `sm4-xts` are enabled
# unconditionally on the core dep here (no forwarding C-shim feature).
# v1.2 (Q2.4): `sm2-key-exchange` joins them under the same posture —
# the SM2-KX FFI symbols + opaque types are always-on.
gmcrypto-core = { path = "../gmcrypto-core", version = "=1.2.0", features = ["sm4-aead", "sm4-xts", "sm2-key-exchange"] }
getrandom.workspace = true
rand_core.workspace = true

[dev-dependencies]
hex-literal.workspace = true
# Needed only by the c_smoke integration test to construct a known
# SM2 scalar before round-tripping it through the FFI.
crypto-bigint.workspace = true

[build-dependencies]
# v0.4 W4 / Q4.12 — cbindgen is a build-time-only dep, gated on the
# `regen-header` feature. Default builds DO NOT run cbindgen; they
# use the committed `include/gmcrypto.h`. CI verifies the committed
# header matches a fresh cbindgen run via `git diff --exit-code`.
cbindgen = { version = "0.29", optional = true, default-features = false }

[features]
default = []
# Opt-in: regenerate `include/gmcrypto.h` via cbindgen on build.
# Default builds skip cbindgen entirely (saves ~5s per build and
# avoids the cbindgen dependency on every consumer's machine).
#
# v0.23 W3 (A-1, 1.0 contract): the AEAD (SM4-GCM/CCM, single-shot +
# streaming) and SM4-XTS FFI surfaces are now compiled UNCONDITIONALLY
# into the C ABI (the `sm4-aead` / `sm4-xts` forwarding features were
# removed — see the `gmcrypto-core` dep above). The committed
# `include/gmcrypto.h` therefore always matches a default
# `cargo build -p gmcrypto-c`, so `regen-header` no longer needs to
# "imply" anything — every prototype, opaque struct type, and const
# is present in any build.
regen-header = ["dep:cbindgen"]

[lints.rust]
# v0.4 W4 / Q4.7 — `unsafe_code = "forbid"` is impossible on an FFI
# crate (slice reconstruction, `Box::from_raw`, `#[unsafe(no_mangle)]`
# all require `unsafe`). Every `unsafe` block in `src/lib.rs` carries
# a `// SAFETY:` comment naming the caller-side preconditions.
# gmcrypto-core itself stays `unsafe_code = "forbid"` workspace-wide.
unsafe_code = "warn"
missing_docs = "warn"

[lints.clippy]
pedantic = { level = "warn", priority = -1 }
nursery = { level = "warn", priority = -1 }
module_name_repetitions = "allow"
must_use_candidate = "allow"
# FFI surfaces use many u8 pointers / size_t pairs; the lint
# generates noise without flagging real issues.
similar_names = "allow"
# v0.9 W4 — the single-shot AEAD entry points reconstruct several
# slices in one scope (key / nonce / aad / pt / ct / tag); short
# `k`/`n`/`a`/`p`/`c`/`t` bindings mirror the existing CBC entry
# points and keep the uniform reconstruct-delegate shape readable.
# Same rationale as `similar_names` above.
many_single_char_names = "allow"
# Pedantic/nursery lints that conflict with the FFI shim shape:
# every entry point follows the same null-check + slice-reconstruct
# + delegate + write_output pattern. Refactoring each `match` into
# `.map_or` or each `if let` into `let-else` would obscure the
# uniform shape without changing semantics.
option_if_let_else = "allow"
manual_let_else = "allow"
missing_const_for_fn = "allow"
unnecessary_box_returns = "allow"
# cbindgen recognizes byte-literal `CStr::from_bytes_with_nul`; the
# `c"..."` literal form is post-MSRV 1.85.
manual_c_str_literals = "allow"
# Long doc comments on the FFI module + per-entry-point bullets.
doc_markdown = "allow"
too_long_first_doc_paragraph = "allow"
