{"record":{"id":"bb8aef81620a3d57","repo":"FiloSottile/age","slug":"invalid-ed25519-public-key-v","errorCode":null,"errorMessage":"invalid Ed25519 public key: %v","messagePattern":"invalid Ed25519 public key: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"agessh/agessh.go","lineNumber":164,"sourceCode":"\nvar _ age.Recipient = &Ed25519Recipient{}\n\nfunc NewEd25519Recipient(pk ssh.PublicKey) (*Ed25519Recipient, error) {\n\tif pk.Type() != \"ssh-ed25519\" {\n\t\treturn nil, errors.New(\"SSH public key is not an Ed25519 key\")\n\t}\n\n\tcpk, ok := pk.(ssh.CryptoPublicKey)\n\tif !ok {\n\t\treturn nil, errors.New(\"pk does not implement ssh.CryptoPublicKey\")\n\t}\n\tepk, ok := cpk.CryptoPublicKey().(ed25519.PublicKey)\n\tif !ok {\n\t\treturn nil, errors.New(\"unexpected public key type\")\n\t}\n\tmpk, err := ed25519PublicKeyToCurve25519(epk)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"invalid Ed25519 public key: %v\", err)\n\t}\n\n\treturn &Ed25519Recipient{\n\t\tsshKey:         pk,\n\t\ttheirPublicKey: mpk,\n\t}, nil\n}\n\nfunc ParseRecipient(s string) (age.Recipient, error) {\n\tpubKey, _, _, _, err := ssh.ParseAuthorizedKey([]byte(s))\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"malformed SSH recipient: %q: %v\", s, err)\n\t}\n\n\tvar r age.Recipient\n\tswitch t := pubKey.Type(); t {\n\tcase \"ssh-rsa\":\n\t\tr, err = NewRSARecipient(pubKey)","sourceCodeStart":146,"sourceCodeEnd":182,"githubUrl":"https://github.com/FiloSottile/age/blob/b74dce4cdbe35b5e5f66c06d9612b72f89028758/agessh/agessh.go#L146-L182","documentation":"NewEd25519Recipient converts an SSH Ed25519 public key to a Curve25519 key for X25519-based encryption. If that conversion (ed25519PublicKeyToCurve25519) fails, the key is reported as an invalid Ed25519 public key. A valid ed25519.PublicKey should convert, so failure implies a malformed or out-of-range key point.","triggerScenarios":"Calling agessh.NewEd25519Recipient (directly or via ParseRecipient) with an ssh-ed25519 public key whose conversion to Curve25519 fails — e.g. a non-canonical or corrupted key point, or an unexpected key type sneaking in earlier (\"unexpected public key type\").","commonSituations":"Hand-edited or corrupted authorized_keys entries; keys generated by non-standard tooling; paste errors truncating or altering the base64 key body.","solutions":["Regenerate the SSH key pair with ssh-keygen -t ed25519 and re-share the public key.","Validate the public key first with ssh.ParseAuthorizedKey and a test conversion before use.","Check the key string for truncation/whitespace corruption when copying between systems."],"exampleFix":"// before\nr, err := agessh.NewEd25519Recipient(corruptedPubKey) // conversion fails\n// after\nif _, _, _, _, err := ssh.ParseAuthorizedKey([]byte(pubKeyStr)); err != nil {\n    return fmt.Errorf(\"invalid public key: %w\", err)\n}\nr, err := agessh.NewEd25519Recipient(parsedKey)","handlingStrategy":"validation","validationCode":"pubKey, _, _, _, err := ssh.ParseAuthorizedKey([]byte(s))\nif err != nil || pubKey.Type() != \"ssh-ed25519\" {\n    return errors.New(\"not a valid ssh-ed25519 public key\")\n}","typeGuard":"func isEd25519Recipient(s string) bool {\n    k, _, _, _, err := ssh.ParseAuthorizedKey([]byte(s))\n    return err == nil && k.Type() == \"ssh-ed25519\"\n}","tryCatchPattern":"r, err := agessh.NewEd25519Recipient(pk)\nif err != nil {\n    return fmt.Errorf(\"cannot use SSH key for age: %w\", err)\n}","preventionTips":["Generate keys with ssh-keygen -t ed25519 from standard OpenSSH tooling.","Never hand-edit or truncate public key strings.","Pre-validate recipient strings with ssh.ParseAuthorizedKey before encryption."],"tags":["ssh","ed25519","key-format"],"backgroundTag":"invalid-ed25519-public-key","analyzedSha":"b74dce4cdbe35b5e5f66c06d9612b72f89028758","analyzedAt":"2026-08-31T23:59:31.627Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T05:18:18.240Z"}