1 /* dsa-2.1.3.js (c) 2016-2026 Kenji Urushimma | kjur.github.io/jsrsasign/license
  2  */
  3 /*
  4  * dsa.js - new DSA class
  5  *
  6  * Copyright (c) 2016-2026 Kenji Urushima (kenji.urushima@gmail.com)
  7  *
  8  * This software is licensed under the terms of the MIT License.
  9  * https://kjur.github.io/jsrsasign/license
 10  *
 11  * The above copyright and license notice shall be 
 12  * included in all copies or substantial portions of the Software.
 13  */
 14 
 15 /**
 16  * @fileOverview
 17  * @name dsa-2.0.js
 18  * @author Kenji Urushima kenji.urushima@gmail.com
 19  * @version jsrsasign 8.0.21 dsa 2.1.2 (2020-Jul-24)
 20  * @since jsrsasign 7.0.0
 21  * @license <a href="https://kjur.github.io/jsrsasign/license/">MIT License</a>
 22  */
 23 
 24 if (typeof KJUR == "undefined" || !KJUR) KJUR = {};
 25 if (typeof KJUR.crypto == "undefined" || !KJUR.crypto) KJUR.crypto = {};
 26 
 27 /**
 28  * class for DSA signing and verification
 29  * @name KJUR.crypto.DSA
 30  * @class class for DSA signing and verifcation
 31  * @since jsrsasign 7.0.0 dsa 2.0.0
 32  * @description
 33  * <p>
 34  * CAUTION: Most of the case, you don't need to use this class.
 35  * Please use {@link KJUR.crypto.Signature} class instead.
 36  * </p>
 37  * <p>
 38  * NOTE: Until jsrsasign 6.2.3, DSA class have used codes from openpgpjs library 1.0.0
 39  * licenced under LGPL licence. To avoid license issue dsa-2.0.js was re-written with
 40  * my own codes in jsrsasign 7.0.0. 
 41  * Some random number generators used in dsa-2.0.js was newly defined
 42  * in KJUR.crypto.Util class. Now all of LGPL codes are removed.
 43  * </p>
 44  */
 45 KJUR.crypto.DSA = function() {
 46     var _ASN1HEX = ASN1HEX,
 47         _getVbyList = _ASN1HEX.getVbyList,
 48         _getVbyListEx = _ASN1HEX.getVbyListEx,
 49 	_isASN1HEX = _ASN1HEX.isASN1HEX,
 50 	_BigInteger = BigInteger,
 51 	_BI_ONE = BigInteger.ONE;
 52 
 53     var _validatePublicArgs = function(p, q, g, y, bYOptional) {
 54 	if (p == null || q == null || g == null ||
 55 	    (y == null && bYOptional !== true))
 56 	    throw new Error("invalid DSA public key");
 57 
 58 	// FIPS 186-4 4.7: domain parameters and public key shall be validated.
 59 	if (y != null && (_BI_ONE.compareTo(y) >= 0 || y.compareTo(p) >= 0))
 60 	    throw new Error("invalid DSA public key");
 61 	if (_BI_ONE.compareTo(q) >= 0 || q.compareTo(p) >= 0)
 62 	    throw new Error("invalid DSA public key");
 63 	if (_BI_ONE.compareTo(g) >= 0 || g.compareTo(p) >= 0)
 64 	    throw new Error("invalid DSA public key");
 65 	if (g.modPow(q, p).compareTo(_BI_ONE) != 0)
 66 	    throw new Error("invalid DSA public key");
 67     };
 68 
 69     this.p = null;
 70     this.q = null;
 71     this.g = null;
 72     this.y = null;
 73     this.x = null;
 74     this.type = "DSA";
 75     this.isPrivate = false;
 76     this.isPublic = false;
 77 
 78     //===========================
 79     // PUBLIC METHODS
 80     //===========================
 81 
 82     /**
 83      * set DSA private key by key parameters of BigInteger object
 84      * @name setPrivate
 85      * @memberOf KJUR.crypto.DSA#
 86      * @function
 87      * @param {BigInteger} p prime P parameter
 88      * @param {BigInteger} q sub prime Q parameter
 89      * @param {BigInteger} g base G parameter
 90      * @param {BigInteger} y public key Y or null
 91      * @param {BigInteger} x private key X
 92      * @since jsrsasign 7.0.0 dsa 2.0.0
 93      */
 94     this.setPrivate = function(p, q, g, y, x) {
 95 	// y is optional for a private key (e.g. PKCS#8 private keys have no
 96 	// public key Y), but domain parameters and the public key shall be
 97 	// validated (FIPS 186-4 4.7) to prevent universal signature forgery.
 98 	_validatePublicArgs(p, q, g, y, true);
 99 
100 	this.isPrivate = true;
101 	this.p = p;
102 	this.q = q;
103 	this.g = g;
104 	this.y = y;
105 	this.x = x;
106     };
107 
108     /**
109      * set DSA private key by key parameters of hexadecimal string
110      * @name setPrivateHex
111      * @memberOf KJUR.crypto.DSA#
112      * @function
113      * @param {String} hP prime P parameter
114      * @param {String} hQ sub prime Q parameter
115      * @param {String} hG base G parameter
116      * @param {String} hY public key Y or null
117      * @param {String} hX private key X
118      * @since jsrsasign 7.1.0 dsa 2.1.0
119      */
120     this.setPrivateHex = function(hP, hQ, hG, hY, hX) {
121 	var biP, biQ, biG, biY, biX;
122         biP = new BigInteger(hP, 16);
123         biQ = new BigInteger(hQ, 16);
124         biG = new BigInteger(hG, 16);
125 	if (typeof hY === "string" && hY.length > 1) {
126             biY = new BigInteger(hY, 16);
127 	} else {
128 	    biY = null;
129 	}
130         biX = new BigInteger(hX, 16);
131         this.setPrivate(biP, biQ, biG, biY, biX);
132     };
133 
134     /**
135      * set DSA public key by key parameters of BigInteger object
136      * @name setPublic
137      * @memberOf KJUR.crypto.DSA#
138      * @function
139      * @param {BigInteger} p prime P parameter
140      * @param {BigInteger} q sub prime Q parameter
141      * @param {BigInteger} g base G parameter
142      * @param {BigInteger} y public key Y
143      * @since jsrsasign 7.0.0 dsa 2.0.0
144      */
145     this.setPublic = function(p, q, g, y) {
146 	_validatePublicArgs(p, q, g, y);
147 
148 	this.isPublic = true;
149 	this.p = p;
150 	this.q = q;
151 	this.g = g;
152 	this.y = y;
153 	this.x = null;
154     };
155 
156     /**
157      * set DSA public key by key parameters of hexadecimal string
158      * @name setPublicHex
159      * @memberOf KJUR.crypto.DSA#
160      * @function
161      * @param {String} hP prime P parameter
162      * @param {String} hQ sub prime Q parameter
163      * @param {String} hG base G parameter
164      * @param {String} hY public key Y
165      * @since jsrsasign 7.1.0 dsa 2.1.0
166      */
167     this.setPublicHex = function(hP, hQ, hG, hY) {
168 	var biP, biQ, biG, biY;
169         biP = new BigInteger(hP, 16);
170         biQ = new BigInteger(hQ, 16);
171         biG = new BigInteger(hG, 16);
172         biY = new BigInteger(hY, 16);
173         this.setPublic(biP, biQ, biG, biY);
174     };
175 
176     /**
177      * sign to hashed message by this DSA private key object
178      * @name signWithMessageHash
179      * @memberOf KJUR.crypto.DSA#
180      * @function
181      * @param {String} sHashHex hexadecimal string of hashed message
182      * @return {String} hexadecimal string of ASN.1 encoded DSA signature value
183      * @since jsrsasign 7.0.0 dsa 2.0.0
184      */
185     this.signWithMessageHash = function(sHashHex) {
186 	var p = this.p; // parameter p
187 	var q = this.q; // parameter q
188 	var g = this.g; // parameter g
189 	var y = this.y; // public key (p q g y)
190 	var x = this.x; // private key
191 
192 	// NIST FIPS 186-4 4.6 DSA Signature Generation (p19)
193 	// 2. get z where the left most min(N, outlen) bits of Hash(M)
194 	var hZ = sHashHex.substr(0, q.bitLength() / 4);
195 	var z = new BigInteger(hZ, 16);
196 
197 	var k, r, s;
198 	do {
199 	    // NIST FIPS 186-4 4.5 DSA Per-Message Secret Number (p18)
200 	    // 1. get random k where 0 < k < q
201 	    k = KJUR.crypto.Util.getRandomBigIntegerMinToMax(BigInteger.ONE.add(BigInteger.ONE),
202 							 q.subtract(BigInteger.ONE));
203 
204 	    // 3. get r where (g^k mod p) mod q, r != 0
205 	    r = (g.modPow(k,p)).mod(q); 
206 
207 	    // 4. get s where k^-1 (z + xr) mod q, s != 0
208 	    s = (k.modInverse(q).multiply(z.add(x.multiply(r)))).mod(q);
209 	} while (r.compareTo(BigInteger.ZERO) == 0 || s.compareTo(BigInteger.ZERO) == 0);
210 
211 	// 5. signature (r, s)
212 	var result = KJUR.asn1.ASN1Util.jsonToASN1HEX({
213 	    "seq": [{"int": {"bigint": r}}, {"int": {"bigint": s}}] 
214 	});
215 	return result;
216     };
217 
218     /**
219      * verify signature by this DSA public key object
220      * @name verifyWithMessageHash
221      * @memberOf KJUR.crypto.DSA#
222      * @function
223      * @param {String} sHashHex hexadecimal string of hashed message
224      * @param {String} hSigVal hexadecimal string of ASN.1 encoded DSA signature value
225      * @return {Boolean} true if the signature is valid otherwise false.
226      * @since jsrsasign 7.0.0 dsa 2.0.0
227      */
228     this.verifyWithMessageHash = function(sHashHex, hSigVal) {
229 	var p = this.p; // parameter p
230 	var q = this.q; // parameter q
231 	var g = this.g; // parameter g
232 	var y = this.y; // public key (p q g y)
233 
234 	// 1. parse ASN.1 signature (r, s)
235 	var rs = this.parseASN1Signature(hSigVal);
236         var r = rs[0];
237         var s = rs[1];
238 
239 	// NIST FIPS 186-4 4.6 DSA Signature Generation (p19)
240 	// 2. get z where the left most min(N, outlen) bits of Hash(M)
241 	var hZ = sHashHex.substr(0, q.bitLength() / 4);
242 	var z = new BigInteger(hZ, 16);
243 
244 	// NIST FIPS 186-4 4.7 DSA Signature Validation (p19)
245 	// 3.1. 0 =< r =< q
246 	if (BigInteger.ZERO.compareTo(r) >= 0 || r.compareTo(q) >= 0)
247 	    throw "invalid DSA signature";
248 
249 	// 3.2. 0 =< s =< q
250 	if (BigInteger.ZERO.compareTo(s) >= 0 || s.compareTo(q) >= 0)
251 	    throw "invalid DSA signature";
252 
253 	// 4. get w where w = s^-1 mod q
254 	var w = s.modInverse(q);
255 
256 	// 5. get u1 where u1 = z w mod q
257 	var u1 = z.multiply(w).mod(q);
258 
259 	// 6. get u2 where u2 = r w mod q
260 	var u2 = r.multiply(w).mod(q);
261 
262 	// 7. get v where v = ((g^u1 y^u2) mod p) mod q
263 	var v = g.modPow(u1,p).multiply(y.modPow(u2,p)).mod(p).mod(q);
264 
265 	// 8. signature is valid when v == r
266 	return v.compareTo(r) == 0;
267     };
268 
269     /**
270      * parse hexadecimal ASN.1 DSA signature value
271      * @name parseASN1Signature
272      * @memberOf KJUR.crypto.DSA#
273      * @function
274      * @param {String} hSigVal hexadecimal string of ASN.1 encoded DSA signature value
275      * @return {Array} array [r, s] of DSA signature value. Both r and s are BigInteger.
276      * @since jsrsasign 7.0.0 dsa 2.0.0
277      */
278     this.parseASN1Signature = function(hSigVal) {
279 	try {
280 	    var r = new _BigInteger(_getVbyListEx(hSigVal, 0, [0], "02"), 16);
281 	    var s = new _BigInteger(_getVbyListEx(hSigVal, 0, [1], "02"), 16);
282 	    return [r, s];
283 	} catch (ex) {
284 	    throw new Error("malformed ASN.1 DSA signature");
285 	}
286     }
287 
288     /**
289      * read an ASN.1 hexadecimal string of PKCS#1/5 plain DSA private key<br/>
290      * @name readPKCS5PrvKeyHex
291      * @memberOf KJUR.crypto.DSA#
292      * @function
293      * @param {String} h hexadecimal string of PKCS#1/5 DSA private key
294      * @since jsrsasign 7.1.0 dsa 2.1.0
295      */
296     this.readPKCS5PrvKeyHex = function(h) {
297 	var hP, hQ, hG, hY, hX;
298 
299 	if (_isASN1HEX(h) === false)
300 	    throw new Error("not ASN.1 hex string");
301 
302 	try {
303 	    hP = _getVbyListEx(h, 0, [1], "02");
304 	    hQ = _getVbyListEx(h, 0, [2], "02");
305 	    hG = _getVbyListEx(h, 0, [3], "02");
306 	    hY = _getVbyListEx(h, 0, [4], "02");
307 	    hX = _getVbyListEx(h, 0, [5], "02");
308 	} catch(ex) {
309 	    //console.log("EXCEPTION:" + ex);
310 	    throw new Error("malformed PKCS#1/5 plain DSA private key");
311 	}
312 
313 	this.setPrivateHex(hP, hQ, hG, hY, hX);
314     };
315 
316     /**
317      * read an ASN.1 hexadecimal string of PKCS#8 plain DSA private key<br/>
318      * @name readPKCS8PrvKeyHex
319      * @memberOf KJUR.crypto.DSA#
320      * @function
321      * @param {String} h hexadecimal string of PKCS#8 DSA private key
322      * @since jsrsasign 7.1.0 dsa 2.1.0
323      */
324     this.readPKCS8PrvKeyHex = function(h) {
325 	var hP, hQ, hG, hX;
326 
327 	if (_isASN1HEX(h) === false)
328 	    throw new Error("not ASN.1 hex string");
329 
330 	try {
331 	    hP = _getVbyListEx(h, 0, [1, 1, 0], "02");
332 	    hQ = _getVbyListEx(h, 0, [1, 1, 1], "02");
333 	    hG = _getVbyListEx(h, 0, [1, 1, 2], "02");
334 	    hX = _getVbyListEx(h, 0, [2, 0], "02");
335 	} catch(ex) {
336 	    //console.log("EXCEPTION:" + ex);
337 	    throw new Error("malformed PKCS#8 plain DSA private key");
338 	}
339 
340 	this.setPrivateHex(hP, hQ, hG, null, hX);
341     };
342 
343     /**
344      * read an ASN.1 hexadecimal string of PKCS#8 plain DSA private key<br/>
345      * @name readPKCS8PubKeyHex
346      * @memberOf KJUR.crypto.DSA#
347      * @function
348      * @param {String} h hexadecimal string of PKCS#8 DSA private key
349      * @since jsrsasign 7.1.0 dsa 2.1.0
350      */
351     this.readPKCS8PubKeyHex = function(h) {
352 	var hP, hQ, hG, hY;
353 
354 	if (_isASN1HEX(h) === false)
355 	    throw new Error("not ASN.1 hex string");
356 
357 	try {
358 	    hP = _getVbyListEx(h, 0, [0, 1, 0], "02");
359 	    hQ = _getVbyListEx(h, 0, [0, 1, 1], "02");
360 	    hG = _getVbyListEx(h, 0, [0, 1, 2], "02");
361 	    hY = _getVbyListEx(h, 0, [1, 0], "02");
362 	} catch(ex) {
363 	    //console.log("EXCEPTION:" + ex);
364 	    throw new Error("malformed PKCS#8 DSA public key");
365 	}
366 
367 	this.setPublicHex(hP, hQ, hG, hY);
368     };
369 
370     /**
371      * read an ASN.1 hexadecimal string of X.509 DSA public key certificate<br/>
372      * @name readCertPubKeyHex
373      * @memberOf KJUR.crypto.DSA#
374      * @function
375      * @param {String} h hexadecimal string of X.509 DSA public key certificate
376      * @param {Integer} nthPKI (DEPRECATED to use)
377      * @since jsrsasign 7.1.0 dsa 2.1.0
378      * @description
379      * This method reads a hexadecimal string of X.509 DSA public key certificate
380      * and set public key parameter internally.
381      * @example
382      * dsa = new KJUR.crypto.DSA();
383      * dsa.readCertPubKeyHex("30...");
384      */
385     this.readCertPubKeyHex = function(h, nthPKI) {
386 	//if (nthPKI !== 5) nthPKI = 6;
387 	var hP, hQ, hG, hY;
388 
389 	if (_isASN1HEX(h) === false)
390 	    throw new Error("not ASN.1 hex string");
391 
392 	try {
393 	    hP = _getVbyListEx(h, 0, [0, 5, 0, 1, 0], "02");
394 	    hQ = _getVbyListEx(h, 0, [0, 5, 0, 1, 1], "02");
395 	    hG = _getVbyListEx(h, 0, [0, 5, 0, 1, 2], "02");
396 	    hY = _getVbyListEx(h, 0, [0, 5, 1, 0], "02");
397 	} catch(ex) {
398 	    //console.log("EXCEPTION:" + ex);
399 	    throw new Error("malformed X.509 certificate DSA public key");
400 	}
401 
402 	this.setPublicHex(hP, hQ, hG, hY);
403     };
404 }
405