From 407f297dcab9da642e8671e6ebcbdecb987948ee Mon Sep 17 00:00:00 2001 From: "Q. T. Felix" <53819958+Quant-TheodoreFelix@users.noreply.github.com> Date: Sat, 28 Mar 2026 12:53:56 +0900 Subject: [PATCH 1/4] =?UTF-8?q?=ED=8C=8C=EC=9D=BC=20=ED=95=B4=EC=8B=9C?= =?UTF-8?q?=EC=97=90=20=EB=94=B0=EB=A5=B8=20CLI=20=EC=BB=A4=EB=A7=A8?= =?UTF-8?q?=EB=93=9C=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- cli/src/cmd/blake.rs | 69 ++++++++++++++++++++++++++++++++++++++++++++ cli/src/cmd/sha3.rs | 40 +++++++++++++++++++++++++ 2 files changed, 109 insertions(+) diff --git a/cli/src/cmd/blake.rs b/cli/src/cmd/blake.rs index 9d787a6..a9f504c 100644 --- a/cli/src/cmd/blake.rs +++ b/cli/src/cmd/blake.rs @@ -1,6 +1,7 @@ use super::hex_encode; use crate::input; use clap::Subcommand; +use entlib_native_blake::file::{blake2b as blake2b_file, blake3 as blake3_file}; use entlib_native_blake::{Blake2b, Blake3}; #[derive(Subcommand)] @@ -19,6 +20,32 @@ pub(crate) enum Ops { #[arg(long)] raw: bool, }, + /// BLAKE2b 파일 스트리밍 해시 + #[command(name = "2b-file")] + Blake2bFile { + /// 출력 바이트 수 (1-64, 기본: 32) + #[arg(long, default_value_t = 32)] + output_len: usize, + /// 해시할 파일 경로 + file: String, + #[arg(long)] + out_file: Option, + #[arg(long)] + raw: bool, + }, + /// BLAKE3 파일 스트리밍 해시 + #[command(name = "3-file")] + Blake3File { + /// 출력 바이트 수 (기본: 32) + #[arg(long, default_value_t = 32)] + output_len: usize, + /// 해시할 파일 경로 + file: String, + #[arg(long)] + out_file: Option, + #[arg(long)] + raw: bool, + }, /// BLAKE3 (32-byte 기본 출력, XOF 지원) #[command(name = "3")] Blake3 { @@ -37,6 +64,48 @@ pub(crate) enum Ops { pub(crate) fn run(op: Ops) { match op { + Ops::Blake2bFile { + output_len, + file, + out_file, + raw, + } => { + if !(1..=64).contains(&output_len) { + eprintln!("output_len은 1-64 범위여야 합니다"); + std::process::exit(1); + } + match blake2b_file::hash_file(&file, output_len) { + Ok(d) => { + let result = if raw { d } else { hex_encode(d) }; + input::write_output(result, out_file.as_deref(), false); + } + Err(e) => { + eprintln!("파일 해시 오류: {e}"); + std::process::exit(1); + } + } + } + Ops::Blake3File { + output_len, + file, + out_file, + raw, + } => { + if output_len == 0 { + eprintln!("output_len은 1 이상이어야 합니다"); + std::process::exit(1); + } + match blake3_file::hash_file(&file, output_len) { + Ok(d) => { + let result = if raw { d } else { hex_encode(d) }; + input::write_output(result, out_file.as_deref(), false); + } + Err(e) => { + eprintln!("파일 해시 오류: {e}"); + std::process::exit(1); + } + } + } Ops::Blake2b { output_len, in_file, diff --git a/cli/src/cmd/sha3.rs b/cli/src/cmd/sha3.rs index 5bd862a..d3404b3 100644 --- a/cli/src/cmd/sha3.rs +++ b/cli/src/cmd/sha3.rs @@ -2,6 +2,7 @@ use super::hex_encode; use crate::input; use clap::Subcommand; use entlib_native_sha3::api::{SHA3_224, SHA3_256, SHA3_384, SHA3_512, SHAKE128, SHAKE256}; +use entlib_native_sha3::file::{sha3_224 as sha3_224_file, sha3_256 as sha3_256_file, sha3_384 as sha3_384_file, sha3_512 as sha3_512_file}; #[derive(Subcommand)] pub(crate) enum Ops { @@ -42,6 +43,18 @@ pub(crate) enum Ops { #[arg(long)] raw: bool, }, + /// 파일 스트리밍 해시 (SHA3-224/256/384/512) + HashFile { + /// SHA3 변형 (224, 256, 384, 512) + #[arg(long, default_value_t = 256)] + bits: u16, + /// 해시할 파일 경로 + file: String, + #[arg(long)] + out_file: Option, + #[arg(long)] + raw: bool, + }, /// XOF SHAKE128 (128-bit security, 가변 출력 길이) Shake128 { /// 출력 바이트 수 @@ -126,6 +139,33 @@ macro_rules! run_xof { pub(crate) fn run(op: Ops) { match op { + Ops::HashFile { + bits, + file, + out_file, + raw, + } => { + let digest = match bits { + 224 => sha3_224_file::hash_file(&file), + 256 => sha3_256_file::hash_file(&file), + 384 => sha3_384_file::hash_file(&file), + 512 => sha3_512_file::hash_file(&file), + _ => { + eprintln!("지원하지 않는 비트 길이: {bits} (224, 256, 384, 512 중 선택)"); + std::process::exit(1); + } + }; + match digest { + Ok(d) => { + let result = if raw { d } else { hex_encode(d) }; + input::write_output(result, out_file.as_deref(), false); + } + Err(e) => { + eprintln!("파일 해시 오류: {e}"); + std::process::exit(1); + } + } + } Ops::Sha3_224 { in_file, out_file, From 99faaa8ccb52242e91bb331edb63e697c2056c71 Mon Sep 17 00:00:00 2001 From: "Q. T. Felix" <53819958+Quant-TheodoreFelix@users.noreply.github.com> Date: Sat, 28 Mar 2026 16:05:34 +0900 Subject: [PATCH 2/4] =?UTF-8?q?=EB=AC=B4=EA=B2=B0=EC=84=B1=20=EA=B2=80?= =?UTF-8?q?=EC=82=AC=20=EC=9B=8C=ED=81=AC=ED=94=8C=EB=A1=9C=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80=20(=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=A7=84=ED=96=89)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/secure_release.yml | 132 +++++++++++++++++++++++++++ 1 file changed, 132 insertions(+) create mode 100644 .github/workflows/secure_release.yml diff --git a/.github/workflows/secure_release.yml b/.github/workflows/secure_release.yml new file mode 100644 index 0000000..3177290 --- /dev/null +++ b/.github/workflows/secure_release.yml @@ -0,0 +1,132 @@ +name: Secure Release & Publish Pipeline + +on: + pull_request: + branches: [ "fix/build-wf" ] + push: + tags: + - 'v*.*.*' + +permissions: + contents: write + pages: write + id-token: write + +jobs: + verify-and-release: + name: Cryptographic Verification and Source Release + runs-on: ubuntu-latest + + steps: + - name: Checkout Repository + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Import Public Key (Trust Anchor) + run: gpg --import ./public/public-key.asc + + - name: Verify PGP Signature + run: | + cd ./target/dist + gpg --verify RELEASE_HASHES.txt.asc RELEASE_HASHES.txt + + - name: Extract Tag Version + id: version + run: echo "tag=${GITHUB_REF#refs/tags/}" >> "$GITHUB_OUTPUT" + + - name: Create GitHub Release + uses: softprops/action-gh-release@v2 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + RELEASE_TAG: ${{ steps.version.outputs.tag }} + with: + name: "entlib-native ${{ steps.version.outputs.tag }}" + body: | + ## Source Distribution + + | File | Description | + |------|-------------| + | `entlib-native-${{ steps.version.outputs.tag }}-source.tar.gz` | Source archive (tar.gz) | + | `entlib-native-${{ steps.version.outputs.tag }}-source.zip` | Source archive (zip) | + | `RELEASE_HASHES.txt` | SHA3-512 + BLAKE3 checksums | + | `RELEASE_HASHES.txt.asc` | PGP detached signature | + | `public-key.asc` | PGP public key (Trust Anchor) | + + ## Verify Integrity + + ```bash + gpg --import public-key.asc + gpg --verify RELEASE_HASHES.txt.asc RELEASE_HASHES.txt + ``` + files: | + ./target/dist/entlib-native-*-source.tar.gz + ./target/dist/entlib-native-*-source.zip + ./target/dist/RELEASE_HASHES.txt + ./target/dist/RELEASE_HASHES.txt.asc + ./target/dist/public-key.asc + + publish-trust-anchor: + name: Publish Trust Anchor to GitHub Pages + needs: verify-and-release + runs-on: ubuntu-latest + + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + + steps: + - name: Checkout Repository + uses: actions/checkout@v4 + + - name: Extract Tag Version + id: version + run: echo "tag=${GITHUB_REF#refs/tags/}" >> "$GITHUB_OUTPUT" + + - name: Build Pages Site + env: + RELEASE_TAG: ${{ steps.version.outputs.tag }} + run: | + mkdir -p _site/keys "_site/releases/${RELEASE_TAG}" + + cp ./public/public-key.asc _site/keys/public-key.asc + + cp ./target/dist/RELEASE_HASHES.txt \ + "_site/releases/${RELEASE_TAG}/RELEASE_HASHES.txt" + cp ./target/dist/RELEASE_HASHES.txt.asc \ + "_site/releases/${RELEASE_TAG}/RELEASE_HASHES.txt.asc" + + cat > _site/index.html << 'PAGE' + + + entlib-native Trust Anchor + +

entlib-native Trust Anchor

+

PGP Public Key

+

public-key.asc

+

Verification

+
+          # 1. Import the public key
+          curl -sO https://quant-off.github.io/entlib-native/keys/public-key.asc
+          gpg --import public-key.asc
+
+          # 2. Download the release hashes and signature
+          VERSION="vX.Y.Z"
+          curl -sO "https://quant-off.github.io/entlib-native/releases/${VERSION}/RELEASE_HASHES.txt"
+          curl -sO "https://quant-off.github.io/entlib-native/releases/${VERSION}/RELEASE_HASHES.txt.asc"
+
+          # 3. Verify the signature
+          gpg --verify RELEASE_HASHES.txt.asc RELEASE_HASHES.txt
+          
+ + + PAGE + + - name: Upload Pages Artifact + uses: actions/upload-pages-artifact@v3 + with: + path: _site + + - name: Deploy to GitHub Pages + id: deployment + uses: actions/deploy-pages@v4 From 029ca7bbe2a112f8084e484a13c881d54a15a011 Mon Sep 17 00:00:00 2001 From: "Q. T. Felix" <53819958+Quant-TheodoreFelix@users.noreply.github.com> Date: Sat, 28 Mar 2026 16:09:56 +0900 Subject: [PATCH 3/4] =?UTF-8?q?PR=20=EB=AA=A9=EC=A0=81=EC=A7=80=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/secure_release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/secure_release.yml b/.github/workflows/secure_release.yml index 3177290..fbca52b 100644 --- a/.github/workflows/secure_release.yml +++ b/.github/workflows/secure_release.yml @@ -2,7 +2,7 @@ name: Secure Release & Publish Pipeline on: pull_request: - branches: [ "fix/build-wf" ] + branches: [ "feature/*" ] push: tags: - 'v*.*.*' From bf0df50f47def94f04907b2c2c1ed45e3eecd8fe Mon Sep 17 00:00:00 2001 From: "Q. T. Felix" <53819958+Quant-TheodoreFelix@users.noreply.github.com> Date: Sat, 28 Mar 2026 16:25:24 +0900 Subject: [PATCH 4/4] =?UTF-8?q?=EC=9B=8C=ED=81=AC=ED=94=8C=EB=A1=9C=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/secure_release.yml | 77 ++++++++++++++++++++++++---- public/RELEASE_HASHES.txt | 4 ++ public/RELEASE_HASHES.txt.asc | 7 +++ 3 files changed, 79 insertions(+), 9 deletions(-) create mode 100644 public/RELEASE_HASHES.txt create mode 100644 public/RELEASE_HASHES.txt.asc diff --git a/.github/workflows/secure_release.yml b/.github/workflows/secure_release.yml index fbca52b..8294193 100644 --- a/.github/workflows/secure_release.yml +++ b/.github/workflows/secure_release.yml @@ -2,7 +2,7 @@ name: Secure Release & Publish Pipeline on: pull_request: - branches: [ "feature/*" ] + branches: [ master ] push: tags: - 'v*.*.*' @@ -13,8 +13,8 @@ permissions: id-token: write jobs: - verify-and-release: - name: Cryptographic Verification and Source Release + verify-integrity: + name: Source Integrity Verification runs-on: ubuntu-latest steps: @@ -23,13 +23,72 @@ jobs: with: fetch-depth: 0 + - name: Build entlib-native + run: cargo build --release + + - name: Generate Source Archives + run: | + VERSION=$(git describe --tags --exact-match 2>/dev/null || echo "dev") + ARCHIVE_NAME="entlib-native-${VERSION}-source" + mkdir -p target/dist + git archive \ + --format=tar.gz \ + --prefix="${ARCHIVE_NAME}/" \ + -o "target/dist/${ARCHIVE_NAME}.tar.gz" \ + HEAD + git archive \ + --format=zip \ + --prefix="${ARCHIVE_NAME}/" \ + -o "target/dist/${ARCHIVE_NAME}.zip" \ + HEAD + cp ./public/public-key.asc target/dist/public-key.asc + echo "[+] Source archives generated" + ls -lh target/dist/ + - name: Import Public Key (Trust Anchor) run: gpg --import ./public/public-key.asc - name: Verify PGP Signature run: | - cd ./target/dist - gpg --verify RELEASE_HASHES.txt.asc RELEASE_HASHES.txt + echo "[*] Verifying PGP signature of RELEASE_HASHES.txt..." + gpg --verify ./public/RELEASE_HASHES.txt.asc ./public/RELEASE_HASHES.txt + echo "[+] PGP signature verification passed" + + create-release: + name: Create GitHub Release + if: startsWith(github.ref, 'refs/tags/v') + needs: verify-integrity + runs-on: ubuntu-latest + + steps: + - name: Checkout Repository + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Build entlib-native + run: cargo build --release + + - name: Prepare Release Artifacts + env: + GITHUB_REF_NAME: ${{ github.ref_name }} + run: | + VERSION=$(git describe --tags --exact-match) + ARCHIVE_NAME="entlib-native-${VERSION}-source" + mkdir -p target/dist + git archive \ + --format=tar.gz \ + --prefix="${ARCHIVE_NAME}/" \ + -o "target/dist/${ARCHIVE_NAME}.tar.gz" \ + HEAD + git archive \ + --format=zip \ + --prefix="${ARCHIVE_NAME}/" \ + -o "target/dist/${ARCHIVE_NAME}.zip" \ + HEAD + cp ./public/public-key.asc target/dist/public-key.asc + cp ./public/RELEASE_HASHES.txt target/dist/RELEASE_HASHES.txt + cp ./public/RELEASE_HASHES.txt.asc target/dist/RELEASE_HASHES.txt.asc - name: Extract Tag Version id: version @@ -39,7 +98,6 @@ jobs: uses: softprops/action-gh-release@v2 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - RELEASE_TAG: ${{ steps.version.outputs.tag }} with: name: "entlib-native ${{ steps.version.outputs.tag }}" body: | @@ -68,7 +126,8 @@ jobs: publish-trust-anchor: name: Publish Trust Anchor to GitHub Pages - needs: verify-and-release + if: startsWith(github.ref, 'refs/tags/v') + needs: create-release runs-on: ubuntu-latest environment: @@ -91,9 +150,9 @@ jobs: cp ./public/public-key.asc _site/keys/public-key.asc - cp ./target/dist/RELEASE_HASHES.txt \ + cp ./public/RELEASE_HASHES.txt \ "_site/releases/${RELEASE_TAG}/RELEASE_HASHES.txt" - cp ./target/dist/RELEASE_HASHES.txt.asc \ + cp ./public/RELEASE_HASHES.txt.asc \ "_site/releases/${RELEASE_TAG}/RELEASE_HASHES.txt.asc" cat > _site/index.html << 'PAGE' diff --git a/public/RELEASE_HASHES.txt b/public/RELEASE_HASHES.txt new file mode 100644 index 0000000..9f9f4df --- /dev/null +++ b/public/RELEASE_HASHES.txt @@ -0,0 +1,4 @@ +SHA3-512 ./entlib-native-dev-source.tar.gz : 6b7f36dc2dae00c9fde2368f8c8d94505c29dfabc8426a825866e9fd26f0bccb360677ed2a9ccc4b25aa50b07e933d6a7d88dcf73df7de24ff4065cadf4e0846 +BLAKE3 ./entlib-native-dev-source.tar.gz : d19e1f445997446bf99b0697bd846b872a252bdbbad819b5d8b4c23106ca9281 +SHA3-512 ./entlib-native-dev-source.zip : 012a9dc5d51ca2928a8ddd6d542f3cbf8a71e06827742e917a638b2a82d11bb43e4d8ee4c8c510a5713542d9b23f0fcbb9c33a79f520c2d271e7304281314945 +BLAKE3 ./entlib-native-dev-source.zip : df88ed5354a2502b60311f430af41751aeb795ba8d6d3e76f6ce0c56486858c6 diff --git a/public/RELEASE_HASHES.txt.asc b/public/RELEASE_HASHES.txt.asc new file mode 100644 index 0000000..4b826bf --- /dev/null +++ b/public/RELEASE_HASHES.txt.asc @@ -0,0 +1,7 @@ +-----BEGIN PGP SIGNATURE----- + +iHUEABYKAB0WIQTkuuTQ1yxQ1k2xdRZbK81mYBTjYQUCaceCFAAKCRBbK81mYBTj +YYFAAQDnu0Yko0v2SqhAr4aL33nRiykvMZ2w/+4N13HCqfNWBgD+I/4lzPs7hW5I +RiQD9cfoDB0kzfFQGuxJMBu2O9n/KwQ= +=niwE +-----END PGP SIGNATURE-----