{"record":{"id":"52f0398efbd3bfa1","repo":"golang/go","slug":"crypto-ecdh-bad-x25519-remote-ecdh-input-low-ord","errorCode":null,"errorMessage":"crypto/ecdh: bad X25519 remote ECDH input: low order point","messagePattern":"crypto/ecdh: bad X25519 remote ECDH input: low order point","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"src/crypto/ecdh/x25519.go","lineNumber":86,"sourceCode":"\nfunc (c *x25519Curve) NewPublicKey(key []byte) (*PublicKey, error) {\n\tif fips140only.Enforced() {\n\t\treturn nil, errors.New(\"crypto/ecdh: use of X25519 is not allowed in FIPS 140-only mode\")\n\t}\n\tif len(key) != x25519PublicKeySize {\n\t\treturn nil, errors.New(\"crypto/ecdh: invalid public key\")\n\t}\n\treturn &PublicKey{\n\t\tcurve:     c,\n\t\tpublicKey: bytes.Clone(key),\n\t}, nil\n}\n\nfunc (c *x25519Curve) ecdh(local *PrivateKey, remote *PublicKey) ([]byte, error) {\n\tout := make([]byte, x25519SharedSecretSize)\n\tx25519ScalarMult(out, local.privateKey, remote.publicKey)\n\tif isZero(out) {\n\t\treturn nil, errors.New(\"crypto/ecdh: bad X25519 remote ECDH input: low order point\")\n\t}\n\treturn out, nil\n}\n\nfunc x25519ScalarMult(dst, scalar, point []byte) {\n\tvar e [32]byte\n\n\tcopy(e[:], scalar[:])\n\te[0] &= 248\n\te[31] &= 127\n\te[31] |= 64\n\n\tvar x1, x2, z2, x3, z3, tmp0, tmp1 field.Element\n\tx1.SetBytes(point[:])\n\tx2.One()\n\tx3.Set(&x1)\n\tz3.One()\n","sourceCodeStart":68,"sourceCodeEnd":104,"githubUrl":"https://github.com/golang/go/blob/b6b368adc57c96c3151d224d172029f233ead2c3/src/crypto/ecdh/x25519.go#L68-L104","documentation":"Thrown by x25519Curve.ecdh after computing the shared secret via x25519ScalarMult. If the output is all zeros (isZero(out)), the remote public key was a low-order point — a well-known malicious or degenerate input that forces the shared secret to zero. This is a critical security check that prevents small-subgroup and all-zero output attacks per RFC 7748 Section 6.1.","triggerScenarios":"Calling ECDH on a PrivateKey with a remote PublicKey that is a low-order point (e.g., all-zero bytes, or one of the few known small-subgroup points on the Montgomery curve). This typically arises when the peer sends a crafted or corrupted public key, or when an attacker injects an all-zero key to force a predictable shared secret.","commonSituations":"A TLS or Noise protocol peer sends a malformed or adversarial X25519 public key; a key exchange where the remote side has a bug producing zero-filled buffers; testing with placeholder/zero keys; a man-in-the-middle injecting a degenerate point to force a known shared secret.","solutions":["Treat this as a security-critical failure: abort the key exchange and close the connection — never proceed with a zero shared secret.","Validate the remote public key provenance (TLS certificate chain, signed key exchange messages) before calling ECDH to prevent adversarial inputs.","If this occurs during testing, replace placeholder zero-byte keys with keys generated by ecdh.X25519().GenerateKey()."],"exampleFix":"// before\nsecret, err := priv.ECDH(remotePub)\nif err != nil { log.Println(err); return err }\n\n// after\nsecret, err := priv.ECDH(remotePub)\nif err != nil {\n    // low-order point — abort key exchange, do not retry\n    return fmt.Errorf(\"key exchange failed (possible attack): %w\", err)\n}","handlingStrategy":"try-catch","validationCode":"func isLikelyLowOrderPoint(pub *ecdh.PublicKey) bool {\n    // X25519 all-zero key is the most common low-order point\n    for _, b := range pub.Bytes() {\n        if b != 0 { return false }\n    }\n    return true\n}","typeGuard":null,"tryCatchPattern":"secret, err := priv.ECDH(remotePub)\nif err != nil {\n    // Abort immediately — do not retry or fall back.\n    // This may indicate an adversarial peer.\n    return fmt.Errorf(\"ECDH failed, possible low-order attack: %w\", err)\n}","preventionTips":["Treat ECDH errors as security-critical: never proceed with a failed key exchange.","Authenticate the peer's public key (e.g., via TLS cert pinning or signed key exchange) before ECDH.","Never use placeholder zero-byte keys in production code paths."],"tags":["crypto","ecdh","x25519","security","key-exchange","attack-prevention"],"backgroundTag":null,"analyzedSha":"b6b368adc57c96c3151d224d172029f233ead2c3","analyzedAt":"2026-08-12T00:22:02.250Z","schemaVersion":2},"datasetVersion":"2026-08-12T13:17:24.610Z"}