{"record":{"id":"19dbee50ac88d17e","repo":"FiloSottile/age","slug":"failed-to-compute-tag-v","errorCode":null,"errorMessage":"failed to compute tag: %v","messagePattern":"failed to compute tag: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"tag/tag.go","lineNumber":124,"sourceCode":"//\n// This is a low-level method exposed for use by plugins that implement\n// identities compatible with tagged recipients.\nfunc (r *Recipient) Tag(enc []byte) ([]byte, error) {\n\tlabel, tagRecipient := \"age-encryption.org/p256tag\", r.Bytes()\n\tif r.Hybrid() {\n\t\tlabel = \"age-encryption.org/mlkem768p256tag\"\n\t\t// In hybrid mode, the tag is computed over just the P-256 part.\n\t\ttagRecipient = tagRecipient[mlkem.EncapsulationKeySize768:]\n\t\tif len(enc) != mlkem.CiphertextSize768+uncompressedPointSize {\n\t\t\treturn nil, fmt.Errorf(\"invalid ciphertext size\")\n\t\t}\n\t} else if len(enc) != uncompressedPointSize {\n\t\treturn nil, fmt.Errorf(\"invalid ciphertext size\")\n\t}\n\trh := sha256.Sum256(tagRecipient)\n\ttag, err := hkdf.Extract(sha256.New, append(slices.Clip(enc), rh[:4]...), []byte(label))\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to compute tag: %v\", err)\n\t}\n\treturn tag[:4], nil\n}\n\n// WrapWithLabels implements [age.RecipientWithLabels], returning a single\n// \"postquantum\" label if r is a hybrid P-256 + ML-KEM-768 recipient. This\n// ensures a hybrid Recipient can't be mixed with other recipients that would\n// defeat its post-quantum security.\n//\n// To unsafely bypass this restriction, wrap Recipient in an [age.Recipient]\n// type that doesn't expose WrapWithLabels.\nfunc (r *Recipient) WrapWithLabels(fileKey []byte) ([]*age.Stanza, []string, error) {\n\tlabel, arg := \"age-encryption.org/p256tag\", \"p256tag\"\n\tif r.Hybrid() {\n\t\tlabel, arg = \"age-encryption.org/mlkem768p256tag\", \"mlkem768p256tag\"\n\t}\n\n\tenc, s, err := hpke.NewSender(r.pk, hpke.HKDFSHA256(), hpke.ChaCha20Poly1305(), []byte(label))","sourceCodeStart":106,"sourceCodeEnd":142,"githubUrl":"https://github.com/FiloSottile/age/blob/b74dce4cdbe35b5e5f66c06d9612b72f89028758/tag/tag.go#L106-L142","documentation":"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.","triggerScenarios":"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.","commonSituations":"Almost never seen in the field; occasionally reported when a custom/forked crypto/hkdf package or exotic build constraint alters behavior.","solutions":["Use the standard library crypto/hkdf package (Go 1.24+) rather than a forked or shimmed implementation.","Retry the operation once to rule out transient backend failure.","If it persists, report the underlying wrapped error (%v detail) upstream to the age project."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"// Not applicable: inputs are already length-validated before hkdf.Extract; failure is not caller-preventable.","typeGuard":null,"tryCatchPattern":"tag, err := r.Tag(enc)\nif err != nil {\n    return fmt.Errorf(\"HKDF tag derivation failed: %w\", err)\n}","preventionTips":["Use standard crypto/hkdf (Go 1.24+), not forks.","Keep Go toolchain and dependencies current.","Retry once to rule out transient backend failure."],"tags":["go","hkdf","sha256","crypto","tag-derivation"],"backgroundTag":"hkdf-derive-failed","analyzedSha":"b74dce4cdbe35b5e5f66c06d9612b72f89028758","analyzedAt":"2026-08-31T23:59:31.627Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T05:18:18.240Z"}