Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
55 commits
Select commit Hold shift + click to select a range
ba58976
Add .gitignore file
j28smith Jan 20, 2026
e71687b
Add CONTRIBUTING.md file
j28smith Jan 20, 2026
649aa9e
Adding CODEOWNERS file
j28smith Jan 20, 2026
c16cd4d
Update README
j28smith Jan 20, 2026
85be7df
Add markdown linting workflow and config
j28smith Jan 20, 2026
4b3d955
Add SBOM canonical hashing specification
j28smith Jan 20, 2026
4fa9f24
Add CycloneDX 1.7 test vectors for JSON and XML
j28smith Jan 20, 2026
9257d15
Add SPDX 3.0 JSON test vectors
j28smith Jan 20, 2026
9d5ce19
Add manifest for CycloneDX 1.7 JSON test vectors
j28smith Jan 20, 2026
9b8a7ea
Add Go Implementation
j28smith Jan 20, 2026
1468b10
Rename CycloneDX JSON test vectors to .cdx.json
j28smith Jan 21, 2026
1bb4be5
Rename SPDX JSON test vectors to .spdx.json
j28smith Jan 21, 2026
df2073f
Expand tests to add min, pretty and canonical variants
j28smith Jan 21, 2026
5983092
Remove SBOM validation and update JSON canonicalization
j28smith Jan 21, 2026
c649994
Change go package name from main to hash
j28smith Jan 21, 2026
109c165
Add GitHub Actions workflow for Go build and test
j28smith Jan 21, 2026
2e7ad4e
Update Go version file path in CI workflow
j28smith Jan 21, 2026
e267286
Update Go workflow to improve build and test steps
j28smith Jan 22, 2026
4f46eee
Add SPDX 3.0 JSON test vectors and update manifest
j28smith Jan 22, 2026
064797f
Add SPDX 2.2 JSON example test vectors
j28smith Jan 22, 2026
c6e701a
"Fix" min versions of JSON test vectors
j28smith Jan 22, 2026
f41e533
Add README.md for go project
j28smith Jan 23, 2026
90c7c83
Cleanup
j28smith Jan 23, 2026
4bfded8
Add python reference implementation
j28smith Jan 23, 2026
1e31e46
Minor update to Go CI workflow
j28smith Jan 23, 2026
86d40ab
Add GitHub Actions workflow for Python build and test
j28smith Jan 23, 2026
55bac6e
Update lint workflow
j28smith Jan 23, 2026
58dbffa
Add README for python reference implementation
j28smith Jan 24, 2026
8f6acd5
Add requirements.txt
j28smith Jan 24, 2026
bc7b3c6
Updates to use requirements file
j28smith Jan 24, 2026
936e0ed
Add ruff as a pre-commit check
j28smith Jan 24, 2026
ee0f9e8
Add Ruff to the Linter CI workflow for python project
j28smith Jan 24, 2026
03291a9
Initial Java implementation
j28smith Feb 4, 2026
7492ea8
Minor fixes
j28smith Feb 4, 2026
183359a
Add JavaScript implementation
j28smith Feb 4, 2026
60900f6
Rust Implementation
j28smith Feb 4, 2026
4e3696c
Add READMEs + Clean Up Documentation
j28smith Feb 4, 2026
7c226ed
Update main README
j28smith Feb 4, 2026
27ac959
Shorten Workflow Names
j28smith Feb 5, 2026
ab41aca
Fix prettier style check
j28smith Feb 5, 2026
852028e
Add badges to each language project README
j28smith Feb 5, 2026
be7b7e5
Make the -pretty and -min versions of JSON files consisten
j28smith Feb 11, 2026
047f15d
Fix JSF Signature Exclusion Logic
j28smith Feb 11, 2026
8c67629
Update authenticity-verification-canonical.cdx.json
j28smith Feb 11, 2026
5a4c58c
Update XML file names
j28smith Feb 11, 2026
0abaf2f
Fix merge issue
j28smith Feb 11, 2026
9dc97b7
Fix JS lint issue
j28smith Feb 11, 2026
fb85c41
Fix Rust lint issue
j28smith Feb 11, 2026
31b1304
Fix another rust lint issue
j28smith Feb 11, 2026
2b655fc
Minor readme update
j28smith Feb 11, 2026
c04dc9d
Fix lint issue
j28smith Feb 11, 2026
d22581f
Update README files
j28smith Feb 12, 2026
244f850
Update README files
j28smith Feb 12, 2026
14f86ac
Ignore markdown line length issues for tables
j28smith Feb 12, 2026
4feb95e
Fix markdown rule
j28smith Feb 12, 2026
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
16 changes: 16 additions & 0 deletions .github/CODEOWNERS
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# Global owners

* @j28smith

## Language specific owners

/go/ @j28smith
/java/ @j28smith
/javascript/ @j28smith
/python/ @j28smith
/rust/ @j28smith

## Specification and Test Vectors

/specs/ @j28smith
/test-vectors/ @j28smith
38 changes: 38 additions & 0 deletions .github/workflows/go.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
name: Go Project

on:
push:
branches: [ "**" ]
pull_request:
branches: [ "main" ]

permissions:
contents: read

jobs:
build:
name: Build and Test
runs-on: ubuntu-latest
env:
GOEXPERIMENT: jsonv2
defaults:
run:
working-directory: ./go
steps:
- uses: actions/checkout@v6

- name: ⚙️ Set up Go
uses: actions/setup-go@v6
with:
go-version-file: 'go/go.mod'
cache-dependency-path: go/go.sum
cache: false

- name: 👀 Display Go version
run: go version

- name: 🔨 Build
run: go build -v

- name: ✅ Run Tests
run: go test -v
33 changes: 33 additions & 0 deletions .github/workflows/java.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
name: Java Project

on:
push:
branches: [ "**" ]
pull_request:
branches: [ "main" ]

permissions:
contents: read

jobs:
build:
name: Build and Test
runs-on: ubuntu-latest
defaults:
run:
working-directory: ./java
steps:
- uses: actions/checkout@v6

- name: ⚙️ Set up JDK
uses: actions/setup-java@v5
with:
distribution: 'temurin'
java-version: '25'
cache: maven

- name: 🔨 Build
run: mvn compile

- name: ✅ Run Tests
run: mvn test
41 changes: 41 additions & 0 deletions .github/workflows/javascript.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
name: JavaScript Project

on:
push:
branches: [ "**" ]
pull_request:
branches: [ "main" ]

permissions:
contents: read

jobs:
test:
name: Run Tests
runs-on: ubuntu-latest

defaults:
run:
working-directory: ./javascript

steps:
- uses: actions/checkout@v6

- name: ⚙️ Setup Node.js
uses: actions/setup-node@v6
with:
node-version: 25
cache: 'npm'
cache-dependency-path: ./javascript/package-lock.json

- name: 📦 Install Dependencies
run: npm ci

- name: ✨ Check Formatting
run: npm run format:check

- name: 🧶 Lint Code
run: npm run lint

- name: ✅ Run Tests
run: npm test
33 changes: 33 additions & 0 deletions .github/workflows/lint.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
name: Linter Checks

on:
push:
branches: [ "**" ]
pull_request:
branches: [ "main" ]

jobs:
markdown-lint:
name: Markdown Linter
runs-on: ubuntu-latest
steps:
- name: Checkout Code
uses: actions/checkout@v6

- name: Run Markdown Linter
uses: davidanson/markdownlint-cli2-action@v22
with:
globs: "**/*.md"
config: ".markdownlint.json"

python-lint:
name: Python Linter
runs-on: ubuntu-latest
steps:
- name: Checkout Code
uses: actions/checkout@v6

- name: Run Ruff Python Linter
uses: astral-sh/ruff-action@v3
with:
src: "./python"
36 changes: 36 additions & 0 deletions .github/workflows/python.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
name: Python Project

on:
push:
branches: [ "**" ]
pull_request:
branches: [ "main" ]

permissions:
contents: read

jobs:
build:
name: Build and Test
runs-on: ubuntu-latest
defaults:
run:
working-directory: ./python
steps:
- uses: actions/checkout@v6

- name: ⚙️ Set up Python
uses: actions/setup-python@v6
with:
python-version: '3.14'

- name: 👀 Display Python version
run: python --version

- name: 📦 Install dependencies
run: |
python -m pip install --upgrade pip
pip install -r requirements.txt

- name: ✅ Run Tests
run: pytest -v test_hash.py
35 changes: 35 additions & 0 deletions .github/workflows/rust.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
name: Rust Project

on:
push:
branches: [ "**" ]
pull_request:
branches: [ "main" ]

permissions:
contents: read

jobs:
build:
name: Build and Test
runs-on: ubuntu-latest
defaults:
run:
working-directory: ./rust
steps:
- uses: actions/checkout@v6
- uses: dtolnay/rust-toolchain@stable
with:
components: clippy, rustfmt

- name: ✨ Check Formatting
run: cargo fmt --all -- --check

- name: 🧶 Lint Code
run: cargo clippy -- -D warnings

- name: 🔨 Build
run: cargo build --verbose

- name: ✅ Run Tests
run: cargo test --verbose
45 changes: 45 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# --- General ---
.DS_Store
.vscode/
.idea/

# --- Go ---
# Binaries for programs and plugins
*.exe
*.exe~
*.dll
*.so
*.dylib
vendor/

# --- Python ---
__pycache__/
*.py[cod]
*$py.class
.venv/
venv/
env/
*.egg-info/

# --- Node.js / TypeScript ---
node_modules/
dist/
npm-debug.log*
yarn-debug.log*
yarn-error.log*

# --- Java ---
*.class
*.log
*.jar
*.war
target/
.mvn/
.gradle/
build/

# --- Rust ---
/target/
Cargo.lock
**/*.rs.bk

6 changes: 6 additions & 0 deletions .markdownlint.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"default": true,
"MD013": {
"tables": false
}
}
42 changes: 42 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# Contributing to SBOM Canonical Hash

Thank you for your interest in contributing! This project aims to demonstrate
interoperable SBOM hashing across multiple languages.

## Project Structure

This is a monorepo. Each language implementation lives in its own directory:

- `/go` - Go module
- `/python` - Python package
- `/rust` - Rust crate
- ...

## Development Guidelines

### 1. Test Vectors are the Source of Truth

- Do not modify test vectors inside language directories.
- The "Golden" test vectors are located in `/test-vectors`.
- All implementations must pass the suite defined in `/test-vectors/manifest.json`.

### 2. Adding a New Language

- Create a new directory for the language.
- Implement the canonicalization logic (JCS for JSON, XML-C14N for XML).
- Ensure your tests read from the root `/test-vectors` directory.
- Add a CI job to `.github/workflows/ci.yml`.

### 3. Reporting Issues

- Please specify which language implementation you are using (or if it is a
flaw in the core spec).

## Documentation Style

We use `markdownlint` to keep our documentation consistent.

- You can run it locally using the
[markdownlint-cli](https://github.com/igorshubovych/markdownlint-cli).
- CI will fail if your PR contains linting errors.
- Configuration is found in `.markdownlint.json`.
59 changes: 57 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,57 @@
# sbom-signing-best-practices
A multi-language reference implementation for computing canonical SBOM hashes. Supports CycloneDX v1.7 & SPDX v3.0 (JSON/XML) to demonstrate cross-language interoperability and best practices for SBOM signing and integrity verification.
# SBOM Signing Best Practices

[![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](LICENSE)
[![Linter](https://github.com/shiftleftcyber/sbom-signing-best-practices/actions/workflows/lint.yml/badge.svg)](https://github.com/shiftleftcyber/sbom-signing-best-practices/actions/workflows/lint.yml)
[![Go](https://github.com/shiftleftcyber/sbom-signing-best-practices/actions/workflows/go.yml/badge.svg)](https://github.com/shiftleftcyber/sbom-signing-best-practices/actions/workflows/go.yml)
[![Java](https://github.com/shiftleftcyber/sbom-signing-best-practices/actions/workflows/java.yml/badge.svg)](https://github.com/shiftleftcyber/sbom-signing-best-practices/actions/workflows/java.yml)
[![JavaScript](https://github.com/shiftleftcyber/sbom-signing-best-practices/actions/workflows/javascript.yml/badge.svg)](https://github.com/shiftleftcyber/sbom-signing-best-practices/actions/workflows/javascript.yml)
[![Python](https://github.com/shiftleftcyber/sbom-signing-best-practices/actions/workflows/python.yml/badge.svg)](https://github.com/shiftleftcyber/sbom-signing-best-practices/actions/workflows/python.yml)
[![Rust](https://github.com/shiftleftcyber/sbom-signing-best-practices/actions/workflows/rust.yml/badge.svg)](https://github.com/shiftleftcyber/sbom-signing-best-practices/actions/workflows/rust.yml)

A set of reference implementations and test suites for computing canonical
hashes of Software Bill of Materials (SBOMs). This project demonstrates
multi-language interoperability to support reliable signing and integrity verification.

## Supported Standards

| Standard | Version | Format | Canonicalization | Signature Spec |
| :--- | :--- | :--- | :--- | :--- |
| **CycloneDX** | 1.7 | JSON | JCS (RFC 8785) | JSF |
| **SPDX** | 2.2 | JSON | JCS (RFC 8785) | Undefined (detached) |
| **SPDX** | 3.0 | JSON-LD | JCS (RFC 8785) | Undefined (detached) |

\* Canonical hashing for XML formats (CycloneDX and SPDX) is currently under
evaluation and is slated for a future release.

## The Goal: Deterministic Hashing

The objective is to compute a hash of the SBOM **content**, independent of its **formatting**.

By applying **canonicalization** (JCS) and **pruning** (JSF) prior to hashing, we
ensure that the same logical data always produces the same hash. This allows
SBOMs to be transmitted in any form — whether "minified" or "pretty-printed" — while
maintaining a stable cryptographic identity across different platforms:

> `Hash(Go_Impl(sbom-pretty.json))` == `Hash(Python_Impl(sbom-min.json))`

## Technical Specification

The exact rules for property exclusion, signature handling, and serialization
are detailed in the technical spec:

- [Technical Specification: JSON SBOM Canonical Hashing](/specs/README.md)

## Directory Structure

- `go/`: Go reference implementation.
- `java/`: Java reference implementation.
- `javascript/`: JavaScript reference implementation.
- `python/`: Python reference implementation.
- `rust/`: Rust reference implementation.
- `specs/`: Technical details on the canonicalization rules used.
- `test-vectors/`: Shared "golden" SBOMs and a test manifest file including the
expected hashes used to verify cross-language parity.

## Getting Started

Refer to the `README.md` in each language directory for build instructions.
Loading