{"record":{"id":"57c000acc089affe","repo":"grafana/k6","slug":"keys-are-not-valid-elliptic-curve-keys","errorCode":null,"errorMessage":"keys are not valid elliptic curve keys","messagePattern":"keys are not valid elliptic curve keys","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/js/modules/k6/webcrypto/elliptic_curve.go","lineNumber":725,"sourceCode":"\t}\n\n\tx, y := elliptic.Unmarshal(crv, k.Bytes()) //nolint:staticcheck // we need to use the Unmarshal function\n\tif x == nil {\n\t\treturn nil, fmt.Errorf(\"unable to convert ECDH public key to ECDSA public key, curve: %s\", crv.Params().Name)\n\t}\n\n\treturn &ecdsa.PublicKey{\n\t\tCurve: crv,\n\t\tX:     x,\n\t\tY:     y,\n\t}, nil\n}\n\nfunc ensureKeysUseSameCurve(k1, k2 CryptoKey) error {\n\tecAlg1, ok1 := k1.Algorithm.(EcKeyAlgorithm)\n\tecAlg2, ok2 := k2.Algorithm.(EcKeyAlgorithm)\n\tif !ok1 || !ok2 {\n\t\treturn errors.New(\"keys are not valid elliptic curve keys\")\n\t}\n\n\tif ecAlg1.NamedCurve != ecAlg2.NamedCurve {\n\t\treturn errors.New(\"keys have different curves \" + string(ecAlg1.NamedCurve) + \" and \" + string(ecAlg2.NamedCurve))\n\t}\n\n\treturn nil\n}\n","sourceCodeStart":707,"sourceCodeEnd":734,"githubUrl":"https://github.com/grafana/k6/blob/93accf6570dcd306ca5e99cc44c393ee3797761b/internal/js/modules/k6/webcrypto/elliptic_curve.go#L707-L734","documentation":"ensureKeysUseSameCurve (internal/js/modules/k6/webcrypto/elliptic_curve.go:721) validates the two keys used in ECDH deriveBits/deriveKey. It first asserts both CryptoKeys carry an EcKeyAlgorithm; if either does not — e.g. one of them is an AES, HMAC, or otherwise non-elliptic key — it returns \"keys are not valid elliptic curve keys\" before any curve comparison happens.","triggerScenarios":"`crypto.subtle.deriveBits({ name: 'ECDH', public: someKey }, baseKey, 256)` where someKey or baseKey is not an elliptic-curve key — for example an AES-GCM key passed as `public`, or a non-ECDH private key as the base key.","commonSituations":"Variable mix-ups where a symmetric key is passed where the peer's ECDH public key belongs; key-management maps that return the wrong entry; refactors that swap argument order in deriveBits/deriveKey calls.","solutions":["Ensure both keys are ECDH keys: the base key generated with { name: 'ECDH', namedCurve: ... } and public being the peer's ECDH public key","Log key.algorithm and key.type before deriving to confirm what each variable holds","Regenerate both keys as a matched ECDH pair if provenance is unclear"],"exampleFix":"// before\nconst aesKey = await crypto.subtle.generateKey({ name: 'AES-GCM', length: 256 }, true, ['encrypt']);\nconst bits = await crypto.subtle.deriveBits(\n  { name: 'ECDH', public: aesKey }, ecdhPrivateKey, 256);\n\n// after\nconst peerPub = await crypto.subtle.generateKey({ name: 'ECDH', namedCurve: 'P-256' }, true, []);\nconst bits = await crypto.subtle.deriveBits(\n  { name: 'ECDH', public: peerPub.publicKey }, ecdhPrivateKey, 256);","handlingStrategy":"validation","validationCode":"const isEcdhKey = (k) => k && k.algorithm && k.algorithm.name === 'ECDH' && k.algorithm.namedCurve;\nif (!isEcdhKey(baseKey) || !isEcdhKey(params.public)) {\n  throw new TypeError('both keys passed to deriveBits/deriveKey must be ECDH keys');\n}","typeGuard":"const isEcdhCryptoKey = (k) =>\n  !!k && typeof k === 'object' && k.algorithm?.name === 'ECDH' && !!k.algorithm.namedCurve;","tryCatchPattern":"try {\n  bits = await crypto.subtle.deriveBits({ name: 'ECDH', public: peerPub }, priv, 256);\n} catch (e) {\n  if (String(e.message).includes('not valid elliptic curve keys')) throw new TypeError('a non-ECDH key was passed to the ECDH derivation');\n  throw e;\n}","preventionTips":["Store keys in typed variables (ecdhPriv, peerPub) instead of generic key maps","Assert key.algorithm.name before every derive call in crypto helpers","Generate the peer public key import with an explicit ECDH algorithm identifier"],"tags":["webcrypto","ecdh","key-management","validation"],"backgroundTag":null,"analyzedSha":"93accf6570dcd306ca5e99cc44c393ee3797761b","analyzedAt":"2026-08-15T21:23:27.118Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}