Expand description
Constant-time-designed pure-Rust SM2 / SM3 / SM4 primitives.
no_std + alloc, no C dependency, MSRV 1.85. Every secret-touching path
is written against subtle’s constant-time
primitives — no ==, no if, no bool on a secret-derived value — and
guarded in CI by a dudect-based
detectable-leak regression harness. Failure modes are deliberately
indistinguishable: fallible operations return one opaque Error or
None, never a reason.
This crate has not been independently audited. Assurance is internal
(KAT vectors, gmssl interop, the timing harness, a cargo-fuzz suite) and
the project is solo-maintained with no support SLA. Read
SECURITY.md
for the threat model and disclosure process, and the
README for scope,
before relying on it.
§Usage
[dependencies]
gmcrypto-core = "1.11"default = [] — the base build is the primitives below with no optional
dependency. Most of what this crate can do is opt-in; see
Crate features.
use gmcrypto_core::sm2::{DEFAULT_SIGNER_ID, Sm2PrivateKey, sign_with_id, verify_with_id};
use gmcrypto_core::{sm3, sm4};
use getrandom::SysRng; // any `rand_core::TryCryptoRng`; this crate ships no RNG
// SM3 (GB/T 32905): 32-byte digest.
let digest = sm3::hash(b"hello");
// SM2 (GB/T 32918): sign and verify under the GB/T default signer ID.
let key = Sm2PrivateKey::from_bytes_be(&secret_32) // your scalar, big-endian
.into_option().ok_or("scalar out of range")?;
let sig = sign_with_id(&key, DEFAULT_SIGNER_ID, b"hello", &mut SysRng)?;
assert!(verify_with_id(&key.public_key(), DEFAULT_SIGNER_ID, b"hello", &sig));
// SM4-CBC (GB/T 32907), PKCS#7 padded. The IV is caller-supplied and must
// be unpredictable per message; a fixed one here only because it is an example.
let key = [0x42u8; sm4::KEY_SIZE];
let iv = [0x24u8; sm4::BLOCK_SIZE];
let ciphertext = sm4::mode_cbc::encrypt(&key, &iv, b"hello world");
let recovered = sm4::mode_cbc::decrypt(&key, &iv, &ciphertext)
.ok_or("bad padding or length — one opaque None either way")?;
assert_eq!(recovered, b"hello world");Signing and public-key encryption take any rand_core::TryCryptoRng; an
RNG failure surfaces as the same opaque Error as any other failure.
§Modules
sm2 | SM2 sign / verify, encrypt / decrypt (GB/T 32918). With sm2-key-exchange: sm2::key_exchange, GM/T 0003.3 key agreement |
sm3 | SM3 hash (GB/T 32905), single-shot and streaming |
sm4 | SM4 block cipher (GB/T 32907) with ECB / CBC / CTR. With sm4-aead: GCM, CCM, incremental GCM, length-committed streaming CCM. With sm4-xts: XTS |
hmac | HMAC-SM3 (RFC 2104), single-shot and streaming |
kdf | PBKDF2-HMAC-SM3 (RFC 8018 §5.2) |
asn1 | Strict-canonical DER for the two SM2 wire structures: RFC 3279 signatures, GM/T 0009 ciphertexts |
pem, spki, sec1, pkcs8 | RFC 7468 PEM, RFC 5280 SubjectPublicKeyInfo, RFC 5915 ECPrivateKey, RFC 5958 OneAsymmetricKey with PBES2 encryption |
x509 | With x509: X.509-with-SM2 leaf parse and signature verify, linear chain verify. No trust decisions beyond structure |
tlcp | With tlcp: TLCP (GB/T 38636-2020) key schedule, record protection, and — with x509 — [sign, enc] pair verification. Not a protocol implementation |
§Crate features
default = []: no_std + alloc, no optional dependency. Every feature
is additive and opt-in. Items behind a feature are badged with it on
docs.rs.
Capability features — pure-core, no new dependency unless stated:
sm4-aead— SM4-GCM and SM4-CCM (sm4::mode_gcm,sm4::mode_ccm), plus incremental-input GCM (sm4::gcm_streaming) and length-committed streaming CCM (sm4::ccm_streaming). Pulls the workspace-internalgmcrypto-simdfor GHASH (CLMUL / PMULL, with a constant-time software fallback).sm4-xts— SM4-XTS sector mode (sm4::mode_xts), per GB/T 17964-2021: bit-reflected α-doubling, not IEEE 1619. Single-shot and in-place multi-sector. Confidentiality only — XTS does not authenticate.sm2-key-exchange— GM/T 0003.3 key agreement (sm2::key_exchange): consume-on-transition role state machines, single-use ephemerals, key released only after the peer’s confirmation tag verifies. The standard-permitted no-confirmation completers are also provided for protocols — TLCP among them — that carry confirmation themselves.x509— X.509-with-SM2 certificate parse and signature verify (GM/T 0015 profile), strict DER, v3 only. Public inputs only, so no constant-time obligation arises. Structural trust only — see the module docs.tlcp— the TLCP (GB/T 38636-2020) crypto toolkit:P_SM3key schedule, SM4-CBC and SM4-GCM record protection with a Lucky13-hardened CBC deprotect, and withx509the[sign, enc]double-certificate pair check. No handshake state machine, framing or I/O.
Implementation features — byte-identical output, different code path:
sm4-bitsliced— routes the SM4 S-box through a table-less, gate-only bitsliced inversion in GF(2^8). Constant-time by construction: no table lookups, no branches on secret bits. The default path is a linear scan with the same property; this one is faster under SIMD.sm4-bitsliced-simd— packs that bitsliced S-box into AVX2 (x86_64) or NEON (aarch64) lanes for the batch paths, with runtime detection and a scalar fallback. Impliessm4-bitsliced; pullsgmcrypto-simd, where the crate’s onlyunsafelives.
Ecosystem trait fits — each pulls one pre-1.0 RustCrypto crate, so a
breaking release of that crate is not covered by this crate’s SemVer:
digest-traits—digest::Digestforsm3::Sm3,digest::Macforhmac::HmacSm3(digest = "0.11").cipher-traits—cipher::{BlockCipherEncrypt, BlockCipherDecrypt, KeyInit}forsm4::Sm4Cipher(cipher = "0.5").aead-traits—aead::{AeadCore, AeadInOut, KeyInit}forsm4::Sm4Gcmandsm4::Sm4Ccm, which yields theVec-returningaead::Aeadthrough that crate’s blanket impl. Thin wrappers overmode_gcm/mode_ccm; every failure becomes the one opaqueaead::Error. Impliessm4-aead(aead = "0.6").crypto-bigint-scalar—sm2::Sm2PrivateKey::from_scalar, taking acrypto_bigint::U256directly. The always-onfrom_bytes_beis the recommended constructor; this exists for callers who already hold the scalar as that type and acceptcrypto-bigint’s major-version contract.
§wasm32-unknown-unknown
Builds on the target, gated in CI at stable and MSRV. The crate does not
pull getrandom’s wasm_js backend or wasm-bindgen into its default
graph; wasm callers enable wasm_js in their own Cargo.toml and pass
getrandom::SysRng — or any other rand_core::TryCryptoRng — to the SM2
operations that need randomness.
§Release notes
Every published version is in
CHANGELOG.md.
The project is at 1.x and additive since 1.0.0; cargo-semver-checks gates
breaking changes in CI, and the three workspace crates release together at
one version.
Modules§
- asn1
- Minimal ASN.1 DER subset.
- hmac
- HMAC-SM3 — RFC 2104 keyed MAC over GB/T 32905-2016 SM3.
- kdf
- Key derivation functions.
- pem
- Hand-rolled PEM (RFC 7468) codec.
- pkcs8
- PKCS#8
OneAsymmetricKeycodec (RFC 5958) + PBES2 encryption (RFC 8018). - sec1
- SEC1
ECPrivateKeycodec (RFC 5915) for SM2 keys. - sm2
- SM2 elliptic curve cryptography (GB/T 32918-2017).
- sm3
- SM3 hash function (GB/T 32905-2016).
- sm4
- SM4 block cipher (GB/T 32907-2016) and operating modes.
- spki
- X.509
SubjectPublicKeyInfocodec (RFC 5280 §4.1.2.7) for SM2 keys. - tlcp
tlcp - TLCP (GB/T 38636-2020) cryptographic toolkit.
- x509
x509 - X.509-with-SM2: leaf certificate parse + SM2-with-SM3 signature verify (v1.3; GM/T 0015 profile over the RFC 5280 structure).
Enums§
- Error
- Workspace-wide failure type.