mtcs 0.3.1

Offline verification of Merkle Tree Certificates (draft-ietf-plants-merkle-tree-certs)
Documentation
name: CI
on:
  push:
  pull_request:
  schedule:
    - cron: '23 4 * * 1' # Weekly, Monday 04:23 UTC.
permissions:
  contents: read
jobs:
  test:
    if: github.event_name != 'schedule'
    strategy:
      fail-fast: false
      matrix:
        os: [ubuntu-latest, macos-latest, windows-latest]
        toolchain: ['1.88.0', stable]
    runs-on: ${{ matrix.os }}
    steps:
      - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
        with:
          persist-credentials: false
      - uses: dtolnay/rust-toolchain@6bed0761d98439e5a578e2877258200ad565ba87
        with:
          toolchain: ${{ matrix.toolchain }}
          components: rustfmt, clippy
          targets: wasm32-unknown-unknown
      - name: Cache Cargo dependencies and build artifacts
        uses: actions/cache@v6
        with:
          path: |
            ~/.cargo/registry
            ~/.cargo/git
            target
          key: ${{ runner.os }}-${{ matrix.toolchain }}-cargo-${{ hashFiles('**/Cargo.lock') }}
          restore-keys: |
            ${{ runner.os }}-${{ matrix.toolchain }}-cargo-
      # The default feature set includes `verify`: cosignature cryptography and
      # the relying-party pipeline.
      - run: cargo test --locked
      # The no-feature base layer must keep building and passing without the
      # cryptographic backends.
      - if: matrix.toolchain == 'stable'
        run: cargo test --no-default-features --locked
      - if: matrix.toolchain == 'stable'
        run: cargo clippy --all-targets --all-features --locked -- -D warnings
      - if: matrix.toolchain == 'stable'
        run: cargo clippy --all-targets --no-default-features --locked -- -D warnings
      - if: matrix.toolchain == 'stable'
        run: cargo run --example inspect_proof --no-default-features --locked -- tests/fixtures/standalone.der
      - if: matrix.toolchain == 'stable'
        run: cargo run --example verify --locked -- tests/fixtures/ca.der tests/fixtures/standalone.der
      - if: matrix.os == 'ubuntu-latest' && matrix.toolchain == 'stable'
        run: cargo fmt --all --check
      - if: matrix.os == 'ubuntu-latest' && matrix.toolchain == 'stable'
        name: Check documentation, wasm and packaging
        env:
          RUSTDOCFLAGS: -D warnings
        run: |
          cargo doc --all-features --no-deps --locked
          cargo doc --no-default-features --no-deps --locked
          cargo build --no-default-features --target wasm32-unknown-unknown --locked
          cargo package --all-features --locked
      - if: matrix.os == 'ubuntu-latest' && matrix.toolchain == 'stable'
        name: Check the base layer stays free of cryptographic backends
        run: |
          ! cargo tree --edges no-dev --no-default-features --locked | grep -E 'p256|p384|ed25519-dalek|ml-dsa|signature'
  fuzz:
    if: github.event_name != 'schedule'
    runs-on: ubuntu-latest
    continue-on-error: true
    steps:
      - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
        with:
          persist-credentials: false
      - uses: dtolnay/rust-toolchain@6bed0761d98439e5a578e2877258200ad565ba87
        with:
          toolchain: nightly
      - name: Cache Cargo dependencies and build artifacts
        uses: actions/cache@v6
        with:
          path: |
            ~/.cargo
            fuzz/target
          key: fuzz-cargo-${{ hashFiles('fuzz/Cargo.lock') }}
          restore-keys: |
            fuzz-cargo-
      - run: cargo install cargo-fuzz --locked --version 0.13.2
      # A short smoke run per target; extended fuzzing belongs to CIFuzz-style
      # infrastructure, not per-commit CI. libFuzzer grows the positional corpus
      # directory in place, so seeds are copied to a scratch directory first.
      - run: |
          set -e
          for spec in "fuzz_verify verify" "fuzz_parse parse" "fuzz_policy policy"; do
            set -- $spec
            corpus="$(mktemp -d)"
            cp fuzz/seeds/"$2"/* "$corpus/"
            cargo fuzz run "$1" --fuzz-dir fuzz -- -max_total_time=60 -dict=fuzz/dictionaries/mtcs.dict "$corpus"
          done
  # A longer weekly run per target. The corpus persists across runs through the
  # Actions cache (a fresh entry per run, restoring the most recent match) and
  # is also uploaded as an artifact for inspection.
  fuzz-weekly:
    if: github.event_name == 'schedule'
    runs-on: ubuntu-latest
    timeout-minutes: 60
    strategy:
      fail-fast: false
      matrix:
        include:
          - { target: fuzz_verify, seeds: verify }
          - { target: fuzz_parse, seeds: parse }
          - { target: fuzz_policy, seeds: policy }
    steps:
      - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
        with:
          persist-credentials: false
      - uses: dtolnay/rust-toolchain@6bed0761d98439e5a578e2877258200ad565ba87
        with:
          toolchain: nightly
      - name: Cache Cargo dependencies and build artifacts
        uses: actions/cache@v6
        with:
          path: |
            ~/.cargo
            fuzz/target
          key: fuzz-cargo-${{ hashFiles('fuzz/Cargo.lock') }}
          restore-keys: |
            fuzz-cargo-
      - run: cargo install cargo-fuzz --locked --version 0.13.2
      - name: Restore the grown corpus
        uses: actions/cache@v6
        with:
          path: fuzz/corpus/${{ matrix.target }}
          key: fuzz-weekly-corpus-${{ matrix.target }}-${{ github.run_id }}
          restore-keys: |
            fuzz-weekly-corpus-${{ matrix.target }}-
      - name: Fuzz for 30 minutes
        run: |
          set -e
          corpus="fuzz/corpus/${{ matrix.target }}"
          mkdir -p "$corpus"
          cp fuzz/seeds/"${{ matrix.seeds }}"/* "$corpus/"
          cargo fuzz run "${{ matrix.target }}" --fuzz-dir fuzz -- -max_total_time=1800 -dict=fuzz/dictionaries/mtcs.dict "$corpus"
      - name: Upload the grown corpus
        uses: actions/upload-artifact@v4
        with:
          name: fuzz-corpus-${{ matrix.target }}
          path: fuzz/corpus/${{ matrix.target }}
          overwrite: true