1 /* rsasign-1.3.4.js (c) 2010-2021 Kenji Urushima | kjur.github.io/jsrsasign/license
  2  */
  3 /*
  4  * rsa-sign.js - adding signing functions to RSAKey class.
  5  *
  6  * Copyright (c) 2010-2021 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 rsasign-1.2.js
 18  * @author Kenji Urushima kenji.urushima@gmail.com
 19  * @version jsrsasign 10.2.0 rsasign 1.3.4 (2021-Apr-13)
 20  * @license <a href="https://kjur.github.io/jsrsasign/license/">MIT License</a>
 21  */
 22 
 23 var _RE_HEXDECONLY = new RegExp("[^0-9a-f]", "gi");
 24 
 25 // ========================================================================
 26 // Signature Generation
 27 // ========================================================================
 28 
 29 function _rsasign_getHexPaddedDigestInfoForString(s, keySize, hashAlg) {
 30     var hashFunc = function(s) { return KJUR.crypto.Util.hashString(s, hashAlg); };
 31     var sHashHex = hashFunc(s);
 32 
 33     return KJUR.crypto.Util.getPaddedDigestInfoHex(sHashHex, hashAlg, keySize);
 34 }
 35 
 36 function _zeroPaddingOfSignature(hex, bitLength) {
 37     var s = "";
 38     var nZero = bitLength / 4 - hex.length;
 39     for (var i = 0; i < nZero; i++) {
 40 	s = s + "0";
 41     }
 42     return s + hex;
 43 }
 44 
 45 /**
 46  * sign for a message string with RSA private key.<br/>
 47  * @name sign
 48  * @memberOf RSAKey
 49  * @function
 50  * @param {String} s message string to be signed.
 51  * @param {String} hashAlg hash algorithm name for signing.<br/>
 52  * @return returns hexadecimal string of signature value.
 53  */
 54 RSAKey.prototype.sign = function(s, hashAlg) {
 55     var hashFunc = function(s) { return KJUR.crypto.Util.hashString(s, hashAlg); };
 56     var sHashHex = hashFunc(s);
 57 
 58     return this.signWithMessageHash(sHashHex, hashAlg);
 59 };
 60 
 61 /**
 62  * sign hash value of message to be signed with RSA private key.<br/>
 63  * @name signWithMessageHash
 64  * @memberOf RSAKey
 65  * @function
 66  * @param {String} sHashHex hexadecimal string of hash value of message to be signed.
 67  * @param {String} hashAlg hash algorithm name for signing.<br/>
 68  * @return returns hexadecimal string of signature value.
 69  * @since rsasign 1.2.6
 70  */
 71 RSAKey.prototype.signWithMessageHash = function(sHashHex, hashAlg) {
 72     var hPM = KJUR.crypto.Util.getPaddedDigestInfoHex(sHashHex, hashAlg, this.n.bitLength());
 73     var biPaddedMessage = parseBigInt(hPM, 16);
 74     var biSign = this.doPrivate(biPaddedMessage);
 75     var hexSign = biSign.toString(16);
 76     return _zeroPaddingOfSignature(hexSign, this.n.bitLength());
 77 }
 78 
 79 // PKCS#1 (PSS) mask generation function
 80 function pss_mgf1_str(seed, len, hash) {
 81     var mask = '', i = 0;
 82 
 83     while (mask.length < len) {
 84         mask += hextorstr(hash(rstrtohex(seed + String.fromCharCode.apply(String, [
 85                 (i & 0xff000000) >> 24,
 86                 (i & 0x00ff0000) >> 16,
 87                 (i & 0x0000ff00) >> 8,
 88                 i & 0x000000ff]))));
 89         i += 1;
 90     }
 91 
 92     return mask;
 93 }
 94 
 95 /**
 96  * sign for a message string with RSA private key by PKCS#1 PSS signing.<br/>
 97  * @name signPSS
 98  * @memberOf RSAKey
 99  * @function
100  * @param {String} s message string to be signed.
101  * @param {String} hashAlg hash algorithm name for signing.
102  * @param {Integer} sLen salt byte length from 0 to (keybytelen - hashbytelen - 2).
103  *        There are two special values:
104  *        <ul>
105  *        <li>-1: sets the salt length to the digest length</li>
106  *        <li>-2: sets the salt length to maximum permissible value
107  *           (i.e. keybytelen - hashbytelen - 2)</li>
108  *        </ul>
109  *        DEFAULT is -1. (NOTE: OpenSSL's default is -2.)
110  * @return returns hexadecimal string of signature value.
111  */
112 RSAKey.prototype.signPSS = function(s, hashAlg, sLen) {
113     var hashFunc = function(sHex) { return KJUR.crypto.Util.hashHex(sHex, hashAlg); } 
114     var hHash = hashFunc(rstrtohex(s));
115 
116     if (sLen === undefined) sLen = -1;
117     return this.signWithMessageHashPSS(hHash, hashAlg, sLen);
118 };
119 
120 /**
121  * sign hash value of message with RSA private key by PKCS#1 PSS signing.<br/>
122  * @name signWithMessageHashPSS
123  * @memberOf RSAKey
124  * @function
125  * @param {String} hHash hexadecimal hash value of message to be signed.
126  * @param {String} hashAlg hash algorithm name for signing.
127  * @param {Integer} sLen salt byte length from 0 to (keybytelen - hashbytelen - 2).
128  *        There are two special values:
129  *        <ul>
130  *        <li>-1: sets the salt length to the digest length</li>
131  *        <li>-2: sets the salt length to maximum permissible value
132  *           (i.e. keybytelen - hashbytelen - 2)</li>
133  *        </ul>
134  *        DEFAULT is -1. (NOTE: OpenSSL's default is -2.)
135  * @return returns hexadecimal string of signature value.
136  * @since rsasign 1.2.6
137  */
138 RSAKey.prototype.signWithMessageHashPSS = function(hHash, hashAlg, sLen) {
139     var mHash = hextorstr(hHash);
140     var hLen = mHash.length;
141     var emBits = this.n.bitLength() - 1;
142     var emLen = Math.ceil(emBits / 8);
143     var i;
144     var hashFunc = function(sHex) { return KJUR.crypto.Util.hashHex(sHex, hashAlg); } 
145 
146     if (sLen === -1 || sLen === undefined) {
147         sLen = hLen; // same as hash length
148     } else if (sLen === -2) {
149         sLen = emLen - hLen - 2; // maximum
150     } else if (sLen < -2) {
151         throw new Error("invalid salt length");
152     }
153 
154     if (emLen < (hLen + sLen + 2)) {
155         throw new Error("data too long");
156     }
157 
158     var salt = '';
159 
160     if (sLen > 0) {
161         salt = new Array(sLen);
162         new SecureRandom().nextBytes(salt);
163         salt = String.fromCharCode.apply(String, salt);
164     }
165 
166     var H = hextorstr(hashFunc(rstrtohex('\x00\x00\x00\x00\x00\x00\x00\x00' + mHash + salt)));
167     var PS = [];
168 
169     for (i = 0; i < emLen - sLen - hLen - 2; i += 1) {
170         PS[i] = 0x00;
171     }
172 
173     var DB = String.fromCharCode.apply(String, PS) + '\x01' + salt;
174     var dbMask = pss_mgf1_str(H, DB.length, hashFunc);
175     var maskedDB = [];
176 
177     for (i = 0; i < DB.length; i += 1) {
178         maskedDB[i] = DB.charCodeAt(i) ^ dbMask.charCodeAt(i);
179     }
180 
181     var mask = (0xff00 >> (8 * emLen - emBits)) & 0xff;
182     maskedDB[0] &= ~mask;
183 
184     for (i = 0; i < hLen; i++) {
185         maskedDB.push(H.charCodeAt(i));
186     }
187 
188     maskedDB.push(0xbc);
189 
190     return _zeroPaddingOfSignature(this.doPrivate(new BigInteger(maskedDB)).toString(16),
191 				   this.n.bitLength());
192 }
193 
194 // ========================================================================
195 // Signature Verification
196 // ========================================================================
197 
198 function _rsasign_getDecryptSignatureBI(biSig, hN, hE) {
199     var rsa = new RSAKey();
200     rsa.setPublic(hN, hE);
201     var biDecryptedSig = rsa.doPublic(biSig);
202     return biDecryptedSig;
203 }
204 
205 function _rsasign_getHexDigestInfoFromSig(biSig, hN, hE) {
206     var biDecryptedSig = _rsasign_getDecryptSignatureBI(biSig, hN, hE);
207     var hDigestInfo = biDecryptedSig.toString(16).replace(/^1f+00/, '');
208     return hDigestInfo;
209 }
210 
211 function _rsasign_getAlgNameAndHashFromHexDisgestInfo(hDigestInfo) {
212     for (var algName in KJUR.crypto.Util.DIGESTINFOHEAD) {
213 	var head = KJUR.crypto.Util.DIGESTINFOHEAD[algName];
214 	var len = head.length;
215 	if (hDigestInfo.substring(0, len) == head) {
216 	    var a = [algName, hDigestInfo.substring(len)];
217 	    return a;
218 	}
219     }
220     return [];
221 }
222 
223 /**
224  * verifies a sigature for a message string with RSA public key.<br/>
225  * @name verify
226  * @memberOf RSAKey#
227  * @function
228  * @param {String} sMsg raw message string to be verified.
229  * @param {String} hSig hexadecimal string of siganture.<br/>
230  *                 non-hexadecimal charactors including new lines will be ignored.
231  * @return returns true if valid, otherwise false
232  *
233  * @description
234  * This method verifies RSA signature with raw message string and
235  * hexadecimal signature value.
236  *
237  * @example
238  * pubkey = new RSAKey();
239  * pubkey.setPublic("1abd...", "10001");
240  * pubkey.verify("hello world", "3da1...") → true or false
241  */
242 RSAKey.prototype.verify = function(sMsg, hSig) {
243     hSig = hSig.toLowerCase();
244     if (hSig.match(/^[0-9a-f]+$/) == null) return false;
245     var biSig = parseBigInt(hSig, 16);
246     var keySize = this.n.bitLength();
247     if (biSig.bitLength() > keySize) return false;
248     var biDecryptedSig = this.doPublic(biSig);
249     var hDecryptedSig = biDecryptedSig.toString(16);
250     if (hDecryptedSig.length + 3 != keySize / 4) return false;
251     var hDigestInfo = hDecryptedSig.replace(/^1f+00/, '');
252     var digestInfoAry = 
253 	_rsasign_getAlgNameAndHashFromHexDisgestInfo(hDigestInfo);
254   
255     if (digestInfoAry.length == 0) return false;
256     var algName = digestInfoAry[0];
257     var diHashValue = digestInfoAry[1];
258     var ff = function(s) { return KJUR.crypto.Util.hashString(s, algName); };
259     var msgHashValue = ff(sMsg);
260     return (diHashValue == msgHashValue);
261 };
262 
263 /**
264  * verifies a sigature for a message string with RSA public key.<br/>
265  * @name verifyWithMessageHash
266  * @memberOf RSAKey
267  * @function
268  * @param {String} sHashHex hexadecimal hash value of message to be verified.
269  * @param {String} hSig hexadecimal string of siganture.<br/>
270  *                 non-hexadecimal charactors including new lines will be ignored.
271  * @return returns 1 if valid, otherwise 0
272  * @since rsasign 1.2.6
273  */
274 RSAKey.prototype.verifyWithMessageHash = function(sHashHex, hSig) {
275     if (hSig.length != Math.ceil(this.n.bitLength() / 4.0)) {
276 	return false;
277     }
278 
279     var biSig = parseBigInt(hSig, 16);
280 
281     if (biSig.bitLength() > this.n.bitLength()) return 0;
282 
283     var biDecryptedSig = this.doPublic(biSig);
284     var hDigestInfo = biDecryptedSig.toString(16).replace(/^1f+00/, '');
285     var digestInfoAry = _rsasign_getAlgNameAndHashFromHexDisgestInfo(hDigestInfo);
286   
287     if (digestInfoAry.length == 0) return false;
288     var algName = digestInfoAry[0];
289     var diHashValue = digestInfoAry[1];
290     return (diHashValue == sHashHex);
291 };
292 
293 /**
294  * verifies a sigature for a message string with RSA public key by PKCS#1 PSS sign.<br/>
295  * @name verifyPSS
296  * @memberOf RSAKey
297  * @function
298  * @param {String} sMsg message string to be verified.
299  * @param {String} hSig hexadecimal string of signature value
300  * @param {String} hashAlg hash algorithm name
301  * @param {Integer} sLen salt byte length from 0 to (keybytelen - hashbytelen - 2).
302  *        There are two special values:
303  *        <ul>
304  *        <li>-1: sets the salt length to the digest length</li>
305  *        <li>-2: sets the salt length to maximum permissible value
306  *           (i.e. keybytelen - hashbytelen - 2)</li>
307  *        </ul>
308  *        DEFAULT is -1. (NOTE: OpenSSL's default is -2.)
309  * @return returns true if valid, otherwise false
310  */
311 RSAKey.prototype.verifyPSS = function(sMsg, hSig, hashAlg, sLen) {
312     var hashFunc = function(sHex) { return KJUR.crypto.Util.hashHex(sHex, hashAlg); };
313     var hHash = hashFunc(rstrtohex(sMsg));
314 
315     if (sLen === undefined) sLen = -1;
316     return this.verifyWithMessageHashPSS(hHash, hSig, hashAlg, sLen);
317 }
318 
319 /**
320  * verifies a sigature for a hash value of message string with RSA public key by PKCS#1 PSS sign.<br/>
321  * @name verifyWithMessageHashPSS
322  * @memberOf RSAKey
323  * @function
324  * @param {String} hHash hexadecimal hash value of message string to be verified.
325  * @param {String} hSig hexadecimal string of signature value
326  * @param {String} hashAlg hash algorithm name
327  * @param {Integer} sLen salt byte length from 0 to (keybytelen - hashbytelen - 2).
328  *        There are two special values:
329  *        <ul>
330  *        <li>-1: sets the salt length to the digest length</li>
331  *        <li>-2: sets the salt length to maximum permissible value
332  *           (i.e. keybytelen - hashbytelen - 2)</li>
333  *        </ul>
334  *        DEFAULT is -1 (NOTE: OpenSSL's default is -2.)
335  * @return returns true if valid, otherwise false
336  * @since rsasign 1.2.6
337  */
338 RSAKey.prototype.verifyWithMessageHashPSS = function(hHash, hSig, hashAlg, sLen) {
339     if (hSig.length != Math.ceil(this.n.bitLength() / 4.0)) {
340 	return false;
341     }
342 
343     var biSig = new BigInteger(hSig, 16);
344 
345     var hashFunc = function(sHex) { return KJUR.crypto.Util.hashHex(sHex, hashAlg); };
346     var mHash = hextorstr(hHash);
347     var hLen = mHash.length;
348     var emBits = this.n.bitLength() - 1;
349     var emLen = Math.ceil(emBits / 8);
350     var i;
351 
352     if (sLen === -1 || sLen === undefined) {
353         sLen = hLen; // same as hash length
354     } else if (sLen === -2) {
355         sLen = emLen - hLen - 2; // recover
356     } else if (sLen < -2) {
357         throw new Error("invalid salt length");
358     }
359 
360     if (emLen < (hLen + sLen + 2)) {
361         throw new Error("data too long");
362     }
363 
364     var em = this.doPublic(biSig).toByteArray();
365 
366     for (i = 0; i < em.length; i += 1) {
367         em[i] &= 0xff;
368     }
369 
370     while (em.length < emLen) {
371         em.unshift(0);
372     }
373 
374     if (em[emLen -1] !== 0xbc) {
375         throw new Error("encoded message does not end in 0xbc");
376     }
377 
378     em = String.fromCharCode.apply(String, em);
379 
380     var maskedDB = em.substr(0, emLen - hLen - 1);
381     var H = em.substr(maskedDB.length, hLen);
382 
383     var mask = (0xff00 >> (8 * emLen - emBits)) & 0xff;
384 
385     if ((maskedDB.charCodeAt(0) & mask) !== 0) {
386         throw new Error("bits beyond keysize not zero");
387     }
388 
389     var dbMask = pss_mgf1_str(H, maskedDB.length, hashFunc);
390     var DB = [];
391 
392     for (i = 0; i < maskedDB.length; i += 1) {
393         DB[i] = maskedDB.charCodeAt(i) ^ dbMask.charCodeAt(i);
394     }
395 
396     DB[0] &= ~mask;
397 
398     var checkLen = emLen - hLen - sLen - 2;
399 
400     for (i = 0; i < checkLen; i += 1) {
401         if (DB[i] !== 0x00) {
402             throw new Error("leftmost octets not zero");
403         }
404     }
405 
406     if (DB[checkLen] !== 0x01) {
407         throw new Error("0x01 marker not found");
408     }
409 
410     return H === hextorstr(hashFunc(rstrtohex('\x00\x00\x00\x00\x00\x00\x00\x00' + mHash +
411 				     String.fromCharCode.apply(String, DB.slice(-sLen)))));
412 }
413 
414 RSAKey.SALT_LEN_HLEN = -1;
415 RSAKey.SALT_LEN_MAX = -2;
416 RSAKey.SALT_LEN_RECOVER = -2;
417 
418 /**
419  * @name RSAKey
420  * @class key of RSA public key algorithm
421  * @description Tom Wu's RSA Key class and extension
422  */
423