Skip to content

Commit 0710e39

Browse files
authored
Merge pull request #645 from Kr0emer/fix/bug-001-sign_s_zero
fix(dsa): retry signing when r or s is zero
2 parents 37a271e + abe0f95 commit 0710e39

File tree

2 files changed

+47
-9
lines changed

2 files changed

+47
-9
lines changed

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-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)