FiloSottile/age · error

failed to compute tag: %v

Error message

failed to compute tag: %v

What it means

After validating enc's length, Tag derives the 4-byte tag with crypto/hkdf.Extract over SHA-256. HKDF-Extract in the standard library effectively cannot fail with these fixed inputs; this wrap exists to satisfy error handling and would only surface on an unexpected crypto/hkdf implementation failure.

Source

Thrown at tag/tag.go:124

//
// This is a low-level method exposed for use by plugins that implement
// identities compatible with tagged recipients.
func (r *Recipient) Tag(enc []byte) ([]byte, error) {
	label, tagRecipient := "age-encryption.org/p256tag", r.Bytes()
	if r.Hybrid() {
		label = "age-encryption.org/mlkem768p256tag"
		// In hybrid mode, the tag is computed over just the P-256 part.
		tagRecipient = tagRecipient[mlkem.EncapsulationKeySize768:]
		if len(enc) != mlkem.CiphertextSize768+uncompressedPointSize {
			return nil, fmt.Errorf("invalid ciphertext size")
		}
	} else if len(enc) != uncompressedPointSize {
		return nil, fmt.Errorf("invalid ciphertext size")
	}
	rh := sha256.Sum256(tagRecipient)
	tag, err := hkdf.Extract(sha256.New, append(slices.Clip(enc), rh[:4]...), []byte(label))
	if err != nil {
		return nil, fmt.Errorf("failed to compute tag: %v", err)
	}
	return tag[:4], nil
}

// WrapWithLabels implements [age.RecipientWithLabels], returning a single
// "postquantum" label if r is a hybrid P-256 + ML-KEM-768 recipient. This
// ensures a hybrid Recipient can't be mixed with other recipients that would
// defeat its post-quantum security.
//
// To unsafely bypass this restriction, wrap Recipient in an [age.Recipient]
// type that doesn't expose WrapWithLabels.
func (r *Recipient) WrapWithLabels(fileKey []byte) ([]*age.Stanza, []string, error) {
	label, arg := "age-encryption.org/p256tag", "p256tag"
	if r.Hybrid() {
		label, arg = "age-encryption.org/mlkem768p256tag", "mlkem768p256tag"
	}

	enc, s, err := hpke.NewSender(r.pk, hpke.HKDFSHA256(), hpke.ChaCha20Poly1305(), []byte(label))

View on GitHub (pinned to b74dce4cdb)

Solutions

  1. Use the standard library crypto/hkdf package (Go 1.24+) rather than a forked or shimmed implementation.
  2. Retry the operation once to rule out transient backend failure.
  3. If it persists, report the underlying wrapped error (%v detail) upstream to the age project.
Defensive patterns

Strategy: try-catch

Validate before calling

// Not applicable: inputs are already length-validated before hkdf.Extract; failure is not caller-preventable.

Try / catch

tag, err := r.Tag(enc)
if err != nil {
    return fmt.Errorf("HKDF tag derivation failed: %w", err)
}

Prevention

When it happens

Trigger: Practically unreachable with crypto/hkdf from the standard library given sha256.New and fixed-length inputs; would require a failing HKDF implementation if the crypto backend is swapped.

Common situations: Almost never seen in the field; occasionally reported when a custom/forked crypto/hkdf package or exotic build constraint alters behavior.

Related errors


AI-assisted analysis of FiloSottile/age@b74dce4cdb (2026-08-31). Data as JSON: /api/errors/19dbee50ac88d17e. Report an issue: GitHub.