bundle

package module
v0.0.2-0...-fea41c3 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 18, 2026 License: Apache-2.0 Imports: 20 Imported by: 0

README

bundle

The BlakBox bundle format — the shared contract that crosses the air gap.

Two things that must stay in lockstep:

  1. The spec (SPEC.md) — the versioned, offline-verifiable bundle + Airlock-Report format.
  2. The Go reference library — signing/verification + encryption used by every component that produces or consumes a bundle.

Why this repo is public

The format and verifier are published so customers (and their assessors) can verify BlakBox artifacts independently, without trusting us — the "verifiable transparency" property.

Consumed by:

  • Blak-Box/exporter — customer-side, produces bundles
  • Blak-Box/verifier — offline verifier
  • Blak-Box/appliance — on-box; consumes by spec + verifier binary, never links this Go directly

Crypto (design: docs/24-tender-airlock.md §5 in the appliance repo)

  • Envelope: in-toto v1 Statement in a DSSE v1.0.2 envelope, verified with a 2-of-2 algorithm-typed threshold.
  • Signing: ECDSA P-384 now, algorithm-agile toward ML-DSA-87 (Ed25519 fails ISM-0471).
  • Bulk: AES-256-GCM STREAM; key wrap ECDH P-384 (not X25519).
  • Built with the Go FIPS module (GOFIPS140=v1.0.0) — the only FIPS 140-3 module validated on aarch64 Linux.

Status

Core crypto implemented and tested (standard + FIPS): ECDSA-P384 sign/verify over DSSE + in-toto Statement; algorithm-typed threshold policy (phase-1 1-of ECDSA -> enforced 2-of-2); AES-256-GCM STREAM (D1) + ECDH-P384 key wrap (D4); and the ML-DSA-87 second signer (phase-2 hedge — not FIPS-validated yet, so never the sole trust path). The phase-1 path passes strict fips140=only; ML-DSA runs under the FIPS build but outside strict mode by design.

Still to land: FastCDC chunk store, published KAT test vectors, the official in-toto/attestation/go/v1 Statement type, and the CLI. The standing Ed25519 -> ECDSA P-384 update-chain migration consumes this library once for the whole product.

Documentation

Overview

Package bundle is the BlakBox bundle-format reference library: signing, verification, and (later) encryption for the signed, offline-verifiable artifacts that cross the air gap.

This file implements the phase-1 signature primitive: ECDSA P-384 over the DSSE Pre-Authentication Encoding (PAE), hashed with SHA-384 (decision D5). The signer/verifier satisfy go-securesystemslib/dsse's Signer/Verifier interfaces so the DSSE library owns the envelope + PAE and we own only the key math. ML-DSA-87 arrives as a second, algorithm-typed signer (D3/phase 2).

Index

Constants

View Source
const (
	// StatementType is the in-toto v1 Statement `_type`.
	StatementType = "https://in-toto.io/Statement/v1"

	// PayloadType is the DSSE payloadType for an in-toto Statement.
	PayloadType = "application/vnd.in-toto+json"

	// BundlePredicateType is BlakBox's bundle predicate.
	BundlePredicateType = "application/vnd.blakbox.bundle+json"
)
View Source
const (
	// SegmentSize is the plaintext size of each STREAM segment (D1): 1 MiB.
	SegmentSize = 1 << 20
	// CEKSize is the length of a content-encryption key (AES-256).
	CEKSize = 32
)

Variables

View Source
var ErrWrongCurve = errors.New("bundle: key is not ECDSA P-384")

ErrWrongCurve is returned when a key is not on the P-384 curve.

Functions

func DecryptStream

func DecryptStream(w io.Writer, r io.Reader, cek, streamAAD []byte) error

DecryptStream reverses EncryptStream, writing recovered plaintext to w. It rejects any tamper, reorder, duplication, truncation, extension, or cross-stream splice.

func EncryptStream

func EncryptStream(w io.Writer, r io.Reader, cek, streamAAD []byte) error

EncryptStream reads plaintext from r and writes the AES-256-GCM STREAM to w.

func GenerateCEK

func GenerateCEK() ([]byte, error)

GenerateCEK returns a fresh random 256-bit content-encryption key.

func KeyFingerprint

func KeyFingerprint(pub crypto.PublicKey) (string, error)

KeyFingerprint returns the stable identity fingerprint of a public key — hex(SHA-256(SPKI DER)) for stdlib keys, hex(SHA-256(raw bytes)) for ML-DSA. It is the exact value the policy uses to match signatures back to pinned anchors (decision D3) and the value written to a keypair's .fingerprint sidecar, so an out-of-band fingerprint comparison and the on-box trust decision agree on one identity.

This is the exported entry point for the same computation the package uses internally; consumers (the CLI, the exporter, signed egress) MUST derive a key's identity through this function so every component agrees.

func SignStatement

func SignStatement(priv *ecdsa.PrivateKey, st *Statement) (*dsse.Envelope, error)

SignStatement marshals st and signs it into a DSSE envelope with ECDSA P-384 (phase-1 signing path, decision D5). The DSSE library handles the envelope and Pre-Authentication Encoding; we supply only the key math.

func SignStatementHybrid

func SignStatementHybrid(ecdsaPriv *ecdsa.PrivateKey, mldsaPriv *mldsa.PrivateKey, st *Statement) (*dsse.Envelope, error)

SignStatementHybrid signs st with BOTH ECDSA-P384 and ML-DSA-87, producing a 2-of-2 DSSE envelope — the post-2030 hybrid-PQC path. Verify it with a Policy in Enforced2of2 mode pinned to the matching ECDSA and ML-DSA anchors.

func UnwrapCEK

func UnwrapCEK(recipient *ecdh.PrivateKey, stanza *WrapStanza) ([]byte, error)

UnwrapCEK recovers the CEK from a stanza using the recipient's static ECDH P-384 private key.

Types

type Algorithm

type Algorithm string

Algorithm identifies a signature algorithm for algorithm-typed threshold enforcement (decision D3).

const (
	AlgECDSAP384 Algorithm = "ECDSA-P384"
	AlgMLDSA87   Algorithm = "ML-DSA-87"
)

type Anchor

type Anchor struct {
	Algorithm Algorithm
	Public    crypto.PublicKey
}

Anchor is a pinned trust anchor: an algorithm tag plus its public key. The policy matches signatures to anchors by PUBLIC KEY, never by the envelope's advisory keyid (decision D3).

type Mode

type Mode int

Mode is the threshold-enforcement mode (decision D3).

const (
	// Phase1 accepts the bundle on at least one valid ECDSA-P384 signature from
	// a pinned anchor — the only FIPS-validated signature path today.
	Phase1 Mode = iota
	// Enforced2of2 requires one valid ECDSA-P384 AND one valid ML-DSA-87
	// signature, each from a distinct pinned anchor (post-2030; hybrid PQC).
	Enforced2of2
)

type Policy

type Policy struct {
	Anchors []Anchor
	Mode    Mode
}

Policy is a set of pinned anchors plus a threshold mode. It is the authoritative verification path; the package-level VerifyStatement is a Phase1 convenience wrapper over it.

func (*Policy) VerifyStatement

func (p *Policy) VerifyStatement(env *dsse.Envelope) (*Statement, error)

VerifyStatement verifies env against the policy and returns the in-toto Statement. It (1) verifies signatures cryptographically, (2) maps each accepted signature back to its pinned anchor by public key, then (3) enforces the algorithm-typed threshold for the policy's Mode. A signature from a key that is not pinned never counts, and one anchor cannot satisfy the threshold twice (de-duplicated by public key).

type Statement

type Statement struct {
	Type          string         `json:"_type"`
	Subject       []Subject      `json:"subject"`
	PredicateType string         `json:"predicateType"`
	Predicate     map[string]any `json:"predicate,omitempty"`
}

Statement is an in-toto v1 Statement.

func NewStatement

func NewStatement(predicateType string, predicate map[string]any, subjects ...Subject) *Statement

NewStatement builds a Statement of the given predicate type over the subjects.

func VerifyStatement

func VerifyStatement(anchors []*ecdsa.PublicKey, env *dsse.Envelope) (*Statement, error)

VerifyStatement is a Phase1 convenience wrapper: it verifies env against the supplied ECDSA-P384 anchor keys, accepting on at least one valid signature, and returns the decoded in-toto Statement. For the full algorithm-typed threshold (including enforced 2-of-2 with ML-DSA), construct a Policy directly and call Policy.VerifyStatement.

type Subject

type Subject struct {
	Name   string            `json:"name"`
	Digest map[string]string `json:"digest"`
}

Subject is a signed-over resource: a name plus a digest set (algorithm -> lowercase hex). Per the design, subjects carry sha256 (+ sha384 for 2030 alignment).

type WrapStanza

type WrapStanza struct {
	// EphemeralPublic is the sender's ephemeral ECDH P-384 public key
	// (uncompressed point, crypto/ecdh encoding).
	EphemeralPublic []byte
	// Wrapped is AES-256-GCM(nonce||ciphertext||tag) of the CEK.
	Wrapped []byte
}

WrapStanza is one recipient's wrapped copy of the CEK.

func WrapCEK

func WrapCEK(recipient *ecdh.PublicKey, cek []byte) (*WrapStanza, error)

WrapCEK wraps cek to a recipient's static ECDH P-384 public key.

Directories

Path Synopsis
cmd
bundle command
Command bundle is the CLI bridge to the BlakBox bundle crypto core.
Command bundle is the CLI bridge to the BlakBox bundle crypto core.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL