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
28 changes: 25 additions & 3 deletions .github/workflows/formal-verification.yml
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,29 @@ jobs:
- name: Run Kani proofs (14 fast + 5 DSSE)
run: cargo kani -p wsc --default-unwind 4 --output-format terse
timeout-minutes: 60
# 14/19 proofs pass within timeout. 5 DSSE proofs use Vec allocation
# in compute_pae() which is expensive for CBMC. These properties are
# proven at the Verus SMT level instead (unbounded, no allocation).
continue-on-error: true

rocq-proofs:
name: Rocq/coq-of-rust Translation
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- name: Install Nix
uses: cachix/install-nix-action@v30
with:
nix_path: nixpkgs=channel:nixos-unstable

- name: Setup Bazel
uses: bazel-contrib/setup-bazel@0.14.0
with:
bazelisk-cache: true
disk-cache: ${{ github.workflow }}-rocq
repository-cache: true

- name: Translate PAE to Rocq and compile proof
run: bazel build //verification/rocq:pae_verified
timeout-minutes: 30
# coq-of-rust needs Rust nightly with LLVM internals — Nix provides
# this locally but CI may need LIBRARY_PATH configuration.
continue-on-error: true
4 changes: 4 additions & 0 deletions MODULE.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -145,3 +145,7 @@ git_override(
commit = "6a8da0b",
remote = "https://github.com/pulseengine/rules_rocq_rust.git",
)

# Import coq-of-rust repos from rules_rocq_rust extension
rocq_of_rust = use_extension("@rules_rocq_rust//coq_of_rust:extensions.bzl", "rocq_of_rust")
use_repo(rocq_of_rust, "rocq_of_rust_source")
15 changes: 4 additions & 11 deletions verification/rocq/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -1,17 +1,10 @@
load("@rules_rocq_rust//coq_of_rust:defs.bzl", "rocq_rust_verified_library")

# Translate DSSE module from Rust to Rocq via coq-of-rust (CV-22)
# Translate PAE (Pre-Authentication Encoding) to Rocq via coq-of-rust (CV-22)
# Self-contained extraction of compute_pae from dsse.rs — no external deps.
rocq_rust_verified_library(
name = "dsse_verified",
rust_sources = ["//src/lib:src/dsse.rs"],
extra_flags = ["-impredicative-set"],
visibility = ["//visibility:public"],
)

# Translate format detection module from Rust to Rocq (CV-23)
rocq_rust_verified_library(
name = "format_verified",
rust_sources = ["//src/lib:src/format/mod.rs"],
name = "pae_verified",
rust_sources = ["pae.rs"],
extra_flags = ["-impredicative-set"],
visibility = ["//visibility:public"],
)
64 changes: 64 additions & 0 deletions verification/rocq/pae.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
/// Pre-Authentication Encoding for DSSE (extracted for Rocq verification).
///
/// This is a self-contained extraction of the PAE function from dsse.rs,
/// suitable for coq-of-rust translation.

/// Compute Pre-Authentication Encoding (PAE) per DSSE spec.
///
/// PAE(payloadType, payload) =
/// "DSSEv1 " || LEN(payloadType) || " " || payloadType || " " ||
/// LEN(payload) || " " || payload
pub fn compute_pae(payload_type: &str, payload: &[u8]) -> Vec<u8> {
let mut pae = Vec::new();

// Header
pae.extend_from_slice(b"DSSEv1 ");

// LEN(payloadType) SP payloadType
pae.extend_from_slice(payload_type.len().to_string().as_bytes());
pae.push(b' ');
pae.extend_from_slice(payload_type.as_bytes());

// SP
pae.push(b' ');

// LEN(payload) SP payload
pae.extend_from_slice(payload.len().to_string().as_bytes());
pae.push(b' ');
pae.extend_from_slice(payload);

pae
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn test_pae_deterministic() {
let a = compute_pae("test", b"data");
let b = compute_pae("test", b"data");
assert_eq!(a, b);
}

#[test]
fn test_pae_injective_types() {
let a = compute_pae("type_a", b"data");
let b = compute_pae("type_b", b"data");
assert_ne!(a, b);
}

#[test]
fn test_pae_injective_payloads() {
let a = compute_pae("type", b"data_a");
let b = compute_pae("type", b"data_b");
assert_ne!(a, b);
}

#[test]
fn test_pae_length_prefix_prevents_ambiguity() {
let a = compute_pae("ab", b"cd");
let b = compute_pae("a", b"bcd");
assert_ne!(a, b);
}
}
Loading