payloadcrypt

package
v0.9.13 Latest Latest
Warning

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

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

Documentation

Overview

Package payloadcrypt seals the sensitive free-text fields of an agent trace (prompt, completion, tool params/results, reasoning) before they leave the emitting process, so the CloudEvent — and the BigQuery row the recorder derives from it — carries ciphertext rather than plaintext.

The scheme is envelope encryption: a fresh AES-256 data-encryption key (DEK) is minted per CloudEvent, the DEK is wrapped by a Cloud KMS symmetric KEK (one KMS Encrypt call per event, regardless of how many fields it seals), and each field is AES-256-GCM-sealed under the DEK with a fresh nonce. Recovering any field requires KMS Decrypt on the KEK (the break-glass second factor, PAM-gated) followed by an AES-GCM open — so a reader holding only BigQuery dataViewer sees ciphertext and nothing more.

The producer side holds encrypt-only (roles/cloudkms.cryptoKeyEncrypter) on the KEK and can never read a payload back. Reader/break-glass code uses Open with an injected unwrap callback, mirroring the GCP-free split in public/sdk/uploads/envelope.go. The Cloud KMS-backed wrap for the producer side lives in the sibling kmsseal package.

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

func DEKWrapAAD

func DEKWrapAAD() []byte

DEKWrapAAD returns a copy of the additional-authenticated-data bound to the KMS DEK wrap, so reader/break-glass code passes the same AAD to KMS Decrypt.

func Open

func Open(envelope []byte, unwrapDEK func(kek string, wrapped []byte) (dek []byte, err error)) ([]byte, error)

Open decrypts a sealed envelope. unwrapDEK recovers the AES DEK from the KMS-wrapped DEK — typically a KMS Decrypt against kek (the envelope's declared Sealed.KEK) with DEKWrapAAD. kek is passed to the callback so a reader spanning envelopes minted under different keys (e.g. across KEK rotation, all living in the same dataset) can Decrypt against the right key without re-parsing the envelope. Open stays GCP-free so tests and readers can reuse the AES-GCM/base64 logic without pulling in cloud.google.com/go/kms.

Types

type Encryptor

type Encryptor struct {
	// contains filtered or unexported fields
}

Encryptor mints per-event sealing sessions. Safe for concurrent use.

Example

ExampleEncryptor shows the producer (seal) and reader (open) halves against a stub DEK-wrap. Production wraps the DEK via Cloud KMS — see kmsseal.New — but the seal/open round-trip is otherwise identical and GCP-free.

package main

import (
	"context"
	"fmt"

	"chainguard.dev/driftlessaf/agents/agenttrace/payloadcrypt"
)

func main() {
	const keyName = "projects/p/locations/us-central1/keyRings/argos/cryptoKeys/agent-trace-payload-key"

	// Stub wrap/unwrap standing in for a KMS Encrypt/Decrypt under the KEK.
	// Real deployments use kmsseal.New for wrap and a PAM-gated KMS Decrypt for
	// unwrap; the DEK never leaves KMS in the clear on the producer side.
	wrap := func(_ context.Context, dek []byte) ([]byte, error) { return dek, nil }
	unwrap := func(_ string, wrapped []byte) ([]byte, error) { return wrapped, nil }

	enc, err := payloadcrypt.New(keyName, wrap)
	if err != nil {
		panic(err)
	}

	// One session per CloudEvent: every field it seals shares a single wrapped
	// DEK, so there is exactly one KMS call per event.
	sess, err := enc.NewSession(context.Background())
	if err != nil {
		panic(err)
	}
	sealed, err := sess.Seal([]byte(`"analyze CVE-2025-1234"`))
	if err != nil {
		panic(err)
	}

	// Reader/break-glass side recovers the plaintext.
	plaintext, err := payloadcrypt.Open(sealed, unwrap)
	if err != nil {
		panic(err)
	}
	fmt.Println(string(plaintext))
}
Output:
"analyze CVE-2025-1234"

func New

func New(keyName string, wrap WrapFunc) (*Encryptor, error)

New returns an Encryptor backed by the given DEK-wrap callback. keyName is the KMS crypto-key resource name recorded in each envelope so readers know which key to Decrypt against.

func (*Encryptor) NewSession

func (e *Encryptor) NewSession(ctx context.Context) (*Session, error)

NewSession mints a fresh DEK, wraps it once via the configured WrapFunc, and returns a Session that seals fields locally under that DEK.

type Sealed

type Sealed struct {
	// Enc is the envelope-version marker; also lets consumers detect ciphertext.
	Enc string `json:"driftlessaf_enc"`
	// KEK is the KMS crypto-key resource name whose Decrypt recovers the DEK.
	KEK string `json:"kek"`
	// WrappedDEK is the base64 KMS-wrapped per-event AES-256 DEK.
	WrappedDEK string `json:"wdek"`
	// IV is the base64 AES-GCM nonce for this field.
	IV string `json:"iv"`
	// Ciphertext is the base64 AES-GCM ciphertext (including the auth tag).
	Ciphertext string `json:"ct"`
}

Sealed is the JSON wire shape written in place of a sensitive field. All binary fields are base64. The recorder loads it as native JSON (JSON columns) or as a JSON string (STRING columns); either way it is opaque to BigQuery.

type Session

type Session struct {
	// contains filtered or unexported fields
}

Session seals many fields under a single KMS-wrapped DEK — one KMS Encrypt per session. Create one per CloudEvent (via NewSession) so a trace's or span's fields share a DEK and per-event KMS calls stay O(1) regardless of how many tool calls or reasoning blocks a trace contains.

func (*Session) Seal

func (s *Session) Seal(plaintext []byte) (json.RawMessage, error)

Seal AES-256-GCM-encrypts plaintext under the session DEK with a fresh nonce and returns the JSON envelope. The envelope version and KEK are bound as GCM additional authenticated data (see envelopeAAD).

type WrapFunc

type WrapFunc func(ctx context.Context, dek []byte) (wrapped []byte, err error)

WrapFunc wraps a freshly-minted DEK and returns the wrapped bytes. The KMS implementation is a symmetric Encrypt under the KEK; tests inject a fake so the package's crypto is exercisable without GCP.

Directories

Path Synopsis
Package kmsseal provides the Cloud KMS-backed wrap for payloadcrypt.
Package kmsseal provides the Cloud KMS-backed wrap for payloadcrypt.

Jump to

Keyboard shortcuts

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