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