Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 33 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -511,6 +511,36 @@ jobs:
ls -la compliance/ || echo "No compliance directory"
find . -name "wsc-*" -type f | head -20

- name: Self-sign release binaries with sigil
run: |
# Use the freshly-built Linux x86_64 binary to sign all ELF binaries
chmod +x native-cli/wsc-linux-x86_64
SIGIL="./native-cli/wsc-linux-x86_64"

echo "Self-signing release binaries with sigil (dogfooding)..."
for binary in native-cli/wsc-linux-x86_64 native-cli/wsc-linux-aarch64; do
if [ -f "$binary" ]; then
echo "Signing $binary..."
"$SIGIL" sign --format elf --keyless \
--input-file "$binary" \
--output-file "${binary}.signed" \
--signature-file "${binary}.sig" 2>&1 || echo "Warning: failed to sign $binary (keyless may not be available)"
fi
done

# Also sign the TPM2 variant if present
if [ -f "native-cli/wsc-linux-x86_64-tpm2" ]; then
echo "Signing native-cli/wsc-linux-x86_64-tpm2..."
"$SIGIL" sign --format elf --keyless \
--input-file "native-cli/wsc-linux-x86_64-tpm2" \
--output-file "native-cli/wsc-linux-x86_64-tpm2.signed" \
--signature-file "native-cli/wsc-linux-x86_64-tpm2.sig" 2>&1 || echo "Warning: failed to sign"
fi

echo "Self-signing complete. Detached signatures:"
ls -la native-cli/*.sig 2>/dev/null || echo "No .sig files generated"
continue-on-error: true

- name: Upload Release Assets
if: github.event_name == 'release' || github.event_name == 'workflow_dispatch' || github.ref_type == 'tag'
uses: softprops/action-gh-release@v2
Expand All @@ -526,6 +556,9 @@ jobs:
native-cli/wsc-linux-x86_64-tpm2.sha256
native-cli/wsc-linux-aarch64
native-cli/wsc-linux-aarch64.sha256
native-cli/wsc-linux-x86_64.sig
native-cli/wsc-linux-x86_64-tpm2.sig
native-cli/wsc-linux-aarch64.sig
native-cli/wsc-macos-x86_64
native-cli/wsc-macos-x86_64.sha256
native-cli/wsc-macos-aarch64
Expand Down
11 changes: 11 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,17 @@
> This file was generated by `rivet init --agents`. Re-run the command
> any time artifacts change to keep this file current.

## Formal Verification

This project uses a three-layer verification pyramid. Follow the
[PulseEngine Verification Guide](https://pulseengine.eu/guides/VERIFICATION-GUIDE.md)
for all proof work.

- **Kani** (bounded model checking): `#[cfg(kani)]` harnesses in source files
- **Verus** (SMT/Z3): `verus! { }` specs in `src/lib/src/verus_proofs/`, built via `rules_verus`
- **Lean4** (Mathlib): algebraic proofs in `lean/Ed25519.lean`, built via `rules_lean`
- **Rocq** (coq-of-rust): strip Verus annotations → translate via `rules_rocq_rust`

## Project Overview

This project uses **Rivet** for SDLC artifact traceability.
Expand Down
10 changes: 10 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,16 @@ wsc (WebAssembly Signature Component) is a **security-critical** cryptographic s
- Air-gapped verification for embedded devices
- Trust bundle management

## Formal Verification

Follow the [PulseEngine Verification Guide](https://pulseengine.eu/guides/VERIFICATION-GUIDE.md) for all proof work. Key rules:

1. Get the spec right before attempting proofs
2. Try the simple thing first — let the solver attempt it
3. Generate multiple candidates (3-5 strategies before concluding hard)
4. Code must satisfy all verification tracks simultaneously (Rust + Verus + coq-of-rust + Kani)
5. No `Vec` in Verus specs — use `Seq`; no trait objects in verified code

## Security-Critical Release Process

**THIS IS A CRYPTOGRAPHIC SECURITY TOOL. RELEASES MUST FOLLOW THIS PROCESS:**
Expand Down
117 changes: 73 additions & 44 deletions lean/Ed25519.lean
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,11 @@

## Status

- `verification_equation_sound`: proof sketch, needs Mathlib ZMod
- `verification_equation_complete`: needs prime-order argument
- `cofactored_verification_sound`: follows from above
- `basepoint_prime_order`: needs Mathlib OrderOf
- `scalarMul_mul_order`: proven (via scalarMul_zero_point axiom)
- `verification_equation_sound`: proven (full mechanized proof)
- `verification_equation_complete`: needs prime-order argument (sorry)
- `cofactored_verification_sound`: proven (follows from soundness)
- `basepoint_prime_order`: needs Mathlib OrderOf (sorry)
-/

import Mathlib.GroupTheory.OrderOfElement
Expand Down Expand Up @@ -51,6 +52,13 @@ axiom add_zero_curvepoint (P : CurvePoint) :
(@instHAdd CurvePoint curveGroup.toAddGroup.toAddMonoid.toAdd)
P (@Zero.zero CurvePoint curveGroup.toAddGroup.toAddMonoid.toZero) = P

-- Scalar multiplication of the zero point is zero.
-- This is a consequence of the group action laws: [n]O = [n]([0]P) = [n*0]P = [0]P = O.
-- We state it as an axiom to avoid threading an arbitrary witness P.
axiom scalarMul_zero_point (n : ℤ) :
[n] (@Zero.zero CurvePoint curveGroup.toAddGroup.toAddMonoid.toZero) =
@Zero.zero CurvePoint curveGroup.toAddGroup.toAddMonoid.toZero

/-! ## Scalar multiplication derived lemmas -/

/-- Scalar multiplication by -1 then adding gives identity. -/
Expand All @@ -66,16 +74,12 @@ lemma scalarMul_neg_one_add (P : CurvePoint) :
/-- Scalar multiplication distributes over multiples of ℓ: [m*ℓ]B = [m]([ℓ]B) = [m]O = O. -/
lemma scalarMul_mul_order (m : ℤ) :
[m * (Curve25519.ℓ : ℤ)] B = @Zero.zero CurvePoint curveGroup.toAddGroup.toAddMonoid.toZero := by
-- Strategy: [m*ℓ]B = [m]([ℓ]B) = [m]O = O
-- Step 1: Factor using scalarMul_mul
-- Step 1: Factor [m*ℓ]B = [m]([ℓ]B) using scalarMul_mul
rw [scalarMul_mul m (Curve25519.ℓ : ℤ) B]
-- Step 2: Apply basepoint_order: [ℓ]B = O
rw [basepoint_order]
-- Step 3: [m]O = O (scalar mul of identity is identity)
-- This follows from: [m]O = [m]([0]B) = [m*0]B = [0]B = O
-- but we need the chain through scalarMul_mul and scalarMul_zero.
-- For now, this step requires a lemma scalarMul_zero_point.
sorry
-- Step 3: [m]O = O by scalarMul_zero_point
exact scalarMul_zero_point m

/-- The order of B divides ℓ (prime order subgroup). -/
theorem basepoint_prime_order :
Expand All @@ -89,6 +93,13 @@ theorem basepoint_prime_order :
-- Required Mathlib lemmas:
-- AddGroup.addOrderOf_dvd_of_nsmul_eq_zero
-- Fact.mk (Nat.Prime Curve25519.ℓ)
--
-- To fully mechanize this, we would need:
-- 1. An AddGroup instance on CurvePoint connected to scalarMul
-- (our axiom-based scalarMul is separate from the nsmul in curveGroup)
-- 2. A proof that addOrderOf B = Curve25519.ℓ, which requires showing
-- ℓ is the *minimal* positive n with [n]B = O (primality helps here)
-- 3. Then: addOrderOf_dvd_of_nsmul_eq_zero gives the result
sorry

/-! ## Main verification theorems -/
Expand All @@ -103,42 +114,47 @@ theorem verification_equation_sound
(hs : s ≡ r + k * a [ZMOD (Curve25519.ℓ : ℤ)]) :
[s] B = @HAdd.hAdd CurvePoint CurvePoint CurvePoint
(@instHAdd CurvePoint curveGroup.toAddGroup.toAddMonoid.toAdd) R ([k] A) := by
-- Proof strategy:
-- Overview:
-- 1. From hs: ∃ m, s = r + k*a + m*ℓ
-- 2. [s]B = [r + k*a + m*ℓ]B
-- 3. = [r]B + [k*a]B + [m*ℓ]B (by scalarMul_add)
-- 4. = R + [k]([a]B) + [m]([ℓ]B) (by scalarMul_mul, hR)
-- 5. = R + [k]A + [m]O (by basepoint_order, hA)
-- 6. = R + [k]A (by add_zero)
-- 3. = [r + k*a]B + [m*ℓ]B (by scalarMul_add)
-- 4. = [r + k*a]B + O (by scalarMul_mul_order)
-- 5. = [r + k*a]B (by add_zero_curvepoint)
-- 6. = [r]B + [k*a]B (by scalarMul_add)
-- 7. = [r]B + [k]([a]B) (by scalarMul_mul)
-- 8. = R + [k]A (by hR, hA)
--
-- Detailed proof:
-- Step 1: Extract the modular congruence witness.
-- Int.ModEq gives us: ∃ m, s - (r + k*a) = m * ℓ, i.e., s = r + k*a + m*ℓ.
obtain ⟨m, hm⟩ := (Int.modEq_iff_dvd.mp hs)
-- hm : ↑ℓ ∣ (s - (r + k * a)), so ∃ m, s - (r + k*a) = m * ℓ
-- which means s = (r + k*a) + m * ℓ
--
-- Step 2: Rewrite [s]B using the decomposition.
-- have hs_eq : s = (r + k * a) + m * (Curve25519.ℓ : ℤ) := by omega
-- rw [hs_eq]
--
-- Step 3: Distribute scalar multiplication.
-- rw [scalarMul_add (r + k * a) (m * ↑Curve25519.ℓ) B]
-- rw [scalarMul_add r (k * a) B]
--
-- Step 4: Factor [k*a]B = [k]([a]B) and [m*ℓ]B = [m]([ℓ]B).
-- rw [scalarMul_mul k a B]
-- rw [scalarMul_mul m (↑Curve25519.ℓ) B]
--
-- Step 5: Apply basepoint_order and hypotheses.
-- rw [basepoint_order] -- [ℓ]B ↦ O
-- rw [scalarMul_zero_point m] -- [m]O ↦ O (needs helper lemma)
-- rw [← hA] -- [a]B ↦ A
-- rw [← hR] -- [r]B ↦ R
--
-- Step 6: R + [k]A + O = R + [k]A by add_zero.
-- rw [add_zero_curvepoint (R + [k]A)] -- ... + O ↦ ...
sorry
-- Int.ModEq is: s ≡ r + k*a [ZMOD ℓ] ↔ (ℓ : ℤ) ∣ (s - (r + k*a)).
-- Int.modEq_iff_dvd.mp gives: (↑ℓ) ∣ (s - (r + k * a)).
-- Destructuring the divisibility yields witness m with hm.
obtain ⟨m, hm⟩ := Int.modEq_iff_dvd.mp hs
-- hm : s - (r + k * a) = ↑ℓ * m
-- Derive: s = (r + k * a) + m * ℓ
have hs_eq : s = (r + k * a) + m * (Curve25519.ℓ : ℤ) := by omega
-- Step 2: Rewrite [s]B using the decomposition s = (r + k*a) + m*ℓ.
rw [hs_eq]
-- Goal: [r + k * a + m * ↑ℓ]B = R + [k]A
-- Step 3: Distribute scalar multiplication over the outer sum.
rw [scalarMul_add (r + k * a) (m * (Curve25519.ℓ : ℤ)) B]
-- Goal: [r + k*a]B + [m*ℓ]B = R + [k]A
-- Step 4: [m*ℓ]B = O via scalarMul_mul_order.
rw [scalarMul_mul_order m]
-- Goal: [r + k*a]B + O = R + [k]A
-- Step 5: Eliminate + O on the left via add_zero_curvepoint.
rw [add_zero_curvepoint ([r + k * a] B)]
-- Goal: [r + k*a]B = R + [k]A
-- Step 6: Distribute scalar multiplication over r + k*a.
rw [scalarMul_add r (k * a) B]
-- Goal: [r]B + [k*a]B = R + [k]A
-- Step 7: Factor [k*a]B = [k]([a]B) via scalarMul_mul.
rw [scalarMul_mul k a B]
-- Goal: [r]B + [k]([a]B) = R + [k]A
-- Step 8: Substitute hypotheses hA and hR.
rw [← hA]
-- Goal: [r]B + [k]A = R + [k]A
rw [← hR]
-- Goal: R + [k]A = R + [k]A ∎

/-- Verification equation completeness: if [s]B = R + [k]A then s ≡ r + k*a (mod ℓ).
This is the converse direction — a valid verification implies the signer knew a. -/
Expand All @@ -154,7 +170,20 @@ theorem verification_equation_complete
-- So [s - (r+k*a)]B = O. By basepoint_prime_order, ℓ ∣ (s - (r+k*a)),
-- which is exactly the definition of s ≡ r+k*a (mod ℓ).
--
-- This direction requires injectivity mod ℓ, i.e., basepoint_prime_order.
-- Proof sketch (requires basepoint_prime_order, which itself needs sorry):
-- rw [hR, hA] at hverify
-- -- hverify : [s]B = [r]B + [k]([a]B)
-- rw [← scalarMul_mul k a B] at hverify
-- -- hverify : [s]B = [r]B + [k*a]B
-- rw [← scalarMul_add r (k * a) B] at hverify
-- -- hverify : [s]B = [r + k*a]B
-- -- Need: [s]B = [r+k*a]B → [s - (r+k*a)]B = O
-- -- This requires an injectivity/cancellation axiom for scalarMul on B,
-- -- which follows from: [s]B - [r+k*a]B = [s - (r+k*a)]B = O
-- -- Then basepoint_prime_order gives ℓ ∣ (s - (r+k*a))
-- -- Then Int.modEq_iff_dvd.mpr closes the goal.
--
-- This direction requires basepoint_prime_order (which is sorry).
sorry

/-- Cofactored verification: [h*s]B = [h](R + [k]A).
Expand Down
Loading