VYPR
Low severityNVD Advisory· Published Aug 2, 2024· Updated Nov 3, 2025

CVE-2024-42460

CVE-2024-42460

Description

In the Elliptic package 6.5.6 for Node.js, ECDSA signature malleability occurs because there is a missing check for whether the leading bit of r and s is zero.

AI Insight

LLM-synthesized narrative grounded in this CVE's description and references.

ECDSA signature malleability in Elliptic.js 6.5.6 due to missing check for leading zero bit in r and s, allowing signature forgery.

Vulnerability

Overview CVE-2024-42460 describes an ECDSA signature malleability flaw in the Elliptic package version 6.5.6 for Node.js. The root cause is the absence of a validation check for whether the leading bit of the signature components r and s is zero [1]. In ECDSA, signatures are malleable if an implementation does not enforce that r and s are in the canonical range (i.e., r and s must be less than the curve order n and have no leading zero bytes). Without this check, an attacker can modify a valid signature to produce another valid signature for the same message and public key.

Exploitation

Conditions Exploitation requires an attacker to have access to a valid ECDSA signature generated by the vulnerable library. No private key is needed; the attacker can simply transform the signature by adding the curve order n to s (or other transformations) to create a different but still valid signature. The attack is network-based and does not require authentication beyond obtaining a valid signature. The malleability arises during signature decoding and encoding, as the library did not reject signatures with leading zero bytes or high bits in r or s [4].

Impact

Signature malleability can undermine protocols that rely on signature uniqueness, such as blockchain transactions (e.g., Bitcoin transaction malleability) or authentication schemes that use signatures as identifiers. An attacker could replay or modify signed messages, potentially leading to double-spending, transaction ID changes, or bypassing integrity checks. The impact is particularly severe in systems where signature uniqueness is critical for security.

Mitigation

The vulnerability is fixed in later versions of the Elliptic package. The commit [4] introduces explicit checks in the DER signature decoding function to reject signatures where r or s have a leading zero byte or a high bit set (i.e., the most significant bit is 1). Users should upgrade to a patched version (e.g., 6.5.7 or later). No workaround is available other than updating the library.

AI Insight generated on May 20, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
ellipticnpm
>= 2.0.0, < 6.5.76.5.7

Affected products

2
  • Node.js/Elliptic packagedescription
  • ghsa-coords
    Range: >= 2.0.0, < 6.5.7

Patches

2
accb61e9c1a0

lib: DER signature decoding correction

https://github.com/indutny/ellipticMarkus SchiffermüllerAug 14, 2024via ghsa
2 files changed · +11 0
  • lib/elliptic/ec/signature.js+10 0 modified
    @@ -38,6 +38,10 @@ function getLength(buf, p) {
         return false;
       }
     
    +  if(buf[p.place] === 0x00) {
    +    return false;
    +  }
    +
       var val = 0;
       for (var i = 0, off = p.place; i < octetLen; i++, off++) {
         val <<= 8;
    @@ -86,6 +90,9 @@ Signature.prototype._importDER = function _importDER(data, enc) {
       if (rlen === false) {
         return false;
       }
    +  if ((data[p.place] & 128) !== 0) {
    +    return false;
    +  }
       var r = data.slice(p.place, rlen + p.place);
       p.place += rlen;
       if (data[p.place++] !== 0x02) {
    @@ -98,6 +105,9 @@ Signature.prototype._importDER = function _importDER(data, enc) {
       if (data.length !== slen + p.place) {
         return false;
       }
    +  if ((data[p.place] & 128) !== 0) {
    +    return false;
    +  }
       var s = data.slice(p.place, slen + p.place);
       if (r[0] === 0) {
         if (r[1] & 0x80) {
    
  • lib/elliptic/eddsa/signature.js+1 0 modified
    @@ -21,6 +21,7 @@ function Signature(eddsa, sig) {
         sig = parseBytes(sig);
     
       if (Array.isArray(sig)) {
    +    assert(sig.length === eddsa.encodingLength * 2, 'Signature has invalid size');
         sig = {
           R: sig.slice(0, eddsa.encodingLength),
           S: sig.slice(eddsa.encodingLength),
    
b6ff1758d9a6

key: explicit methods for priv/pub/object

https://github.com/indutny/ellipticFedor IndutnyJan 16, 2015via ghsa
6 files changed · +88 64
  • lib/elliptic/curve/edwards.js+1 1 modified
    @@ -13,7 +13,7 @@ function EdwardsCurve(conf) {
       this.mOneA = this.twisted && conf.a == -1;
       this.extended = this.mOneA;
     
    -  Base.call(this, 'mont', conf);
    +  Base.call(this, 'edwards', conf);
     
       this.a = new bn(conf.a, 16).mod(this.red.m).toRed(this.red);
       this.c = new bn(conf.c, 16).toRed(this.red);
    
  • lib/elliptic/curve/mont.js+1 1 modified
    @@ -25,7 +25,7 @@ MontCurve.prototype.point = function point(x, z) {
     
     MontCurve.prototype.pointFromJSON = function pointFromJSON(obj) {
       return Point.fromJSON(this, obj);
    -}
    +};
     
     MontCurve.prototype.validate = function validate(point) {
       var x = point.normalize().x;
    
  • lib/elliptic/ec/index.js+21 8 modified
    @@ -35,8 +35,16 @@ function EC(options) {
     }
     module.exports = EC;
     
    -EC.prototype.keyPair = function keyPair(priv, pub) {
    -  return new KeyPair(this, priv, pub);
    +EC.prototype.keyPair = function keyPair(options) {
    +  return new KeyPair(this, options);
    +};
    +
    +EC.prototype.keyFromPrivate = function keyFromPrivate(priv, enc) {
    +  return KeyPair.fromPrivate(this, priv, enc);
    +};
    +
    +EC.prototype.keyFromPublic = function keyFromPublic(pub, enc) {
    +  return KeyPair.fromPublic(this, pub, enc);
     };
     
     EC.prototype.genKeyPair = function genKeyPair(options) {
    @@ -59,7 +67,7 @@ EC.prototype.genKeyPair = function genKeyPair(options) {
           continue;
     
         priv.iaddn(1);
    -    return this.keyPair(priv);
    +    return this.keyFromPrivate(priv);
       } while (true);
     };
     
    @@ -73,12 +81,17 @@ EC.prototype._truncateToN = function truncateToN(msg, truncOnly) {
         return msg;
     };
     
    -EC.prototype.sign = function sign(msg, key, options) {
    -  key = this.keyPair(key, 'hex');
    -  msg = this._truncateToN(new bn(msg, 16));
    +EC.prototype.sign = function sign(msg, key, enc, options) {
    +  if (typeof enc === 'object') {
    +    options = enc;
    +    enc = null;
    +  }
       if (!options)
         options = {};
     
    +  key = this.keyFromPrivate(key, enc);
    +  msg = this._truncateToN(new bn(msg, 16));
    +
       // Zero-extend key to provide enough entropy
       var bytes = this.n.byteLength();
       var bkey = key.getPrivate().toArray();
    @@ -125,9 +138,9 @@ EC.prototype.sign = function sign(msg, key, options) {
       } while (true);
     };
     
    -EC.prototype.verify = function verify(msg, signature, key) {
    +EC.prototype.verify = function verify(msg, signature, key, enc) {
       msg = this._truncateToN(new bn(msg, 16));
    -  key = this.keyPair(key, 'hex');
    +  key = this.keyFromPublic(key, enc);
       signature = new Signature(signature, 'hex');
     
       // Perform primitive values validation
    
  • lib/elliptic/ec/key.js+53 48 modified
    @@ -4,47 +4,39 @@ var elliptic = require('../../elliptic');
     var utils = elliptic.utils;
     var assert = utils.assert;
     
    -function KeyPair(ec, priv, pub) {
    -  if (priv instanceof KeyPair)
    -    return priv;
    -  if (pub instanceof KeyPair)
    -    return pub;
    -
    -  if (!priv) {
    -    priv = pub;
    -    pub = null;
    -  }
    -  if (priv !== null && typeof priv === 'object') {
    -    if (priv.x) {
    -      // KeyPair(public)
    -      pub = priv;
    -      priv = null;
    -    } else if (priv.priv || priv.pub) {
    -      // KeyPair({ priv: ..., pub: ... })
    -      pub = priv.pub;
    -      priv = priv.priv;
    -    }
    -  }
    -
    +function KeyPair(ec, options) {
       this.ec = ec;
       this.priv = null;
       this.pub = null;
     
    -  // KeyPair(public, 'hex')
    -  if (this._importPublicHex(priv, pub))
    -    return;
    -
    -  if (pub === 'hex')
    -    pub = null;
    -
    -  // KeyPair(priv, pub)
    -  if (priv)
    -    this._importPrivate(priv);
    -  if (pub)
    -    this._importPublic(pub);
    +  // KeyPair(ec, { priv: ..., pub: ... })
    +  if (options.priv)
    +    this._importPrivate(options.priv, options.privEnc);
    +  if (options.pub)
    +    this._importPublic(options.pub, options.pubEnc);
     }
     module.exports = KeyPair;
     
    +KeyPair.fromPublic = function fromPublic(ec, pub, enc) {
    +  if (pub instanceof KeyPair)
    +    return pub;
    +
    +  return new KeyPair(ec, {
    +    pub: pub,
    +    pubEnc: enc
    +  });
    +};
    +
    +KeyPair.fromPrivate = function fromPrivate(ec, priv, enc) {
    +  if (priv instanceof KeyPair)
    +    return priv;
    +
    +  return new KeyPair(ec, {
    +    priv: priv,
    +    privEnc: enc
    +  });
    +};
    +
     KeyPair.prototype.validate = function validate() {
       var pub = this.getPublic();
     
    @@ -77,14 +69,19 @@ KeyPair.prototype.getPublic = function getPublic(compact, enc) {
       for (var i = x.length; i < len; i++)
         x.unshift(0);
     
    -  if (compact) {
    -    var res = [ this.pub.getY().isEven() ? 0x02 : 0x03 ].concat(x);
    +  if (this.ec.curve.type !== 'mont') {
    +    if (compact) {
    +      var res = [ this.pub.getY().isEven() ? 0x02 : 0x03 ].concat(x);
    +    } else {
    +      var y = this.pub.getY().toArray();
    +      for (var i = y.length; i < len; i++)
    +        y.unshift(0);
    +      var res = [ 0x04 ].concat(x, y);
    +    }
       } else {
    -    var y = this.pub.getY().toArray();
    -    for (var i = y.length; i < len; i++)
    -      y.unshift(0);
    -    var res = [ 0x04 ].concat(x, y);
    +    res = x;
       }
    +
       return utils.encode(res, enc);
     };
     
    @@ -95,20 +92,28 @@ KeyPair.prototype.getPrivate = function getPrivate(enc) {
         return this.priv;
     };
     
    -KeyPair.prototype._importPrivate = function _importPrivate(key) {
    +KeyPair.prototype._importPrivate = function _importPrivate(key, enc) {
       this.priv = new bn(key, 16);
     
       // Ensure that the priv won't be bigger than n, otherwise we may fail
       // in fixed multiplication method
       this.priv = this.priv.mod(this.ec.curve.n);
     };
     
    -KeyPair.prototype._importPublic = function _importPublic(key) {
    -  this.pub = this.ec.curve.point(key.x, key.y);
    -};
    +KeyPair.prototype._importPublic = function _importPublic(key, enc) {
    +  if (key.x || key.y) {
    +    this.pub = this.ec.curve.point(key.x, key.y);
    +    return;
    +  }
     
    -KeyPair.prototype._importPublicHex = function _importPublic(key, enc) {
       key = utils.toArray(key, enc);
    +  if (this.ec.curve.type !== 'mont')
    +    return this._importPublicShort(key);
    +  else
    +    return this._importPublicMont(key);
    +};
    +
    +KeyPair.prototype._importPublicShort = function _importPublicShort(key) {
       var len = this.ec.curve.p.byteLength();
       if (key[0] === 0x04 && key.length - 1 === 2 * len) {
         this.pub = this.ec.curve.point(
    @@ -117,11 +122,11 @@ KeyPair.prototype._importPublicHex = function _importPublic(key, enc) {
       } else if ((key[0] === 0x02 || key[0] === 0x03) && key.length - 1 === len) {
         this.pub = this.ec.curve.pointFromX(key[0] === 0x03,
                                             key.slice(1, 1 +len));
    -  } else {
    -    return false;
       }
    +};
     
    -  return true;
    +KeyPair.prototype._importPublicMont = function _importPublicMont(key) {
    +  this.pub = this.ec.curve.point(key, 1);
     };
     
     // ECDH
    
  • test/ecdh-test.js+6 0 modified
    @@ -12,6 +12,12 @@ describe('ECDH', function() {
           var sh2 = s2.derive(s1.getPublic());
     
           assert.equal(sh1.toString(16), sh2.toString(16));
    +
    +      var sh1 = s1.derive(ecdh.keyFromPublic(s2.getPublic('hex'), 'hex')
    +                              .getPublic());
    +      var sh2 = s2.derive(ecdh.keyFromPublic(s1.getPublic('hex'), 'hex')
    +                              .getPublic());
    +      assert.equal(sh1.toString(16), sh2.toString(16));
         });
       }
     
    
  • test/ecdsa-test.js+6 6 modified
    @@ -34,15 +34,15 @@ describe('ECDSA', function() {
           assert(keys.verify(msg, signature), 'On-key verify');
     
           // Load private key from hex
    -      var keys = ecdsa.keyPair(keys.getPrivate('hex'), 'hex');
    +      var keys = ecdsa.keyFromPrivate(keys.getPrivate('hex'), 'hex');
           var signature = ecdsa.sign(msg, keys);
           assert(ecdsa.verify(msg, signature, keys), 'hex-private verify');
     
           // Load public key from compact hex
    -      var keys = ecdsa.keyPair(keys.getPublic(true, 'hex'), 'hex');
    +      var keys = ecdsa.keyFromPublic(keys.getPublic(true, 'hex'), 'hex');
     
           // Load public key from hex
    -      var keys = ecdsa.keyPair(keys.getPublic('hex'), 'hex');
    +      var keys = ecdsa.keyFromPublic(keys.getPublic('hex'), 'hex');
     
           // DER encoding
           var dsign = signature.toDER('hex');
    @@ -55,8 +55,8 @@ describe('ECDSA', function() {
           assert(!ecdsa.verify(msg, signature, keys), 'Wrong key verify');
     
           // Invalid private key
    -      var keys = ecdsa.keyPair(keys.getPrivate('hex') + keys.getPrivate('hex'),
    -                               'hex');
    +      var keys = ecdsa.keyFromPrivate(keys.getPrivate('hex') +
    +                                      keys.getPrivate('hex'));
           assert(!ecdsa.verify(msg, signature, keys), 'Wrong key verify');
         });
       }
    @@ -77,7 +77,7 @@ describe('ECDSA', function() {
               var sign = ecdsa.sign(dgst, opt.key);
               assert.equal(sign.r.toString(16), c.r);
               assert.equal(sign.s.toString(16), c.s);
    -          assert.ok(ecdsa.keyPair(opt.pub).validate().result,
    +          assert.ok(ecdsa.keyFromPublic(opt.pub).validate().result,
                         'Invalid public key');
               assert.ok(ecdsa.verify(dgst, sign, opt.pub),
                         'Invalid signature');
    

Vulnerability mechanics

Generated on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

6

News mentions

0

No linked articles in our index yet.