CVE-2023-46234: Improper Verification of Cryptographic Signature (CWE-347)
value: browserify-sign is a package to duplicate the functionality of node's crypto public key functions, much of this is based on Fedor Indutny's work on indutny/tls.js. An upper bound check issue in `dsaVerify` function allows an attacker to construct signatures that can be successfully verified by any public key, thus leading to a signature forgery attack. All places in this project that involve DSA verification of user-input signatures will be affected by this vulnerability. This issue has been patched in version 4.2.2.

CWE description: The product does not verify, or incorrectly verifies, the cryptographic signature for data.
CVSS v3 base score: 7.5
Published: 2023-10-26T15:15Z
Repository: https://github.com/browserify/browserify-sign
Language: JavaScript
Files changed: browser/verify.js, test/index.js
Commit date: 2023-10-21 13:08:48 +0800
Commit message: [Fix] properly check the upper bound for DSA signatures

Co-authored-by: roadicing <roadicing@gmail.com>
Co-authored-by: Jordan Harband <ljharb@gmail.com>

Diff: diff --git a/browser/verify.js b/browser/verify.js
index 4e7074c..5a18080 100644
--- a/browser/verify.js
+++ b/browser/verify.js
@@ -78,9 +78,9 @@ function dsaVerify(sig, hash, pub) {
   return v.cmp(r) === 0;
 }
 
 function checkValue(b, q) {
   if (b.cmpn(0) <= 0) { throw new Error('invalid sig'); }
-  if (b.cmp(q) >= q) { throw new Error('invalid sig'); }
+  if (b.cmp(q) >= 0) { throw new Error('invalid sig'); }
 }
 
 module.exports = verify;
diff --git a/test/index.js b/test/index.js
index 7a4930f..f94f932 100644
--- a/test/index.js
+++ b/test/index.js
@@ -3,10 +3,13 @@
 var Buffer = require('safe-buffer').Buffer;
 var asn1 = require('parse-asn1/asn1');
 var test = require('tape').test;
 var nCrypto = require('crypto');
 var semver = require('semver');
+var BN = require('bn.js');
+var parseKeys = require('parse-asn1');
+
 var bCrypto = require('../browser');
 var fixtures = require('./fixtures');
 
 fixtures.valid.rsa.forEach(function (f) {
   var message = Buffer.from(f.message);
@@ -152,10 +155,39 @@ fixtures.valid.ec.forEach(function (f) {
       t.ok(bVer.update(message).verify(pub, nSig), 'browser validate node sig');
 
       t.end();
     });
   }
+
+  var s = parseKeys(pub).data.q;
+  test(
+    f.message + ' against a fake signature',
+    { skip: !s || '(this test only applies to DSA signatures and not EC signatures, this is ' + f.scheme + ')' },
+    function (t) {
+      var messageBase64 = Buffer.from(f.message, 'base64');
+
+      // forge a fake signature
+      var r = new BN('1');
+
+      try {
+        var fakeSig = asn1.signature.encode({ r: r, s: s }, 'der');
+      } catch (e) {
+        t.ifError(e);
+        t.end();
+        return;
+      }
+
+      var bVer = bCrypto.createVerify(f.scheme);
+      t['throws'](
+        function () { bVer.update(messageBase64).verify(pub, fakeSig); },
+        Error,
+        'fake signature is invalid'
+      );
+
+      t.end();
+    }
+  );
 });
 
 fixtures.valid.kvectors.forEach(function (f) {
   test('kvector algo: ' + f.algo + ' key len: ' + f.key.length + ' msg: ' + f.msg, function (t) {
     var key = Buffer.from(f.key, 'base64');

Vulnerable code:   if (b.cmp(q) >= q) { throw new Error('invalid sig'); }
Fixed code:   if (b.cmp(q) >= 0) { throw new Error('invalid sig'); }
var BN = require('bn.js');
var parseKeys = require('parse-asn1');


  var s = parseKeys(pub).data.q;
  test(
    f.message + ' against a fake signature',
    { skip: !s || '(this test only applies to DSA signatures and not EC signatures, this is ' + f.scheme + ')' },
    function (t) {
      var messageBase64 = Buffer.from(f.message, 'base64');

      // forge a fake signature
      var r = new BN('1');

      try {
        var fakeSig = asn1.signature.encode({ r: r, s: s }, 'der');
      } catch (e) {
        t.ifError(e);
        t.end();
        return;
      }

      var bVer = bCrypto.createVerify(f.scheme);
      t['throws'](
        function () { bVer.update(messageBase64).verify(pub, fakeSig); },
        Error,
        'fake signature is invalid'
      );

      t.end();
    }
  );