{"record":{"id":"6afddd1e15d5c98e","repo":"FiloSottile/age","slug":"failed-to-encrypt-file-key-v","errorCode":null,"errorMessage":"failed to encrypt file key: %v","messagePattern":"failed to encrypt file key: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"tag/tag.go","lineNumber":148,"sourceCode":"// \"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))\n\tif err != nil {\n\t\treturn nil, nil, fmt.Errorf(\"failed to set up HPKE sender: %v\", err)\n\t}\n\tct, err := s.Seal(nil, fileKey)\n\tif err != nil {\n\t\treturn nil, nil, fmt.Errorf(\"failed to encrypt file key: %v\", err)\n\t}\n\n\ttag, err := r.Tag(enc)\n\tif err != nil {\n\t\treturn nil, nil, fmt.Errorf(\"failed to compute tag: %v\", err)\n\t}\n\n\tl := &age.Stanza{\n\t\tType: arg,\n\t\tArgs: []string{\n\t\t\tformat.EncodeToString(tag[:4]),\n\t\t\tformat.EncodeToString(enc),\n\t\t},\n\t\tBody: ct,\n\t}\n\n\tif r.Hybrid() {\n\t\treturn []*age.Stanza{l}, []string{\"postquantum\"}, nil","sourceCodeStart":130,"sourceCodeEnd":166,"githubUrl":"https://github.com/FiloSottile/age/blob/b74dce4cdbe35b5e5f66c06d9612b72f89028758/tag/tag.go#L130-L166","documentation":"After the HPKE sender is set up, WrapWithLabels seals the 16-byte age file key with ChaCha20Poly1305 via s.Seal. HPKE Seal fails if the plaintext exceeds the AEAD's size limits or the context is in an invalid state; for a 16-byte file key this is effectively unreachable, but it is wrapped and reported here.","triggerScenarios":"Calling Wrap/WrapWithLabels with a fileKey exceeding ChaCha20Poly1305 plaintext limits, or with an HPKE sender context that failed to initialize cleanly (only reachable through abnormal internal state or a swapped crypto backend).","commonSituations":"Plugin code passing an oversized buffer instead of the 16-byte file key; patched crypto backends; memory-corruption style bugs in exotic environments.","solutions":["Pass exactly the 16-byte age file key to Wrap/WrapWithLabels.","Use the standard crypto stack (unmodified filippo.io/hpke) and a current Go toolchain.","Inspect the wrapped %v detail to identify the underlying Seal failure.","Retry encryption with a freshly generated file key if the input may be corrupted."],"exampleFix":"// before\nct, err := r.Wrap(entireHeader) // oversized plaintext\n// after\nvar fileKey [16]byte\n// ... fill fileKey ...\nstanzas, err := r.Wrap(fileKey[:])","handlingStrategy":"validation","validationCode":"const fileKeySize = 16\nfunc validFileKey(k []byte) bool { return len(k) == fileKeySize }","typeGuard":"func is16Bytes(b []byte) bool { return len(b) == 16 }","tryCatchPattern":"stanzas, _, err := r.WrapWithLabels(fileKey)\nif err != nil {\n    return fmt.Errorf(\"file key encryption failed: %w\", err)\n}","preventionTips":["Pass exactly the 16-byte age file key.","Use unmodified filippo.io/hpke and a current Go toolchain.","Include the wrapped error text in diagnostics."],"tags":["go","age","hpke","aead","encryption","chacha20poly1305"],"backgroundTag":"file-key-encryption-failed","analyzedSha":"b74dce4cdbe35b5e5f66c06d9612b72f89028758","analyzedAt":"2026-08-31T23:59:31.627Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T05:18:18.240Z"}