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