diff --git a/.github/scripts/sanity_check.sh b/.github/scripts/sanity_check.sh
new file mode 100755
index 0000000..ff1c079
--- /dev/null
+++ b/.github/scripts/sanity_check.sh
@@ -0,0 +1,162 @@
+#!/usr/bin/env bash
+# =============================================================================
+# libsmx 安全性扫描脚本
+# =============================================================================
+# 被 CI 工作流调用,执行以下检查:
+# 1. 扫描非测试代码中的 panic!/unwrap()/expect()
+# 2. 检查是否有硬编码密钥泄露
+# 3. 验证 Cargo.toml 版本号与 Git Tag 匹配(仅在 tag 构建时)
+# =============================================================================
+
+set -euo pipefail
+
+RED='\033[0;31m'
+GREEN='\033[0;32m'
+YELLOW='\033[1;33m'
+NC='\033[0m'
+
+ERRORS=0
+
+pass() { echo -e "${GREEN}[PASS]${NC} $1"; }
+warn() { echo -e "${YELLOW}[WARN]${NC} $1"; }
+fail() { echo -e "${RED}[FAIL]${NC} $1"; ERRORS=$((ERRORS + 1)); }
+
+echo "=========================================="
+echo " libsmx Security Sanity Checks"
+echo "=========================================="
+echo ""
+
+# ─────────────────────────────────────────────────────────────────────────────
+# 1. 扫描非测试代码中的 panic!/unwrap()/expect()
+# ─────────────────────────────────────────────────────────────────────────────
+# Reason: 密码学库不应在非测试代码中 panic,应返回 Result/Option
+echo "--- 检查 panic/unwrap/expect ---"
+
+# 排除规则:
+# - #[cfg(test)] 块和 mod tests 块内的代码
+# - benches/ 和 tests/ 目录
+# - 白名单:try_into().unwrap() 用于固定大小切片转换(已知安全)
+
+# 收集 src/ 下所有非测试 .rs 文件中的匹配项
+PANIC_HITS=$(grep -rn 'panic!\|\.unwrap()\|\.expect(' src/ --include='*.rs' \
+ | grep -v '#\[cfg(test)\]' \
+ | grep -v 'mod tests' \
+ | grep -v '// test' \
+ | grep -v '#\[test\]' \
+ | grep -v 'fn test_' \
+ || true)
+
+if [ -n "$PANIC_HITS" ]; then
+ # 统计行数
+ COUNT=$(echo "$PANIC_HITS" | wc -l)
+
+ # 分类统计
+ UNWRAP_COUNT=$(echo "$PANIC_HITS" | grep -c '\.unwrap()' || true)
+ EXPECT_COUNT=$(echo "$PANIC_HITS" | grep -c '\.expect(' || true)
+ PANIC_COUNT=$(echo "$PANIC_HITS" | grep -c 'panic!' || true)
+
+ # try_into().unwrap() 是固定大小切片转换的惯用法,已知不会 panic
+ SAFE_UNWRAP=$(echo "$PANIC_HITS" | grep -c 'try_into().unwrap()' || true)
+ UNSAFE_UNWRAP=$((UNWRAP_COUNT - SAFE_UNWRAP))
+
+ if [ "$PANIC_COUNT" -gt 0 ]; then
+ fail "发现 $PANIC_COUNT 处 panic! 调用(非测试代码)"
+ echo "$PANIC_HITS" | grep 'panic!' | head -5
+ fi
+
+ if [ "$UNSAFE_UNWRAP" -gt 0 ]; then
+ warn "发现 $UNSAFE_UNWRAP 处 .unwrap() 调用(不含 try_into().unwrap())"
+ echo "$PANIC_HITS" | grep '\.unwrap()' | grep -v 'try_into().unwrap()' | head -5
+ fi
+
+ if [ "$EXPECT_COUNT" -gt 0 ]; then
+ warn "发现 $EXPECT_COUNT 处 .expect() 调用"
+ echo "$PANIC_HITS" | grep '\.expect(' | head -5
+ fi
+
+ if [ "$SAFE_UNWRAP" -gt 0 ]; then
+ echo -e " ${GREEN}(白名单)${NC} $SAFE_UNWRAP 处 try_into().unwrap()(固定大小切片转换,已知安全)"
+ fi
+else
+ pass "非测试代码中无 panic/unwrap/expect"
+fi
+
+# ─────────────────────────────────────────────────────────────────────────────
+# 2. 检查硬编码密钥/敏感信息
+# ─────────────────────────────────────────────────────────────────────────────
+echo ""
+echo "--- 检查硬编码敏感信息 ---"
+
+# Reason: 防止私钥、密码等敏感信息被提交到仓库
+SENSITIVE_PATTERNS='(private.?key|secret.?key|password|passwd|api.?key|token)\s*=\s*["\x27][^\x27"]+["\x27]'
+SENSITIVE_HITS=$(grep -rniE "$SENSITIVE_PATTERNS" src/ --include='*.rs' \
+ | grep -v '#\[cfg(test)\]' \
+ | grep -v 'mod tests' \
+ | grep -v '// test' \
+ | grep -v 'fn test_' \
+ || true)
+
+if [ -n "$SENSITIVE_HITS" ]; then
+ warn "发现疑似硬编码敏感信息(请人工审查):"
+ echo "$SENSITIVE_HITS" | head -5
+else
+ pass "未发现硬编码敏感信息"
+fi
+
+# ─────────────────────────────────────────────────────────────────────────────
+# 3. 验证 Cargo.toml 版本号与 Git Tag 匹配
+# ─────────────────────────────────────────────────────────────────────────────
+echo ""
+echo "--- 检查版本号一致性 ---"
+
+CARGO_VERSION=$(grep '^version' Cargo.toml | head -1 | sed 's/.*"\(.*\)".*/\1/')
+echo " Cargo.toml version: $CARGO_VERSION"
+
+# 如果在 CI 中且有 GITHUB_REF_NAME(tag 构建),验证匹配
+if [ -n "${GITHUB_REF_NAME:-}" ] && [[ "${GITHUB_REF:-}" == refs/tags/v* ]]; then
+ TAG_VERSION="${GITHUB_REF_NAME#v}"
+ echo " Git tag version: $TAG_VERSION"
+ if [ "$TAG_VERSION" != "$CARGO_VERSION" ]; then
+ fail "Tag 版本 ($TAG_VERSION) 与 Cargo.toml 版本 ($CARGO_VERSION) 不匹配!"
+ else
+ pass "Tag 与 Cargo.toml 版本匹配"
+ fi
+else
+ echo " (非 tag 构建,跳过版本匹配检查)"
+ pass "Cargo.toml 版本号格式正确: $CARGO_VERSION"
+fi
+
+# ─────────────────────────────────────────────────────────────────────────────
+# 4. 检查 unsafe 代码
+# ─────────────────────────────────────────────────────────────────────────────
+echo ""
+echo "--- 检查 unsafe 代码 ---"
+
+UNSAFE_HITS=$(grep -rn 'unsafe' src/ --include='*.rs' \
+ | grep -v '#!\[forbid(unsafe_code)\]' \
+ | grep -v '// unsafe' \
+ | grep -v '#\[cfg(test)\]' \
+ | grep -v 'mod tests' \
+ || true)
+
+if [ -n "$UNSAFE_HITS" ]; then
+ fail "发现 unsafe 代码(本项目使用 #![forbid(unsafe_code)]):"
+ echo "$UNSAFE_HITS" | head -5
+else
+ pass "未发现 unsafe 代码"
+fi
+
+# ─────────────────────────────────────────────────────────────────────────────
+# 汇总
+# ─────────────────────────────────────────────────────────────────────────────
+echo ""
+echo "=========================================="
+if [ "$ERRORS" -gt 0 ]; then
+ echo -e " ${RED}发现 $ERRORS 个错��!${NC}"
+ echo "=========================================="
+ exit 1
+else
+ echo -e " ${GREEN}所有安全检查通过${NC}"
+ echo "=========================================="
+ exit 0
+fi
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
new file mode 100644
index 0000000..dba55c0
--- /dev/null
+++ b/.github/workflows/ci.yml
@@ -0,0 +1,273 @@
+# =============================================================================
+# libsmx CI/CD 工作流
+# =============================================================================
+# 触发条件:
+# - main 分支的 push / PR → lint + test + coverage
+# - v* tag 推送 → 上述全部 + 自动发布
+# - PR → 额外运行 benchmark 并评论结果
+# =============================================================================
+
+name: CI
+
+on:
+ push:
+ branches: [main]
+ tags: ["v*"]
+ pull_request:
+ branches: [main]
+
+# 同一 PR 的新提交自动取消旧的运行,节省 CI 资源
+concurrency:
+ group: ${{ github.workflow }}-${{ github.ref }}
+ cancel-in-progress: true
+
+env:
+ CARGO_TERM_COLOR: always
+ # 强制使用 sparse 协议,加速 crates.io 索引同步
+ CARGO_REGISTRIES_CRATES_IO_PROTOCOL: sparse
+
+# =============================================================================
+# Job 1: 代码质量检查(格式化 + Clippy + 依赖漏洞扫描)
+# =============================================================================
+jobs:
+ lint:
+ name: Lint & Audit
+ runs-on: ubuntu-latest
+ steps:
+ - name: 签出代码
+ uses: actions/checkout@v4
+
+ - name: 安装 Rust 工具链(stable + rustfmt + clippy)
+ uses: dtolnay/rust-toolchain@stable
+ with:
+ components: rustfmt, clippy
+
+ - name: 缓存 Cargo 编译产物
+ uses: Swatinem/rust-cache@v2
+
+ - name: 检查代码格式
+ run: cargo fmt --check
+
+ - name: Clippy 检查(默认 features,所有警告视为错误)
+ run: cargo clippy --all-targets -- -D warnings
+
+ - name: Clippy 检查(no_std,无 alloc)
+ # Reason: alloc-gated 函数在 no_std 模式下会产生 "unused" 假阳性,
+ # 因此只做编译检查,不启用 -D warnings
+ run: cargo check --no-default-features
+
+ - name: 安装 cargo-audit
+ run: cargo install cargo-audit --locked
+
+ - name: 依赖漏洞扫描(CVE 检查)
+ run: cargo audit
+
+ - name: 安全性扫描(panic/unwrap 检查)
+ run: bash .github/scripts/sanity_check.sh
+ shell: bash
+
+ # =============================================================================
+ # Job 2: 多平台测试矩阵
+ # =============================================================================
+ test-matrix:
+ name: Test (${{ matrix.os }})
+ needs: lint
+ strategy:
+ fail-fast: false
+ matrix:
+ os: [ubuntu-latest, macos-latest, windows-latest]
+ runs-on: ${{ matrix.os }}
+ steps:
+ - name: 签出代码
+ uses: actions/checkout@v4
+
+ - name: 安装 Rust 工具链(stable)
+ uses: dtolnay/rust-toolchain@stable
+
+ - name: 缓存 Cargo 编译产物
+ uses: Swatinem/rust-cache@v2
+
+ # Reason: 在所有平台上显式设置 bash 以统一 Windows/Linux/macOS 行为
+ - name: 运行测试(默认 features = alloc)
+ run: cargo test --all-targets
+ shell: bash
+
+ - name: 运行测试(no_std,无 alloc)
+ # Reason: 验证嵌入式 / WASM / 裸机兼容性
+ run: cargo test --no-default-features --lib
+ shell: bash
+
+ - name: 运行文档测试
+ run: cargo test --doc
+ shell: bash
+
+ - name: 构建文档(警告视为错误)
+ run: cargo doc --no-deps
+ env:
+ RUSTDOCFLAGS: "-D warnings"
+ shell: bash
+
+ # =============================================================================
+ # Job 3: MSRV 验证(最低支持版本)
+ # =============================================================================
+ msrv:
+ name: MSRV (1.72.0)
+ needs: lint
+ runs-on: ubuntu-latest
+ steps:
+ - name: 签出代码
+ uses: actions/checkout@v4
+
+ - name: 安装 MSRV 工具链
+ uses: dtolnay/rust-toolchain@master
+ with:
+ toolchain: "1.72.0"
+
+ - name: 缓存 Cargo 编译产物
+ uses: Swatinem/rust-cache@v2
+
+ # Reason: MSRV 只验证能编译,不运行测试(避免 dev-dependencies 兼容问题)
+ - name: 检查编译(默认 features)
+ run: cargo check
+
+ - name: 检查编译(no_std)
+ run: cargo check --no-default-features
+
+ # =============================================================================
+ # Job 4: 测试覆盖率(仅 Linux,上传 Codecov)
+ # =============================================================================
+ coverage:
+ name: Coverage
+ needs: lint
+ runs-on: ubuntu-latest
+ steps:
+ - name: 签出代码
+ uses: actions/checkout@v4
+
+ - name: 安装 Rust 工具链(stable)
+ uses: dtolnay/rust-toolchain@stable
+
+ - name: 缓存 Cargo 编译产物
+ uses: Swatinem/rust-cache@v2
+
+ - name: 安装 cargo-tarpaulin
+ run: cargo install cargo-tarpaulin --locked
+
+ - name: 生成覆盖率报告
+ run: cargo tarpaulin --out xml --output-dir coverage/
+
+ - name: 上传覆盖率到 Codecov
+ uses: codecov/codecov-action@v4
+ with:
+ files: coverage/cobertura.xml
+ token: ${{ secrets.CODECOV_TOKEN }}
+ fail_ci_if_error: false
+ # Reason: fail_ci_if_error=false 避免 Codecov 暂时不可用时阻断 CI
+
+ # =============================================================================
+ # Job 5: 性能回归检测(仅 PR 触发)
+ # =============================================================================
+ benchmark:
+ name: Benchmark
+ if: github.event_name == 'pull_request'
+ needs: lint
+ runs-on: ubuntu-latest
+ permissions:
+ pull-requests: write
+ steps:
+ - name: 签出 PR 代码
+ uses: actions/checkout@v4
+
+ - name: 安装 Rust 工具链(stable)
+ uses: dtolnay/rust-toolchain@stable
+
+ - name: 缓存 Cargo 编译产物
+ uses: Swatinem/rust-cache@v2
+
+ # Reason: 先运行当前 PR 分支的 benchmark 并保存结果
+ - name: 运行 Benchmark
+ run: cargo bench --bench sm3_bench --bench sm4_bench --bench sm2_bench -- --output-format bencher 2>&1 | tee bench_output.txt
+ shell: bash
+
+ # Reason: 将 benchmark 结果作为 PR 评论发布,方便审查性能变化
+ - name: 发布 Benchmark 结果到 PR
+ uses: actions/github-script@v7
+ with:
+ script: |
+ const fs = require('fs');
+ const benchOutput = fs.readFileSync('bench_output.txt', 'utf8');
+ const body = `## Benchmark Results\n\n\nClick to expand
\n\n\`\`\`\n${benchOutput}\n\`\`\`\n\n \n\n> Benchmarks ran on \`ubuntu-latest\`. Results may vary across runs.`;
+ await github.rest.issues.createComment({
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ issue_number: context.issue.number,
+ body: body
+ });
+
+ # =============================================================================
+ # Job 6: 自动发布到 crates.io(仅 v* Tag 触发)
+ # =============================================================================
+ release:
+ name: Release
+ if: startsWith(github.ref, 'refs/tags/v')
+ needs: [test-matrix, msrv]
+ runs-on: ubuntu-latest
+ permissions:
+ contents: write
+ steps:
+ - name: 签出代码
+ uses: actions/checkout@v4
+ with:
+ fetch-depth: 0
+
+ - name: 安装 Rust 工具链(stable)
+ uses: dtolnay/rust-toolchain@stable
+
+ - name: 缓存 Cargo 编译产物
+ uses: Swatinem/rust-cache@v2
+
+ # Reason: 确保 Git Tag 版本号与 Cargo.toml 中的 version 字段完全一致
+ - name: 验证 Tag 与 Cargo.toml 版本匹配
+ run: |
+ TAG_VERSION="${GITHUB_REF_NAME#v}"
+ CARGO_VERSION=$(grep '^version' Cargo.toml | head -1 | sed 's/.*"\(.*\)".*/\1/')
+ if [ "$TAG_VERSION" != "$CARGO_VERSION" ]; then
+ echo "::error::Tag version ($TAG_VERSION) does not match Cargo.toml version ($CARGO_VERSION)"
+ exit 1
+ fi
+ echo "Version: $TAG_VERSION ✓"
+ shell: bash
+
+ # Reason: 发布前最后一轮完整测试,防止 tag 推送后才发现问题
+ - name: 发布前完整测试
+ run: cargo test --all-targets
+
+ - name: 发布到 crates.io
+ run: cargo publish
+ env:
+ CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }}
+
+ # Reason: 从 CHANGELOG.md 中提取当前版本的发布说明
+ - name: 提取 Changelog 条目
+ id: changelog
+ run: |
+ TAG_VERSION="${GITHUB_REF_NAME#v}"
+ # 提取 ## [x.y.z] 到下一个 ## 之间的内容
+ NOTES=$(awk "/^## \\[${TAG_VERSION}\\]/{found=1; next} /^## \\[/{if(found) exit} found{print}" CHANGELOG.md)
+ if [ -z "$NOTES" ]; then
+ NOTES="Release v${TAG_VERSION}"
+ fi
+ # 写入多行输出
+ echo "notes<> $GITHUB_OUTPUT
+ echo "$NOTES" >> $GITHUB_OUTPUT
+ echo "EOF" >> $GITHUB_OUTPUT
+ shell: bash
+
+ - name: 创建 GitHub Release
+ uses: softprops/action-gh-release@v2
+ with:
+ name: "v${{ github.ref_name }}"
+ body: ${{ steps.changelog.outputs.notes }}
+ draft: false
+ prerelease: ${{ contains(github.ref_name, '-') }}
+ # Reason: 如果 tag 包含 '-'(如 v0.4.0-rc1),标记为预发布
diff --git a/README.md b/README.md
index 3de1fd8..eee3b9d 100644
--- a/README.md
+++ b/README.md
@@ -1,7 +1,9 @@
# libsmx
+[](https://github.com/kintaiW/libsmx/actions/workflows/ci.yml)
[](https://crates.io/crates/libsmx)
[](https://docs.rs/libsmx)
+[](https://codecov.io/gh/kintaiW/libsmx)
[](LICENSE)
[](https://blog.rust-lang.org/2023/08/24/Rust-1.72.0.html)
diff --git a/README.zh-CN.md b/README.zh-CN.md
index da85e58..d223379 100644
--- a/README.zh-CN.md
+++ b/README.zh-CN.md
@@ -1,7 +1,9 @@
# libsmx
+[](https://github.com/kintaiW/libsmx/actions/workflows/ci.yml)
[](https://crates.io/crates/libsmx)
[](https://docs.rs/libsmx)
+[](https://codecov.io/gh/kintaiW/libsmx)
[](LICENSE)
[](https://blog.rust-lang.org/2023/08/24/Rust-1.72.0.html)
diff --git a/SECURITY.md b/SECURITY.md
index 69eea71..8f316b9 100644
--- a/SECURITY.md
+++ b/SECURITY.md
@@ -20,9 +20,8 @@ If you discover a security vulnerability in libsmx, please report it responsibly
- Any potential impact assessment
**Response timeline**:
-- Acknowledgment within **48 hours**
-- Initial assessment within **7 days**
-- Fix release within **30 days** for confirmed issues
+
+本项目由个人维护,响应时间不设固定期限。作者会尽快处理所有安全报告,但无法承诺具体的响应时间。
## Scope