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