Skip to content

Commit 37b4c06

Browse files
authored
Merge pull request #646 from Kr0emer/fix/bug-002-dsa-domain-params-validation
fix(dsa): reject invalid domain parameters to prevent universal signature forgery
2 parents ca5b027 + f508ddd commit 37b4c06

File tree

3 files changed

+81
-2
lines changed

3 files changed

+81
-2
lines changed

src/dsa-2.0.js

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,24 @@ KJUR.crypto.DSA = function() {
4747
_getVbyList = _ASN1HEX.getVbyList,
4848
_getVbyListEx = _ASN1HEX.getVbyListEx,
4949
_isASN1HEX = _ASN1HEX.isASN1HEX,
50-
_BigInteger = BigInteger;
50+
_BigInteger = BigInteger,
51+
_BI_ONE = BigInteger.ONE;
52+
53+
var _validatePublicArgs = function(p, q, g, y) {
54+
if (p == null || q == null || g == null || y == null)
55+
throw new Error("invalid DSA public key");
56+
57+
// FIPS 186-4 4.7: domain parameters and public key shall be validated.
58+
if (_BI_ONE.compareTo(q) >= 0 || q.compareTo(p) >= 0)
59+
throw new Error("invalid DSA public key");
60+
if (_BI_ONE.compareTo(g) >= 0 || g.compareTo(p) >= 0)
61+
throw new Error("invalid DSA public key");
62+
if (_BI_ONE.compareTo(y) >= 0 || y.compareTo(p) >= 0)
63+
throw new Error("invalid DSA public key");
64+
if (g.modPow(q, p).compareTo(_BI_ONE) != 0)
65+
throw new Error("invalid DSA public key");
66+
};
67+
5168
this.p = null;
5269
this.q = null;
5370
this.g = null;
@@ -120,6 +137,8 @@ KJUR.crypto.DSA = function() {
120137
* @since jsrsasign 7.0.0 dsa 2.0.0
121138
*/
122139
this.setPublic = function(p, q, g, y) {
140+
_validatePublicArgs(p, q, g, y);
141+
123142
this.isPublic = true;
124143
this.p = p;
125144
this.q = q;

test/qunit-do-dsa.html

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

132+
test("setPublicHex rejects invalid domain parameter g=1", function() {
133+
var dsa = new KJUR.crypto.DSA();
134+
var f = false;
135+
try {
136+
dsa.setPublicHex("17", "0b", "01", "01");
137+
} catch (ex) {
138+
f = true;
139+
}
140+
ok(f, "invalid dsa public key rejected");
141+
});
142+
132143
test("signWithMessageHash retries when s is zero", function() {
133144
var pSmall = new BigInteger("17", 16);
134145
var qSmall = new BigInteger("0b", 16);

test/qunit-do-x509.html

Lines changed: 50 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -611,6 +611,56 @@
611611
equal(x.verifySignature(pubkey), false, "false");
612612
});
613613

614+
test("verifySignature DSA forged cert with g=1 shall be rejected", function() {
615+
var DERSeq = function(arr) { return new KJUR.asn1.DERSequence({array: arr}); };
616+
var DERSet = function(arr) { return new KJUR.asn1.DERSet({array: arr}); };
617+
var DERInt = function(v) {
618+
if (typeof v == "string") return new KJUR.asn1.DERInteger({hex: v});
619+
return new KJUR.asn1.DERInteger({"int": v});
620+
};
621+
var DEROid = function(o) { return new KJUR.asn1.DERObjectIdentifier({oid: o}); };
622+
var DERUtf8 = function(s) { return new KJUR.asn1.DERUTF8String({str: s}); };
623+
var DERBits = function(hex) { return new KJUR.asn1.DERBitString({hex: "00" + hex}); };
624+
var DERUtcT = function(s) { return new KJUR.asn1.DERUTCTime({str: s}); };
625+
var DERTag = function(t, o) {
626+
return new KJUR.asn1.DERTaggedObject({tag: t, explicit: true, obj: o});
627+
};
628+
var rdnCN = function(cn) { return DERSeq([DERSet([DERSeq([DEROid("2.5.4.3"), DERUtf8(cn)])])]); };
629+
630+
var pHex = "17";
631+
var qHex = "0b";
632+
var gHex = "01";
633+
var yHex = "01";
634+
635+
var dsaParams = DERSeq([DERInt(pHex), DERInt(qHex), DERInt(gHex)]);
636+
var spki = DERSeq([DERSeq([DEROid("1.2.840.10040.4.1"), dsaParams]),
637+
DERBits(DERInt(yHex).getEncodedHex())]);
638+
639+
var tbs = DERSeq([
640+
DERTag("a0", DERInt(2)),
641+
DERInt(1),
642+
DERSeq([DEROid("1.2.840.10040.4.3")]),
643+
rdnCN("Malicious CA"),
644+
DERSeq([DERUtcT("250101000000Z"), DERUtcT("351231235959Z")]),
645+
rdnCN("Malicious CA"),
646+
spki
647+
]);
648+
var forgedSigHex = DERSeq([DERInt(1), DERInt(7)]).getEncodedHex();
649+
var certHex = DERSeq([tbs, DERSeq([DEROid("1.2.840.10040.4.3")]), DERBits(forgedSigHex)]).getEncodedHex();
650+
var certPEM = KJUR.asn1.ASN1Util.getPEMStringFromHex(certHex, "CERTIFICATE");
651+
652+
var accepted = false;
653+
try {
654+
var x = new X509();
655+
x.readCertPEM(certPEM);
656+
var pubkey = x.getPublicKey();
657+
accepted = x.verifySignature(pubkey);
658+
} catch (ex) {
659+
accepted = false;
660+
}
661+
equal(accepted, false, "forged certificate rejected");
662+
});
663+
614664
test("verifySignature ECDSA k1.self.sha1.cer with k1.pub.p8 VALID", function() {
615665
var pubkey = KEYUTIL.getKey(K1PUBP8);
616666
var x = new X509();
@@ -691,4 +741,3 @@
691741

692742
</body>
693743
</html>
694-

0 commit comments

Comments
 (0)