gmcrypto-core 0.1.0

Constant-time-designed pure-Rust SM2/SM3 primitives (no_std + alloc) with an in-CI dudect timing-leak regression harness
Documentation

gm-crypto-rs

Constant-time-designed pure-Rust SM2 / SM3 SDK for Chinese national cryptography. v0.2 will add SM4 and SM2 encrypt/decrypt — see the roadmap below.

CI dudect smoke dudect nightly

Personal project notice: not affiliated with, endorsed by, sponsored by, or certified by any upstream cryptography project, payment gateway, standards body, or vendor.

What this is

A small, auditable, pure-Rust SM2 + SM3 SDK whose central differentiating commitment is that the SM2 private-key path uses constant-time-designed code paths guarded by an in-CI dudect-bencher detectable-leak regression harness. SM4 lands in v0.2.

The harness reports timing-leak detection events. It does not prove constant-time. Low |tau| values mean the test could not detect a leak with the budget given, not that no leak exists. Language taken directly from dudect-bencher's own docs.

v0.1 has a known limitation worth surfacing here, not just in SECURITY.md: the harness's ct_sign target splits classes by the private key d, so it catches (1+d).invert() leaks (currently diluted under the gate) but is structurally blind to leaks on the per-sample nonce k — including the Fp::invert(Z) inside kg.to_affine() after mul_g(k). v0.2 fixes both invert sites and reworks the harness to specifically exercise the nonce path. See SECURITY.md for the full posture.

The differentiator vs. existing Rust SM2 crates (notably RustCrypto/sm2, which already aims for constant-time secret-dependent operations in its design) is the in-CI regression gate, not the design intent in isolation.

What this isn't

  • Not a TLS/TLCP implementation.
  • Not SM9, ZUC, post-quantum.
  • Not an HSM/SDF/SKF integration.
  • Not a certified cryptographic module.
  • Not constant-time on CPUs with data-dependent multiply latencies (some older x86, some embedded).
  • Not a comprehensive SM-crypto library yet — see the milestone roadmap.

v0.1 scope

  • SM3 hash function (#![no_std] + alloc).
  • SM2 sign / verify with custom signer ID (default 1234567812345678 per GM/T 0009).
  • Constant-time-designed Fp and Fn field arithmetic via crypto-bigint = 0.6.
  • Renes-Costello-Batina complete addition formulas for the SM2 curve (a=-3 specialized).
  • Fixed-base and variable-base scalar multiplication, both constant-time-designed with subtle::ConditionallySelectable linear-scan table lookup.
  • Fixed-K masked-select signing retry: the retry loop runs K=2 iterations unconditionally, regardless of which (if any) iteration produced a valid signature. The constant-time contract holds for any RNG that respects CryptoRng; pathological RNGs cannot leak the secret via observable retry count.
  • Minimal ASN.1 DER for SEQUENCE { r, s }.
  • KAT vectors from GB/T 32905-2016 (SM3) and GB/T 32918.2-2017 (SM2).
  • gmssl CLI reachability check (full bidirectional interop deferred to v0.3 when PKCS#8 + X.509 SPKI ship).
  • dudect-bencher harness with PR-smoke (10⁴ samples, |tau|<0.20) and nightly (10⁵ samples, same |tau|<0.20 gate, more samples = tighter empirical confidence) modes, plus a deliberately-leaky negative control that proves the harness can detect leaks.

See SECURITY.md for v0.1's known constant-time limitations, notably the dependency on crypto-bigint::ConstMontyForm::invert.

Roadmap

Version Scope
v0.2 Fermat-invert via pow_bounded_exp (replaces non-CT crypto-bigint::invert); SM2 encrypt/decrypt; GM/T 0009 ciphertext DER; SM4, SM4-CBC; HMAC-SM3; PBKDF2-HMAC-SM3
v0.3 Full ASN.1, PEM, encrypted PKCS#8, X.509 SPKI extractor; full bidirectional gmssl interop
v0.4 C ABI (gmcrypto-c), wasm32-unknown-unknown build target
v1.0 API stabilization

Quick-start

use gmcrypto_core::sm2::{
    sign_with_id, verify_with_id, Sm2PrivateKey, Sm2PublicKey, DEFAULT_SIGNER_ID,
};
use crypto_bigint::U256;
use rand_core::OsRng;

let d = U256::from_be_hex(
    "3945208F7B2144B13F36E38AC6D39F95889393692860B51A42FB81EF4DF7C5B8",
);
let key = Sm2PrivateKey::new(d).expect("d in [1, n-2]");
let public = Sm2PublicKey::from_point(key.public_key());

let sig = sign_with_id(&key, DEFAULT_SIGNER_ID, b"hello", &mut OsRng).unwrap();
assert!(verify_with_id(&public, DEFAULT_SIGNER_ID, b"hello", &sig));

Threat model

See SECURITY.md. Briefly: server-side use, dedicated host, operator-trusted, network MITM in scope, side-channel attacks beyond what the dudect harness covers are NOT in scope.

Build & test

cargo test --workspace                                # unit + integration
cargo bench --bench timing_leaks                      # local timing harness (~75s)
DUDECT_SAMPLES=10000 cargo bench --bench timing_leaks # match CI smoke budget

gmssl interop test (gated; install gmssl v3.1.1 to enable):

GMCRYPTO_GMSSL=1 cargo test --test interop_gmssl

License

Apache-2.0. See LICENSE.

Some reference outputs use the upstream gmssl tool. This project is independent of that project.