Skip to content

shiftleftcyber/securesbom-verifier

Repository files navigation

Secure SBOM Verifier

securesbom-verifier is a Go library for verifying signed SBOMs and signed digests with public keys. It is designed to be reused by:

  • API servers that need SBOM verification business logic
  • offline or air-gapped verification workflows
  • other Go services that need digest signature verification
  • command-line tooling that wants to wrap the same library behavior

What This Project Contains

  • CycloneDX embedded signature verification
  • AI-BOM embedded signature verification
  • SPDX detached signature verification
  • digest signature verification
  • JSON canonicalization helpers used during verification
  • PEM normalization and key utility helpers
  • an optional offline verification CLI
  • tests for the lower-level verification building blocks

Verification-Only Scope

This module intentionally excludes:

  • key generation
  • signing
  • private-key verification helpers
  • key store backends

The only cryptographic operations here are verification using a public key.

Quick Start

make test
make build-cli

Library Usage

Import the module root directly:

import securesbomverifier "github.com/shiftleftcyber/securesbom-verifier"

For CycloneDX embedded signatures:

verifier := securesbomverifier.NewVerifier()

result, err := verifier.VerifyCycloneDXEmbeddedVersioned(
  signedSBOM,
  string(publicKeyPEM),
  securesbomverifier.VerificationV2,
)

When key metadata is available, use the key-based root API:

result, err := verifier.VerifyCycloneDXEmbeddedWithKeyVersioned(
  signedSBOM,
  securesbomverifier.VerificationKey{
    KeyID:     "production-key-2026-04",
    Algorithm: "ES256",
    PublicKey: string(publicKeyPEM),
  },
  securesbomverifier.VerificationV2,
)

For AI-BOM embedded signatures:

verifier := securesbomverifier.NewVerifier()

result, err := verifier.VerifyAIBOMEmbeddedVersioned(
  signedAIBOM,
  string(publicKeyPEM),
  securesbomverifier.VerificationV2,
)

AI-BOM verification is supported only with VerificationV2.

When key metadata is available, use the key-based root API:

result, err := verifier.VerifyAIBOMEmbeddedWithKeyVersioned(
  signedAIBOM,
  securesbomverifier.VerificationKey{
    KeyID:     "production-ai-bom-key-2026-05",
    Algorithm: "ES256",
    PublicKey: string(publicKeyPEM),
  },
  securesbomverifier.VerificationV2,
)

For SPDX detached signatures:

verifier := securesbomverifier.NewVerifier()

result, err := verifier.VerifySPDXDetachedVersioned(
  spdxSBOM,
  signatureB64,
  string(publicKeyPEM),
  securesbomverifier.VerificationV2,
)
if err != nil {
  if errors.Is(err, securesbomverifier.ErrSignatureFail) {
    // Signature did not verify.
  }
}

For digest verification:

import (
  "errors"

  securesbomverifier "github.com/shiftleftcyber/securesbom-verifier"
)

verifier := securesbomverifier.NewVerifier()

result, err := verifier.VerifyDigest(
  securesbomverifier.VerifyDigestInput{
    KeyID:         "production-key-2026-04",
    HashAlgorithm: "sha256",
    Digest:        digestB64,
    Signature:     signatureB64,
  },
  securesbomverifier.VerificationKey{
    KeyID:     "production-key-2026-04",
    Algorithm: "ES256",
    PublicKey: string(publicKeyPEM),
  },
)
if err != nil {
  switch {
  case errors.Is(err, securesbomverifier.ErrInvalidDigest),
    errors.Is(err, securesbomverifier.ErrInvalidSignature),
    errors.Is(err, securesbomverifier.ErrInvalidHashAlgorithm),
    errors.Is(err, securesbomverifier.ErrInvalidKeyID),
    errors.Is(err, securesbomverifier.ErrInvalidKey):
    // Bad request or bad key metadata.
  case errors.Is(err, securesbomverifier.ErrVerificationFailed):
    // Well-formed input, but the signature did not verify.
  default:
    // Unexpected operational failure.
  }
  return err
}
_ = result.Verified

The lower-level packages remain available for advanced integrations:

import (
  digestsigning "github.com/shiftleftcyber/securesbom-verifier/services/digest"
  "github.com/shiftleftcyber/securesbom-verifier/verificationkey"
)

Digest Verification

VerifyDigest verifies an ASN.1 DER ECDSA signature over a digest that was computed by the caller.

Request fields:

  • KeyID: required key identifier. It must match VerificationKey.KeyID when the key metadata includes one.
  • HashAlgorithm: required digest algorithm. Currently only sha256 is supported.
  • Digest: required standard-base64 encoding of the raw digest bytes. For sha256, this must decode to exactly 32 bytes.
  • Signature: required standard-base64 encoding of the ASN.1 DER ECDSA signature bytes.

Key metadata uses the same root type for digest and SBOM integrations:

type VerificationKey struct {
  KeyID     string
  Algorithm string
  PublicKey string
}

Algorithm is the signing key algorithm. Digest verification accepts ECDSA algorithm labels ES256, ES384, and ES512; current digest hashing is sha256.

Stable Errors

The root package exports stable errors for mapping verification results to API responses. Use errors.Is because errors may include additional context.

  • Bad input or bad metadata: ErrInvalidSBOM, ErrMissingSignature, ErrInvalidHashAlgorithm, ErrInvalidDigest, ErrInvalidKeyID, ErrInvalidSignature, ErrInvalidKey, ErrKeyNotFound.
  • Cryptographic verification failure: ErrSignatureFail for SBOM signatures and ErrVerificationFailed for digest signatures.

For HTTP APIs, bad input generally maps to 400 Bad Request, missing keys to 404 Not Found, and cryptographic verification failures to 422 Unprocessable Entity or a domain-specific verification-failed response.

Versioned Verification Semantics

Use VerificationV2 for new integrations.

  • VerificationV1 preserves the original canonicalization behavior used by the source service. It is useful when verifying signatures produced by older signing flows.
  • VerificationV2 uses the newer canonicalization behavior and is the default recommendation for new signatures, including AI-BOM embedded signatures.

AI-BOM embedded verification is only available with VerificationV2; the V1 verification behavior remains limited to the existing CycloneDX and SPDX flows.

Migration note: keep verifying historical artifacts with the version used when they were signed. Sign new CycloneDX embedded, AI-BOM embedded, and SPDX detached artifacts with VerificationV2, then update callers to pass securesbomverifier.VerificationV2.

_, err = verifier.VerifyCycloneDXEmbeddedVersioned(signedSBOM, publicKeyPEM, securesbomverifier.VerificationV1)
_, err = verifier.VerifyCycloneDXEmbeddedVersioned(signedSBOM, publicKeyPEM, securesbomverifier.VerificationV2)
_, err = verifier.VerifyAIBOMEmbeddedVersioned(signedAIBOM, publicKeyPEM, securesbomverifier.VerificationV2)
_, err = verifier.VerifySPDXDetachedVersioned(spdxSBOM, signatureB64, publicKeyPEM, securesbomverifier.VerificationV1)
_, err = verifier.VerifySPDXDetachedVersioned(spdxSBOM, signatureB64, publicKeyPEM, securesbomverifier.VerificationV2)

Production Integration Example

See examples/production-api/main.go for an HTTP-style integration that fetches public key metadata, verifies CycloneDX, SPDX detached, and digest requests, handles stable errors, and maps them to responses.

Contract Fixtures

The testsupport package provides reusable fixtures for downstream service tests:

fixtures := testsupport.NewContractFixtures(t)

The fixture set includes valid and invalid CycloneDX embedded signatures, AI-BOM embedded signatures, SPDX detached signatures, and digest signatures, along with public keys and expected verification outcomes.

Build Note

This project currently relies on GOEXPERIMENT=jsonv2, matching the behavior already used by the source repository. The included Makefile sets that for the common build and test flows.

Container Image

Build the offline verification CLI as a container:

make docker-build

Run it locally:

docker run --rm \
  -v "$PWD:/work" \
  secure-sbom-verification-cli:dev \
  --sbom /work/path/to/signed-sbom.json \
  --pubkey /work/path/to/public.pem

Optional CLI

The offline CLI is an optional command. Library consumers do not need to import or build it. It lives at:

cmd/sbom-offline-verification

Example:

GOEXPERIMENT=jsonv2 go run ./cmd/sbom-offline-verification \
  --sbom ./path/to/signed-sbom.json \
  --pubkey ./path/to/public.pem

The same command verifies signed CycloneDX or signed AI-BOM files. The verifier detects AI-BOM files as AI-SBOM and verifies the embedded metadata.sbomAuthorSignature value with the supplied public key.

For detached SPDX verification:

GOEXPERIMENT=jsonv2 go run ./cmd/sbom-offline-verification \
  --sbom ./path/to/sample.spdx.json \
  --signature BASE64_SIGNATURE \
  --pubkey ./path/to/public.pem \
  --verification-version v2

For digest verification:

GOEXPERIMENT=jsonv2 go run ./cmd/sbom-offline-verification \
  --digest BASE64_DIGEST \
  --signature BASE64_SIGNATURE \
  --pubkey ./path/to/public.pem \
  --hash-algorithm sha256 \
  --signature-algorithm ES256

Releases

Tagged releases matching vX.X.X are built with Goreleaser using .goreleaser.yml. The release workflow publishes multi-platform CLI archives, a checksum file, and a container image to GitHub-hosted release surfaces. The container image is published to GitHub Container Registry as ghcr.io/<owner>/<repo> for linux/amd64 and linux/arm64, with tags for the full version, vMAJOR.MINOR, vMAJOR, and latest.

Examples

See examples/library/main.go for a minimal embedding example for CycloneDX verification, examples/spdx-detached/main.go for SPDX detached verification, examples/digest/main.go for digest verification, and MIGRATION.md for a suggested next-step extraction plan from sbom-signing-api.

The example programs expect you to provide signed content, signatures, and/or a public key path at runtime depending on the verification mode.

About

Verification library and offline CLI for signed SBOMs and digest signatures.

Resources

License

Contributing

Stars

2 stars

Watchers

0 watching

Forks

Packages

 
 
 

Contributors