Skip to main content

gmcrypto_core/
lib.rs

1//! Constant-time-designed pure-Rust SM2 / SM3 / SM4 primitives.
2//!
3//! See the workspace `README.md` for scope, threat model, and the honest
4//! framing of the in-CI `dudect`-based timing-leak regression harness.
5//!
6//! # Modules
7//!
8//! - [`sm2`] — SM2 elliptic-curve sign / verify / encrypt / decrypt
9//!   (GB/T 32918). Comb-table fixed-base scalar mult (v0.3 W6). The
10//!   opt-in `sm2-key-exchange` feature (v1.1) adds
11//!   `sm2::key_exchange` — GM/T 0003.3 key agreement with key
12//!   confirmation (`Sm2KxInitiator` / `Sm2KxResponder` role
13//!   state-machines).
14//! - [`sm3`] — SM3 hash (GB/T 32905) with streaming `new/update/finalize`.
15//! - `x509` (the module appears with the feature of the same name, v1.3) —
16//!   X.509-with-SM2 LEAF certificate parse + SM2-with-SM3 signature verify
17//!   over the exact wire `tbsCertificate` bytes. **No trust decisions**
18//!   (no chains / time checks / extension interpretation / revocation).
19//! - [`sm4`] — SM4 block cipher (GB/T 32907) + CBC and CTR modes
20//!   (single-shot and streaming). The opt-in `sm4-aead` feature adds
21//!   SM4-GCM (single-shot + incremental-input buffered) and SM4-CCM;
22//!   the opt-in `sm4-xts` feature adds SM4-XTS (GB/T 17964-2021,
23//!   single-shot + in-place multi-sector). v0.4 W3 adds an opt-in
24//!   bitsliced (table-less, gate-only) S-box behind the
25//!   `sm4-bitsliced` feature.
26//! - [`hmac`] — HMAC-SM3 (RFC 2104), single-shot + v0.3 W5 streaming.
27//! - [`kdf`] — PBKDF2-HMAC-SM3 (RFC 8018 §5.2).
28//! - [`asn1`] — strict-canonical DER reader / writer / OID constants
29//!   (v0.3 W1); GM/T 0009 SM2 ciphertext SEQUENCE; RFC 3279 SM2
30//!   signature SEQUENCE.
31//! - [`pem`] — RFC 7468 PEM codec (v0.3 W2; hand-rolled, `no_std`).
32//! - [`spki`] — RFC 5280 `SubjectPublicKeyInfo` for SM2 (v0.3 W2).
33//! - [`sec1`] — RFC 5915 `ECPrivateKey` + SEC1 uncompressed point (v0.3 W2).
34//! - [`pkcs8`] — RFC 5958 `OneAsymmetricKey` + RFC 8018 PBES2 (v0.3 W2).
35//! - [`traits`] — in-crate `Hash` / `Mac` / `BlockCipher` traits
36//!   (v0.3 W5). v0.4 W2 adds RustCrypto-trait fit (`digest::Digest`,
37//!   `digest::Mac`, `cipher::BlockCipherEncrypt`/`BlockCipherDecrypt`)
38//!   behind the opt-in `digest-traits` / `cipher-traits` features
39//!   (migrated to `digest 0.11` / `cipher 0.5` in v0.11).
40//!
41//! # Crate features
42//!
43//! - `default` — `no_std`, `alloc`-only. No optional dependencies.
44//! - `digest-traits` — opt-in (v0.4 W2). Implements `digest::Digest` for
45//!   [`sm3::Sm3`] and `digest::Mac` for [`hmac::HmacSm3`]. Pulls
46//!   `digest = "0.11"` — a pre-1.0 ecosystem crate, so a breaking `digest`
47//!   release is **not** covered by `gmcrypto-core`'s `SemVer` (bump your own).
48//! - `cipher-traits` — opt-in (v0.4 W2). Implements
49//!   `cipher::{BlockCipherEncrypt, BlockCipherDecrypt, BlockSizeUser,
50//!   KeySizeUser, KeyInit}` for [`sm4::Sm4Cipher`]. Pulls `cipher = "0.5"` —
51//!   a pre-1.0 ecosystem crate, so a breaking `cipher` release is **not**
52//!   covered by `gmcrypto-core`'s `SemVer` (bump your own).
53//! - `sm4-bitsliced` — opt-in (v0.4 W3). Routes the SM4 S-box through
54//!   a bitsliced (table-less, gate-only) Itoh-Tsujii inversion in
55//!   GF(2^8). Byte-identical output to the default linear-scan path;
56//!   constant-time by construction (no table lookups, no branches on
57//!   secret bits).
58//! - `sm4-bitsliced-simd` — opt-in (v0.5 W4 scaffolding; AVX2 / NEON
59//!   intrinsic implementations land in v0.5.x). Implies
60//!   `sm4-bitsliced`. Default-off.
61//! - `crypto-bigint-scalar` — opt-in (v0.5 W5). Exposes
62//!   [`sm2::Sm2PrivateKey::from_scalar`] which takes a
63//!   `crypto_bigint::U256` directly. Default-off; the always-on
64//!   `from_bytes_be` constructor is the recommended path for callers
65//!   who don't want a transitive `crypto-bigint` dep.
66//! - `sm4-aead` — opt-in (v0.8). SM4-GCM (`sm4::mode_gcm`, plus the
67//!   v0.9 incremental-input buffered `sm4::gcm_streaming`) and
68//!   SM4-CCM (`sm4::mode_ccm`) authenticated encryption. Pulls the
69//!   workspace-internal `gmcrypto-simd` for the GHASH primitive
70//!   (CLMUL / PMULL / constant-time software fallback).
71//! - `sm4-xts` — opt-in (v0.12). SM4-XTS tweakable disk/sector mode
72//!   (`sm4::mode_xts`; GB/T 17964-2021, bit-reflected α-doubling —
73//!   **not** IEEE 1619), single-shot + the v0.15 in-place
74//!   multi-sector helpers. Pure-core, no new dependency.
75//!   Confidentiality only — XTS does not authenticate.
76//! - `sm2-key-exchange` — opt-in (v1.1). GM/T 0003.3 ≡ GB/T
77//!   32918.3-2016 key agreement with mandatory key confirmation
78//!   (`sm2::key_exchange`): consume-on-transition role state-machines,
79//!   single-use ephemerals, commit-on-confirm key release,
80//!   `ZeroizeOnDrop` agreed key. Pure-core, no new dependency;
81//!   byte-identical to the GM/T 0003.5 recommended-curve worked
82//!   example. The C ABI projection ships in `gmcrypto-c` (v1.2).
83//! - `x509` — opt-in (v1.3). X.509-with-SM2 leaf certificate parse +
84//!   signature verify (GM/T 0015 profile): strict in-repo DER, v3-only,
85//!   `sm2-sign-with-sm3` outer==inner, SPKI delegated to [`spki`].
86//!   Pure-core, no new dependency, public inputs only (no constant-time
87//!   obligations arise). NO trust decisions — see the module docs.
88//!
89//! # `wasm32-unknown-unknown`
90//!
91//! Builds clean as of v0.4 W1. The crate is `no_std + alloc` only and
92//! does NOT pull `getrandom`'s `wasm_js` backend or `wasm-bindgen` /
93//! `js-sys` into its default dep graph. Wasm callers wire their own
94//! `rand_core::Rng` impl — see the workspace `README.md`.
95
96#![no_std]
97#![deny(missing_docs)]
98#![doc(html_root_url = "https://docs.rs/gmcrypto-core/1.0.0")]
99
100extern crate alloc;
101
102pub mod asn1;
103pub mod hmac;
104pub mod kdf;
105pub mod pem;
106pub mod pkcs8;
107pub mod sec1;
108pub mod sm2;
109pub mod sm3;
110pub mod sm4;
111pub mod spki;
112// v1.3 — X.509-with-SM2 leaf certificate parse + signature verify. Opt-in
113// via the `x509` feature; default builds are byte-identical. NO trust
114// decisions. See docs/v1.3-x509-sm2-design.md.
115#[cfg(feature = "x509")]
116pub mod x509;
117// Not public API / not SemVer — low-level in-crate trait surface kept pub for internal cross-module + dev-crate use; the public trait fit is the opt-in RustCrypto digest/cipher impls.
118#[doc(hidden)]
119pub mod traits;
120
121/// Internal helper: canonical 32-byte big-endian encoding of a `U256`.
122///
123/// `crypto-bigint`'s `Encoding::to_be_bytes` returns an `EncodedUint`
124/// wrapper, not a `[u8; 32]`. v0.22 reshaped the byte-adjacent public
125/// types (`asn1::sig` signatures, `asn1::ciphertext::Sm2Ciphertext`) to
126/// `[u8; 32]` so the public API names no `crypto-bigint` type; this pins
127/// the conversion in one place for the internal producers (sign / encrypt /
128/// raw-ciphertext). Not part of the public API.
129#[inline]
130pub(crate) fn u256_to_be32(v: &crypto_bigint::U256) -> [u8; 32] {
131    v.to_be_bytes().into()
132}
133
134/// Workspace-wide failure type (v0.5 W5).
135///
136/// Every fallible public surface in `gmcrypto-core` that does not
137/// return `Option` / `bool` / `subtle::CtOption` returns
138/// `Result<_, Error>`. The single `Failed` variant is deliberate per
139/// the **failure-mode invariant** (see `SECURITY.md`): distinguishing
140/// failure modes leaks information to padding-oracle / invalid-curve /
141/// password-oracle attackers.
142///
143/// Per-module aliases keep the established import paths working:
144/// `sm2::Error`, `pem::Error`, `pkcs8::Error` are type aliases for
145/// this one type. Prior to v0.5 these were separate per-module enums
146/// (`SignError`, `EncryptError`, `DecryptError`, `pem::Error`,
147/// `pkcs8::Error`) all with a single `Failed` variant; v0.5 unifies
148/// them per Q5.16 in `docs/v0.5-scope.md`.
149#[derive(Debug, Clone, Copy, PartialEq, Eq)]
150#[non_exhaustive]
151pub enum Error {
152    /// The operation failed. No further information is exposed —
153    /// distinguishing failure modes leaks attacker-useful signal.
154    Failed,
155}
156
157impl core::fmt::Display for Error {
158    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
159        f.write_str("gmcrypto-core operation failed")
160    }
161}
162
163impl core::error::Error for Error {}