{"record":{"id":"aa42ec9e7fd7b4ec","repo":"FiloSottile/age","slug":"wrong-ecdh-curve","errorCode":null,"errorMessage":"wrong ecdh Curve","messagePattern":"wrong ecdh Curve","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"plugin/encode.go","lineNumber":90,"sourceCode":"func validPluginName(name string) bool {\n\tif name == \"\" {\n\t\treturn false\n\t}\n\tallowed := \"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+-._\"\n\tfor _, r := range name {\n\t\tif !strings.ContainsRune(allowed, r) {\n\t\t\treturn false\n\t\t}\n\t}\n\treturn true\n}\n\n// EncodeX25519Recipient encodes a native X25519 recipient from a\n// [crypto/ecdh.X25519] public key. It's meant for plugins that implement\n// identities that are compatible with native recipients.\nfunc EncodeX25519Recipient(pk *ecdh.PublicKey) (string, error) {\n\tif pk.Curve() != ecdh.X25519() {\n\t\treturn \"\", fmt.Errorf(\"wrong ecdh Curve\")\n\t}\n\treturn bech32.Encode(\"age\", pk.Bytes())\n}\n\n// EncodeHybridRecipient encodes a native MLKEM768-X25519 recipient from a\n// [crypto/mlkem.EncapsulationKey768] and a [crypto/ecdh.X25519] public key.\n// It's meant for plugins that implement identities that are compatible with\n// native recipients.\nfunc EncodeHybridRecipient(pq *mlkem.EncapsulationKey768, t *ecdh.PublicKey) (string, error) {\n\tif t.Curve() != ecdh.X25519() {\n\t\treturn \"\", fmt.Errorf(\"wrong ecdh Curve\")\n\t}\n\tpk, err := hpke.NewHybridPublicKey(pq, t)\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"failed to create hybrid public key: %v\", err)\n\t}\n\treturn bech32.Encode(\"age1pq\", pk.Bytes())\n}","sourceCodeStart":72,"sourceCodeEnd":108,"githubUrl":"https://github.com/FiloSottile/age/blob/b74dce4cdbe35b5e5f66c06d9612b72f89028758/plugin/encode.go#L72-L108","documentation":"EncodeX25519Recipient converts a crypto/ecdh X25519 public key into a native 'age1...' recipient string for plugins wrapping compatible identities. It refuses any ecdh.PublicKey whose curve is not X25519, since the bech32 payload would otherwise be a key of the wrong type/length.","triggerScenarios":"Passing an ecdh.PublicKey created with ecdh.P256(), ecdh.P384(), ecdh.P521(), or ecdh.X448() instead of ecdh.X25519().","commonSituations":"Generic ECDH helper code that selects curves by config; refactoring from P256 to X25519 (or vice versa) but reusing the same key variable; plugins that support multiple curves calling the wrong encoder.","solutions":["Create the key with ecdh.X25519(): e.g. ecdh.X25519().NewPublicKey(pubBytes).","Check pk.Curve() == ecdh.X25519() before calling.","Use a curve-specific encoder if the key is NIST/X448.","Surface a clear error distinguishing curve types at the API boundary."],"exampleFix":"// before\npk, _ := ecdh.P256().NewPublicKey(pubBytes)\ns, err := plugin.EncodeX25519Recipient(pk)\n// after\npk, err := ecdh.X25519().NewPublicKey(pubBytes)\nif err != nil { return err }\ns, err := plugin.EncodeX25519Recipient(pk)","handlingStrategy":"type-guard","validationCode":"if pk.Curve() != ecdh.X25519() { return fmt.Errorf(\"need X25519 key, got %v\", pk.Curve()) }","typeGuard":"func isX25519(pk *ecdh.PublicKey) bool { return pk != nil && pk.Curve() == ecdh.X25519() }","tryCatchPattern":"s, err := plugin.EncodeX25519Recipient(pk)\nif err != nil {\n\treturn fmt.Errorf(\"EncodeX25519Recipient: %w (curve=%v)\", err, pk.Curve())\n}","preventionTips":["Construct keys only via ecdh.X25519().NewPublicKey/GenerateKey","Check Curve() at function boundaries when keys flow through generic code","Name variables by curve (x25519Pub, p256Pub) to avoid mixups"],"tags":["go","crypto","ecdh","x25519"],"backgroundTag":"wrong-curve-key-type","analyzedSha":"b74dce4cdbe35b5e5f66c06d9612b72f89028758","analyzedAt":"2026-08-31T23:59:31.627Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T05:18:18.240Z"}