1 /*! dsa-modified-1.0.1.js (c) Recurity Labs GmbH, Kenji Urushimma | github.com/openpgpjs/openpgpjs/blob/master/LICENSE 2 */ 3 /* 4 * dsa-modified.js - modified DSA class of OpenPGP-JS 5 * 6 * Copyright (c) 2011-2013 Recurity Labs GmbH (github.com/openpgpjs) 7 * Kenji Urushima (kenji.urushima@gmail.com) 8 * LICENSE 9 * https://github.com/openpgpjs/openpgpjs/blob/master/LICENSE 10 */ 11 12 /** 13 * @fileOverview 14 * @name dsa-modified-1.0.js 15 * @author Recurity Labs GmbH (github.com/openpgpjs) and Kenji Urushima (kenji.urushima@gmail.com) 16 * @version 1.0.1 (2013-Oct-06) 17 * @since jsrsasign 4.1.6 18 * @license <a href="https://github.com/openpgpjs/openpgpjs/blob/master/LICENSE">LGPL License</a> 19 */ 20 21 if (typeof KJUR == "undefined" || !KJUR) KJUR = {}; 22 if (typeof KJUR.crypto == "undefined" || !KJUR.crypto) KJUR.crypto = {}; 23 24 /** 25 * class for DSA signing and verification 26 * @name KJUR.crypto.DSA 27 * @class class for DSA signing and verifcation 28 * @description 29 * <p> 30 * CAUTION: Most of the case, you don't need to use this class. 31 * Please use {@link KJUR.crypto.Signature} class instead. 32 * </p> 33 * <p> 34 * This class was originally developped by Recurity Labs GmbH for OpenPGP JavaScript library. 35 * (See {@link https://github.com/openpgpjs/openpgpjs/blob/master/src/ciphers/asymmetric/dsa.js}) 36 * </p> 37 */ 38 /* https://github.com/openpgpjs/openpgpjs/blob/master/src/ciphers/asymmetric/dsa.js */ 39 KJUR.crypto.DSA = function() { 40 this.p = null; 41 this.q = null; 42 this.g = null; 43 this.y = null; 44 this.x = null; 45 this.type = "DSA"; 46 47 //=========================== 48 // PUBLIC METHODS 49 //=========================== 50 51 /** 52 * set DSA private key by key specs 53 * @name setPrivate 54 * @memberOf KJUR.crypto.DSA 55 * @function 56 * @param {BigInteger} p prime P 57 * @param {BigInteger} q sub prime Q 58 * @param {BigInteger} g base G 59 * @param {BigInteger} y public key Y 60 * @param {BigInteger} x private key X 61 * @since dsa-modified 1.0.0 62 */ 63 this.setPrivate = function(p, q, g, y, x) { 64 this.isPrivate = true; 65 this.p = p; 66 this.q = q; 67 this.g = g; 68 this.y = y; 69 this.x = x; 70 }; 71 72 /** 73 * set DSA public key by key specs 74 * @name setPublic 75 * @memberOf KJUR.crypto.DSA 76 * @function 77 * @param {BigInteger} p prime P 78 * @param {BigInteger} q sub prime Q 79 * @param {BigInteger} g base G 80 * @param {BigInteger} y public key Y 81 * @since dsa-modified 1.0.0 82 */ 83 this.setPublic = function(p, q, g, y) { 84 this.isPublic = true; 85 this.p = p; 86 this.q = q; 87 this.g = g; 88 this.y = y; 89 this.x = null; 90 }; 91 92 /** 93 * sign to hashed message by this DSA private key object 94 * @name signWithMessageHash 95 * @memberOf KJUR.crypto.DSA 96 * @function 97 * @param {String} sHashHex hexadecimal string of hashed message 98 * @return {String} hexadecimal string of ASN.1 encoded DSA signature value 99 * @since dsa-modified 1.0.0 100 */ 101 this.signWithMessageHash = function(sHashHex) { 102 var p = this.p; 103 var q = this.q; 104 var g = this.g; 105 var y = this.y; 106 var x = this.x; 107 108 // 1. trim message hash 109 var hashHex = sHashHex.substr(0, q.bitLength() / 4); 110 var hash = new BigInteger(sHashHex, 16); 111 112 var k = getRandomBigIntegerInRange(BigInteger.ONE.add(BigInteger.ONE), 113 q.subtract(BigInteger.ONE)); 114 var s1 = (g.modPow(k,p)).mod(q); 115 var s2 = (k.modInverse(q).multiply(hash.add(x.multiply(s1)))).mod(q); 116 117 var result = KJUR.asn1.ASN1Util.jsonToASN1HEX({ 118 'seq': [{'int': {'bigint': s1}}, {'int': {'bigint': s2}}] 119 }); 120 return result; 121 }; 122 123 /** 124 * verify signature by this DSA public key object 125 * @name verifyWithMessageHash 126 * @memberOf KJUR.crypto.DSA 127 * @function 128 * @param {String} sHashHex hexadecimal string of hashed message 129 * @param {String} hSigVal hexadecimal string of ASN.1 encoded DSA signature value 130 * @return {Boolean} true if the signature is valid otherwise false. 131 * @since dsa-modified 1.0.0 132 */ 133 this.verifyWithMessageHash = function(sHashHex, hSigVal) { 134 var p = this.p; 135 var q = this.q; 136 var g = this.g; 137 var y = this.y; 138 139 // 1. parse ASN.1 signature 140 var s1s2 = this.parseASN1Signature(hSigVal); 141 var s1 = s1s2[0]; 142 var s2 = s1s2[1]; 143 144 // 2. trim message hash 145 var sHashHex = sHashHex.substr(0, q.bitLength() / 4); 146 var hash = new BigInteger(sHashHex, 16); 147 148 if (BigInteger.ZERO.compareTo(s1) > 0 || 149 s1.compareTo(q) > 0 || 150 BigInteger.ZERO.compareTo(s2) > 0 || 151 s2.compareTo(q) > 0) { 152 throw "invalid DSA signature"; 153 } 154 var w = s2.modInverse(q); 155 var u1 = hash.multiply(w).mod(q); 156 var u2 = s1.multiply(w).mod(q); 157 var dopublic = g.modPow(u1,p).multiply(y.modPow(u2,p)).mod(p).mod(q); 158 return dopublic.compareTo(s1) == 0; 159 }; 160 161 /** 162 * parse hexadecimal ASN.1 DSA signature value 163 * @name parseASN1Signature 164 * @memberOf KJUR.crypto.DSA 165 * @function 166 * @param {String} hSigVal hexadecimal string of ASN.1 encoded DSA signature value 167 * @return {Array} array [s1, s2] of DSA signature value. Both s1 and s2 are BigInteger. 168 * @since dsa-modified 1.0.0 169 */ 170 this.parseASN1Signature = function(hSigVal) { 171 try { 172 var s1 = new BigInteger(ASN1HEX.getVbyList(hSigVal, 0, [0], "02"), 16); 173 var s2 = new BigInteger(ASN1HEX.getVbyList(hSigVal, 0, [1], "02"), 16); 174 return [s1, s2]; 175 } catch (ex) { 176 throw "malformed DSA signature"; 177 } 178 } 179 180 // s1 = ((g**s) mod p) mod q 181 // s1 = ((s**-1)*(sha-1(m)+(s1*x) mod q) 182 function sign(hashalgo, m, g, p, q, x) { 183 // If the output size of the chosen hash is larger than the number of 184 // bits of q, the hash result is truncated to fit by taking the number 185 // of leftmost bits equal to the number of bits of q. This (possibly 186 // truncated) hash function result is treated as a number and used 187 // directly in the DSA signature algorithm. 188 189 var hashHex = KJUR.crypto.Util.hashString(m, hashalgo.toLowerCase()); 190 var hashHex = hashHex.substr(0, q.bitLength() / 4); 191 var hash = new BigInteger(hashHex, 16); 192 193 var k = getRandomBigIntegerInRange(BigInteger.ONE.add(BigInteger.ONE), 194 q.subtract(BigInteger.ONE)); 195 var s1 = (g.modPow(k,p)).mod(q); 196 var s2 = (k.modInverse(q).multiply(hash.add(x.multiply(s1)))).mod(q); 197 var result = new Array(); 198 result[0] = s1; 199 result[1] = s2; 200 return result; 201 } 202 203 function select_hash_algorithm(q) { 204 var usersetting = openpgp.config.config.prefer_hash_algorithm; 205 /* 206 * 1024-bit key, 160-bit q, SHA-1, SHA-224, SHA-256, SHA-384, or SHA-512 hash 207 * 2048-bit key, 224-bit q, SHA-224, SHA-256, SHA-384, or SHA-512 hash 208 * 2048-bit key, 256-bit q, SHA-256, SHA-384, or SHA-512 hash 209 * 3072-bit key, 256-bit q, SHA-256, SHA-384, or SHA-512 hash 210 */ 211 switch (Math.round(q.bitLength() / 8)) { 212 case 20: // 1024 bit 213 if (usersetting != 2 && 214 usersetting > 11 && 215 usersetting != 10 && 216 usersetting < 8) 217 return 2; // prefer sha1 218 return usersetting; 219 case 28: // 2048 bit 220 if (usersetting > 11 && 221 usersetting < 8) 222 return 11; 223 return usersetting; 224 case 32: // 4096 bit // prefer sha224 225 if (usersetting > 10 && 226 usersetting < 8) 227 return 8; // prefer sha256 228 return usersetting; 229 default: 230 util.print_debug("DSA select hash algorithm: returning null for an unknown length of q"); 231 return null; 232 233 } 234 } 235 this.select_hash_algorithm = select_hash_algorithm; 236 237 function verify(hashalgo, s1,s2,m,p,q,g,y) { 238 var hashHex = KJUR.crypto.Util.hashString(m, hashalgo.toLowerCase()); 239 var hashHex = hashHex.substr(0, q.bitLength() / 4); 240 var hash = new BigInteger(hashHex, 16); 241 242 if (BigInteger.ZERO.compareTo(s1) > 0 || 243 s1.compareTo(q) > 0 || 244 BigInteger.ZERO.compareTo(s2) > 0 || 245 s2.compareTo(q) > 0) { 246 util.print_error("invalid DSA Signature"); 247 return null; 248 } 249 var w = s2.modInverse(q); 250 var u1 = hash.multiply(w).mod(q); 251 var u2 = s1.multiply(w).mod(q); 252 var dopublic = g.modPow(u1,p).multiply(y.modPow(u2,p)).mod(p).mod(q); 253 return dopublic.compareTo(s1) == 0; 254 } 255 256 /* 257 * unused code. This can be used as a start to write a key generator 258 * function. 259 */ 260 function generateKey(bitcount) { 261 var qi = new BigInteger(bitcount, primeCenterie); 262 var pi = generateP(q, 512); 263 var gi = generateG(p, q, bitcount); 264 var xi; 265 do { 266 xi = new BigInteger(q.bitCount(), rand); 267 } while (x.compareTo(BigInteger.ZERO) != 1 && x.compareTo(q) != -1); 268 var yi = g.modPow(x, p); 269 return {x: xi, q: qi, p: pi, g: gi, y: yi}; 270 } 271 272 function generateP(q, bitlength, randomfn) { 273 if (bitlength % 64 != 0) { 274 return false; 275 } 276 var pTemp; 277 var pTemp2; 278 do { 279 pTemp = randomfn(bitcount, true); 280 pTemp2 = pTemp.subtract(BigInteger.ONE); 281 pTemp = pTemp.subtract(pTemp2.remainder(q)); 282 } while (!pTemp.isProbablePrime(primeCenterie) || pTemp.bitLength() != l); 283 return pTemp; 284 } 285 286 function generateG(p, q, bitlength, randomfn) { 287 var aux = p.subtract(BigInteger.ONE); 288 var pow = aux.divide(q); 289 var gTemp; 290 do { 291 gTemp = randomfn(bitlength); 292 } while (gTemp.compareTo(aux) != -1 && gTemp.compareTo(BigInteger.ONE) != 1); 293 return gTemp.modPow(pow, p); 294 } 295 296 function generateK(q, bitlength, randomfn) { 297 var tempK; 298 do { 299 tempK = randomfn(bitlength, false); 300 } while (tempK.compareTo(q) != -1 && tempK.compareTo(BigInteger.ZERO) != 1); 301 return tempK; 302 } 303 304 function generateR(q,p) { 305 k = generateK(q); 306 var r = g.modPow(k, p).mod(q); 307 return r; 308 } 309 310 function generateS(hashfn,k,r,m,q,x) { 311 var hash = hashfn(m); 312 s = (k.modInverse(q).multiply(hash.add(x.multiply(r)))).mod(q); 313 return s; 314 } 315 this.sign = sign; 316 this.verify = verify; 317 // this.generate = generateKey; 318 319 // 320 // METHODS FROM 321 // https://github.com/openpgpjs/openpgpjs/blob/master/src/ciphers/openpgp.crypto.js 322 // 323 function getRandomBigIntegerInRange(min, max) { 324 if (max.compareTo(min) <= 0) 325 return; 326 var range = max.subtract(min); 327 var r = getRandomBigInteger(range.bitLength()); 328 while (r > range) { 329 r = getRandomBigInteger(range.bitLength()); 330 } 331 return min.add(r); 332 } 333 334 function getRandomBigInteger(bits) { 335 if (bits < 0) 336 return null; 337 var numBytes = Math.floor((bits+7)/8); 338 339 var randomBits = getRandomBytes(numBytes); 340 if (bits % 8 > 0) { 341 randomBits = String.fromCharCode((Math.pow(2,bits % 8)-1) & 342 randomBits.charCodeAt(0)) + 343 randomBits.substring(1); 344 } 345 return new BigInteger(hexstrdump(randomBits), 16); 346 } 347 348 function getRandomBytes(length) { 349 var result = ''; 350 for (var i = 0; i < length; i++) { 351 result += String.fromCharCode(getSecureRandomOctet()); 352 } 353 return result; 354 } 355 356 function getSecureRandomOctet() { 357 var buf = new Uint32Array(1); 358 window.crypto.getRandomValues(buf); 359 return buf[0] & 0xFF; 360 } 361 362 // https://github.com/openpgpjs/openpgpjs/blob/master/src/util/util.js 363 function hexstrdump(str) { 364 if (str == null) 365 return ""; 366 var r=[]; 367 var e=str.length; 368 var c=0; 369 var h; 370 while(c<e){ 371 h=str[c++].charCodeAt().toString(16); 372 while(h.length<2) h="0"+h; 373 r.push(""+h); 374 } 375 return r.join(''); 376 } 377 378 this.getRandomBigIntegerInRange = getRandomBigIntegerInRange; 379 this.getRandomBigInteger = getRandomBigInteger; 380 this.getRandomBytes = getRandomBytes; 381 } 382