Skip to content

Commit 7a3ae70

Browse files
authored
Merge branch 'master' into fix/bug-004-modinverse-dos
2 parents 2af4cb4 + dc41d49 commit 7a3ae70

File tree

5 files changed

+82
-11
lines changed

5 files changed

+82
-11
lines changed

ext/jsbn.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -354,7 +354,7 @@ function bnpSquareTo(r) {
354354
// r != q, this != m. q or r may be null.
355355
function bnpDivRemTo(m,q,r) {
356356
var pm = m.abs();
357-
if(pm.t <= 0) return;
357+
if(pm.t <= 0) throw "BigInteger divide by zero";
358358
var pt = this.abs();
359359
if(pt.t < pm.t) {
360360
if(q != null) q.fromInt(0);

ext/rsa.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,15 @@ function RSASetPublic(N, E) {
167167
} else {
168168
throw "Invalid RSA public key";
169169
}
170+
171+
if (this.n == null ||
172+
typeof this.n.compareTo !== "function" ||
173+
this.n.compareTo(BigInteger.ONE) <= 0 ||
174+
this.e == null ||
175+
isNaN(this.e) ||
176+
this.e <= 0) {
177+
throw "Invalid RSA public key";
178+
}
170179
}
171180

172181
// Perform raw public operation on "x": return x^e (mod n)

src/dsa-2.0.js

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -164,21 +164,24 @@ KJUR.crypto.DSA = function() {
164164
var y = this.y; // public key (p q g y)
165165
var x = this.x; // private key
166166

167-
// NIST FIPS 186-4 4.5 DSA Per-Message Secret Number (p18)
168-
// 1. get random k where 0 < k < q
169-
var k = KJUR.crypto.Util.getRandomBigIntegerMinToMax(BigInteger.ONE.add(BigInteger.ONE),
170-
q.subtract(BigInteger.ONE));
171-
172167
// NIST FIPS 186-4 4.6 DSA Signature Generation (p19)
173168
// 2. get z where the left most min(N, outlen) bits of Hash(M)
174169
var hZ = sHashHex.substr(0, q.bitLength() / 4);
175170
var z = new BigInteger(hZ, 16);
176171

177-
// 3. get r where (g^k mod p) mod q, r != 0
178-
var r = (g.modPow(k,p)).mod(q);
172+
var k, r, s;
173+
do {
174+
// NIST FIPS 186-4 4.5 DSA Per-Message Secret Number (p18)
175+
// 1. get random k where 0 < k < q
176+
k = KJUR.crypto.Util.getRandomBigIntegerMinToMax(BigInteger.ONE.add(BigInteger.ONE),
177+
q.subtract(BigInteger.ONE));
178+
179+
// 3. get r where (g^k mod p) mod q, r != 0
180+
r = (g.modPow(k,p)).mod(q);
179181

180-
// 4. get s where k^-1 (z + xr) mod q, s != 0
181-
var s = (k.modInverse(q).multiply(z.add(x.multiply(r)))).mod(q);
182+
// 4. get s where k^-1 (z + xr) mod q, s != 0
183+
s = (k.modInverse(q).multiply(z.add(x.multiply(r)))).mod(q);
184+
} while (r.compareTo(BigInteger.ZERO) == 0 || s.compareTo(BigInteger.ZERO) == 0);
182185

183186
// 5. signature (r, s)
184187
var result = KJUR.asn1.ASN1Util.jsonToASN1HEX({

test/qunit-do-crypto.html

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -232,6 +232,31 @@
232232
equal(n, 100, "100 times success:" + n0 + ":" + n1 + ":" + n2 + ":" + n3);
233233
});
234234

235+
test("RSASetPublic rejects zero modulus", function() {
236+
throws(function() {
237+
var pub = new RSAKey();
238+
pub.setPublic("00", "10001");
239+
},
240+
"Invalid RSA public key",
241+
"reject zero modulus");
242+
});
243+
244+
test("KEYUTIL.getKey rejects JWK with zero modulus", function() {
245+
throws(function() {
246+
KEYUTIL.getKey({kty: "RSA", n: "AA", e: "AQAB"});
247+
},
248+
"Invalid RSA public key",
249+
"reject JWK n=0");
250+
});
251+
252+
test("BigInteger.modPowInt throws when modulus is zero", function() {
253+
throws(function() {
254+
new BigInteger("deadbeef", 16).modPowInt(65537, BigInteger.ZERO);
255+
},
256+
"BigInteger divide by zero",
257+
"reject mod(0)");
258+
});
259+
235260
test("BigInteger.modInverse returns quickly for zero input", function() {
236261
var biM = new BigInteger("97", 10);
237262
var biR = new BigInteger("0", 10).modInverse(biM);
@@ -432,4 +457,3 @@ <h2 id="qunit-userAgent"></h2>
432457
</p>
433458
</body>
434459
</html>
435-

test/qunit-do-dsa.html

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,41 @@
129129
ok(dsa2.verifyWithMessageHash(sHashHex, hSigVal), "");
130130
});
131131

132+
test("signWithMessageHash retries when s is zero", function() {
133+
var pSmall = new BigInteger("17", 16);
134+
var qSmall = new BigInteger("0b", 16);
135+
var gSmall = new BigInteger("04", 16);
136+
var xSmall = new BigInteger("03", 16);
137+
var ySmall = gSmall.modPow(xSmall, pSmall);
138+
var dsaPrv = new KJUR.crypto.DSA();
139+
dsaPrv.setPrivate(pSmall, qSmall, gSmall, null, xSmall);
140+
var dsaPub = new KJUR.crypto.DSA();
141+
dsaPub.setPublic(pSmall, qSmall, gSmall, ySmall);
142+
143+
var kList = [new BigInteger("2", 10), new BigInteger("3", 10)];
144+
var kIdx = 0;
145+
var fOrig = KJUR.crypto.Util.getRandomBigIntegerMinToMax;
146+
147+
var r0 = gSmall.modPow(kList[0], pSmall).mod(qSmall);
148+
var z = qSmall.subtract(xSmall.multiply(r0).mod(qSmall)).mod(qSmall);
149+
var sHashHex = z.toString(16);
150+
151+
KJUR.crypto.Util.getRandomBigIntegerMinToMax = function() {
152+
return kList[kIdx++];
153+
};
154+
155+
try {
156+
var hSigVal = dsaPrv.signWithMessageHash(sHashHex);
157+
var rs = dsaPrv.parseASN1Signature(hSigVal);
158+
ok(kIdx >= 2, "retry with a new k");
159+
ok(rs[0].compareTo(BigInteger.ZERO) != 0, "r != 0");
160+
ok(rs[1].compareTo(BigInteger.ZERO) != 0, "s != 0");
161+
ok(dsaPub.verifyWithMessageHash(sHashHex, hSigVal), "signature verifies");
162+
} finally {
163+
KJUR.crypto.Util.getRandomBigIntegerMinToMax = fOrig;
164+
}
165+
});
166+
132167
test("readPKCS5PrvKeyHex d1", function() {
133168
var key = new KJUR.crypto.DSA();
134169
key.readPKCS5PrvKeyHex(D1PRVP5HEX);

0 commit comments

Comments
 (0)