CVE-2023-25653 (published 2023-02-16T19:15Z)

node-jose is a JavaScript implementation of the JSON Object Signing and Encryption (JOSE) for web browsers and node.js-based servers. Prior to version 2.2.0, when using the non-default "fallback" crypto back-end, ECC operations in `node-jose` can trigger a Denial-of-Service (DoS) condition, due to a possible infinite loop in an internal calculation.  For some ECC operations, this condition is triggered randomly; for others, it can be triggered by malicious input. The issue has been patched in version 2.2.0. Since this issue is only present in the "fallback" crypto implementation, it can be avoided by ensuring that either WebCrypto or the Node `crypto` module is available in the JS environment where `node-jose` is being run.

CVSS v3 base score: 7.5, HIGH (CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H)

Weakness information:
cwe_id: CWE-835
cwe_name: Loop with Unreachable Exit Condition ('Infinite Loop')
description: The product contains an iteration or loop with an exit condition that cannot be reached, i.e., an infinite loop.
extended_description: If the loop can be influenced by an attacker, this weakness could allow attackers to consume excessive resources such as CPU or memory.
         

Fix information:
repo_url: https://github.com/cisco/node-jose
commit_details: repo_url: https://github.com/cisco/node-jose
author: Richard Barnes
author_date: 2023-02-08 08:28:24-05:00
msg: Merge pull request from GHSA-5h4j-qrvg-9xhw
repository: repo_url: https://github.com/cisco/node-jose
repo_name: cisco/node-jose
repo_language: JavaScript
owner: cisco
file_changes: filename: math.js
change_type: ModificationType.MODIFY
diff: @@ -46,6 +46,7 @@ function barrettRevert(x) { return x; }
 
 // x = x mod m (HAC 14.42)
 function barrettReduce(x) {
+  if (x.s < 0) { throw Error("Barrett reduction on negative input"); }
   x.drShiftTo(this.m.t-1,this.r2);
   if(x.t > this.m.t+1) { x.t = this.m.t+1; x.clamp(); }
   this.mu.multiplyUpperTo(this.r2,this.m.t+1,this.q3);

programming_language: JavaScript
filename: forge.js
change_type: ModificationType.MODIFY
diff: @@ -88,4 +88,13 @@ modeRaw.prototype.decrypt = function(input, output, finish) {
   forge.cipher.registerAlgorithm(name, factory);
 })();
 
+// Ensure that the jsbn modInverse function always returns a positive result
+const originalModInverse = forge.jsbn.BigInteger.prototype.modInverse;
+const positiveModInverse = function(m) {
+  const inv = originalModInverse.apply(this, [m]);
+  return inv.mod(m);
+}
+
+forge.jsbn.BigInteger.prototype.modInverse = positiveModInverse;
+
 module.exports = forge;

programming_language: JavaScript
filename: ecc-test.js
change_type: ModificationType.ADD
diff: @@ -0,0 +1,29 @@
+/*!
+ *
+ * Copyright (c) 2015 Cisco Systems, Inc. See LICENSE file.
+ */
+"use strict";
+
+var assert = require("chai").assert;
+
+const CURVES = require('../../lib/deps/ecc/curves.js');
+const BigInteger = require('../../lib/deps/forge').jsbn.BigInteger;
+
+describe("ecc/positive", function() {
+  const negativeModInverseCases = [
+    '101067240514044546216936289506154965497874315269115226505131909313278720169941',
+    '47260992668897782856940293132731814279826643476197468731642996160637470667669',
+  ]
+  
+  const p = CURVES["P-256"].curve.p;
+
+  const runner = () => {
+    for (const kStr of negativeModInverseCases) {
+      const k = new BigInteger(kStr);
+      const kinv = k.modInverse(p);
+      assert.isAtLeast(kinv.s, 0, "Negative mod inverse");
+    }
+  };
+
+  it('normalizes negative modular inverses', runner);
+})

programming_language: JavaScript