protect

package
v1.132.0 Latest Latest
Warning

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

Go to latest
Published: Sep 4, 2026 License: Apache-2.0 Imports: 22 Imported by: 0

Documentation

Overview

Package protect signs or encrypts agent YAML shipped in OCI artifacts and verifies it on the way back.

A key is either a symmetric secret or an asymmetric key (private, or public-only). The kind is detected from the file contents: PEM-encoded (RFC 7468) or OpenSSH keys are asymmetric; anything else is a raw secret.

The YAML always stays in clear in the artifact layer. Depending on the Mode chosen by the publisher, the manifest annotations carry a signature (MAC or digital signature) and optionally an authenticated encrypted copy of the whole YAML that key holders can decrypt.

Security model

Verification answers "was this produced by a holder of the key?". For a symmetric secret, both the MAC and the AEAD ciphertext require the secret, so either annotation alone is proof. For an asymmetric key, only a signature proves possession of the private key: anyone holding the public key can encrypt. Encrypt mode with an asymmetric key therefore requires the private key and records both a signature and an encrypted copy, and verification with an asymmetric key always requires a signature. This also rules out downgrading a signed artifact to an encrypted-only one.

Signatures cover the layer bytes only. Re-tagging a signed artifact or serving an older signed version under a tag is not detected; pin digests when that matters.

Index

Constants

View Source
const (
	// AnnotationSignature holds the base64 signature (or MAC) of the YAML layer.
	AnnotationSignature = "io.docker.agent.signature"
	// AnnotationSignatureAlgorithm names the algorithm behind AnnotationSignature.
	AnnotationSignatureAlgorithm = "io.docker.agent.signature.algorithm"
	// AnnotationEncrypted holds a base64 authenticated-encrypted copy of the
	// whole YAML layer.
	AnnotationEncrypted = "io.docker.agent.encrypted"
	// AnnotationEncryptedAlgorithm names the algorithm behind AnnotationEncrypted.
	AnnotationEncryptedAlgorithm = "io.docker.agent.encrypted.algorithm"
)
View Source
const (
	AlgAESGCM  = "aes-256-gcm"
	AlgRSAOAEP = "rsa-oaep-sha256-aes-256-gcm"
)
View Source
const (
	AlgHMACSHA256   = "hmac-sha256"
	AlgEd25519      = "ed25519"
	AlgECDSASHA256  = "ecdsa-sha256"
	AlgRSAPSSSHA256 = "rsa-pss-sha256"
)
View Source
const MinRSABits = 2048

MinRSABits is the smallest accepted RSA modulus; smaller keys are considered broken.

View Source
const MinSecretLen = 16

MinSecretLen is the minimum accepted length of a symmetric secret. The clear YAML plus its MAC/ciphertext is an offline oracle for guessing the secret, and HKDF adds no entropy, so short secrets are refused outright.

Variables

View Source
var (
	ErrNotProtected     = errors.New("artifact is neither signed nor encrypted")
	ErrNotEncrypted     = errors.New("artifact has no encrypted copy")
	ErrNotSigned        = errors.New("artifact is not signed: an encrypted copy alone does not prove who published it when the key is asymmetric")
	ErrTampered         = errors.New("encrypted copy does not match the artifact content")
	ErrAlgorithmMism    = errors.New("algorithm mismatch")
	ErrEncryptNeedsPriv = errors.New("encrypt mode with an asymmetric key requires the private key, so the artifact can also be signed")
)
View Source
var (
	ErrCannotEncrypt = errors.New("key cannot encrypt")
	ErrCannotDecrypt = errors.New("key cannot decrypt: a private key or secret is required")
	ErrDecryption    = errors.New("decryption failed")
)
View Source
var (
	ErrInvalidSignature = errors.New("signature verification failed")
	ErrCannotSign       = errors.New("key cannot sign: a private key or secret is required")
	ErrCannotVerify     = errors.New("key cannot verify signatures")
)
View Source
var ErrSecretTooShort = fmt.Errorf("symmetric secret must be at least %d bytes (generate one with `openssl rand -hex 32`)", MinSecretLen)

Functions

func IsProtected

func IsProtected(annotations map[string]string) bool

IsProtected reports whether annotations carry a signature or encrypted copy.

Types

type Key

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

Key is a symmetric secret or an asymmetric key (private, or public-only).

func LoadKey

func LoadKey(path string) (*Key, error)

LoadKey reads and parses a key file. See ParseKey.

func ParseKey

func ParseKey(data []byte) (*Key, error)

ParseKey detects the key kind from its encoding. PEM blocks and OpenSSH authorized_keys lines (with or without options) are parsed as asymmetric keys. Anything else is a raw symmetric secret (surrounding whitespace is trimmed so a trailing newline does not change the key) — unless it contains a PEM boundary or an OpenSSH key-type token, in which case it is treated as a broken key file and rejected. Failing closed here matters: a truncated or BOM-prefixed public key must never silently become an HMAC key made of public material. Random secrets never contain those markers.

func (*Key) CanDecrypt

func (k *Key) CanDecrypt() bool

CanDecrypt reports whether the key can decrypt (private material required).

func (*Key) CanEncrypt

func (k *Key) CanEncrypt() bool

CanEncrypt reports whether the key can encrypt (any key of an encryption-capable type; a public key is enough).

func (*Key) CanSign

func (k *Key) CanSign() bool

CanSign reports whether the key can produce signatures.

func (*Key) CanVerify

func (k *Key) CanVerify() bool

CanVerify reports whether the key can verify signatures.

func (*Key) Decrypt

func (k *Key) Decrypt(blob []byte) ([]byte, error)

Decrypt opens a blob produced by Encrypt with the matching key.

func (*Key) Describe

func (k *Key) Describe() string

Describe returns a short human-readable description of the key.

func (*Key) Encrypt

func (k *Key) Encrypt(data []byte) ([]byte, error)

Encrypt returns an authenticated ciphertext of data that only holders of the secret (symmetric) or of the private key (asymmetric) can open. The algorithm label is bound as AEAD additional data.

Blob layouts:

  • aes-256-gcm: nonce || ciphertext
  • ecies-*: ephemeralPub || nonce || ciphertext
  • rsa-oaep-*: wrappedKey || nonce || ciphertext

func (*Key) EncryptAlgorithm

func (k *Key) EncryptAlgorithm() string

EncryptAlgorithm returns the encryption algorithm this key supports, or "" if the key type cannot encrypt (Ed25519).

func (*Key) Fingerprint

func (k *Key) Fingerprint() string

Fingerprint returns a stable hex identifier for the key material: SHA-256 of the secret, or of the PKIX-encoded public key. Private and public halves of the same pair share a fingerprint.

func (*Key) Identity

func (k *Key) Identity() string

Identity extends Fingerprint with the key's role, so that the private and public halves of a pair — which have different verification capabilities — are told apart. Suitable as a cache key for verification results.

func (*Key) Private

func (k *Key) Private() bool

Private reports whether the key holds private material (a secret or a private key), as opposed to a public-only key.

func (*Key) Protect

func (k *Key) Protect(annotations map[string]string, data []byte, mode Mode) error

Protect records the protection for data in annotations according to mode.

func (*Key) Recover

func (k *Key) Recover(annotations map[string]string) ([]byte, error)

Recover decrypts the encrypted copy carried in annotations, returning the clear YAML. It works from the annotations alone, without the layer. Note that it does not check the signature; use VerifyAnnotations for that.

func (*Key) Sign

func (k *Key) Sign(data []byte) ([]byte, error)

Sign returns the raw signature (or MAC) of data. The signed message is domain-separated (see domainInput) so a signature cannot be reused in another protocol or under another algorithm label.

func (*Key) SignAlgorithm

func (k *Key) SignAlgorithm() string

SignAlgorithm returns the signature algorithm this key supports.

func (*Key) Supports

func (k *Key) Supports(mode Mode) error

Supports reports whether the key can publish in mode, with a descriptive error when it cannot.

func (*Key) Symmetric

func (k *Key) Symmetric() bool

Symmetric reports whether the key is a raw secret.

func (*Key) Verify

func (k *Key) Verify(data, sig []byte) error

Verify checks that sig is a valid signature of data for this key.

func (*Key) VerifyAnnotations

func (k *Key) VerifyAnnotations(annotations map[string]string, data []byte) (Verification, error)

VerifyAnnotations checks that data is what a holder of this key published, using the protection annotations carry, and reports what was checked.

A signature, when present, is always verified. An encrypted copy is decrypted and compared to data when the key can decrypt; a public key only checks its algorithm label and relies on the signature. With an asymmetric key a signature is mandatory, since anyone holding the public key could have produced the encrypted copy. ErrNotProtected is returned when the artifact carries no protection at all.

type Mode

type Mode string

Mode selects what the publisher records in the annotations.

const (
	// ModeSign records a signature (asymmetric key) or MAC (secret).
	// Holders of the matching public key or secret can verify integrity.
	ModeSign Mode = "sign"
	// ModeEncrypt records an encrypted copy of the whole YAML. Holders of the
	// secret or private key can both verify integrity and recover the YAML
	// from the annotation alone. With an asymmetric key a signature is
	// recorded as well (see the package security model).
	ModeEncrypt Mode = "encrypt"
)

type Verification

type Verification struct {
	// SignatureAlgorithm is set when a signature was verified.
	SignatureAlgorithm string
	// EncryptedAlgorithm is set when the encrypted copy was decrypted and
	// matched the content. It stays empty for a public key, which can only
	// check the copy's algorithm label.
	EncryptedAlgorithm string
}

Verification reports which protections VerifyAnnotations actually checked.

func (Verification) String

func (v Verification) String() string

Jump to

Keyboard shortcuts

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