1 /* dsa-2.1.2.js (c) 2016-2020 Kenji Urushimma | kjur.github.io/jsrsasign/license
  2  */
  3 /*
  4  * dsa.js - new DSA class
  5  *
  6  * Copyright (c) 2016-2020 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) {
 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 
 68     this.p = null;
 69     this.q = null;
 70     this.g = null;
 71     this.y = null;
 72     this.x = null;
 73     this.type = "DSA";
 74     this.isPrivate = false;
 75     this.isPublic = false;
 76 
 77     //===========================
 78     // PUBLIC METHODS
 79     //===========================
 80 
 81     /**
 82      * set DSA private key by key parameters of BigInteger object
 83      * @name setPrivate
 84      * @memberOf KJUR.crypto.DSA#
 85      * @function
 86      * @param {BigInteger} p prime P parameter
 87      * @param {BigInteger} q sub prime Q parameter
 88      * @param {BigInteger} g base G parameter
 89      * @param {BigInteger} y public key Y or null
 90      * @param {BigInteger} x private key X
 91      * @since jsrsasign 7.0.0 dsa 2.0.0
 92      */
 93     this.setPrivate = function(p, q, g, y, x) {
 94 	this.isPrivate = true;
 95 	this.p = p;
 96 	this.q = q;
 97 	this.g = g;
 98 	this.y = y;
 99 	this.x = x;
100     };
101 
102     /**
103      * set DSA private key by key parameters of hexadecimal string
104      * @name setPrivateHex
105      * @memberOf KJUR.crypto.DSA#
106      * @function
107      * @param {String} hP prime P parameter
108      * @param {String} hQ sub prime Q parameter
109      * @param {String} hG base G parameter
110      * @param {String} hY public key Y or null
111      * @param {String} hX private key X
112      * @since jsrsasign 7.1.0 dsa 2.1.0
113      */
114     this.setPrivateHex = function(hP, hQ, hG, hY, hX) {
115 	var biP, biQ, biG, biY, biX;
116         biP = new BigInteger(hP, 16);
117         biQ = new BigInteger(hQ, 16);
118         biG = new BigInteger(hG, 16);
119 	if (typeof hY === "string" && hY.length > 1) {
120             biY = new BigInteger(hY, 16);
121 	} else {
122 	    biY = null;
123 	}
124         biX = new BigInteger(hX, 16);
125         this.setPrivate(biP, biQ, biG, biY, biX);
126     };
127 
128     /**
129      * set DSA public key by key parameters of BigInteger object
130      * @name setPublic
131      * @memberOf KJUR.crypto.DSA#
132      * @function
133      * @param {BigInteger} p prime P parameter
134      * @param {BigInteger} q sub prime Q parameter
135      * @param {BigInteger} g base G parameter
136      * @param {BigInteger} y public key Y
137      * @since jsrsasign 7.0.0 dsa 2.0.0
138      */
139     this.setPublic = function(p, q, g, y) {
140 	_validatePublicArgs(p, q, g, y);
141 
142 	this.isPublic = true;
143 	this.p = p;
144 	this.q = q;
145 	this.g = g;
146 	this.y = y;
147 	this.x = null;
148     };
149 
150     /**
151      * set DSA public key by key parameters of hexadecimal string
152      * @name setPublicHex
153      * @memberOf KJUR.crypto.DSA#
154      * @function
155      * @param {String} hP prime P parameter
156      * @param {String} hQ sub prime Q parameter
157      * @param {String} hG base G parameter
158      * @param {String} hY public key Y
159      * @since jsrsasign 7.1.0 dsa 2.1.0
160      */
161     this.setPublicHex = function(hP, hQ, hG, hY) {
162 	var biP, biQ, biG, biY;
163         biP = new BigInteger(hP, 16);
164         biQ = new BigInteger(hQ, 16);
165         biG = new BigInteger(hG, 16);
166         biY = new BigInteger(hY, 16);
167         this.setPublic(biP, biQ, biG, biY);
168     };
169 
170     /**
171      * sign to hashed message by this DSA private key object
172      * @name signWithMessageHash
173      * @memberOf KJUR.crypto.DSA#
174      * @function
175      * @param {String} sHashHex hexadecimal string of hashed message
176      * @return {String} hexadecimal string of ASN.1 encoded DSA signature value
177      * @since jsrsasign 7.0.0 dsa 2.0.0
178      */
179     this.signWithMessageHash = function(sHashHex) {
180 	var p = this.p; // parameter p
181 	var q = this.q; // parameter q
182 	var g = this.g; // parameter g
183 	var y = this.y; // public key (p q g y)
184 	var x = this.x; // private key
185 
186 	// NIST FIPS 186-4 4.6 DSA Signature Generation (p19)
187 	// 2. get z where the left most min(N, outlen) bits of Hash(M)
188 	var hZ = sHashHex.substr(0, q.bitLength() / 4);
189 	var z = new BigInteger(hZ, 16);
190 
191 	var k, r, s;
192 	do {
193 	    // NIST FIPS 186-4 4.5 DSA Per-Message Secret Number (p18)
194 	    // 1. get random k where 0 < k < q
195 	    k = KJUR.crypto.Util.getRandomBigIntegerMinToMax(BigInteger.ONE.add(BigInteger.ONE),
196 							 q.subtract(BigInteger.ONE));
197 
198 	    // 3. get r where (g^k mod p) mod q, r != 0
199 	    r = (g.modPow(k,p)).mod(q); 
200 
201 	    // 4. get s where k^-1 (z + xr) mod q, s != 0
202 	    s = (k.modInverse(q).multiply(z.add(x.multiply(r)))).mod(q);
203 	} while (r.compareTo(BigInteger.ZERO) == 0 || s.compareTo(BigInteger.ZERO) == 0);
204 
205 	// 5. signature (r, s)
206 	var result = KJUR.asn1.ASN1Util.jsonToASN1HEX({
207 	    "seq": [{"int": {"bigint": r}}, {"int": {"bigint": s}}] 
208 	});
209 	return result;
210     };
211 
212     /**
213      * verify signature by this DSA public key object
214      * @name verifyWithMessageHash
215      * @memberOf KJUR.crypto.DSA#
216      * @function
217      * @param {String} sHashHex hexadecimal string of hashed message
218      * @param {String} hSigVal hexadecimal string of ASN.1 encoded DSA signature value
219      * @return {Boolean} true if the signature is valid otherwise false.
220      * @since jsrsasign 7.0.0 dsa 2.0.0
221      */
222     this.verifyWithMessageHash = function(sHashHex, hSigVal) {
223 	var p = this.p; // parameter p
224 	var q = this.q; // parameter q
225 	var g = this.g; // parameter g
226 	var y = this.y; // public key (p q g y)
227 
228 	// 1. parse ASN.1 signature (r, s)
229 	var rs = this.parseASN1Signature(hSigVal);
230         var r = rs[0];
231         var s = rs[1];
232 
233 	// NIST FIPS 186-4 4.6 DSA Signature Generation (p19)
234 	// 2. get z where the left most min(N, outlen) bits of Hash(M)
235 	var hZ = sHashHex.substr(0, q.bitLength() / 4);
236 	var z = new BigInteger(hZ, 16);
237 
238 	// NIST FIPS 186-4 4.7 DSA Signature Validation (p19)
239 	// 3.1. 0 < r < q
240 	if (BigInteger.ZERO.compareTo(r) > 0 || r.compareTo(q) > 0)
241 	    throw "invalid DSA signature";
242 
243 	// 3.2. 0 < s < q
244 	if (BigInteger.ZERO.compareTo(s) >= 0 || s.compareTo(q) > 0)
245 	    throw "invalid DSA signature";
246 
247 	// 4. get w where w = s^-1 mod q
248 	var w = s.modInverse(q);
249 
250 	// 5. get u1 where u1 = z w mod q
251 	var u1 = z.multiply(w).mod(q);
252 
253 	// 6. get u2 where u2 = r w mod q
254 	var u2 = r.multiply(w).mod(q);
255 
256 	// 7. get v where v = ((g^u1 y^u2) mod p) mod q
257 	var v = g.modPow(u1,p).multiply(y.modPow(u2,p)).mod(p).mod(q);
258 
259 	// 8. signature is valid when v == r
260 	return v.compareTo(r) == 0;
261     };
262 
263     /**
264      * parse hexadecimal ASN.1 DSA signature value
265      * @name parseASN1Signature
266      * @memberOf KJUR.crypto.DSA#
267      * @function
268      * @param {String} hSigVal hexadecimal string of ASN.1 encoded DSA signature value
269      * @return {Array} array [r, s] of DSA signature value. Both r and s are BigInteger.
270      * @since jsrsasign 7.0.0 dsa 2.0.0
271      */
272     this.parseASN1Signature = function(hSigVal) {
273 	try {
274 	    var r = new _BigInteger(_getVbyListEx(hSigVal, 0, [0], "02"), 16);
275 	    var s = new _BigInteger(_getVbyListEx(hSigVal, 0, [1], "02"), 16);
276 	    return [r, s];
277 	} catch (ex) {
278 	    throw new Error("malformed ASN.1 DSA signature");
279 	}
280     }
281 
282     /**
283      * read an ASN.1 hexadecimal string of PKCS#1/5 plain DSA private key<br/>
284      * @name readPKCS5PrvKeyHex
285      * @memberOf KJUR.crypto.DSA#
286      * @function
287      * @param {String} h hexadecimal string of PKCS#1/5 DSA private key
288      * @since jsrsasign 7.1.0 dsa 2.1.0
289      */
290     this.readPKCS5PrvKeyHex = function(h) {
291 	var hP, hQ, hG, hY, hX;
292 
293 	if (_isASN1HEX(h) === false)
294 	    throw new Error("not ASN.1 hex string");
295 
296 	try {
297 	    hP = _getVbyListEx(h, 0, [1], "02");
298 	    hQ = _getVbyListEx(h, 0, [2], "02");
299 	    hG = _getVbyListEx(h, 0, [3], "02");
300 	    hY = _getVbyListEx(h, 0, [4], "02");
301 	    hX = _getVbyListEx(h, 0, [5], "02");
302 	} catch(ex) {
303 	    //console.log("EXCEPTION:" + ex);
304 	    throw new Error("malformed PKCS#1/5 plain DSA private key");
305 	}
306 
307 	this.setPrivateHex(hP, hQ, hG, hY, hX);
308     };
309 
310     /**
311      * read an ASN.1 hexadecimal string of PKCS#8 plain DSA private key<br/>
312      * @name readPKCS8PrvKeyHex
313      * @memberOf KJUR.crypto.DSA#
314      * @function
315      * @param {String} h hexadecimal string of PKCS#8 DSA private key
316      * @since jsrsasign 7.1.0 dsa 2.1.0
317      */
318     this.readPKCS8PrvKeyHex = function(h) {
319 	var hP, hQ, hG, hX;
320 
321 	if (_isASN1HEX(h) === false)
322 	    throw new Error("not ASN.1 hex string");
323 
324 	try {
325 	    hP = _getVbyListEx(h, 0, [1, 1, 0], "02");
326 	    hQ = _getVbyListEx(h, 0, [1, 1, 1], "02");
327 	    hG = _getVbyListEx(h, 0, [1, 1, 2], "02");
328 	    hX = _getVbyListEx(h, 0, [2, 0], "02");
329 	} catch(ex) {
330 	    //console.log("EXCEPTION:" + ex);
331 	    throw new Error("malformed PKCS#8 plain DSA private key");
332 	}
333 
334 	this.setPrivateHex(hP, hQ, hG, null, hX);
335     };
336 
337     /**
338      * read an ASN.1 hexadecimal string of PKCS#8 plain DSA private key<br/>
339      * @name readPKCS8PubKeyHex
340      * @memberOf KJUR.crypto.DSA#
341      * @function
342      * @param {String} h hexadecimal string of PKCS#8 DSA private key
343      * @since jsrsasign 7.1.0 dsa 2.1.0
344      */
345     this.readPKCS8PubKeyHex = function(h) {
346 	var hP, hQ, hG, hY;
347 
348 	if (_isASN1HEX(h) === false)
349 	    throw new Error("not ASN.1 hex string");
350 
351 	try {
352 	    hP = _getVbyListEx(h, 0, [0, 1, 0], "02");
353 	    hQ = _getVbyListEx(h, 0, [0, 1, 1], "02");
354 	    hG = _getVbyListEx(h, 0, [0, 1, 2], "02");
355 	    hY = _getVbyListEx(h, 0, [1, 0], "02");
356 	} catch(ex) {
357 	    //console.log("EXCEPTION:" + ex);
358 	    throw new Error("malformed PKCS#8 DSA public key");
359 	}
360 
361 	this.setPublicHex(hP, hQ, hG, hY);
362     };
363 
364     /**
365      * read an ASN.1 hexadecimal string of X.509 DSA public key certificate<br/>
366      * @name readCertPubKeyHex
367      * @memberOf KJUR.crypto.DSA#
368      * @function
369      * @param {String} h hexadecimal string of X.509 DSA public key certificate
370      * @param {Integer} nthPKI (DEPRECATED to use)
371      * @since jsrsasign 7.1.0 dsa 2.1.0
372      * @description
373      * This method reads a hexadecimal string of X.509 DSA public key certificate
374      * and set public key parameter internally.
375      * @example
376      * dsa = new KJUR.crypto.DSA();
377      * dsa.readCertPubKeyHex("30...");
378      */
379     this.readCertPubKeyHex = function(h, nthPKI) {
380 	//if (nthPKI !== 5) nthPKI = 6;
381 	var hP, hQ, hG, hY;
382 
383 	if (_isASN1HEX(h) === false)
384 	    throw new Error("not ASN.1 hex string");
385 
386 	try {
387 	    hP = _getVbyListEx(h, 0, [0, 5, 0, 1, 0], "02");
388 	    hQ = _getVbyListEx(h, 0, [0, 5, 0, 1, 1], "02");
389 	    hG = _getVbyListEx(h, 0, [0, 5, 0, 1, 2], "02");
390 	    hY = _getVbyListEx(h, 0, [0, 5, 1, 0], "02");
391 	} catch(ex) {
392 	    //console.log("EXCEPTION:" + ex);
393 	    throw new Error("malformed X.509 certificate DSA public key");
394 	}
395 
396 	this.setPublicHex(hP, hQ, hG, hY);
397     };
398 }
399